Tornado源码分析之HTTPConnection ---- http协议解析

上回说道,IOLoop接收到客户端的连接并生成HTTPConnection对象。接上回:HTTPConnection的主要功能是读取客户端发来的数据,并以http协议解析,生成HTTPRequest对象。HTTP协议在相关的rfc文档中有描述,心里头有个大概的了解就行。用python代码来实现http解析也不是很难,大概就这么几步:解析消息头,一般的get,head,delete,option类型的请求只有消息头,无消息体。如果是post或者put请求,会有消息体,主要是提交页面<form>中的表单数据,如果有上传的文件,还需要解析这上传文件数据。现在详细说说代码,发一下HTTPConnection的构造函数的code,因为这里的故事,从构造HTTPConnection开始。翠花,上代码:

  1. def __init__(self, stream, address, request_callback, no_keep_alive=False,  
  2.              xheaders=False):  
  3.     self.stream = stream  
  4.     if self.stream.socket.family not in (socket.AF_INET, socket.AF_INET6):  
  5.         # Unix (or other) socket; fake the remote address  
  6.         address = ('0.0.0.0'0)  
  7.     self.address = address  
  8.     self.request_callback = request_callback  # 亲,留意一下这个回调函数  
  9.     self.no_keep_alive = no_keep_alive  
  10.     self.xheaders = xheaders  
  11.       
  12.     self._request = None  
  13.     self._request_finished = False  
  14.     # Save stack context here, outside of any request.  This keeps  
  15.     # contexts from one request from leaking into the next.  
  16.     self._header_callback = stack_context.wrap(self._on_headers)  
  17.     self.stream.read_until(b("\r\n\r\n"), self._header_callback)    # 向epoll注册read事件  
  18.     self._write_callback = None  

代码不是很难,关键的一行是:self.stream.read_until(b("\r\n\r\n"), self._header_callback)。 这是一个异步操作,当读取到 "\r\n\r\n"时,就触发 _header_callback,这个回调函数,其实就是HTTPConnection的_on_headers方法,主要用于解析请求头。Http请求头大概是这个样子的(如果脑子里完全没有概念的话,读代码会很辛苦的,有时候一图抵万言):

除了图的第一行外,其他都是以 Key: Value 这种样式,第一行有三个部分组成:HTTP_METHOD, REQUEST_URI, HTTP_VERSION

      有了上面一张图,现在就分析一下_on_headers方法,主要用于解析request header (代码有所省略):

  1. data = native_str(data.decode('latin1'))  
  2. eol = data.find("\r\n")  
  3. start_line = data[:eol]  
  4. try:  
  5.     method, uri, version = start_line.split(" ")  # 解析第一行,获取:HTTP_METHOD, REQUEST_URI, HTTP_VERSION  
  6. except ValueError:  
  7.     raise _BadRequestException("Malformed HTTP request line")  
  8. if not version.startswith("HTTP/"):  
  9.     raise _BadRequestException("Malformed HTTP version in HTTP Request-Line")  
  10. headers = httputil.HTTPHeaders.parse(data[eol:])   # 解析请求头的剩余部分  
  11. self._request = HTTPRequest(  
  12.     connection=self, method=method, uri=uri, version=version,  
  13.     headers=headers, remote_ip=self.address[0])  
  14.   
  15. content_length = headers.get("Content-Length")  
  16. if content_length:  # 一般 post请求 有content-length,而 get请求 只有 request header  
  17.     content_length = int(content_length)  
  18.     if content_length > self.stream.max_buffer_size:  
  19.         raise _BadRequestException("Content-Length too long")  
  20.     if headers.get("Expect") == "100-continue":  
  21.         self.stream.write(b("HTTP/1.1 100 (Continue)\r\n\r\n"))  
  22.     self.stream.read_bytes(content_length, self._on_request_body)  # 这是一个异步操作,读取body。  
  23.     return  
  24.   
  25. self.request_callback(self._request)  # 注意这一行,如果是GET, HEAD等这种没有请求体请求,就直接把请求提交给相应的处理函数。  

针对POST和PUT有消息体的请求,还需要接着解析消息体, self.stream.read_bytes(content_length, self._on_request_body) 也是异步操作,解析消息体的方法是_on_request_body,主要的代码:

  1. if content_type.startswith("application/x-www-form-urlencoded"):  
  2.     arguments = parse_qs_bytes(native_str(self._request.body))  
  3.     for name, values in arguments.iteritems():  
  4.         values = [v for v in values if v]  
  5.         if values:  
  6.             self._request.arguments.setdefault(name, []).extend(  
  7.                 values)  
  8. elif content_type.startswith("multipart/form-data"):    # 包含文件上传  
  9.     fields = content_type.split(";")  
  10.     for field in fields:  
  11.         k, sep, v = field.strip().partition("=")  
  12.         if k == "boundary" and v:  
  13.             httputil.parse_multipart_form_data(  
  14.                 utf8(v), data,  
  15.                 self._request.arguments,  
  16.                 self._request.files)  
  17.             break  
  18.     else:  
  19.         logging.warning("Invalid multipart/form-data")  

代码也不是很难,看到application/x-www-form-urlencoded 与 multipart/form-data 是不是觉得很熟悉,写表单,上传文件的时候<form>中的属性。呵呵,原来就这么一回事。

      言而总之,总而言之,HTTPConnection主要将socket连接接收到的请求数据解析成一个HttpRequest对象,最后将这个对象提交给相应的handler处理,完成最后一击的是:self.request_callback(self._request) 。self.request_callback 在构造函数中初始化。详细功能,待下回分解。


参考 :http://www.darkbull.net/article/tornado_src_study_httpconnection/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值