~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Andrew Starr-Bochicchio
  • Date: 2015-07-31 01:04:41 UTC
  • mto: This revision was merged to the branch mainline in revision 6606.
  • Revision ID: a.starr.b@gmail.com-20150731010441-3domwjjtnjijxlr2
Use hexlify() from binascii directly as paramiko removed hexify().

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
# TODO: Should be renamed to bzrlib.transport.http.tests?
24
24
# TODO: What about renaming to bzrlib.tests.transport.http ?
25
25
 
26
 
from cStringIO import StringIO
27
26
import httplib
28
 
import os
29
 
import select
30
27
import SimpleHTTPServer
31
28
import socket
32
29
import sys
34
31
 
35
32
import bzrlib
36
33
from bzrlib import (
37
 
    bzrdir,
38
34
    config,
 
35
    controldir,
 
36
    debug,
39
37
    errors,
40
38
    osutils,
41
39
    remote as _mod_remote,
42
40
    tests,
 
41
    trace,
43
42
    transport,
44
43
    ui,
45
 
    urlutils,
46
44
    )
47
45
from bzrlib.tests import (
48
46
    features,
50
48
    http_utils,
51
49
    test_server,
52
50
    )
 
51
from bzrlib.tests.scenarios import (
 
52
    load_tests_apply_scenarios,
 
53
    multiply_scenarios,
 
54
    )
53
55
from bzrlib.transport import (
54
56
    http,
55
57
    remote,
64
66
    from bzrlib.transport.http._pycurl import PyCurlTransport
65
67
 
66
68
 
67
 
def load_tests(standard_tests, module, loader):
68
 
    """Multiply tests for http clients and protocol versions."""
69
 
    result = loader.suiteClass()
70
 
 
71
 
    # one for each transport implementation
72
 
    t_tests, remaining_tests = tests.split_suite_by_condition(
73
 
        standard_tests, tests.condition_isinstance((
74
 
                TestHttpTransportRegistration,
75
 
                TestHttpTransportUrls,
76
 
                Test_redirected_to,
77
 
                )))
 
69
load_tests = load_tests_apply_scenarios
 
70
 
 
71
 
 
72
def vary_by_http_client_implementation():
 
73
    """Test the two libraries we can use, pycurl and urllib."""
78
74
    transport_scenarios = [
79
75
        ('urllib', dict(_transport=_urllib.HttpTransport_urllib,
80
76
                        _server=http_server.HttpServer_urllib,
85
81
            ('pycurl', dict(_transport=PyCurlTransport,
86
82
                            _server=http_server.HttpServer_PyCurl,
87
83
                            _url_protocol='http+pycurl',)))
88
 
    tests.multiply_tests(t_tests, transport_scenarios, result)
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
 
 
102
 
    # each implementation tested with each HTTP version
103
 
    tp_tests, remaining_tests = tests.split_suite_by_condition(
104
 
        remaining_tests, tests.condition_isinstance((
105
 
                SmartHTTPTunnellingTest,
106
 
                TestDoCatchRedirections,
107
 
                TestHTTPConnections,
108
 
                TestHTTPRedirections,
109
 
                TestHTTPSilentRedirections,
110
 
                TestLimitedRangeRequestServer,
111
 
                TestPost,
112
 
                TestProxyHttpServer,
113
 
                TestRanges,
114
 
                TestSpecificRequestHandler,
115
 
                )))
116
 
    tp_scenarios = tests.multiply_scenarios(transport_scenarios,
117
 
                                            protocol_scenarios)
118
 
    tests.multiply_tests(tp_tests, tp_scenarios, result)
119
 
 
120
 
    # proxy auth: each auth scheme on all http versions on all implementations.
121
 
    tppa_tests, remaining_tests = tests.split_suite_by_condition(
122
 
        remaining_tests, tests.condition_isinstance((
123
 
                TestProxyAuth,
124
 
                )))
125
 
    proxy_auth_scheme_scenarios = [
126
 
        ('basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
127
 
        ('digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
128
 
        ('basicdigest',
129
 
         dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
 
84
    return transport_scenarios
 
85
 
 
86
 
 
87
def vary_by_http_protocol_version():
 
88
    """Test on http/1.0 and 1.1"""
 
89
    return [
 
90
        ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
 
91
        ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
130
92
        ]
131
 
    tppa_scenarios = tests.multiply_scenarios(tp_scenarios,
132
 
                                              proxy_auth_scheme_scenarios)
133
 
    tests.multiply_tests(tppa_tests, tppa_scenarios, result)
134
 
 
135
 
    # auth: each auth scheme on all http versions on all implementations.
136
 
    tpa_tests, remaining_tests = tests.split_suite_by_condition(
137
 
        remaining_tests, tests.condition_isinstance((
138
 
                TestAuth,
139
 
                )))
140
 
    auth_scheme_scenarios = [
 
93
 
 
94
 
 
95
def vary_by_http_auth_scheme():
 
96
    scenarios = [
141
97
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
142
98
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
143
99
        ('basicdigest',
144
 
         dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
145
 
        ]
146
 
    tpa_scenarios = tests.multiply_scenarios(tp_scenarios,
147
 
                                             auth_scheme_scenarios)
148
 
    tests.multiply_tests(tpa_tests, tpa_scenarios, result)
149
 
 
150
 
    # activity: on all http[s] versions on all implementations
151
 
    tpact_tests, remaining_tests = tests.split_suite_by_condition(
152
 
        remaining_tests, tests.condition_isinstance((
153
 
                TestActivity,
154
 
                )))
 
100
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
 
101
        ]
 
102
    # Add some attributes common to all scenarios
 
103
    for scenario_id, scenario_dict in scenarios:
 
104
        scenario_dict.update(_auth_header='Authorization',
 
105
                             _username_prompt_prefix='',
 
106
                             _password_prompt_prefix='')
 
107
    return scenarios
 
108
 
 
109
 
 
110
def vary_by_http_proxy_auth_scheme():
 
111
    scenarios = [
 
112
        ('proxy-basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
 
113
        ('proxy-digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
 
114
        ('proxy-basicdigest',
 
115
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
 
116
        ]
 
117
    # Add some attributes common to all scenarios
 
118
    for scenario_id, scenario_dict in scenarios:
 
119
        scenario_dict.update(_auth_header='Proxy-Authorization',
 
120
                             _username_prompt_prefix='Proxy ',
 
121
                             _password_prompt_prefix='Proxy ')
 
122
    return scenarios
 
123
 
 
124
 
 
125
def vary_by_http_activity():
155
126
    activity_scenarios = [
156
127
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
157
 
                             _transport=_urllib.HttpTransport_urllib,)),
 
128
                            _transport=_urllib.HttpTransport_urllib,)),
158
129
        ]
159
 
    if tests.HTTPSServerFeature.available():
 
130
    if features.pycurl.available():
 
131
        activity_scenarios.append(
 
132
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
 
133
                                _transport=PyCurlTransport,)),)
 
134
    if features.HTTPSServerFeature.available():
 
135
        # FIXME: Until we have a better way to handle self-signed certificates
 
136
        # (like allowing them in a test specific authentication.conf for
 
137
        # example), we need some specialized pycurl/urllib transport for tests.
 
138
        # -- vila 2012-01-20
 
139
        from bzrlib.tests import (
 
140
            ssl_certs,
 
141
            )
 
142
        class HTTPS_urllib_transport(_urllib.HttpTransport_urllib):
 
143
 
 
144
            def __init__(self, base, _from_transport=None):
 
145
                super(HTTPS_urllib_transport, self).__init__(
 
146
                    base, _from_transport=_from_transport,
 
147
                    ca_certs=ssl_certs.build_path('ca.crt'))
 
148
 
160
149
        activity_scenarios.append(
161
150
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
162
 
                                  _transport=_urllib.HttpTransport_urllib,)),)
163
 
    if features.pycurl.available():
164
 
        activity_scenarios.append(
165
 
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
166
 
                                 _transport=PyCurlTransport,)),)
167
 
        if tests.HTTPSServerFeature.available():
168
 
            from bzrlib.tests import (
169
 
                ssl_certs,
170
 
                )
171
 
            # FIXME: Until we have a better way to handle self-signed
172
 
            # certificates (like allowing them in a test specific
173
 
            # authentication.conf for example), we need some specialized pycurl
174
 
            # transport for tests.
 
151
                                  _transport=HTTPS_urllib_transport,)),)
 
