OLD | NEW |
(Empty) | |
| 1 from utils import HTTPException |
| 2 |
| 3 |
| 4 class RangeParser(object): |
| 5 def __call__(self, header, file_size): |
| 6 prefix = "bytes=" |
| 7 if not header.startswith(prefix): |
| 8 raise HTTPException(416, message="Unrecognised range type %s" % (hea
der,)) |
| 9 |
| 10 parts = header[len(prefix):].split(",") |
| 11 ranges = [] |
| 12 for item in parts: |
| 13 components = item.split("-") |
| 14 if len(components) != 2: |
| 15 raise HTTPException(416, "Bad range specifier %s" % (item)) |
| 16 data = [] |
| 17 for component in components: |
| 18 if component == "": |
| 19 data.append(None) |
| 20 else: |
| 21 try: |
| 22 data.append(int(component)) |
| 23 except ValueError: |
| 24 raise HTTPException(416, "Bad range specifier %s" % (ite
m)) |
| 25 try: |
| 26 ranges.append(Range(data[0], data[1], file_size)) |
| 27 except ValueError: |
| 28 raise HTTPException(416, "Bad range specifier %s" % (item)) |
| 29 |
| 30 return self.coalesce_ranges(ranges, file_size) |
| 31 |
| 32 def coalesce_ranges(self, ranges, file_size): |
| 33 rv = [] |
| 34 target = None |
| 35 for current in reversed(sorted(ranges)): |
| 36 if target is None: |
| 37 target = current |
| 38 else: |
| 39 new = target.coalesce(current) |
| 40 target = new[0] |
| 41 if len(new) > 1: |
| 42 rv.append(new[1]) |
| 43 rv.append(target) |
| 44 |
| 45 return rv[::-1] |
| 46 |
| 47 |
| 48 class Range(object): |
| 49 def __init__(self, lower, upper, file_size): |
| 50 self.file_size = file_size |
| 51 self.lower, self.upper = self._abs(lower, upper) |
| 52 if self.lower >= self.upper or self.lower >= self.file_size: |
| 53 raise ValueError |
| 54 |
| 55 def __repr__(self): |
| 56 return "<Range %s-%s>" % (self.lower, self.upper) |
| 57 |
| 58 def __lt__(self, other): |
| 59 return self.lower < other.lower |
| 60 |
| 61 def __gt__(self, other): |
| 62 return self.lower > other.lower |
| 63 |
| 64 def __eq__(self, other): |
| 65 return self.lower == other.lower and self.upper == other.upper |
| 66 |
| 67 def _abs(self, lower, upper): |
| 68 if lower is None and upper is None: |
| 69 lower, upper = 0, self.file_size |
| 70 elif lower is None: |
| 71 lower, upper = max(0, self.file_size - upper), self.file_size |
| 72 elif upper is None: |
| 73 lower, upper = lower, self.file_size |
| 74 else: |
| 75 lower, upper = lower, min(self.file_size, upper + 1) |
| 76 |
| 77 return lower, upper |
| 78 |
| 79 def coalesce(self, other): |
| 80 assert self.file_size == other.file_size |
| 81 |
| 82 if (self.upper < other.lower or self.lower > other.upper): |
| 83 return sorted([self, other]) |
| 84 else: |
| 85 return [Range(min(self.lower, other.lower), |
| 86 max(self.upper, other.upper) - 1, |
| 87 self.file_size)] |
| 88 |
| 89 def header_value(self): |
| 90 return "bytes %i-%i/%i" % (self.lower, self.upper - 1, self.file_size) |
OLD | NEW |