~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

Merge bzr.dev, update to use new hooks.

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
35
32
import bzrlib
36
33
from bzrlib import (
37
34
    bzrdir,
 
35
    cethread,
38
36
    config,
 
37
    debug,
39
38
    errors,
40
39
    osutils,
41
40
    remote as _mod_remote,
42
41
    tests,
 
42
    trace,
43
43
    transport,
44
44
    ui,
45
 
    urlutils,
46
45
    )
47
46
from bzrlib.tests import (
48
47
    features,
50
49
    http_utils,
51
50
    test_server,
52
51
    )
 
52
from bzrlib.tests.scenarios import (
 
53
    load_tests_apply_scenarios,
 
54
    multiply_scenarios,
 
55
    )
53
56
from bzrlib.transport import (
54
57
    http,
55
58
    remote,
64
67
    from bzrlib.transport.http._pycurl import PyCurlTransport
65
68
 
66
69
 
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
 
                )))
 
70
load_tests = load_tests_apply_scenarios
 
71
 
 
72
 
 
73
def vary_by_http_client_implementation():
 
74
    """Test the two libraries we can use, pycurl and urllib."""
78
75
    transport_scenarios = [
79
76
        ('urllib', dict(_transport=_urllib.HttpTransport_urllib,
80
77
                        _server=http_server.HttpServer_urllib,
85
82
            ('pycurl', dict(_transport=PyCurlTransport,
86
83
                            _server=http_server.HttpServer_PyCurl,
87
84
                            _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)),
 
85
    return transport_scenarios
 
86
 
 
87
 
 
88
def vary_by_http_protocol_version():
 
89
    """Test on http/1.0 and 1.1"""
 
90
    return [
 
91
        ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
 
92
        ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
130
93
        ]
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 = [
 
94
 
 
95
 
 
96
def vary_by_http_auth_scheme():
 
97
    scenarios = [
141
98
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
142
99
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
143
100
        ('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
 
                )))
 
101
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
 
102
        ]
 
103
    # Add some attributes common to all scenarios
 
104
    for scenario_id, scenario_dict in scenarios:
 
105
        scenario_dict.update(_auth_header='Authorization',
 
106
                             _username_prompt_prefix='',
 
107
                             _password_prompt_prefix='')
 
108
    return scenarios
 
109
 
 
110
 
 
111
def vary_by_http_proxy_auth_scheme():
 
112
    scenarios = [
 
113
        ('proxy-basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
 
114
        ('proxy-digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
 
115
        ('proxy-basicdigest',
 
116
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
 
117
        ]
 
118
    # Add some attributes common to all scenarios
 
119
    for scenario_id, scenario_dict in scenarios:
 
120
        scenario_dict.update(_auth_header='Proxy-Authorization',
 
121
                             _username_prompt_prefix='Proxy ',
 
122
                             _password_prompt_prefix='Proxy ')
 
123
    return scenarios
 
124
 
 
125
 
 
126
def vary_by_http_activity():
155
127
    activity_scenarios = [
156
128
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
157
 
                             _transport=_urllib.HttpTransport_urllib,)),
 
129
                            _transport=_urllib.HttpTransport_urllib,)),
158
130
        ]
159
 
    if tests.HTTPSServerFeature.available():
 
131
    if features.HTTPSServerFeature.available():
160
132
        activity_scenarios.append(
161
133
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
162
 
                                  _transport=_urllib.HttpTransport_urllib,)),)
 
134
                                _transport=_urllib.HttpTransport_urllib,)),)
163
135
    if features.pycurl.available():
164
136
        activity_scenarios.append(
165
137
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
166
 
                                 _transport=PyCurlTransport,)),)
167
 
        if tests.HTTPSServerFeature.available():
 
138
                                _transport=PyCurlTransport,)),)
 
139
        if features.HTTPSServerFeature.available():
168
140
            from bzrlib.tests import (
169
141
                ssl_certs,
170
142
                )
181
153
 
182
154
            activity_scenarios.append(
183
155
                ('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
 
156
                                    _transport=HTTPS_pycurl_transport,)),)
 
157
    return activity_scenarios
194
158
 
195
159
 
196
160
class FakeManager(object):
229
193
        self._sock.bind(('127.0.0.1', 0))
230
194
        self.host, self.port = self._sock.getsockname()
231
195
        self._ready = threading.Event()
232
 
        self._thread = test_server.ThreadWithException(
233
 
            event=self._ready, target=self._accept_read_and_reply)
 
196
        self._thread = test_server.TestThread(
 
197
            sync_event=self._ready, target=self._accept_read_and_reply)
234
198
        self._thread.start()
235
199
        if 'threads' in tests.selftest_debug_flags:
236
200
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
305
269
        self.assertEqual('realm="Thou should not pass"', remainder)
