~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Ian Clatworthy
  • Date: 2009-09-09 15:30:59 UTC
  • mto: (4634.37.2 prepare-2.0)
  • mto: This revision was merged to the branch mainline in revision 4689.
  • Revision ID: ian.clatworthy@canonical.com-20090909153059-sb038agvd38ci2q8
more link fixes in the User Guide

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2012, 2015, 2016 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
26
27
import httplib
 
28
import os
 
29
import select
27
30
import SimpleHTTPServer
28
31
import socket
29
32
import sys
31
34
 
32
35
import bzrlib
33
36
from bzrlib import (
 
37
    bzrdir,
34
38
    config,
35
 
    controldir,
36
 
    debug,
37
39
    errors,
38
40
    osutils,
39
41
    remote as _mod_remote,
40
42
    tests,
41
 
    trace,
42
43
    transport,
43
44
    ui,
 
45
    urlutils,
 
46
    )
 
47
from bzrlib.symbol_versioning import (
 
48
    deprecated_in,
44
49
    )
45
50
from bzrlib.tests import (
46
 
    features,
47
51
    http_server,
48
52
    http_utils,
49
 
    test_server,
50
 
    )
51
 
from bzrlib.tests.scenarios import (
52
 
    load_tests_apply_scenarios,
53
 
    multiply_scenarios,
54
53
    )
55
54
from bzrlib.transport import (
56
55
    http,
62
61
    )
63
62
 
64
63
 
65
 
if features.pycurl.available():
 
64
try:
66
65
    from bzrlib.transport.http._pycurl import PyCurlTransport
67
 
 
68
 
 
69
 
load_tests = load_tests_apply_scenarios
70
 
 
71
 
 
72
 
def vary_by_http_client_implementation():
73
 
    """Test the two libraries we can use, pycurl and urllib."""
 
66
    pycurl_present = True
 
67
except errors.DependencyNotPresent:
 
68
    pycurl_present = False
 
69
 
 
70
 
 
71
def load_tests(standard_tests, module, loader):
 
72
    """Multiply tests for http clients and protocol versions."""
 
73
    result = loader.suiteClass()
 
74
 
 
75
    # one for each transport implementation
 
76
    t_tests, remaining_tests = tests.split_suite_by_condition(
 
77
        standard_tests, tests.condition_isinstance((
 
78
                TestHttpTransportRegistration,
 
79
                TestHttpTransportUrls,
 
80
                Test_redirected_to,
 
81
                )))
74
82
    transport_scenarios = [
75
83
        ('urllib', dict(_transport=_urllib.HttpTransport_urllib,
76
84
                        _server=http_server.HttpServer_urllib,
77
 
                        _url_protocol='http+urllib',)),
 
85
                        _qualified_prefix='http+urllib',)),
78
86
        ]
79
 
    if features.pycurl.available():
 
87
    if pycurl_present:
80
88
        transport_scenarios.append(
81
89
            ('pycurl', dict(_transport=PyCurlTransport,
82
90
                            _server=http_server.HttpServer_PyCurl,
83
 
                            _url_protocol='http+pycurl',)))
84
 
    return transport_scenarios
85
 
 
86
 
 
87
 
def vary_by_http_protocol_version():
88
 
    """Test on http/1.0 and 1.1"""
89
 
    return [
90
 
        ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
91
 
        ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
 
91
                            _qualified_prefix='http+pycurl',)))
 
92
    tests.multiply_tests(t_tests, transport_scenarios, result)
 
93
 
 
94
    # each implementation tested with each HTTP version
 
95
    tp_tests, remaining_tests = tests.split_suite_by_condition(
 
96
        remaining_tests, tests.condition_isinstance((
 
97
                SmartHTTPTunnellingTest,
 
98
                TestDoCatchRedirections,
 
99
                TestHTTPConnections,
 
100
                TestHTTPRedirections,
 
101
                TestHTTPSilentRedirections,
 
102
                TestLimitedRangeRequestServer,
 
103
                TestPost,
 
104
                TestProxyHttpServer,
 
105
                TestRanges,
 
106
                TestSpecificRequestHandler,
 
107
                )))
 
108
    protocol_scenarios = [
 
109
            ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
 
110
            ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
 
111
            ]
 
112
    tp_scenarios = tests.multiply_scenarios(transport_scenarios,
 
113
                                            protocol_scenarios)
 
114
    tests.multiply_tests(tp_tests, tp_scenarios, result)
 
115
 
 
116
    # proxy auth: each auth scheme on all http versions on all implementations.
 
117
    tppa_tests, remaining_tests = tests.split_suite_by_condition(
 
118
        remaining_tests, tests.condition_isinstance((
 
119
                TestProxyAuth,
 
120
                )))
 
121
    proxy_auth_scheme_scenarios = [
 
122
        ('basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
 
123
        ('digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
 
124
        ('basicdigest',
 
125
         dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
92
126
        ]
93
 
 
94
 
 
95
 
def vary_by_http_auth_scheme():
96
 
    scenarios = [
 
127
    tppa_scenarios = tests.multiply_scenarios(tp_scenarios,
 
128
                                              proxy_auth_scheme_scenarios)
 
129
    tests.multiply_tests(tppa_tests, tppa_scenarios, result)
 
130
 
 
131
    # auth: each auth scheme on all http versions on all implementations.
 
132
    tpa_tests, remaining_tests = tests.split_suite_by_condition(
 
133
        remaining_tests, tests.condition_isinstance((
 
134
                TestAuth,
 
135
                )))
 
136
    auth_scheme_scenarios = [
97
137
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
98
138
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
99
139
        ('basicdigest',
100
 
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
101
 
        ]
102
 
    # Add some attributes common to all scenarios
103
 
    for scenario_id, scenario_dict in scenarios:
104
 
        scenario_dict.update(_auth_header='Authorization',
105
 
                             _username_prompt_prefix='',
106
 
                             _password_prompt_prefix='')
107
 
    return scenarios
108
 
 
109
 
 
110
 
def vary_by_http_proxy_auth_scheme():
111
 
    scenarios = [
112
 
        ('proxy-basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
113
 
        ('proxy-digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
114
 
        ('proxy-basicdigest',
115
 
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
116
 
        ]
117
 
    # Add some attributes common to all scenarios
118
 
    for scenario_id, scenario_dict in scenarios:
119
 
        scenario_dict.update(_auth_header='Proxy-Authorization',
120
 
                             _username_prompt_prefix='Proxy ',
121
 
                             _password_prompt_prefix='Proxy ')
122
 
    return scenarios
123
 
 
124
 
 
125
 
def vary_by_http_activity():
 
140
         dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
 
141
        ]
 
142
    tpa_scenarios = tests.multiply_scenarios(tp_scenarios,
 
143
                                             auth_scheme_scenarios)
 
144
    tests.multiply_tests(tpa_tests, tpa_scenarios, result)
 
145
 
 
146
    # activity: on all http[s] versions on all implementations
 
147
    tpact_tests, remaining_tests = tests.split_suite_by_condition(
 
148
        remaining_tests, tests.condition_isinstance((
 
149
                TestActivity,
 
150
                )))
126
151
    activity_scenarios = [
127
152
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
128
 
                            _transport=_urllib.HttpTransport_urllib,)),
 
153
                             _transport=_urllib.HttpTransport_urllib,)),
129
154
        ]
130
 
    if features.pycurl.available():
 
155
    if tests.HTTPSServerFeature.available():
 
156
        activity_scenarios.append(
 
157
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
 
158
                                  _transport=_urllib.HttpTransport_urllib,)),)
 
159
    if pycurl_present:
131
160
        activity_scenarios.append(
132
161
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
133
 
                                _transport=PyCurlTransport,)),)
134
 
    if features.HTTPSServerFeature.available():
135
 
        # FIXME: Until we have a better way to handle self-signed certificates
136
 
        # (like allowing them in a test specific authentication.conf for
137
 
        # example), we need some specialized pycurl/urllib transport for tests.
138
 
        # -- vila 2012-01-20
139
 
        from bzrlib.tests import (
140
 
            ssl_certs,
141
 
            )
142
 
        class HTTPS_urllib_transport(_urllib.HttpTransport_urllib):
143
 
 
144
 
            def __init__(self, base, _from_transport=None):
145
 
                super(HTTPS_urllib_transport, self).__init__(
146
 
                    base, _from_transport=_from_transport,
147
 
                    ca_certs=ssl_certs.build_path('ca.crt'))
148
 
 
149
 
        activity_scenarios.append(
150
 
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
151
 
                                  _transport=HTTPS_urllib_transport,)),)
152
 
        if features.pycurl.available():
 
162
                                 _transport=PyCurlTransport,)),)
 
163
        if tests.HTTPSServerFeature.available():
 
164
            from bzrlib.tests import (
 
165
                ssl_certs,
 
166
                )
 
167
            # FIXME: Until we have a better way to handle self-signed
 
168
            # certificates (like allowing them in a test specific
 
169
            # authentication.conf for example), we need some specialized pycurl
 
170
            # transport for tests.
153
171
            class HTTPS_pycurl_transport(PyCurlTransport):
154
172
 
155
173
                def __init__(self, base, _from_transport=None):
159
177
 
160
178
            activity_scenarios.append(
161
179
                ('pycurl,https', dict(_activity_server=ActivityHTTPSServer,
162
 
                                    _transport=HTTPS_pycurl_transport,)),)
163
 
    return activity_scenarios
 
180
                                      _transport=HTTPS_pycurl_transport,)),)
 
181
 
 
182
    tpact_scenarios = tests.multiply_scenarios(activity_scenarios,
 
183
                                               protocol_scenarios)
 
184
    tests.multiply_tests(tpact_tests, tpact_scenarios, result)
 
185
 
 
186
    # No parametrization for the remaining tests
 
187
    result.addTests(remaining_tests)
 
188
 
 
189
    return result
164
190
 
165
191
 
166
192
class FakeManager(object):
178
204
    It records the bytes sent to it, and replies with a 200.
179
205
    """
180
206
 
181
 
    def __init__(self, expect_body_tail=None, scheme=''):
 
207
    def __init__(self, expect_body_tail=None):
182
208
        """Constructor.
183
209
 
184
210
        :type expect_body_tail: str
189
215
        self.host = None
190
216
        self.port = None
191
217
        self.received_bytes = ''
192
 
        self.scheme = scheme
193
 
 
194
 
    def get_url(self):
195
 
        return '%s://%s:%s/' % (self.scheme, self.host, self.port)
196
 
 
197
 
    def start_server(self):
 
218
 
 
219
    def setUp(self):
198
220
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
199
221
        self._sock.bind(('127.0.0.1', 0))
200
222
        self.host, self.port = self._sock.getsockname()
201
223
        self._ready = threading.Event()
202
 
        self._thread = test_server.TestThread(
203
 
            sync_event=self._ready, target=self._accept_read_and_reply)
 
224
        self._thread = threading.Thread(target=self._accept_read_and_reply)
 
225
        self._thread.setDaemon(True)
204
226
        self._thread.start()
205
 
        if 'threads' in tests.selftest_debug_flags:
206
 
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
207
 
        self._ready.wait()
 
227
        self._ready.wait(5)
208
228
 
209
229
    def _accept_read_and_reply(self):
210
230
        self._sock.listen(1)
211
231
        self._ready.set()
212
 
        conn, address = self._sock.accept()
213
 
        if self._expect_body_tail is not None:
 
232
        self._sock.settimeout(5)
 
233
        try:
 
