/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/HttpServer.py

Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
88
88
    _tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
89
89
 
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.
 
92
 
 
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.
 
96
        """
92
97
        tail = 0
93
98
        ranges = []
94
 
        assert ranges_header.startswith('bytes=')
 
99
        if not ranges_header.startswith('bytes='):
 
100
            # Syntactically invalid header
 
101
            return 0, []
 
102
 
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'))
 
110
                if start > end:
 
111
                    # Syntactically invalid range
 
112
                    return 0, []
 
113
                ranges.append((start, end))
101
114
            else:
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'))
 
118
                else:
 
119
                    # Syntactically invalid range
 
120
                    return 0, []
105
121
        return tail, ranges
106
122
 
107
123
    def send_range_content(self, file, start, length):
159
175
            file = open(path, 'rb')
160
176
        except IOError:
161
177
            self.send_error(404, "File not found")
162
 
            return None
 
178
            return
163
179
 
164
180
        file_size = os.fstat(file.fileno())[6]
165
181
        tail, ranges = self.parse_ranges(ranges_header_value)
167
183
        if tail != 0:
168
184
            ranges.append((file_size - tail, file_size))
169
185
 
170
 
        ranges_valid = True
 
186
        self._satisfiable_ranges = True
171
187
        if len(ranges) == 0:
172
 
            ranges_valid = False
 
188
            self._satisfiable_ranges = False
173
189
        else:
174
 
            for (start, end) in ranges:
175
 
                if start >= file_size or end >= file_size:
176
 
                    ranges_valid = False
177
 
                    break
178
 
        if not ranges_valid:
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
182
 
            # present
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 !
 
195
                    return 0, 0
 
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)
 
199
                return start, end
 
200
 
 
201
            ranges = map(check_range, ranges)
 
202
 
 
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.
 
207
            file.close()
 
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")
 
212
            return
185
213
 
186
214
        if len(ranges) == 1:
187
215
            (start, end) = ranges[0]
248
276
    def _http_start(self):
249
277
        httpd = None
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,
 
281
                                                      self.port)
253
282
        self._http_starting.release()
254
283
        httpd.socket.settimeout(0.1)
255
284
 
269
298
        else:
270
299
            remote_path = '/'.join(path_parts)
271
300
 
272
 
        self._http_starting.acquire()
273
 
        self._http_starting.release()
274
301
        return self._http_base_url + remote_path
275
302
 
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()
299
326
        self.logs = []
300
327
 
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:
306
 
            import os
307
 
            os.environ["http_proxy"] = self._http_proxy
308
332
 
309
333
    def get_url(self):
310
334
        """See bzrlib.transport.Server.get_url."""