~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/HttpServer.py

  • Committer: Alexander Belchenko
  • Date: 2006-12-19 08:26:36 UTC
  • mfrom: (2198 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2204.
  • Revision ID: bialix@ukr.net-20061219082636-xbb55np3wnamva8t
merge 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):
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()
294
321
        self.logs = []
295
322
 
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:
301
 
            import os
302
 
            os.environ["http_proxy"] = self._http_proxy
303
327
 
304
328
    def get_url(self):
305
329
        """See bzrlib.transport.Server.get_url."""