~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Jelmer Vernooij
  • Date: 2011-12-16 19:18:39 UTC
  • mto: This revision was merged to the branch mainline in revision 6391.
  • Revision ID: jelmer@samba.org-20111216191839-eg681lxqibi1qxu1
Fix remaining tests.

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
32
32
import bzrlib
33
33
from bzrlib import (
34
34
    bzrdir,
 
35
    cethread,
35
36
    config,
 
37
    debug,
36
38
    errors,
37
39
    osutils,
38
40
    remote as _mod_remote,
39
41
    tests,
 
42
    trace,
40
43
    transport,
41
44
    ui,
42
45
    )
90
93
        ]
91
94
 
92
95
 
93
 
def vary_by_http_proxy_auth_scheme():
94
 
    return [
95
 
        ('basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
96
 
        ('digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
97
 
        ('basicdigest',
98
 
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
99
 
        ]
100
 
 
101
 
 
102
96
def vary_by_http_auth_scheme():
103
 
    return [
 
97
    scenarios = [
104
98
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
105
99
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
106
100
        ('basicdigest',
107
101
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
108
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
109
124
 
110
125
 
111
126
def vary_by_http_activity():
113
128
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
114
129
                            _transport=_urllib.HttpTransport_urllib,)),
115
130
        ]
116
 
    if tests.HTTPSServerFeature.available():
 
131
    if features.HTTPSServerFeature.available():
117
132
        activity_scenarios.append(
118
133
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
119
134
                                _transport=_urllib.HttpTransport_urllib,)),)
121
136
        activity_scenarios.append(
122
137
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
123
138
                                _transport=PyCurlTransport,)),)
124
 
        if tests.HTTPSServerFeature.available():
 
139
        if features.HTTPSServerFeature.available():
125
140
            from bzrlib.tests import (
126
141
                ssl_certs,
127
142
                )
178
193
        self._sock.bind(('127.0.0.1', 0))
179
194
        self.host, self.port = self._sock.getsockname()
180
195
        self._ready = threading.Event()
181
 
        self._thread = test_server.ThreadWithException(
182
 
            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)
183
198
        self._thread.start()
184
199
        if 'threads' in tests.selftest_debug_flags:
185
200
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
254
269
        self.assertEqual('realm="Thou should not pass"', remainder)
255
270
 
256
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
 
257
306
class TestHTTPServer(tests.TestCase):
258
307
    """Test the HTTP servers implementations."""
259
308
 
354
403
 
355
404
    def test_abs_url(self):
356
405
        """Construction of absolute http URLs"""
357
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
406
        t = self._transport('http://example.com/bzr/bzr.dev/')
358
407
        eq = self.assertEqualDiff
359
 
        eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
360
 
        eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
361
 
        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')
362
411
        eq(t.abspath('.bzr/1//2/./3'),
363
 
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
 
412
           'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
364
413
 
365
414
    def test_invalid_http_urls(self):
366
415
        """Trap invalid construction of urls"""
367
 
        self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
416
        self._transport('http://example.com/bzr/bzr.dev/')
368
417
        self.assertRaises(errors.InvalidURL,
369
418
                          self._transport,
370
 
                          'http://http://bazaar-vcs.org/bzr/bzr.dev/')
 
419
                          'http://http://example.com/bzr/bzr.dev/')
371
420
 
372
421
    def test_http_root_urls(self):
373
422
        """Construction of URLs from server root"""
374
 
        t = self._transport('http://bzr.ozlabs.org/')
 
423
        t = self._transport('http://example.com/')
375
424
        eq = self.assertEqualDiff
376
425
        eq(t.abspath('.bzr/tree-version'),
377
 
           'http://bzr.ozlabs.org/.bzr/tree-version')
 
426
           'http://example.com/.bzr/tree-version')
378
427
 
379
428
    def test_http_impl_urls(self):
380
429
        """There are servers which ask for particular clients to connect"""
427
476
    """Test the http connections."""
428
477
 
