BaseHTTPServer模块分析

因为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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")

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()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值