~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 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
31
31
 
32
32
import bzrlib
33
33
from bzrlib import (
34
 
    bzrdir,
35
 
    cethread,
36
34
    config,
 
35
    controldir,
 
36
    debug,
37
37
    errors,
38
38
    osutils,
39
39
    remote as _mod_remote,
40
40
    tests,
 
41
    trace,
41
42
    transport,
42
43
    ui,
43
44
    )
91
92
        ]
92
93
 
93
94
 
94
 
def vary_by_http_proxy_auth_scheme():
95
 
    return [
96
 
        ('basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
97
 
        ('digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
98
 
        ('basicdigest',
99
 
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
100
 
        ]
101
 
 
102
 
 
103
95
def vary_by_http_auth_scheme():
104
 
    return [
 
96
    scenarios = [
105
97
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
106
98
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
107
99
        ('basicdigest',
108
100
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
109
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
110
123
 
111
124
 
112
125
def vary_by_http_activity():
114
127
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
115
128
                            _transport=_urllib.HttpTransport_urllib,)),
116
129
        ]
117
 
    if tests.HTTPSServerFeature.available():
118
 
        activity_scenarios.append(
119
 
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
120
 
                                _transport=_urllib.HttpTransport_urllib,)),)
121
130
    if features.pycurl.available():
122
131
        activity_scenarios.append(
123
132
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
124
133
                                _transport=PyCurlTransport,)),)
125
 
        if tests.HTTPSServerFeature.available():
126
 
            from bzrlib.tests import (
127
 
                ssl_certs,
128
 
                )
129
 
            # FIXME: Until we have a better way to handle self-signed
130
 
            # certificates (like allowing them in a test specific
131
 
            # authentication.conf for example), we need some specialized pycurl
132
 
            # transport for tests.
 
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
 
 
149
        activity_scenarios.append(
 
150
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
 
151
                                  _transport=HTTPS_urllib_transport,)),)
 
152
        if features.pycurl.available():
133
153
            class HTTPS_pycurl_transport(PyCurlTransport):
134
154
 
135
155
                def __init__(self, base, _from_transport=None):
364
384
    _transport = property(_get_pycurl_maybe)
365
385
 
366
386
 
367
 
class TestHttpUrls(tests.TestCase):
368
 
 
369
 
    # TODO: This should be moved to authorization tests once they
370
 
    # are written.
371
 
 
372
 
    def test_url_parsing(self):
373
 
        f = FakeManager()
374
 
        url = http.extract_auth('http://example.com', f)
375
 
        self.assertEqual('http://example.com', url)
376
 
        self.assertEqual(0, len(f.credentials))
377
 
        url = http.extract_auth(
378
 
            'http://user:pass@example.com/bzr/bzr.dev', f)
379
 
        self.assertEqual('http://example.com/bzr/bzr.dev', url)
380
 
        self.assertEqual(1, len(f.credentials))
381
 
        self.assertEqual([None, 'example.com', 'user', 'pass'],
382
 
                         f.credentials[0])
383
 
 
384
 
 
385
387
class TestHttpTransportUrls(tests.TestCase):
386
388
    """Test the http urls."""
387
389
 
467
469
        )
468
470
 
469
471
    def setUp(self):
470
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
472
        super(TestHTTPConnections, self).setUp()
471
473
        self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
472
474
                        transport=self.get_transport())
473
475
 
519
521
    scenarios = vary_by_http_client_implementation()
520
522
 
521
523
    def test_http_registered(self):
522
 
        t = transport.get_transport('%s://foo.com/' % self._url_protocol)
 
524
        t = transport.get_transport_from_url(
 
525
            '%s://foo.com/' % self._url_protocol)
523
526
        self.assertIsInstance(t, transport.Transport)
524
527
        self.assertIsInstance(t, self._transport)
525
528
 
537
540
        self.start_server(server)
538
541
        url = server.get_url()
539
542
        # FIXME: needs a cleanup -- vila 20100611
540
 
        http_transport = transport.get_transport(url)
 
543
        http_transport = transport.get_transport_from_url(url)
541
544
        code, response = http_transport._post('abc def end-of-body')
