88
88
_tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
90
90
def parse_ranges(self, ranges_header):
91
"""Parse the range header value and returns ranges and tail"""
91
"""Parse the range header value and returns ranges and tail.
93
RFC2616 14.35 says that syntactically invalid range
94
specifiers MUST be ignored. In that case, we return 0 for
95
tail and [] for ranges.
94
assert ranges_header.startswith('bytes=')
99
if not ranges_header.startswith('bytes='):
100
# Syntactically invalid header
95
103
ranges_header = ranges_header[len('bytes='):]
96
104
for range_str in ranges_header.split(','):
105
# FIXME: RFC2616 says end is optional and default to file_size
97
106
range_match = self._range_regexp.match(range_str)
98
107
if range_match is not None:
99
ranges.append((int(range_match.group('start')),
100
int(range_match.group('end'))))
108
start = int(range_match.group('start'))
109
end = int(range_match.group('end'))
111
# Syntactically invalid range
113
ranges.append((start, end))
102
115
tail_match = self._tail_regexp.match(range_str)
103
116
if tail_match is not None:
104
117
tail = int(tail_match.group('tail'))
119
# Syntactically invalid range
105
121
return tail, ranges
107
123
def send_range_content(self, file, start, length):
168
184
ranges.append((file_size - tail, file_size))
186
self._satisfiable_ranges = True
171
187
if len(ranges) == 0:
188
self._satisfiable_ranges = False
174
for (start, end) in ranges:
175
if start >= file_size or end >= file_size:
179
# RFC2616 14-16 says that invalid Range headers
180
# should be ignored and in that case, the whole file
181
# should be returned as if no Range header was
183
file.close() # Will be reopened by the following call
184
return SimpleHTTPRequestHandler.do_GET(self)
190
def check_range(range_specifier):
191
start, end = range_specifier
192
# RFC2616 14.35, ranges are invalid if start >= file_size
193
if start >= file_size:
194
self._satisfiable_ranges = False # Side-effect !
196
# RFC2616 14.35, end values should be truncated
197
# to file_size -1 if they exceed it
198
end = min(end, file_size - 1)
201
ranges = map(check_range, ranges)
203
if not self._satisfiable_ranges:
204
# RFC2616 14.16 and 14.35 says that when a server
205
# encounters unsatisfiable range specifiers, it
206
# SHOULD return a 416.
208
# FIXME: We SHOULD send a Content-Range header too,
209
# but the implementation of send_error does not
210
# allows that. So far.
211
self.send_error(416, "Requested range not satisfiable")
186
214
if len(ranges) == 1:
187
215
(start, end) = ranges[0]
248
276
def _http_start(self):
250
278
httpd = self._get_httpd()
251
host, port = httpd.socket.getsockname()
252
self._http_base_url = '%s://localhost:%s/' % (self._url_protocol, port)
279
host, self.port = httpd.socket.getsockname()
280
self._http_base_url = '%s://localhost:%s/' % (self._url_protocol,
253
282
self._http_starting.release()
254
283
httpd.socket.settimeout(0.1)
270
299
remote_path = '/'.join(path_parts)
272
self._http_starting.acquire()
273
self._http_starting.release()
274
301
return self._http_base_url + remote_path
276
303
def log(self, format, *args):
293
320
self._http_thread = threading.Thread(target=self._http_start)
294
321
self._http_thread.setDaemon(True)
295
322
self._http_thread.start()
296
self._http_proxy = os.environ.get("http_proxy")
297
if self._http_proxy is not None:
298
del os.environ["http_proxy"]
323
# Wait for the server thread to start (i.e release the lock)
324
self._http_starting.acquire()
325
self._http_starting.release()
301
328
def tearDown(self):
302
329
"""See bzrlib.transport.Server.tearDown."""
303
330
self._http_running = False
304
331
self._http_thread.join()
305
if self._http_proxy is not None:
307
os.environ["http_proxy"] = self._http_proxy
309
333
def get_url(self):
310
334
"""See bzrlib.transport.Server.get_url."""