httpserver里面定义的类有HTTPServer、HTTPConnection、HTTPRequest。HTTPServer是一个非常简单的HTTP服务器,主要作用就是创建监听套接字,设置监听套接字的读事件hander,(创建多进程模式),接下来调用HTTPConnection处理整个连接。HTTPConnection读取请求并解析,调用HTTPServer设置的callback(一般是Application)处理请求,并提供发送响应的接口。这几个类都很简单。
HTTPServer单进程版本使用如下:
http_server = httpserver.HTTPServer(handle_request)
http_server.listen(8888) # 里面调用bind,start
ioloop.IOLoop.instance().start()
多进程版本需要显示调用bind()和start()接口。
def bind(self, port, address=""):
assert not self._socket
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
flags = fcntl.fcntl(self._socket.fileno(), fcntl.F_GETFD)
flags |= fcntl.FD_CLOEXEC
fcntl.fcntl(self._socket.fileno(), fcntl.F_SETFD, flags)
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._socket.setblocking(0)
self._socket.bind((address, port))
print address, port
self._socket.listen(128)
def start(self, num_processes=1):
assert not self._started
self._started = True
if num_processes is None or num_processes <= 0:
num_processes = _cpu_count()
if num_processes > 1 and ioloop.IOLoop.initialized():
logging.error("...")
num_processes = 1
if num_processes > 1:
logging.info("Pre-forking %d server processes", num_processes)
for i in range(num_processes):
if os.fork() == 0: # hp: child process
import random
from binascii import hexlify
...
# hp:每个子进程都有一个HTTPServer实例和IOLoop实例
# hp:会有惊群现象
self.io_loop = ioloop.IOLoop.instance()
self.io_loop.add_handler(
self._socket.fileno(), self._handle_events,
ioloop.IOLoop.READ)
# hp: child process over? No, 执行xxx.start()后面的代码
return
# hp: 父进程只等待任意一个子进程的结束么?
os.waitpid(-1, 0)
else:
if not self.io_loop:
self.io_loop = ioloop.IOLoop.instance()
# hp: 把( listen_socket, read, handle ) 加入epoll中
self.io_loop.add_handler(self._socket.fileno(),
self._handle_events,
ioloop.IOLoop.READ)
_handle_events是监听套接字的处理函数,也很简单:
def _handle_events(self, fd, events):
while True:
try:
# hp: _socket nonblocking
connection, address = self._socket.accept()
except socket.error, e:
...
if self.ssl_options is not None:
...
try:
if self.ssl_options is not None:
...
else:
# hp: read and write interface of connection socekt
stream = iostream.IOStream(connection, io_loop=self.io_loop)
HTTPConnection(stream, address, self.request_callback,
self.no_keep_alive, self.xheaders)
except:
...
这样子,连接套字的读写事件都是由HTTPConnection处理的。HTTPConnections首先调用self.stream.read_until("\r\n\r\n", self._header_callback),_header_callback函数解析请求行和请求头部,得到请求体长度Content-Lenght,接着调用self.stream.read_bytes(content_length, self._on_request_body),请求全部读完后就会调用_on_request_body函数,里面做一些解析,最后调用HTTPServer设置的request_callback(
用来产生响应,一般是Application)。
# hp: 读完请求头部(\r\n\r\n)执行的函数,由IOStream._read_from_buffer里面调用
def _on_headers(self, data):
try:
eol = data.find("\r\n")
start_line = data[:eol]
try:
method, uri, version = start_line.split(" ")
except ValueError:
...
# hp:解析请求头部, 字典
headers = httputil.HTTPHeaders.parse(data[eol:])
self._request = HTTPRequest(
connection=self, method=method, uri=uri, version=version,
headers=headers, remote_ip=self.address[0])
content_length = headers.get("Content-Length")
if content_length:
content_length = int(content_length)
if content_length > self.stream.max_buffer_size:
raise _BadRequestException("Content-Length too long")
# hp: Expect头部表示client对server有一定的要求,"100-continue"表示client
# 先发送请求头部(不含有请求内容),判断源服务器是否接受
if headers.get("Expect") == "100-continue":
self.stream.write("HTTP/1.1 100 (Continue)\r\n\r\n")
# hp: 设置http请求读取完毕需要执行的回调函数
self.stream.read_bytes(content_length, self._on_request_body)
return
# hp:请求不含有请求体,表示请求全部读取完,执行HTTPServer设置的钩子(Application)
self.request_callback(self._request)
except _BadRequestException, e:
...
发送响应时,可以调用HTTPConnection的write接口,里面直接调用IOStream的write接口,并设置发送结束的函数是_finish_request,里面判断是否是长连接。
def _finish_request(self):
if self.no_keep_alive:
disconnect = True
else:
connection_header = self._request.headers.get("Connection")
if self._request.supports_http_1_1():
disconnect = connection_header == "close"
elif ("Content-Length" in self._request.headers
or self._request.method in ("HEAD", "GET")):
disconnect = connection_header != "Keep-Alive"
else:
disconnect = True
self._request = None
self._request_finished = False
if disconnect:
self.stream.close()
return
# hp: 长连接,等待下一个请求
self.stream.read_until("\r\n\r\n", self._header_callback)
我用一个简单的例子测试一下:
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
def answer( request ):
print "invoked answer"
res = "method:{0}\nuri:{1}\nversion:{2}\n".format(request.method,request.uri,request.version)
for k,v in request.headers.iteritems():
res += k + ":" + v + "\n"
request.connection.write(res)
request.connection.finish()
def main():
http_server = tornado.httpserver.HTTPServer(answer, False)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()