542
545
        self.assertTrue(
543
546
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
653
656
 
654
657
    _req_handler_class = BadStatusRequestHandler
655
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
 
656
670
    def test_http_has(self):
657
671
        t = self.get_readonly_transport()
658
 
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
672
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
 
673
                           errors.InvalidHttpResponse),
 
674
                          t.has, 'foo/bar')
659
675
 
660
676
    def test_http_get(self):
661
677
        t = self.get_readonly_transport()
662
 
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
678
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
 
679
                           errors.InvalidHttpResponse),
 
680
                          t.get, 'foo/bar')
663
681
 
664
682
 
665
683
class InvalidStatusRequestHandler(http_server.TestingHTTPRequestHandler):
1034
1052
        self.assertEqual('single', t._range_hint)
1035
1053
 
1036
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
 
1037
1121
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1038
1122
    """Errors out when range specifiers exceed the limit"""
1039
1123
 
1076
1160
                                      protocol_version=self._protocol_version)
1077
1161
 
1078
1162
    def setUp(self):
1079
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
1163
        super(TestLimitedRangeRequestServer, self).setUp()
1080
1164
        # We need to manipulate ranges that correspond to real chunks in the
1081
1165
        # response, so we build a content appropriately.
1082
1166
        filler = ''.join(['abcdefghij' for x in range(102)])
1268
1352
        )
1269
1353
 
1270
1354
    def setUp(self):
1271
 
        http_utils.TestCaseWithWebserver.setUp(self)
 
1355
        super(TestRanges, self).setUp()
1272
1356
        self.build_tree_contents([('a', '0123456789')],)
1273
1357
 
1274
1358
    def create_transport_readonly_server(self):
1489
1573
                          self.get_a, self.old_transport, redirected)
1490
1574
 
1491
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
 
1492
1605
class TestAuth(http_utils.TestCaseWithWebserver):
1493
1606
    """Test authentication scheme"""
1494
1607
 
1498
1611
        vary_by_http_auth_scheme(),
1499
1612
        )
1500
1613
 
1501
 
    _auth_header = 'Authorization'
1502
 
    _password_prompt_prefix = ''
1503
 
    _username_prompt_prefix = ''
1504
 
    # Set by load_tests
1505
 
    _auth_server = None
1506
 
 
1507
1614
    def setUp(self):
1508
1615
        super(TestAuth, self).setUp()
1509
1616
        self.server = self.get_readonly_server()
1532
1639
        return url
1533
1640
 
1534
1641
    def get_user_transport(self, user, password):
1535
 
        t = transport.get_transport(self.get_user_url(user, password))
 
1642
        t = transport.get_transport_from_url(
 
1643
            self.get_user_url(user, password))
1536
1644
        return t
1537
1645
 
1538
1646
    def test_no_user(self):
1650
1758
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1651
1759
                                            stderr=tests.StringIOWrapper())
1652
1760
        # Create a minimal config file with the right password
1653
 
        _setup_authentication_config(
1654
 
            scheme='http', 
1655
 
            port=self.server.port,
1656
 
            user=user,
1657
 
            password=password)
 
1761
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1762
                                     user=user, password=password)
1658
1763
        # Issue a request to the server to connect
1659
1764
        self.assertEqual('contents of a\n',t.get('a').read())
1660
1765
        # stdin should have  been left untouched
1667
1772
                                     http_utils.ProxyDigestAuthServer):
1668
1773
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1669
1774
        if self._testing_pycurl():
1670
 
            raise tests.KnownFailure(
 
1775
            self.knownFailure(
1671
1776
                'pycurl does not handle a nonce change')
1672
1777
        self.server.add_user('joe', 'foo')
1673
1778
        t = self.get_user_transport('joe', 'foo')
1690
1795
        user = 'joe'
1691
1796
        password = 'foo'
1692
1797
        self.server.add_user(user, password)
1693
 
        _setup_authentication_config(
1694
 
            scheme='http', 
1695
 
            port=self.server.port,
1696
 
            user=user,
1697
 
            password=password)
 
1798
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1799
                                     user=user, password=password)
1698
1800
        t = self.get_user_transport(None, None)
1699
1801
        # Issue a request to the server to connect
1700
1802
        self.assertEqual('contents of a\n', t.get('a').read())
