~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Patch Queue Manager
  • Date: 2016-01-31 13:36:59 UTC
  • mfrom: (6613.1.5 1538480-match-hostname)
  • Revision ID: pqm@pqm.ubuntu.com-20160131133659-ouy92ee2wlv9xz8m
(vila) Use ssl.match_hostname instead of our own. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2015 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
 
648
656
 
649
657
    _req_handler_class = BadStatusRequestHandler
650
658
 
 
659
    def setUp(self):
 
660
        super(TestBadStatusServer, self).setUp()
 
661
        # See https://bugs.launchpad.net/bzr/+bug/1451448 for details.
 
662
        # TD;LR: Running both a TCP client and server in the same process and
 
663
        # thread uncovers a race in python. The fix is to run the server in a
 
664
        # different process. Trying to fix yet another race here is not worth
 
665
        # the effort. -- vila 2015-09-06
 
666
        if 'HTTP/1.0' in self.id():
 
667
            raise tests.TestSkipped(
 
668
                'Client/Server in the same process and thread can hang')
 
669
 
651
670
    def test_http_has(self):
652
671
        t = self.get_readonly_transport()
653
 
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
672
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
 
673
                           errors.InvalidHttpResponse),
 
674
                          t.has, 'foo/bar')
654
675
 
655
676
    def test_http_get(self):
656
677
        t = self.get_readonly_transport()
657
 
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
678
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
 
679
                           errors.InvalidHttpResponse),
 
680
                          t.get, 'foo/bar')
658
681
 
659
682
 
660
683
class InvalidStatusRequestHandler(http_server.TestingHTTPRequestHandler):
841
864
        t = self.get_readonly_transport()
842
865
        # force transport to issue multiple requests
843
866
        t._get_max_size = 2
844
 
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
 
867
        list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
845
868
        # The server should have issued 3 requests
846
869
        self.assertEqual(3, server.GET_request_nb)
847
870
        self.assertEqual('0123456789', t.get_bytes('a'))
924
947
    def get_multiple_ranges(self, file, file_size, ranges):
925
948
        self.send_response(206)
926
949
        self.send_header('Accept-Ranges', 'bytes')
 
950
        # XXX: this is strange; the 'random' name below seems undefined and
 
951
        # yet the tests pass -- mbp 2010-10-11 bug 658773
927
952
        boundary = "%d" % random.randint(0,0x7FFFFFFF)
928
953
        self.send_header("Content-Type",
929
954
                         "multipart/byteranges; boundary=%s" % boundary)
991
1016
                return
992
1017
            self.send_range_content(file, start, end - start + 1)
993
1018
            cur += 1
994
 
        # No final boundary
 
1019
        # Final boundary
995
1020
        self.wfile.write(boundary_line)
996
1021
 
997
1022
 
1026
1051
        # that mode
1027
1052
        self.assertEqual('single', t._range_hint)
1028
1053
 
 
1054
 
 
1055
class TruncatedBeforeBoundaryRequestHandler(
 
1056
    http_server.TestingHTTPRequestHandler):
 
1057
    """Truncation before a boundary, like in bug 198646"""
 
1058
 
 
1059
    _truncated_ranges = 1
 
1060
 
 
1061
    def get_multiple_ranges(self, file, file_size, ranges):
 
1062
        self.send_response(206)
 
1063
        self.send_header('Accept-Ranges', 'bytes')
 
1064
        boundary = 'tagada'
 
1065
        self.send_header('Content-Type',
 
1066
                         'multipart/byteranges; boundary=%s' % boundary)
 
1067
        boundary_line = '--%s\r\n' % boundary
 
1068
        # Calculate the Content-Length
 
1069
        content_length = 0
 
1070
        for (start, end) in ranges:
 
1071
            content_length += len(boundary_line)
 
1072
            content_length += self._header_line_length(
 
1073
                'Content-type', 'application/octet-stream')
 
