128
128
def _handle_one_request(self):
129
129
SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
131
_range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
131
_range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)?$')
132
132
_tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
134
def parse_ranges(self, ranges_header):
135
"""Parse the range header value and returns ranges and tail.
137
RFC2616 14.35 says that syntactically invalid range
138
specifiers MUST be ignored. In that case, we return 0 for
139
tail and [] for ranges.
134
def _parse_ranges(self, ranges_header, file_size):
135
"""Parse the range header value and returns ranges.
137
RFC2616 14.35 says that syntactically invalid range specifiers MUST be
138
ignored. In that case, we return None instead of a range list.
140
:param ranges_header: The 'Range' header value.
142
:param file_size: The size of the requested file.
144
:return: A list of (start, end) tuples or None if some invalid range
145
specifier is encountered.
143
147
if not ranges_header.startswith('bytes='):
144
148
# Syntactically invalid header
147
153
ranges_header = ranges_header[len('bytes='):]
148
154
for range_str in ranges_header.split(','):
149
# FIXME: RFC2616 says end is optional and default to file_size
150
155
range_match = self._range_regexp.match(range_str)
151
156
if range_match is not None:
152
157
start = int(range_match.group('start'))
153
end = int(range_match.group('end'))
158
end_match = range_match.group('end')
159
if end_match is None:
160
# RFC2616 says end is optional and default to file_size
155
165
# Syntactically invalid range
157
167
ranges.append((start, end))
159
169
tail_match = self._tail_regexp.match(range_str)
161
171
tail = int(tail_match.group('tail'))
163
173
# Syntactically invalid range
176
# Normalize tail into ranges
177
ranges.append((max(0, file_size - tail), file_size))
180
for start, end in ranges:
181
if start >= file_size:
182
# RFC2616 14.35, ranges are invalid if start >= file_size
184
# RFC2616 14.35, end values should be truncated
185
# to file_size -1 if they exceed it
186
end = min(end, file_size - 1)
187
checked_ranges.append((start, end))
188
return checked_ranges
167
190
def _header_line_length(self, keyword, value):
168
191
header_line = '%s: %s\r\n' % (keyword, value)
260
283
file_size = os.fstat(f.fileno())[6]
261
tail, ranges = self.parse_ranges(ranges_header_value)
262
# Normalize tail into ranges
264
ranges.append((file_size - tail, file_size))
266
self._satisfiable_ranges = True
268
self._satisfiable_ranges = False
270
def check_range(range_specifier):
271
start, end = range_specifier
272
# RFC2616 14.35, ranges are invalid if start >= file_size
273
if start >= file_size:
274
self._satisfiable_ranges = False # Side-effect !
276
# RFC2616 14.35, end values should be truncated
277
# to file_size -1 if they exceed it
278
end = min(end, file_size - 1)
281
ranges = map(check_range, ranges)
283
if not self._satisfiable_ranges:
284
ranges = self._parse_ranges(ranges_header_value, file_size)
284
286
# RFC2616 14.16 and 14.35 says that when a server
285
287
# encounters unsatisfiable range specifiers, it
286
288
# SHOULD return a 416.