Rev 3120: Most refactoring regarding parameterization for urllib/pycurl and custom in file:///v/home/vila/src/bzr/bugs/175524/

Vincent Ladeuil v.ladeuil+lp at free.fr
Wed Dec 19 11:29:16 GMT 2007


At file:///v/home/vila/src/bzr/bugs/175524/

------------------------------------------------------------
revno: 3120
revision-id:v.ladeuil+lp at free.fr-20071219112912-fd3wdp4z3tmknf0v
parent: v.ladeuil+lp at free.fr-20071219093256-ibps16qrj0iji35v
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: 175524
timestamp: Wed 2007-12-19 12:29:12 +0100
message:
  Most refactoring regarding parameterization for urllib/pycurl and custom
  request handlers done.
  
  * bzrlib/tests/test_http_implementations.py: 
  Transfer more tests from test_http.py and dedicated request
  handlers from http_utils.py.
  
  * bzrlib/tests/test_http.py: 
  Transfer more tests to test_http_implementations.
  
  * bzrlib/tests/http_utils.py: 
  Transfer to http_test_implementations.py all request hadlers that
  exercise "bogus" behaviors since they are specific to some tests
  and not expected to be reused.
modified:
  bzrlib/tests/http_utils.py     HTTPTestUtil.py-20050914180604-247d3aafb7a43343
  bzrlib/tests/test_http.py      testhttp.py-20051018020158-b2eef6e867c514d9
  bzrlib/tests/test_http_implementations.py test_http_implementa-20071218210003-65nh81gglcfvurw6-1
-------------- next part --------------
=== modified file 'bzrlib/tests/http_utils.py'
--- a/bzrlib/tests/http_utils.py	2007-12-19 09:29:21 +0000
+++ b/bzrlib/tests/http_utils.py	2007-12-19 11:29:12 +0000
@@ -37,78 +37,6 @@
     )
 
 
