Python - HTTP Server
BaseHTTPServer/http.server
Python2.7: BaseHTTPServer
Python3+: http.server
http.server 模块提供了创建 HTTP 服务器的简略接口。
http.server.HTTPServer
1 | class HTTPServer(socketserver.TCPServer): |
- 可以看到,
HTTPServer
实际上就是socketserver.TCPServer
的一个子类。所以,HTTPServer
的使用和TCPServer
是相似的,通过 Handler 类来处理请求。 http.server
模块提供的一个处理 HTTP 请求的 Handler 基类BaseHTTPRequestHandler
。- 同样,也可以继承
socketserver.ThreadingMixin
或socketserver.ForkingMixin
来处理请求。
http.server.BaseHTTPRequestHandler
BaseHTTPRequestHandler
和其它 Handler 类一样都是,通过 handler()
方法来处理请求。BaseHTTPRequestHandler
的 handle()
调用了另外一个自定义的方法 handle_one_request()
来处理请求。
handle_one_request()
中将请求的的数据通过 parse_request()
方法解析:
self.command
: string,请求方法,如HEAD
,GET
, etc.self.path
: string,请求方法之后的请求路径, 如/
。self.request_version
: string, 请求使用的 HTTP 协议版本,如HTTP/1.1
。self.headers
:mimetools.Message(rfc822.Message)
的一个实例。常用方法/属性:headers
: list, 请求头部信息列表。注意,每个信息是带换行符号的字符串。dict
: dict, 请求头部信息字典。items()
: list, 请求头部信息列表,不过每个信息是一个(key, value)
的二元组get(name, default=None)
: string, 获取 headers 信息中name
属性的值,没有就返回default
的值。
实际上,self.headers.get(name, default=None)
最终是调用self.headers.dict.get(name.lower(), default)
1
2
3
4self.headers.headers # ['Host: localhost\r\n', 'Accept: text/html\r\n']
self.headers.dict # {'host': 'localhost', 'accept': 'text/html'}
self.headers.items() # [('host', 'localhost'), ('accept', 'text/html')]
self.headers.get('host') # localhost
handle_one_request()
通过调用 parse_request()
方法解析出需要的信息之后,会执行以下代码:1
2
3
4
5
6mname = 'do_' + self.command
if not hasattr(self, mname):
self.send_error(501, "Unsupported method (%r)" % self.command)
return
method = getattr(self, mname)
method()
也就是说,如果请求的方法为 HEAD
,Handler 类最终会调用一个名为 do_HEAD()
的方法。这个方法在 BaseHTTPRequestHandler
是没有定义的,需要在子类去创建,然后通过该方法来处理请求。
socketserver 模块的 Handler 是通过子类的 handle()
处理请求,BaseHTTPServer/http.server 的子类是通过一个名为 do_xx
的方法来处理请求(xx
为请求方法)
Handler 类实例其它常用属性:
self.client_address
: tuple,客户端的地址(host, port)
,继承自socketserver.StreamRequestHandler
。self.server
: Server 类实例,继承自socketserver.StreamRequestHandler
。self.close_connection
: Boolean,是否关闭当前的连接。True/1,关闭连接;False/0,保持连接。self.rfile
: 继承自socketserver.StreamRequestHandler
,io.BufferedIOBase
输入流对象。self.wfile
: 继承自socketserver.StreamRequestHandler
, 输出流对象。
Handler 类属性:
server_version
: string, 服务器版本号,主要用于调用send_response()
方法的时候返回头部信息中Server
的值。sys_version
: string, Python 版本,主要用于调用send_response()
方法的时候返回头部信息中Server
的值。protocol_version
: string, HTTP 版本,默认为HTTP/1.0
。HTTP/1.1
会自动保持连接不关闭。responses
: dict, HTTP 返回状态码和相应的信息,{100: ('Continue', 'Request received, please continue'), ...}
。
Handler 类常用方法:
address_string()
: 返回客户端地址。1
2
3def address_string(self):
host, port = self.client_address[:2]
return socket.getfqdn(host)version_string()
: 服务器的版本信息,主要用于send_response()
方法中返回头部信息Server
的值。1
2def version_string(self):
return self.server_version + ' ' + self.sys_versionsend_response(code, message=None)
,返回信息给客户端。1
2
3
4
5
6
7
8
9
10
11
12self.log_request(code)
if message is None:
if code in self.responses:
message = self.responses[code][0]
else:
message = ''
if self.request_version != 'HTTP/0.9':
self.wfile.write("%s %d %s\r\n" %
(self.protocol_version, code, message))
# print (self.protocol_version, code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())code
: 请求返回状态码,e.g.200
,404
, etc.message
: 返回状态信息,e.g.ok
->HTTP/1.1 200 ok
.
默认情况下,调用这个方法返回状态信息,还附带返回了头部信息中的Server
和Data
信息。
send_header()
: 返回头部信息。1
2
3
4
5
6
7
8
9def send_header(self, keyword, value):
if self.request_version != 'HTTP/0.9':
self.wfile.write("%s: %s\r\n" % (keyword, value))
if keyword.lower() == 'connection':
if value.lower() == 'close':
self.close_connection = 1
elif value.lower() == 'keep-alive':
self.close_connection = 0返回头部信息,如果返回信息中有
Connection
属性,会根据value
的值自动关闭或保持连接。end_headers()
: 结束头部信息。HTTP 协议规定返回信息中头部和主体要用\r\n
或\n
分开。1
2
3def end_headers(self):
if self.request_version != 'HTTP/0.9':
self.wfile.write("\r\n")
BaseHTTPServer/http.server 实现 HTTP 服务器
首先,定义 BaseHTTPRequestHandler
子类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def version_string(self):
return 'nginx/1.13.1'
def send_body(self):
self.wfile.write('<div>hello world!</div>')
self.wfile.write("\r\n")
def do_HEAD(self):
if self.path != '/':
self.send_error(404)
return False
self.send_response(200, 'hi~')
self.send_header('Connect-Type', 'text/html')
# HTTP/1.0 keep-alive
if self.headers.get('connection') == 'keep-alive':
self.send_header('connection', 'keep-alive')
self.end_headers()
self.send_body()
if __name__ == '__main__':
server = HTTPServer(('localhost', 8080), HTTPHandler)
server.serve_forever()
上面例子中 do_HEAD
方法会处理 HTTP HEAD
方法的请求。如请求数据为:1
2HEAD / HTTP/1.1
Host: localhost
返回数据为:1
2
3
4
5
6HTTP/1.0 200 hi~
Server: nginx/1.13.1
Date: Sun, 11 Feb 2018 08:55:09 GMT
Connect-Type: text/html
<div>hello world!</div>
SimpleHTTPServer/http.server
Python2.7: SimpleHTTPServer
Python3+: http.server
Python3 以后,将 Python2 的SimpleHTTPServer
模块合并到了http.server
中。
SimpleHTTPServer
模块主要提供了一个 HTTP 协议的简单实现 Handler:SimpleHTTPRequestHandler
。SimpleHTTPRequestHandler
实现了 HTTP 的 HEAD 和 GET 请求方法:do_HEAD()
和do_GET()
。do_GET()
会根据请求路径对服务器当前目录下的文件进行访问,默认主页为index.html
或index.htm
简单 HTTP 服务器实现
1 | from http.server import HTTPServer, SimpleHTTPRequestHandler |
在当前代码运行的目录下创建文件 index.html
,然后访问 http://localhost:8080
,服务器响应的就是 index.html
的内容.
CGIHTTPServer/http.server
Python2.7: CGIHTTPServer
Python3+: http.server
Python3 以后,将 Python2 的CGIHTTPServer
模块合并到了http.server
中。
CGIHTTPServer/http.server
模块定义了 CGI 协议的处理器CGIHTTPRequestHandler
,该 Handler 继承自SimpleHTTPRequestHandler
。CGIHTTPRequestHandler
可以处理 CGI 脚本,在SimpleHTTPRequestHandler
的基础上添加了 HTTP POST 方法的处理do_POST
。
CGI: Common Gateway Interface, 通用网关接口。
当网站的页面为静态内容的时候,服务器直接返回相应的资源文件就可以了(如:index.html), 但是很多情况下,静态页面是不能够实现我们的需求的,我们需要用 PHP,Python等语言来完成页面的内容(如:index.php)。
那么,这个时候服务器要怎么响应客户端的请求呢? 像静态文件一样直接返回肯定是不行的,返回的只是代码,我们需要的是代码执行后的内容。
所以,当客户端请求的内容是一个动态页面(如:.php
,.py
等文件)的时候,服务器要去执行这个动态脚本,然后返回脚本的输出内容。
CGI 就是 web 服务器与CGI脚本 (如:
.php
,.py
等文件) 之间的接口协议。CGI 程序从环境变量和标准输入中读取数据执行完毕之后想标准输出输出数据。web服务器将CGI程序的输出数据返回给客户端。
标准输入:标准输入中的数据一般是服务器接收到的客户端 GET,POST,PUT等发送过来的数据。
环境变量:服务器将一些必要的参数声明成环境变量来传递给 CGI 脚本就。常见的CGI环境变量:
SCRIPT_NAME
: CGI 脚本名称。SCRIPT_FILENAME
: CGI 脚本的绝对路径。CONTENT_TYPE
: 标准输入中传递的信息的MIME类型,如:application/x-www-form-urlencoded
。CONTENT_LENGTH
: 传递内容的长度。HTTP_COOKIE
: 客户端的cookie,即请求 HTTP 头部Cookie/Cookie2
传过来的 cookie。HTTP_USER_AGENT
: 请求 HTTP 头部的User-Agent
.PATH_INFO
: 表示请求URL中紧接在CGI脚本名称后面的path。QUERY_STING
: URL 中的query,即?
后面的参数(不包括#
)。REMOTE_ADDR
: 客户端IP地址。REQUEST_METHOD
: 请求方法,如:GET
,POST
等。
了解 CGI 之后,我们来看一下 CGIHTTPRequestHandler
:
cgi_directories
: 类变量cgi_directories
定义了 CGI 脚本的目录1
cgi_directories = ['/cgi-bin', '/htbin']
所以,把 CGI 脚本存放到服务器运行目录下的
cgi-bin
或htbin
目录下,或者通过修改cgi_directories
自定义目录。run_cgi()
: 处理 CGI 脚本的主要方法。首先,初始化 CGI 的环境变量,然后通过os.fork()
或subprocess.Popen()
来运行 CGI 脚本。do_POST()
: 处理 HTTP POST 方法的请求,仅处理 CGI 脚本的请求。主要通过run_cgi()
来处理动态脚本。send_head()
: 重写SimpleHTTPRequestHandler
的send_head()
,使 HTTPHEAD
和GET
请求也支持 CGI。
支持 CGI 协议的简单 HTTP 服务器
服务器脚本 server.py
:1
2
3
4
5
6from http.server import HTTPServer, CGIHTTPRequestHandler
if __name__ == '__main__':
CGIHTTPRequestHandler.cgi_directories += ['/apps'] # add custom cgi directory
server = HTTPServer(('localhost', 8000), CGIHTTPRequestHandler)
server.serve_forever()
运行服务器脚本 server.py
,并在当前路径下创建 apps
目录,用来保存 CGI 程序。apps/index.sh
:1
2
3
4
5
6#!/bin/bash
echo "REQUEST METHOD: $REQUEST_METHOD"
echo "QUERY STRING: $QUERY_STRING"
read vars
echo "CONTENT: $vars"
客户端发送数据:1
2
3
4
5POST /apps/index.sh?action=create HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
name=lizs&age=26
接收数据:1
2
3
4
5
6
7HTTP/1.0 200 Script output follows
Server: SimpleHTTP/0.6 Python/2.7.12
Date: Tue, 06 Mar 2018 13:01:42 GMT
REQUEST METHOD: POST
QUERY STRING: action=create
CONTENT: name=lizs&age=26
CGI 脚本 apps/index.py
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import cgi
def msg(name):
print('%s: %s' % (name, os.environ.get(name, '')))
msg('SCRIPT_NAME')
msg('REQUEST_METHOD')
msg('QUERY_STRING')
storage = cgi.FieldStorage()
print(storage.getvalue('name'))
print(storage.getvalue('age'))
客户端请求数据:1
2GET /apps/index.py?name=lizs&age=26 HTTP/1.1
Host: localhost
接收数据:1
2
3
4
5
6
7
8
9HTTP/1.0 200 Script output follows
Server: SimpleHTTP/0.6 Python/2.7.12
Date: Tue, 06 Mar 2018 13:11:29 GMT
SCRIPT_NAME: /apps/index.py
REQUEST_METHOD: GET
QUERY_STRING: name=lizs&age=26
name: lizs
age: 26
发送数据:1
2
3
4
5POST /apps/index.py?action=create HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
name=lizs&age=26
接收数据:1
2
3
4
5
6
7HTTP/1.0 200 Script output follows
Server: SimpleHTTP/0.6 Python/2.7.12
Date: Tue, 06 Mar 2018 13:13:21 GMT
SCRIPT_NAME: /apps/index.py
REQUEST_METHOD: POST
QUERY_STRING: action=create
获取content失败?
Python3+ BaseHTTPRequestHandler
源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
# The Python system version, truncated to its first component.
sys_version = "Python/" + sys.version.split()[0]
# The server software version. You may want to override this.
# The format is multiple whitespace-separated strings,
# where each string is of the form name[/version].
server_version = "BaseHTTP/" + __version__
error_message_format = DEFAULT_ERROR_MESSAGE
error_content_type = DEFAULT_ERROR_CONTENT_TYPE
# The default request version. This only affects responses up until
# the point where the request line is parsed, so it mainly decides what
# the client gets back when sending a malformed request line.
# Most web servers default to HTTP 0.9, i.e. don't send a status line.
default_request_version = "HTTP/0.9"
def parse_request(self):
"""Parse a request (internal).
The request should be stored in self.raw_requestline; the results
are in self.command, self.path, self.request_version and
self.headers.
Return True for success, False for failure; on failure, an
error is sent back.
"""
self.command = None # set in case of error on the first line
self.request_version = version = self.default_request_version
self.close_connection = True
requestline = str(self.raw_requestline, 'iso-8859-1')
requestline = requestline.rstrip('\r\n')
self.requestline = requestline
words = requestline.split()
if len(words) == 3:
command, path, version = words
if version[:5] != 'HTTP/':
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad request version (%r)" % version)
return False
try:
base_version_number = version.split('/', 1)[1]
version_number = base_version_number.split(".")
# RFC 2145 section 3.1 says there can be only one "." and
# - major and minor numbers MUST be treated as
# separate integers;
# - HTTP/2.4 is a lower version than HTTP/2.13, which in
# turn is lower than HTTP/12.3;
# - Leading zeros MUST be ignored by recipients.
if len(version_number) != 2:
raise ValueError
version_number = int(version_number[0]), int(version_number[1])
except (ValueError, IndexError):
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad request version (%r)" % version)
return False
if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
self.close_connection = False
if version_number >= (2, 0):
self.send_error(
HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
"Invalid HTTP Version (%s)" % base_version_number)
return False
elif len(words) == 2:
command, path = words
self.close_connection = True
if command != 'GET':
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad HTTP/0.9 request type (%r)" % command)
return False
elif not words:
return False
else:
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad request syntax (%r)" % requestline)
return False
self.command, self.path, self.request_version = command, path, version
# Examine the headers and look for a Connection directive.
try:
self.headers = http.client.parse_headers(self.rfile,
_class=self.MessageClass)
except http.client.LineTooLong:
self.send_error(
HTTPStatus.BAD_REQUEST,
"Line too long")
return False
except http.client.HTTPException as err:
self.send_error(
HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
"Too many headers",
str(err)
)
return False
conntype = self.headers.get('Connection', "")
if conntype.lower() == 'close':
self.close_connection = True
elif (conntype.lower() == 'keep-alive' and
self.protocol_version >= "HTTP/1.1"):
self.close_connection = False
# Examine the headers and look for an Expect directive
expect = self.headers.get('Expect', "")
if (expect.lower() == "100-continue" and
self.protocol_version >= "HTTP/1.1" and
self.request_version >= "HTTP/1.1"):
if not self.handle_expect_100():
return False
return True
def handle_expect_100(self):
"""Decide what to do with an "Expect: 100-continue" header.
If the client is expecting a 100 Continue response, we must
respond with either a 100 Continue or a final response before
waiting for the request body. The default is to always respond
with a 100 Continue. You can behave differently (for example,
reject unauthorized requests) by overriding this method.
This method should either return True (possibly after sending
a 100 Continue response) or send an error response and return
False.
"""
self.send_response_only(HTTPStatus.CONTINUE)
self.end_headers()
return True
def handle_one_request(self):
"""Handle a single HTTP request.
You normally don't need to override this method; see the class
__doc__ string for information on how to handle specific HTTP
commands such as GET and POST.
"""
try:
self.raw_requestline = self.rfile.readline(65537)
if len(self.raw_requestline) > 65536:
self.requestline = ''
self.request_version = ''
self.command = ''
self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
return
if not self.raw_requestline:
self.close_connection = True
return
if not self.parse_request():
# An error code has been sent, just exit
return
mname = 'do_' + self.command
if not hasattr(self, mname):
self.send_error(
HTTPStatus.NOT_IMPLEMENTED,
"Unsupported method (%r)" % self.command)
return
method = getattr(self, mname)
method()
self.wfile.flush() #actually send the response if not already done.
except socket.timeout as e:
#a read or a write timed out. Discard this connection
self.log_error("Request timed out: %r", e)
self.close_connection = True
return
def handle(self):
"""Handle multiple requests if necessary."""
self.close_connection = True
self.handle_one_request()
while not self.close_connection:
self.handle_one_request()
def send_error(self, code, message=None, explain=None):
"""Send and log an error reply.
Arguments are
* code: an HTTP error code
3 digits
* message: a simple optional 1 line reason phrase.
*( HTAB / SP / VCHAR / %x80-FF )
defaults to short entry matching the response code
* explain: a detailed message defaults to the long entry
matching the response code.
This sends an error response (so it must be called before any
output has been generated), logs the error, and finally sends
a piece of HTML explaining the error to the user.
"""
try:
shortmsg, longmsg = self.responses[code]
except KeyError:
shortmsg, longmsg = '???', '???'
if message is None:
message = shortmsg
if explain is None:
explain = longmsg
self.log_error("code %d, message %s", code, message)
self.send_response(code, message)
self.send_header('Connection', 'close')
# Message body is omitted for cases described in:
# - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
# - RFC7231: 6.3.6. 205(Reset Content)
body = None
if (code >= 200 and
code not in (HTTPStatus.NO_CONTENT,
HTTPStatus.RESET_CONTENT,
HTTPStatus.NOT_MODIFIED)):
# HTML encode to prevent Cross Site Scripting attacks
# (see bug #1100201)
content = (self.error_message_format % {
'code': code,
'message': _quote_html(message),
'explain': _quote_html(explain)
})
body = content.encode('UTF-8', 'replace')
self.send_header("Content-Type", self.error_content_type)
self.send_header('Content-Length', int(len(body)))
self.end_headers()
if self.command != 'HEAD' and body:
self.wfile.write(body)
def send_response(self, code, message=None):
"""Add the response header to the headers buffer and log the
response code.
Also send two standard headers with the server software
version and the current date.
"""
self.log_request(code)
self.send_response_only(code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())
def send_response_only(self, code, message=None):
"""Send the response header only."""
if message is None:
if code in self.responses:
message = self.responses[code][0]
else:
message = ''
if self.request_version != 'HTTP/0.9':
if not hasattr(self, '_headers_buffer'):
self._headers_buffer = []
self._headers_buffer.append(("%s %d %s\r\n" %
(self.protocol_version, code, message)).encode(
'latin-1', 'strict'))
def send_header(self, keyword, value):
"""Send a MIME header to the headers buffer."""
if self.request_version != 'HTTP/0.9':
if not hasattr(self, '_headers_buffer'):
self._headers_buffer = []
self._headers_buffer.append(
("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
if keyword.lower() == 'connection':
if value.lower() == 'close':
self.close_connection = True
elif value.lower() == 'keep-alive':
self.close_connection = False
def end_headers(self):
"""Send the blank line ending the MIME headers."""
if self.request_version != 'HTTP/0.9':
self._headers_buffer.append(b"\r\n")
self.flush_headers()
def flush_headers(self):
if hasattr(self, '_headers_buffer'):
self.wfile.write(b"".join(self._headers_buffer))
self._headers_buffer = []
def log_request(self, code='-', size='-'):
"""Log an accepted request.
This is called by send_response().
"""
if isinstance(code, HTTPStatus):
code = code.value
self.log_message('"%s" %s %s',
self.requestline, str(code), str(size))
def log_error(self, format, *args):
"""Log an error.
This is called when a request cannot be fulfilled. By
default it passes the message on to log_message().
Arguments are the same as for log_message().
XXX This should go to the separate error log.
"""
self.log_message(format, *args)
def log_message(self, format, *args):
"""Log an arbitrary message.
This is used by all other logging functions. Override
it if you have specific logging wishes.
The first argument, FORMAT, is a format string for the
message to be logged. If the format string contains
any % escapes requiring parameters, they should be
specified as subsequent arguments (it's just like
printf!).
The client ip and current date/time are prefixed to
every message.
"""
sys.stderr.write("%s - - [%s] %s\n" %
(self.address_string(),
self.log_date_time_string(),
format%args))
def version_string(self):
"""Return the server software version string."""
return self.server_version + ' ' + self.sys_version
def date_time_string(self, timestamp=None):
"""Return the current date and time formatted for a message header."""
if timestamp is None:
timestamp = time.time()
year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
self.weekdayname[wd],
day, self.monthname[month], year,
hh, mm, ss)
return s
def log_date_time_string(self):
"""Return the current time formatted for logging."""
now = time.time()
year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
s = "%02d/%3s/%04d %02d:%02d:%02d" % (
day, self.monthname[month], year, hh, mm, ss)
return s
weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
monthname = [None,
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
def address_string(self):
"""Return the client address."""
return self.client_address[0]
# Essentially static class variables
# The version of the HTTP protocol we support.
# Set this to HTTP/1.1 to enable automatic keepalive
protocol_version = "HTTP/1.0"
# MessageClass used to parse headers
MessageClass = http.client.HTTPMessage
# hack to maintain backwards compatibility
responses = {
v: (v.phrase, v.description)
for v in HTTPStatus.__members__.values()
}