152
        if features.pycurl.available():
175
153
            class HTTPS_pycurl_transport(PyCurlTransport):
176
154
 
177
155
                def __init__(self, base, _from_transport=None):
181
159
 
182
160
            activity_scenarios.append(
183
161
                ('pycurl,https', dict(_activity_server=ActivityHTTPSServer,
184
 
                                      _transport=HTTPS_pycurl_transport,)),)
185
 
 
186
 
    tpact_scenarios = tests.multiply_scenarios(activity_scenarios,
187
 
                                               protocol_scenarios)
188
 
    tests.multiply_tests(tpact_tests, tpact_scenarios, result)
189
 
 
190
 
    # No parametrization for the remaining tests
191
 
    result.addTests(remaining_tests)
192
 
 
193
 
    return result
 
162
                                    _transport=HTTPS_pycurl_transport,)),)
 
163
    return activity_scenarios
194
164
 
195
165
 
196
166
class FakeManager(object):
229
199
        self._sock.bind(('127.0.0.1', 0))
230
200
        self.host, self.port = self._sock.getsockname()
231
201
        self._ready = threading.Event()
232
 
        self._thread = test_server.ThreadWithException(
233
 
            event=self._ready, target=self._accept_read_and_reply)
 
202
        self._thread = test_server.TestThread(
 
203
            sync_event=self._ready, target=self._accept_read_and_reply)
234
204
        self._thread.start()
235
205
        if 'threads' in tests.selftest_debug_flags:
236
206
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
305
275
        self.assertEqual('realm="Thou should not pass"', remainder)
306
276
 
307
277
 
 
278
class TestHTTPRangeParsing(tests.TestCase):
 
279
 
 
280
    def setUp(self):
 
281
        super(TestHTTPRangeParsing, self).setUp()
 
282
        # We focus on range  parsing here and ignore everything else
 
283
        class RequestHandler(http_server.TestingHTTPRequestHandler):
 
284
            def setup(self): pass
 
285
            def handle(self): pass
 
286
            def finish(self): pass
 
287
 
 
288
        self.req_handler = RequestHandler(None, None, None)
 
289
 
 
290
    def assertRanges(self, ranges, header, file_size):
 
291
        self.assertEquals(ranges,
 
292
                          self.req_handler._parse_ranges(header, file_size))
 
293
 
 
294
    def test_simple_range(self):
 
295
        self.assertRanges([(0,2)], 'bytes=0-2', 12)
 
296
 
 
297
    def test_tail(self):
 
298
        self.assertRanges([(8, 11)], 'bytes=-4', 12)
 
299
 
 
300
    def test_tail_bigger_than_file(self):
 
301
        self.assertRanges([(0, 11)], 'bytes=-99', 12)
 
302
 
 
303
    def test_range_without_end(self):
 
304
        self.assertRanges([(4, 11)], 'bytes=4-', 12)
 
305
 
 
306
    def test_invalid_ranges(self):
 
307
        self.assertRanges(None, 'bytes=12-22', 12)
 
308
        self.assertRanges(None, 'bytes=1-3,12-22', 12)
 
309
        self.assertRanges(None, 'bytes=-', 12)
 
310
 
 
311
 
308
312
class TestHTTPServer(tests.TestCase):
309
313
    """Test the HTTP servers implementations."""
310
314
 
380
384
    _transport = property(_get_pycurl_maybe)
381
385
 
382
386
 
383
 
class TestHttpUrls(tests.TestCase):
384
 
 
385
 
    # TODO: This should be moved to authorization tests once they
386
 
    # are written.
387
 
 
388
 
    def test_url_parsing(self):
389
 
        f = FakeManager()
390
 
        url = http.extract_auth('http://example.com', f)
391
 
        self.assertEqual('http://example.com', url)
392
 
        self.assertEqual(0, len(f.credentials))
393
 
        url = http.extract_auth(
394
 
            'http://user:pass@example.com/bzr/bzr.dev', f)