234
            conn, address = self._sock.accept()
 
235
            # On win32, the accepted connection will be non-blocking to start
 
236
            # with because we're using settimeout.
 
237
            conn.setblocking(True)
214
238
            while not self.received_bytes.endswith(self._expect_body_tail):
215
239
                self.received_bytes += conn.recv(4096)
216
240
            conn.sendall('HTTP/1.1 200 OK\r\n')
217
 
        try:
 
241
        except socket.timeout:
 
242
            # Make sure the client isn't stuck waiting for us to e.g. accept.
218
243
            self._sock.close()
219
244
        except socket.error:
220
245
            # The client may have already closed the socket.
221
246
            pass
222
247
 
223
 
    def stop_server(self):
 
248
    def tearDown(self):
224
249
        try:
225
 
            # Issue a fake connection to wake up the server and allow it to
226
 
            # finish quickly
227
 
            fake_conn = osutils.connect_socket((self.host, self.port))
228
 
            fake_conn.close()
 
250
            self._sock.close()
229
251
        except socket.error:
230
252
            # We might have already closed it.  We don't care.
231
253
            pass
232
254
        self.host = None
233
255
        self.port = None
234
 
        self._thread.join()
235
 
        if 'threads' in tests.selftest_debug_flags:
236
 
            sys.stderr.write('Thread  joined: %s\n' % (self._thread.ident,))
237
256
 
238
257
 
239
258
class TestAuthHeader(tests.TestCase):
246
265
 
247
266
    def test_empty_header(self):
248
267
        scheme, remainder = self.parse_header('')
249
 
        self.assertEqual('', scheme)
 
268
        self.assertEquals('', scheme)
250
269
        self.assertIs(None, remainder)
251
270
 
252
271
    def test_negotiate_header(self):
253
272
        scheme, remainder = self.parse_header('Negotiate')
254
 
        self.assertEqual('negotiate', scheme)
 
273
        self.assertEquals('negotiate', scheme)
255
274
        self.assertIs(None, remainder)
256
275
 
257
276
    def test_basic_header(self):
258
277
        scheme, remainder = self.parse_header(
259
278
            'Basic realm="Thou should not pass"')
260
 
        self.assertEqual('basic', scheme)
261
 
        self.assertEqual('realm="Thou should not pass"', remainder)
 
279
        self.assertEquals('basic', scheme)
 
280
        self.assertEquals('realm="Thou should not pass"', remainder)
262
281
 
263
282
    def test_basic_extract_realm(self):
264
283
        scheme, remainder = self.parse_header(
266
285
            _urllib2_wrappers.BasicAuthHandler)
267
286
        match, realm = self.auth_handler.extract_realm(remainder)
268
287
        self.assertTrue(match is not None)
269
 
        self.assertEqual('Thou should not pass', realm)
 
288
        self.assertEquals('Thou should not pass', realm)
270
289
 
271
290
    def test_digest_header(self):
272
291
        scheme, remainder = self.parse_header(
273
292
            'Digest realm="Thou should not pass"')
274
 
        self.assertEqual('digest', scheme)
275
 
        self.assertEqual('realm="Thou should not pass"', remainder)
276
 
 
277
 
 
278
 
class TestHTTPRangeParsing(tests.TestCase):
279
 
 
280
 
    def setUp(self):
281
 
        super(TestHTTPRangeParsing, self).setUp()
282
 
        # We focus on range  parsing here and ignore everything else
283
 
        class RequestHandler(http_server.TestingHTTPRequestHandler):
284
 
            def setup(self): pass
285
 
            def handle(self): pass
286
 
            def finish(self): pass
287
 
 
288
 
        self.req_handler = RequestHandler(None, None, None)
289
 
 
290
 
    def assertRanges(self, ranges, header, file_size):
291
 
        self.assertEqual(ranges,
292
 
                          self.req_handler._parse_ranges(header, file_size))
293
 
 
294
 
    def test_simple_range(self):
295
 
        self.assertRanges([(0,2)], 'bytes=0-2', 12)
296
 
 
297
 
    def test_tail(self):
298
 
        self.assertRanges([(8, 11)], 'bytes=-4', 12)
299
 
 
300
 
    def test_tail_bigger_than_file(self):
301
 
        self.assertRanges([(0, 11)], 'bytes=-99', 12)
302
 
 
303
 
    def test_range_without_end(self):
304
 
        self.assertRanges([(4, 11)], 'bytes=4-', 12)
305
 
 
306
 
    def test_invalid_ranges(self):
307
 
        self.assertRanges(None, 'bytes=12-22', 12)
308
 
        self.assertRanges(None, 'bytes=1-3,12-22', 12)
309
 
        self.assertRanges(None, 'bytes=-', 12)
 
293
        self.assertEquals('digest', scheme)
 
294
        self.assertEquals('realm="Thou should not pass"', remainder)
310
295
 
311
296
 
312
297
class TestHTTPServer(tests.TestCase):
317
302
 
318
303
            protocol_version = 'HTTP/0.1'
319
304
 
320
 
        self.assertRaises(httplib.UnknownProtocol,
321
 
                          http_server.HttpServer, BogusRequestHandler)
 
305
        server = http_server.HttpServer(BogusRequestHandler)
 
306
        try:
 
307
            self.assertRaises(httplib.UnknownProtocol,server.setUp)
 
308
        except:
 
309
            server.tearDown()
 
310
            self.fail('HTTP Server creation did not raise UnknownProtocol')
322
311
 
323
312
    def test_force_invalid_protocol(self):
324
 
        self.assertRaises(httplib.UnknownProtocol,
325
 
                          http_server.HttpServer, protocol_version='HTTP/0.1')
 
313
        server = http_server.HttpServer(protocol_version='HTTP/0.1')
 
314
        try:
 
315
            self.assertRaises(httplib.UnknownProtocol,server.setUp)
 
316
        except:
 
317
            server.tearDown()
 
318
            self.fail('HTTP Server creation did not raise UnknownProtocol')
326
319
 
327
320
    def test_server_start_and_stop(self):
328
321
        server = http_server.HttpServer()
329
 
        self.addCleanup(server.stop_server)
330
 
        server.start_server()
331
 
        self.assertTrue(server.server is not None)
332
 
        self.assertTrue(server.server.serving is not None)
333
 
        self.assertTrue(server.server.serving)
 
322
        server.setUp()
 
323
        self.assertTrue(server._http_running)
 
324
        server.tearDown()
 
325
        self.assertFalse(server._http_running)
334
326
 
335
327
    def test_create_http_server_one_zero(self):
336
328
        class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
338
330
            protocol_version = 'HTTP/1.0'
339
331
 
340
332
        server = http_server.HttpServer(RequestHandlerOneZero)
341
 
        self.start_server(server)
342
 
        self.assertIsInstance(server.server, http_server.TestingHTTPServer)
 
333
        server.setUp()
 
334
        self.addCleanup(server.tearDown)
 
335
        self.assertIsInstance(server._httpd, http_server.TestingHTTPServer)
343
336
 
344
337
    def test_create_http_server_one_one(self):
345
338
        class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
347
340
            protocol_version = 'HTTP/1.1'
348
341
 
349
342
        server = http_server.HttpServer(RequestHandlerOneOne)
350
 
        self.start_server(server)
