From: David Wolever Date: Fri, 17 May 2013 04:34:36 +0000 (-0400) Subject: Handle negative byte ranges X-Git-Tag: v3.1.0~68^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=802bb9c6614d3a0b2645c8972d56a4565a41ee7e;p=thirdparty%2Ftornado.git Handle negative byte ranges --- diff --git a/tornado/httputil.py b/tornado/httputil.py index 83e4558d8..67814a233 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -235,7 +235,7 @@ class HTTPFile(ObjectDict): """ pass -def parse_request_range(range_header): +def parse_request_range(range_header, ): """Parses a Range header. Returns either ``None`` or an instance of ``slice``:: @@ -247,12 +247,18 @@ def parse_request_range(range_header): [1, 2] >>> parse_request_range("bytes=6-") slice(6, None, None) + >>> parse_request_range("bytes=-6") + slice(-6, None, None) >>> parse_request_range("bytes=") slice(None, None, None) >>> parse_request_range("foo=42") >>> parse_request_range("bytes=1-2,6-10") Note: only supports one range (ex, ``bytes=1-2,6-10`` is not allowed). + + See [0] for the details of the range header. + + [0]: http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p5-range-latest.html#byte.ranges """ unit, _, value = range_header.partition("=") unit, value = unit.strip(), value.strip() @@ -265,7 +271,11 @@ def parse_request_range(range_header): except ValueError: return None if end is not None: - end += 1 + if start is None: + start = -end + end = None + else: + end += 1 return slice(start, end) def get_content_range(data, request_range): @@ -280,11 +290,12 @@ def get_content_range(data, request_range): """ data_len = len(data) - return "%s-%s/%s" %( - request_range.start or 0, - (request_range.stop or data_len) - 1, - data_len, - ) + start, stop = request_range.start, request_range.stop + start = start or 0 + if start < 0: + start = data_len + start + stop = (stop or data_len) - 1 + return "%s-%s/%s" %(start, stop, data_len) def _int_or_none(val): val = val.strip() diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index c305a8513..09f04ae6e 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -878,6 +878,22 @@ class StaticFileTest(WebTestCase): self.assertEqual(response.headers.get("Content-Range"), "0-9/26") + def test_static_with_range_end_edge(self): + response = self.fetch('/static/robots.txt', headers={ + 'Range': 'bytes=22-'}) + self.assertEqual(response.body, b": /\n") + self.assertEqual(response.headers.get("Content-Length"), "4") + self.assertEqual(response.headers.get("Content-Range"), + "22-25/26") + + def test_static_with_range_neg_end(self): + response = self.fetch('/static/robots.txt', headers={ + 'Range': 'bytes=-4'}) + self.assertEqual(response.body, b": /\n") + self.assertEqual(response.headers.get("Content-Length"), "4") + self.assertEqual(response.headers.get("Content-Range"), + "22-25/26") + def test_static_invalid_range(self): response = self.fetch('/static/robots.txt', headers={ 'Range': 'asdf'})