因为BaseHTTPServer是个基础模块,仅提供了对HTTP请求的解析和响应框架。通过对该模块的了解,可以熟悉如何解析HTTP请求,窥探web服务的内部基础。
该模块理解起来很容易,所以就没写例子,可以直接看注释的源码。但在这之前,推荐先了解一下HTTP协议,《HTTP协议详解》是一篇非常经典的文章。
##
# @file $python27$\Lib\BaseHTTPServer.py
# @brief 解析HTTP请求,但未提供相应响应,是SimpleHTTPServer基础模块
# @date 2015-02-11
# @remark 为减少字符量,文件中的注释和对理解无关的内容都被我删除了,想详细了解请见原文件
__version__ = "0.3"
# 出错时响应给客户端的默认html模板,%(***)s、%(***)d为字典型式
DEFAULT_ERROR_MESSAGE = """\
<head>
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code %(code)d.
<p>Message: %(message)s.
<p>Error code explanation: %(code)s = %(explain)s.
</body>
"""
DEFAULT_ERROR_CONTENT_TYPE = "text/html"
# 字符串到html的转义
def _quote_html(html):
return html.replace("&", "&").replace("<", "<").replace(">", ">")
class HTTPServer(SocketServer.TCPServer):
allow_reuse_address = 1 # 允许socket端口重用
# 重写了基类TCPServer的函数,多保存了计算机名称
def server_bind(self):
SocketServer.TCPServer.server_bind(self)
host, port = self.socket.getsockname()[:2]
self.server_name = socket.getfqdn(host) # 获得计算机名称
self.server_port = port
class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
default_request_version = "HTTP/0.9" # 缺省时HTTP请求的版本
# 解析HTTP请求的第一行,以及HTTP请求头(该项由mimetools.Message类完成)
def parse_request(self):
self.command = None # 保存了HTTP请求方式,GET、POST...
self.close_connection = 1
requestline = self.raw_requestline.rstrip('\r\n')
words = requestline.split() # HTTP请求第一行由三部分组成:方式、路径、协议,如GET /index HTTP/1.1
if len(words) == 3:
command, path, version = words
if version[:5] != 'HTTP/':
self.send_error(400, "Bad request version (%r)" % version)
return False
try:
base_version_number = version.split('/', 1)[1]
version_number = base_version_number.split(".")
if len(version_number) != 2:
raise ValueError
version_number = int(version_number[0]), int(version_number[1])
except (ValueError, IndexError):
self.send_error(400, "Bad request version (%r)" % version)
return False
# HTTP1.0需要"Connection:keep-alive"才能支持KeepAlive,而HTTP1.1默认支持
if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
self.close_connection = 0
if version_number >= (2, 0):
self.send_error(505,
"Invalid HTTP Version (%s)" % base_version_number)
return False
elif len(words) == 2: # 应该是为了支持HTTP/0.9吧
...
elif not words:
return False
else:
self.send_error(400, "Bad request syntax (%r)" % requestline)
return False
# 依次为:请求方式、请求相对路径、请求协议
self.command, self.path, self.request_version = command, path, version
# 采用mimetools.Message类解析HTTP请求头
self.headers = self.MessageClass(self.rfile, 0)
# 处理Connection,这在HTTP/1.0和HTTP/1.1上有区别
conntype = self.headers.get('Connection', "")
if conntype.lower() == 'close':
self.close_connection = 1
elif (conntype.lower() == 'keep-alive' and
self.protocol_version >= "HTTP/1.1"):
self.close_connection = 0
return True
# 处理单次HTTP请求
def handle_one_request(self):
try:
# 读取HTTP请求的第1行
self.raw_requestline = self.rfile.readline(65537)
if len(self.raw_requestline) > 65536:
...
self.send_error(414)
return
if not self.raw_requestline:
self.close_connection = 1
return
if not self.parse_request(): # 处理第1行请求,并处理HTTP请求头
return
# 通过HTTP请求方式映射到上层相应的处理函数,如GET -> do_GET(),POST -> do_POST()
mname = 'do_' + self.command
if not hasattr(self, mname):
...
method = getattr(self, mname)
method()
self.wfile.flush() # 将socket写缓存中的数据都发送出去
except socket.timeout, e:
self.log_error("Request timed out: %r", e)
self.close_connection = 1
return
# serve_forever()中accept到socket后会丢到这个函数处理请求
def handle(self):
self.close_connection = 1
# 非常奇怪的设计,因为现在HTTP请求版本为1.1,Connection: keep-alive,如果HTTP响应
# 版本为1.1,则self.close_connection = 0,每个请求需要等待一会才结束
self.handle_one_request()
while not self.close_connection:
self.handle_one_request()
# 向客户端响应请求错误
def send_error(self, code, message=None):
try:
short, long = self.responses[code]
except KeyError:
short, long = '???', '???'
if message is None:
message = short
explain = long
self.log_error("code %d, message %s", code, message)
content = (self.error_message_format %
{'code': code, 'message': _quote_html(message), 'explain': explain})
self.send_response(code, message)
self.send_header("Content-Type", self.error_content_type)
self.send_header('Connection', 'close')
self.end_headers()
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
self.wfile.write(content)
error_message_format = DEFAULT_ERROR_MESSAGE
error_content_type = DEFAULT_ERROR_CONTENT_TYPE
# 进行HTTP响应,发送响应第1行,包括三部分:版本、状态码、状态消息,例如“HTTP/1.0 200 OK”
# 还发送部分响应头,Server和Data
def send_response(self, code, message=None):
self.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())
# 发送单个HTTP响应报头
def 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
# 发送HTTP响应报头结束后的空行
def end_headers(self):
if self.request_version != 'HTTP/0.9': self.wfile.write("\r\n")
# 返回服务端版本号
def version_string(self):
return self.server_version + ' ' + self.sys_version
protocol_version = "HTTP/1.0" # 服务端响应时的HTTP协议
MessageClass = mimetools.Message # 解析HTTP请求报头信息类
responses = {
100: ('Continue', 'Request received, please continue'),
101: ('Switching Protocols',
'Switching to new protocol; obey Upgrade header'),
...
}
# '__main__'
server_address = ('', 8000)
httpd = HTTPServer(server_address, BaseHTTPRequestHandler)
sa = httpd.socket.getsockname()
print "Serving HTTP on", sa[0], "port", sa[1], "..."
httpd.serve_forever()