351
 
        self.assertIsInstance(server.server,
 
343
        server.setUp()
 
344
        self.addCleanup(server.tearDown)
 
345
        self.assertIsInstance(server._httpd,
352
346
                              http_server.TestingThreadingHTTPServer)
353
347
 
354
348
    def test_create_http_server_force_one_one(self):
358
352
 
359
353
        server = http_server.HttpServer(RequestHandlerOneZero,
360
354
                                        protocol_version='HTTP/1.1')
361
 
        self.start_server(server)
362
 
        self.assertIsInstance(server.server,
 
355
        server.setUp()
 
356
        self.addCleanup(server.tearDown)
 
357
        self.assertIsInstance(server._httpd,
363
358
                              http_server.TestingThreadingHTTPServer)
364
359
 
365
360
    def test_create_http_server_force_one_zero(self):
369
364
 
370
365
        server = http_server.HttpServer(RequestHandlerOneOne,
371
366
                                        protocol_version='HTTP/1.0')
372
 
        self.start_server(server)
373
 
        self.assertIsInstance(server.server,
 
367
        server.setUp()
 
368
        self.addCleanup(server.tearDown)
 
369
        self.assertIsInstance(server._httpd,
374
370
                              http_server.TestingHTTPServer)
375
371
 
376
372
 
378
374
    """Test case to inherit from if pycurl is present"""
379
375
 
380
376
    def _get_pycurl_maybe(self):
381
 
        self.requireFeature(features.pycurl)
382
 
        return PyCurlTransport
 
377
        try:
 
378
            from bzrlib.transport.http._pycurl import PyCurlTransport
 
379
            return PyCurlTransport
 
380
        except errors.DependencyNotPresent:
 
381
            raise tests.TestSkipped('pycurl not present')
383
382
 
384
383
    _transport = property(_get_pycurl_maybe)
385
384
 
386
385
 
 
386
class TestHttpUrls(tests.TestCase):
 
387
 
 
388
    # TODO: This should be moved to authorization tests once they
 
389
    # are written.
 
390
 
 
391
    def test_url_parsing(self):
 
392
        f = FakeManager()
 
393
        url = http.extract_auth('http://example.com', f)
 
394
        self.assertEquals('http://example.com', url)
 
395
        self.assertEquals(0, len(f.credentials))
 
396
        url = http.extract_auth(
 
397
            'http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
 
398
        self.assertEquals('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
 
399
        self.assertEquals(1, len(f.credentials))
 
400
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'],
 
401
                          f.credentials[0])
 
402
 
 
403
 
387
404
class TestHttpTransportUrls(tests.TestCase):
388
405
    """Test the http urls."""
389
406
 
390
 
    scenarios = vary_by_http_client_implementation()
391
 
 
392
407
    def test_abs_url(self):
393
408
        """Construction of absolute http URLs"""
394
 
        t = self._transport('http://example.com/bzr/bzr.dev/')
 
409
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
395
410
        eq = self.assertEqualDiff
396
 
        eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
397
 
        eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
398
 
        eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
 
411
        eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
 
412
        eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
 
413
        eq(t.abspath('.bzr'), 'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
399
414
        eq(t.abspath('.bzr/1//2/./3'),
400
 
           'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
 
415
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
401
416
 
402
417
    def test_invalid_http_urls(self):
403
418
        """Trap invalid construction of urls"""
404
 
        self._transport('http://example.com/bzr/bzr.dev/')
 
419
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
405
420
        self.assertRaises(errors.InvalidURL,
406
421
                          self._transport,
407
 
                          'http://http://example.com/bzr/bzr.dev/')
 
422
                          'http://http://bazaar-vcs.org/bzr/bzr.dev/')
408
423
 
409
424
    def test_http_root_urls(self):
410
425
        """Construction of URLs from server root"""
411
 
        t = self._transport('http://example.com/')
 
426
        t = self._transport('http://bzr.ozlabs.org/')
412
427
        eq = self.assertEqualDiff
413
428
        eq(t.abspath('.bzr/tree-version'),
414
 
           'http://example.com/.bzr/tree-version')
 
429
           'http://bzr.ozlabs.org/.bzr/tree-version')
415
430
 
416
431
    def test_http_impl_urls(self):
417
432
        """There are servers which ask for particular clients to connect"""
418
433
        server = self._server()
419
 
        server.start_server()
420
434
        try:
 
435
            server.setUp()
421
436
            url = server.get_url()
422
 
            self.assertTrue(url.startswith('%s://' % self._url_protocol))
 
437
            self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
423
438
        finally:
424
 
            server.stop_server()
 
439
            server.tearDown()
425
440
 
426
441
 
427
442
class TestHttps_pycurl(TestWithTransport_pycurl, tests.TestCase):
436
451
        https by supplying a fake version_info that do not
437
452
        support it.
438
453
        """
439
 
        self.requireFeature(features.pycurl)
440
 
        # Import the module locally now that we now it's available.
441
 
        pycurl = features.pycurl.module
 
454
        try:
 
455
            import pycurl
 
456
        except ImportError:
 
457
            raise tests.TestSkipped('pycurl not present')
442
458
 
443
 
        self.overrideAttr(pycurl, 'version_info',
444
 
                          # Fake the pycurl version_info This was taken from
445
 
                          # a windows pycurl without SSL (thanks to bialix)
446
 
                          lambda : (2,
447
 
                                    '7.13.2',
448
 
                                    462082,
449
 
                                    'i386-pc-win32',
450
 
                                    2576,
451
 
                                    None,
452
 
                                    0,
453
 
                                    None,
454
 
                                    ('ftp', 'gopher', 'telnet',
455
 
                                     'dict', 'ldap', 'http', 'file'),
456
 
                                    None,
457
 
                                    0,
458
 
                                    None))
459
 
        self.assertRaises(errors.DependencyNotPresent, self._transport,
460
 
                          'https://launchpad.net')
 
459
        version_info_orig = pycurl.version_info
 
460
        try:
 
461
            # Now that we have pycurl imported, we can fake its version_info
 
462
            # This was taken from a windows pycurl without SSL
 
463
            # (thanks to bialix)
 
464
            pycurl.version_info = lambda : (2,
 
465
                                            '7.13.2',
 
466
                                            462082,
 
467
                                            'i386-pc-win32',
 
468
                                            2576,
 
469
                                            None,
 
470
                                            0,
 
471
                                            None,
 
472
                                            ('ftp', 'gopher', 'telnet',
 
473
                                             'dict', 'ldap', 'http', 'file'),
 
474
                                            None,
 
475
                                            0,
 
476
                                            None)
 
477
            self.assertRaises(errors.DependencyNotPresent, self._transport,
 
478
                              'https://launchpad.net')
 
479
        finally:
 
480
            # Restore the right function
 
481
            pycurl.version_info = version_info_orig
461
482
 
462
483
 
463
484
class TestHTTPConnections(http_utils.TestCaseWithWebserver):
464
485
    """Test the http connections."""
465
486
 
466
 
    scenarios = multiply_scenarios(
467
 
        vary_by_http_client_implementation(),
468
 
        vary_by_http_protocol_version(),
469
 
        )
470
 
 
471
487
    def setUp(self):
472
 
        super(TestHTTPConnections, self).setUp()
 
488
        http_utils.TestCaseWithWebserver.setUp(self)
473
489
        self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
474
490
                        transport=self.get_transport())
475
491
 
476
492
    def test_http_has(self):
477
493
        server = self.get_readonly_server()
478
 
        t = self.get_readonly_transport()
 
494
        t = self._transport(server.get_url())
479
495
        self.assertEqual(t.has('foo/bar'), True)
480
496
        self.assertEqual(len(server.logs), 1)
481
497
        self.assertContainsRe(server.logs[0],
483
499
 
484
500
    def test_http_has_not_found(self):
485
501
        server = self.get_readonly_server()
486
 
        t = self.get_readonly_transport()
 
502
        t = self._transport(server.get_url())
487
503
        self.assertEqual(t.has('not-found'), False)
488
504
        self.assertContainsRe(server.logs[1],
489
505
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
490
506
 
491
507
    def test_http_get(self):
492
508
        server = self.get_readonly_server()
493
 
        t = self.get_readonly_transport()
 
509
        t = self._transport(server.get_url())
494
510
        fp = t.get('foo/bar')
495
511
        self.assertEqualDiff(
496
512
            fp.read(),
518
534
class TestHttpTransportRegistration(tests.TestCase):
519
535
    """Test registrations of various http implementations"""
520
536
 
521
 
    scenarios = vary_by_http_client_implementation()
522
 
 
523
537
    def test_http_registered(self):
524
 
        t = transport.get_transport_from_url(
525
 
            '%s://foo.com/' % self._url_protocol)
 
538
        t = transport.get_transport('%s://foo.com/' % self._qualified_prefix)
526
539
        self.assertIsInstance(t, transport.Transport)
527
540
        self.assertIsInstance(t, self._transport)
528
541
 
529
542
 
530
543
class TestPost(tests.TestCase):
531
544
 
532
 
    scenarios = multiply_scenarios(
533
 
        vary_by_http_client_implementation(),
534
 
        vary_by_http_protocol_version(),
535
 
        )
536
 
 
537
545
    def test_post_body_is_received(self):
538
 
        server = RecordingServer(expect_body_tail='end-of-body',
539
 
                                 scheme=self._url_protocol)
540
 
        self.start_server(server)
541
 
        url = server.get_url()
542
 
        # FIXME: needs a cleanup -- vila 20100611
543
 
        http_transport = transport.get_transport_from_url(url)
 
546
        server = RecordingServer(expect_body_tail='end-of-body')
 
547
        server.setUp()
 
548
        self.addCleanup(server.tearDown)
 
549
        scheme = self._qualified_prefix
 
550
        url = '%s://%s:%s/' % (scheme, server.host, server.port)
 
551
        http_transport = self._transport(url)
544
552
        code, response = http_transport._post('abc def end-of-body')
545
553
        self.assertTrue(
546
554
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
547
555
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
548
 
        self.assertTrue('content-type: application/octet-stream\r'
549
 
                        in server.received_bytes.lower())
550
556
        # The transport should not be assuming that the server can accept
551
557
        # chunked encoding the first time it connects, because HTTP/1.1, so we
552
558
        # check for the literal string.
588
594
    Daughter classes are expected to override _req_handler_class
589
595
    """
590
596
 
591
 
    scenarios = multiply_scenarios(
592
 
        vary_by_http_client_implementation(),
593
 
        vary_by_http_protocol_version(),
594
 
        )
595
 
 
596
597
    # Provide a useful default
597
598
    _req_handler_class = http_server.TestingHTTPRequestHandler
598
599
 
599
600
    def create_transport_readonly_server(self):
600
 
        server = http_server.HttpServer(self._req_handler_class,
601
 
                                        protocol_version=self._protocol_version)
602
 
        server._url_protocol = self._url_protocol
603
 
        return server
 
601
        return http_server.HttpServer(self._req_handler_class,
 
602
                                      protocol_version=self._protocol_version)
604
603
 
605
604
    def _testing_pycurl(self):
606
 
        # TODO: This is duplicated for lots of the classes in this file
607
 
        return (features.pycurl.available()
608
 
                and self._transport == PyCurlTransport)
 
605
        return pycurl_present and self._transport == PyCurlTransport
609
606
 
610
607
 
611
608
class WallRequestHandler(http_server.TestingHTTPRequestHandler):
612
609
    """Whatever request comes in, close the connection"""
613
610
 
614
 
    def _handle_one_request(self):
 
611
    def handle_one_request(self):
615
612
        """Handle a single HTTP request, by abruptly closing the connection"""
616
613
        self.close_connection = 1
617
614
 
622
619
    _req_handler_class = WallRequestHandler
623
620
 
624
621
    def test_http_has(self):
625
 
        t = self.get_readonly_transport()
 
622
        server = self.get_readonly_server()
 
623
        t = self._transport(server.get_url())
626
624
        # Unfortunately httplib (see HTTPResponse._read_status
627
625
        # for details) make no distinction between a closed
628
626
        # socket and badly formatted status line, so we can't
634
632
                          t.has, 'foo/bar')
635
633
 
636
634
    def test_http_get(self):
637
 
        t = self.get_readonly_transport()
 
635
        server = self.get_readonly_server()
 
636
        t = self._transport(server.get_url())
638
637
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
639
638
                           errors.InvalidHttpResponse),
640
639
                          t.get, 'foo/bar')
656
655
 
657
656
    _req_handler_class = BadStatusRequestHandler
658
657
 
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
 
 
670
658
    def test_http_has(self):
671
 
        t = self.get_readonly_transport()
672
 
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
673
 
                           errors.InvalidHttpResponse),
674
 
                          t.has, 'foo/bar')
 
659
        server = self.get_readonly_server()
 
660
        t = self._transport(server.get_url())
 
661
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
675
662
 
676
663
    def test_http_get(self):
677
 
        t = self.get_readonly_transport()
678
 
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
679
 
                           errors.InvalidHttpResponse),
680
 
                          t.get, 'foo/bar')
 
664
        server = self.get_readonly_server()
 
665
        t = self._transport(server.get_url())
 
666
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
681
667
 
682
668
 
683
669
class InvalidStatusRequestHandler(http_server.TestingHTTPRequestHandler):
687
673
        """Fakes handling a single HTTP request, returns a bad status"""
688
674
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
689
675
        self.wfile.write("Invalid status line\r\n")
690
 
        # If we don't close the connection pycurl will hang. Since this is a
691
 
        # stress test we don't *have* to respect the protocol, but we don't
692
 
        # have to sabotage it too much either.
693
 
        self.close_connection = True
694
676
        return False
695
677
 
696
678
 
702
684
 
703
685
    _req_handler_class = InvalidStatusRequestHandler
704
686
 
 
687
    def test_http_has(self):
 
688
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
 
689
            raise tests.KnownFailure(
 
690
                'pycurl hangs if the server send back garbage')
 
691
        super(TestInvalidStatusServer, self).test_http_has()
 
692
 
 
693
    def test_http_get(self):
 
694
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
 
695
            raise tests.KnownFailure(
 
696
                'pycurl hangs if the server send back garbage')
 
697
        super(TestInvalidStatusServer, self).test_http_get()
 
698
 
705
699
 
706
700
class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
707
701
    """Whatever request comes in, returns a bad protocol version"""
723
717
    _req_handler_class = BadProtocolRequestHandler
724
718
 
725
719
    def setUp(self):
726
 
        if self._testing_pycurl():
 
720
        if pycurl_present and self._transport == PyCurlTransport:
727
721
            raise tests.TestNotApplicable(
728
722
                "pycurl doesn't check the protocol version")
729
723
        super(TestBadProtocolServer, self).setUp()
730
724
 
731
725
    def test_http_has(self):
732
 
        t = self.get_readonly_transport()
 
726
        server = self.get_readonly_server()
 
727
        t = self._transport(server.get_url())
733
728
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
734
729
 
735
730
    def test_http_get(self):
736
 
        t = self.get_readonly_transport()
 
731
        server = self.get_readonly_server()
 
732
        t = self._transport(server.get_url())
737
733
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
738
734
 
739
735
 
753
749
    _req_handler_class = ForbiddenRequestHandler
754
750
 
755
751
    def test_http_has(self):
756
 
        t = self.get_readonly_transport()
 
752
        server = self.get_readonly_server()
 
753
        t = self._transport(server.get_url())
757
754
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
758
755
 
759
756
    def test_http_get(self):
760
 
        t = self.get_readonly_transport()
 
757
        server = self.get_readonly_server()
 
758
        t = self._transport(server.get_url())
761
759
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
762
760
 
763
761
 
