]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Fix #2521: Serve full file when range start is negative and exceeds the total length...
authorGaren Chan <1412950785@qq.com>
Fri, 2 Nov 2018 19:40:33 +0000 (03:40 +0800)
committerBen Darnell <ben@bendarnell.com>
Fri, 2 Nov 2018 19:40:33 +0000 (15:40 -0400)
* Serve full file while range negative past start.

Fix issue #2521.

* Return 416 while range end less than start.

When range end less than start, the content length will be negative and
HTTPOutputError will be raised.

tornado/test/web_test.py
tornado/web.py

index d65213eaebed4109f6d0a7101b45a96af97b8e4d..be38a99847898c13c62f15ee3001535798aaa9c0 100644 (file)
@@ -1280,6 +1280,17 @@ class StaticFileTest(WebTestCase):
         self.assertEqual(response.headers.get("Content-Length"), "4")
         self.assertEqual(response.headers.get("Content-Range"), "bytes 22-25/26")
 
+    def test_static_with_range_neg_past_start(self):
+        response = self.get_and_head(
+            "/static/robots.txt", headers={"Range": "bytes=-1000000"}
+        )
+        self.assertEqual(response.code, 200)
+        robots_file_path = os.path.join(self.static_dir, "robots.txt")
+        with open(robots_file_path) as f:
+            self.assertEqual(response.body, utf8(f.read()))
+        self.assertEqual(response.headers.get("Content-Length"), "26")
+        self.assertEqual(response.headers.get("Content-Range"), None)
+
     def test_static_invalid_range(self):
         response = self.get_and_head("/static/robots.txt", headers={"Range": "asdf"})
         self.assertEqual(response.code, 200)
@@ -1298,6 +1309,13 @@ class StaticFileTest(WebTestCase):
         self.assertEqual(response.code, 416)
         self.assertEqual(response.headers.get("Content-Range"), "bytes */26")
 
+    def test_static_unsatisfiable_range_end_less_than_start(self):
+        response = self.get_and_head(
+            "/static/robots.txt", headers={"Range": "bytes=10-3"}
+        )
+        self.assertEqual(response.code, 416)
+        self.assertEqual(response.headers.get("Content-Range"), "bytes */26")
+
     def test_static_head(self):
         response = self.fetch("/static/robots.txt", method="HEAD")
         self.assertEqual(response.code, 200)
index 2affd8e3101c3a66a6fca946cc4d7f0c5182ba19..263f429bd481ab1ae93032d973632d565b5f0344 100644 (file)
@@ -2600,16 +2600,24 @@ class StaticFileHandler(RequestHandler):
         size = self.get_content_size()
         if request_range:
             start, end = request_range
-            if (start is not None and start >= size) or end == 0:
+            if start is not None and start < 0:
+                start += size
+                if start < 0:
+                    start = 0
+            if (
+                start is not None
+                and (start >= size or (end is not None and start >= end))
+            ) or end == 0:
                 # As per RFC 2616 14.35.1, a range is not satisfiable only: if
                 # the first requested byte is equal to or greater than the
-                # content, or when a suffix with length 0 is specified
+                # content, or when a suffix with length 0 is specified.
+                # https://tools.ietf.org/html/rfc7233#section-2.1
+                # A byte-range-spec is invalid if the last-byte-pos value is present
+                # and less than the first-byte-pos.
                 self.set_status(416)  # Range Not Satisfiable
                 self.set_header("Content-Type", "text/plain")
                 self.set_header("Content-Range", "bytes */%s" % (size,))
                 return
-            if start is not None and start < 0:
-                start += size
             if end is not None and end > size:
                 # Clients sometimes blindly use a large range to limit their
                 # download size; cap the endpoint at the actual file size.