]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
(incomplete) Honnoring Range request header
authorDavid Wolever <david@wolever.net>
Thu, 16 May 2013 23:37:30 +0000 (19:37 -0400)
committerDavid Wolever <david@wolever.net>
Thu, 16 May 2013 23:37:30 +0000 (19:37 -0400)
tornado/httputil.py
tornado/web.py

index 7573610b98aca2bf16d6928b03abf19cca8ac33b..83e4558d8b7dd8a838b1a21827c3625babf40309 100644 (file)
@@ -235,6 +235,63 @@ class HTTPFile(ObjectDict):
     """
     pass
 
+def parse_request_range(range_header):
+    """Parses a Range header.
+
+    Returns either ``None`` or an instance of ``slice``::
+
+        >>> rh = parse_request_range("bytes=1-2")
+        >>> rh
+        slice(1, 3, None)
+        >>> [0, 1, 2, 3, 4][rh]
+        [1, 2]
+        >>> 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).
+    """
+    unit, _, value = range_header.partition("=")
+    unit, value = unit.strip(), value.strip()
+    if unit != "bytes":
+        return None
+    start_b, _, end_b = value.partition("-")
+    try:
+        start = _int_or_none(start_b)
+        end = _int_or_none(end_b)
+    except ValueError:
+        return None
+    if end is not None:
+        end += 1
+    return slice(start, end)
+
+def get_content_range(data, request_range):
+    """ Returns a suitable Content-Range header::
+
+        >>> print(get_content_range("abcd", slice(None, 1)))
+        0-0/4
+        >>> print(get_content_range("abcd", slice(1, 3)))
+        1-2/4
+        >>> print(get_content_range("abcd", slice(None, None)))
+        0-3/4
+    """
+
+    data_len = len(data)
+    return "%s-%s/%s" %(
+        request_range.start or 0,
+        (request_range.stop or data_len) - 1,
+        data_len,
+    )
+
+def _int_or_none(val):
+    val = val.strip()
+    if val == "":
+        return None
+    return int(val)
+
 
 def parse_body_arguments(content_type, body, arguments, files):
     """Parses a form request body.
index 8b4d31738f2e49a7661992c4913c89344050ef52..b41c08c3f80eb79436303cf920544ad7f1b13d81 100644 (file)
@@ -1754,8 +1754,17 @@ class StaticFileHandler(RequestHandler):
                 self.set_status(304)
                 return
 
+        request_range = None
+        range_header = self.request.headers.get("Range")
+        if range_header:
+            request_range = httputil.parse_request_range(range_header)
+
         with open(abspath, "rb") as file:
             data = file.read()
+            if request_range:
+                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: