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):
288
315
self._http_thread = threading.Thread(target=self._http_start)
289
316
self._http_thread.setDaemon(True)
290
317
self._http_thread.start()
291
self._http_proxy = os.environ.get("http_proxy")
292
if self._http_proxy is not None:
293
del os.environ["http_proxy"]
318
# Wait for the server thread to start (i.e release the lock)
319
self._http_starting.acquire()
320
self._http_starting.release()
296
323
def tearDown(self):
297
324
"""See bzrlib.transport.Server.tearDown."""
298
325
self._http_running = False
299
326
self._http_thread.join()
300
if self._http_proxy is not None:
302
os.environ["http_proxy"] = self._http_proxy
304
328
def get_url(self):
305
329
"""See bzrlib.transport.Server.get_url."""