306
270
 
307
271
 
 
272
class TestHTTPRangeParsing(tests.TestCase):
 
273
 
 
274
    def setUp(self):
 
275
        super(TestHTTPRangeParsing, self).setUp()
 
276
        # We focus on range  parsing here and ignore everything else
 
277
        class RequestHandler(http_server.TestingHTTPRequestHandler):
 
278
            def setup(self): pass
 
279
            def handle(self): pass
 
280
            def finish(self): pass
 
281
 
 
282
        self.req_handler = RequestHandler(None, None, None)
 
283
 
 
284
    def assertRanges(self, ranges, header, file_size):
 
285
        self.assertEquals(ranges,
 
286
                          self.req_handler._parse_ranges(header, file_size))
 
287
 
 
288
    def test_simple_range(self):
 
289
        self.assertRanges([(0,2)], 'bytes=0-2', 12)
 
290
 
 
291
    def test_tail(self):
 
292
        self.assertRanges([(8, 11)], 'bytes=-4', 12)
 
293
 
 
294
    def test_tail_bigger_than_file(self):
 
295
        self.assertRanges([(0, 11)], 'bytes=-99', 12)
 
296
 
 
297
    def test_range_without_end(self):
 
298
        self.assertRanges([(4, 11)], 'bytes=4-', 12)
 
299
 
 
300
    def test_invalid_ranges(self):
 
301
        self.assertRanges(None, 'bytes=12-22', 12)
 
302
        self.assertRanges(None, 'bytes=1-3,12-22', 12)
 
303
        self.assertRanges(None, 'bytes=-', 12)
 
304
 
 
305
 
308
306
class TestHTTPServer(tests.TestCase):
309
307
    """Test the HTTP servers implementations."""
310
308
 
401
399
class TestHttpTransportUrls(tests.TestCase):
402
400
    """Test the http urls."""
403
401
 
 
402
    scenarios = vary_by_http_client_implementation()
 
403
 
404
404
    def test_abs_url(self):
405
405
        """Construction of absolute http URLs"""
406
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
406
        t = self._transport('http://example.com/bzr/bzr.dev/')
407
407
        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')
 
408
        eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
 
409
        eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
 
410
        eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
411
411
        eq(t.abspath('.bzr/1//2/./3'),
412
 
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
 
412
           'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
413
413
 
414
414
    def test_invalid_http_urls(self):
415
415
        """Trap invalid construction of urls"""
416
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
416
        self._transport('http://example.com/bzr/bzr.dev/')
417
417
        self.assertRaises(errors.InvalidURL,
418
418
                          self._transport,
419
 
                          'http://http://bazaar-vcs.org/bzr/bzr.dev/')
 
419
                          'http://http://example.com/bzr/bzr.dev/')
420
420
 
421
421
    def test_http_root_urls(self):
422
422
        """Construction of URLs from server root"""
423
 
        t = self._transport('http://bzr.ozlabs.org/')
 
423
        t = self._transport('http://example.com/')
424
424
        eq = self.assertEqualDiff
425
425
        eq(t.abspath('.bzr/tree-version'),
426
 
           'http://bzr.ozlabs.org/.bzr/tree-version')
 
426
           'http://example.com/.bzr/tree-version')
427
427
 
428
428
    def test_http_impl_urls(self):
429
429
        """There are servers which ask for particular clients to connect"""
475
475
class TestHTTPConnections(http_utils.TestCaseWithWebserver):
476
476
    """Test the http connections."""
477
477
 
 
478
    scenarios = multiply_scenarios(
 
479
        vary_by_http_client_implementation(),
 
480
        vary_by_http_protocol_version(),
 
481
        )
 
482
 
478
483
    def setUp(self):
479
484
        http_utils.TestCaseWithWebserver.setUp(self)
480
485
        self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
525
530
class TestHttpTransportRegistration(tests.TestCase):
526
531
    """Test registrations of various http implementations"""
527
532
 
 
533
    scenarios = vary_by_http_client_implementation()
 
534
 
528
535
    def test_http_registered(self):
529
 
        t = transport.get_transport('%s://foo.com/' % self._url_protocol)
 
536
        t = transport.get_transport_from_url(
 
537
            '%s://foo.com/' % self._url_protocol)
530
538
        self.assertIsInstance(t, transport.Transport)
531
539
        self.assertIsInstance(t, self._transport)
532
540
 
533
541
 
534
542
class TestPost(tests.TestCase):
535
543
 
 
544
    scenarios = multiply_scenarios(
 
545
        vary_by_http_client_implementation(),
 
546
        vary_by_http_protocol_version(),
 
547
        )
 
548
 
536
549
    def test_post_body_is_received(self):
