From: Garen Chan <1412950785@qq.com> Date: Fri, 2 Nov 2018 19:40:33 +0000 (+0800) Subject: Fix #2521: Serve full file when range start is negative and exceeds the total length... X-Git-Tag: v6.0.0b1~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=db529031a1e1a6e951826aba0b7d0b18f05cd4c7;p=thirdparty%2Ftornado.git Fix #2521: Serve full file when range start is negative and exceeds the total length (#2522) * 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. --- diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index d65213eae..be38a9984 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -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) diff --git a/tornado/web.py b/tornado/web.py index 2affd8e31..263f429bd 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -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.