1074
            content_length += self._header_line_length(
 
1075
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
 
1076
            content_length += len('\r\n') # end headers
 
1077
            content_length += end - start # + 1
 
1078
        content_length += len(boundary_line)
 
1079
        self.send_header('Content-length', content_length)
 
1080
        self.end_headers()
 
1081
 
 
1082
        # Send the multipart body
 
1083
        cur = 0
 
1084
        for (start, end) in ranges:
 
1085
            if cur + self._truncated_ranges >= len(ranges):
 
1086
                # Abruptly ends the response and close the connection
 
1087
                self.close_connection = 1
 
1088
                return
 
1089
            self.wfile.write(boundary_line)
 
1090
            self.send_header('Content-type', 'application/octet-stream')
 
1091
            self.send_header('Content-Range', 'bytes %d-%d/%d'
 
1092
                             % (start, end, file_size))
 
1093
            self.end_headers()
 
1094
            self.send_range_content(file, start, end - start + 1)
 
1095
            cur += 1
 
1096
        # Final boundary
 
1097
        self.wfile.write(boundary_line)
 
1098
 
 
1099
 
 
1100
class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
 
1101
    """Tests the case of bug 198646, disconnecting before a boundary."""
 
1102
 
 
1103
    _req_handler_class = TruncatedBeforeBoundaryRequestHandler
 
1104
 
 
1105
    def setUp(self):
 
1106
        super(TestTruncatedBeforeBoundary, self).setUp()
 
1107
        self.build_tree_contents([('a', '0123456789')],)
 
1108
 
 
1109
    def test_readv_with_short_reads(self):
 
1110
        server = self.get_readonly_server()
 
1111
        t = self.get_readonly_transport()
 
1112
        # Force separate ranges for each offset
 
1113
        t._bytes_to_read_before_seek = 0
 
1114
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
 
1115
        self.assertEqual((0, '0'), ireadv.next())
 
1116
        self.assertEqual((2, '2'), ireadv.next())
 
1117
        self.assertEqual((4, '45'), ireadv.next())
 
1118
        self.assertEqual((9, '9'), ireadv.next())
 
1119
 
 
1120
 
1029
1121
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1030
1122
    """Errors out when range specifiers exceed the limit"""
1031
1123
 
1055
1147
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1056
1148
    """Tests readv requests against a server erroring out on too much ranges."""
1057
1149
 
 
1150
    scenarios = multiply_scenarios(
 
1151
        vary_by_http_client_implementation(),
 
1152
        vary_by_http_protocol_version(),
 
1153
        )
 
1154
 
1058
1155
    # Requests with more range specifiers will error out
1059
1156
    range_limit = 3
1060
1157
 
1063
1160
                                      protocol_version=self._protocol_version)
1064
1161
 
1065
1162
    def setUp(self):
1066
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
1163
        super(TestLimitedRangeRequestServer, self).setUp()
1067
1164
        # We need to manipulate ranges that correspond to real chunks in the
1068
1165
        # response, so we build a content appropriately.
1069
1166
        filler = ''.join(['abcdefghij' for x in range(102)])
1095
1192
    Only the urllib implementation is tested here.
1096
1193
    """
1097
1194
 
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
1195
    def _proxied_request(self):
1112
1196
        handler = _urllib2_wrappers.ProxyHandler()
1113
 
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
 
1197
        request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
1114
1198
        handler.set_proxy(request, 'http')
1115
1199
        return request
1116
1200
 
 
1201
    def assertEvaluateProxyBypass(self, expected, host, no_proxy):
 
1202
        handler = _urllib2_wrappers.ProxyHandler()
 
1203
        self.assertEquals(expected,
 
1204
                          handler.evaluate_proxy_bypass(host, no_proxy))
 
1205
 
1117
1206
    def test_empty_user(self):
1118
 
        self._install_env({'http_proxy': 'http://bar.com'})
 
1207
        self.overrideEnv('http_proxy', 'http://bar.com')
 
1208
        request = self._proxied_request()
 
1209
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
 
1210
 
 
1211
    def test_user_with_at(self):
 
1212
        self.overrideEnv('http_proxy',
 
1213
                         'http://username@domain:password@proxy_host:1234')
1119
1214
        request = self._proxied_request()
1120
1215
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1121
1216
 
1122
1217
    def test_invalid_proxy(self):
1123
1218
        """A proxy env variable without scheme"""
1124
 
        self._install_env({'http_proxy': 'host:1234'})
 
1219
        self.overrideEnv('http_proxy', 'host:1234')
1125
1220
        self.assertRaises(errors.InvalidURL, self._proxied_request)
1126
1221
 
 
1222
    def test_evaluate_proxy_bypass_true(self):
 
1223
        """The host is not proxied"""
 
1224
        self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
 
1225
        self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
 
1226
 
 
1227
    def test_evaluate_proxy_bypass_false(self):
 
1228
        """The host is proxied"""
 
1229
        self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
 
1230
 
 
1231
    def test_evaluate_proxy_bypass_unknown(self):
 
1232
        """The host is not explicitly proxied"""
 
1233
        self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
 
1234
        self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
 
1235
 
 
1236
    def test_evaluate_proxy_bypass_empty_entries(self):
 
1237
        """Ignore empty entries"""
 
1238
        self.assertEvaluateProxyBypass(None, 'example.com', '')
 
1239
        self.assertEvaluateProxyBypass(None, 'example.com', ',')
 
1240
        self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
 
1241
 
1127
1242
 
1128
1243
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
1129
1244
    """Tests proxy server.