429
478
    scenarios = multiply_scenarios(
430
 
        vary_by_http_client_implementation(), 
 
479
        vary_by_http_client_implementation(),
431
480
        vary_by_http_protocol_version(),
432
481
        )
433
482
 
484
533
    scenarios = vary_by_http_client_implementation()
485
534
 
486
535
    def test_http_registered(self):
487
 
        t = transport.get_transport('%s://foo.com/' % self._url_protocol)
 
536
        t = transport.get_transport_from_url(
 
537
            '%s://foo.com/' % self._url_protocol)
488
538
        self.assertIsInstance(t, transport.Transport)
489
539
        self.assertIsInstance(t, self._transport)
490
540
 
492
542
class TestPost(tests.TestCase):
493
543
 
494
544
    scenarios = multiply_scenarios(
495
 
        vary_by_http_client_implementation(), 
 
545
        vary_by_http_client_implementation(),
496
546
        vary_by_http_protocol_version(),
497
547
        )
498
548
 
502
552
        self.start_server(server)
503
553
        url = server.get_url()
504
554
        # FIXME: needs a cleanup -- vila 20100611
505
 
        http_transport = transport.get_transport(url)
 
555
        http_transport = transport.get_transport_from_url(url)
506
556
        code, response = http_transport._post('abc def end-of-body')
507
557
        self.assertTrue(
508
558
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
551
601
    """
552
602
 
553
603
    scenarios = multiply_scenarios(
554
 
        vary_by_http_client_implementation(), 
 
604
        vary_by_http_client_implementation(),
555
605
        vary_by_http_protocol_version(),
556
606
        )
557
607
 
999
1049
        self.assertEqual('single', t._range_hint)
1000
1050
 
1001
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
 
1002
1118
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1003
1119
    """Errors out when range specifiers exceed the limit"""
1004
1120
 
1029
1145
    """Tests readv requests against a server erroring out on too much ranges."""
1030
1146
 
1031
1147
    scenarios = multiply_scenarios(
1032
 
        vary_by_http_client_implementation(), 
 
1148
        vary_by_http_client_implementation(),
1033
1149
        vary_by_http_protocol_version(),
1034
1150
        )
1035
1151
 
1073
1189
    Only the urllib implementation is tested here.
1074
1190
    """
1075
1191
 
1076
 
    def setUp(self):
1077
 
        tests.TestCase.setUp(self)
1078
 
        self._old_env = {}
1079
 
        self.addCleanup(self._restore_env)
1080
 
 
1081
 
    def _install_env(self, env):
1082
 
        for name, value in env.iteritems():
1083
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1084
 
 
1085
 
    def _restore_env(self):
1086
 
        for name, value in self._old_env.iteritems():
1087
 
            osutils.set_or_unset_env(name, value)
1088
 
 
1089
1192
    def _proxied_request(self):
1090
1193
        handler = _urllib2_wrappers.ProxyHandler()
1091
 
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
 
1194
        request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
1092
1195
        handler.set_proxy(request, 'http')
1093
1196
        return request
1094
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
 
1095
1203
    def test_empty_user(self):
1096
 
        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')
1097
1211
        request = self._proxied_request()
1098
1212
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1099
1213
 
1100
1214
    def test_invalid_proxy(self):
1101
1215
        """A proxy env variable without scheme"""
1102
 
        self._install_env({'http_proxy': 'host:1234'})
 
1216
        self.overrideEnv('http_proxy', 'host:1234')
1103
1217
        self.assertRaises(errors.InvalidURL, self._proxied_request)
1104
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
 
1105
1239
 
1106
1240
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
1107
1241
    """Tests proxy server.
1113
1247
    """
1114
1248
 
1115
1249
    scenarios = multiply_scenarios(
1116
 
        vary_by_http_client_implementation(), 
 
1250
        vary_by_http_client_implementation(),
1117
1251
        vary_by_http_protocol_version(),
1118
1252
        )
1119
1253
 
1136
1270
            self.no_proxy_host = self.server_host_port
1137
1271
        # The secondary server is the proxy
1138
1272
        self.proxy_url = self.get_secondary_url()
1139
 
        self._old_env = {}
1140
1273
 
1141
1274
    def _testing_pycurl(self):
1142
1275
        # TODO: This is duplicated for lots of the classes in this file
1143
1276
        return (features.pycurl.available()
1144
1277
                and self._transport == PyCurlTransport)
1145
1278
 
1146
 
    def _install_env(self, env):
1147
 
        for name, value in env.iteritems():
1148
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1149
 
 
1150
 
    def _restore_env(self):
1151
 
        for name, value in self._old_env.iteritems():
1152
 
            osutils.set_or_unset_env(name, value)
1153
 
 
1154
 
    def proxied_in_env(self, env):
1155
 
        self._install_env(env)
1156
 
        t = self.get_readonly_transport()
1157
 
        try:
1158
 
            self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1159
 
        finally:
1160
 
            self._restore_env()
1161
 
 
1162
 
    def not_proxied_in_env(self, env):
1163
 
        self._install_env(env)
1164
 
        t = self.get_readonly_transport()
1165
 
        try:
1166
 
            self.assertEqual('contents of foo\n', t.get('foo').read())
1167
 
        finally:
1168
 
            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())
