Tornado returns 304 status code continuously when processing get request

Time:2019-12-15

Tornado source code analysis — Etag implementation

Etag (entity tag of URL):

What is the specific Etag, request process and implementation principle are not introduced here. Please refer to the following link:
http://www.oschina.net/questi…
https://zh.wikipedia.org/wiki…

Analysis of tornado implementation:

First, from the call order of tornado processing a request (from the document: http://www.tornadoweb.cn/documentation):
The program creates a requesthandler object for each request
The program calls the initialize() function, whose parameters are the key parameter definitions in the application configuration. (the initialize method is a new one added in tornado 1.1. In the old version, you need to rewrite "init" to achieve the same purpose.) the initialize method generally just saves the passed parameters into the member variables, without generating some output or calling methods such as send error.
The program calls prepare(). No matter which HTTP method is used, prepare is called to, so this method is usually defined in a base class and reused in a subclass. Prepare can produce output information. If it calls the finish (or send error) function, the whole process is finished.
The program calls an HTTP method, such as get(), post(), put(), and so on. If there is a grouping match in the regular expression pattern of the URL, the correlation match is passed in as a parameter to the method.
At the end of a request, Etag must be processed, so find the finish() function called:

Finish() function — Address: Tornado / Web. PY

  def finish(self, chunk=None):
      # Automatically support ETags and add the Content-Length header if
      # we have not flushed any content yet.
      if not self._headers_written:
          if (self._status_code == 200 and
              self.request.method in ("GET", "HEAD") and
                  "Etag" not in self._headers):
              self.set_etag_header()
              if self.check_etag_header():
                 self._write_buffer = []
                 self.set_status(304)
          if self._status_code in (204, 304):
             assert not self._write_buffer, "Cannot send body with %s" % self._status_code
             self._clear_headers_for_304()
          elif "Content-Length" not in self._headers:
             content_length = sum(len(part) for part in self._write_buffer)
             self.set_header("Content-Length", content_length)

Analysis:
When the finish() function is called, the HTTP request is judged. If the status code is 200, the request method is get or head, and Etag is not in the HTTP header information, then the request is the first time. Next, call the set ﹐ Etag ﹐ header() function to write the Etag to the header information

Set ﹐ Etag ﹐ header() function —- address: Tornado / web.py

def set_etag_header(self):
    etag = self.compute_etag()
    if etag is not None:
        self.set_header("Etag", etag)

Analysis:
Then, call the compute_etag() function to generate the Etag. If the return is successful, call the set_header() function to write the Etag into the “Etag” field of the header header information. To view the compute ABCD etag() function:

Compute_etag() function —- address: Tornado / web.py

def compute_etag(self):
    hasher = hashlib.sha1()
    for part in self._write_buffer:
        hasher.update(part)
    return '"%s"' % hasher.hexdigest()

Analysis:
Here, the corresponding Etag is generated by calling the hashlib library, and then through the self write buffer loop, when the server file changes, the update() function in hashlib is called to update the generated new object hasher, so as to return the latest Etag
Note: self write buffer has been defined during initialization. If a page has changed, it will be recorded to determine whether the page requested by the client has changed in the server. Here, the set tag header() function of the Etag generation function has been introduced, and then check tag header() verification is performed Function analysis:

Check ﹣ Etag ﹣ header() check function —- address: Tornado / web.py

 def check_etag_header(self):
     etags = re.findall(
         br'\*|(?:W/)?"[^"]*"',
         utf8(self.request.headers.get("If-None-Match", ""))
     )
     if not computed_etag or not etags:
         return False
 
     match = False
     if etags[0] == b'*':
         match = True
     else:
         # Use a weak comparison when comparing entity-tags.
         def val(x):
             return x[2:] if x.startswith(b'W/') else x
 
         for etag in etags:
             if val(etag) == val(computed_etag):
                 match = True
                 break
     return match

Analysis:
First, the server obtains the “if none match” field in the header header information sent by the client, obtains the Etag, and matches it with the regular expression to see whether it is the same as the Etag saved by the server. If the Etag field in the header header information is not obtained or does not match the Etag of the server, false is returned and true is denied.
After that, if the check ﹣ Etag ﹣ header() function returns true, it means that the request contains the Etag and the Etag is the same as the one saved by the server. Next, t clears the field through self. ﹣ write ﹣ buffer = [] (indicating that the requested page is not modified temporarily), and returns the status code 304 to the client.

Solve problem 304

Delete ‘if none match’ in request header

del self.request.headers['If-None-Match']

Reference material