本文讲解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): |