395
 
        self.assertEqual('http://example.com/bzr/bzr.dev', url)
396
 
        self.assertEqual(1, len(f.credentials))
397
 
        self.assertEqual([None, 'example.com', 'user', 'pass'],
398
 
                         f.credentials[0])
399
 
 
400
 
 
401
387
class TestHttpTransportUrls(tests.TestCase):
402
388
    """Test the http urls."""
403
389
 
 
390
    scenarios = vary_by_http_client_implementation()
 
391
 
404
392
    def test_abs_url(self):
405
393
        """Construction of absolute http URLs"""
406
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
394
        t = self._transport('http://example.com/bzr/bzr.dev/')
407
395
        eq = self.assertEqualDiff
408
 
        eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
409
 
        eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
410
 
        eq(t.abspath('.bzr'), 'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
 
396
        eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
 
397
        eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
 
398
        eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
411
399
        eq(t.abspath('.bzr/1//2/./3'),
412
 
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
 
400
           'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
413
401
 
414
402
    def test_invalid_http_urls(self):
415
403
        """Trap invalid construction of urls"""
416
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
404
        self._transport('http://example.com/bzr/bzr.dev/')
417
405
        self.assertRaises(errors.InvalidURL,
418
406
                          self._transport,
419
 
                          'http://http://bazaar-vcs.org/bzr/bzr.dev/')
 
407
                          'http://http://example.com/bzr/bzr.dev/')
420
408
 
421
409
    def test_http_root_urls(self):
422
410
        """Construction of URLs from server root"""
423
 
        t = self._transport('http://bzr.ozlabs.org/')
 
411
        t = self._transport('http://example.com/')
424
412
        eq = self.assertEqualDiff
425
413
        eq(t.abspath('.bzr/tree-version'),
426
 
           'http://bzr.ozlabs.org/.bzr/tree-version')
 
414
           'http://example.com/.bzr/tree-version')
427
415
 
428
416
    def test_http_impl_urls(self):
429
417
        """There are servers which ask for particular clients to connect"""
475
463
class TestHTTPConnections(http_utils.TestCaseWithWebserver):
476
464
    """Test the http connections."""
477
465
 
 
466
    scenarios = multiply_scenarios(
 
467
        vary_by_http_client_implementation(),
 
468
        vary_by_http_protocol_version(),
 
469
        )
 
470
 
478
471
    def setUp(self):
479
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
472
        super(TestHTTPConnections, self).setUp()
480
473
        self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
481
474
                        transport=self.get_transport())
482
475
 
525
518
class TestHttpTransportRegistration(tests.TestCase):
526
519
    """Test registrations of various http implementations"""
527
520
 
 
521
    scenarios = vary_by_http_client_implementation()
 
522
 
528
523
    def test_http_registered(self):
529
 
        t = transport.get_transport('%s://foo.com/' % self._url_protocol)
 
524
        t = transport.get_transport_from_url(
 
525
            '%s://foo.com/' % self._url_protocol)
530
526
        self.assertIsInstance(t, transport.Transport)
531
527
        self.assertIsInstance(t, self._transport)
532
528
 
533
529
 
534
530
class TestPost(tests.TestCase):
535
531
 
 
532
    scenarios = multiply_scenarios(
 
533
        vary_by_http_client_implementation(),
 
534
        vary_by_http_protocol_version(),
 
535
        )
 
536
 
536
537
    def test_post_body_is_received(self):
537
538
        server = RecordingServer(expect_body_tail='end-of-body',
538
539
                                 scheme=self._url_protocol)
539
540
        self.start_server(server)
540
541
        url = server.get_url()
541
542
        # FIXME: needs a cleanup -- vila 20100611
542
 
        http_transport = transport.get_transport(url)
 
543
        http_transport = transport.get_transport_from_url(url)
543
544
        code, response = http_transport._post('abc def end-of-body')