537
550
        server = RecordingServer(expect_body_tail='end-of-body',
538
551
                                 scheme=self._url_protocol)
539
552
        self.start_server(server)
540
553
        url = server.get_url()
541
554
        # FIXME: needs a cleanup -- vila 20100611
542
 
        http_transport = transport.get_transport(url)
 
555
        http_transport = transport.get_transport_from_url(url)
543
556
        code, response = http_transport._post('abc def end-of-body')
544
557
        self.assertTrue(
545
558
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
546
559
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
 
560
        self.assertTrue('content-type: application/octet-stream\r'
 
561
                        in server.received_bytes.lower())
547
562
        # The transport should not be assuming that the server can accept
548
563
        # chunked encoding the first time it connects, because HTTP/1.1, so we
549
564
        # check for the literal string.
585
600
    Daughter classes are expected to override _req_handler_class
586
601
    """
587
602
 
 
603
    scenarios = multiply_scenarios(
 
604
        vary_by_http_client_implementation(),
 
605
        vary_by_http_protocol_version(),
 
606
        )
 
607
 
588
608
    # Provide a useful default
589
609
    _req_handler_class = http_server.TestingHTTPRequestHandler
590
610
 
841
861
        t = self.get_readonly_transport()
842
862
        # force transport to issue multiple requests
843
863
        t._get_max_size = 2
844
 
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
 
864
        list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
845
865
        # The server should have issued 3 requests
846
866
        self.assertEqual(3, server.GET_request_nb)
847
867
        self.assertEqual('0123456789', t.get_bytes('a'))
924
944
    def get_multiple_ranges(self, file, file_size, ranges):
925
945
        self.send_response(206)
926
946
        self.send_header('Accept-Ranges', 'bytes')
 
947
        # XXX: this is strange; the 'random' name below seems undefined and
 
948
        # yet the tests pass -- mbp 2010-10-11 bug 658773
927
949
        boundary = "%d" % random.randint(0,0x7FFFFFFF)
928
950
        self.send_header("Content-Type",
929
951
                         "multipart/byteranges; boundary=%s" % boundary)
991
1013
                return
992
1014
            self.send_range_content(file, start, end - start + 1)
993
1015
            cur += 1
994
 
        # No final boundary
 
1016
        # Final boundary
995
1017
        self.wfile.write(boundary_line)
996
1018
 
997
1019
 
1026
1048
        # that mode
1027
1049
        self.assertEqual('single', t._range_hint)
1028
1050
 
 
1051
 
 
1052
class TruncatedBeforeBoundaryRequestHandler(
 
1053
    http_server.TestingHTTPRequestHandler):
 
1054
    """Truncation before a boundary, like in bug 198646"""
 
1055
 
 
1056
    _truncated_ranges = 1
 
1057
 
 
1058
    def get_multiple_ranges(self, file, file_size, ranges):
 
1059
        self.send_response(206)
 
1060
        self.send_header('Accept-Ranges', 'bytes')
 
1061
        boundary = 'tagada'
 
1062
        self.send_header('Content-Type',
 
1063
                         'multipart/byteranges; boundary=%s' % boundary)
 
1064
        boundary_line = '--%s\r\n' % boundary
 
1065
        # Calculate the Content-Length
 
1066
        content_length = 0
 
1067
        for (start, end) in ranges:
 
1068
            content_length += len(boundary_line)
 
1069
            content_length += self._header_line_length(
 
1070
                'Content-type', 'application/octet-stream')
 
1071
            content_length += self._header_line_length(
 
1072
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
 
1073
            content_length += len('\r\n') # end headers
 
1074
            content_length += end - start # + 1
 
1075
        content_length += len(boundary_line)
 
1076
        self.send_header('Content-length', content_length)
 
1077
        self.end_headers()
 
1078
 
 
1079
        # Send the multipart body
 
1080
        cur = 0
 
1081
        for (start, end) in ranges:
 
1082
            if cur + self._truncated_ranges >= len(ranges):
 
1083
                # Abruptly ends the response and close the connection
 
1084
                self.close_connection = 1
 
1085
                return
 
1086
            self.wfile.write(boundary_line)
 
1087
            self.send_header('Content-type', 'application/octet-stream')
 
1088
            self.send_header('Content-Range', 'bytes %d-%d/%d'
 
1089
                             % (start, end, file_size))
 
1090
            self.end_headers()
 
1091
            self.send_range_content(file, start, end - start + 1)
 
1092
            cur += 1
 
1093
        # Final boundary
 
1094
        self.wfile.write(boundary_line)
 
1095
 
 
1096
 
 
1097
class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
 
1098
    """Tests the case of bug 198646, disconnecting before a boundary."""
 
1099
 
 
1100
    _req_handler_class = TruncatedBeforeBoundaryRequestHandler
 
1101
 
 
1102
    def setUp(self):
 
1103
        super(TestTruncatedBeforeBoundary, self).setUp()
 
1104
        self.build_tree_contents([('a', '0123456789')],)
 
1105
 
 
1106
    def test_readv_with_short_reads(self):
 
1107
        server = self.get_readonly_server()
 
1108
        t = self.get_readonly_transport()
 
1109
        # Force separate ranges for each offset
 
1110
        t._bytes_to_read_before_seek = 0
 
1111
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
 
1112
        self.assertEqual((0, '0'), ireadv.next())
 
1113
        self.assertEqual((2, '2'), ireadv.next())
 
1114
        self.assertEqual((4, '45'), ireadv.next())
 
1115
        self.assertEqual((9, '9'), ireadv.next())
 
1116
 
 
1117
 
1029
1118
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1030
1119
    """Errors out when range specifiers exceed the limit"""
1031
1120
 
1055
1144
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1056
1145
    """Tests readv requests against a server erroring out on too much ranges."""
1057
1146
 
 
1147
    scenarios = multiply_scenarios(
 
1148
        vary_by_http_client_implementation(),
 
1149
        vary_by_http_protocol_version(),
 
1150
        )
 
1151
 
1058
1152
    # Requests with more range specifiers will error out
1059
1153
    range_limit = 3
1060
1154
 
1095
1189
    Only the urllib implementation is tested here.
1096
1190
    """
1097
1191
 
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
1192
    def _proxied_request(self):
1112
1193
        handler = _urllib2_wrappers.ProxyHandler()
1113
 
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
 
1194
        request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
1114
1195
        handler.set_proxy(request, 'http')
1115
1196
        return request
1116
1197
 
 
1198
    def assertEvaluateProxyBypass(self, expected, host, no_proxy):
 
1199
        handler = _urllib2_wrappers.ProxyHandler()
 
1200
        self.assertEquals(expected,
 
1201
                          handler.evaluate_proxy_bypass(host, no_proxy))
 
1202
 
1117
1203
    def test_empty_user(self):
1118
 
        self._install_env({'http_proxy': 'http://bar.com'})
 
1204
        self.overrideEnv('http_proxy', 'http://bar.com')
 
1205
        request = self._proxied_request()
 
1206
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
 
1207
 
 
1208
    def test_user_with_at(self):
 
1209
        self.overrideEnv('http_proxy',
 
1210
                         'http://username@domain:password@proxy_host:1234')
1119
1211
        request = self._proxied_request()
1120
1212
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1121
1213
 
1122
1214
    def test_invalid_proxy(self):
1123
1215
        """A proxy env variable without scheme"""
1124
 
        self._install_env({'http_proxy': 'host:1234'})
 
1216
        self.overrideEnv('http_proxy', 'host:1234')
1125
1217
        self.assertRaises(errors.InvalidURL, self._proxied_request)
1126
1218
 
 
1219
    def test_evaluate_proxy_bypass_true(self):
 
1220
        """The host is not proxied"""
 
1221
        self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
 
1222
        self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
 
1223
 
 
1224
    def test_evaluate_proxy_bypass_false(self):
 
1225
        """The host is proxied"""
 
1226
        self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
 
1227
 
 
1228
    def test_evaluate_proxy_bypass_unknown(self):
 
1229
        """The host is not explicitly proxied"""
 
1230
        self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
 
1231
        self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
 
1232
 
 
1233
    def test_evaluate_proxy_bypass_empty_entries(self):
 
1234
        """Ignore empty entries"""
 
1235
        self.assertEvaluateProxyBypass(None, 'example.com', '')
 
1236
        self.assertEvaluateProxyBypass(None, 'example.com', ',')
 
1237
        self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
 
1238
 
1127
1239
 
1128
1240
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
1129
1241
    """Tests proxy server.
1134
1246
    to the file names).
1135
1247
    """
1136
1248
 
 
1249
    scenarios = multiply_scenarios(
 
1250
        vary_by_http_client_implementation(),
 
1251
        vary_by_http_protocol_version(),
 
1252
        )
 
1253
 
1137
1254
    # FIXME: We don't have an https server available, so we don't
1138
1255
    # test https connections. --vila toolongago
1139
1256
 
1153
1270
            self.no_proxy_host = self.server_host_port
1154
1271
        # The secondary server is the proxy
1155
1272
        self.proxy_url = self.get_secondary_url()
1156
 
        self._old_env = {}
1157
1273
 
1158
1274
    def _testing_pycurl(self):
1159
1275
        # TODO: This is duplicated for lots of the classes in this file
1160
1276
        return (features.pycurl.available()
1161
1277
                and self._transport == PyCurlTransport)
1162
1278
 
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()
 
1279
    def assertProxied(self):
 
1280
        t = self.get_readonly_transport()
 
1281
        self.assertEqual('proxied contents of foo\n', t.get('foo').read())
 
1282
 
 
1283
    def assertNotProxied(self):
 
1284
        t = self.get_readonly_transport()
 
1285
        self.assertEqual('contents of foo\n', t.get('foo').read())
1186
1286
 
1187
1287
    def test_http_proxy(self):
1188
 
        self.proxied_in_env({'http_proxy': self.proxy_url})
 
1288
        self.overrideEnv('http_proxy', self.proxy_url)
 
1289
        self.assertProxied()
1189
1290
 
1190
1291
    def test_HTTP_PROXY(self):
1191
1292
        if self._testing_pycurl():
1194
1295
            # about. Should we ?)
1195
1296
            raise tests.TestNotApplicable(
1196
1297
                'pycurl does not check HTTP_PROXY for security reasons')
1197
 
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
 
1298
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1299
        self.assertProxied()
1198
1300
 
1199
1301
    def test_all_proxy(self):
1200
 
        self.proxied_in_env({'all_proxy': self.proxy_url})
 
1302
        self.overrideEnv('all_proxy', self.proxy_url)
 
1303
        self.assertProxied()
1201
1304
 
1202
1305
    def test_ALL_PROXY(self):
1203
 
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
 
1306
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1307
        self.assertProxied()
1204
1308
 
1205
1309
    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})
 
