Tornado Auto Etag 机制
为了研究缓存看了 tornado web.py
里的 finish
函数
代码如下
1def finish(self, chunk: Union[str, bytes, dict] = None) -> "Future[None]":
2 """Finishes this response, ending the HTTP request.
3
4 Passing a ``chunk`` to ``finish()`` is equivalent to passing that
5 chunk to ``write()`` and then calling ``finish()`` with no arguments.
6
7 Returns a `.Future` which may optionally be awaited to track the sending
8 of the response to the client. This `.Future` resolves when all the response
9 data has been sent, and raises an error if the connection is closed before all
10 data can be sent.
11
12 .. versionchanged:: 5.1
13
14 Now returns a `.Future` instead of ``None``.
15 """
16 if self._finished:
17 raise RuntimeError("finish() called twice")
18
19 if chunk is not None:
20 self.write(chunk)
21
22 # Automatically support ETags and add the Content-Length header if
23 # we have not flushed any content yet.
24 if not self._headers_written:
25 if (
26 self._status_code == 200
27 and self.request.method in ("GET", "HEAD")
28 and "Etag" not in self._headers
29 ):
30 self.set_etag_header()
31 if self.check_etag_header():
32 self._write_buffer = []
33 self.set_status(304)
34 if self._status_code in (204, 304) or (
35 self._status_code >= 100 and self._status_code < 200
36 ):
37 assert not self._write_buffer, (
38 "Cannot send body with %s" % self._status_code
39 )
40 self._clear_headers_for_304()
41 elif "Content-Length" not in self._headers:
42 content_length = sum(len(part) for part in self._write_buffer)
43 self.set_header("Content-Length", content_length)
44
45 assert self.request.connection is not None
46 # Now that the request is finished, clear the callback we
47 # set on the HTTPConnection (which would otherwise prevent the
48 # garbage collection of the RequestHandler when there
49 # are keepalive connections)
50 self.request.connection.set_close_callback(None) # type: ignore
51
52 future = self.flush(include_footers=True)
53 self.request.connection.finish()
54 self._log()
55 self._finished = True
56 self.on_finish()
57 self._break_cycles()
58 return future
1def finish(self, chunk: Union[str, bytes, dict] = None) -> "Future[None]":
2 """Finishes this response, ending the HTTP request.
3
4 Passing a ``chunk`` to ``finish()`` is equivalent to passing that
5 chunk to ``write()`` and then calling ``finish()`` with no arguments.
6
7 Returns a `.Future` which may optionally be awaited to track the sending
8 of the response to the client. This `.Future` resolves when all the response
9 data has been sent, and raises an error if the connection is closed before all
10 data can be sent.
11
12 .. versionchanged:: 5.1
13
14 Now returns a `.Future` instead of ``None``.
15 """
16 if self._finished:
17 raise RuntimeError("finish() called twice")
18
19 if chunk is not None:
20 self.write(chunk)
21
22 # Automatically support ETags and add the Content-Length header if
23 # we have not flushed any content yet.
24 if not self._headers_written:
25 if (
26 self._status_code == 200
27 and self.request.method in ("GET", "HEAD")
28 and "Etag" not in self._headers
29 ):
30 self.set_etag_header()
31 if self.check_etag_header():
32 self._write_buffer = []
33 self.set_status(304)
34 if self._status_code in (204, 304) or (
35 self._status_code >= 100 and self._status_code < 200
36 ):
37 assert not self._write_buffer, (
38 "Cannot send body with %s" % self._status_code
39 )
40 self._clear_headers_for_304()
41 elif "Content-Length" not in self._headers:
42 content_length = sum(len(part) for part in self._write_buffer)
43 self.set_header("Content-Length", content_length)
44
45 assert self.request.connection is not None
46 # Now that the request is finished, clear the callback we
47 # set on the HTTPConnection (which would otherwise prevent the
48 # garbage collection of the RequestHandler when there
49 # are keepalive connections)
50 self.request.connection.set_close_callback(None) # type: ignore
51
52 future = self.flush(include_footers=True)
53 self.request.connection.finish()
54 self._log()
55 self._finished = True
56 self.on_finish()
57 self._break_cycles()
58 return future
从代码中可以看出, 满足下面条件的请求:
self._headers_written
为不为True
- http status 为
200
的GET
、HEAD
请求 - 同时没有
Etag
在response header
的情况下
tornado 会自动计算返回结果的 sha1, 并设置 Etag 若客户端支持 Etag 机制, 正确返回 If-None-Match
, 就能节约一波流量, 美滋滋.