1701
1803
        # Only one 'Authentication Required' error should occur
1702
1804
        self.assertEqual(1, self.server.auth_required_errors)
1703
1805
 
1704
 
 
1705
 
def _setup_authentication_config(**kwargs):
1706
 
    conf = config.AuthenticationConfig()
1707
 
    conf._get_config().update({'httptest': kwargs})
1708
 
    conf._save()
1709
 
 
1710
 
 
1711
 
 
1712
 
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
1713
 
    """Unit tests for glue by which urllib2 asks us for authentication"""
1714
 
 
1715
 
    def test_get_user_password_without_port(self):
1716
 
        """We cope if urllib2 doesn't tell us the port.
1717
 
 
1718
 
        See https://bugs.launchpad.net/bzr/+bug/654684
1719
 
        """
 
1806
    def test_no_credential_leaks_in_log(self):
 
1807
        self.overrideAttr(debug, 'debug_flags', set(['http']))
1720
1808
        user = 'joe'
1721
 
        password = 'foo'
1722
 
        _setup_authentication_config(
1723
 
            scheme='http', 
1724
 
            host='localhost',
1725
 
            user=user,
1726
 
            password=password)
1727
 
        handler = _urllib2_wrappers.HTTPAuthHandler()
1728
 
        got_pass = handler.get_user_password(dict(
1729
 
            user='joe',
1730
 
            protocol='http',
1731
 
            host='localhost',
1732
 
            path='/',
1733
 
            realm='Realm',
1734
 
            ))
1735
 
        self.assertEquals((user, password), got_pass)
 
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,))
1736
1831
 
1737
1832
 
1738
1833
class TestProxyAuth(TestAuth):
1739
 
    """Test proxy authentication schemes."""
 
1834
    """Test proxy authentication schemes.
 
1835
 
 
1836
    This inherits from TestAuth to tweak the setUp and filter some failing
 
1837
    tests.
 
1838
    """
1740
1839
 
1741
1840
    scenarios = multiply_scenarios(
1742
1841
        vary_by_http_client_implementation(),
1744
1843
        vary_by_http_proxy_auth_scheme(),
1745
1844
        )
1746
1845
 
1747
 
    _auth_header = 'Proxy-authorization'
1748
 
    _password_prompt_prefix = 'Proxy '
1749
 
    _username_prompt_prefix = 'Proxy '
1750
 
 
1751
1846
    def setUp(self):
1752
1847
        super(TestProxyAuth, self).setUp()
1753
1848
        # Override the contents to avoid false positives
1765
1860
        if self._testing_pycurl():
1766
1861
            import pycurl
1767
1862
            if pycurl.version_info()[1] < '7.16.0':