544
545
        self.assertTrue(
545
546
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
546
547
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
 
548
        self.assertTrue('content-type: application/octet-stream\r'
 
549
                        in server.received_bytes.lower())
547
550
        # The transport should not be assuming that the server can accept
548
551
        # chunked encoding the first time it connects, because HTTP/1.1, so we
549
552
        # check for the literal string.
585
588
    Daughter classes are expected to override _req_handler_class
586
589
    """
587
590
 
 
591
    scenarios = multiply_scenarios(
 
592
        vary_by_http_client_implementation(),
 
593
        vary_by_http_protocol_version(),
 
594
        )
 
595
 
588
596
    # Provide a useful default
589
597
    _req_handler_class = http_server.TestingHTTPRequestHandler
590
598
 
841
849
        t = self.get_readonly_transport()
842
850
        # force transport to issue multiple requests
843
851
        t._get_max_size = 2
844
 
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
 
852
        list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
845
853
        # The server should have issued 3 requests
846
854
        self.assertEqual(3, server.GET_request_nb)
847
855
        self.assertEqual('0123456789', t.get_bytes('a'))
924
932
    def get_multiple_ranges(self, file, file_size, ranges):
925
933
        self.send_response(206)
926
934
        self.send_header('Accept-Ranges', 'bytes')
 
935
        # XXX: this is strange; the 'random' name below seems undefined and
 
936
        # yet the tests pass -- mbp 2010-10-11 bug 658773
927
937
        boundary = "%d" % random.randint(0,0x7FFFFFFF)
928
938
        self.send_header("Content-Type",
929
939
                         "multipart/byteranges; boundary=%s" % boundary)
991
1001
                return
992
1002
            self.send_range_content(file, start, end - start + 1)
993
1003
            cur += 1
994
 
        # No final boundary
 
1004
        # Final boundary
995
1005
        self.wfile.write(boundary_line)
996
1006
 
997
1007
 
1026
1036
        # that mode
1027
1037
        self.assertEqual('single', t._range_hint)
1028
1038
 
 
1039
 
 
1040
class TruncatedBeforeBoundaryRequestHandler(
 
1041
    http_server.TestingHTTPRequestHandler):
 
1042
    """Truncation before a boundary, like in bug 198646"""
 
1043
 
 
1044
    _truncated_ranges = 1
 
1045
 
 
1046
    def get_multiple_ranges(self, file, file_size, ranges):
 
1047
        self.send_response(206)
 
1048
        self.send_header('Accept-Ranges', 'bytes')
 
1049
        boundary = 'tagada'
 
1050
        self.send_header('Content-Type',
 
1051
                         'multipart/byteranges; boundary=%s' % boundary)
 
1052
        boundary_line = '--%s\r\n' % boundary
 
1053
        # Calculate the Content-Length
 
1054
        content_length = 0
 
1055
        for (start, end) in ranges:
 
1056
            content_length += len(boundary_line)
 
1057
            content_length += self._header_line_length(
 
1058
                'Content-type', 'application/octet-stream')
 
1059
            content_length += self._header_line_length(
 
1060
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
 
1061
            content_length += len('\r\n') # end headers
 
1062
            content_length += end - start # + 1
 
1063
        content_length += len(boundary_line)
 
1064
        self.send_header('Content-length', content_length)
 
1065
        self.end_headers()
 
1066
 
 
1067
        # Send the multipart body
 
1068
        cur = 0
 
1069
        for (start, end) in ranges:
 
1070
            if cur + self._truncated_ranges >= len(ranges):
 
1071
                # Abruptly ends the response and close the connection
 
1072
                self.close_connection = 1
 
1073
                return
 
1074
            self.wfile.write(boundary_line)
 
1075
            self.send_header('Content-type', 'application/octet-stream')
 
1076
            self.send_header('Content-Range', 'bytes %d-%d/%d'
 
1077
                             % (start, end, file_size))
 
1078
            self.end_headers()
 
1079
            self.send_range_content(file, start, end - start + 1)
 
1080
            cur += 1
 
1081
        # Final boundary
 
1082
        self.wfile.write(boundary_line)
 
1083
 
 
1084
 
 
1085
class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
 
1086
    """Tests the case of bug 198646, disconnecting before a boundary."""
 
1087
 
 
1088
    _req_handler_class = TruncatedBeforeBoundaryRequestHandler
 
1089
 
 
1090
    def setUp(self):
 
1091
        super(TestTruncatedBeforeBoundary, self).setUp()
 
1092
        self.build_tree_contents([('a', '0123456789')],)
 
1093
 
 
1094
    def test_readv_with_short_reads(self):
 
1095
        server = self.get_readonly_server()
 
1096
        t = self.get_readonly_transport()
 
1097
        # Force separate ranges for each offset
 
1098
        t._bytes_to_read_before_seek = 0
 
1099
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
 
1100
        self.assertEqual((0, '0'), ireadv.next())
 
1101
        self.assertEqual((2, '2'), ireadv.next())
 
1102
        self.assertEqual((4, '45'), ireadv.next())
 
1103
        self.assertEqual((9, '9'), ireadv.next())
 
1104
 
 
1105
 
1029
1106
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1030
1107
    """Errors out when range specifiers exceed the limit"""
1031
1108
 
1055
1132
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1056
1133
    """Tests readv requests against a server erroring out on too much ranges."""
1057
1134
 
 
1135
    scenarios = multiply_scenarios(
 
1136
        vary_by_http_client_implementation(),
 
1137
        vary_by_http_protocol_version(),
 
1138
        )
 
1139
 
1058
1140
    # Requests with more range specifiers will error out
1059
1141
    range_limit = 3
1060
1142
 
1063
1145
                                      protocol_version=self._protocol_version)
1064
1146
 
1065
1147
    def setUp(self):
1066
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
1148
        super(TestLimitedRangeRequestServer, self).setUp()
1067
1149
        # We need to manipulate ranges that correspond to real chunks in the
1068
1150
        # response, so we build a content appropriately.
1069
1151
        filler = ''.join(['abcdefghij' for x in range(102)])
1095
1177
    Only the urllib implementation is tested here.
1096
1178
    """
1097
1179
 
1098
 
    def setUp(self):
1099
 
        tests.TestCase.setUp(self)
1100
 
        self._old_env = {}
1101
 
        self.addCleanup(self._restore_env)
1102
 
 
1103
 
    def _install_env(self, env):
1104
 
        for name, value in env.iteritems():
1105
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1106
 
 
1107
 
    def _restore_env(self):
1108
 
        for name, value in self._old_env.iteritems():
1109
 
            osutils.set_or_unset_env(name, value)
1110
 
 
1111
1180
    def _proxied_request(self):
1112
1181
        handler = _urllib2_wrappers.ProxyHandler()
1113
 
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
 
1182
        request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
1114
1183
        handler.set_proxy(request, 'http')
1115
1184
        return request
1116
1185
 
 
1186
    def assertEvaluateProxyBypass(self, expected, host, no_proxy):
 
1187
        handler = _urllib2_wrappers.ProxyHandler()
 
1188
        self.assertEquals(expected,
 
1189
                          handler.evaluate_proxy_bypass(host, no_proxy))
 
1190
 
1117
1191
    def test_empty_user(self):
1118
 
        self._install_env({'http_proxy': 'http://bar.com'})
 
1192
        self.overrideEnv('http_proxy', 'http://bar.com')
 
1193
        request = self._proxied_request()
 
1194
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
 
1195
 
 
1196
    def test_user_with_at(self):
 
1197
        self.overrideEnv('http_proxy',
 
1198
                         'http://username@domain:password@proxy_host:1234')
1119
1199
        request = self._proxied_request()
1120
1200
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1121
1201
 
1122
1202
    def test_invalid_proxy(self):
1123
1203
        """A proxy env variable without scheme"""
1124
 
        self._install_env({'http_proxy': 'host:1234'})
 
1204
        self.overrideEnv('http_proxy', 'host:1234')
1125
1205
        self.assertRaises(errors.InvalidURL, self._proxied_request)
1126
1206
 
 
1207
    def test_evaluate_proxy_bypass_true(self):
 
1208
        """The host is not proxied"""
 
1209
        self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
 
1210
        self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
 
1211
 
 
1212
    def test_evaluate_proxy_bypass_false(self):
 
1213
        """The host is proxied"""
 
1214
        self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
 
1215
 
 
1216
    def test_evaluate_proxy_bypass_unknown(self):
 
1217
        """The host is not explicitly proxied"""
 
1218
        self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
 
1219
        self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
 
1220
 
 
1221
    def test_evaluate_proxy_bypass_empty_entries(self):
 
1222
        """Ignore empty entries"""
 
1223
        self.assertEvaluateProxyBypass(None, 'example.com', '')
 
1224
        self.assertEvaluateProxyBypass(None, 'example.com', ',')
 
1225
        self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
 
1226
 
1127
1227
 
1128
1228
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
1129
1229
    """Tests proxy server.
1134
1234
    to the file names).
1135
1235
    """
1136
1236
 
 
1237
    scenarios = multiply_scenarios(
 
1238
        vary_by_http_client_implementation(),
 
1239
        vary_by_http_protocol_version(),
 
1240
        )
 
1241
 
1137
1242
    # FIXME: We don't have an https server available, so we don't
1138
1243
    # test https connections. --vila toolongago
1139
1244
 
1153
1258
            self.no_proxy_host = self.server_host_port
1154
1259
        # The secondary server is the proxy
1155
1260
        self.proxy_url = self.get_secondary_url()
1156
 
        self._old_env = {}
1157
1261
 
1158
1262
    def _testing_pycurl(self):
1159
1263
        # TODO: This is duplicated for lots of the classes in this file
1160
1264
        return (features.pycurl.available()
1161
1265
                and self._transport == PyCurlTransport)
1162
1266
 
1163
 
    def _install_env(self, env):
1164
 
        for name, value in env.iteritems():
1165
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1166
 
 
1167
 
    def _restore_env(self):
1168
 
        for name, value in self._old_env.iteritems():
1169
 
            osutils.set_or_unset_env(name, value)
1170
 
 
1171
 
    def proxied_in_env(self, env):
1172
 
        self._install_env(env)
1173
 
        t = self.get_readonly_transport()
1174
 
        try:
1175
 
            self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1176
 
        finally:
1177
 
            self._restore_env()
1178
 
 
1179
 
    def not_proxied_in_env(self, env):
1180
 
        self._install_env(env)
1181
 
        t = self.get_readonly_transport()
1182
 
        try:
1183
 
            self.assertEqual('contents of foo\n', t.get('foo').read())
1184
 
        finally:
1185
 
            self._restore_env()
 
1267
    def assertProxied(self):
 
1268
        t = self.get_readonly_transport()
 
1269
        self.assertEqual('proxied contents of foo\n', t.get('foo').read())
 
1270
 
 
1271
    def assertNotProxied(self):
 
1272
        t = self.get_readonly_transport()
 
1273
        self.assertEqual('contents of foo\n', t.get('foo').read())
1186
1274
 
1187
1275
    def test_http_proxy(self):
1188
 
        self.proxied_in_env({'http_proxy': self.proxy_url})
 
1276
        self.overrideEnv('http_proxy', self.proxy_url)
 
1277
        self.assertProxied()
1189
1278
 
1190
1279
    def test_HTTP_PROXY(self):
1191
1280
        if self._testing_pycurl():
1194
1283
            # about. Should we ?)
1195
1284
            raise tests.TestNotApplicable(
1196
1285
                'pycurl does not check HTTP_PROXY for security reasons')
1197
 
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
 
1286
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1287
        self.assertProxied()
1198
1288
 
1199
1289
    def test_all_proxy(self):
1200
 
        self.proxied_in_env({'all_proxy': self.proxy_url})
 
1290
        self.overrideEnv('all_proxy', self.proxy_url)
 
1291
        self.assertProxied()
1201
1292
 
1202
1293
    def test_ALL_PROXY(self):
1203
 
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
 
1294
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1295
        self.assertProxied()
1204
1296
 
1205
1297
    def test_http_proxy_with_no_proxy(self):
1206
 
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
1207
 
                                 'no_proxy': self.no_proxy_host})
 
