本文讲解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错误,表示请求的URL过长。
1 | def handle_one_request(self): |