1169
1286
 
1170
1287
    def test_http_proxy(self):
1171
 
        self.proxied_in_env({'http_proxy': self.proxy_url})
 
1288
        self.overrideEnv('http_proxy', self.proxy_url)
 
1289
        self.assertProxied()
1172
1290
 
1173
1291
    def test_HTTP_PROXY(self):
1174
1292
        if self._testing_pycurl():
1177
1295
            # about. Should we ?)
1178
1296
            raise tests.TestNotApplicable(
1179
1297
                'pycurl does not check HTTP_PROXY for security reasons')
1180
 
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
 
1298
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1299
        self.assertProxied()
1181
1300
 
1182
1301
    def test_all_proxy(self):
1183
 
        self.proxied_in_env({'all_proxy': self.proxy_url})
 
1302
        self.overrideEnv('all_proxy', self.proxy_url)
 
1303
        self.assertProxied()
1184
1304
 
1185
1305
    def test_ALL_PROXY(self):
1186
 
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
 
1306
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1307
        self.assertProxied()
1187
1308
 
1188
1309
    def test_http_proxy_with_no_proxy(self):
1189
 
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
1190
 
                                 '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()
1191
1313
 
1192
1314
    def test_HTTP_PROXY_with_NO_PROXY(self):
1193
1315
        if self._testing_pycurl():
1194
1316
            raise tests.TestNotApplicable(
1195
1317
                'pycurl does not check HTTP_PROXY for security reasons')
1196
 
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
1197
 
                                 '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()
1198
1321
 
1199
1322
    def test_all_proxy_with_no_proxy(self):
1200
 
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
1201
 
                                 '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()
1202
1326
 
1203
1327
    def test_ALL_PROXY_with_NO_PROXY(self):
1204
 
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
1205
 
                                 '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()
1206
1331
 
1207
1332
    def test_http_proxy_without_scheme(self):
 
1333
        self.overrideEnv('http_proxy', self.server_host_port)
1208
1334
        if self._testing_pycurl():
1209
1335
            # pycurl *ignores* invalid proxy env variables. If that ever change
1210
1336
            # in the future, this test will fail indicating that pycurl do not
1211
1337
            # ignore anymore such variables.
1212
 
            self.not_proxied_in_env({'http_proxy': self.server_host_port})
 
1338
            self.assertNotProxied()
1213
1339
        else:
1214
 
            self.assertRaises(errors.InvalidURL,
1215
 
                              self.proxied_in_env,
1216
 
                              {'http_proxy': self.server_host_port})
 
1340
            self.assertRaises(errors.InvalidURL, self.assertProxied)
1217
1341
 
1218
1342
 
1219
1343
class TestRanges(http_utils.TestCaseWithWebserver):
1220
1344
    """Test the Range header in GET methods."""
1221
1345
 
1222
1346
    scenarios = multiply_scenarios(
1223
 
        vary_by_http_client_implementation(), 
 
1347
        vary_by_http_client_implementation(),
1224
1348
        vary_by_http_protocol_version(),
1225
1349
        )
