对于具体Etag是什么,请求流程,实现原理,这里不进行介绍,能够参考下面连接:web
http://www.oschina.net/question/234345_42536?sort=time正则表达式
https://zh.wikipedia.org/wiki/HTTP_ETag函数
先从Tornado处理一个请求的调用顺序开始看(摘自文档:http://www.tornadoweb.cn/documentation):tornado
initialize()
函数,这个函数的参数是 Application
配置中的关键字 参数定义。(initialize
方法是 Tornado 1.1 中新添加的,旧版本中你须要 重写 __init__
以达到一样的目的) initialize
方法通常只是把传入的参数存 到成员变量中,而不会产生一些输出或者调用像 send_error
之类的方法。prepare()
。不管使用了哪一种 HTTP 方法,prepare
都会被调用到,所以 这个方法一般会被定义在一个基类中,而后在子类中重用。prepare能够产生输出 信息。若是它调用了
finish(或
send_error` 等函数),那么整个处理流程 就此结束。get()
、post()
、put()
等。若是 URL 的正则表达式模式中有分组匹配,那么相关匹配会做为参数传入方法。在一个请求结束的时候确定会进行Etag的处理,因此找到调用的 finish() 函数:post
finish() 函数 ---- 地址:tornado/web.py(删除了部分不在此主题的代码)spa
1 def finish(self, chunk=None): 2 # Automatically support ETags and add the Content-Length header if 3 # we have not flushed any content yet. 4 if not self._headers_written: 5 if (self._status_code == 200 and 6 self.request.method in ("GET", "HEAD") and 7 "Etag" not in self._headers): 8 self.set_etag_header() 9 if self.check_etag_header(): 10 self._write_buffer = [] 11 self.set_status(304) 12 if self._status_code in (204, 304): 13 assert not self._write_buffer, "Cannot send body with %s" % self._status_code 14 self._clear_headers_for_304() 15 elif "Content-Length" not in self._headers: 16 content_length = sum(len(part) for part in self._write_buffer) 17 self.set_header("Content-Length", content_length)
分析:.net
在调用 finish() 函数的时候,对HTTP请求进行判断,若是 状态码为200,请求的方法为 GET 或 HEAD,而且 Etag 不在HTTP头信息里面,则说明该请求是第一次发生。接下来,调用 set_etag_header() 函数,将 etag 写入到 header头信息中code
set_etag_header() 函数 ---- 地址:tornado/web.py对象
1 def set_etag_header(self): 2 etag = self.compute_etag() 3 if etag is not None: 4 self.set_header("Etag", etag)
分析:blog
接着调用 compute_etag() 函数生成 etag,若是返回成功,则调用 set_header() 函数将 etag 写入header头信息的 “Etag” 字段。查看 compute_etag() 函数:
compute_etag() 函数 ---- 地址:tornado/web.py
1 def compute_etag(self): 2 hasher = hashlib.sha1() 3 for part in self._write_buffer: 4 hasher.update(part) 5 return '"%s"' % hasher.hexdigest()
分析:
这里经过 调用 hashlib库 生成相应的 etag,而后经过对于 self._write_buffer的循环,当服务端文件有改变的时候,调用hashlib中的 update() 函数更新生成的新的对象 hasher,从而返回最新的 etag
注:self._write_buffer在初始化的时候已经进行了定义 self._write_buffer = [ ], 若是某一个页面有改变,则会进行记录,从而来判断是否客户端请求的页面在服务端是否有改变
这里对于 etag 的生成函数 set_etag_header() 函数已经介绍完了,接着进行 check_etag_header() 校验函数的分析:
check_etag_header() 校验函数 ---- 地址:tornado/web.py
1 def check_etag_header(self): 2 etags = re.findall( 3 br'\*|(?:W/)?"[^"]*"', 4 utf8(self.request.headers.get("If-None-Match", "")) 5 ) 6 if not computed_etag or not etags: 7 return False 8 9 match = False 10 if etags[0] == b'*': 11 match = True 12 else: 13 # Use a weak comparison when comparing entity-tags. 14 def val(x): 15 return x[2:] if x.startswith(b'W/') else x 16 17 for etag in etags: 18 if val(etag) == val(computed_etag): 19 match = True 20 break 21 return match
分析:
首先服务端获取 客户端发送过来的 header头信息 中的 “If-None-Match” 字段,拿到该 etag,并经过正则表达式匹配,查看是否跟服务端保存的etag相同。若是 没有获取到header头信息中的 etag字段或跟服务端etag不匹配,则返回 False,否定返回 True。
以后,若是该 check_etag_header() 函数 返回True 的话, 则说明,该请求中包含有该 etag 而且该etag和服务端保存的相同,接下来t经过 self._write_buffer = [ ] 对这个字段进行清空处理(代表该请求的页面暂时没有任何修改), 而且返回 状态码304 给客户端。