~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
    ui,
45
45
    urlutils,
46
46
    )
47
 
from bzrlib.symbol_versioning import (
48
 
    deprecated_in,
49
 
    )
50
47
from bzrlib.tests import (
51
48
    features,
52
49
    http_server,
53
50
    http_utils,
 
51
    test_server,
54
52
    )
55
53
from bzrlib.transport import (
56
54
    http,
80
78
    transport_scenarios = [
81
79
        ('urllib', dict(_transport=_urllib.HttpTransport_urllib,
82
80
                        _server=http_server.HttpServer_urllib,
83
 
                        _qualified_prefix='http+urllib',)),
 
81
                        _url_protocol='http+urllib',)),
84
82
        ]
85
83
    if features.pycurl.available():
86
84
        transport_scenarios.append(
87
85
            ('pycurl', dict(_transport=PyCurlTransport,
88
86
                            _server=http_server.HttpServer_PyCurl,
89
 
                            _qualified_prefix='http+pycurl',)))
 
87
                            _url_protocol='http+pycurl',)))
90
88
    tests.multiply_tests(t_tests, transport_scenarios, result)
91
89
 
 
90
    protocol_scenarios = [
 
91
            ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
 
92
            ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
 
93
            ]
 
94
 
 
95
    # some tests are parametrized by the protocol version only
 
96
    p_tests, remaining_tests = tests.split_suite_by_condition(
 
97
        remaining_tests, tests.condition_isinstance((
 
98
                TestAuthOnRedirected,
 
99
                )))
 
100
    tests.multiply_tests(p_tests, protocol_scenarios, result)
 
101
 
92
102
    # each implementation tested with each HTTP version
93
103
    tp_tests, remaining_tests = tests.split_suite_by_condition(
94
104
        remaining_tests, tests.condition_isinstance((
103
113
                TestRanges,
104
114
                TestSpecificRequestHandler,
105
115
                )))
106
 
    protocol_scenarios = [
107
 
            ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
108
 
            ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
109
 
            ]
110
116
    tp_scenarios = tests.multiply_scenarios(transport_scenarios,
111
117
                                            protocol_scenarios)
112
118
    tests.multiply_tests(tp_tests, tp_scenarios, result)
223
229
        self._sock.bind(('127.0.0.1', 0))
224
230
        self.host, self.port = self._sock.getsockname()
225
231
        self._ready = threading.Event()
226
 
        self._thread = threading.Thread(target=self._accept_read_and_reply)
227
 
        self._thread.setDaemon(True)
 
232
        self._thread = test_server.ThreadWithException(
 
233
            event=self._ready, target=self._accept_read_and_reply)
228
234
        self._thread.start()
229
 
        self._ready.wait(5)
 
235
        if 'threads' in tests.selftest_debug_flags:
 
236
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
 
237
        self._ready.wait()
230
238
 
231
239
    def _accept_read_and_reply(self):
232
240
        self._sock.listen(1)
233
241
        self._ready.set()
234
 
        self._sock.settimeout(5)
235
 
        try:
236
 
            conn, address = self._sock.accept()
237
 
            # On win32, the accepted connection will be non-blocking to start
238
 
            # with because we're using settimeout.
239
 
            conn.setblocking(True)
 
242
        conn, address = self._sock.accept()
 
243
        if self._expect_body_tail is not None:
240
244
            while not self.received_bytes.endswith(self._expect_body_tail):
241
245
                self.received_bytes += conn.recv(4096)
242
246
            conn.sendall('HTTP/1.1 200 OK\r\n')
243
 
        except socket.timeout:
244
 
            # Make sure the client isn't stuck waiting for us to e.g. accept.
 
247
        try:
245
248
            self._sock.close()
246
249
        except socket.error:
247
250
            # The client may have already closed the socket.
249
252
 
250
253
    def stop_server(self):
251
254
        try:
252
 
            self._sock.close()
 
255
            # Issue a fake connection to wake up the server and allow it to
 
256
            # finish quickly
 
257
            fake_conn = osutils.connect_socket((self.host, self.port))
 
258
            fake_conn.close()
253
259
        except socket.error:
254
260
            # We might have already closed it.  We don't care.
255
261
            pass
256
262
        self.host = None
257
263
        self.port = None
 
264
        self._thread.join()
 
265
        if 'threads' in tests.selftest_debug_flags:
 
266
            sys.stderr.write('Thread  joined: %s\n' % (self._thread.ident,))
258
267
 
259
268
 
260
269
class TestAuthHeader(tests.TestCase):
304
313
 
305
314
            protocol_version = 'HTTP/0.1'
306
315
 
307
 
        server = http_server.HttpServer(BogusRequestHandler)
308
 
        try:
309
 
            self.assertRaises(httplib.UnknownProtocol, server.start_server)
310
 
        except:
311
 
            server.stop_server()
312
 
            self.fail('HTTP Server creation did not raise UnknownProtocol')
 
316
        self.assertRaises(httplib.UnknownProtocol,
 
317
                          http_server.HttpServer, BogusRequestHandler)
313
318
 
314
319
    def test_force_invalid_protocol(self):
315
 
        server = http_server.HttpServer(protocol_version='HTTP/0.1')
316
 
        try:
317
 
            self.assertRaises(httplib.UnknownProtocol, server.start_server)
318
 
        except:
319
 
            server.stop_server()
320
 
            self.fail('HTTP Server creation did not raise UnknownProtocol')
 
320
        self.assertRaises(httplib.UnknownProtocol,
 
321
                          http_server.HttpServer, protocol_version='HTTP/0.1')
