~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/http_server.py

  • Committer: Andrew Bennetts
  • Date: 2011-02-14 12:03:05 UTC
  • mto: This revision was merged to the branch mainline in revision 5664.
  • Revision ID: andrew.bennetts@canonical.com-20110214120305-7l7iu1h6f13voeo7
Add release note.

Show diffs side-by-side

added added

removed removed

Lines of Context:
128
128
    def _handle_one_request(self):
129
129
        SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
130
130
 
131
 
    _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)?$')
 
131
    _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
132
132
    _tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
133
133
 
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.
 
134
    def parse_ranges(self, ranges_header):
 
135
        """Parse the range header value and returns ranges and tail.
 
136
 
 
137
        RFC2616 14.35 says that syntactically invalid range
 
138
        specifiers MUST be ignored. In that case, we return 0 for
 
139
        tail and [] for ranges.
146
140
        """
 
141
        tail = 0
 
142
        ranges = []
147
143
        if not ranges_header.startswith('bytes='):
148
144
            # Syntactically invalid header
149
 
            return None
 
145
            return 0, []
150
146
 
151
 
        tail = None
152
 
        ranges = []
153
147
        ranges_header = ranges_header[len('bytes='):]
154
148
        for range_str in ranges_header.split(','):
 
149
            # FIXME: RFC2616 says end is optional and default to file_size
155
150
            range_match = self._range_regexp.match(range_str)
156
151
            if range_match is not None:
157
152
                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)
 
153
                end = int(range_match.group('end'))
164
154
                if start > end:
165
155
                    # Syntactically invalid range
166
 
                    return None
 
156
                    return 0, []
167
157
                ranges.append((start, end))
168
158
            else:
169
159
                tail_match = self._tail_regexp.match(range_str)
171
161
                    tail = int(tail_match.group('tail'))
172
162
                else:
173
163
                    # 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
 
164
                    return 0, []
 
165
        return tail, ranges
189
166
 
190
167
    def _header_line_length(self, keyword, value):
191
168
        header_line = '%s: %s\r\n' % (keyword, value)
281
258
            return
282
259
 
283
260
        file_size = os.fstat(f.fileno())[6]
284
 
        ranges = self._parse_ranges(ranges_header_value, file_size)
285
 
        if not ranges:
 
261
        tail, ranges = self.parse_ranges(ranges_header_value)
 
262
        # Normalize tail into ranges
 
263
        if tail != 0:
 
264
            ranges.append((file_size - tail, file_size))
 
265
 
 
266
        self._satisfiable_ranges = True
 
267
        if len(ranges) == 0:
 
268
            self._satisfiable_ranges = False
 
269
        else:
 
270
            def check_range(range_specifier):
 
271
                start, end = range_specifier
 
272
                # RFC2616 14.35, ranges are invalid if start >= file_size
 
273
                if start >= file_size:
 
274
                    self._satisfiable_ranges = False # Side-effect !
 
275
                    return 0, 0
 
276
                # RFC2616 14.35, end values should be truncated
 
277
                # to file_size -1 if they exceed it
 
278
                end = min(end, file_size - 1)
 
279
                return start, end
 
280
 
 
281
            ranges = map(check_range, ranges)
 
282
 
 
283
        if not self._satisfiable_ranges:
286
284
            # RFC2616 14.16 and 14.35 says that when a server
287
285
            # encounters unsatisfiable range specifiers, it
288
286
            # SHOULD return a 416.