1298
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1299
        self.overrideEnv('http_proxy', self.proxy_url)
 
1300
        self.assertNotProxied()
1208
1301
 
1209
1302
    def test_HTTP_PROXY_with_NO_PROXY(self):
1210
1303
        if self._testing_pycurl():
1211
1304
            raise tests.TestNotApplicable(
1212
1305
                'pycurl does not check HTTP_PROXY for security reasons')
1213
 
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
1214
 
                                 'NO_PROXY': self.no_proxy_host})
 
1306
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1307
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1308
        self.assertNotProxied()
1215
1309
 
1216
1310
    def test_all_proxy_with_no_proxy(self):
1217
 
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
1218
 
                                 'no_proxy': self.no_proxy_host})
 
1311
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1312
        self.overrideEnv('all_proxy', self.proxy_url)
 
1313
        self.assertNotProxied()
1219
1314
 
1220
1315
    def test_ALL_PROXY_with_NO_PROXY(self):
1221
 
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
1222
 
                                 'NO_PROXY': self.no_proxy_host})
 
1316
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1317
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1318
        self.assertNotProxied()
1223
1319
 
1224
1320
    def test_http_proxy_without_scheme(self):
 
1321
        self.overrideEnv('http_proxy', self.server_host_port)
1225
1322
        if self._testing_pycurl():
1226
1323
            # pycurl *ignores* invalid proxy env variables. If that ever change
1227
1324
            # in the future, this test will fail indicating that pycurl do not
1228
1325
            # ignore anymore such variables.
1229
 
            self.not_proxied_in_env({'http_proxy': self.server_host_port})
 
1326
            self.assertNotProxied()
1230
1327
        else:
1231
 
            self.assertRaises(errors.InvalidURL,
1232
 
                              self.proxied_in_env,
1233
 
                              {'http_proxy': self.server_host_port})
 
1328
            self.assertRaises(errors.InvalidURL, self.assertProxied)
1234
1329
 
1235
1330
 
1236
1331
class TestRanges(http_utils.TestCaseWithWebserver):
1237
1332
    """Test the Range header in GET methods."""
1238
1333
 
 
1334
    scenarios = multiply_scenarios(
 
1335
        vary_by_http_client_implementation(),
 
1336
        vary_by_http_protocol_version(),
 
1337
        )
 
1338
 
1239
1339
    def setUp(self):