1310
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1311
        self.overrideEnv('http_proxy', self.proxy_url)
 
1312
        self.assertNotProxied()
1208
1313
 
1209
1314
    def test_HTTP_PROXY_with_NO_PROXY(self):
1210
1315
        if self._testing_pycurl():
1211
1316
            raise tests.TestNotApplicable(
1212
1317
                '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})
 
1318
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1319
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1320
        self.assertNotProxied()
1215
1321
 
1216
1322
    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})
 
1323
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1324
        self.overrideEnv('all_proxy', self.proxy_url)
 
1325
        self.assertNotProxied()
1219
1326
 
1220
1327
    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})
 
1328
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1329
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1330
        self.assertNotProxied()
1223
1331
 
1224
1332
    def test_http_proxy_without_scheme(self):
 
1333
        self.overrideEnv('http_proxy', self.server_host_port)
1225
1334
        if self._testing_pycurl():
1226
1335
            # pycurl *ignores* invalid proxy env variables. If that ever change
1227
1336
            # in the future, this test will fail indicating that pycurl do not
1228
1337
            # ignore anymore such variables.
1229
 
            self.not_proxied_in_env({'http_proxy': self.server_host_port})
 
1338
            self.assertNotProxied()
1230
1339
        else:
1231
 
            self.assertRaises(errors.InvalidURL,
1232
 
                              self.proxied_in_env,
1233
 
                              {'http_proxy': self.server_host_port})
 