-class WallRequestHandler(TestingHTTPRequestHandler):
-    """Whatever request comes in, close the connection"""
-
-    def handle_one_request(self):
-        """Handle a single HTTP request, by abruptly closing the connection"""
-        self.close_connection = 1
-
-
-class BadStatusRequestHandler(TestingHTTPRequestHandler):
-    """Whatever request comes in, returns a bad status"""
-
-    def parse_request(self):
-        """Fakes handling a single HTTP request, returns a bad status"""
-        ignored = TestingHTTPRequestHandler.parse_request(self)
-        try:
-            self.send_response(0, "Bad status")
-            self.end_headers()
-        except socket.error, e:
-            # We don't want to pollute the test results with
-            # spurious server errors while test succeed. In our
-            # case, it may occur that the test has already read
-            # the 'Bad Status' and closed the socket while we are
-            # still trying to send some headers... So the test is
-            # ok, but if we raise the exception, the output is
-            # dirty. So we don't raise, but we close the
-            # connection, just to be safe :)
-            spurious = [errno.EPIPE,
-                        errno.ECONNRESET,
-                        errno.ECONNABORTED,
-                        ]
-            if (len(e.args) > 0) and (e.args[0] in spurious):
-                self.close_connection = 1
-                pass
-            else:
-                raise
-        return False
-
-
-class InvalidStatusRequestHandler(TestingHTTPRequestHandler):
-    """Whatever request comes in, returns am invalid status"""
-
-    def parse_request(self):
-        """Fakes handling a single HTTP request, returns a bad status"""
-        ignored = TestingHTTPRequestHandler.parse_request(self)
-        self.wfile.write("Invalid status line\r\n")
-        return False
-
-
-class BadProtocolRequestHandler(TestingHTTPRequestHandler):
-    """Whatever request comes in, returns a bad protocol version"""
-
-    def parse_request(self):
-        """Fakes handling a single HTTP request, returns a bad status"""
-        ignored = TestingHTTPRequestHandler.parse_request(self)
-        # Returns an invalid protocol version, but curl just
-        # ignores it and those cannot be tested.
-        self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
-                                           404,
-                                           'Look at my protocol version'))
-        return False
-
-
-class ForbiddenRequestHandler(TestingHTTPRequestHandler):
-    """Whatever request comes in, returns a 403 code"""
-
-    def parse_request(self):
-        """Handle a single HTTP request, by replying we cannot handle it"""
-        ignored = TestingHTTPRequestHandler.parse_request(self)
-        self.send_error(403)
-        return False
-
-
 class HTTPServerWithSmarts(HttpServer):
     """HTTPServerWithSmarts extends the HttpServer with POST methods that will
     trigger a smart server to execute with a transport rooted at the rootdir of
@@ -151,65 +79,6 @@
         self.wfile.write(out_buffer.getvalue())
 
 
-class LimitedRangeRequestHandler(TestingHTTPRequestHandler):
-    """Errors out when range specifiers exceed the limit"""
-
-    def get_multiple_ranges(self, file, file_size, ranges):
-        """Refuses the multiple ranges request"""
-        tcs = self.server.test_case_server
-        if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
-            file.close()
-            # Emulate apache behavior
-            self.send_error(400, "Bad Request")
-            return
-        return TestingHTTPRequestHandler.get_multiple_ranges(self, file,
-                                                             file_size, ranges)
-
-
-class LimitedRangeHTTPServer(HttpServer):
-    """An HttpServer erroring out on requests with too much range specifiers"""
-
-    def __init__(self, request_handler=LimitedRangeRequestHandler,
-                 range_limit=None):
-        HttpServer.__init__(self, request_handler)
-        self.range_limit = range_limit
-
-
-class SingleRangeRequestHandler(TestingHTTPRequestHandler):
-    """Always reply to range request as if they were single.
-
-    Don't be explicit about it, just to annoy the clients.
-    """
-
-    def get_multiple_ranges(self, file, file_size, ranges):
-        """Answer as if it was a single range request and ignores the rest"""
-        (start, end) = ranges[0]
-        return self.get_single_range(file, file_size, start, end)
-
-
-class SingleOnlyRangeRequestHandler(TestingHTTPRequestHandler):
-    """Only reply to simple range requests, errors out on multiple"""
-
-    def get_multiple_ranges(self, file, file_size, ranges):
-        """Refuses the multiple ranges request"""
-        if len(ranges) > 1:
-            file.close()
-            self.send_error(416, "Requested range not satisfiable")
-            return
-        (start, end) = ranges[0]
-        return self.get_single_range(file, file_size, start, end)
-
-
-class NoRangeRequestHandler(TestingHTTPRequestHandler):
-    """Ignore range requests without notice"""
-
-    def do_GET(self):
-        # Update the statistics
-        self.server.test_case_server.GET_request_nb += 1
-        # Just bypass the range handling done by TestingHTTPRequestHandler
-        return SimpleHTTPRequestHandler.do_GET(self)
-
-
 class TestCaseWithWebserver(TestCaseWithTransport):
     """A support class that provides readonly urls that are http://.
 

=== modified file 'bzrlib/tests/test_http.py'
--- a/bzrlib/tests/test_http.py	2007-12-19 09:29:21 +0000
+++ b/bzrlib/tests/test_http.py	2007-12-19 11:29:12 +0000
@@ -46,24 +46,15 @@
     StringIOWrapper,
     )
 from bzrlib.tests.http_utils import (
-    BadProtocolRequestHandler,
-    BadStatusRequestHandler,
-    ForbiddenRequestHandler,
     HTTPBasicAuthServer,
     HTTPDigestAuthServer,
     HTTPServerRedirecting,
-    InvalidStatusRequestHandler,
-    LimitedRangeHTTPServer,
-    NoRangeRequestHandler,
     ProxyBasicAuthServer,
     ProxyDigestAuthServer,
     ProxyServer,
-    SingleRangeRequestHandler,
-    SingleOnlyRangeRequestHandler,
     TestCaseWithRedirectedWebserver,
     TestCaseWithTwoWebservers,
     TestCaseWithWebserver,
-    WallRequestHandler,
     )
 from bzrlib.transport import (
     _CoalescedOffset,
@@ -170,11 +161,7 @@
                           f.credentials[0])
 
 
-class TestHttpUrls_pycurl(TestWithTransport_pycurl, tests.TestCase):
-    """Test http urls with pycurl"""
-
-    _server = http_server.HttpServer_PyCurl
-    _qualified_prefix = 'http+pycurl'
+class TestHttps_pycurl(TestWithTransport_pycurl, tests.TestCase):
 
     # TODO: This should really be moved into another pycurl
     # specific test. When https tests will be implemented, take