769
767
        self.assertEqual(None, server.host)
770
768
        self.assertEqual(None, server.port)
771
769
 
772
 
    def test_setUp_and_stop(self):
 
770
    def test_setUp_and_tearDown(self):
773
771
        server = RecordingServer(expect_body_tail=None)
774
 
        server.start_server()
 
772
        server.setUp()
775
773
        try:
776
774
            self.assertNotEqual(None, server.host)
777
775
            self.assertNotEqual(None, server.port)
778
776
        finally:
779
 
            server.stop_server()
 
777
            server.tearDown()
780
778
        self.assertEqual(None, server.host)
781
779
        self.assertEqual(None, server.port)
782
780
 
783
781
    def test_send_receive_bytes(self):
784
 
        server = RecordingServer(expect_body_tail='c', scheme='http')
785
 
        self.start_server(server)
 
782
        server = RecordingServer(expect_body_tail='c')
 
783
        server.setUp()
 
784
        self.addCleanup(server.tearDown)
786
785
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
787
786
        sock.connect((server.host, server.port))
788
787
        sock.sendall('abc')
802
801
        self.build_tree_contents([('a', '0123456789')],)
803
802
 
804
803
    def test_readv(self):
805
 
        t = self.get_readonly_transport()
 
804
        server = self.get_readonly_server()
 
805
        t = self._transport(server.get_url())
806
806
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
807
807
        self.assertEqual(l[0], (0, '0'))
808
808
        self.assertEqual(l[1], (1, '1'))
810
810
        self.assertEqual(l[3], (9, '9'))
811
811
 
812
812
    def test_readv_out_of_order(self):
813
 
        t = self.get_readonly_transport()
 
813
        server = self.get_readonly_server()
 
814
        t = self._transport(server.get_url())
814
815
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
815
816
        self.assertEqual(l[0], (1, '1'))
816
817
        self.assertEqual(l[1], (9, '9'))
818
819
        self.assertEqual(l[3], (3, '34'))
819
820
 
820
821
    def test_readv_invalid_ranges(self):
821
 
        t = self.get_readonly_transport()
 
822
        server = self.get_readonly_server()
 
823
        t = self._transport(server.get_url())
822
824
 
823
825
        # This is intentionally reading off the end of the file
824
826
        # since we are sure that it cannot get there
832
834
 
833
835
    def test_readv_multiple_get_requests(self):
834
836
        server = self.get_readonly_server()
835
 
        t = self.get_readonly_transport()
 
837
        t = self._transport(server.get_url())
836
838
        # force transport to issue multiple requests
837
839
        t._max_readv_combine = 1
838
840
        t._max_get_ranges = 1
846
848
 
847
849
    def test_readv_get_max_size(self):
848
850
        server = self.get_readonly_server()
849
 
        t = self.get_readonly_transport()
 
851
        t = self._transport(server.get_url())
850
852
        # force transport to issue multiple requests by limiting the number of
851
853
        # bytes by request. Note that this apply to coalesced offsets only, a
852
854
        # single range will keep its size even if bigger than the limit.
861
863
 
862
864
    def test_complete_readv_leave_pipe_clean(self):
863
865
        server = self.get_readonly_server()
864
 
        t = self.get_readonly_transport()
 
866
        t = self._transport(server.get_url())
865
867
        # force transport to issue multiple requests
866
868
        t._get_max_size = 2
867
 
        list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
 
869
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
868
870
        # The server should have issued 3 requests
869
871
        self.assertEqual(3, server.GET_request_nb)
870
872
        self.assertEqual('0123456789', t.get_bytes('a'))
872
874
 
873
875
    def test_incomplete_readv_leave_pipe_clean(self):
874
876
        server = self.get_readonly_server()
875
 
        t = self.get_readonly_transport()
 
877
        t = self._transport(server.get_url())
876
878
        # force transport to issue multiple requests
877
879
        t._get_max_size = 2
878
880
        # Don't collapse readv results into a list so that we leave unread
947
949
    def get_multiple_ranges(self, file, file_size, ranges):
948
950
        self.send_response(206)
949
951
        self.send_header('Accept-Ranges', 'bytes')
950
 
        # XXX: this is strange; the 'random' name below seems undefined and
951
 
        # yet the tests pass -- mbp 2010-10-11 bug 658773
952
952
        boundary = "%d" % random.randint(0,0x7FFFFFFF)
953
953
        self.send_header("Content-Type",
954
954
                         "multipart/byteranges; boundary=%s" % boundary)
1016
1016
                return
1017
1017
            self.send_range_content(file, start, end - start + 1)
1018
1018
            cur += 1
1019
 
        # Final boundary
 
1019
        # No final boundary
1020
1020
        self.wfile.write(boundary_line)
1021
1021
 
1022
1022
 
1030
1030
 
1031
1031
    def test_readv_with_short_reads(self):
1032
1032
        server = self.get_readonly_server()
1033
 
        t = self.get_readonly_transport()
 
1033
        t = self._transport(server.get_url())
1034
1034
        # Force separate ranges for each offset
1035
1035
        t._bytes_to_read_before_seek = 0
1036
1036
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1051
1051
        # that mode
1052
1052
        self.assertEqual('single', t._range_hint)
1053
1053
 
1054
 
 
1055
 
class TruncatedBeforeBoundaryRequestHandler(
1056
 
    http_server.TestingHTTPRequestHandler):
1057
 
    """Truncation before a boundary, like in bug 198646"""
1058
 
 
1059
 
    _truncated_ranges = 1
1060
 
 
1061
 
    def get_multiple_ranges(self, file, file_size, ranges):
1062
 
        self.send_response(206)
1063
 
        self.send_header('Accept-Ranges', 'bytes')
1064
 
        boundary = 'tagada'
1065
 
        self.send_header('Content-Type',
1066
 
                         'multipart/byteranges; boundary=%s' % boundary)
1067
 
        boundary_line = '--%s\r\n' % boundary
1068
 
        # Calculate the Content-Length
1069
 
        content_length = 0
1070
 
        for (start, end) in ranges:
1071
 
            content_length += len(boundary_line)
1072
 
            content_length += self._header_line_length(
1073
 
                'Content-type', 'application/octet-stream')
1074
 
            content_length += self._header_line_length(
1075
 
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
1076
 
            content_length += len('\r\n') # end headers
1077
 
            content_length += end - start # + 1
1078
 
        content_length += len(boundary_line)
1079
 
        self.send_header('Content-length', content_length)
1080
 
        self.end_headers()
1081
 
 
1082
 
        # Send the multipart body
1083
 
        cur = 0
1084
 
        for (start, end) in ranges:
1085
 
            if cur + self._truncated_ranges >= len(ranges):
1086
 
                # Abruptly ends the response and close the connection
1087
 
                self.close_connection = 1
1088
 
                return
1089
 
            self.wfile.write(boundary_line)
1090
 
            self.send_header('Content-type', 'application/octet-stream')
1091
 
            self.send_header('Content-Range', 'bytes %d-%d/%d'
1092
 
                             % (start, end, file_size))
1093
 
            self.end_headers()
1094
 
            self.send_range_content(file, start, end - start + 1)
1095
 
            cur += 1
1096
 
        # Final boundary
1097
 
        self.wfile.write(boundary_line)
1098
 
 
1099
 
 
1100
 
class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
1101
 
    """Tests the case of bug 198646, disconnecting before a boundary."""
1102
 
 
1103
 
    _req_handler_class = TruncatedBeforeBoundaryRequestHandler
1104
 
 
1105
 
    def setUp(self):
1106
 
        super(TestTruncatedBeforeBoundary, self).setUp()
1107
 
        self.build_tree_contents([('a', '0123456789')],)
1108
 
 
1109
 
    def test_readv_with_short_reads(self):
1110
 
        server = self.get_readonly_server()
1111
 
        t = self.get_readonly_transport()
1112
 
        # Force separate ranges for each offset
1113
 
        t._bytes_to_read_before_seek = 0
1114
 
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1115
 
        self.assertEqual((0, '0'), ireadv.next())
1116
 
        self.assertEqual((2, '2'), ireadv.next())
1117
 
        self.assertEqual((4, '45'), ireadv.next())
1118
 
        self.assertEqual((9, '9'), ireadv.next())
1119
 
 
1120
 
 
1121
1054
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1122
1055
    """Errors out when range specifiers exceed the limit"""
1123
1056
 
1147
1080
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1148
1081
    """Tests readv requests against a server erroring out on too much ranges."""
1149
1082
 
1150
 
    scenarios = multiply_scenarios(
1151
 
        vary_by_http_client_implementation(),
1152
 
        vary_by_http_protocol_version(),
1153
 
        )
1154
 
 
1155
1083
    # Requests with more range specifiers will error out
1156
1084
    range_limit = 3
1157
1085
 
1159
1087
        return LimitedRangeHTTPServer(range_limit=self.range_limit,
1160
1088
                                      protocol_version=self._protocol_version)
1161
1089
 
 
1090
    def get_transport(self):
 
1091
        return self._transport(self.get_readonly_server().get_url())
 
1092
 
1162
1093
    def setUp(self):
1163
 
        super(TestLimitedRangeRequestServer, self).setUp()
 
1094
        http_utils.TestCaseWithWebserver.setUp(self)
1164
1095
        # We need to manipulate ranges that correspond to real chunks in the
1165
1096
        # response, so we build a content appropriately.
1166
1097
        filler = ''.join(['abcdefghij' for x in range(102)])
1168
1099
        self.build_tree_contents([('a', content)],)
1169
1100
 
1170
1101
    def test_few_ranges(self):
1171
 
        t = self.get_readonly_transport()
 
1102
        t = self.get_transport()
1172
1103
        l = list(t.readv('a', ((0, 4), (1024, 4), )))
1173
1104
        self.assertEqual(l[0], (0, '0000'))
1174
1105
        self.assertEqual(l[1], (1024, '0001'))
1175
1106
        self.assertEqual(1, self.get_readonly_server().GET_request_nb)
1176
1107
 
1177
1108
    def test_more_ranges(self):
1178
 
        t = self.get_readonly_transport()
 
1109
        t = self.get_transport()
1179
1110
        l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
1180
1111
        self.assertEqual(l[0], (0, '0000'))
1181
1112
        self.assertEqual(l[1], (1024, '0001'))
1192
1123
    Only the urllib implementation is tested here.
1193
1124
    """
1194
1125
 
 
1126
    def setUp(self):
 
1127
        tests.TestCase.setUp(self)
 
1128
        self._old_env = {}
 
1129
 
 
1130
    def tearDown(self):
 
1131
        self._restore_env()
 
1132
        tests.TestCase.tearDown(self)
 
1133
 
 
1134
    def _install_env(self, env):
 
1135
        for name, value in env.iteritems():
 
1136
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
1137
 
 
1138
    def _restore_env(self):
 
1139
        for name, value in self._old_env.iteritems():
 
1140
            osutils.set_or_unset_env(name, value)
 
1141
 
1195
1142
    def _proxied_request(self):
1196
1143
        handler = _urllib2_wrappers.ProxyHandler()
1197
 
        request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
 
1144
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
1198
1145
        handler.set_proxy(request, 'http')
1199
1146
        return request
1200
1147
 
1201
 
    def assertEvaluateProxyBypass(self, expected, host, no_proxy):
1202
 
        handler = _urllib2_wrappers.ProxyHandler()
1203
 
        self.assertEqual(expected,
1204
 
                          handler.evaluate_proxy_bypass(host, no_proxy))
1205
 
 
1206
1148
    def test_empty_user(self):
1207
 
        self.overrideEnv('http_proxy', 'http://bar.com')
1208
 
        request = self._proxied_request()
1209
 
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1210
 
 
1211
 
    def test_user_with_at(self):
1212
 
        self.overrideEnv('http_proxy',
1213
 
                         'http://username@domain:password@proxy_host:1234')
 
1149
        self._install_env({'http_proxy': 'http://bar.com'})
1214
1150
        request = self._proxied_request()
1215
1151
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1216
1152
 
1217
1153
    def test_invalid_proxy(self):
1218
1154
        """A proxy env variable without scheme"""
1219
 
        self.overrideEnv('http_proxy', 'host:1234')
 
1155
        self._install_env({'http_proxy': 'host:1234'})
1220
1156
        self.assertRaises(errors.InvalidURL, self._proxied_request)
1221
1157
 
1222
 
    def test_evaluate_proxy_bypass_true(self):
1223
 
        """The host is not proxied"""
1224
 
        self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
1225
 
        self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
1226
 
 
1227
 
    def test_evaluate_proxy_bypass_false(self):
1228
 
        """The host is proxied"""
1229
 
        self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
1230
 
 
1231
 
    def test_evaluate_proxy_bypass_unknown(self):
1232
 
        """The host is not explicitly proxied"""
1233
 
        self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
1234
 
        self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
1235
 
 
1236
 
    def test_evaluate_proxy_bypass_empty_entries(self):
1237
 
        """Ignore empty entries"""
1238
 
        self.assertEvaluateProxyBypass(None, 'example.com', '')
1239
 
        self.assertEvaluateProxyBypass(None, 'example.com', ',')
1240
 
        self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
1241
 
 
1242
1158
 
1243
1159
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
1244
1160
    """Tests proxy server.
1249
1165
    to the file names).