1340
            self.assertRaises(errors.InvalidURL, self.assertProxied)
1234
1341
 
1235
1342
 
1236
1343
class TestRanges(http_utils.TestCaseWithWebserver):
1237
1344
    """Test the Range header in GET methods."""
1238
1345
 
 
1346
    scenarios = multiply_scenarios(
 
1347
        vary_by_http_client_implementation(),
 
1348
        vary_by_http_protocol_version(),
 
1349
        )
 
1350
 
1239
1351
    def setUp(self):
1240
1352
        http_utils.TestCaseWithWebserver.setUp(self)
1241
1353
        self.build_tree_contents([('a', '0123456789')],)
1281
1393
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1282
1394
    """Test redirection between http servers."""
1283
1395
 
 
1396
    scenarios = multiply_scenarios(
 
1397
        vary_by_http_client_implementation(),
 
1398
        vary_by_http_protocol_version(),
 
1399
        )
 
1400
 
1284
1401
    def setUp(self):
1285
1402
        super(TestHTTPRedirections, self).setUp()
1286
1403
        self.build_tree_contents([('a', '0123456789'),
1349
1466
    -- vila 20070212
1350
1467
    """
1351
1468
 
 
1469
    scenarios = multiply_scenarios(
 
1470
        vary_by_http_client_implementation(),
 
1471
        vary_by_http_protocol_version(),
 
1472
        )
 
1473
 
1352
1474
    def setUp(self):
1353
1475
        if (features.pycurl.available()
1354
1476
            and self._transport == PyCurlTransport):
1399
1521
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1400
1522
    """Test transport.do_catching_redirections."""
1401
1523
 
 
1524
    scenarios = multiply_scenarios(
 
1525
        vary_by_http_client_implementation(),
 
1526
        vary_by_http_protocol_version(),
 
1527
        )
 
1528
 
1402
1529
    def setUp(self):
1403
1530
        super(TestDoCatchRedirections, self).setUp()
1404
1531
        self.build_tree_contents([('a', '0123456789'),],)
1443
1570
                          self.get_a, self.old_transport, redirected)
1444
1571
 
1445
1572
 
 
1573
def _setup_authentication_config(**kwargs):
 
1574
    conf = config.AuthenticationConfig()
 
1575
    conf._get_config().update({'httptest': kwargs})
 
1576
    conf._save()
 
1577
 
 
1578
 
 
1579
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
 
1580
    """Unit tests for glue by which urllib2 asks us for authentication"""
 
1581
 
 
1582
    def test_get_user_password_without_port(self):
 
1583
        """We cope if urllib2 doesn't tell us the port.
 
1584
 
 
1585
        See https://bugs.launchpad.net/bzr/+bug/654684
 
1586
        """
 
1587
        user = 'joe'
 
1588
        password = 'foo'
 
1589
        _setup_authentication_config(scheme='http', host='localhost',
 
1590
                                     user=user, password=password)
 
1591
        handler = _urllib2_wrappers.HTTPAuthHandler()
 
1592
        got_pass = handler.get_user_password(dict(
 
1593
            user='joe',
 
1594
            protocol='http',
 
1595
            host='localhost',
 
1596
            path='/',
 
1597
            realm='Realm',
 
1598
            ))
 
1599
        self.assertEquals((user, password), got_pass)
 
1600
 
 
1601
 
1446
1602
class TestAuth(http_utils.TestCaseWithWebserver):
1447
1603
    """Test authentication scheme"""
1448
1604
 
1449
 
    _auth_header = 'Authorization'
1450
 
    _password_prompt_prefix = ''
1451
 
    _username_prompt_prefix = ''
1452
 
    # Set by load_tests
1453
 
    _auth_server = None
 
1605
    scenarios = multiply_scenarios(
 
1606
        vary_by_http_client_implementation(),
 
1607
        vary_by_http_protocol_version(),
 
1608
        vary_by_http_auth_scheme(),
 
1609
        )
1454
1610
 
1455
1611
    def setUp(self):
1456
1612
        super(TestAuth, self).setUp()
1480
1636
        return url
1481
1637
 
1482
1638
    def get_user_transport(self, user, password):
1483
 
        t = transport.get_transport(self.get_user_url(user, password))
 
1639
        t = transport.get_transport_from_url(
 
1640
            self.get_user_url(user, password))
1484
1641
        return t
1485
1642
 
1486
1643
    def test_no_user(self):
1598
1755
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1599
1756
                                            stderr=tests.StringIOWrapper())
1600
1757
        # 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()
 
1758
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1759
                                     user=user, password=password)
1606
1760
        # Issue a request to the server to connect
1607
1761
        self.assertEqual('contents of a\n',t.get('a').read())
1608
1762
        # stdin should have  been left untouched
1610
1764
        # Only one 'Authentication Required' error should occur
1611
1765
        self.assertEqual(1, self.server.auth_required_errors)
1612
1766
 
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
1767
    def test_changing_nonce(self):
1633
1768
        if self._auth_server not in (http_utils.HTTPDigestAuthServer,
1634
1769
                                     http_utils.ProxyDigestAuthServer):
1635
1770
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1636
1771
        if self._testing_pycurl():
1637
 
            raise tests.KnownFailure(
 
1772
            self.knownFailure(
1638
1773
                'pycurl does not handle a nonce change')
1639
1774
        self.server.add_user('joe', 'foo')
1640
1775
        t = self.get_user_transport('joe', 'foo')
1650
1785
        # initial 'who are you' and a second 'who are you' with the new nonce)
1651
1786
        self.assertEqual(2, self.server.auth_required_errors)
1652
1787
 
 
1788
    def test_user_from_auth_conf(self):
 
1789
        if self._testing_pycurl():
 
1790
            raise tests.TestNotApplicable(
 
1791
                'pycurl does not support authentication.conf')
 
1792
        user = 'joe'
 
1793
        password = 'foo'
 
1794
        self.server.add_user(user, password)
 
1795
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1796
                                     user=user, password=password)
 
1797
        t = self.get_user_transport(None, None)
 
1798
        # Issue a request to the server to connect
 
1799
        self.assertEqual('contents of a\n', t.get('a').read())
 
1800
        # Only one 'Authentication Required' error should occur
 
1801
        self.assertEqual(1, self.server.auth_required_errors)
 
1802
 
 
1803
    def test_no_credential_leaks_in_log(self):
 
1804
        self.overrideAttr(debug, 'debug_flags', set(['http']))
 
1805
        user = 'joe'
 
1806
        password = 'very-sensitive-password'
 
1807
        self.server.add_user(user, password)
 
1808
        t = self.get_user_transport(user, password)
 
1809
        # Capture the debug calls to mutter
 
1810
        self.mutters = []
 
1811
        def mutter(*args):
 
1812
            lines = args[0] % args[1:]
 
1813
            # Some calls output multiple lines, just split them now since we
 
1814
            # care about a single one later.
 
1815
            self.mutters.extend(lines.splitlines())
 
1816
        self.overrideAttr(trace, 'mutter', mutter)
 
1817
        # Issue a request to the server to connect
 
1818
        self.assertEqual(True, t.has('a'))
 
1819
        # Only one 'Authentication Required' error should occur
 
1820
        self.assertEqual(1, self.server.auth_required_errors)
 
1821
        # Since the authentification succeeded, there should be a corresponding
 
1822
        # debug line
 
1823
        sent_auth_headers = [line for line in self.mutters
 
1824
                             if line.startswith('> %s' % (self._auth_header,))]
 
1825
        self.assertLength(1, sent_auth_headers)
 
1826
        self.assertStartsWith(sent_auth_headers[0],
 
1827
                              '> %s: <masked>' % (self._auth_header,))
1653
1828
 
1654
1829
 
1655
1830
class TestProxyAuth(TestAuth):
1656
 
    """Test proxy authentication schemes."""
1657
 
 
1658
 
    _auth_header = 'Proxy-authorization'
1659
 
    _password_prompt_prefix = 'Proxy '
1660
 
    _username_prompt_prefix = 'Proxy '
 
1831
    """Test proxy authentication schemes.
 
1832
 
 
1833
    This inherits from TestAuth to tweak the setUp and filter some failing
 
1834
    tests.
 
1835
    """
 
1836
 
 
1837
    scenarios = multiply_scenarios(
 
1838
        vary_by_http_client_implementation(),
 
1839
        vary_by_http_protocol_version(),
 
1840
        vary_by_http_proxy_auth_scheme(),
 
1841
        )
1661
1842
 
1662
1843
    def setUp(self):
1663
1844
        super(TestProxyAuth, self).setUp()
1664
 
        self._old_env = {}
1665
 
        self.addCleanup(self._restore_env)
1666
1845
        # Override the contents to avoid false positives
1667
1846
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1668
1847
                                  ('b', 'not proxied contents of b\n'),
1671
1850
                                  ])
1672
1851
 
1673
1852
    def get_user_transport(self, user, password):
1674
 
        self._install_env({'all_proxy': self.get_user_url(user, password)})
 
1853
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
1675
1854
        return TestAuth.get_user_transport(self, user, password)
1676
1855
 
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
1856
    def test_empty_pass(self):
1686
1857
        if self._testing_pycurl():
1687
1858
            import pycurl
1688
1859
            if pycurl.version_info()[1] < '7.16.0':
1689
 
                raise tests.KnownFailure(
 
1860
                self.knownFailure(
1690
1861
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1691
1862
        super(TestProxyAuth, self).test_empty_pass()
1692
1863
 
1716
1887
 
1717
1888
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1718
1889
 
 
1890
    scenarios = multiply_scenarios(
 
1891
        vary_by_http_client_implementation(),
 
1892
        vary_by_http_protocol_version(),
 
1893
        )
 
1894
 
1719
1895
    def setUp(self):
1720
1896
        super(SmartHTTPTunnellingTest, self).setUp()
1721
1897
        # We use the VFS layer as part of HTTP tunnelling tests.
1722
 
        self._captureVar('BZR_NO_SMART_VFS', None)
 
1898
        self.overrideEnv('BZR_NO_SMART_VFS', None)
1723
1899
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1724
1900
        self.http_server = self.get_readonly_server()
1725
1901
 
1741
1917
        # The 'readv' command in the smart protocol both sends and receives
1742
1918
        # bulk data, so we use that.
1743
1919
        self.build_tree(['data-file'])
1744
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1920
        http_transport = transport.get_transport_from_url(
 
1921
            self.http_server.get_url())
1745
1922
        medium = http_transport.get_smart_medium()
1746
1923
        # Since we provide the medium, the url below will be mostly ignored
1747
1924
        # during the test, as long as the path is '/'.
1755
1932
        post_body = 'hello\n'
1756
1933
        expected_reply_body = 'ok\x012\n'
1757
1934
 
1758
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1935
        http_transport = transport.get_transport_from_url(
 
1936
            self.http_server.get_url())
1759
1937
        medium = http_transport.get_smart_medium()
1760
1938
        response = medium.send_http_smart_request(post_body)
1761
1939
        reply_body = response.read()
1810
1988
 
1811
1989
class Test_redirected_to(tests.TestCase):
1812
1990
 
 
1991
    scenarios = vary_by_http_client_implementation()
 
1992
 
1813
1993
    def test_redirected_to_subdir(self):
1814
1994
        t = self._transport('http://www.example.com/foo')
1815
1995
        r = t._redirected_to('http://www.example.com/foo',
1817
1997
        self.assertIsInstance(r, type(t))
1818
1998
        # Both transports share the some connection
1819
1999
        self.assertEqual(t._get_connection(), r._get_connection())
 
2000
        self.assertEquals('http://www.example.com/foo/subdir/', r.base)
1820
2001
 
1821
2002
    def test_redirected_to_self_with_slash(self):
1822
2003
        t = self._transport('http://www.example.com/foo')
1833
2014
        r = t._redirected_to('http://www.example.com/foo',
1834
2015
                             'http://foo.example.com/foo/subdir')
1835
2016
        self.assertIsInstance(r, type(t))
 
2017
        self.assertEquals('http://foo.example.com/foo/subdir/',
 
2018
            r.external_url())
1836
2019
 
1837
2020
    def test_redirected_to_same_host_sibling_protocol(self):
1838
2021
        t = self._transport('http://www.example.com/foo')
1839
2022
        r = t._redirected_to('http://www.example.com/foo',
1840
2023
                             'https://www.example.com/foo')
1841
2024
        self.assertIsInstance(r, type(t))
 
2025
        self.assertEquals('https://www.example.com/foo/',
 
2026
            r.external_url())
1842
2027
 
1843
2028
    def test_redirected_to_same_host_different_protocol(self):
1844
2029
        t = self._transport('http://www.example.com/foo')
1845
2030
        r = t._redirected_to('http://www.example.com/foo',
1846
2031
                             'ftp://www.example.com/foo')
1847
2032
        self.assertNotEquals(type(r), type(t))
 
2033
        self.assertEquals('ftp://www.example.com/foo/', r.external_url())
 
2034
 
 
2035
    def test_redirected_to_same_host_specific_implementation(self):
 
2036
        t = self._transport('http://www.example.com/foo')
 
2037
        r = t._redirected_to('http://www.example.com/foo',
 
2038
                             'https+urllib://www.example.com/foo')
 
2039
        self.assertEquals('https://www.example.com/foo/', r.external_url())
1848
2040
 
1849
2041
    def test_redirected_to_different_host_same_user(self):
1850
2042
        t = self._transport('http://joe@www.example.com/foo')
1851
2043
        r = t._redirected_to('http://www.example.com/foo',
1852
2044
                             'https://foo.example.com/foo')
1853
2045
        self.assertIsInstance(r, type(t))
1854
 
        self.assertEqual(t._user, r._user)
 
2046
        self.assertEqual(t._parsed_url.user, r._parsed_url.user)
 
2047
        self.assertEquals('https://joe@foo.example.com/foo/', r.external_url())
1855
2048
 
1856
2049
 
1857
2050
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
1910
2103
    pass
1911
2104
 
1912
2105
 
1913
 
if tests.HTTPSServerFeature.available():
 
2106
if features.HTTPSServerFeature.available():
1914
2107
    from bzrlib.tests import https_server
1915
2108
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
1916
2109
        pass
1927
2120
        tests.TestCase.setUp(self)
1928
2121
        self.server = self._activity_server(self._protocol_version)
1929
2122
        self.server.start_server()
1930
 
        self.activities = {}
 
2123
        _activities = {} # Don't close over self and create a cycle
1931
2124
        def report_activity(t, bytes, direction):
1932
 
            count = self.activities.get(direction, 0)
 
2125
            count = _activities.get(direction, 0)
1933
2126
            count += bytes
1934
 
            self.activities[direction] = count
 
2127
            _activities[direction] = count
 
2128
        self.activities = _activities
1935
2129
 
1936
2130
        # We override at class level because constructors may propagate the
1937
2131
        # bound method and render instance overriding ineffective (an
2061
2255
 
2062
2256
class TestActivity(tests.TestCase, TestActivityMixin):
2063
2257
 
 
2258
    scenarios = multiply_scenarios(
 
2259
        vary_by_http_activity(),
 
2260
        vary_by_http_protocol_version(),
 
2261
        )
 
2262
 
2064
2263
    def setUp(self):
2065
2264
        TestActivityMixin.setUp(self)
2066
2265
 
2087
2286
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2088
2287
    """Test authentication on the redirected http server."""
2089
2288
 
 
2289
    scenarios = vary_by_http_protocol_version()
 
2290
 
2090
2291
    _auth_header = 'Authorization'
2091
2292
    _password_prompt_prefix = ''
2092
2293
    _username_prompt_prefix = ''
2155
2356
        # stdout should be empty, stderr will contains the prompts
2156
2357
        self.assertEqual('', stdout.getvalue())
2157
2358
 
2158