@@ -209,16 +196,6 @@
         self.assertRaises(errors.DependencyNotPresent, self._transport,
                           'https://launchpad.net')
 
-class TestHttpTransportRegistration(tests.TestCase):
-    """Test registrations of various http implementations"""
-
-    def test_http_registered(self):
-        # urlllib should always be present
-        t = get_transport('http+urllib://bzr.google.com/')
-        self.assertIsInstance(t, Transport)
-        self.assertIsInstance(t, HttpTransport_urllib)
-
-
 class TestRangeHeader(tests.TestCase):
     """Test range_header method"""
 
@@ -247,153 +224,6 @@
                           tail=50)
 
 
-class TestWallServer(object):
-    """Tests exceptions during the connection phase"""
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(WallRequestHandler)
-
-    def test_http_has(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        # Unfortunately httplib (see HTTPResponse._read_status
-        # for details) make no distinction between a closed
-        # socket and badly formatted status line, so we can't
-        # just test for ConnectionError, we have to test
-        # InvalidHttpResponse too.
-        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
-                          t.has, 'foo/bar')
-
-    def test_http_get(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
-                          t.get, 'foo/bar')
-
-
-class TestWallServer_urllib(TestWallServer, TestCaseWithWebserver):
-    """Tests "wall" server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestWallServer_pycurl(TestWithTransport_pycurl,
-                            TestWallServer,
-                            TestCaseWithWebserver):
-    """Tests "wall" server for pycurl implementation"""
-
-
-class TestBadStatusServer(object):
-    """Tests bad status from server."""
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(BadStatusRequestHandler)
-
-    def test_http_has(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
-
-    def test_http_get(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
-
-
-class TestBadStatusServer_urllib(TestBadStatusServer, TestCaseWithWebserver):
-    """Tests bad status server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestBadStatusServer_pycurl(TestWithTransport_pycurl,
-                                 TestBadStatusServer,
-                                 TestCaseWithWebserver):
-    """Tests bad status server for pycurl implementation"""
-
-
-class TestInvalidStatusServer(TestBadStatusServer):
-    """Tests invalid status from server.
-
-    Both implementations raises the same error as for a bad status.
-    """
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(InvalidStatusRequestHandler)
-
-
-class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
-                                     TestCaseWithWebserver):
-    """Tests invalid status server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
-                                     TestInvalidStatusServer,
-                                     TestCaseWithWebserver):
-    """Tests invalid status server for pycurl implementation"""
-
-
-class TestBadProtocolServer(object):
-    """Tests bad protocol from server."""
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(BadProtocolRequestHandler)
-
-    def test_http_has(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
-
-    def test_http_get(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
-
-
-class TestBadProtocolServer_urllib(TestBadProtocolServer,
-                                   TestCaseWithWebserver):
-    """Tests bad protocol server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-# curl don't check the protocol version
-#class TestBadProtocolServer_pycurl(TestWithTransport_pycurl,
-#                                   TestBadProtocolServer,
-#                                   TestCaseWithWebserver):
-#    """Tests bad protocol server for pycurl implementation"""
-
-
-class TestForbiddenServer(object):
-    """Tests forbidden server"""
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(ForbiddenRequestHandler)
-
-    def test_http_has(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
-
-    def test_http_get(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
-
-
-class TestForbiddenServer_urllib(TestForbiddenServer, TestCaseWithWebserver):
-    """Tests forbidden server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestForbiddenServer_pycurl(TestWithTransport_pycurl,
-                                 TestForbiddenServer,
-                                 TestCaseWithWebserver):
-    """Tests forbidden server for pycurl implementation"""
-
-
 class TestRecordingServer(tests.TestCase):
 
     def test_create(self):
@@ -425,204 +255,6 @@
         self.assertEqual('abc', server.received_bytes)
 
 
-class TestRangeRequestServer(object):
-    """Tests readv requests against server.
-
-    This MUST be used by daughter classes that also inherit from
-    TestCaseWithWebserver.
-
-    We can't inherit directly from TestCaseWithWebserver or the
-    test framework will try to create an instance which cannot
-    run, its implementation being incomplete.
-    """
-
-    def setUp(self):
-        TestCaseWithWebserver.setUp(self)
-        self.build_tree_contents([('a', '0123456789')],)
-
-    def test_readv(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
-        self.assertEqual(l[0], (0, '0'))
-        self.assertEqual(l[1], (1, '1'))
-        self.assertEqual(l[2], (3, '34'))
-        self.assertEqual(l[3], (9, '9'))
-
-    def test_readv_out_of_order(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
-        self.assertEqual(l[0], (1, '1'))
-        self.assertEqual(l[1], (9, '9'))
-        self.assertEqual(l[2], (0, '0'))
-        self.assertEqual(l[3], (3, '34'))
-
-    def test_readv_invalid_ranges(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-
-        # This is intentionally reading off the end of the file
-        # since we are sure that it cannot get there
-        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
-                              t.readv, 'a', [(1,1), (8,10)])
-
-        # This is trying to seek past the end of the file, it should
-        # also raise a special error
-        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
-                              t.readv, 'a', [(12,2)])
-
-    def test_readv_multiple_get_requests(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        # force transport to issue multiple requests
-        t._max_readv_combine = 1
-        t._max_get_ranges = 1
-        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
-        self.assertEqual(l[0], (0, '0'))
-        self.assertEqual(l[1], (1, '1'))
-        self.assertEqual(l[2], (3, '34'))
-        self.assertEqual(l[3], (9, '9'))
-        # The server should have issued 4 requests
-        self.assertEqual(4, server.GET_request_nb)
-
-    def test_readv_get_max_size(self):
-        server = self.get_readonly_server()
-        t = self._transport(server.get_url())
-        # force transport to issue multiple requests by limiting the number of
-        # bytes by request. Note that this apply to coalesced offsets only, a
-        # single range ill keep its size even if bigger than the limit.
-        t._get_max_size = 2
-        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
-        self.assertEqual(l[0], (0, '0'))
-        self.assertEqual(l[1], (1, '1'))
-        self.assertEqual(l[2], (2, '2345'))
-        self.assertEqual(l[3], (6, '6789'))
-        # The server should have issued 3 requests
-        self.assertEqual(3, server.GET_request_nb)
-
-
-class TestSingleRangeRequestServer(TestRangeRequestServer):
-    """Test readv against a server which accept only single range requests"""
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(SingleRangeRequestHandler)
-
-
-class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
-                                          TestCaseWithWebserver):
-    """Tests single range requests accepting server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
-                                          TestSingleRangeRequestServer,
-                                          TestCaseWithWebserver):
-    """Tests single range requests accepting server for pycurl implementation"""
-
-
-class TestSingleOnlyRangeRequestServer(TestRangeRequestServer):
-    """Test readv against a server which only accept single range requests"""
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(SingleOnlyRangeRequestHandler)
-
-
-class TestSingleOnlyRangeRequestServer_urllib(TestSingleOnlyRangeRequestServer,
-                                              TestCaseWithWebserver):
-    """Tests single range requests accepting server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestSingleOnlyRangeRequestServer_pycurl(TestWithTransport_pycurl,
-                                              TestSingleOnlyRangeRequestServer,
-                                              TestCaseWithWebserver):
-    """Tests single range requests accepting server for pycurl implementation"""
-
-
-class TestNoRangeRequestServer(TestRangeRequestServer):
-    """Test readv against a server which do not accept range requests"""
-
-    def create_transport_readonly_server(self):
-        return http_server.HttpServer(NoRangeRequestHandler)
-
-
-class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
-                                      TestCaseWithWebserver):
-    """Tests range requests refusing server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
-                                      TestNoRangeRequestServer,
-                                      TestCaseWithWebserver):
-    """Tests range requests refusing server for pycurl implementation"""
-
-
-class TestLimitedRangeRequestServer(object):
-    """Tests readv requests against server that errors out on too much ranges.
-
-    This MUST be used by daughter classes that also inherit from
-    TestCaseWithWebserver.
-
-    We can't inherit directly from TestCaseWithWebserver or the
-    test framework will try to create an instance which cannot
-    run, its implementation being incomplete.
-    """
-
-    range_limit = 3
-
-    def create_transport_readonly_server(self):
-        # Requests with more range specifiers will error out
-        return LimitedRangeHTTPServer(range_limit=self.range_limit)
-
-    def get_transport(self):
-        return self._transport(self.get_readonly_server().get_url())
-
-    def setUp(self):
-        TestCaseWithWebserver.setUp(self)
-        # We need to manipulate ranges that correspond to real chunks in the
-        # response, so we build a content appropriately.
-        filler = ''.join(['abcdefghij' for x in range(102)])
-        content = ''.join(['%04d' % v + filler for v in range(16)])
-        self.build_tree_contents([('a', content)],)
-
-    def test_few_ranges(self):
-        t = self.get_transport()
-        l = list(t.readv('a', ((0, 4), (1024, 4), )))
-        self.assertEqual(l[0], (0, '0000'))
-        self.assertEqual(l[1], (1024, '0001'))
-        self.assertEqual(1, self.get_readonly_server().GET_request_nb)
-
-    def test_more_ranges(self):
-        t = self.get_transport()
-        l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
-        self.assertEqual(l[0], (0, '0000'))
-        self.assertEqual(l[1], (1024, '0001'))
-        self.assertEqual(l[2], (4096, '0004'))
-        self.assertEqual(l[3], (8192, '0008'))
-        # The server will refuse to serve the first request (too much ranges),
-        # a second request will succeeds.
-        self.assertEqual(2, self.get_readonly_server().GET_request_nb)
-
-
-class TestLimitedRangeRequestServer_urllib(TestLimitedRangeRequestServer,
-                                          TestCaseWithWebserver):
-    """Tests limited range requests server for urllib implementation"""
-
-    _transport = HttpTransport_urllib
-
-
-class TestLimitedRangeRequestServer_pycurl(TestWithTransport_pycurl,
-                                          TestLimitedRangeRequestServer,
-                                          TestCaseWithWebserver):
-    """Tests limited range requests server for pycurl implementation"""
-
-
-
 class TestHttpProxyWhiteBox(tests.TestCase):
     """Whitebox test proxy http authorization.
 

=== modified file 'bzrlib/tests/test_http_implementations.py'
--- a/bzrlib/tests/test_http_implementations.py	2007-12-19 09:29:21 +0000
+++ b/bzrlib/tests/test_http_implementations.py	2007-12-19 11:29:12 +0000
@@ -25,6 +25,7 @@
 protocol implementation to guarantee the robustness of our transports.
 """
 