1250
1166
    """
1251
1167
 
1252
 
    scenarios = multiply_scenarios(
1253
 
        vary_by_http_client_implementation(),
1254
 
        vary_by_http_protocol_version(),
1255
 
        )
1256
 
 
1257
1168
    # FIXME: We don't have an https server available, so we don't
1258
 
    # test https connections. --vila toolongago
 
1169
    # test https connections.
1259
1170
 
1260
1171
    def setUp(self):
1261
1172
        super(TestProxyHttpServer, self).setUp()
1262
 
        self.transport_secondary_server = http_utils.ProxyServer
1263
1173
        self.build_tree_contents([('foo', 'contents of foo\n'),
1264
1174
                                  ('foo-proxied', 'proxied contents of foo\n')])
1265
1175
        # Let's setup some attributes for tests
1266
 
        server = self.get_readonly_server()
1267
 
        self.server_host_port = '%s:%d' % (server.host, server.port)
 
1176
        self.server = self.get_readonly_server()
 
1177
        self.proxy_address = '%s:%d' % (self.server.host, self.server.port)
1268
1178
        if self._testing_pycurl():
1269
1179
            # Oh my ! pycurl does not check for the port as part of
1270
1180
            # no_proxy :-( So we just test the host part
1271
 
            self.no_proxy_host = server.host
 
1181
            self.no_proxy_host = 'localhost'
1272
1182
        else:
1273
 
            self.no_proxy_host = self.server_host_port
 
1183
            self.no_proxy_host = self.proxy_address
1274
1184
        # The secondary server is the proxy
1275
 
        self.proxy_url = self.get_secondary_url()
 
1185
        self.proxy = self.get_secondary_server()
 
1186
        self.proxy_url = self.proxy.get_url()
 
1187
        self._old_env = {}
1276
1188
 
1277
1189
    def _testing_pycurl(self):
1278
 
        # TODO: This is duplicated for lots of the classes in this file
1279
 
        return (features.pycurl.available()
1280
 
                and self._transport == PyCurlTransport)
1281
 
 
1282
 
    def assertProxied(self):
1283
 
        t = self.get_readonly_transport()
1284
 
        self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1285
 
 
1286
 
    def assertNotProxied(self):
1287
 
        t = self.get_readonly_transport()
1288
 
        self.assertEqual('contents of foo\n', t.get('foo').read())
 
1190
        return pycurl_present and self._transport == PyCurlTransport
 
1191
 
 
1192
    def create_transport_secondary_server(self):
 
1193
        """Creates an http server that will serve files with
 
1194
        '-proxied' appended to their names.
 
1195
        """
 
1196
        return http_utils.ProxyServer(protocol_version=self._protocol_version)
 
1197
 
 
1198
    def _install_env(self, env):
 
1199
        for name, value in env.iteritems():
 
1200
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
1201
 
 
1202
    def _restore_env(self):
 
1203
        for name, value in self._old_env.iteritems():
 
1204
            osutils.set_or_unset_env(name, value)
 
1205
 
 
1206
    def proxied_in_env(self, env):
 
1207
        self._install_env(env)
 
1208
        url = self.server.get_url()
 
1209
        t = self._transport(url)
 
1210
        try:
 
1211
            self.assertEqual('proxied contents of foo\n', t.get('foo').read())
 
1212
        finally:
 
1213
            self._restore_env()
 
1214
 
 
1215
    def not_proxied_in_env(self, env):
 
1216
        self._install_env(env)
 
1217
        url = self.server.get_url()
 
1218
        t = self._transport(url)
 
1219
        try:
 
1220
            self.assertEqual('contents of foo\n', t.get('foo').read())
 
1221
        finally:
 
1222
            self._restore_env()
1289
1223
 
1290
1224
    def test_http_proxy(self):
1291
 
        self.overrideEnv('http_proxy', self.proxy_url)
1292
 
        self.assertProxied()
 
1225
        self.proxied_in_env({'http_proxy': self.proxy_url})
1293
1226
 
1294
1227
    def test_HTTP_PROXY(self):
1295
1228
        if self._testing_pycurl():
1298
1231
            # about. Should we ?)
1299
1232
            raise tests.TestNotApplicable(
1300
1233
                'pycurl does not check HTTP_PROXY for security reasons')
1301
 
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
1302
 
        self.assertProxied()
 
1234
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
1303
1235
 
1304
1236
    def test_all_proxy(self):
1305
 
        self.overrideEnv('all_proxy', self.proxy_url)
1306
 
        self.assertProxied()
 
1237
        self.proxied_in_env({'all_proxy': self.proxy_url})
1307
1238
 
1308
1239
    def test_ALL_PROXY(self):
1309
 
        self.overrideEnv('ALL_PROXY', self.proxy_url)
1310
 
        self.assertProxied()
 
1240
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
1311
1241
 
1312
1242
    def test_http_proxy_with_no_proxy(self):
1313
 
        self.overrideEnv('no_proxy', self.no_proxy_host)
1314
 
        self.overrideEnv('http_proxy', self.proxy_url)
1315
 
        self.assertNotProxied()
 
1243
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
 
1244
                                 'no_proxy': self.no_proxy_host})
1316
1245
 
1317
1246
    def test_HTTP_PROXY_with_NO_PROXY(self):
1318
1247
        if self._testing_pycurl():
1319
1248
            raise tests.TestNotApplicable(
1320
1249
                'pycurl does not check HTTP_PROXY for security reasons')
1321
 
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
1322
 
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
1323
 
        self.assertNotProxied()
 
1250
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
 
1251
                                 'NO_PROXY': self.no_proxy_host})
1324
1252
 
1325
1253
    def test_all_proxy_with_no_proxy(self):
1326
 
        self.overrideEnv('no_proxy', self.no_proxy_host)
1327
 
        self.overrideEnv('all_proxy', self.proxy_url)
1328
 
        self.assertNotProxied()
 
1254
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
 
1255
                                 'no_proxy': self.no_proxy_host})
1329
1256
 
1330
1257
    def test_ALL_PROXY_with_NO_PROXY(self):
1331
 
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
1332
 
        self.overrideEnv('ALL_PROXY', self.proxy_url)
1333
 
        self.assertNotProxied()
 
1258
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
 
1259
                                 'NO_PROXY': self.no_proxy_host})
1334
1260
 
1335
1261
    def test_http_proxy_without_scheme(self):
1336
 
        self.overrideEnv('http_proxy', self.server_host_port)
1337
1262
        if self._testing_pycurl():
1338
1263
            # pycurl *ignores* invalid proxy env variables. If that ever change
1339
1264
            # in the future, this test will fail indicating that pycurl do not
1340
1265
            # ignore anymore such variables.
1341
 
            self.assertNotProxied()
 
1266
            self.not_proxied_in_env({'http_proxy': self.proxy_address})
1342
1267
        else:
1343
 
            self.assertRaises(errors.InvalidURL, self.assertProxied)
 
1268
            self.assertRaises(errors.InvalidURL,
 
1269
                              self.proxied_in_env,
 
1270
                              {'http_proxy': self.proxy_address})
1344
1271
 
1345
1272
 
1346
1273
class TestRanges(http_utils.TestCaseWithWebserver):
1347
1274
    """Test the Range header in GET methods."""
1348
1275
 
1349
 
    scenarios = multiply_scenarios(
1350
 
        vary_by_http_client_implementation(),
1351
 
        vary_by_http_protocol_version(),
1352
 
        )
1353
 
 
1354
1276
    def setUp(self):
1355
 
        super(TestRanges, self).setUp()
 
1277
        http_utils.TestCaseWithWebserver.setUp(self)
1356
1278
        self.build_tree_contents([('a', '0123456789')],)
 
1279
        server = self.get_readonly_server()
 
1280
        self.transport = self._transport(server.get_url())
1357
1281
 
1358
1282
    def create_transport_readonly_server(self):
1359
1283
        return http_server.HttpServer(protocol_version=self._protocol_version)
1360
1284
 
1361
1285
    def _file_contents(self, relpath, ranges):
1362
 
        t = self.get_readonly_transport()
1363
1286
        offsets = [ (start, end - start + 1) for start, end in ranges]
1364
 
        coalesce = t._coalesce_offsets
 
1287
        coalesce = self.transport._coalesce_offsets
1365
1288
        coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
1366
 
        code, data = t._get(relpath, coalesced)
 
1289
        code, data = self.transport._get(relpath, coalesced)
1367
1290
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1368
1291
        for start, end in ranges:
1369
1292
            data.seek(start)
1370
1293
            yield data.read(end - start + 1)
1371
1294
 
1372
1295
    def _file_tail(self, relpath, tail_amount):
1373
 
        t = self.get_readonly_transport()
1374
 
        code, data = t._get(relpath, [], tail_amount)
 
1296
        code, data = self.transport._get(relpath, [], tail_amount)
1375
1297
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1376
1298
        data.seek(-tail_amount, 2)
1377
1299
        return data.read(tail_amount)
1396
1318
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1397
1319
    """Test redirection between http servers."""
1398
1320
 
1399
 
    scenarios = multiply_scenarios(
1400
 
        vary_by_http_client_implementation(),
1401
 
        vary_by_http_protocol_version(),
1402
 
        )
 
1321
    def create_transport_secondary_server(self):
 
1322
        """Create the secondary server redirecting to the primary server"""
 
1323
        new = self.get_readonly_server()
 
1324
 
 
1325
        redirecting = http_utils.HTTPServerRedirecting(
 
1326
            protocol_version=self._protocol_version)
 
1327
        redirecting.redirect_to(new.host, new.port)
 
1328
        return redirecting
1403
1329
 
1404
1330
    def setUp(self):
1405
1331
        super(TestHTTPRedirections, self).setUp()
1407
1333
                                  ('bundle',
1408
1334
                                  '# Bazaar revision bundle v0.9\n#\n')
1409
1335
                                  ],)
 
1336
        # The requests to the old server will be redirected to the new server
 
1337
        self.old_transport = self._transport(self.old_server.get_url())
1410
1338
 
1411
1339
    def test_redirected(self):
1412
 
        self.assertRaises(errors.RedirectRequested,
1413
 
                          self.get_old_transport().get, 'a')
1414
 
        self.assertEqual('0123456789', self.get_new_transport().get('a').read())
 
1340
        self.assertRaises(errors.RedirectRequested, self.old_transport.get, 'a')
 
1341
        t = self._transport(self.new_server.get_url())
 
1342
        self.assertEqual('0123456789', t.get('a').read())
 
1343
 
 
1344
    def test_read_redirected_bundle_from_url(self):
 
1345
        from bzrlib.bundle import read_bundle_from_url
 
1346
        url = self.old_transport.abspath('bundle')
 
1347
        bundle = self.applyDeprecated(deprecated_in((1, 12, 0)),
 
1348
                read_bundle_from_url, url)
 
1349
        # If read_bundle_from_url was successful we get an empty bundle
 
1350
        self.assertEqual([], bundle.revisions)
1415
1351
 
1416
1352
 
1417
1353
class RedirectedRequest(_urllib2_wrappers.Request):
1430
1366
        self.follow_redirections = True
1431
1367
 
1432
1368
 
1433
 
def install_redirected_request(test):
1434
 
    test.overrideAttr(_urllib2_wrappers, 'Request', RedirectedRequest)
1435
 
 
1436
 
 
1437
 
def cleanup_http_redirection_connections(test):
1438
 
    # Some sockets are opened but never seen by _urllib, so we trap them at
1439
 
    # the _urllib2_wrappers level to be able to clean them up.
1440
 
    def socket_disconnect(sock):
1441
 
        try:
1442
 
            sock.shutdown(socket.SHUT_RDWR)
1443
 
            sock.close()
1444
 
        except socket.error:
1445
 
            pass
1446
 
    def connect(connection):
1447
 
        test.http_connect_orig(connection)
1448
 
        test.addCleanup(socket_disconnect, connection.sock)
1449
 
    test.http_connect_orig = test.overrideAttr(
1450
 
        _urllib2_wrappers.HTTPConnection, 'connect', connect)
1451
 
    def connect(connection):
1452
 
        test.https_connect_orig(connection)
1453
 
        test.addCleanup(socket_disconnect, connection.sock)
1454
 
    test.https_connect_orig = test.overrideAttr(
1455
 
        _urllib2_wrappers.HTTPSConnection, 'connect', connect)
1456
 
 
1457
 
 
1458
1369
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1459
1370
    """Test redirections.