321
322
 
322
323
    def test_server_start_and_stop(self):
323
324
        server = http_server.HttpServer()
 
325
        self.addCleanup(server.stop_server)
324
326
        server.start_server()
325
 
        try:
326
 
            self.assertTrue(server._http_running)
327
 
        finally:
328
 
            server.stop_server()
329
 
        self.assertFalse(server._http_running)
 
327
        self.assertTrue(server.server is not None)
 
328
        self.assertTrue(server.server.serving is not None)
 
329
        self.assertTrue(server.server.serving)
330
330
 
331
331
    def test_create_http_server_one_zero(self):
332
332
        class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
335
335
 
336
336
        server = http_server.HttpServer(RequestHandlerOneZero)
337
337
        self.start_server(server)
338
 
        self.assertIsInstance(server._httpd, http_server.TestingHTTPServer)
 
338
        self.assertIsInstance(server.server, http_server.TestingHTTPServer)
339
339
 
340
340
    def test_create_http_server_one_one(self):
341
341
        class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
344
344
 
345
345
        server = http_server.HttpServer(RequestHandlerOneOne)
346
346
        self.start_server(server)
347
 
        self.assertIsInstance(server._httpd,
 
347
        self.assertIsInstance(server.server,
348
348
                              http_server.TestingThreadingHTTPServer)
349
349
 
350
350
    def test_create_http_server_force_one_one(self):
355
355
        server = http_server.HttpServer(RequestHandlerOneZero,
356
356
                                        protocol_version='HTTP/1.1')
357
357
        self.start_server(server)
358
 
        self.assertIsInstance(server._httpd,
 
358
        self.assertIsInstance(server.server,
359
359
                              http_server.TestingThreadingHTTPServer)
360
360
 
361
361
    def test_create_http_server_force_one_zero(self):
366
366
        server = http_server.HttpServer(RequestHandlerOneOne,
367
367
                                        protocol_version='HTTP/1.0')
368
368
        self.start_server(server)
369
 
        self.assertIsInstance(server._httpd,
 
369
        self.assertIsInstance(server.server,
370
370
                              http_server.TestingHTTPServer)
371
371
 
372
372
 
431
431
        server.start_server()
432
432
        try:
433
433
            url = server.get_url()
434
 
            self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
 
434
            self.assertTrue(url.startswith('%s://' % self._url_protocol))
435
435
        finally:
436
436
            server.stop_server()
437
437
 
482
482
 
483
483
    def test_http_has(self):
484
484
        server = self.get_readonly_server()
485
 
        t = self._transport(server.get_url())
 
485
        t = self.get_readonly_transport()
486
486
        self.assertEqual(t.has('foo/bar'), True)
487
487
        self.assertEqual(len(server.logs), 1)
488
488
        self.assertContainsRe(server.logs[0],
490
490
 
491
491
    def test_http_has_not_found(self):
492
492
        server = self.get_readonly_server()
493
 
        t = self._transport(server.get_url())
 
493
        t = self.get_readonly_transport()
494
494
        self.assertEqual(t.has('not-found'), False)
495
495
        self.assertContainsRe(server.logs[1],
496
496
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
497
497
 
498
498
    def test_http_get(self):
499
499
        server = self.get_readonly_server()
500
 
        t = self._transport(server.get_url())
 
500
        t = self.get_readonly_transport()
501
501
        fp = t.get('foo/bar')
502
502
        self.assertEqualDiff(
503
503
            fp.read(),
526
526
    """Test registrations of various http implementations"""
527
527
 
528
528
    def test_http_registered(self):
529
 
        t = transport.get_transport('%s://foo.com/' % self._qualified_prefix)
 
529
        t = transport.get_transport('%s://foo.com/' % self._url_protocol)
530
530
        self.assertIsInstance(t, transport.Transport)
531
531
        self.assertIsInstance(t, self._transport)
532
532
 
535
535
 
536
536
    def test_post_body_is_received(self):
537
537
        server = RecordingServer(expect_body_tail='end-of-body',
538
 
            scheme=self._qualified_prefix)
 
538
                                 scheme=self._url_protocol)
539
539
        self.start_server(server)
540
540
        url = server.get_url()
541
 
        http_transport = self._transport(url)
 
541
        # FIXME: needs a cleanup -- vila 20100611
 
542
        http_transport = transport.get_transport(url)
542
543
        code, response = http_transport._post('abc def end-of-body')
543
544
        self.assertTrue(
544
545
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
588
589
    _req_handler_class = http_server.TestingHTTPRequestHandler
589
590
 
590
591
    def create_transport_readonly_server(self):
591
 
        return http_server.HttpServer(self._req_handler_class,
592
 
                                      protocol_version=self._protocol_version)
 
592
        server = http_server.HttpServer(self._req_handler_class,
 
593
                                        protocol_version=self._protocol_version)
 
594
        server._url_protocol = self._url_protocol
 
595
        return server
593
596
 
594
597
    def _testing_pycurl(self):
595
598
        # TODO: This is duplicated for lots of the classes in this file
600
603
class WallRequestHandler(http_server.TestingHTTPRequestHandler):
601
604
    """Whatever request comes in, close the connection"""
602
605
 
603
 
    def handle_one_request(self):
 
606
    def _handle_one_request(self):
604
607
        """Handle a single HTTP request, by abruptly closing the connection"""
605
608
        self.close_connection = 1
606
609
 
611
614
    _req_handler_class = WallRequestHandler
612
615
 
613
616
    def test_http_has(self):
614
 
        server = self.get_readonly_server()
615
 
        t = self._transport(server.get_url())
 
617
        t = self.get_readonly_transport()
616
618
        # Unfortunately httplib (see HTTPResponse._read_status
617
619
        # for details) make no distinction between a closed
618
620
        # socket and badly formatted status line, so we can't
624
626
                          t.has, 'foo/bar')
625
627
 
626
628
    def test_http_get(self):
627
 
        server = self.get_readonly_server()
628
 
        t = self._transport(server.get_url())
 
629
        t = self.get_readonly_transport()
629
630
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
630
631
                           errors.InvalidHttpResponse),
631
632
                          t.get, 'foo/bar')
648
649
    _req_handler_class = BadStatusRequestHandler
649
650
 
650
651
    def test_http_has(self):
651
 
        server = self.get_readonly_server()
652
 
        t = self._transport(server.get_url())
 
652
        t = self.get_readonly_transport()
653
653
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
654
654
 
655
655
    def test_http_get(self):
656
 
        server = self.get_readonly_server()
657
 
        t = self._transport(server.get_url())
 
656
        t = self.get_readonly_transport()
658
657
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
659
658
 
660
659
 
665
664
        """Fakes handling a single HTTP request, returns a bad status"""
666
665
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
667
666
        self.wfile.write("Invalid status line\r\n")
 
667
        # If we don't close the connection pycurl will hang. Since this is a
 
668
        # stress test we don't *have* to respect the protocol, but we don't
 
669
        # have to sabotage it too much either.
 
670
        self.close_connection = True
668
671
        return False
669
672
 
670
673
 
676
679
 
677
680
    _req_handler_class = InvalidStatusRequestHandler
678
681
 
679
 
    def test_http_has(self):
680
 
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
681
 
            raise tests.KnownFailure(
682
 
                'pycurl hangs if the server send back garbage')
683
 
        super(TestInvalidStatusServer, self).test_http_has()
684
 
 
685
 
    def test_http_get(self):
686
 
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
687
 
            raise tests.KnownFailure(
688
 
                'pycurl hangs if the server send back garbage')
689
 
        super(TestInvalidStatusServer, self).test_http_get()
690
 
 
691
682
 
692
683
class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
693
684
    """Whatever request comes in, returns a bad protocol version"""
715
706
        super(TestBadProtocolServer, self).setUp()
716
707
 
717
708
    def test_http_has(self):
718
 
        server = self.get_readonly_server()
719
 
        t = self._transport(server.get_url())
 
709
        t = self.get_readonly_transport()
720
710
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
721
711
 
722
712
    def test_http_get(self):
723
 
        server = self.get_readonly_server()
724
 
        t = self._transport(server.get_url())
 
713
        t = self.get_readonly_transport()
725
714
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
726
715
 
727
716
 
741
730
    _req_handler_class = ForbiddenRequestHandler
742
731
 
743
732
    def test_http_has(self):
744
 
        server = self.get_readonly_server()
745
 
        t = self._transport(server.get_url())
 
733
        t = self.get_readonly_transport()
746
734
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
747
735
 
748
736
    def test_http_get(self):
749
 
        server = self.get_readonly_server()
750
 
        t = self._transport(server.get_url())
 
737
        t = self.get_readonly_transport()
751
738
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
752
739
 
753
740
 
792
779
        self.build_tree_contents([('a', '0123456789')],)
793
780
 
794
781
    def test_readv(self):
795
 
        server = self.get_readonly_server()
796
 
        t = self._transport(server.get_url())
 
782
        t = self.get_readonly_transport()
797
783
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
798
784
        self.assertEqual(l[0], (0, '0'))
799
785
        self.assertEqual(l[1], (1, '1'))
801
787
        self.assertEqual(l[3], (9, '9'))
802
788
 
803
789
    def test_readv_out_of_order(self):
804
 
        server = self.get_readonly_server()
805
 
        t = self._transport(server.get_url())
 
790
        t = self.get_readonly_transport()
806
791
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
807
792
        self.assertEqual(l[0], (1, '1'))
808
793
        self.assertEqual(l[1], (9, '9'))
810
795
        self.assertEqual(l[3], (3, '34'))
811
796
 
812
797
    def test_readv_invalid_ranges(self):
813
 
        server = self.get_readonly_server()
814
 
        t = self._transport(server.get_url())
 
798
        t = self.get_readonly_transport()
815
799
 
816
800
        # This is intentionally reading off the end of the file
817
801
        # since we are sure that it cannot get there
825
809
 
826
810
    def test_readv_multiple_get_requests(self):
827
811
        server = self.get_readonly_server()
828
 
        t = self._transport(server.get_url())
 
812
        t = self.get_readonly_transport()
829
813
        # force transport to issue multiple requests
830
814
        t._max_readv_combine = 1
831
815
        t._max_get_ranges = 1
839
823
 
840
824
    def test_readv_get_max_size(self):
841
825
        server = self.get_readonly_server()
842
 
        t = self._transport(server.get_url())
 
826
        t = self.get_readonly_transport()
843
827
        # force transport to issue multiple requests by limiting the number of
844
828
        # bytes by request. Note that this apply to coalesced offsets only, a
845
829
        # single range will keep its size even if bigger than the limit.
854
838
 
855
839
    def test_complete_readv_leave_pipe_clean(self):
856
840
        server = self.get_readonly_server()
857
 
        t = self._transport(server.get_url())
 
841
        t = self.get_readonly_transport()
858
842
        # force transport to issue multiple requests
859
843
        t._get_max_size = 2
860
844
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
865
849
 
866
850
    def test_incomplete_readv_leave_pipe_clean(self):
867
851
        server = self.get_readonly_server()
868
 
        t = self._transport(server.get_url())
 
852
        t = self.get_readonly_transport()
869
853
        # force transport to issue multiple requests
870
854
        t._get_max_size = 2
871
855
        # Don't collapse readv results into a list so that we leave unread
1021
1005
 
1022
1006
    def test_readv_with_short_reads(self):
1023
1007
        server = self.get_readonly_server()
1024
 
        t = self._transport(server.get_url())
 
1008
        t = self.get_readonly_transport()
1025
1009
        # Force separate ranges for each offset
1026
1010
        t._bytes_to_read_before_seek = 0
1027
1011
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1078
1062
        return LimitedRangeHTTPServer(range_limit=self.range_limit,
1079
1063
                                      protocol_version=self._protocol_version)
1080
1064
 
1081
 
    def get_transport(self):
1082
 
        return self._transport(self.get_readonly_server().get_url())
1083
 
 
1084
1065
    def setUp(self):
1085
1066
        http_utils.TestCaseWithWebserver.setUp(self)
1086
1067
        # We need to manipulate ranges that correspond to real chunks in the
1090
1071
        self.build_tree_contents([('a', content)],)
1091
1072
 
1092
1073
    def test_few_ranges(self):
1093
 
        t = self.get_transport()
 
1074
        t = self.get_readonly_transport()
1094
1075
        l = list(t.readv('a', ((0, 4), (1024, 4), )))
1095
1076
        self.assertEqual(l[0], (0, '0000'))
1096
1077
        self.assertEqual(l[1], (1024, '0001'))
1097
1078
        self.assertEqual(1, self.get_readonly_server().GET_request_nb)
1098
1079
 
1099
1080
    def test_more_ranges(self):
1100
 
        t = self.get_transport()
 
1081
        t = self.get_readonly_transport()
1101
1082
        l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
1102
1083
        self.assertEqual(l[0], (0, '0000'))
1103
1084
        self.assertEqual(l[1], (1024, '0001'))
1117
1098
    def setUp(self):
1118
1099
        tests.TestCase.setUp(self)
1119
1100
        self._old_env = {}
1120
 
 
1121
 
    def tearDown(self):
1122
 
        self._restore_env()
1123
 
        tests.TestCase.tearDown(self)
 
1101
        self.addCleanup(self._restore_env)
1124
1102
 
1125
1103
    def _install_env(self, env):
1126
1104
        for name, value in env.iteritems():
1157
1135
    """
1158
1136
 
1159
1137
    # FIXME: We don't have an https server available, so we don't
1160
 
    # test https connections.
 
1138
    # test https connections. --vila toolongago
1161
1139
 
1162
1140
    def setUp(self):
1163
1141
        super(TestProxyHttpServer, self).setUp()
 
1142
        self.transport_secondary_server = http_utils.ProxyServer
1164
1143
        self.build_tree_contents([('foo', 'contents of foo\n'),
1165
1144
                                  ('foo-proxied', 'proxied contents of foo\n')])
1166
1145
        # Let's setup some attributes for tests
1167
 
        self.server = self.get_readonly_server()
1168
 
        self.proxy_address = '%s:%d' % (self.server.host, self.server.port)
 
1146
        server = self.get_readonly_server()
 
1147
        self.server_host_port = '%s:%d' % (server.host, server.port)
1169
1148
        if self._testing_pycurl():
1170
1149
            # Oh my ! pycurl does not check for the port as part of
1171
1150
            # no_proxy :-( So we just test the host part
1172
 
            self.no_proxy_host = self.server.host
 
1151
            self.no_proxy_host = server.host
1173
1152
        else:
1174
 
            self.no_proxy_host = self.proxy_address
 
1153
            self.no_proxy_host = self.server_host_port
1175
1154
        # The secondary server is the proxy
1176
 
        self.proxy = self.get_secondary_server()
1177
 
        self.proxy_url = self.proxy.get_url()
 
1155
        self.proxy_url = self.get_secondary_url()
1178
1156
        self._old_env = {}
1179
1157
 
1180
1158
    def _testing_pycurl(self):
1182
1160
        return (features.pycurl.available()
1183
1161
                and self._transport == PyCurlTransport)
1184
1162
 
1185
 
    def create_transport_secondary_server(self):
1186
 
        """Creates an http server that will serve files with
1187
 
        '-proxied' appended to their names.
1188
 
        """
1189
 
        return http_utils.ProxyServer(protocol_version=self._protocol_version)
1190
 
 
1191
1163
    def _install_env(self, env):
1192
1164
        for name, value in env.iteritems():
1193
1165
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1198
1170
 
1199
1171
    def proxied_in_env(self, env):
1200
1172
        self._install_env(env)
1201
 
        url = self.server.get_url()
1202
 
        t = self._transport(url)
 
1173
        t = self.get_readonly_transport()
1203
1174
        try:
1204
1175
            self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1205
1176
        finally:
1207
1178
 
1208
1179
    def not_proxied_in_env(self, env):
1209
1180
        self._install_env(env)
1210
 
        url = self.server.get_url()
1211
 
        t = self._transport(url)
 
1181
        t = self.get_readonly_transport()
1212
1182
        try:
1213
1183
            self.assertEqual('contents of foo\n', t.get('foo').read())
1214
1184
        finally:
1256
1226
            # pycurl *ignores* invalid proxy env variables. If that ever change
1257
1227
            # in the future, this test will fail indicating that pycurl do not
1258
1228
            # ignore anymore such variables.
1259
 
            self.not_proxied_in_env({'http_proxy': self.proxy_address})
 
1229
            self.not_proxied_in_env({'http_proxy': self.server_host_port})
1260
1230
        else:
1261
1231
            self.assertRaises(errors.InvalidURL,
1262
1232
                              self.proxied_in_env,
1263
 
                              {'http_proxy': self.proxy_address})
 
1233
                              {'http_proxy': self.server_host_port})
1264
1234
 
1265
1235
 
1266
1236
class TestRanges(http_utils.TestCaseWithWebserver):
1269
1239
    def setUp(self):
1270
1240
        http_utils.TestCaseWithWebserver.setUp(self)
1271
1241
        self.build_tree_contents([('a', '0123456789')],)
1272
 
        server = self.get_readonly_server()
1273
 
        self.transport = self._transport(server.get_url())
1274
1242
 
1275
1243
    def create_transport_readonly_server(self):
1276
1244
        return http_server.HttpServer(protocol_version=self._protocol_version)
1277
1245
 
1278
1246
    def _file_contents(self, relpath, ranges):
 
1247
        t = self.get_readonly_transport()
1279
1248
        offsets = [ (start, end - start + 1) for start, end in ranges]
1280
 
        coalesce = self.transport._coalesce_offsets
 
1249
        coalesce = t._coalesce_offsets
1281
1250
        coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
1282
 
        code, data = self.transport._get(relpath, coalesced)
 
1251
        code, data = t._get(relpath, coalesced)
1283
1252
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1284
1253
        for start, end in ranges:
1285
1254
            data.seek(start)
1286
1255
            yield data.read(end - start + 1)
1287
1256
 
1288
1257
    def _file_tail(self, relpath, tail_amount):
1289
 
        code, data = self.transport._get(relpath, [], tail_amount)
 
1258
        t = self.get_readonly_transport()
 
1259
        code, data = t._get(relpath, [], tail_amount)
1290
1260
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1291
1261
        data.seek(-tail_amount, 2)
1292
1262
        return data.read(tail_amount)
1311
1281
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1312
1282
    """Test redirection between http servers."""
1313
1283
 
1314
 
    def create_transport_secondary_server(self):
1315
 
        """Create the secondary server redirecting to the primary server"""
1316
 
        new = self.get_readonly_server()
1317
 
 
1318
 
        redirecting = http_utils.HTTPServerRedirecting(
1319
 
            protocol_version=self._protocol_version)
1320
 
        redirecting.redirect_to(new.host, new.port)
1321
 
        return redirecting
1322
 
 
1323
1284
    def setUp(self):
1324
1285
        super(TestHTTPRedirections, self).setUp()
1325
1286
        self.build_tree_contents([('a', '0123456789'),
1326
1287
                                  ('bundle',
1327
1288
                                  '# Bazaar revision bundle v0.9\n#\n')
1328
1289
                                  ],)
1329
 
        # The requests to the old server will be redirected to the new server
1330
 
        self.old_transport = self._transport(self.old_server.get_url())
1331
1290
 
1332
1291
    def test_redirected(self):
1333
 
        self.assertRaises(errors.RedirectRequested, self.old_transport.get, 'a')
1334
 
        t = self._transport(self.new_server.get_url())
1335
 
        self.assertEqual('0123456789', t.get('a').read())
1336
 
 
1337
 
    def test_read_redirected_bundle_from_url(self):
1338
 
        from bzrlib.bundle import read_bundle_from_url
1339
 
        url = self.old_transport.abspath('bundle')
1340
 
        bundle = self.applyDeprecated(deprecated_in((1, 12, 0)),
1341
 
                read_bundle_from_url, url)
1342
 
        # If read_bundle_from_url was successful we get an empty bundle
1343
 
        self.assertEqual([], bundle.revisions)
 
1292
        self.assertRaises(errors.RedirectRequested,
 
1293
                          self.get_old_transport().get, 'a')
 
1294
        self.assertEqual('0123456789', self.get_new_transport().get('a').read())
1344
1295
 
1345
1296
 
1346
1297
class RedirectedRequest(_urllib2_wrappers.Request):
1363
1314
    test.overrideAttr(_urllib2_wrappers, 'Request', RedirectedRequest)
1364
1315
 
1365
1316
 
 
1317
def cleanup_http_redirection_connections(test):
 
1318
    # Some sockets are opened but never seen by _urllib, so we trap them at
 
1319
    # the _urllib2_wrappers level to be able to clean them up.
 
1320
    def socket_disconnect(sock):
 
1321
        try:
 
1322
            sock.shutdown(socket.SHUT_RDWR)
 
1323
            sock.close()
 
1324
        except socket.error:
 
1325
            pass
 
1326
    def connect(connection):
 
1327
        test.http_connect_orig(connection)
 
1328
        test.addCleanup(socket_disconnect, connection.sock)
 
1329
    test.http_connect_orig = test.overrideAttr(
 
1330
        _urllib2_wrappers.HTTPConnection, 'connect', connect)
 
1331
    def connect(connection):
 
1332
        test.https_connect_orig(connection)
 
1333
        test.addCleanup(socket_disconnect, connection.sock)
 
1334
    test.https_connect_orig = test.overrideAttr(
 
1335
        _urllib2_wrappers.HTTPSConnection, 'connect', connect)
 
1336
 
 
1337
 
1366
1338
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1367
1339
    """Test redirections.
1368
1340
 
1381
1353
        if (features.pycurl.available()
1382
1354
            and self._transport == PyCurlTransport):
1383
1355
            raise tests.TestNotApplicable(
1384
 
                "pycurl doesn't redirect silently annymore")
 
1356
                "pycurl doesn't redirect silently anymore")
1385
1357
        super(TestHTTPSilentRedirections, self).setUp()
1386
1358
        install_redirected_request(self)
 
1359
        cleanup_http_redirection_connections(self)
1387
1360
        self.build_tree_contents([('a','a'),
1388
1361
                                  ('1/',),
1389
1362
                                  ('1/a', 'redirected once'),
1397
1370
                                  ('5/a', 'redirected 5 times'),
1398
1371
                                  ],)
1399
1372
 
1400
 
        self.old_transport = self._transport(self.old_server.get_url())
1401
 
 
1402
 
    def create_transport_secondary_server(self):
1403
 
        """Create the secondary server, redirections are defined in the tests"""
1404
 
        return http_utils.HTTPServerRedirecting(
1405
 
            protocol_version=self._protocol_version)
1406
 
 
1407
1373
    def test_one_redirection(self):
1408
 
        t = self.old_transport
1409
 
 
1410
 
        req = RedirectedRequest('GET', t.abspath('a'))
 
1374
        t = self.get_old_transport()
 
1375
        req = RedirectedRequest('GET', t._remote_path('a'))
1411
1376
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1412
1377
                                       self.new_server.port)
1413
1378
        self.old_server.redirections = \
1414
1379
            [('(.*)', r'%s/1\1' % (new_prefix), 301),]
1415
 
        self.assertEqual('redirected once',t._perform(req).read())
 
1380
        self.assertEqual('redirected once', t._perform(req).read())
1416
1381
 
1417
1382
    def test_five_redirections(self):
1418
 
        t = self.old_transport
1419
 
 
1420
 
        req = RedirectedRequest('GET', t.abspath('a'))
 
1383
        t = self.get_old_transport()
 
1384
        req = RedirectedRequest('GET', t._remote_path('a'))
1421
1385
        old_prefix = 'http://%s:%s' % (self.old_server.host,
1422
1386
                                       self.old_server.port)
1423
1387
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1429
1393
            ('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1430
1394
            ('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1431
1395
            ]
1432
 
        self.assertEqual('redirected 5 times',t._perform(req).read())
 
1396
        self.assertEqual('redirected 5 times', t._perform(req).read())
1433
1397
 
1434
1398
 
1435
1399
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1438
1402
    def setUp(self):
1439
1403
        super(TestDoCatchRedirections, self).setUp()
1440
1404
        self.build_tree_contents([('a', '0123456789'),],)
1441
 
 
1442
 
        self.old_transport = self._transport(self.old_server.get_url())
1443
 
 
1444
 
    def get_a(self, transport):
1445
 
        return transport.get('a')
 
1405
        cleanup_http_redirection_connections(self)
 
1406
 
 
1407
        self.old_transport = self.get_old_transport()
 
1408
 
 
1409
    def get_a(self, t):
 
1410
        return t.get('a')
1446
1411
 
1447
1412
    def test_no_redirection(self):
1448
 
        t = self._transport(self.new_server.get_url())
 
1413
        t = self.get_new_transport()
1449
1414
 
1450
1415
        # We use None for redirected so that we fail if redirected
1451
1416
        self.assertEqual('0123456789',
1455
1420
    def test_one_redirection(self):
1456
1421
        self.redirections = 0
1457
1422
 
1458
 
        def redirected(transport, exception, redirection_notice):
 
1423
        def redirected(t, exception, redirection_notice):
1459
1424
            self.redirections += 1
1460
 
            dir, file = urlutils.split(exception.target)
1461
 
            return self._transport(dir)
 
1425
            redirected_t = t._redirected_to(exception.source, exception.target)
 
1426
            return redirected_t
1462
1427
 
1463
1428
        self.assertEqual('0123456789',
1464
1429
                         transport.do_catching_redirections(
1494
1459
                                  ('b', 'contents of b\n'),])
1495
1460
 
1496
1461
    def create_transport_readonly_server(self):
1497
 
        return self._auth_server(protocol_version=self._protocol_version)
 
1462
        server = self._auth_server(protocol_version=self._protocol_version)
 
1463
        server._url_protocol = self._url_protocol
 
1464
        return server
1498
1465
 
1499
1466
    def _testing_pycurl(self):
1500
1467
        # TODO: This is duplicated for lots of the classes in this file
1513
1480
        return url
1514
1481
 
1515
1482
    def get_user_transport(self, user, password):
1516
 
        return self._transport(self.get_user_url(user, password))
 
1483
        t = transport.get_transport(self.get_user_url(user, password))
 
1484
        return t
1517
1485
 
1518
1486
    def test_no_user(self):
1519
1487
        self.server.add_user('joe', 'foo')
1704
1672
 
1705
1673
    def get_user_transport(self, user, password):
1706
1674
        self._install_env({'all_proxy': self.get_user_url(user, password)})
1707
 
        return self._transport(self.server.get_url())
 
1675
        return TestAuth.get_user_transport(self, user, password)
1708
1676
 
1709
1677
    def _install_env(self, env):
1710
1678
        for name, value in env.iteritems():
1737
1705
        self.readfile = StringIO(socket_read_content)
1738
1706
        self.writefile = StringIO()
1739
1707
        self.writefile.close = lambda: None
 
1708
        self.close = lambda: None
1740
1709
 
1741
1710
    def makefile(self, mode='r', bufsize=None):
1742
1711
        if 'r' in mode:
1752
1721
        # We use the VFS layer as part of HTTP tunnelling tests.
1753
1722
        self._captureVar('BZR_NO_SMART_VFS', None)
1754
1723
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
 
1724
        self.http_server = self.get_readonly_server()
1755
1725
 
1756
1726
    def create_transport_readonly_server(self):
1757
 
        return http_utils.HTTPServerWithSmarts(
 
1727
        server = http_utils.HTTPServerWithSmarts(
1758
1728
            protocol_version=self._protocol_version)
 
1729
        server._url_protocol = self._url_protocol
 
1730
        return server
1759
1731
 
1760
1732
    def test_open_bzrdir(self):
1761
1733
        branch = self.make_branch('relpath')
1762
 
        http_server = self.get_readonly_server()
1763
 
        url = http_server.get_url() + 'relpath'
 
1734
        url = self.http_server.get_url() + 'relpath'
1764
1735
        bd = bzrdir.BzrDir.open(url)
 
1736
        self.addCleanup(bd.transport.disconnect)
1765
1737
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1766
1738
 
1767
1739
    def test_bulk_data(self):
1769
1741
        # The 'readv' command in the smart protocol both sends and receives
1770
1742
        # bulk data, so we use that.
1771
1743
        self.build_tree(['data-file'])
1772
 
        http_server = self.get_readonly_server()
1773
 
        http_transport = self._transport(http_server.get_url())
 
1744
        http_transport = transport.get_transport(self.http_server.get_url())
1774
1745
        medium = http_transport.get_smart_medium()
1775
1746
        # Since we provide the medium, the url below will be mostly ignored
1776
1747
        # during the test, as long as the path is '/'.
1784
1755
        post_body = 'hello\n'
1785
1756
        expected_reply_body = 'ok\x012\n'
1786
1757
 
1787
 
        http_server = self.get_readonly_server()
1788
 
        http_transport = self._transport(http_server.get_url())
 
1758
        http_transport = transport.get_transport(self.http_server.get_url())
1789
1759
        medium = http_transport.get_smart_medium()
1790
1760
        response = medium.send_http_smart_request(post_body)
1791
1761
        reply_body = response.read()
1792
1762
        self.assertEqual(expected_reply_body, reply_body)
1793
1763
 
1794
1764
    def test_smart_http_server_post_request_handler(self):
1795
 
        httpd = self.get_readonly_server()._get_httpd()
 
1765
        httpd = self.http_server.server
1796
1766
 
1797
1767
        socket = SampleSocket(
1798
1768
            'POST /.bzr/smart %s \r\n' % self._protocol_version
1830
1800
 
1831
1801
    def test_probe_smart_server(self):
1832
1802
        """Test error handling against server refusing smart requests."""
1833
 
        server = self.get_readonly_server()
1834
 
        t = self._transport(server.get_url())
 
1803
        t = self.get_readonly_transport()
1835
1804
        # No need to build a valid smart request here, the server will not even
1836
1805
        # try to interpret it.
1837
1806
        self.assertRaises(errors.SmartProtocolError,
1838
1807
                          t.get_smart_medium().send_http_smart_request,
1839
1808
                          'whatever')
1840
1809
 
 
1810
 
1841
1811
class Test_redirected_to(tests.TestCase):
1842
1812
 
1843
1813
    def test_redirected_to_subdir(self):
1896
1866
    line.
1897
1867
    """
1898
1868
 
1899
 
    def handle_one_request(self):
 
1869
    def _handle_one_request(self):
1900
1870
        tcs = self.server.test_case_server
1901
1871
        requestline = self.rfile.readline()
1902
1872
        headers = self.MessageClass(self.rfile, 0)
1966
1936
        # We override at class level because constructors may propagate the
1967
1937
        # bound method and render instance overriding ineffective (an
1968
1938
        # alternative would be to define a specific ui factory instead...)
1969
 
        self.orig_report_activity = self._transport._report_activity
1970
 
        self._transport._report_activity = report_activity
1971
 
 
1972
 
    def tearDown(self):
1973
 
        self._transport._report_activity = self.orig_report_activity
1974
 
        self.server.stop_server()
1975
 
        tests.TestCase.tearDown(self)
 
1939
        self.overrideAttr(self._transport, '_report_activity', report_activity)
 
1940
        self.addCleanup(self.server.stop_server)
1976
1941
 
1977
1942
    def get_transport(self):
1978
 
        return self._transport(self.server.get_url())
 
1943
        t = self._transport(self.server.get_url())
 
1944
        # FIXME: Needs cleanup -- vila 20100611
 
1945
        return t
1979
1946
 
1980
1947
    def assertActivitiesMatch(self):
1981
1948
        self.assertEqual(self.server.bytes_read,
2086
2053
'''
2087
2054
        t = self.get_transport()
2088
2055
        # We must send a single line of body bytes, see
2089
 
        # PredefinedRequestHandler.handle_one_request
 
2056
        # PredefinedRequestHandler._handle_one_request
2090
2057
        code, f = t._post('abc def end-of-body\n')
2091
2058
        self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2092
2059
        self.assertActivitiesMatch()
2095
2062
class TestActivity(tests.TestCase, TestActivityMixin):
2096
2063
 
2097
2064
    def setUp(self):
2098
 
        tests.TestCase.setUp(self)
2099
 
        self.server = self._activity_server(self._protocol_version)
2100
 
        self.server.start_server()
2101
 
        self.activities = {}
2102
 
        def report_activity(t, bytes, direction):
2103
 
            count = self.activities.get(direction, 0)
2104
 
            count += bytes
2105
 
            self.activities[direction] = count
2106
 
 
2107
 
        # We override at class level because constructors may propagate the
2108
 
        # bound method and render instance overriding ineffective (an
2109
 
        # alternative would be to define a specific ui factory instead...)
2110
 
        self.orig_report_activity = self._transport._report_activity
2111
 
        self._transport._report_activity = report_activity
2112
 
 
2113
 
    def tearDown(self):
2114
 
        self._transport._report_activity = self.orig_report_activity
2115
 
        self.server.stop_server()
2116
 
        tests.TestCase.tearDown(self)
 
2065
        TestActivityMixin.setUp(self)
2117
2066
 
2118
2067
 
2119
2068
class TestNoReportActivity(tests.TestCase, TestActivityMixin):
2120
2069
 
 
2070
    # Unlike TestActivity, we are really testing ReportingFileSocket and
 
2071
    # ReportingSocket, so we don't need all the parametrization. Since
 
2072
    # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
 
2073
    # test them through their use by the transport than directly (that's a
 
2074
    # bit less clean but far more simpler and effective).
 
2075
    _activity_server = ActivityHTTPServer
 
2076
    _protocol_version = 'HTTP/1.1'
 
2077
 
2121
2078
    def setUp(self):
2122
 
        tests.TestCase.setUp(self)
2123
 
        # Unlike TestActivity, we are really testing ReportingFileSocket and
2124
 
        # ReportingSocket, so we don't need all the parametrization. Since
2125
 
        # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
2126
 
        # test them through their use by the transport than directly (that's a
2127
 
        # bit less clean but far more simpler and effective).
2128
 
        self.server = ActivityHTTPServer('HTTP/1.1')
2129
 
        self._transport=_urllib.HttpTransport_urllib
2130
 
 
2131
 
        self.server.start_server()
2132
 
 
2133
 
        # We override at class level because constructors may propagate the
2134
 
        # bound method and render instance overriding ineffective (an
2135
 
        # alternative would be to define a specific ui factory instead...)
2136
 
        self.orig_report_activity = self._transport._report_activity
2137
 
        self._transport._report_activity = None
2138
 
 
2139
 
    def tearDown(self):
2140
 
        self._transport._report_activity = self.orig_report_activity
2141
 
        self.server.stop_server()
2142
 
        tests.TestCase.tearDown(self)
 
2079
        self._transport =_urllib.HttpTransport_urllib
 
2080
        TestActivityMixin.setUp(self)
2143
2081
 
2144
2082
    def assertActivitiesMatch(self):
2145
2083
        # Nothing to check here
2155
2093
    _auth_server = http_utils.HTTPBasicAuthServer
2156
2094
    _transport = _urllib.HttpTransport_urllib
2157
2095
 
2158
 
    def create_transport_readonly_server(self):
2159
 
        return self._auth_server()
2160
 
 
2161
 
    def create_transport_secondary_server(self):
2162
 
        """Create the secondary server redirecting to the primary server"""
2163
 
        new = self.get_readonly_server()
2164
 
 
2165
 
        redirecting = http_utils.HTTPServerRedirecting()
2166
 
        redirecting.redirect_to(new.host, new.port)
2167
 
        return redirecting
2168
 
 
2169
2096
    def setUp(self):
2170
2097
        super(TestAuthOnRedirected, self).setUp()
2171
2098
        self.build_tree_contents([('a','a'),
2176
2103
                                       self.new_server.port)
2177
2104
        self.old_server.redirections = [
2178
2105
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2179
 
        self.old_transport = self._transport(self.old_server.get_url())
 
2106
        self.old_transport = self.get_old_transport()
2180
2107
        self.new_server.add_user('joe', 'foo')
2181
 
 
2182
 
    def get_a(self, transport):
2183
 
        return transport.get('a')
 
2108
        cleanup_http_redirection_connections(self)
 
2109
 
 
2110
    def create_transport_readonly_server(self):
 
2111
        server = self._auth_server(protocol_version=self._protocol_version)
 
2112
        server._url_protocol = self._url_protocol
 
2113
        return server
 
2114
 
 
2115
    def get_a(self, t):
 
2116
        return t.get('a')
2184
2117
 
2185
2118
    def test_auth_on_redirected_via_do_catching_redirections(self):
2186
2119
        self.redirections = 0
2187
2120
 
2188
 
        def redirected(transport, exception, redirection_notice):
 
2121
        def redirected(t, exception, redirection_notice):
2189
2122
            self.redirections += 1
2190
 
            dir, file = urlutils.split(exception.target)
2191
 
            return self._transport(dir)
 
2123
            redirected_t = t._redirected_to(exception.source, exception.target)
 
2124
            self.addCleanup(redirected_t.disconnect)
 
2125
            return redirected_t
2192
2126
 
2193
2127
        stdout = tests.StringIOWrapper()
2194
2128
        stderr = tests.StringIOWrapper()
2215
2149
                                       self.new_server.port)
2216
2150
        self.old_server.redirections = [
2217
2151
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2218
 
        self.assertEqual('redirected once',t._perform(req).read())
 
2152
        self.assertEqual('redirected once', t._perform(req).read())
2219
2153
        # stdin should be empty
2220
2154
        self.assertEqual('', ui.ui_factory.stdin.readline())
2221
2155
        # stdout should be empty, stderr will contains the prompts