1240
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
1340
        super(TestRanges, self).setUp()
1241
1341
        self.build_tree_contents([('a', '0123456789')],)
1242
1342
 
1243
1343
    def create_transport_readonly_server(self):
1281
1381
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1282
1382
    """Test redirection between http servers."""
1283
1383
 
 
1384
    scenarios = multiply_scenarios(
 
1385
        vary_by_http_client_implementation(),
 
1386
        vary_by_http_protocol_version(),
 
1387
        )
 
1388
 
1284
1389
    def setUp(self):
1285
1390
        super(TestHTTPRedirections, self).setUp()
1286
1391
        self.build_tree_contents([('a', '0123456789'),
1349
1454
    -- vila 20070212
1350
1455
    """
1351
1456
 
 
1457
    scenarios = multiply_scenarios(
 
1458
        vary_by_http_client_implementation(),
 
1459
        vary_by_http_protocol_version(),
 
1460
        )
 
1461
 
1352
1462
    def setUp(self):
1353
1463
        if (features.pycurl.available()
1354
1464
            and self._transport == PyCurlTransport):
1399
1509
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1400
1510
    """Test transport.do_catching_redirections."""
1401
1511
 
 
1512
    scenarios = multiply_scenarios(
 
1513
        vary_by_http_client_implementation(),
 
1514
        vary_by_http_protocol_version(),
 
1515
        )
 
1516
 
1402
1517
    def setUp(self):
1403
1518
        super(TestDoCatchRedirections, self).setUp()
1404
1519
        self.build_tree_contents([('a', '0123456789'),],)
1443
1558
                          self.get_a, self.old_transport, redirected)
1444
1559
 
1445
1560
 
 
1561
def _setup_authentication_config(**kwargs):
 
1562
    conf = config.AuthenticationConfig()
 
1563
    conf._get_config().update({'httptest': kwargs})
 
1564
    conf._save()
 
1565
 
 
1566
 
 
1567
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
 
1568
    """Unit tests for glue by which urllib2 asks us for authentication"""
 
1569
 
 
1570
    def test_get_user_password_without_port(self):
 
1571
        """We cope if urllib2 doesn't tell us the port.
 
1572
 
 
1573
        See https://bugs.launchpad.net/bzr/+bug/654684
 
1574
        """
 
1575
        user = 'joe'
 
1576
        password = 'foo'
 
1577
        _setup_authentication_config(scheme='http', host='localhost',
 
1578
                                     user=user, password=password)
 
1579
        handler = _urllib2_wrappers.HTTPAuthHandler()
 
1580
        got_pass = handler.get_user_password(dict(
 
1581
            user='joe',
 
1582
            protocol='http',
 
1583
            host='localhost',
 
1584
            path='/',
 
1585
            realm='Realm',
 
1586
            ))
 
1587
        self.assertEquals((user, password), got_pass)
 
1588
 
 
1589
 
1446
1590
class TestAuth(http_utils.TestCaseWithWebserver):
1447
1591
    """Test authentication scheme"""
1448
1592
 
1449
 
    _auth_header = 'Authorization'
1450
 
    _password_prompt_prefix = ''
1451
 
    _username_prompt_prefix = ''
1452
 
    # Set by load_tests
1453
 
    _auth_server = None
 
1593
    scenarios = multiply_scenarios(
 
1594
        vary_by_http_client_implementation(),
 
1595
        vary_by_http_protocol_version(),
 
1596
        vary_by_http_auth_scheme(),
 
1597
        )
1454
1598
 
1455
1599
    def setUp(self):
1456
1600
        super(TestAuth, self).setUp()
1480
1624
        return url
1481
1625
 
1482
1626
    def get_user_transport(self, user, password):
1483
 
        t = transport.get_transport(self.get_user_url(user, password))
 
1627
        t = transport.get_transport_from_url(
 
1628
            self.get_user_url(user, password))
1484
1629
        return t
1485
1630
 
1486
1631
    def test_no_user(self):
1598
1743
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1599
1744
                                            stderr=tests.StringIOWrapper())
1600
1745
        # Create a minimal config file with the right password
1601
 
        conf = config.AuthenticationConfig()
1602
 
        conf._get_config().update(
1603
 
            {'httptest': {'scheme': 'http', 'port': self.server.port,
1604
 
                          'user': user, 'password': password}})
1605
 
        conf._save()
 
1746
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1747
                                     user=user, password=password)
1606
1748
        # Issue a request to the server to connect
1607
1749
        self.assertEqual('contents of a\n',t.get('a').read())
1608
1750
        # stdin should have  been left untouched
1610
1752
        # Only one 'Authentication Required' error should occur
1611
1753
        self.assertEqual(1, self.server.auth_required_errors)
1612
1754
 
1613
 
    def test_user_from_auth_conf(self):
1614
 
        if self._testing_pycurl():
1615
 
            raise tests.TestNotApplicable(
1616
 
                'pycurl does not support authentication.conf')
1617
 
        user = 'joe'
1618
 
        password = 'foo'
1619
 
        self.server.add_user(user, password)
1620
 
        # Create a minimal config file with the right password
1621
 
        conf = config.AuthenticationConfig()
1622
 
        conf._get_config().update(
1623
 
            {'httptest': {'scheme': 'http', 'port': self.server.port,
1624
 
                          'user': user, 'password': password}})
1625
 
        conf._save()
1626
 
        t = self.get_user_transport(None, None)
1627
 
        # Issue a request to the server to connect
1628
 
        self.assertEqual('contents of a\n', t.get('a').read())
1629
 
        # Only one 'Authentication Required' error should occur
1630
 
        self.assertEqual(1, self.server.auth_required_errors)
1631
 
 
1632
1755
    def test_changing_nonce(self):
1633
1756
        if self._auth_server not in (http_utils.HTTPDigestAuthServer,
1634
1757
                                     http_utils.ProxyDigestAuthServer):
1635
1758
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1636
1759
        if self._testing_pycurl():
1637
 
            raise tests.KnownFailure(
 
1760
            self.knownFailure(
1638
1761
                'pycurl does not handle a nonce change')
1639
1762
        self.server.add_user('joe', 'foo')
1640
1763
        t = self.get_user_transport('joe', 'foo')
1650
1773
        # initial 'who are you' and a second 'who are you' with the new nonce)
1651
1774
        self.assertEqual(2, self.server.auth_required_errors)
1652
1775
 
 
1776
    def test_user_from_auth_conf(self):
 
1777
        if self._testing_pycurl():
 
1778
            raise tests.TestNotApplicable(
 
1779
                'pycurl does not support authentication.conf')
 
1780
        user = 'joe'
 
1781
        password = 'foo'
 
1782
        self.server.add_user(user, password)
 
1783
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1784
                                     user=user, password=password)
 
1785
        t = self.get_user_transport(None, None)
 
1786
        # Issue a request to the server to connect
 
1787
        self.assertEqual('contents of a\n', t.get('a').read())
 
1788
        # Only one 'Authentication Required' error should occur
 
1789
        self.assertEqual(1, self.server.auth_required_errors)
 
1790
 
 
1791
    def test_no_credential_leaks_in_log(self):
 
1792
        self.overrideAttr(debug, 'debug_flags', set(['http']))
 
1793
        user = 'joe'
 
1794
        password = 'very-sensitive-password'
 
1795
        self.server.add_user(user, password)
 
1796
        t = self.get_user_transport(user, password)
 
1797
        # Capture the debug calls to mutter
 
1798
        self.mutters = []
 
1799
        def mutter(*args):
 
1800
            lines = args[0] % args[1:]
 
1801
            # Some calls output multiple lines, just split them now since we
 
1802
            # care about a single one later.
 
1803
            self.mutters.extend(lines.splitlines())
 
1804
        self.overrideAttr(trace, 'mutter', mutter)
 
1805
        # Issue a request to the server to connect
 
1806
        self.assertEqual(True, t.has('a'))
 
1807
        # Only one 'Authentication Required' error should occur
 
1808
        self.assertEqual(1, self.server.auth_required_errors)
 
1809
        # Since the authentification succeeded, there should be a corresponding
 
1810
        # debug line
 
1811
        sent_auth_headers = [line for line in self.mutters
 
1812
                             if line.startswith('> %s' % (self._auth_header,))]
 
1813
        self.assertLength(1, sent_auth_headers)
 
1814
        self.assertStartsWith(sent_auth_headers[0],
 
1815
                              '> %s: <masked>' % (self._auth_header,))
1653
1816
 
1654
1817
 
1655
1818
class TestProxyAuth(TestAuth):
1656
 
    """Test proxy authentication schemes."""
1657
 
 
1658
 
    _auth_header = 'Proxy-authorization'
1659
 
    _password_prompt_prefix = 'Proxy '
1660
 
    _username_prompt_prefix = 'Proxy '
 
1819
    """Test proxy authentication schemes.
 
1820
 
 
1821
    This inherits from TestAuth to tweak the setUp and filter some failing
 
1822
    tests.
 
1823
    """
 
1824
 
 
1825
    scenarios = multiply_scenarios(
 
1826
        vary_by_http_client_implementation(),
 
1827
        vary_by_http_protocol_version(),
 
1828
        vary_by_http_proxy_auth_scheme(),
 
1829
        )
1661
1830
 
1662
1831
    def setUp(self):
1663
1832
        super(TestProxyAuth, self).setUp()
1664
 
        self._old_env = {}
1665
 
        self.addCleanup(self._restore_env)
1666
1833
        # Override the contents to avoid false positives
1667
1834
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1668
1835
                                  ('b', 'not proxied contents of b\n'),
1671
1838
                                  ])
1672
1839
 
1673
1840
    def get_user_transport(self, user, password):
1674
 
        self._install_env({'all_proxy': self.get_user_url(user, password)})
 
1841
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
1675
1842
        return TestAuth.get_user_transport(self, user, password)
1676
1843
 
1677
 
    def _install_env(self, env):
1678
 
        for name, value in env.iteritems():
1679
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1680
 
 
1681
 
    def _restore_env(self):
1682
 
        for name, value in self._old_env.iteritems():
1683
 
            osutils.set_or_unset_env(name, value)
1684
 
 
1685
1844
    def test_empty_pass(self):
1686
1845
        if self._testing_pycurl():
1687
1846
            import pycurl
1688
1847
            if pycurl.version_info()[1] < '7.16.0':
1689
 
                raise tests.KnownFailure(
 
1848
                self.knownFailure(
1690
1849
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1691
1850
        super(TestProxyAuth, self).test_empty_pass()
1692
1851
 
1716
1875
 
1717
1876
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1718
1877
 
 
1878
    scenarios = multiply_scenarios(
 
1879
        vary_by_http_client_implementation(),
 
1880
        vary_by_http_protocol_version(),
 
1881
        )
 
1882
 
1719
1883
    def setUp(self):
1720
1884
        super(SmartHTTPTunnellingTest, self).setUp()
1721
1885
        # We use the VFS layer as part of HTTP tunnelling tests.
1722
 
        self._captureVar('BZR_NO_SMART_VFS', None)
 
1886
        self.overrideEnv('BZR_NO_SMART_VFS', None)
1723
1887
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1724
1888
        self.http_server = self.get_readonly_server()
1725
1889
 
1729
1893
        server._url_protocol = self._url_protocol
1730
1894
        return server
1731
1895
 
1732
 
    def test_open_bzrdir(self):
 
1896
    def test_open_controldir(self):
1733
1897
        branch = self.make_branch('relpath')
1734
1898
        url = self.http_server.get_url() + 'relpath'
1735
 
        bd = bzrdir.BzrDir.open(url)
 
1899
        bd = controldir.ControlDir.open(url)
1736
1900
        self.addCleanup(bd.transport.disconnect)
1737
1901
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1738
1902
 
1741
1905
        # The 'readv' command in the smart protocol both sends and receives
1742
1906
        # bulk data, so we use that.
1743
1907
        self.build_tree(['data-file'])
1744
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1908
        http_transport = transport.get_transport_from_url(
 
1909
            self.http_server.get_url())
1745
1910
        medium = http_transport.get_smart_medium()
1746
1911
        # Since we provide the medium, the url below will be mostly ignored
1747
1912
        # during the test, as long as the path is '/'.
1755
1920
        post_body = 'hello\n'
1756
1921
        expected_reply_body = 'ok\x012\n'
1757
1922
 
1758
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1923
        http_transport = transport.get_transport_from_url(
 
1924
            self.http_server.get_url())
1759
1925
        medium = http_transport.get_smart_medium()
1760
1926
        response = medium.send_http_smart_request(post_body)
1761
1927
        reply_body = response.read()
1810
1976
 
1811
1977
class Test_redirected_to(tests.TestCase):
1812
1978
 
 
1979
    scenarios = vary_by_http_client_implementation()
 
1980
 
1813
1981
    def test_redirected_to_subdir(self):
1814
1982
        t = self._transport('http://www.example.com/foo')
1815
1983
        r = t._redirected_to('http://www.example.com/foo',
1817
1985
        self.assertIsInstance(r, type(t))
1818
1986
        # Both transports share the some connection
1819
1987
        self.assertEqual(t._get_connection(), r._get_connection())
 
1988
        self.assertEquals('http://www.example.com/foo/subdir/', r.base)
1820
1989
 
1821
1990
    def test_redirected_to_self_with_slash(self):
1822
1991
        t = self._transport('http://www.example.com/foo')
1833
2002
        r = t._redirected_to('http://www.example.com/foo',
1834
2003
                             'http://foo.example.com/foo/subdir')
1835
2004
        self.assertIsInstance(r, type(t))
 
2005
        self.assertEquals('http://foo.example.com/foo/subdir/',
 
2006
            r.external_url())
1836
2007
 
1837
2008
    def test_redirected_to_same_host_sibling_protocol(self):
1838
2009
        t = self._transport('http://www.example.com/foo')
1839
2010
        r = t._redirected_to('http://www.example.com/foo',
1840
2011
                             'https://www.example.com/foo')
1841
2012
        self.assertIsInstance(r, type(t))
 
2013
        self.assertEquals('https://www.example.com/foo/',
 
2014
            r.external_url())
1842
2015
 
1843
2016
    def test_redirected_to_same_host_different_protocol(self):
1844
2017
        t = self._transport('http://www.example.com/foo')
1845
2018
        r = t._redirected_to('http://www.example.com/foo',
1846
2019
                             'ftp://www.example.com/foo')
1847
2020
        self.assertNotEquals(type(r), type(t))
 
2021
        self.assertEquals('ftp://www.example.com/foo/', r.external_url())
 
2022
 
 
2023
    def test_redirected_to_same_host_specific_implementation(self):
 
2024
        t = self._transport('http://www.example.com/foo')
 
2025
        r = t._redirected_to('http://www.example.com/foo',
 
2026
                             'https+urllib://www.example.com/foo')
 
2027
        self.assertEquals('https://www.example.com/foo/', r.external_url())
1848
2028
 
1849
2029
    def test_redirected_to_different_host_same_user(self):
1850
2030
        t = self._transport('http://joe@www.example.com/foo')
1851
2031
        r = t._redirected_to('http://www.example.com/foo',
1852
2032
                             'https://foo.example.com/foo')
1853
2033
        self.assertIsInstance(r, type(t))
1854
 
        self.assertEqual(t._user, r._user)
 
2034
        self.assertEqual(t._parsed_url.user, r._parsed_url.user)
 
2035
        self.assertEquals('https://joe@foo.example.com/foo/', r.external_url())
1855
2036
 
1856
2037
 
1857
2038
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
1910
2091
    pass
1911
2092
 
1912
2093
 
1913
 
if tests.HTTPSServerFeature.available():
 
2094
if features.HTTPSServerFeature.available():
1914
2095
    from bzrlib.tests import https_server
1915
2096
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
1916
2097
        pass
1924
2105
    """
1925
2106
 
1926
2107
    def setUp(self):
1927
 
        tests.TestCase.setUp(self)
1928
2108
        self.server = self._activity_server(self._protocol_version)
1929
2109
        self.server.start_server()
1930
 
        self.activities = {}
 
2110
        self.addCleanup(self.server.stop_server)
 
2111
        _activities = {} # Don't close over self and create a cycle
1931
2112
        def report_activity(t, bytes, direction):
1932
 
            count = self.activities.get(direction, 0)
 
2113
            count = _activities.get(direction, 0)
1933
2114
            count += bytes
1934
 
            self.activities[direction] = count
1935
 
 
 
2115
            _activities[direction] = count
 
2116
        self.activities = _activities
1936
2117
        # We override at class level because constructors may propagate the
1937
2118
        # bound method and render instance overriding ineffective (an
1938
2119
        # alternative would be to define a specific ui factory instead...)
1939
2120
        self.overrideAttr(self._transport, '_report_activity', report_activity)
1940
 
        self.addCleanup(self.server.stop_server)
1941
2121
 
1942
2122
    def get_transport(self):
1943
2123
        t = self._transport(self.server.get_url())
2061
2241
 
2062
2242
class TestActivity(tests.TestCase, TestActivityMixin):
2063
2243
 
 
2244
    scenarios = multiply_scenarios(
 
2245
        vary_by_http_activity(),
 
2246
        vary_by_http_protocol_version(),
 
2247
        )
 
2248
 
2064
2249
    def setUp(self):
 
2250
        super(TestActivity, self).setUp()
2065
2251
        TestActivityMixin.setUp(self)
2066
2252
 
2067
2253
 
2076
2262
    _protocol_version = 'HTTP/1.1'
2077
2263
 
2078
2264
    def setUp(self):
 
2265
        super(TestNoReportActivity, self).setUp()
2079
2266
        self._transport =_urllib.HttpTransport_urllib
2080
2267
        TestActivityMixin.setUp(self)
2081
2268
 
2087
2274
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2088
2275
    """Test authentication on the redirected http server."""
2089
2276
 
 
2277
    scenarios = vary_by_http_protocol_version()
 
2278
 
2090
2279
    _auth_header = 'Authorization'
2091
2280
    _password_prompt_prefix = ''
2092
2281
    _username_prompt_prefix = ''
2155
2344
        # stdout should be empty, stderr will contains the prompts
2156
2345
        self.assertEqual('', stdout.getvalue())
2157
2346
 
2158