~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/http_server.py

(gz) Change minimum required testtools version for selftest to 0.9.5 for
 unicode fixes (Martin [gz])

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
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
20
20
import posixpath
21
21
import random
22
22
import re
 
23
import select
23
24
import SimpleHTTPServer
24
25
import socket
 
26
import SocketServer
 
27
import sys
 
28
import threading
 
29
import time
25
30
import urllib
26
31
import urlparse
27
32
 
 
33
from bzrlib import (
 
34
    osutils,
 
35
    tests,
 
36
    transport,
 
37
    )
28
38
from bzrlib.tests import test_server
 
39
from bzrlib.transport import local
29
40
 
30
41
 
31
42
class BadWebserverPath(ValueError):
128
139
    def _handle_one_request(self):
129
140
        SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
130
141
 
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+)$')
133
144
 
134
 
    def _parse_ranges(self, ranges_header, file_size):
135
 
        """Parse the range header value and returns ranges.
136
 
 
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.
139
 
 
140
 
        :param ranges_header: The 'Range' header value.
141
 
 
142
 
        :param file_size: The size of the requested file.
143
 
 
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.
 
147
 
 
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.
146
151
        """
 
152
        tail = 0
 
153
        ranges = []
147
154
        if not ranges_header.startswith('bytes='):
148
155
            # Syntactically invalid header
149
 
            return None
 
156
            return 0, []
150
157
 
151
 
        tail = None
152
 
        ranges = []
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
161
 
                    end = file_size
162
 
                else:
163
 
                    end = int(end_match)
 
164
                end = int(range_match.group('end'))
164
165
                if start > end:
165
166
                    # Syntactically invalid range
166
 
                    return None
 
167
                    return 0, []
167
168
                ranges.append((start, end))
168
169
            else:
169
170
                tail_match = self._tail_regexp.match(range_str)
171
172
                    tail = int(tail_match.group('tail'))
172
173
                else:
173
174
                    # Syntactically invalid range
174
 
                    return None
175
 
        if tail is not None:
176
 
            # Normalize tail into ranges
177
 
            ranges.append((max(0, file_size - tail), file_size))
178
 
 
179
 
        checked_ranges = []
180
 
        for start, end in ranges:
181
 
            if start >= file_size:
182
 
                # RFC2616 14.35, ranges are invalid if start >= file_size
183
 
                return None
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
 
175
                    return 0, []
 
176
        return tail, ranges
189
177
 
190
178
    def _header_line_length(self, keyword, value):
191
179
        header_line = '%s: %s\r\n' % (keyword, value)
281
269
            return
282
270
 
283
271
        file_size = os.fstat(f.fileno())[6]
284
 
        ranges = self._parse_ranges(ranges_header_value, file_size)
285
 
        if not ranges:
 
272
        tail, ranges = self.parse_ranges(ranges_header_value)
 
273
        # Normalize tail into ranges
 
274
        if tail != 0:
 
275
            ranges.append((file_size - tail, file_size))
 
276
 
 
277
        self._satisfiable_ranges = True
 
278
        if len(ranges) == 0:
 
279
            self._satisfiable_ranges = False
 
280
        else:
 
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 !
 
286
                    return 0, 0
 
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)
 
290
                return start, end
 
291
 
 
292
            ranges = map(check_range, ranges)
 
293
 
 
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.