1460
1371
 
1469
1380
    -- vila 20070212
1470
1381
    """
1471
1382
 
1472
 
    scenarios = multiply_scenarios(
1473
 
        vary_by_http_client_implementation(),
1474
 
        vary_by_http_protocol_version(),
1475
 
        )
1476
 
 
1477
1383
    def setUp(self):
1478
 
        if (features.pycurl.available()
1479
 
            and self._transport == PyCurlTransport):
 
1384
        if pycurl_present and self._transport == PyCurlTransport:
1480
1385
            raise tests.TestNotApplicable(
1481
 
                "pycurl doesn't redirect silently anymore")
 
1386
                "pycurl doesn't redirect silently annymore")
1482
1387
        super(TestHTTPSilentRedirections, self).setUp()
1483
 
        install_redirected_request(self)
1484
 
        cleanup_http_redirection_connections(self)
 
1388
        self.setup_redirected_request()
 
1389
        self.addCleanup(self.cleanup_redirected_request)
1485
1390
        self.build_tree_contents([('a','a'),
1486
1391
                                  ('1/',),
1487
1392
                                  ('1/a', 'redirected once'),
1495
1400
                                  ('5/a', 'redirected 5 times'),
1496
1401
                                  ],)
1497
1402
 
 
1403
        self.old_transport = self._transport(self.old_server.get_url())
 
1404
 
 
1405
    def setup_redirected_request(self):
 
1406
        self.original_class = _urllib2_wrappers.Request
 
1407
        _urllib2_wrappers.Request = RedirectedRequest
 
1408
 
 
1409
    def cleanup_redirected_request(self):
 
1410
        _urllib2_wrappers.Request = self.original_class
 
1411
 
 
1412
    def create_transport_secondary_server(self):
 
1413
        """Create the secondary server, redirections are defined in the tests"""
 
1414
        return http_utils.HTTPServerRedirecting(
 
1415
            protocol_version=self._protocol_version)
 
1416
 
1498
1417
    def test_one_redirection(self):
1499
 
        t = self.get_old_transport()
1500
 
        req = RedirectedRequest('GET', t._remote_path('a'))
 
1418
        t = self.old_transport
 
1419
 
 
1420
        req = RedirectedRequest('GET', t.abspath('a'))
 
1421
        req.follow_redirections = True
1501
1422
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1502
1423
                                       self.new_server.port)
1503
1424
        self.old_server.redirections = \
1504
1425
            [('(.*)', r'%s/1\1' % (new_prefix), 301),]
1505
 
        self.assertEqual('redirected once', t._perform(req).read())
 
1426
        self.assertEquals('redirected once',t._perform(req).read())
1506
1427
 
1507
1428
    def test_five_redirections(self):
1508
 
        t = self.get_old_transport()
1509
 
        req = RedirectedRequest('GET', t._remote_path('a'))
 
1429
        t = self.old_transport
 
1430
 
 
1431
        req = RedirectedRequest('GET', t.abspath('a'))
 
1432
        req.follow_redirections = True
1510
1433
        old_prefix = 'http://%s:%s' % (self.old_server.host,
1511
1434
                                       self.old_server.port)
1512
1435
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1518
1441
            ('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1519
1442
            ('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1520
1443
            ]
1521
 
        self.assertEqual('redirected 5 times', t._perform(req).read())
 
1444
        self.assertEquals('redirected 5 times',t._perform(req).read())
1522
1445
 
1523
1446
 
1524
1447
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1525
1448
    """Test transport.do_catching_redirections."""
1526
1449
 
1527
 
    scenarios = multiply_scenarios(
1528
 
        vary_by_http_client_implementation(),
1529
 
        vary_by_http_protocol_version(),
1530
 
        )
1531
 
 
1532
1450
    def setUp(self):
1533
1451
        super(TestDoCatchRedirections, self).setUp()
1534
1452
        self.build_tree_contents([('a', '0123456789'),],)
1535
 
        cleanup_http_redirection_connections(self)
1536
 
 
1537
 
        self.old_transport = self.get_old_transport()
1538
 
 
1539
 
    def get_a(self, t):
1540
 
        return t.get('a')
 
1453
 
 
1454
        self.old_transport = self._transport(self.old_server.get_url())
 
1455
 
 
1456
    def get_a(self, transport):
 
1457
        return transport.get('a')
1541
1458
 
1542
1459
    def test_no_redirection(self):
1543
 
        t = self.get_new_transport()
 
1460
        t = self._transport(self.new_server.get_url())
1544
1461
 
1545
1462
        # We use None for redirected so that we fail if redirected
1546
 
        self.assertEqual('0123456789',
1547
 
                         transport.do_catching_redirections(
 
1463
        self.assertEquals('0123456789',
 
1464
                          transport.do_catching_redirections(
1548
1465
                self.get_a, t, None).read())
1549
1466
 
1550
1467
    def test_one_redirection(self):
1551
1468
        self.redirections = 0
1552
1469
 
1553
 
        def redirected(t, exception, redirection_notice):
 
1470
        def redirected(transport, exception, redirection_notice):
1554
1471
            self.redirections += 1
1555
 
            redirected_t = t._redirected_to(exception.source, exception.target)
1556
 
            return redirected_t
 
1472
            dir, file = urlutils.split(exception.target)
 
1473
            return self._transport(dir)
1557
1474
 
1558
 
        self.assertEqual('0123456789',
1559
 
                         transport.do_catching_redirections(
 
1475
        self.assertEquals('0123456789',
 
1476
                          transport.do_catching_redirections(
1560
1477
                self.get_a, self.old_transport, redirected).read())
1561
 
        self.assertEqual(1, self.redirections)
 
1478
        self.assertEquals(1, self.redirections)
1562
1479
 
1563
1480
    def test_redirection_loop(self):
1564
1481
 
1573
1490
                          self.get_a, self.old_transport, redirected)
1574
1491
 
1575
1492
 
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.assertEqual((user, password), got_pass)
1603
 
 
1604
 
 
1605
1493
class TestAuth(http_utils.TestCaseWithWebserver):
1606
1494
    """Test authentication scheme"""
1607
1495
 
1608
 
    scenarios = multiply_scenarios(
1609
 
        vary_by_http_client_implementation(),
1610
 
        vary_by_http_protocol_version(),
1611
 
        vary_by_http_auth_scheme(),
1612
 
        )
 
1496
    _auth_header = 'Authorization'
 
1497
    _password_prompt_prefix = ''
 
1498
    _username_prompt_prefix = ''
 
1499
    # Set by load_tests
 
1500
    _auth_server = None
1613
1501
 
1614
1502
    def setUp(self):
1615
1503
        super(TestAuth, self).setUp()
1618
1506
                                  ('b', 'contents of b\n'),])
1619
1507
 
1620
1508
    def create_transport_readonly_server(self):
1621
 
        server = self._auth_server(protocol_version=self._protocol_version)
1622
 
        server._url_protocol = self._url_protocol
1623
 
        return server
 
1509
        return self._auth_server(protocol_version=self._protocol_version)
1624
1510
 
1625
1511
    def _testing_pycurl(self):
1626
 
        # TODO: This is duplicated for lots of the classes in this file
1627
 
        return (features.pycurl.available()
1628
 
                and self._transport == PyCurlTransport)
 
1512
        return pycurl_present and self._transport == PyCurlTransport
1629
1513
 
1630
1514
    def get_user_url(self, user, password):
1631
1515
        """Build an url embedding user and password"""
1639
1523
        return url
1640
1524
 
1641
1525
    def get_user_transport(self, user, password):
1642
 
        t = transport.get_transport_from_url(
1643
 
            self.get_user_url(user, password))
1644
 
        return t
 
1526
        return self._transport(self.get_user_url(user, password))
1645
1527
 
1646
1528
    def test_no_user(self):
1647
1529
        self.server.add_user('joe', 'foo')
1698
1580
        self.assertEqual('', ui.ui_factory.stdin.readline())
1699
1581
        stderr.seek(0)
1700
1582
        expected_prompt = self._expected_username_prompt(t._unqualified_scheme)
1701
 
        self.assertEqual(expected_prompt, stderr.read(len(expected_prompt)))
1702
 
        self.assertEqual('', stdout.getvalue())
 
1583
        self.assertEquals(expected_prompt, stderr.read(len(expected_prompt)))
 
1584
        self.assertEquals('', stdout.getvalue())
1703
1585
        self._check_password_prompt(t._unqualified_scheme, 'joe',
1704
1586
                                    stderr.readline())
1705
1587
 
1720
1602
        self.assertEqual('', ui.ui_factory.stdin.readline())
1721
1603
        self._check_password_prompt(t._unqualified_scheme, 'joe',
1722
1604
                                    stderr.getvalue())
1723
 
        self.assertEqual('', stdout.getvalue())
 
1605
        self.assertEquals('', stdout.getvalue())
1724
1606
        # And we shouldn't prompt again for a different request
1725
1607
        # against the same transport.
1726
1608
        self.assertEqual('contents of b\n',t.get('b').read())
1736
1618
                              % (scheme.upper(),
1737
1619
                                 user, self.server.host, self.server.port,
1738
1620
                                 self.server.auth_realm)))
1739
 
        self.assertEqual(expected_prompt, actual_prompt)
 
1621
        self.assertEquals(expected_prompt, actual_prompt)
1740
1622
 
1741
1623
    def _expected_username_prompt(self, scheme):
1742
1624
        return (self._username_prompt_prefix
1756
1638
        self.server.add_user(user, password)
1757
1639
        t = self.get_user_transport(user, None)
1758
1640
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1759
 
                                            stderr=tests.StringIOWrapper())
 
1641
                                            stdout=tests.StringIOWrapper())
1760
1642
        # Create a minimal config file with the right password
1761
 
        _setup_authentication_config(scheme='http', port=self.server.port,
1762
 
                                     user=user, password=password)
 
1643
        conf = config.AuthenticationConfig()
 
1644
        conf._get_config().update(
 
1645
            {'httptest': {'scheme': 'http', 'port': self.server.port,
 
1646
                          'user': user, 'password': password}})
 
1647
        conf._save()
1763
1648
        # Issue a request to the server to connect
1764
1649
        self.assertEqual('contents of a\n',t.get('a').read())
1765
1650
        # stdin should have  been left untouched
1767
1652
        # Only one 'Authentication Required' error should occur
1768
1653
        self.assertEqual(1, self.server.auth_required_errors)
1769
1654
 
 
1655
    def test_user_from_auth_conf(self):
 
1656
        if self._testing_pycurl():
 
1657
            raise tests.TestNotApplicable(
 
1658
                'pycurl does not support authentication.conf')
 
1659
        user = 'joe'
 
1660
        password = 'foo'
 
1661
        self.server.add_user(user, password)
 
1662
        # Create a minimal config file with the right password
 
1663
        conf = config.AuthenticationConfig()
 
1664
        conf._get_config().update(
 
1665
            {'httptest': {'scheme': 'http', 'port': self.server.port,
 
1666
                          'user': user, 'password': password}})
 
1667
        conf._save()
 
1668
        t = self.get_user_transport(None, None)
 
1669
        # Issue a request to the server to connect
 
1670
        self.assertEqual('contents of a\n', t.get('a').read())
 
1671
        # Only one 'Authentication Required' error should occur
 
1672
        self.assertEqual(1, self.server.auth_required_errors)
 
1673
 
1770
1674
    def test_changing_nonce(self):
1771
1675
        if self._auth_server not in (http_utils.HTTPDigestAuthServer,
1772
1676
                                     http_utils.ProxyDigestAuthServer):
1773
1677
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1774
1678
        if self._testing_pycurl():
1775
 
            self.knownFailure(
 
1679
            raise tests.KnownFailure(
1776
1680
                'pycurl does not handle a nonce change')
1777
1681
        self.server.add_user('joe', 'foo')
1778
1682
        t = self.get_user_transport('joe', 'foo')
1788
1692
        # initial 'who are you' and a second 'who are you' with the new nonce)
1789
1693
        self.assertEqual(2, self.server.auth_required_errors)
1790
1694
 
1791
 
    def test_user_from_auth_conf(self):
1792
 
        if self._testing_pycurl():
1793
 
            raise tests.TestNotApplicable(
1794
 
                'pycurl does not support authentication.conf')
1795
 
        user = 'joe'
1796
 
        password = 'foo'
1797
 
        self.server.add_user(user, password)
1798
 
        _setup_authentication_config(scheme='http', port=self.server.port,
1799
 
                                     user=user, password=password)
1800
 
        t = self.get_user_transport(None, None)
1801
 
        # Issue a request to the server to connect
1802
 
        self.assertEqual('contents of a\n', t.get('a').read())
1803
 
        # Only one 'Authentication Required' error should occur
1804
 
        self.assertEqual(1, self.server.auth_required_errors)
1805
 
 
1806
 
    def test_no_credential_leaks_in_log(self):
1807
 
        self.overrideAttr(debug, 'debug_flags', set(['http']))
1808
 
        user = 'joe'
1809
 
        password = 'very-sensitive-password'
1810
 
        self.server.add_user(user, password)
1811
 
        t = self.get_user_transport(user, password)
1812
 
        # Capture the debug calls to mutter
1813
 
        self.mutters = []
1814
 
        def mutter(*args):
1815
 
            lines = args[0] % args[1:]
1816
 
            # Some calls output multiple lines, just split them now since we
1817
 
            # care about a single one later.
1818
 
            self.mutters.extend(lines.splitlines())
1819
 
        self.overrideAttr(trace, 'mutter', mutter)
1820
 
        # Issue a request to the server to connect
1821
 
        self.assertEqual(True, t.has('a'))
1822
 
        # Only one 'Authentication Required' error should occur
1823
 
        self.assertEqual(1, self.server.auth_required_errors)
1824
 
        # Since the authentification succeeded, there should be a corresponding
1825
 
        # debug line
1826
 
        sent_auth_headers = [line for line in self.mutters
1827
 
                             if line.startswith('> %s' % (self._auth_header,))]
1828
 
        self.assertLength(1, sent_auth_headers)
1829
 
        self.assertStartsWith(sent_auth_headers[0],
1830
 
                              '> %s: <masked>' % (self._auth_header,))
1831
1695
 
1832
1696
 
1833
1697
class TestProxyAuth(TestAuth):
1834
 
    """Test proxy authentication schemes.