1134
1249
    to the file names).
1135
1250
    """
1136
1251
 
 
1252
    scenarios = multiply_scenarios(
 
1253
        vary_by_http_client_implementation(),
 
1254
        vary_by_http_protocol_version(),
 
1255
        )
 
1256
 
1137
1257
    # FIXME: We don't have an https server available, so we don't
1138
1258
    # test https connections. --vila toolongago
1139
1259
 
1153
1273
            self.no_proxy_host = self.server_host_port
1154
1274
        # The secondary server is the proxy
1155
1275
        self.proxy_url = self.get_secondary_url()
1156
 
        self._old_env = {}
1157
1276
 
1158
1277
    def _testing_pycurl(self):
1159
1278
        # TODO: This is duplicated for lots of the classes in this file
1160
1279
        return (features.pycurl.available()
1161
1280
                and self._transport == PyCurlTransport)
1162
1281
 
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()
 
1282
    def assertProxied(self):
 
1283
        t = self.get_readonly_transport()
 
1284
        self.assertEqual('proxied contents of foo\n', t.get('foo').read())
 
1285
 
 
1286
    def assertNotProxied(self):
 
1287
        t = self.get_readonly_transport()
 
1288
        self.assertEqual('contents of foo\n', t.get('foo').read())
1186
1289
 
1187
1290
    def test_http_proxy(self):
1188
 
        self.proxied_in_env({'http_proxy': self.proxy_url})
 
1291
        self.overrideEnv('http_proxy', self.proxy_url)
 
1292
        self.assertProxied()
1189
1293
 
1190
1294
    def test_HTTP_PROXY(self):
1191
1295
        if self._testing_pycurl():
1194
1298
            # about. Should we ?)
1195
1299
            raise tests.TestNotApplicable(
1196
1300
                'pycurl does not check HTTP_PROXY for security reasons')
1197
 
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
 
1301
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1302
        self.assertProxied()
1198
1303
 
1199
1304
    def test_all_proxy(self):
1200
 
        self.proxied_in_env({'all_proxy': self.proxy_url})
 
1305
        self.overrideEnv('all_proxy', self.proxy_url)
 
1306
        self.assertProxied()
1201
1307
 
1202
1308
    def test_ALL_PROXY(self):
1203
 
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
 
1309
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1310
        self.assertProxied()
1204
1311
 
1205
1312
    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})
 
1313
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1314
        self.overrideEnv('http_proxy', self.proxy_url)
 
1315
        self.assertNotProxied()
1208
1316
 
1209
1317
    def test_HTTP_PROXY_with_NO_PROXY(self):
1210
1318
        if self._testing_pycurl():
1211
1319
            raise tests.TestNotApplicable(
1212
1320
                '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})
 
1321
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1322
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1323
        self.assertNotProxied()
1215
1324
 
1216
1325
    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})
 
1326
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1327
        self.overrideEnv('all_proxy', self.proxy_url)
 
1328
        self.assertNotProxied()
1219
1329
 
1220
1330
    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})
 
1331
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1332
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1333
        self.assertNotProxied()
1223
1334
 
1224
1335
    def test_http_proxy_without_scheme(self):
 
1336
        self.overrideEnv('http_proxy', self.server_host_port)
1225
1337
        if self._testing_pycurl():
1226
1338
            # pycurl *ignores* invalid proxy env variables. If that ever change
1227
1339
            # in the future, this test will fail indicating that pycurl do not
1228
1340
            # ignore anymore such variables.
1229
 
            self.not_proxied_in_env({'http_proxy': self.server_host_port})
 
1341
            self.assertNotProxied()
1230
1342
        else:
1231
 
            self.assertRaises(errors.InvalidURL,
1232
 
                              self.proxied_in_env,
1233
 
                              {'http_proxy': self.server_host_port})
 
1343
            self.assertRaises(errors.InvalidURL, self.assertProxied)
1234
1344
 
1235
1345
 
1236
1346
class TestRanges(http_utils.TestCaseWithWebserver):
1237
1347
    """Test the Range header in GET methods."""
1238
1348
 
 
1349
    scenarios = multiply_scenarios(
 
1350
        vary_by_http_client_implementation(),
 
1351
        vary_by_http_protocol_version(),
 
1352
        )
 
1353
 
1239
1354
    def setUp(self):
1240
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
1355
        super(TestRanges, self).setUp()
1241
1356
        self.build_tree_contents([('a', '0123456789')],)
1242
1357
 
1243
1358
    def create_transport_readonly_server(self):
1281
1396
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1282
1397
    """Test redirection between http servers."""
1283
1398
 
 
1399
    scenarios = multiply_scenarios(
 
1400
        vary_by_http_client_implementation(),
 
1401
        vary_by_http_protocol_version(),
 
1402
        )
 
1403
 
1284
1404
    def setUp(self):
1285
1405
        super(TestHTTPRedirections, self).setUp()
1286
1406
        self.build_tree_contents([('a', '0123456789'),
1349
1469
    -- vila 20070212
1350
1470
    """