+import SimpleHTTPServer
 import socket
 
 import bzrlib
@@ -190,3 +191,372 @@
             server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
 
 
+class TestHttpTransportRegistration(tests.TestCase):
+    """Test registrations of various http implementations"""
+
+    def test_http_registered(self):
+        t = transport.get_transport('%s://foo.com/' % self._qualified_prefix)
+        self.assertIsInstance(t, transport.Transport)
+        self.assertIsInstance(t, self._transport)
+
+
+class TestSpecificRequestHandler(http_utils.TestCaseWithWebserver):
+    """Tests a specific request handler.
+
+
+    Daughter class are expected to override _req_handler_class
+    """
+
+    # Provide a useful default
+    _req_handler_class = http_server.TestingHTTPRequestHandler
+
+    def create_transport_readonly_server(self):
+        return http_server.HttpServer(self._req_handler_class)
+
+
+class WallRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Whatever request comes in, close the connection"""
+
+    def handle_one_request(self):
+        """Handle a single HTTP request, by abruptly closing the connection"""
+        self.close_connection = 1
+
+
+class TestWallServer(TestSpecificRequestHandler):
+    """Tests exceptions during the connection phase"""
+
+    _req_handler_class = WallRequestHandler
+
+    def test_http_has(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        # Unfortunately httplib (see HTTPResponse._read_status
+        # for details) make no distinction between a closed
+        # socket and badly formatted status line, so we can't
+        # just test for ConnectionError, we have to test
+        # InvalidHttpResponse too.
+        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
+                          t.has, 'foo/bar')
+
+    def test_http_get(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
+                          t.get, 'foo/bar')
+
+
+class BadStatusRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Whatever request comes in, returns a bad status"""
+
+    def parse_request(self):
+        """Fakes handling a single HTTP request, returns a bad status"""
+        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
+        try:
+            self.send_response(0, "Bad status")
+            self.end_headers()
+        except socket.error, e:
+            # We don't want to pollute the test results with
+            # spurious server errors while test succeed. In our
+            # case, it may occur that the test has already read
+            # the 'Bad Status' and closed the socket while we are
+            # still trying to send some headers... So the test is
+            # ok, but if we raise the exception, the output is
+            # dirty. So we don't raise, but we close the
+            # connection, just to be safe :)
+            spurious = [errno.EPIPE,
+                        errno.ECONNRESET,
+                        errno.ECONNABORTED,
+                        ]
+            if (len(e.args) > 0) and (e.args[0] in spurious):
+                self.close_connection = 1
+                pass
+            else:
+                raise
+        return False
+
+
+class TestBadStatusServer(TestSpecificRequestHandler):
+    """Tests bad status from server."""
+
+    _req_handler_class = BadStatusRequestHandler
+
+    def test_http_has(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
+
+    def test_http_get(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
+
+
+class InvalidStatusRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Whatever request comes in, returns am invalid status"""
+
+    def parse_request(self):
+        """Fakes handling a single HTTP request, returns a bad status"""
+        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
+        self.wfile.write("Invalid status line\r\n")
+        return False
+
+
+class TestInvalidStatusServer(TestBadStatusServer):
+    """Tests invalid status from server.
+
+    Both implementations raises the same error as for a bad status.
+    """
+
+    _req_handler_class = InvalidStatusRequestHandler
+
+
+class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Whatever request comes in, returns a bad protocol version"""
+
+    def parse_request(self):
+        """Fakes handling a single HTTP request, returns a bad status"""
+        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
+        # Returns an invalid protocol version, but curl just
+        # ignores it and those cannot be tested.
+        self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
+                                           404,
+                                           'Look at my protocol version'))
+        return False
+
+
+class TestBadProtocolServer(TestSpecificRequestHandler):
+    """Tests bad protocol from server."""
+
+    _req_handler_class = BadProtocolRequestHandler
+
+    def setUp(self):
+        if pycurl_present and self._transport == PyCurlTransport:
+            raise tests.TestNotApplicable(
+                "pycurl doesn't check the protocol version")
+        super(TestBadProtocolServer, self).setUp()
+
+    def test_http_has(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
+
+    def test_http_get(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
+
+
+class ForbiddenRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Whatever request comes in, returns a 403 code"""
+
+    def parse_request(self):
+        """Handle a single HTTP request, by replying we cannot handle it"""
+        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
+        self.send_error(403)
+        return False
+
+
+class TestForbiddenServer(TestSpecificRequestHandler):
+    """Tests forbidden server"""
+
+    _req_handler_class = ForbiddenRequestHandler
+
+    def test_http_has(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
+
+    def test_http_get(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
+
+
+class TestRangeRequestServer(TestSpecificRequestHandler):
+    """Tests readv requests against server.
+
+    We test against default "normal" server.
+    """
+
+    def setUp(self):
+        super(TestRangeRequestServer, self).setUp()
+        self.build_tree_contents([('a', '0123456789')],)
+
+    def test_readv(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
+        self.assertEqual(l[0], (0, '0'))
+        self.assertEqual(l[1], (1, '1'))
+        self.assertEqual(l[2], (3, '34'))
+        self.assertEqual(l[3], (9, '9'))
+
+    def test_readv_out_of_order(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
+        self.assertEqual(l[0], (1, '1'))
+        self.assertEqual(l[1], (9, '9'))
+        self.assertEqual(l[2], (0, '0'))
+        self.assertEqual(l[3], (3, '34'))
+
+    def test_readv_invalid_ranges(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+
+        # This is intentionally reading off the end of the file
+        # since we are sure that it cannot get there
+        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
+                              t.readv, 'a', [(1,1), (8,10)])
+
+        # This is trying to seek past the end of the file, it should
+        # also raise a special error
+        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
+                              t.readv, 'a', [(12,2)])
+
+    def test_readv_multiple_get_requests(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        # force transport to issue multiple requests
+        t._max_readv_combine = 1
+        t._max_get_ranges = 1
+        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
+        self.assertEqual(l[0], (0, '0'))
+        self.assertEqual(l[1], (1, '1'))
+        self.assertEqual(l[2], (3, '34'))
+        self.assertEqual(l[3], (9, '9'))
+        # The server should have issued 4 requests
+        self.assertEqual(4, server.GET_request_nb)
+
+    def test_readv_get_max_size(self):
+        server = self.get_readonly_server()
+        t = self._transport(server.get_url())
+        # force transport to issue multiple requests by limiting the number of
+        # bytes by request. Note that this apply to coalesced offsets only, a
+        # single range ill keep its size even if bigger than the limit.
+        t._get_max_size = 2
+        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
+        self.assertEqual(l[0], (0, '0'))
+        self.assertEqual(l[1], (1, '1'))
+        self.assertEqual(l[2], (2, '2345'))
+        self.assertEqual(l[3], (6, '6789'))
+        # The server should have issued 3 requests
+        self.assertEqual(3, server.GET_request_nb)
+
+
+class SingleRangeRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Always reply to range request as if they were single.
+
+    Don't be explicit about it, just to annoy the clients.
+    """
+
+    def get_multiple_ranges(self, file, file_size, ranges):
+        """Answer as if it was a single range request and ignores the rest"""
+        (start, end) = ranges[0]
+        return self.get_single_range(file, file_size, start, end)
+
+
+class TestSingleRangeRequestServer(TestRangeRequestServer):
+    """Test readv against a server which accept only single range requests"""
+
+    _req_handler_class = SingleRangeRequestHandler
+
+
+class SingleOnlyRangeRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Only reply to simple range requests, errors out on multiple"""
+
+    def get_multiple_ranges(self, file, file_size, ranges):
+        """Refuses the multiple ranges request"""
+        if len(ranges) > 1:
+            file.close()
+            self.send_error(416, "Requested range not satisfiable")
+            return
+        (start, end) = ranges[0]
+        return self.get_single_range(file, file_size, start, end)
+
+
+class TestSingleOnlyRangeRequestServer(TestRangeRequestServer):
+    """Test readv against a server which only accept single range requests"""
+
+    _req_handler_class = SingleOnlyRangeRequestHandler
+
+
+class NoRangeRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Ignore range requests without notice"""
+
+    def do_GET(self):
+        # Update the statistics
+        self.server.test_case_server.GET_request_nb += 1
+        # Just bypass the range handling done by TestingHTTPRequestHandler
+        return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+
+
+class TestNoRangeRequestServer(TestRangeRequestServer):
+    """Test readv against a server which do not accept range requests"""
+
+    _req_handler_class = NoRangeRequestHandler
+
+
+class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
+    """Errors out when range specifiers exceed the limit"""
+
+    def get_multiple_ranges(self, file, file_size, ranges):
+        """Refuses the multiple ranges request"""
+        tcs = self.server.test_case_server
+        if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
+            file.close()
+            # Emulate apache behavior
+            self.send_error(400, "Bad Request")
+            return
+        return http_server.TestingHTTPRequestHandler.get_multiple_ranges(
+            self, file, file_size, ranges)
+
+
+class LimitedRangeHTTPServer(http_server.HttpServer):
+    """An HttpServer erroring out on requests with too much range specifiers"""
+
+    def __init__(self, request_handler=LimitedRangeRequestHandler,
+                 range_limit=None):
+        http_server.HttpServer.__init__(self, request_handler)
+        self.range_limit = range_limit
+
+
+class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
+    """Tests readv requests against a server erroring out on too much ranges."""
+
+    range_limit = 3
+
+    def create_transport_readonly_server(self):
+        # Requests with more range specifiers will error out
+        return LimitedRangeHTTPServer(range_limit=self.range_limit)
+
+    def get_transport(self):
+        return self._transport(self.get_readonly_server().get_url())
+
+    def setUp(self):
+        http_utils.TestCaseWithWebserver.setUp(self)
+        # We need to manipulate ranges that correspond to real chunks in the
+        # response, so we build a content appropriately.
+        filler = ''.join(['abcdefghij' for x in range(102)])
+        content = ''.join(['%04d' % v + filler for v in range(16)])
+        self.build_tree_contents([('a', content)],)
+
+    def test_few_ranges(self):
+        t = self.get_transport()
+        l = list(t.readv('a', ((0, 4), (1024, 4), )))
+        self.assertEqual(l[0], (0, '0000'))
+        self.assertEqual(l[1], (1024, '0001'))
+        self.assertEqual(1, self.get_readonly_server().GET_request_nb)
+
+    def test_more_ranges(self):
+        t = self.get_transport()
+        l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
+        self.assertEqual(l[0], (0, '0000'))
+        self.assertEqual(l[1], (1024, '0001'))
+        self.assertEqual(l[2], (4096, '0004'))
+        self.assertEqual(l[3], (8192, '0008'))
+        # The server will refuse to serve the first request (too much ranges),
+        # a second request will succeeds.
+        self.assertEqual(2, self.get_readonly_server().GET_request_nb)
+
+



More information about the bazaar-commits mailing list