對於具體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 給客戶端。