Rev 5706: (vila) Correctly parse partial range specifiers in the HTTP test server. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Mar 8 16:59:39 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5706 [merge]
revision-id: pqm at pqm.ubuntu.com-20110308165936-hp3voq41wvr83wnl
parent: pqm at pqm.ubuntu.com-20110308014146-kflr8jyyx1yahq31
parent: v.ladeuil+lp at free.fr-20110308161618-kon43u6huz2cwfv0
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2011-03-08 16:59:36 +0000
message:
(vila) Correctly parse partial range specifiers in the HTTP test server.
(Vincent Ladeuil)
modified:
bzrlib/tests/http_server.py httpserver.py-20061012142527-m1yxdj1xazsf8d7s-1
bzrlib/tests/test_http.py testhttp.py-20051018020158-b2eef6e867c514d9
doc/en/release-notes/bzr-2.4.txt bzr2.4.txt-20110114053217-k7ym9jfz243fddjm-1
=== modified file 'bzrlib/tests/http_server.py'
--- a/bzrlib/tests/http_server.py 2011-01-12 01:01:53 +0000
+++ b/bzrlib/tests/http_server.py 2011-03-08 16:00:55 +0000
@@ -128,32 +128,42 @@
def _handle_one_request(self):
SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
- _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
+ _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)?$')
_tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
- def parse_ranges(self, ranges_header):
- """Parse the range header value and returns ranges and tail.
-
- RFC2616 14.35 says that syntactically invalid range
- specifiers MUST be ignored. In that case, we return 0 for
- tail and [] for ranges.
+ def _parse_ranges(self, ranges_header, file_size):
+ """Parse the range header value and returns ranges.
+
+ RFC2616 14.35 says that syntactically invalid range specifiers MUST be
+ ignored. In that case, we return None instead of a range list.
+
+ :param ranges_header: The 'Range' header value.
+
+ :param file_size: The size of the requested file.
+
+ :return: A list of (start, end) tuples or None if some invalid range
+ specifier is encountered.
"""
- tail = 0
- ranges = []
if not ranges_header.startswith('bytes='):
# Syntactically invalid header
- return 0, []
+ return None
+ tail = None
+ ranges = []
ranges_header = ranges_header[len('bytes='):]
for range_str in ranges_header.split(','):
- # FIXME: RFC2616 says end is optional and default to file_size
range_match = self._range_regexp.match(range_str)
if range_match is not None:
start = int(range_match.group('start'))
- end = int(range_match.group('end'))
+ end_match = range_match.group('end')
+ if end_match is None:
+ # RFC2616 says end is optional and default to file_size
+ end = file_size
+ else:
+ end = int(end_match)
if start > end:
# Syntactically invalid range
- return 0, []
+ return None
ranges.append((start, end))
else:
tail_match = self._tail_regexp.match(range_str)
@@ -161,8 +171,21 @@
tail = int(tail_match.group('tail'))
else:
# Syntactically invalid range
- return 0, []
- return tail, ranges
+ return None
+ if tail is not None:
+ # Normalize tail into ranges
+ ranges.append((max(0, file_size - tail), file_size))
+
+ checked_ranges = []
+ for start, end in ranges:
+ if start >= file_size:
+ # RFC2616 14.35, ranges are invalid if start >= file_size
+ return None
+ # RFC2616 14.35, end values should be truncated
+ # to file_size -1 if they exceed it
+ end = min(end, file_size - 1)
+ checked_ranges.append((start, end))
+ return checked_ranges
def _header_line_length(self, keyword, value):
header_line = '%s: %s\r\n' % (keyword, value)
@@ -258,29 +281,8 @@
return
file_size = os.fstat(f.fileno())[6]
- tail, ranges = self.parse_ranges(ranges_header_value)
- # Normalize tail into ranges
- if tail != 0:
- ranges.append((file_size - tail, file_size))
-
- self._satisfiable_ranges = True
- if len(ranges) == 0:
- self._satisfiable_ranges = False
- else:
- def check_range(range_specifier):
- start, end = range_specifier
- # RFC2616 14.35, ranges are invalid if start >= file_size
- if start >= file_size:
- self._satisfiable_ranges = False # Side-effect !
- return 0, 0
- # RFC2616 14.35, end values should be truncated
- # to file_size -1 if they exceed it
- end = min(end, file_size - 1)
- return start, end
-
- ranges = map(check_range, ranges)
-
- if not self._satisfiable_ranges:
+ ranges = self._parse_ranges(ranges_header_value, file_size)
+ if not ranges:
# RFC2616 14.16 and 14.35 says that when a server
# encounters unsatisfiable range specifiers, it
# SHOULD return a 416.
=== modified file 'bzrlib/tests/test_http.py'
--- a/bzrlib/tests/test_http.py 2011-02-10 12:37:27 +0000
+++ b/bzrlib/tests/test_http.py 2011-03-08 16:16:18 +0000
@@ -255,6 +255,40 @@
self.assertEqual('realm="Thou should not pass"', remainder)
+class TestHTTPRangeParsing(tests.TestCase):
+
+ def setUp(self):
+ super(TestHTTPRangeParsing, self).setUp()
+ # We focus on range parsing here and ignore everything else
+ class RequestHandler(http_server.TestingHTTPRequestHandler):
+ def setup(self): pass
+ def handle(self): pass
+ def finish(self): pass
+
+ self.req_handler = RequestHandler(None, None, None)
+
+ def assertRanges(self, ranges, header, file_size):
+ self.assertEquals(ranges,
+ self.req_handler._parse_ranges(header, file_size))
+
+ def test_simple_range(self):
+ self.assertRanges([(0,2)], 'bytes=0-2', 12)
+
+ def test_tail(self):
+ self.assertRanges([(8, 11)], 'bytes=-4', 12)
+
+ def test_tail_bigger_than_file(self):
+ self.assertRanges([(0, 11)], 'bytes=-99', 12)
+
+ def test_range_without_end(self):
+ self.assertRanges([(4, 11)], 'bytes=4-', 12)
+
+ def test_invalid_ranges(self):
+ self.assertRanges(None, 'bytes=12-22', 12)
+ self.assertRanges(None, 'bytes=1-3,12-22', 12)
+ self.assertRanges(None, 'bytes=-', 12)
+
+
class TestHTTPServer(tests.TestCase):
"""Test the HTTP servers implementations."""
@@ -428,7 +462,7 @@
"""Test the http connections."""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -493,7 +527,7 @@
class TestPost(tests.TestCase):
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -552,7 +586,7 @@
"""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -1030,7 +1064,7 @@
"""Tests readv requests against a server erroring out on too much ranges."""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -1076,7 +1110,7 @@
def _proxied_request(self):
handler = _urllib2_wrappers.ProxyHandler()
- request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
+ request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
handler.set_proxy(request, 'http')
return request
@@ -1090,6 +1124,12 @@
request = self._proxied_request()
self.assertFalse(request.headers.has_key('Proxy-authorization'))
+ def test_user_with_at(self):
+ self.overrideEnv('http_proxy',
+ 'http://username@domain:password@proxy_host:1234')
+ request = self._proxied_request()
+ self.assertFalse(request.headers.has_key('Proxy-authorization'))
+
def test_invalid_proxy(self):
"""A proxy env variable without scheme"""
self.overrideEnv('http_proxy', 'host:1234')
@@ -1126,7 +1166,7 @@
"""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -1223,7 +1263,7 @@
"""Test the Range header in GET methods."""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -1273,7 +1313,7 @@
"""Test redirection between http servers."""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -1346,7 +1386,7 @@
"""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -1401,7 +1441,7 @@
"""Test transport.do_catching_redirections."""
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
@@ -1756,7 +1796,7 @@
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
scenarios = multiply_scenarios(
- vary_by_http_client_implementation(),
+ vary_by_http_client_implementation(),
vary_by_http_protocol_version(),
)
=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- a/doc/en/release-notes/bzr-2.4.txt 2011-03-06 22:45:05 +0000
+++ b/doc/en/release-notes/bzr-2.4.txt 2011-03-08 16:00:55 +0000
@@ -221,5 +221,8 @@
suite. This can include new facilities for writing tests, fixes to
spurious test failures and changes to the way things should be tested.
+* The Range parsing for HTTP requests will correctly parse incomplete ranges.
+ (Vincent Ladeuil, #731240)
+
..
vim: tw=74 ft=rst ff=unix
More information about the bazaar-commits
mailing list