Index: Tools/Scripts/webkitpy/thirdparty/wpt/wpt/tools/wptserve/wptserve/ranges.py |
diff --git a/Tools/Scripts/webkitpy/thirdparty/wpt/wpt/tools/wptserve/wptserve/ranges.py b/Tools/Scripts/webkitpy/thirdparty/wpt/wpt/tools/wptserve/wptserve/ranges.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..12935fd91b1d009ed42644fec0793e9d912e7b18 |
--- /dev/null |
+++ b/Tools/Scripts/webkitpy/thirdparty/wpt/wpt/tools/wptserve/wptserve/ranges.py |
@@ -0,0 +1,90 @@ |
+from utils import HTTPException |
+ |
+ |
+class RangeParser(object): |
+ def __call__(self, header, file_size): |
+ prefix = "bytes=" |
+ if not header.startswith(prefix): |
+ raise HTTPException(416, message="Unrecognised range type %s" % (header,)) |
+ |
+ parts = header[len(prefix):].split(",") |
+ ranges = [] |
+ for item in parts: |
+ components = item.split("-") |
+ if len(components) != 2: |
+ raise HTTPException(416, "Bad range specifier %s" % (item)) |
+ data = [] |
+ for component in components: |
+ if component == "": |
+ data.append(None) |
+ else: |
+ try: |
+ data.append(int(component)) |
+ except ValueError: |
+ raise HTTPException(416, "Bad range specifier %s" % (item)) |
+ try: |
+ ranges.append(Range(data[0], data[1], file_size)) |
+ except ValueError: |
+ raise HTTPException(416, "Bad range specifier %s" % (item)) |
+ |
+ return self.coalesce_ranges(ranges, file_size) |
+ |
+ def coalesce_ranges(self, ranges, file_size): |
+ rv = [] |
+ target = None |
+ for current in reversed(sorted(ranges)): |
+ if target is None: |
+ target = current |
+ else: |
+ new = target.coalesce(current) |
+ target = new[0] |
+ if len(new) > 1: |
+ rv.append(new[1]) |
+ rv.append(target) |
+ |
+ return rv[::-1] |
+ |
+ |
+class Range(object): |
+ def __init__(self, lower, upper, file_size): |
+ self.file_size = file_size |
+ self.lower, self.upper = self._abs(lower, upper) |
+ if self.lower >= self.upper or self.lower >= self.file_size: |
+ raise ValueError |
+ |
+ def __repr__(self): |
+ return "<Range %s-%s>" % (self.lower, self.upper) |
+ |
+ def __lt__(self, other): |
+ return self.lower < other.lower |
+ |
+ def __gt__(self, other): |
+ return self.lower > other.lower |
+ |
+ def __eq__(self, other): |
+ return self.lower == other.lower and self.upper == other.upper |
+ |
+ def _abs(self, lower, upper): |
+ if lower is None and upper is None: |
+ lower, upper = 0, self.file_size |
+ elif lower is None: |
+ lower, upper = max(0, self.file_size - upper), self.file_size |
+ elif upper is None: |
+ lower, upper = lower, self.file_size |
+ else: |
+ lower, upper = lower, min(self.file_size, upper + 1) |
+ |
+ return lower, upper |
+ |
+ def coalesce(self, other): |
+ assert self.file_size == other.file_size |
+ |
+ if (self.upper < other.lower or self.lower > other.upper): |
+ return sorted([self, other]) |
+ else: |
+ return [Range(min(self.lower, other.lower), |
+ max(self.upper, other.upper) - 1, |
+ self.file_size)] |
+ |
+ def header_value(self): |
+ return "bytes %i-%i/%i" % (self.lower, self.upper - 1, self.file_size) |