1226
1350
 
1270
1394
    """Test redirection between http servers."""
1271
1395
 
1272
1396
    scenarios = multiply_scenarios(
1273
 
        vary_by_http_client_implementation(), 
 
1397
        vary_by_http_client_implementation(),
1274
1398
        vary_by_http_protocol_version(),
1275
1399
        )
1276
1400
 
1343
1467
    """
1344
1468
 
1345
1469
    scenarios = multiply_scenarios(
1346
 
        vary_by_http_client_implementation(), 
 
1470
        vary_by_http_client_implementation(),
1347
1471
        vary_by_http_protocol_version(),
1348
1472
        )
1349
1473
 
1398
1522
    """Test transport.do_catching_redirections."""
1399
1523
 
1400
1524
    scenarios = multiply_scenarios(
1401
 
        vary_by_http_client_implementation(), 
 
1525
        vary_by_http_client_implementation(),
1402
1526
        vary_by_http_protocol_version(),
1403
1527
        )
1404
1528
 
1446
1570
                          self.get_a, self.old_transport, redirected)
1447
1571
 
1448
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
 
1449
1602
class TestAuth(http_utils.TestCaseWithWebserver):
1450
1603
    """Test authentication scheme"""
1451
1604
 
1455
1608
        vary_by_http_auth_scheme(),
1456
1609
        )
1457
1610
 
1458
 
    _auth_header = 'Authorization'
1459
 
    _password_prompt_prefix = ''
1460
 
    _username_prompt_prefix = ''
1461
 
    # Set by load_tests
1462
 
    _auth_server = None
1463
 
 
1464
1611
    def setUp(self):
1465
1612
        super(TestAuth, self).setUp()
1466
1613
        self.server = self.get_readonly_server()
1489
1636
        return url
1490
1637
 
1491
1638
    def get_user_transport(self, user, password):
1492
 
        t = transport.get_transport(self.get_user_url(user, password))
 
1639
        t = transport.get_transport_from_url(
 
1640
            self.get_user_url(user, password))
1493
1641
        return t
1494
1642
 
1495
1643
    def test_no_user(self):
1607
1755
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1608
1756
                                            stderr=tests.StringIOWrapper())
1609
1757
        # Create a minimal config file with the right password
1610
 
        _setup_authentication_config(
1611
 
            scheme='http', 
1612
 
            port=self.server.port,
1613
 
            user=user,
1614
 
            password=password)
 
1758
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1759
                                     user=user, password=password)
1615
1760
        # Issue a request to the server to connect
1616
1761
        self.assertEqual('contents of a\n',t.get('a').read())
1617
1762
        # stdin should have  been left untouched
1624
1769
                                     http_utils.ProxyDigestAuthServer):
1625
1770
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1626
1771
        if self._testing_pycurl():
1627
 
            raise tests.KnownFailure(
 
1772
            self.knownFailure(
1628
1773
                'pycurl does not handle a nonce change')
1629
1774
        self.server.add_user('joe', 'foo')
1630
1775
        t = self.get_user_transport('joe', 'foo')
1647
1792
        user = 'joe'
1648
1793
        password = 'foo'
1649
1794
        self.server.add_user(user, password)
1650
 
        _setup_authentication_config(
1651
 
            scheme='http', 
1652
 
            port=self.server.port,
1653
 
            user=user,
1654
 
            password=password)
 
1795
        _setup_authentication_config(scheme='http', port=self.server.port,
 
1796
                                     user=user, password=password)
1655
1797
        t = self.get_user_transport(None, None)
1656
1798
        # Issue a request to the server to connect
1657
1799
        self.assertEqual('contents of a\n', t.get('a').read())
1658
1800
        # Only one 'Authentication Required' error should occur
1659
1801
        self.assertEqual(1, self.server.auth_required_errors)
1660
1802
 
1661
 
 
1662
 
def _setup_authentication_config(**kwargs):
1663
 
    conf = config.AuthenticationConfig()
1664
 
    conf._get_config().update({'httptest': kwargs})
1665
 
    conf._save()
1666
 
 
1667
 
 
1668
 
 
1669
 
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
1670
 
    """Unit tests for glue by which urllib2 asks us for authentication"""
1671
 
 
1672
 
    def test_get_user_password_without_port(self):
1673
 
        """We cope if urllib2 doesn't tell us the port.