1351
1471
 
 
1472
    scenarios = multiply_scenarios(
 
1473
        vary_by_http_client_implementation(),
 
1474
        vary_by_http_protocol_version(),
 
1475
        )
 
1476
 
1352
1477
    def setUp(self):
1353
1478
        if (features.pycurl.available()
1354
1479
            and self._transport == PyCurlTransport):
1399
1524
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1400
1525
    """Test transport.do_catching_redirections."""
1401
1526
 
 
1527
    scenarios = multiply_scenarios(
 
1528
        vary_by_http_client_implementation(),
 
1529
        vary_by_http_protocol_version(),
 
1530
        )
 
1531
 
1402
1532
    def setUp(self):
1403
1533
        super(TestDoCatchRedirections, self).setUp()
1404
1534
        self.build_tree_contents([('a', '0123456789'),],)
1443
1573
                          self.get_a, self.old_transport, redirected)
1444
1574
 
1445
1575
 
 
1576
def _setup_authentication_config(**kwargs):
 
1577
    conf = config.AuthenticationConfig()
 
1578
    conf._get_config().update({'httptest': kwargs})
 
1579
    conf._save()
 
1580
 
 
1581
 
 
1582
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
 
1583
    """Unit tests for glue by which urllib2 asks us for authentication"""
 
1584
 
 
1585
    def test_get_user_password_without_port(self):
 
1586
        """We cope if urllib2 doesn't tell us the port.
 
1587
 
 
1588
        See https://bugs.launchpad.net/bzr/+bug/654684
 
1589
        """
 
1590
        user = 'joe'
 
1591
        password = 'foo'
 
1592
        _setup_authentication_config(scheme='http', host='localhost',
 
1593
                                     user=user, password=password)
 
1594
        handler = _urllib2_wrappers.HTTPAuthHandler()
 
1595
        got_pass = handler.get_user_password(dict(
 
1596
            user='joe',
 
1597
            protocol='http',
 
1598
            host='localhost',
 
1599
            path='/',
 
1600
            realm='Realm',
 
1601
            ))
 
1602
        self.assertEquals((user, password), got_pass)
 
1603
 
 
1604
 
1446
1605
class TestAuth(http_utils.TestCaseWithWebserver):
1447
1606
    """Test authentication scheme"""
1448
1607
 
1449
 
    _auth_header = 'Authorization'
1450
 
    _password_prompt_prefix = ''
1451
 
    _username_prompt_prefix = ''
1452
 
    # Set by load_tests
1453
 
    _auth_server = None
 
1608
    scenarios = multiply_scenarios(
 
1609
        vary_by_http_client_implementation(),
 
1610
        vary_by_http_protocol_version(),
 
1611
        vary_by_http_auth_scheme(),
 
1612
        )
1454
1613
 
1455
1614
    def setUp(self):
1456
1615
        super(TestAuth, self).setUp()
1480
1639
        return url
1481
1640
 
1482
1641
    def get_user_transport(self, user, password):
1483
 
        t = transport.get_transport(self.get_user_url(user, password))
 
1642
        t = transport.get_transport_from_url(
 
1643
            self.get_user_url(user, password))
1484
1644
        return t