1835
 
 
1836
 
    This inherits from TestAuth to tweak the setUp and filter some failing
1837
 
    tests.
1838
 
    """
1839
 
 
1840
 
    scenarios = multiply_scenarios(
1841
 
        vary_by_http_client_implementation(),
1842
 
        vary_by_http_protocol_version(),
1843
 
        vary_by_http_proxy_auth_scheme(),
1844
 
        )
 
1698
    """Test proxy authentication schemes."""
 
1699
 
 
1700
    _auth_header = 'Proxy-authorization'
 
1701
    _password_prompt_prefix = 'Proxy '
 
1702
    _username_prompt_prefix = 'Proxy '
1845
1703
 
1846
1704
    def setUp(self):
1847
1705
        super(TestProxyAuth, self).setUp()
 
1706
        self._old_env = {}
 
1707
        self.addCleanup(self._restore_env)
1848
1708
        # Override the contents to avoid false positives
1849
1709
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1850
1710
                                  ('b', 'not proxied contents of b\n'),
1853
1713
                                  ])
1854
1714
 
1855
1715
    def get_user_transport(self, user, password):
1856
 
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
1857
 
        return TestAuth.get_user_transport(self, user, password)
 
1716
        self._install_env({'all_proxy': self.get_user_url(user, password)})
 
1717
        return self._transport(self.server.get_url())
 
1718
 
 
1719
    def _install_env(self, env):
 
1720
        for name, value in env.iteritems():
 
1721
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
1722
 
 
1723
    def _restore_env(self):
 
1724
        for name, value in self._old_env.iteritems():
 
1725
            osutils.set_or_unset_env(name, value)
1858
1726
 
1859
1727
    def test_empty_pass(self):
1860
1728
        if self._testing_pycurl():
1861
1729
            import pycurl
1862
1730
            if pycurl.version_info()[1] < '7.16.0':
1863
 
                self.knownFailure(
 
1731
                raise tests.KnownFailure(
1864
1732
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1865
1733
        super(TestProxyAuth, self).test_empty_pass()
1866
1734
 
1879
1747
        self.readfile = StringIO(socket_read_content)
1880
1748
        self.writefile = StringIO()
1881
1749
        self.writefile.close = lambda: None
1882
 
        self.close = lambda: None
1883
1750
 
1884
1751
    def makefile(self, mode='r', bufsize=None):
1885
1752
        if 'r' in mode:
1890
1757
 
1891
1758
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1892
1759
 
1893
 
    scenarios = multiply_scenarios(
1894
 
        vary_by_http_client_implementation(),
1895
 
        vary_by_http_protocol_version(),
1896
 
        )
1897
 
 
1898
1760
    def setUp(self):
1899
1761
        super(SmartHTTPTunnellingTest, self).setUp()
1900
1762
        # We use the VFS layer as part of HTTP tunnelling tests.
1901
 
        self.overrideEnv('BZR_NO_SMART_VFS', None)
 
1763
        self._captureVar('BZR_NO_SMART_VFS', None)
1902
1764
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1903
 
        self.http_server = self.get_readonly_server()
1904
1765
 
1905
1766
    def create_transport_readonly_server(self):
1906
 
        server = http_utils.HTTPServerWithSmarts(
 
1767
        return http_utils.HTTPServerWithSmarts(
1907
1768
            protocol_version=self._protocol_version)
1908
 
        server._url_protocol = self._url_protocol
1909
 
        return server
1910
1769
 
1911
 
    def test_open_controldir(self):
 
1770
    def test_open_bzrdir(self):
1912
1771
        branch = self.make_branch('relpath')
1913
 
        url = self.http_server.get_url() + 'relpath'
1914
 
        bd = controldir.ControlDir.open(url)
1915
 
        self.addCleanup(bd.transport.disconnect)
 
1772
        http_server = self.get_readonly_server()
 
1773
        url = http_server.get_url() + 'relpath'
 
1774
        bd = bzrdir.BzrDir.open(url)
1916
1775
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1917
1776
 
1918
1777
    def test_bulk_data(self):
1920
1779
        # The 'readv' command in the smart protocol both sends and receives
1921
1780
        # bulk data, so we use that.
1922
1781
        self.build_tree(['data-file'])
1923
 
        http_transport = transport.get_transport_from_url(
1924
 
            self.http_server.get_url())
 
1782
        http_server = self.get_readonly_server()
 
1783
        http_transport = self._transport(http_server.get_url())
1925
1784
        medium = http_transport.get_smart_medium()
1926
1785
        # Since we provide the medium, the url below will be mostly ignored
1927
1786
        # during the test, as long as the path is '/'.
1935
1794
        post_body = 'hello\n'
1936
1795
        expected_reply_body = 'ok\x012\n'
1937
1796
 
1938
 
        http_transport = transport.get_transport_from_url(
1939
 
            self.http_server.get_url())
 
1797
        http_server = self.get_readonly_server()
 
1798
        http_transport = self._transport(http_server.get_url())
1940
1799
        medium = http_transport.get_smart_medium()
1941
1800
        response = medium.send_http_smart_request(post_body)
1942
1801
        reply_body = response.read()
1943
1802
        self.assertEqual(expected_reply_body, reply_body)
1944
1803
 
1945
1804
    def test_smart_http_server_post_request_handler(self):
1946
 
        httpd = self.http_server.server
 
1805
        httpd = self.get_readonly_server()._get_httpd()
1947
1806
 
1948
1807
        socket = SampleSocket(
1949
1808
            'POST /.bzr/smart %s \r\n' % self._protocol_version
1981
1840
 
1982
1841
    def test_probe_smart_server(self):
1983
1842
        """Test error handling against server refusing smart requests."""
1984
 
        t = self.get_readonly_transport()
 
1843
        server = self.get_readonly_server()
 
1844
        t = self._transport(server.get_url())
1985
1845
        # No need to build a valid smart request here, the server will not even
1986
1846
        # try to interpret it.
1987
1847
        self.assertRaises(errors.SmartProtocolError,
1988
1848
                          t.get_smart_medium().send_http_smart_request,
1989
1849
                          'whatever')
1990
1850
 
1991
 
 
1992
1851
class Test_redirected_to(tests.TestCase):
1993
1852
 
1994
 
    scenarios = vary_by_http_client_implementation()
1995
 
 
1996
1853
    def test_redirected_to_subdir(self):
1997
1854
        t = self._transport('http://www.example.com/foo')
1998
1855
        r = t._redirected_to('http://www.example.com/foo',
1999
1856
                             'http://www.example.com/foo/subdir')
2000
1857
        self.assertIsInstance(r, type(t))
2001
1858
        # Both transports share the some connection
2002
 
        self.assertEqual(t._get_connection(), r._get_connection())
2003
 
        self.assertEqual('http://www.example.com/foo/subdir/', r.base)
 
1859
        self.assertEquals(t._get_connection(), r._get_connection())
2004
1860
 
2005
1861
    def test_redirected_to_self_with_slash(self):
2006
1862
        t = self._transport('http://www.example.com/foo')
2010
1866
        # Both transports share the some connection (one can argue that we
2011
1867
        # should return the exact same transport here, but that seems
2012
1868
        # overkill).
2013
 
        self.assertEqual(t._get_connection(), r._get_connection())
 
1869
        self.assertEquals(t._get_connection(), r._get_connection())
2014
1870
 
2015
1871
    def test_redirected_to_host(self):
2016
1872
        t = self._transport('http://www.example.com/foo')
2017
1873
        r = t._redirected_to('http://www.example.com/foo',
2018
1874
                             'http://foo.example.com/foo/subdir')
2019
1875
        self.assertIsInstance(r, type(t))
2020
 
        self.assertEqual('http://foo.example.com/foo/subdir/',
2021
 
            r.external_url())
2022
1876
 
2023
1877
    def test_redirected_to_same_host_sibling_protocol(self):
2024
1878
        t = self._transport('http://www.example.com/foo')
2025
1879
        r = t._redirected_to('http://www.example.com/foo',
2026
1880
                             'https://www.example.com/foo')
2027
1881
        self.assertIsInstance(r, type(t))
2028
 
        self.assertEqual('https://www.example.com/foo/',
2029
 
            r.external_url())
2030
1882
 
2031
1883
    def test_redirected_to_same_host_different_protocol(self):
2032
1884
        t = self._transport('http://www.example.com/foo')
2033
1885
        r = t._redirected_to('http://www.example.com/foo',
2034
1886
                             'ftp://www.example.com/foo')
2035
 
        self.assertNotEqual(type(r), type(t))
2036
 
        self.assertEqual('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.assertEqual('https://www.example.com/foo/', r.external_url())
 
1887
        self.assertNotEquals(type(r), type(t))
2043
1888
 
2044
1889
    def test_redirected_to_different_host_same_user(self):
2045
1890
        t = self._transport('http://joe@www.example.com/foo')
2046
1891
        r = t._redirected_to('http://www.example.com/foo',
2047
1892
                             'https://foo.example.com/foo')
2048
1893
        self.assertIsInstance(r, type(t))
2049
 
        self.assertEqual(t._parsed_url.user, r._parsed_url.user)
2050
 
        self.assertEqual('https://joe@foo.example.com/foo/', r.external_url())
 
1894
        self.assertEquals(t._user, r._user)
2051
1895
 
2052
1896
 
2053
1897
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
2062
1906
    line.
2063
1907
    """