1674
 
 
1675
 
        See https://bugs.launchpad.net/bzr/+bug/654684
1676
 
        """
 
1803
    def test_no_credential_leaks_in_log(self):
 
1804
        self.overrideAttr(debug, 'debug_flags', set(['http']))
1677
1805
        user = 'joe'
1678
 
        password = 'foo'
1679
 
        _setup_authentication_config(
1680
 
            scheme='http', 
1681
 
            host='localhost',
1682
 
            user=user,
1683
 
            password=password)
1684
 
        handler = _urllib2_wrappers.HTTPAuthHandler()
1685
 
        got_pass = handler.get_user_password(dict(
1686
 
            user='joe',
1687
 
            protocol='http',
1688
 
            host='localhost',
1689
 
            path='/',
1690
 
            realm='Realm',
1691
 
            ))
1692
 
        self.assertEquals((user, password), got_pass)
 
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,))
1693
1828
 
1694
1829
 
1695
1830
class TestProxyAuth(TestAuth):
1696
 
    """Test proxy authentication schemes."""
 
1831
    """Test proxy authentication schemes.
 
1832
 
 
1833
    This inherits from TestAuth to tweak the setUp and filter some failing
 
1834
    tests.
 
1835
    """
1697
1836
 
1698
1837
    scenarios = multiply_scenarios(
1699
1838
        vary_by_http_client_implementation(),
1701
1840
        vary_by_http_proxy_auth_scheme(),
1702
1841
        )
1703
1842
 
1704
 
    _auth_header = 'Proxy-authorization'
1705
 
    _password_prompt_prefix = 'Proxy '
1706
 
    _username_prompt_prefix = 'Proxy '
1707
 
 
1708
1843
    def setUp(self):
1709
1844
        super(TestProxyAuth, self).setUp()
1710
 
        self._old_env = {}
1711
 
        self.addCleanup(self._restore_env)
1712
1845
        # Override the contents to avoid false positives
1713
1846
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1714
1847
                                  ('b', 'not proxied contents of b\n'),
1717
1850
                                  ])
1718
1851
 
1719
1852
    def get_user_transport(self, user, password):
1720
 
        self._install_env({'all_proxy': self.get_user_url(user, password)})
 
1853
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
1721
1854
        return TestAuth.get_user_transport(self, user, password)
1722
1855
 
1723
 
    def _install_env(self, env):
1724
 
        for name, value in env.iteritems():
1725
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1726
 
 
1727
 
    def _restore_env(self):
1728
 
        for name, value in self._old_env.iteritems():
1729
 
            osutils.set_or_unset_env(name, value)
1730
 
 
1731
1856
    def test_empty_pass(self):
1732
1857
        if self._testing_pycurl():
1733
1858
            import pycurl
1734
1859
            if pycurl.version_info()[1] < '7.16.0':
1735
 
                raise tests.KnownFailure(
 
1860
                self.knownFailure(
1736
1861
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1737
1862
        super(TestProxyAuth, self).test_empty_pass()
1738
1863
 
1763
1888
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1764
1889
 
1765
1890
    scenarios = multiply_scenarios(
1766
 
        vary_by_http_client_implementation(), 
 
1891
        vary_by_http_client_implementation(),
1767
1892
        vary_by_http_protocol_version(),
1768
1893
        )
1769
1894
 
1770
1895
    def setUp(self):
1771
1896
        super(SmartHTTPTunnellingTest, self).setUp()
1772
1897
        # We use the VFS layer as part of HTTP tunnelling tests.
1773
 
        self._captureVar('BZR_NO_SMART_VFS', None)
 
1898
        self.overrideEnv('BZR_NO_SMART_VFS', None)
1774
1899
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1775
1900
        self.http_server = self.get_readonly_server()
1776
1901
 
1792
1917
        # The 'readv' command in the smart protocol both sends and receives
1793
1918
        # bulk data, so we use that.
1794
1919
        self.build_tree(['data-file'])
1795
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1920
        http_transport = transport.get_transport_from_url(
 
1921
            self.http_server.get_url())
1796
1922
        medium = http_transport.get_smart_medium()
1797
1923
        # Since we provide the medium, the url below will be mostly ignored
1798
1924
        # during the test, as long as the path is '/'.
1806
1932
        post_body = 'hello\n'
1807
1933
        expected_reply_body = 'ok\x012\n'
1808
1934
 
1809
 
        http_transport = transport.get_transport(self.http_server.get_url())
 
1935
        http_transport = transport.get_transport_from_url(
 
1936
            self.http_server.get_url())
1810
1937
        medium = http_transport.get_smart_medium()
1811
1938
        response = medium.send_http_smart_request(post_body)
1812
1939
        reply_body = response.read()
1870
1997
        self.assertIsInstance(r, type(t))
1871
1998
        # Both transports share the some connection
1872
1999
        self.assertEqual(t._get_connection(), r._get_connection())
 
2000
        self.assertEquals('http://www.example.com/foo/subdir/', r.base)
1873
2001
 
1874
2002
    def test_redirected_to_self_with_slash(self):
1875
2003
        t = self._transport('http://www.example.com/foo')
1886
2014
        r = t._redirected_to('http://www.example.com/foo',
1887
2015
                             'http://foo.example.com/foo/subdir')
1888
2016
        self.assertIsInstance(r, type(t))
 
2017
        self.assertEquals('http://foo.example.com/foo/subdir/',
 
2018
            r.external_url())
1889
2019
 
1890
2020
    def test_redirected_to_same_host_sibling_protocol(self):
1891
2021
        t = self._transport('http://www.example.com/foo')
1892
2022
        r = t._redirected_to('http://www.example.com/foo',
1893
2023
                             'https://www.example.com/foo')
1894
2024
        self.assertIsInstance(r, type(t))
 
2025
        self.assertEquals('https://www.example.com/foo/',
 
2026
            r.external_url())
1895
2027
 
1896
2028
    def test_redirected_to_same_host_different_protocol(self):
1897
2029
        t = self._transport('http://www.example.com/foo')
1898
2030
        r = t._redirected_to('http://www.example.com/foo',
1899
2031
                             'ftp://www.example.com/foo')
1900
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())
1901
2040
 
1902
2041
    def test_redirected_to_different_host_same_user(self):
1903
2042
        t = self._transport('http://joe@www.example.com/foo')
1904
2043
        r = t._redirected_to('http://www.example.com/foo',
1905
2044
                             'https://foo.example.com/foo')
1906
2045
        self.assertIsInstance(r, type(t))
1907
 
        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())
1908
2048
 
1909
2049
 
1910
2050
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
1963
2103
    pass
1964
2104
 
1965
2105
 
1966
 
if tests.HTTPSServerFeature.available():
 
2106
if features.HTTPSServerFeature.available():
1967
2107
    from bzrlib.tests import https_server
1968
2108
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
1969
2109
        pass
1980
2120
        tests.TestCase.setUp(self)
1981
2121
        self.server = self._activity_server(self._protocol_version)
1982
2122
        self.server.start_server()
1983
 
        self.activities = {}
 
2123
        _activities = {} # Don't close over self and create a cycle
1984
2124
        def report_activity(t, bytes, direction):
1985
 
            count = self.activities.get(direction, 0)
 
2125
            count = _activities.get(direction, 0)
1986
2126
            count += bytes
1987
 
            self.activities[direction] = count
 
2127
            _activities[direction] = count
 
2128
        self.activities = _activities
1988
2129
 
1989
2130
        # We override at class level because constructors may propagate the
1990
2131
        # bound method and render instance overriding ineffective (an
2215
2356
        # stdout should be empty, stderr will contains the prompts
2216
2357
        self.assertEqual('', stdout.getvalue())
2217
2358
 
2218