1485
1645
 
1486
1646
    def test_no_user(self):
1598
1758
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1599
1759
                                            stderr=tests.StringIOWrapper())
1600
1760
        # 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()
 
1761
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1762
                                     user=user, password=password)
1606
1763
        # Issue a request to the server to connect
1607
1764
        self.assertEqual('contents of a\n',t.get('a').read())
1608
1765
        # stdin should have  been left untouched
1610
1767
        # Only one 'Authentication Required' error should occur
1611
1768
        self.assertEqual(1, self.server.auth_required_errors)
1612
1769
 
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
1770
    def test_changing_nonce(self):
1633
1771
        if self._auth_server not in (http_utils.HTTPDigestAuthServer,
1634
1772
                                     http_utils.ProxyDigestAuthServer):
1635
1773
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1636
1774
        if self._testing_pycurl():
1637
 
            raise tests.KnownFailure(
 
1775
            self.knownFailure(
1638
1776
                'pycurl does not handle a nonce change')
1639
1777
        self.server.add_user('joe', 'foo')
1640
1778
        t = self.get_user_transport('joe', 'foo')
1650
1788
        # initial 'who are you' and a second 'who are you' with the new nonce)
1651
1789
        self.assertEqual(2, self.server.auth_required_errors)
1652
1790
 
 
1791
    def test_user_from_auth_conf(self):
 
1792
        if self._testing_pycurl():
 
1793
            raise tests.TestNotApplicable(
 
1794
                'pycurl does not support authentication.conf')
 
1795
        user = 'joe'
 
1796
        password = 'foo'
 
1797
        self.server.add_user(user, password)
 
1798
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1799
                                     user=user, password=password)
 
1800
        t = self.get_user_transport(None, None)
 
1801
        # Issue a request to the server to connect
 
1802
        self.assertEqual('contents of a\n', t.get('a').read())
 
1803
        # Only one 'Authentication Required' error should occur
 
1804
        self.assertEqual(1, self.server.auth_required_errors)
 
1805
 
 
1806
    def test_no_credential_leaks_in_log(self):
 
1807
        self.overrideAttr(debug, 'debug_flags', set(['http']))
 
1808
        user = 'joe'
 
1809
        password = 'very-sensitive-password'
 
1810
        self.server.add_user(user, password)
 
1811
        t = self.get_user_transport(user, password)
 
1812
        # Capture the debug calls to mutter
 
1813
        self.mutters = []
 
1814
        def mutter(*args):
 
1815
            lines = args[0] % args[1:]
 
1816
            # Some calls output multiple lines, just split them now since we
 
1817
            # care about a single one later.
 
1818
            self.mutters.extend(lines.splitlines())
 
1819
        self.overrideAttr(trace, 'mutter', mutter)
 
1820
        # Issue a request to the server to connect
 
1821
        self.assertEqual(True, t.has('a'))
 
1822
        # Only one 'Authentication Required' error should occur
 
1823
        self.assertEqual(1, self.server.auth_required_errors)
 
1824
        # Since the authentification succeeded, there should be a corresponding
 
1825
        # debug line
 
1826
        sent_auth_headers = [line for line in self.mutters
 
1827
                             if line.startswith('> %s' % (self._auth_header,))]
 
1828
        self.assertLength(1, sent_auth_headers)
 
1829
        self.assertStartsWith(sent_auth_headers[0],
 
1830
                              '> %s: <masked>' % (self._auth_header,))
1653
1831
 
1654
1832
 
1655
1833
class TestProxyAuth(TestAuth):
1656
 
    """Test proxy authentication schemes."""
1657
 
 
1658
 
    _auth_header = 'Proxy-authorization'
1659
 
    _password_prompt_prefix = 'Proxy '
1660
 
    _username_prompt_prefix = 'Proxy '
 
1834
    """Test proxy authentication schemes.
 
1835
 
 
1836
    This inherits from TestAuth to tweak the setUp and filter some failing
 
1837
    tests.
 
1838
    """
 
1839
 
 
1840
    scenarios = multiply_scenarios(
 
1841
        vary_by_http_client_implementation(),
 
1842
        vary_by_http_protocol_version(),
 
1843
        vary_by_http_proxy_auth_scheme(),
 
1844
        )
1661
1845
 
1662
1846
    def setUp(self):
1663
1847
        super(TestProxyAuth, self).setUp()
1664
 
        self._old_env = {}
1665
 
        self.addCleanup(self._restore_env)