1768
 
                raise tests.KnownFailure(
 
1863
                self.knownFailure(
1769
1864
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1770
1865
        super(TestProxyAuth, self).test_empty_pass()
1771
1866
 
1813
1908
        server._url_protocol = self._url_protocol
1814
1909
        return server
1815
1910
 
1816
 
    def test_open_bzrdir(self):
 
1911
    def test_open_controldir(self):
1817
1912
        branch = self.make_branch('relpath')
1818
1913
        url = self.http_server.get_url() + 'relpath'
1819
 
        bd = bzrdir.BzrDir.open(url)
 
1914
        bd = controldir.ControlDir.open(url)
1820
1915
        self.addCleanup(bd.transport.disconnect)
1821
1916
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1822
1917
 
1825
1920
        # The 'readv' command in the smart protocol both sends and receives
1826
1921
        # bulk data, so we use that.
1827
1922
        self.build_tree(['data-file'])
1828
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1923
        http_transport = transport.get_transport_from_url(
 
1924
            self.http_server.get_url())
1829
1925
        medium = http_transport.get_smart_medium()
1830
1926
        # Since we provide the medium, the url below will be mostly ignored
1831
1927
        # during the test, as long as the path is '/'.
1839
1935
        post_body = 'hello\n'
1840
1936
        expected_reply_body = 'ok\x012\n'
1841
1937
 
1842
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1938
        http_transport = transport.get_transport_from_url(
 
1939
            self.http_server.get_url())
1843
1940
        medium = http_transport.get_smart_medium()
1844
1941
        response = medium.send_http_smart_request(post_body)
1845
1942
        reply_body = response.read()
1903
2000
        self.assertIsInstance(r, type(t))
1904
2001
        # Both transports share the some connection
1905
2002
        self.assertEqual(t._get_connection(), r._get_connection())
 
2003
        self.assertEquals('http://www.example.com/foo/subdir/', r.base)
1906
2004
 
1907
2005
    def test_redirected_to_self_with_slash(self):
1908
2006
        t = self._transport('http://www.example.com/foo')
1919
2017
        r = t._redirected_to('http://www.example.com/foo',
1920
2018
                             'http://foo.example.com/foo/subdir')
1921
2019
        self.assertIsInstance(r, type(t))
 
2020
        self.assertEquals('http://foo.example.com/foo/subdir/',
 
2021
            r.external_url())
1922
2022
 
1923
2023
    def test_redirected_to_same_host_sibling_protocol(self):
1924
2024
        t = self._transport('http://www.example.com/foo')
1925
2025
        r = t._redirected_to('http://www.example.com/foo',
1926
2026
                             'https://www.example.com/foo')
1927
2027
        self.assertIsInstance(r, type(t))
 
2028
        self.assertEquals('https://www.example.com/foo/',
 
2029
            r.external_url())
1928
2030
 
1929
2031
    def test_redirected_to_same_host_different_protocol(self):
1930
2032
        t = self._transport('http://www.example.com/foo')
1931
2033
        r = t._redirected_to('http://www.example.com/foo',
1932
2034
                             'ftp://www.example.com/foo')
1933
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())
1934
2043
 
1935
2044
    def test_redirected_to_different_host_same_user(self):
1936
2045
        t = self._transport('http://joe@www.example.com/foo')
1937
2046
        r = t._redirected_to('http://www.example.com/foo',
1938
2047
                             'https://foo.example.com/foo')
1939
2048
        self.assertIsInstance(r, type(t))
1940
 
        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())
1941
2051
 
1942
2052
 
1943
2053
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
1996
2106
    pass
1997
2107
 
1998
2108
 
1999
 
if tests.HTTPSServerFeature.available():
 
2109
if features.HTTPSServerFeature.available():
2000
2110
    from bzrlib.tests import https_server
2001
2111
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
2002
2112
        pass
2010
2120
    """
2011
2121
 
2012
2122
    def setUp(self):
2013
 
        tests.TestCase.setUp(self)
2014
2123
        self.server = self._activity_server(self._protocol_version)
2015
2124
        self.server.start_server()
2016
 
        self.activities = {}
 
2125
        self.addCleanup(self.server.stop_server)
 
2126
        _activities = {} # Don't close over self and create a cycle
2017
2127
        def report_activity(t, bytes, direction):
2018
 
            count = self.activities.get(direction, 0)
 
2128
            count = _activities.get(direction, 0)
2019
2129
            count += bytes
2020
 
            self.activities[direction] = count
2021
 
 
 
2130
            _activities[direction] = count
 
2131
        self.activities = _activities
2022
2132
        # We override at class level because constructors may propagate the
2023
2133
        # bound method and render instance overriding ineffective (an
2024
2134
        # alternative would be to define a specific ui factory instead...)
2025
2135
        self.overrideAttr(self._transport, '_report_activity', report_activity)
2026
 
        self.addCleanup(self.server.stop_server)
2027
2136
 
2028
2137
    def get_transport(self):
2029
2138
        t = self._transport(self.server.get_url())
2153
2262
        )
2154
2263
 
2155
2264
    def setUp(self):
 
2265
        super(TestActivity, self).setUp()
2156
2266
        TestActivityMixin.setUp(self)
2157
2267
 
2158
2268
 
2167
2277
    _protocol_version = 'HTTP/1.1'
2168
2278
 
2169
2279
    def setUp(self):
 
2280
        super(TestNoReportActivity, self).setUp()
2170
2281
        self._transport =_urllib.HttpTransport_urllib
2171
2282
        TestActivityMixin.setUp(self)
2172
2283
 
2248
2359
        # stdout should be empty, stderr will contains the prompts
2249
2360
        self.assertEqual('', stdout.getvalue())
2250
2361
 
2251