本文讲解Python 2.7版本上的HTTP Server的实现。
钻研这个代码主要是因为线上出现了一个这样的问题。
1 | Traceback (most recent call last): |
一开始我是怀疑 HTTP1.0 的 Server 不能正确响应 HTTP1.1 的请求,但后来发现 Python2.7 对 HTTP1.1 的实现也就是是否设置 close_connection 字段的问题,结果看了一圈代码,没发现这个库会在套接口被关闭之后会再处理读取这个套接口。事实上,只有在 TCPServer.server_close 函数中才会调用 self.socket.close() 方法,而 socket.close() 方法也是唯一会将 self._sock 清空的。
一个请求的路由
首先介绍一下打交道最多的 BaseHTTPRequestHandler,在里面需要用户自己定义对每个请求的处理方法,例如要实现 do_GET、do_POST 等。这个东西实际上是每个请求都会创建一个,所以我们需要把全局用到的东西写到类成员里面。BaseHTTPRequestHandler 的继承链是 BaseRequestHandler -> StreamRequestHandler -> BaseHTTPRequestHandler。
Server 的继承链是 BaseServer -> TCPServer -> HTTPServer
从Server到Handler
BaseServer(TCPServer).process_request -> BaseServer(TCPServer).finish_request -> BaseRequestHandler.__init__
这个调用链解释了请求从Server到Handler的过程。可以看出,在finish_request中会直接创建一个RequestHandlerClass的实例,这个对应的就是我们继承实现的BaseHTTPRequestHandler。
1 | class BaseServer: |
在StreamRequestHandler中handle和finish被重写,在这里设置wfile和rfile。
1 | class StreamRequestHandler(BaseRequestHandler): |
Handler
下面是BaseRequestHandler.__init__的实现,主要涉及setup()和handle()方法。setup()继承自StreamRequestHandler。
1 | def setup(self): |
handle()代码如下,可以看到,当开启HTTP1.1后,close_connection会变成0,此时会一直handle_one_request。只有当收到的HTTP请求中也是HTTP1.1的,并且Connection: Keep-Alive时候close_connection才会变为0。当超时、Connection: Close、请求为空时,close_connection会变成1。
1 | def handle(self): |
handle_one_request
这个是实际处理 HTTP 的逻辑。
注意 self.rfile.readline 可能会阻塞。readline 参数接受一个表示 size 的字段,格式类似 GET /login_validate?username=aaa&password=bbb HTTP/1.1\r\n。如果读到的过长,会返回 414 错误,表示请求的 UR L过长。
1 | def handle_one_request(self): |