1666
1848
        # Override the contents to avoid false positives
1667
1849
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1668
1850
                                  ('b', 'not proxied contents of b\n'),
1671
1853
                                  ])
1672
1854
 
1673
1855
    def get_user_transport(self, user, password):
1674
 
        self._install_env({'all_proxy': self.get_user_url(user, password)})
 
1856
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
1675
1857
        return TestAuth.get_user_transport(self, user, password)
1676
1858
 
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
1859
    def test_empty_pass(self):
1686
1860
        if self._testing_pycurl():
1687
1861
            import pycurl
1688
1862
            if pycurl.version_info()[1] < '7.16.0':
1689
 
                raise tests.KnownFailure(
 
1863
                self.knownFailure(
1690
1864
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1691
1865
        super(TestProxyAuth, self).test_empty_pass()
1692
1866
 
1716
1890
 
1717
1891
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1718
1892
 
 
1893
    scenarios = multiply_scenarios(
 
1894
        vary_by_http_client_implementation(),
 
1895
        vary_by_http_protocol_version(),
 
1896
        )
 
1897
 
1719
1898
    def setUp(self):
1720
1899
        super(SmartHTTPTunnellingTest, self).setUp()
1721
1900
        # We use the VFS layer as part of HTTP tunnelling tests.
1722
 
        self._captureVar('BZR_NO_SMART_VFS', None)
 
1901
        self.overrideEnv('BZR_NO_SMART_VFS', None)
1723
1902
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1724
1903
        self.http_server = self.get_readonly_server()
1725
1904
 
1729
1908
        server._url_protocol = self._url_protocol
1730
1909
        return server
1731
1910
 
1732
 
    def test_open_bzrdir(self):
 
1911
    def test_open_controldir(self):
1733
1912
        branch = self.make_branch('relpath')
1734
1913
        url = self.http_server.get_url() + 'relpath'
1735
 
        bd = bzrdir.BzrDir.open(url)
 
1914
        bd = controldir.ControlDir.open(url)
1736
1915
        self.addCleanup(bd.transport.disconnect)
1737
1916
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1738
1917
 
1741
1920
        # The 'readv' command in the smart protocol both sends and receives
1742
1921
        # bulk data, so we use that.
1743
1922
        self.build_tree(['data-file'])
1744
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1923
        http_transport = transport.get_transport_from_url(
 
1924
            self.http_server.get_url())
1745
1925
        medium = http_transport.get_smart_medium()
1746
1926
        # Since we provide the medium, the url below will be mostly ignored
1747
1927
        # during the test, as long as the path is '/'.
1755
1935
        post_body = 'hello\n'
1756
1936
        expected_reply_body = 'ok\x012\n'
1757
1937
 
1758
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1938
        http_transport = transport.get_transport_from_url(
 
1939
            self.http_server.get_url())
1759
1940
        medium = http_transport.get_smart_medium()
1760
1941
        response = medium.send_http_smart_request(post_body)
1761
1942
        reply_body = response.read()
1810
1991
 
1811
1992
class Test_redirected_to(tests.TestCase):
1812
1993
 
 
1994
    scenarios = vary_by_http_client_implementation()
 
1995
 
1813
1996
    def test_redirected_to_subdir(self):
1814
1997
        t = self._transport('http://www.example.com/foo')
1815
1998
        r = t._redirected_to('http://www.example.com/foo',
1817
2000
        self.assertIsInstance(r, type(t))
1818
2001
        # Both transports share the some connection
1819
2002
        self.assertEqual(t._get_connection(), r._get_connection())
 
2003
        self.assertEquals('http://www.example.com/foo/subdir/', r.base)
1820
2004
 
1821
2005
    def test_redirected_to_self_with_slash(self):
1822
2006
        t = self._transport('http://www.example.com/foo')
1833
2017
        r = t._redirected_to('http://www.example.com/foo',
1834
2018
                             'http://foo.example.com/foo/subdir')
1835
2019
        self.assertIsInstance(r, type(t))
 
2020
        self.assertEquals('http://foo.example.com/foo/subdir/',
 
2021
            r.external_url())
1836
2022
 
1837
2023
    def test_redirected_to_same_host_sibling_protocol(self):
1838
2024
        t = self._transport('http://www.example.com/foo')
1839
2025
        r = t._redirected_to('http://www.example.com/foo',
1840
2026
                             'https://www.example.com/foo')
1841
2027
        self.assertIsInstance(r, type(t))
 
2028
        self.assertEquals('https://www.example.com/foo/',
 
2029
            r.external_url())
1842
2030
 
1843
2031
    def test_redirected_to_same_host_different_protocol(self):
1844
2032
        t = self._transport('http://www.example.com/foo')
1845
2033
        r = t._redirected_to('http://www.example.com/foo',
1846
2034
                             'ftp://www.example.com/foo')
1847
2035
        self.assertNotEquals(type(r), type(t))
 
2036
        self.assertEquals('ftp://www.example.com/foo/', r.external_url())
 
2037
 
 
2038
    def test_redirected_to_same_host_specific_implementation(self):
 
2039
        t = self._transport('http://www.example.com/foo')
 
2040
        r = t._redirected_to('http://www.example.com/foo',
 
2041
                             'https+urllib://www.example.com/foo')
 
2042
        self.assertEquals('https://www.example.com/foo/', r.external_url())
1848
2043
 
1849
2044
    def test_redirected_to_different_host_same_user(self):
1850
2045
        t = self._transport('http://joe@www.example.com/foo')
1851
2046
        r = t._redirected_to('http://www.example.com/foo',
1852
2047
                             'https://foo.example.com/foo')
1853
2048
        self.assertIsInstance(r, type(t))
1854
 
        self.assertEqual(t._user, r._user)
 
2049
        self.assertEqual(t._parsed_url.user, r._parsed_url.user)
 
2050
        self.assertEquals('https://joe@foo.example.com/foo/', r.external_url())
1855
2051
 
1856
2052
 
1857
2053
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
1910
2106
    pass
1911
2107
 
1912
2108
 
1913
 
if tests.HTTPSServerFeature.available():
 
2109
if features.HTTPSServerFeature.available():
1914
2110
    from bzrlib.tests import https_server
1915
2111
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
1916
2112
        pass
1924
2120
    """
1925
2121
 
1926
2122
    def setUp(self):
1927
 
        tests.TestCase.setUp(self)
1928
2123
        self.server = self._activity_server(self._protocol_version)
1929
2124
        self.server.start_server()
1930
 
        self.activities = {}
 
2125
        self.addCleanup(self.server.stop_server)
 
2126
        _activities = {} # Don't close over self and create a cycle
1931
2127
        def report_activity(t, bytes, direction):
1932
 
            count = self.activities.get(direction, 0)
 
2128
            count = _activities.get(direction, 0)
1933
2129
            count += bytes
1934
 
            self.activities[direction] = count
1935
 
 
 
2130
            _activities[direction] = count
 
2131
        self.activities = _activities
1936
2132
        # We override at class level because constructors may propagate the
1937
2133
        # bound method and render instance overriding ineffective (an
1938
2134
        # alternative would be to define a specific ui factory instead...)
1939
2135
        self.overrideAttr(self._transport, '_report_activity', report_activity)
1940
 
        self.addCleanup(self.server.stop_server)
1941
2136
 
1942
2137
    def get_transport(self):
1943
2138
        t = self._transport(self.server.get_url())
2061
2256
 
2062
2257
class TestActivity(tests.TestCase, TestActivityMixin):
2063
2258
 
 
2259
    scenarios = multiply_scenarios(
 
2260
        vary_by_http_activity(),
 
2261
        vary_by_http_protocol_version(),
 
2262
        )
 
2263
 
2064
2264
    def setUp(self):
 
2265
        super(TestActivity, self).setUp()
2065
2266
        TestActivityMixin.setUp(self)
2066
2267
 
2067
2268
 
2076
2277
    _protocol_version = 'HTTP/1.1'
2077
2278
 
2078
2279
    def setUp(self):
 
2280
        super(TestNoReportActivity, self).setUp()
2079
2281
        self._transport =_urllib.HttpTransport_urllib
2080
2282
        TestActivityMixin.setUp(self)
2081
2283
 
2087
2289
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2088
2290
    """Test authentication on the redirected http server."""
2089
2291
 
 
2292
    scenarios = vary_by_http_protocol_version()
 
2293
 
2090
2294
    _auth_header = 'Authorization'
2091
2295
    _password_prompt_prefix = ''
2092
2296
    _username_prompt_prefix = ''
2155
2359
        # stdout should be empty, stderr will contains the prompts
2156
2360
        self.assertEqual('', stdout.getvalue())
2157
2361
 
2158