]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Merge remote-tracking branch 'birknilson/refactoring/reduce_statichandler_complexity...
authorBen Darnell <ben@bendarnell.com>
Sun, 19 May 2013 03:59:00 +0000 (23:59 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 19 May 2013 03:59:00 +0000 (23:59 -0400)
Conflicts:
tornado/web.py

1  2 
tornado/web.py

diff --cc tornado/web.py
index 690d2ecc44922ed62ae2061f88a59f304c5bd62a,20c382d5a96e8ce9ead72cd531b476dd40da3432..fde2428d7113fecacc07903b0abbce6860bc9d41
@@@ -1729,6 -1489,43 +1729,68 @@@ class StaticFileHandler(RequestHandler)
  
      def get(self, path, include_body=True):
          path = self.parse_url_path(path)
 -        hasher = hashlib.sha1()
 -        hasher.update(body)
 -        self.set_header("Etag", '"%s"' % hasher.hexdigest())
 -        if include_body:
 -            self.write(body)
 -        else:
 -            assert self.request.method == "HEAD"
 -            self.set_header("Content-Length", len(body))
+         if not self.set_headers(path):
+             return
+         body = self.get_content(path)
+         if not body:
+             return
++        self.set_etag_header()
++        if self.check_etag_header():
++            self.set_status(304)
++            return
++
++        self.set_header("Accept-Ranges", "bytes")
++        request_range = None
++        range_header = self.request.headers.get("Range")
++        if range_header:
++            request_range = httputil.parse_request_range(range_header)
++            if not request_range:
++                # 416: Range Not Satisfiable
++                self.set_status(416)
++                self.set_header("Content-Type", "text/plain")
++                self.write(utf8("The provided Range header is not valid: %r\n"
++                                "Note: multiple ranges are not supported."
++                                 %(range_header, )))
++                return
++
++        with open(self.get_absolute_path(path), "rb") as file:
++            data = file.read()
++            if request_range:
++                # 206: Partial Content
++                self.set_status(206)
++                content_range = httputil.get_content_range(data, request_range)
++                self.set_header("Content-Range", content_range)
++                data = data[request_range]
++            if include_body:
++                self.write(data)
++            else:
++                assert self.request.method == "HEAD"
++                self.set_header("Content-Length", len(data))
++
+     def on_finish(self):
+         # Cleanup the cached computation of the absolute path associated with
+         # the requested resource. This is crucial in order to ensure accurate
+         # responses in upcoming requests which would otherwise utilize the same
+         # absolute path as the initial one - which would be chaotic.
+         self._abspath = None
+     def get_absolute_path(self, path):
+         """Retrieve the absolute path on the filesystem where the resource
+         corresponding to the given URL ``path`` can be found.
+         This method also handles the validation of the given path and ensures
+         resources outside of the static directory cannot be accessed.
+         """
+         # In case the ``_abspath`` attribute exists and contains a value
+         # other than None the abspath has already been computed and verified.
+         # It can be returned instantly in order to avoid recomputation.
+         abspath = getattr(self, '_abspath', None)
+         if abspath is not None:
+             return abspath
          abspath = os.path.abspath(os.path.join(self.root, path))
          # os.path.abspath strips a trailing /
          # it needs to be temporarily added back for requests to root/
          if not os.path.isfile(abspath):
              raise HTTPError(403, "%s is not a file", path)
  
+         self._abspath = abspath
+         return abspath
+     def set_headers(self, path):
+         """Set the response headers in order to ensure that client browsers
+         will cache the requested resource and not proceed to retrieve its content
+         in the case of a 304 response.
+         """
+         abspath = self.get_absolute_path(path)
          stat_result = os.stat(abspath)
 -        modified = datetime.datetime.fromtimestamp(stat_result[stat.ST_MTIME])
 +        modified = datetime.datetime.utcfromtimestamp(stat_result[stat.ST_MTIME])
  
          self.set_header("Last-Modified", modified)
  
          ims_value = self.request.headers.get("If-Modified-Since")
          if ims_value is not None:
              date_tuple = email.utils.parsedate(ims_value)
 -            if_since = datetime.datetime.fromtimestamp(time.mktime(date_tuple))
 +            if_since = datetime.datetime(*date_tuple[:6])
              if if_since >= modified:
                  self.set_status(304)
-                 return
-         self.set_etag_header()
-         if self.check_etag_header():
-             self.set_status(304)
-             return
-         self.set_header("Accept-Ranges", "bytes")
-         request_range = None
-         range_header = self.request.headers.get("Range")
-         if range_header:
-             request_range = httputil.parse_request_range(range_header)
-             if not request_range:
-                 # 416: Range Not Satisfiable
-                 self.set_status(416)
-                 self.set_header("Content-Type", "text/plain")
-                 self.write(utf8("The provided Range header is not valid: %r\n"
-                                 "Note: multiple ranges are not supported."
-                                  %(range_header, )))
-                 return
+                 return False
 -
+         return True
  
+     def get_content(self, path):
+         """Retrieve the content of the requested resource which is located
+         at the given absolute ``path``.
+         """
+         abspath = self.get_absolute_path(path)
          with open(abspath, "rb") as file:
-             data = file.read()
-             if request_range:
-                 # 206: Partial Content
-                 self.set_status(206)
-                 content_range = httputil.get_content_range(data, request_range)
-                 self.set_header("Content-Range", content_range)
-                 data = data[request_range]
-             if include_body:
-                 self.write(data)
-             else:
-                 assert self.request.method == "HEAD"
-                 self.set_header("Content-Length", len(data))
+             return file.read()
+         return None
  
      def set_extra_headers(self, path):
          """For subclass to add extra headers to the response"""