~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/http_server.py

  • Committer: Vincent Ladeuil
  • Date: 2012-03-13 17:25:29 UTC
  • mfrom: (6499 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6501.
  • Revision ID: v.ladeuil+lp@free.fr-20120313172529-i0suyjnepsor25i7
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
24
23
import SimpleHTTPServer
25
24
import socket
26
 
import SocketServer
27
 
import sys
28
 
import threading
29
 
import time
30
 
import urllib
31
25
import urlparse
32
26
 
33
 
from bzrlib import (
34
 
    osutils,
35
 
    tests,
36
 
    transport,
37
 
    )
 
27
from bzrlib import urlutils
38
28
from bzrlib.tests import test_server
39
 
from bzrlib.transport import local
40
29
 
41
30
 
42
31
class BadWebserverPath(ValueError):
139
128
    def _handle_one_request(self):
140
129
        SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
141
130
 
142
 
    _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
 
131
    _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)?$')
143
132
    _tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
144
133
 
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.
 
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.
151
146
        """
152
 
        tail = 0
153
 
        ranges = []
154
147
        if not ranges_header.startswith('bytes='):
155
148
            # Syntactically invalid header
156
 
            return 0, []
 
149
            return None
157
150
 
 
151
        tail = None
 
152
        ranges = []
158
153
        ranges_header = ranges_header[len('bytes='):]
159
154
        for range_str in ranges_header.split(','):
160
 
            # FIXME: RFC2616 says end is optional and default to file_size
161
155
            range_match = self._range_regexp.match(range_str)
162
156
            if range_match is not None:
163
157
                start = int(range_match.group('start'))
164
 
                end = int(range_match.group('end'))
 
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)
165
164
                if start > end:
166
165
                    # Syntactically invalid range
167
 
                    return 0, []
 
166
                    return None
168
167
                ranges.append((start, end))
169
168
            else:
170
169
                tail_match = self._tail_regexp.match(range_str)
172
171
                    tail = int(tail_match.group('tail'))
173
172
                else:
174
173
                    # Syntactically invalid range
175
 
                    return 0, []
176
 
        return tail, ranges
 
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
177
189
 
178
190
    def _header_line_length(self, keyword, value):
179
191
        header_line = '%s: %s\r\n' % (keyword, value)
269
281
            return
270
282
 
271
283
        file_size = os.fstat(f.fileno())[6]
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:
 
284
        ranges = self._parse_ranges(ranges_header_value, file_size)
 
285
        if not ranges:
295
286
            # RFC2616 14.16 and 14.35 says that when a server
296
287
            # encounters unsatisfiable range specifiers, it
297
288
            # SHOULD return a 416.
345
336
        """
346
337
        # abandon query parameters
347
338
        path = urlparse.urlparse(path)[2]
348
 
        path = posixpath.normpath(urllib.unquote(path))
 
339
        path = posixpath.normpath(urlutils.unquote(path))
349
340
        path = path.decode('utf-8')
350
341
        words = path.split('/')
351
342
        words = filter(None, words)