1
# Copyright (C) 2006-2011 Canonical Ltd
1
# Copyright (C) 2006-2010 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
128
139
def _handle_one_request(self):
129
140
SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
131
_range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)?$')
142
_range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
132
143
_tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
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.
145
def parse_ranges(self, ranges_header):
146
"""Parse the range header value and returns ranges and tail.
148
RFC2616 14.35 says that syntactically invalid range
149
specifiers MUST be ignored. In that case, we return 0 for
150
tail and [] for ranges.
147
154
if not ranges_header.startswith('bytes='):
148
155
# Syntactically invalid header
153
158
ranges_header = ranges_header[len('bytes='):]
154
159
for range_str in ranges_header.split(','):
160
# FIXME: RFC2616 says end is optional and default to file_size
155
161
range_match = self._range_regexp.match(range_str)
156
162
if range_match is not None:
157
163
start = int(range_match.group('start'))
158
end_match = range_match.group('end')
159
if end_match is None:
160
# RFC2616 says end is optional and default to file_size
164
end = int(range_match.group('end'))
165
166
# Syntactically invalid range
167
168
ranges.append((start, end))
169
170
tail_match = self._tail_regexp.match(range_str)
171
172
tail = int(tail_match.group('tail'))
173
174
# 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
190
178
def _header_line_length(self, keyword, value):
191
179
header_line = '%s: %s\r\n' % (keyword, value)
283
271
file_size = os.fstat(f.fileno())[6]
284
ranges = self._parse_ranges(ranges_header_value, file_size)
272
tail, ranges = self.parse_ranges(ranges_header_value)
273
# Normalize tail into ranges
275
ranges.append((file_size - tail, file_size))
277
self._satisfiable_ranges = True
279
self._satisfiable_ranges = False
281
def check_range(range_specifier):
282
start, end = range_specifier
283
# RFC2616 14.35, ranges are invalid if start >= file_size
284
if start >= file_size:
285
self._satisfiable_ranges = False # Side-effect !
287
# RFC2616 14.35, end values should be truncated
288
# to file_size -1 if they exceed it
289
end = min(end, file_size - 1)
292
ranges = map(check_range, ranges)
294
if not self._satisfiable_ranges:
286
295
# RFC2616 14.16 and 14.35 says that when a server
287
296
# encounters unsatisfiable range specifiers, it
288
297
# SHOULD return a 416.