2064
1908
 
2065
 
    def _handle_one_request(self):
 
1909
    def handle_one_request(self):
2066
1910
        tcs = self.server.test_case_server
2067
1911
        requestline = self.rfile.readline()
2068
1912
        headers = self.MessageClass(self.rfile, 0)
2106
1950
    pass
2107
1951
 
2108
1952
 
2109
 
if features.HTTPSServerFeature.available():
 
1953
if tests.HTTPSServerFeature.available():
2110
1954
    from bzrlib.tests import https_server
2111
1955
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
2112
1956
        pass
2113
1957
 
2114
1958
 
2115
 
class TestActivityMixin(object):
 
1959
class TestActivity(tests.TestCase):
2116
1960
    """Test socket activity reporting.
2117
1961
 
2118
1962
    We use a special purpose server to control the bytes sent and received and
2120
1964
    """
2121
1965
 
2122
1966
    def setUp(self):
 
1967
        tests.TestCase.setUp(self)
2123
1968
        self.server = self._activity_server(self._protocol_version)
2124
 
        self.server.start_server()
2125
 
        self.addCleanup(self.server.stop_server)
2126
 
        _activities = {} # Don't close over self and create a cycle
 
1969
        self.server.setUp()
 
1970
        self.activities = {}
2127
1971
        def report_activity(t, bytes, direction):
2128
 
            count = _activities.get(direction, 0)
 
1972
            count = self.activities.get(direction, 0)
2129
1973
            count += bytes
2130
 
            _activities[direction] = count
2131
 
        self.activities = _activities
 
1974
            self.activities[direction] = count
 
1975
 
2132
1976
        # We override at class level because constructors may propagate the
2133
1977
        # bound method and render instance overriding ineffective (an
2134
1978
        # alternative would be to define a specific ui factory instead...)
2135
 
        self.overrideAttr(self._transport, '_report_activity', report_activity)
 
1979
        self.orig_report_activity = self._transport._report_activity
 
1980
        self._transport._report_activity = report_activity
 
1981
 
 
1982
    def tearDown(self):
 
1983
        self._transport._report_activity = self.orig_report_activity
 
1984
        self.server.tearDown()
 
1985
        tests.TestCase.tearDown(self)
2136
1986
 
2137
1987
    def get_transport(self):
2138
 
        t = self._transport(self.server.get_url())
2139
 
        # FIXME: Needs cleanup -- vila 20100611
2140
 
        return t
 
1988
        return self._transport(self.server.get_url())
2141
1989
 
2142
1990
    def assertActivitiesMatch(self):
2143
1991
        self.assertEqual(self.server.bytes_read,
2248
2096
'''
2249
2097
        t = self.get_transport()
2250
2098
        # We must send a single line of body bytes, see
2251
 
        # PredefinedRequestHandler._handle_one_request
 
2099
        # PredefinedRequestHandler.handle_one_request
2252
2100
        code, f = t._post('abc def end-of-body\n')
2253
2101
        self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2254
2102
        self.assertActivitiesMatch()
2255
 
 
2256
 
 
2257
 
class TestActivity(tests.TestCase, TestActivityMixin):
2258
 
 
2259
 
    scenarios = multiply_scenarios(
2260
 
        vary_by_http_activity(),
2261
 
        vary_by_http_protocol_version(),
2262
 
        )
2263
 
 
2264
 
    def setUp(self):
2265
 
        super(TestActivity, self).setUp()
2266
 
        TestActivityMixin.setUp(self)
2267
 
 
2268
 
 
2269
 
class TestNoReportActivity(tests.TestCase, TestActivityMixin):
2270
 
 
2271
 
    # Unlike TestActivity, we are really testing ReportingFileSocket and
2272
 
    # ReportingSocket, so we don't need all the parametrization. Since
2273
 
    # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
2274
 
    # test them through their use by the transport than directly (that's a
2275
 
    # bit less clean but far more simpler and effective).
2276
 
    _activity_server = ActivityHTTPServer
2277
 
    _protocol_version = 'HTTP/1.1'
2278
 
 
2279
 
    def setUp(self):
2280
 
        super(TestNoReportActivity, self).setUp()
2281
 
        self._transport =_urllib.HttpTransport_urllib
2282
 
        TestActivityMixin.setUp(self)
2283
 
 
2284
 
    def assertActivitiesMatch(self):
2285
 
        # Nothing to check here
2286
 
        pass
2287
 
 
2288
 
 
2289
 
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2290
 
    """Test authentication on the redirected http server."""
2291
 
 
2292
 
    scenarios = vary_by_http_protocol_version()
2293
 
 
2294
 
    _auth_header = 'Authorization'
2295
 
    _password_prompt_prefix = ''
2296
 
    _username_prompt_prefix = ''
2297
 
    _auth_server = http_utils.HTTPBasicAuthServer
2298
 
    _transport = _urllib.HttpTransport_urllib
2299
 
 
2300
 
    def setUp(self):
2301
 
        super(TestAuthOnRedirected, self).setUp()
2302
 
        self.build_tree_contents([('a','a'),
2303
 
                                  ('1/',),
2304
 
                                  ('1/a', 'redirected once'),
2305
 
                                  ],)
2306
 
        new_prefix = 'http://%s:%s' % (self.new_server.host,
2307
 
                                       self.new_server.port)
2308
 
        self.old_server.redirections = [
2309
 
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2310
 
        self.old_transport = self.get_old_transport()
2311
 
        self.new_server.add_user('joe', 'foo')
2312
 
        cleanup_http_redirection_connections(self)
2313
 
 
2314
 
    def create_transport_readonly_server(self):
2315
 
        server = self._auth_server(protocol_version=self._protocol_version)
2316
 
        server._url_protocol = self._url_protocol
2317
 
        return server
2318
 
 
2319
 
    def get_a(self, t):
2320
 
        return t.get('a')
2321
 
 
2322
 
    def test_auth_on_redirected_via_do_catching_redirections(self):
2323
 
        self.redirections = 0
2324
 
 
2325
 
        def redirected(t, exception, redirection_notice):
2326
 
            self.redirections += 1
2327
 
            redirected_t = t._redirected_to(exception.source, exception.target)
2328
 
            self.addCleanup(redirected_t.disconnect)
2329
 
            return redirected_t
2330
 
 
2331
 
        stdout = tests.StringIOWrapper()
2332
 
        stderr = tests.StringIOWrapper()
2333
 
        ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2334
 
                                            stdout=stdout, stderr=stderr)
2335
 
        self.assertEqual('redirected once',
2336
 
                         transport.do_catching_redirections(
2337
 
                self.get_a, self.old_transport, redirected).read())
2338
 
        self.assertEqual(1, self.redirections)
2339
 
        # stdin should be empty
2340
 
        self.assertEqual('', ui.ui_factory.stdin.readline())
2341
 
        # stdout should be empty, stderr will contains the prompts
2342
 
        self.assertEqual('', stdout.getvalue())
2343
 
 
2344
 
    def test_auth_on_redirected_via_following_redirections(self):
2345
 
        self.new_server.add_user('joe', 'foo')
2346
 
        stdout = tests.StringIOWrapper()
2347
 
        stderr = tests.StringIOWrapper()
2348
 
        ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2349
 
                                            stdout=stdout, stderr=stderr)
2350
 
        t = self.old_transport
2351
 
        req = RedirectedRequest('GET', t.abspath('a'))
2352
 
        new_prefix = 'http://%s:%s' % (self.new_server.host,
2353
 
                                       self.new_server.port)
2354
 
        self.old_server.redirections = [
2355
 
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2356
 
        self.assertEqual('redirected once', t._perform(req).read())
2357
 
        # stdin should be empty
2358
 
        self.assertEqual('', ui.ui_factory.stdin.readline())
2359
 
        # stdout should be empty, stderr will contains the prompts
2360
 
        self.assertEqual('', stdout.getvalue())
2361