/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: John Arbash Meinel
  • Date: 2011-04-20 09:46:28 UTC
  • mfrom: (5609.33.4 2.3)
  • mto: (5609.33.5 2.3)
  • mto: This revision was merged to the branch mainline in revision 5811.
  • Revision ID: john@arbash-meinel.com-20110420094628-l0bafq1lwb6ib1v2
Merge lp:bzr/2.3 @ 5640 so we can update the release notes (aka NEWS)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
# TODO: Should be renamed to bzrlib.transport.http.tests?
24
24
# TODO: What about renaming to bzrlib.tests.transport.http ?
25
25
 
26
 
from cStringIO import StringIO
27
26
import httplib
28
 
import os
29
 
import select
30
27
import SimpleHTTPServer
31
28
import socket
32
29
import sys
42
39
    tests,
43
40
    transport,
44
41
    ui,
45
 
    urlutils,
46
 
    )
47
 
from bzrlib.symbol_versioning import (
48
 
    deprecated_in,
49
42
    )
50
43
from bzrlib.tests import (
51
44
    features,
52
45
    http_server,
53
46
    http_utils,
 
47
    test_server,
 
48
    )
 
49
from bzrlib.tests.scenarios import (
 
50
    load_tests_apply_scenarios,
 
51
    multiply_scenarios,
54
52
    )
55
53
from bzrlib.transport import (
56
54
    http,
66
64
    from bzrlib.transport.http._pycurl import PyCurlTransport
67
65
 
68
66
 
69
 
def load_tests(standard_tests, module, loader):
70
 
    """Multiply tests for http clients and protocol versions."""
71
 
    result = loader.suiteClass()
72
 
 
73
 
    # one for each transport implementation
74
 
    t_tests, remaining_tests = tests.split_suite_by_condition(
75
 
        standard_tests, tests.condition_isinstance((
76
 
                TestHttpTransportRegistration,
77
 
                TestHttpTransportUrls,
78
 
                Test_redirected_to,
79
 
                )))
 
67
load_tests = load_tests_apply_scenarios
 
68
 
 
69
 
 
70
def vary_by_http_client_implementation():
 
71
    """Test the two libraries we can use, pycurl and urllib."""
80
72
    transport_scenarios = [
81
73
        ('urllib', dict(_transport=_urllib.HttpTransport_urllib,
82
74
                        _server=http_server.HttpServer_urllib,
83
 
                        _qualified_prefix='http+urllib',)),
 
75
                        _url_protocol='http+urllib',)),
84
76
        ]
85
77
    if features.pycurl.available():
86
78
        transport_scenarios.append(
87
79
            ('pycurl', dict(_transport=PyCurlTransport,
88
80
                            _server=http_server.HttpServer_PyCurl,
89
 
                            _qualified_prefix='http+pycurl',)))
90
 
    tests.multiply_tests(t_tests, transport_scenarios, result)
91
 
 
92
 
    # each implementation tested with each HTTP version
93
 
    tp_tests, remaining_tests = tests.split_suite_by_condition(
94
 
        remaining_tests, tests.condition_isinstance((
95
 
                SmartHTTPTunnellingTest,
96
 
                TestDoCatchRedirections,
97
 
                TestHTTPConnections,
98
 
                TestHTTPRedirections,
99
 
                TestHTTPSilentRedirections,
100
 
                TestLimitedRangeRequestServer,
101
 
                TestPost,
102
 
                TestProxyHttpServer,
103
 
                TestRanges,
104
 
                TestSpecificRequestHandler,
105
 
                )))
106
 
    protocol_scenarios = [
107
 
            ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
108
 
            ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
109
 
            ]
110
 
    tp_scenarios = tests.multiply_scenarios(transport_scenarios,
111
 
                                            protocol_scenarios)
112
 
    tests.multiply_tests(tp_tests, tp_scenarios, result)
113
 
 
114
 
    # proxy auth: each auth scheme on all http versions on all implementations.
115
 
    tppa_tests, remaining_tests = tests.split_suite_by_condition(
116
 
        remaining_tests, tests.condition_isinstance((
117
 
                TestProxyAuth,
118
 
                )))
119
 
    proxy_auth_scheme_scenarios = [
 
81
                            _url_protocol='http+pycurl',)))
 
82
    return transport_scenarios
 
83
 
 
84
 
 
85
def vary_by_http_protocol_version():
 
86
    """Test on http/1.0 and 1.1"""
 
87
    return [
 
88
        ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
 
89
        ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
 
90
        ]
 
91
 
 
92
 
 
93
def vary_by_http_proxy_auth_scheme():
 
94
    return [
120
95
        ('basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
121
96
        ('digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
122
97
        ('basicdigest',
123
 
         dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
 
98
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
124
99
        ]
125
 
    tppa_scenarios = tests.multiply_scenarios(tp_scenarios,
126
 
                                              proxy_auth_scheme_scenarios)
127
 
    tests.multiply_tests(tppa_tests, tppa_scenarios, result)
128
 
 
129
 
    # auth: each auth scheme on all http versions on all implementations.
130
 
    tpa_tests, remaining_tests = tests.split_suite_by_condition(
131
 
        remaining_tests, tests.condition_isinstance((
132
 
                TestAuth,
133
 
                )))
134
 
    auth_scheme_scenarios = [
 
100
 
 
101
 
 
102
def vary_by_http_auth_scheme():
 
103
    return [
135
104
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
136
105
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
137
106
        ('basicdigest',
138
 
         dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
 
107
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
139
108
        ]
140
 
    tpa_scenarios = tests.multiply_scenarios(tp_scenarios,
141
 
                                             auth_scheme_scenarios)
142
 
    tests.multiply_tests(tpa_tests, tpa_scenarios, result)
143
 
 
144
 
    # activity: on all http[s] versions on all implementations
145
 
    tpact_tests, remaining_tests = tests.split_suite_by_condition(
146
 
        remaining_tests, tests.condition_isinstance((
147
 
                TestActivity,
148
 
                )))
 
109
 
 
110
 
 
111
def vary_by_http_activity():
149
112
    activity_scenarios = [
150
113
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
151
 
                             _transport=_urllib.HttpTransport_urllib,)),
 
114
                            _transport=_urllib.HttpTransport_urllib,)),
152
115
        ]
153
116
    if tests.HTTPSServerFeature.available():
154
117
        activity_scenarios.append(
155
118
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
156
 
                                  _transport=_urllib.HttpTransport_urllib,)),)
 
119
                                _transport=_urllib.HttpTransport_urllib,)),)
157
120
    if features.pycurl.available():
158
121
        activity_scenarios.append(
159
122
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
160
 
                                 _transport=PyCurlTransport,)),)
 
123
                                _transport=PyCurlTransport,)),)
161
124
        if tests.HTTPSServerFeature.available():
162
125
            from bzrlib.tests import (
163
126
                ssl_certs,
175
138
 
176
139
            activity_scenarios.append(
177
140
                ('pycurl,https', dict(_activity_server=ActivityHTTPSServer,
178
 
                                      _transport=HTTPS_pycurl_transport,)),)
179
 
 
180
 
    tpact_scenarios = tests.multiply_scenarios(activity_scenarios,
181
 
                                               protocol_scenarios)
182
 
    tests.multiply_tests(tpact_tests, tpact_scenarios, result)
183
 
 
184
 
    # No parametrization for the remaining tests
185
 
    result.addTests(remaining_tests)
186
 
 
187
 
    return result
 
141
                                    _transport=HTTPS_pycurl_transport,)),)
 
142
    return activity_scenarios
188
143
 
189
144
 
190
145
class FakeManager(object):
223
178
        self._sock.bind(('127.0.0.1', 0))
224
179
        self.host, self.port = self._sock.getsockname()
225
180
        self._ready = threading.Event()
226
 
        self._thread = threading.Thread(target=self._accept_read_and_reply)
227
 
        self._thread.setDaemon(True)
 
181
        self._thread = test_server.ThreadWithException(
 
182
            event=self._ready, target=self._accept_read_and_reply)
228
183
        self._thread.start()
229
 
        self._ready.wait(5)
 
184
        if 'threads' in tests.selftest_debug_flags:
 
185
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
 
186
        self._ready.wait()
230
187
 
231
188
    def _accept_read_and_reply(self):
232
189
        self._sock.listen(1)
233
190
        self._ready.set()
234
 
        self._sock.settimeout(5)
235
 
        try:
236
 
            conn, address = self._sock.accept()
237
 
            # On win32, the accepted connection will be non-blocking to start
238
 
            # with because we're using settimeout.
239
 
            conn.setblocking(True)
 
191
        conn, address = self._sock.accept()
 
192
        if self._expect_body_tail is not None:
240
193
            while not self.received_bytes.endswith(self._expect_body_tail):
241
194
                self.received_bytes += conn.recv(4096)
242
195
            conn.sendall('HTTP/1.1 200 OK\r\n')
243
 
        except socket.timeout:
244
 
            # Make sure the client isn't stuck waiting for us to e.g. accept.
 
196
        try:
245
197
            self._sock.close()
246
198
        except socket.error:
247
199
            # The client may have already closed the socket.
249
201
 
250
202
    def stop_server(self):
251
203
        try:
252
 
            self._sock.close()
 
204
            # Issue a fake connection to wake up the server and allow it to
 
205
            # finish quickly
 
206
            fake_conn = osutils.connect_socket((self.host, self.port))
 
207
            fake_conn.close()
253
208
        except socket.error:
254
209
            # We might have already closed it.  We don't care.
255
210
            pass
256
211
        self.host = None
257
212
        self.port = None
 
213
        self._thread.join()
 
214
        if 'threads' in tests.selftest_debug_flags:
 
215
            sys.stderr.write('Thread  joined: %s\n' % (self._thread.ident,))
258
216
 
259
217
 
260
218
class TestAuthHeader(tests.TestCase):
304
262
 
305
263
            protocol_version = 'HTTP/0.1'
306
264
 
307
 
        server = http_server.HttpServer(BogusRequestHandler)
308
 
        try:
309
 
            self.assertRaises(httplib.UnknownProtocol, server.start_server)
310
 
        except:
311
 
            server.stop_server()
312
 
            self.fail('HTTP Server creation did not raise UnknownProtocol')
 
265
        self.assertRaises(httplib.UnknownProtocol,
 
266
                          http_server.HttpServer, BogusRequestHandler)
313
267
 
314
268
    def test_force_invalid_protocol(self):
315
 
        server = http_server.HttpServer(protocol_version='HTTP/0.1')
316
 
        try:
317
 
            self.assertRaises(httplib.UnknownProtocol, server.start_server)
318
 
        except:
319
 
            server.stop_server()
320
 
            self.fail('HTTP Server creation did not raise UnknownProtocol')
 
269
        self.assertRaises(httplib.UnknownProtocol,
 
270
                          http_server.HttpServer, protocol_version='HTTP/0.1')
321
271
 
322
272
    def test_server_start_and_stop(self):
323
273
        server = http_server.HttpServer()
 
274
        self.addCleanup(server.stop_server)
324
275
        server.start_server()
325
 
        try:
326
 
            self.assertTrue(server._http_running)
327
 
        finally:
328
 
            server.stop_server()
329
 
        self.assertFalse(server._http_running)
 
276
        self.assertTrue(server.server is not None)
 
277
        self.assertTrue(server.server.serving is not None)
 
278
        self.assertTrue(server.server.serving)
330
279
 
331
280
    def test_create_http_server_one_zero(self):
332
281
        class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
335
284
 
336
285
        server = http_server.HttpServer(RequestHandlerOneZero)
337
286
        self.start_server(server)
338
 
        self.assertIsInstance(server._httpd, http_server.TestingHTTPServer)
 
287
        self.assertIsInstance(server.server, http_server.TestingHTTPServer)
339
288
 
340
289
    def test_create_http_server_one_one(self):
341
290
        class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
344
293
 
345
294
        server = http_server.HttpServer(RequestHandlerOneOne)
346
295
        self.start_server(server)
347
 
        self.assertIsInstance(server._httpd,
 
296
        self.assertIsInstance(server.server,
348
297
                              http_server.TestingThreadingHTTPServer)
349
298
 
350
299
    def test_create_http_server_force_one_one(self):
355
304
        server = http_server.HttpServer(RequestHandlerOneZero,
356
305
                                        protocol_version='HTTP/1.1')
357
306
        self.start_server(server)
358
 
        self.assertIsInstance(server._httpd,
 
307
        self.assertIsInstance(server.server,
359
308
                              http_server.TestingThreadingHTTPServer)
360
309
 
361
310
    def test_create_http_server_force_one_zero(self):
366
315
        server = http_server.HttpServer(RequestHandlerOneOne,
367
316
                                        protocol_version='HTTP/1.0')
368
317
        self.start_server(server)
369
 
        self.assertIsInstance(server._httpd,
 
318
        self.assertIsInstance(server.server,
370
319
                              http_server.TestingHTTPServer)
371
320
 
372
321
 
391
340
        self.assertEqual('http://example.com', url)
392
341
        self.assertEqual(0, len(f.credentials))
393
342
        url = http.extract_auth(
394
 
            'http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
395
 
        self.assertEqual('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
 
343
            'http://user:pass@example.com/bzr/bzr.dev', f)
 
344
        self.assertEqual('http://example.com/bzr/bzr.dev', url)
396
345
        self.assertEqual(1, len(f.credentials))
397
 
        self.assertEqual([None, 'www.bazaar-vcs.org', 'user', 'pass'],
 
346
        self.assertEqual([None, 'example.com', 'user', 'pass'],
398
347
                         f.credentials[0])
399
348
 
400
349
 
401
350
class TestHttpTransportUrls(tests.TestCase):
402
351
    """Test the http urls."""
403
352
 
 
353
    scenarios = vary_by_http_client_implementation()
 
354
 
404
355
    def test_abs_url(self):
405
356
        """Construction of absolute http URLs"""
406
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
357
        t = self._transport('http://example.com/bzr/bzr.dev/')
407
358
        eq = self.assertEqualDiff
408
 
        eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
409
 
        eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
410
 
        eq(t.abspath('.bzr'), 'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
 
359
        eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
 
360
        eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
 
361
        eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
411
362
        eq(t.abspath('.bzr/1//2/./3'),
412
 
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
 
363
           'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
413
364
 
414
365
    def test_invalid_http_urls(self):
415
366
        """Trap invalid construction of urls"""
416
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
367
        self._transport('http://example.com/bzr/bzr.dev/')
417
368
        self.assertRaises(errors.InvalidURL,
418
369
                          self._transport,
419
 
                          'http://http://bazaar-vcs.org/bzr/bzr.dev/')
 
370
                          'http://http://example.com/bzr/bzr.dev/')
420
371
 
421
372
    def test_http_root_urls(self):
422
373
        """Construction of URLs from server root"""
423
 
        t = self._transport('http://bzr.ozlabs.org/')
 
374
        t = self._transport('http://example.com/')
424
375
        eq = self.assertEqualDiff
425
376
        eq(t.abspath('.bzr/tree-version'),
426
 
           'http://bzr.ozlabs.org/.bzr/tree-version')
 
377
           'http://example.com/.bzr/tree-version')
427
378
 
428
379
    def test_http_impl_urls(self):
429
380
        """There are servers which ask for particular clients to connect"""
431
382
        server.start_server()
432
383
        try:
433
384
            url = server.get_url()
434
 
            self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
 
385
            self.assertTrue(url.startswith('%s://' % self._url_protocol))
435
386
        finally:
436
387
            server.stop_server()
437
388
 
452
403
        # Import the module locally now that we now it's available.
453
404
        pycurl = features.pycurl.module
454
405
 
455
 
        version_info_orig = pycurl.version_info
456
 
        def restore():
457
 
            pycurl.version_info = version_info_orig
458
 
        self.addCleanup(restore)
459
 
 
460
 
        # Fake the pycurl version_info This was taken from a windows pycurl
461
 
        # without SSL (thanks to bialix)
462
 
        pycurl.version_info = lambda : (2,
463
 
                                        '7.13.2',
464
 
                                        462082,
465
 
                                        'i386-pc-win32',
466
 
                                        2576,
467
 
                                        None,
468
 
                                        0,
469
 
                                        None,
470
 
                                        ('ftp', 'gopher', 'telnet',
471
 
                                         'dict', 'ldap', 'http', 'file'),
472
 
                                        None,
473
 
                                        0,
474
 
                                        None)
 
406
        self.overrideAttr(pycurl, 'version_info',
 
407
                          # Fake the pycurl version_info This was taken from
 
408
                          # a windows pycurl without SSL (thanks to bialix)
 
409
                          lambda : (2,
 
410
                                    '7.13.2',
 
411
                                    462082,
 
412
                                    'i386-pc-win32',
 
413
                                    2576,
 
414
                                    None,
 
415
                                    0,
 
416
                                    None,
 
417
                                    ('ftp', 'gopher', 'telnet',
 
418
                                     'dict', 'ldap', 'http', 'file'),
 
419
                                    None,
 
420
                                    0,
 
421
                                    None))
475
422
        self.assertRaises(errors.DependencyNotPresent, self._transport,
476
423
                          'https://launchpad.net')
477
424
 
479
426
class TestHTTPConnections(http_utils.TestCaseWithWebserver):
480
427
    """Test the http connections."""
481
428
 
 
429
    scenarios = multiply_scenarios(
 
430
        vary_by_http_client_implementation(), 
 
431
        vary_by_http_protocol_version(),
 
432
        )
 
433
 
482
434
    def setUp(self):
483
435
        http_utils.TestCaseWithWebserver.setUp(self)
484
436
        self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
486
438
 
487
439
    def test_http_has(self):
488
440
        server = self.get_readonly_server()
489
 
        t = self._transport(server.get_url())
 
441
        t = self.get_readonly_transport()
490
442
        self.assertEqual(t.has('foo/bar'), True)
491
443
        self.assertEqual(len(server.logs), 1)
492
444
        self.assertContainsRe(server.logs[0],
494
446
 
495
447
    def test_http_has_not_found(self):
496
448
        server = self.get_readonly_server()
497
 
        t = self._transport(server.get_url())
 
449
        t = self.get_readonly_transport()
498
450
        self.assertEqual(t.has('not-found'), False)
499
451
        self.assertContainsRe(server.logs[1],
500
452
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
501
453
 
502
454
    def test_http_get(self):
503
455
        server = self.get_readonly_server()
504
 
        t = self._transport(server.get_url())
 
456
        t = self.get_readonly_transport()
505
457
        fp = t.get('foo/bar')
506
458
        self.assertEqualDiff(
507
459
            fp.read(),
529
481
class TestHttpTransportRegistration(tests.TestCase):
530
482
    """Test registrations of various http implementations"""
531
483
 
 
484
    scenarios = vary_by_http_client_implementation()
 
485
 
532
486
    def test_http_registered(self):
533
 
        t = transport.get_transport('%s://foo.com/' % self._qualified_prefix)
 
487
        t = transport.get_transport('%s://foo.com/' % self._url_protocol)
534
488
        self.assertIsInstance(t, transport.Transport)
535
489
        self.assertIsInstance(t, self._transport)
536
490
 
537
491
 
538
492
class TestPost(tests.TestCase):
539
493
 
 
494
    scenarios = multiply_scenarios(
 
495
        vary_by_http_client_implementation(), 
 
496
        vary_by_http_protocol_version(),
 
497
        )
 
498
 
540
499
    def test_post_body_is_received(self):
541
500
        server = RecordingServer(expect_body_tail='end-of-body',
542
 
            scheme=self._qualified_prefix)
 
501
                                 scheme=self._url_protocol)
543
502
        self.start_server(server)
544
503
        url = server.get_url()
545
 
        http_transport = self._transport(url)
 
504
        # FIXME: needs a cleanup -- vila 20100611
 
505
        http_transport = transport.get_transport(url)
546
506
        code, response = http_transport._post('abc def end-of-body')
547
507
        self.assertTrue(
548
508
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
549
509
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
 
510
        self.assertTrue('content-type: application/octet-stream\r'
 
511
                        in server.received_bytes.lower())
550
512
        # The transport should not be assuming that the server can accept
551
513
        # chunked encoding the first time it connects, because HTTP/1.1, so we
552
514
        # check for the literal string.
588
550
    Daughter classes are expected to override _req_handler_class
589
551
    """
590
552
 
 
553
    scenarios = multiply_scenarios(
 
554
        vary_by_http_client_implementation(), 
 
555
        vary_by_http_protocol_version(),
 
556
        )
 
557
 
591
558
    # Provide a useful default
592
559
    _req_handler_class = http_server.TestingHTTPRequestHandler
593
560
 
594
561
    def create_transport_readonly_server(self):
595
 
        return http_server.HttpServer(self._req_handler_class,
596
 
                                      protocol_version=self._protocol_version)
 
562
        server = http_server.HttpServer(self._req_handler_class,
 
563
                                        protocol_version=self._protocol_version)
 
564
        server._url_protocol = self._url_protocol
 
565
        return server
597
566
 
598
567
    def _testing_pycurl(self):
599
568
        # TODO: This is duplicated for lots of the classes in this file
604
573
class WallRequestHandler(http_server.TestingHTTPRequestHandler):
605
574
    """Whatever request comes in, close the connection"""
606
575
 
607
 
    def handle_one_request(self):
 
576
    def _handle_one_request(self):
608
577
        """Handle a single HTTP request, by abruptly closing the connection"""
609
578
        self.close_connection = 1
610
579
 
615
584
    _req_handler_class = WallRequestHandler
616
585
 
617
586
    def test_http_has(self):
618
 
        server = self.get_readonly_server()
619
 
        t = self._transport(server.get_url())
 
587
        t = self.get_readonly_transport()
620
588
        # Unfortunately httplib (see HTTPResponse._read_status
621
589
        # for details) make no distinction between a closed
622
590
        # socket and badly formatted status line, so we can't
628
596
                          t.has, 'foo/bar')
629
597
 
630
598
    def test_http_get(self):
631
 
        server = self.get_readonly_server()
632
 
        t = self._transport(server.get_url())
 
599
        t = self.get_readonly_transport()
633
600
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
634
601
                           errors.InvalidHttpResponse),
635
602
                          t.get, 'foo/bar')
652
619
    _req_handler_class = BadStatusRequestHandler
653
620
 
654
621
    def test_http_has(self):
655
 
        server = self.get_readonly_server()
656
 
        t = self._transport(server.get_url())
 
622
        t = self.get_readonly_transport()
657
623
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
658
624
 
659
625
    def test_http_get(self):
660
 
        server = self.get_readonly_server()
661
 
        t = self._transport(server.get_url())
 
626
        t = self.get_readonly_transport()
662
627
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
663
628
 
664
629
 
669
634
        """Fakes handling a single HTTP request, returns a bad status"""
670
635
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
671
636
        self.wfile.write("Invalid status line\r\n")
 
637
        # If we don't close the connection pycurl will hang. Since this is a
 
638
        # stress test we don't *have* to respect the protocol, but we don't
 
639
        # have to sabotage it too much either.
 
640
        self.close_connection = True
672
641
        return False
673
642
 
674
643
 
680
649
 
681
650
    _req_handler_class = InvalidStatusRequestHandler
682
651
 
683
 
    def test_http_has(self):
684
 
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
685
 
            raise tests.KnownFailure(
686
 
                'pycurl hangs if the server send back garbage')
687
 
        super(TestInvalidStatusServer, self).test_http_has()
688
 
 
689
 
    def test_http_get(self):
690
 
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
691
 
            raise tests.KnownFailure(
692
 
                'pycurl hangs if the server send back garbage')
693
 
        super(TestInvalidStatusServer, self).test_http_get()
694
 
 
695
652
 
696
653
class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
697
654
    """Whatever request comes in, returns a bad protocol version"""
719
676
        super(TestBadProtocolServer, self).setUp()
720
677
 
721
678
    def test_http_has(self):
722
 
        server = self.get_readonly_server()
723
 
        t = self._transport(server.get_url())
 
679
        t = self.get_readonly_transport()
724
680
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
725
681
 
726
682
    def test_http_get(self):
727
 
        server = self.get_readonly_server()
728
 
        t = self._transport(server.get_url())
 
683
        t = self.get_readonly_transport()
729
684
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
730
685
 
731
686
 
745
700
    _req_handler_class = ForbiddenRequestHandler
746
701
 
747
702
    def test_http_has(self):
748
 
        server = self.get_readonly_server()
749
 
        t = self._transport(server.get_url())
 
703
        t = self.get_readonly_transport()
750
704
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
751
705
 
752
706
    def test_http_get(self):
753
 
        server = self.get_readonly_server()
754
 
        t = self._transport(server.get_url())
 
707
        t = self.get_readonly_transport()
755
708
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
756
709
 
757
710
 
796
749
        self.build_tree_contents([('a', '0123456789')],)
797
750
 
798
751
    def test_readv(self):
799
 
        server = self.get_readonly_server()
800
 
        t = self._transport(server.get_url())
 
752
        t = self.get_readonly_transport()
801
753
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
802
754
        self.assertEqual(l[0], (0, '0'))
803
755
        self.assertEqual(l[1], (1, '1'))
805
757
        self.assertEqual(l[3], (9, '9'))
806
758
 
807
759
    def test_readv_out_of_order(self):
808
 
        server = self.get_readonly_server()
809
 
        t = self._transport(server.get_url())
 
760
        t = self.get_readonly_transport()
810
761
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
811
762
        self.assertEqual(l[0], (1, '1'))
812
763
        self.assertEqual(l[1], (9, '9'))
814
765
        self.assertEqual(l[3], (3, '34'))
815
766
 
816
767
    def test_readv_invalid_ranges(self):
817
 
        server = self.get_readonly_server()
818
 
        t = self._transport(server.get_url())
 
768
        t = self.get_readonly_transport()
819
769
 
820
770
        # This is intentionally reading off the end of the file
821
771
        # since we are sure that it cannot get there
829
779
 
830
780
    def test_readv_multiple_get_requests(self):
831
781
        server = self.get_readonly_server()
832
 
        t = self._transport(server.get_url())
 
782
        t = self.get_readonly_transport()
833
783
        # force transport to issue multiple requests
834
784
        t._max_readv_combine = 1
835
785
        t._max_get_ranges = 1
843
793
 
844
794
    def test_readv_get_max_size(self):
845
795
        server = self.get_readonly_server()
846
 
        t = self._transport(server.get_url())
 
796
        t = self.get_readonly_transport()
847
797
        # force transport to issue multiple requests by limiting the number of
848
798
        # bytes by request. Note that this apply to coalesced offsets only, a
849
799
        # single range will keep its size even if bigger than the limit.
858
808
 
859
809
    def test_complete_readv_leave_pipe_clean(self):
860
810
        server = self.get_readonly_server()
861
 
        t = self._transport(server.get_url())
 
811
        t = self.get_readonly_transport()
862
812
        # force transport to issue multiple requests
863
813
        t._get_max_size = 2
864
 
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
 
814
        list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
865
815
        # The server should have issued 3 requests
866
816
        self.assertEqual(3, server.GET_request_nb)
867
817
        self.assertEqual('0123456789', t.get_bytes('a'))
869
819
 
870
820
    def test_incomplete_readv_leave_pipe_clean(self):
871
821
        server = self.get_readonly_server()
872
 
        t = self._transport(server.get_url())
 
822
        t = self.get_readonly_transport()
873
823
        # force transport to issue multiple requests
874
824
        t._get_max_size = 2
875
825
        # Don't collapse readv results into a list so that we leave unread
944
894
    def get_multiple_ranges(self, file, file_size, ranges):
945
895
        self.send_response(206)
946
896
        self.send_header('Accept-Ranges', 'bytes')
 
897
        # XXX: this is strange; the 'random' name below seems undefined and
 
898
        # yet the tests pass -- mbp 2010-10-11 bug 658773
947
899
        boundary = "%d" % random.randint(0,0x7FFFFFFF)
948
900
        self.send_header("Content-Type",
949
901
                         "multipart/byteranges; boundary=%s" % boundary)
1011
963
                return
1012
964
            self.send_range_content(file, start, end - start + 1)
1013
965
            cur += 1
1014
 
        # No final boundary
 
966
        # Final boundary
1015
967
        self.wfile.write(boundary_line)
1016
968
 
1017
969
 
1025
977
 
1026
978
    def test_readv_with_short_reads(self):
1027
979
        server = self.get_readonly_server()
1028
 
        t = self._transport(server.get_url())
 
980
        t = self.get_readonly_transport()
1029
981
        # Force separate ranges for each offset
1030
982
        t._bytes_to_read_before_seek = 0
1031
983
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1046
998
        # that mode
1047
999
        self.assertEqual('single', t._range_hint)
1048
1000
 
 
1001
 
1049
1002
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1050
1003
    """Errors out when range specifiers exceed the limit"""
1051
1004
 
1075
1028
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1076
1029
    """Tests readv requests against a server erroring out on too much ranges."""
1077
1030
 
 
1031
    scenarios = multiply_scenarios(
 
1032
        vary_by_http_client_implementation(), 
 
1033
        vary_by_http_protocol_version(),
 
1034
        )
 
1035
 
1078
1036
    # Requests with more range specifiers will error out
1079
1037
    range_limit = 3
1080
1038
 
1082
1040
        return LimitedRangeHTTPServer(range_limit=self.range_limit,
1083
1041
                                      protocol_version=self._protocol_version)
1084
1042
 
1085
 
    def get_transport(self):
1086
 
        return self._transport(self.get_readonly_server().get_url())
1087
 
 
1088
1043
    def setUp(self):
1089
1044
        http_utils.TestCaseWithWebserver.setUp(self)
1090
1045
        # We need to manipulate ranges that correspond to real chunks in the
1094
1049
        self.build_tree_contents([('a', content)],)
1095
1050
 
1096
1051
    def test_few_ranges(self):
1097
 
        t = self.get_transport()
 
1052
        t = self.get_readonly_transport()
1098
1053
        l = list(t.readv('a', ((0, 4), (1024, 4), )))
1099
1054
        self.assertEqual(l[0], (0, '0000'))
1100
1055
        self.assertEqual(l[1], (1024, '0001'))
1101
1056
        self.assertEqual(1, self.get_readonly_server().GET_request_nb)
1102
1057
 
1103
1058
    def test_more_ranges(self):
1104
 
        t = self.get_transport()
 
1059
        t = self.get_readonly_transport()
1105
1060
        l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
1106
1061
        self.assertEqual(l[0], (0, '0000'))
1107
1062
        self.assertEqual(l[1], (1024, '0001'))
1118
1073
    Only the urllib implementation is tested here.
1119
1074
    """
1120
1075
 
1121
 
    def setUp(self):
1122
 
        tests.TestCase.setUp(self)
1123
 
        self._old_env = {}
1124
 
 
1125
 
    def tearDown(self):
1126
 
        self._restore_env()
1127
 
        tests.TestCase.tearDown(self)
1128
 
 
1129
 
    def _install_env(self, env):
1130
 
        for name, value in env.iteritems():
1131
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1132
 
 
1133
 
    def _restore_env(self):
1134
 
        for name, value in self._old_env.iteritems():
1135
 
            osutils.set_or_unset_env(name, value)
1136
 
 
1137
1076
    def _proxied_request(self):
1138
1077
        handler = _urllib2_wrappers.ProxyHandler()
1139
1078
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
1141
1080
        return request
1142
1081
 
1143
1082
    def test_empty_user(self):
1144
 
        self._install_env({'http_proxy': 'http://bar.com'})
 
1083
        self.overrideEnv('http_proxy', 'http://bar.com')
1145
1084
        request = self._proxied_request()
1146
1085
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1147
1086
 
1148
1087
    def test_invalid_proxy(self):
1149
1088
        """A proxy env variable without scheme"""
1150
 
        self._install_env({'http_proxy': 'host:1234'})
 
1089
        self.overrideEnv('http_proxy', 'host:1234')
1151
1090
        self.assertRaises(errors.InvalidURL, self._proxied_request)
1152
1091
 
1153
1092
 
1160
1099
    to the file names).
1161
1100
    """
1162
1101
 
 
1102
    scenarios = multiply_scenarios(
 
1103
        vary_by_http_client_implementation(), 
 
1104
        vary_by_http_protocol_version(),
 
1105
        )
 
1106
 
1163
1107
    # FIXME: We don't have an https server available, so we don't
1164
 
    # test https connections.
 
1108
    # test https connections. --vila toolongago
1165
1109
 
1166
1110
    def setUp(self):
1167
1111
        super(TestProxyHttpServer, self).setUp()
 
1112
        self.transport_secondary_server = http_utils.ProxyServer
1168
1113
        self.build_tree_contents([('foo', 'contents of foo\n'),
1169
1114
                                  ('foo-proxied', 'proxied contents of foo\n')])
1170
1115
        # Let's setup some attributes for tests
1171
 
        self.server = self.get_readonly_server()
1172
 
        self.proxy_address = '%s:%d' % (self.server.host, self.server.port)
 
1116
        server = self.get_readonly_server()
 
1117
        self.server_host_port = '%s:%d' % (server.host, server.port)
1173
1118
        if self._testing_pycurl():
1174
1119
            # Oh my ! pycurl does not check for the port as part of
1175
1120
            # no_proxy :-( So we just test the host part
1176
 
            self.no_proxy_host = self.server.host
 
1121
            self.no_proxy_host = server.host
1177
1122
        else:
1178
 
            self.no_proxy_host = self.proxy_address
 
1123
            self.no_proxy_host = self.server_host_port
1179
1124
        # The secondary server is the proxy
1180
 
        self.proxy = self.get_secondary_server()
1181
 
        self.proxy_url = self.proxy.get_url()
1182
 
        self._old_env = {}
 
1125
        self.proxy_url = self.get_secondary_url()
1183
1126
 
1184
1127
    def _testing_pycurl(self):
1185
1128
        # TODO: This is duplicated for lots of the classes in this file
1186
1129
        return (features.pycurl.available()
1187
1130
                and self._transport == PyCurlTransport)
1188
1131
 
1189
 
    def create_transport_secondary_server(self):
1190
 
        """Creates an http server that will serve files with
1191
 
        '-proxied' appended to their names.
1192
 
        """
1193
 
        return http_utils.ProxyServer(protocol_version=self._protocol_version)
1194
 
 
1195
 
    def _install_env(self, env):
1196
 
        for name, value in env.iteritems():
1197
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1198
 
 
1199
 
    def _restore_env(self):
1200
 
        for name, value in self._old_env.iteritems():
1201
 
            osutils.set_or_unset_env(name, value)
1202
 
 
1203
 
    def proxied_in_env(self, env):
1204
 
        self._install_env(env)
1205
 
        url = self.server.get_url()
1206
 
        t = self._transport(url)
1207
 
        try:
1208
 
            self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1209
 
        finally:
1210
 
            self._restore_env()
1211
 
 
1212
 
    def not_proxied_in_env(self, env):
1213
 
        self._install_env(env)
1214
 
        url = self.server.get_url()
1215
 
        t = self._transport(url)
1216
 
        try:
1217
 
            self.assertEqual('contents of foo\n', t.get('foo').read())
1218
 
        finally:
1219
 
            self._restore_env()
 
1132
    def assertProxied(self):
 
1133
        t = self.get_readonly_transport()
 
1134
        self.assertEqual('proxied contents of foo\n', t.get('foo').read())
 
1135
 
 
1136
    def assertNotProxied(self):
 
1137
        t = self.get_readonly_transport()
 
1138
        self.assertEqual('contents of foo\n', t.get('foo').read())
1220
1139
 
1221
1140
    def test_http_proxy(self):
1222
 
        self.proxied_in_env({'http_proxy': self.proxy_url})
 
1141
        self.overrideEnv('http_proxy', self.proxy_url)
 
1142
        self.assertProxied()
1223
1143
 
1224
1144
    def test_HTTP_PROXY(self):
1225
1145
        if self._testing_pycurl():
1228
1148
            # about. Should we ?)
1229
1149
            raise tests.TestNotApplicable(
1230
1150
                'pycurl does not check HTTP_PROXY for security reasons')
1231
 
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
 
1151
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1152
        self.assertProxied()
1232
1153
 
1233
1154
    def test_all_proxy(self):
1234
 
        self.proxied_in_env({'all_proxy': self.proxy_url})
 
1155
        self.overrideEnv('all_proxy', self.proxy_url)
 
1156
        self.assertProxied()
1235
1157
 
1236
1158
    def test_ALL_PROXY(self):
1237
 
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
 
1159
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1160
        self.assertProxied()
1238
1161
 
1239
1162
    def test_http_proxy_with_no_proxy(self):
1240
 
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
1241
 
                                 'no_proxy': self.no_proxy_host})
 
1163
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1164
        self.overrideEnv('http_proxy', self.proxy_url)
 
1165
        self.assertNotProxied()
1242
1166
 
1243
1167
    def test_HTTP_PROXY_with_NO_PROXY(self):
1244
1168
        if self._testing_pycurl():
1245
1169
            raise tests.TestNotApplicable(
1246
1170
                'pycurl does not check HTTP_PROXY for security reasons')
1247
 
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
1248
 
                                 'NO_PROXY': self.no_proxy_host})
 
1171
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1172
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
 
1173
        self.assertNotProxied()
1249
1174
 
1250
1175
    def test_all_proxy_with_no_proxy(self):
1251
 
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
1252
 
                                 'no_proxy': self.no_proxy_host})
 
1176
        self.overrideEnv('no_proxy', self.no_proxy_host)
 
1177
        self.overrideEnv('all_proxy', self.proxy_url)
 
1178
        self.assertNotProxied()
1253
1179
 
1254
1180
    def test_ALL_PROXY_with_NO_PROXY(self):
1255
 
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
1256
 
                                 'NO_PROXY': self.no_proxy_host})
 
1181
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
 
1182
        self.overrideEnv('ALL_PROXY', self.proxy_url)
 
1183
        self.assertNotProxied()
1257
1184
 
1258
1185
    def test_http_proxy_without_scheme(self):
 
1186
        self.overrideEnv('http_proxy', self.server_host_port)
1259
1187
        if self._testing_pycurl():
1260
1188
            # pycurl *ignores* invalid proxy env variables. If that ever change
1261
1189
            # in the future, this test will fail indicating that pycurl do not
1262
1190
            # ignore anymore such variables.
1263
 
            self.not_proxied_in_env({'http_proxy': self.proxy_address})
 
1191
            self.assertNotProxied()
1264
1192
        else:
1265
 
            self.assertRaises(errors.InvalidURL,
1266
 
                              self.proxied_in_env,
1267
 
                              {'http_proxy': self.proxy_address})
 
1193
            self.assertRaises(errors.InvalidURL, self.assertProxied)
1268
1194
 
1269
1195
 
1270
1196
class TestRanges(http_utils.TestCaseWithWebserver):
1271
1197
    """Test the Range header in GET methods."""
1272
1198
 
 
1199
    scenarios = multiply_scenarios(
 
1200
        vary_by_http_client_implementation(), 
 
1201
        vary_by_http_protocol_version(),
 
1202
        )
 
1203
 
1273
1204
    def setUp(self):
1274
1205
        http_utils.TestCaseWithWebserver.setUp(self)
1275
1206
        self.build_tree_contents([('a', '0123456789')],)
1276
 
        server = self.get_readonly_server()
1277
 
        self.transport = self._transport(server.get_url())
1278
1207
 
1279
1208
    def create_transport_readonly_server(self):
1280
1209
        return http_server.HttpServer(protocol_version=self._protocol_version)
1281
1210
 
1282
1211
    def _file_contents(self, relpath, ranges):
 
1212
        t = self.get_readonly_transport()
1283
1213
        offsets = [ (start, end - start + 1) for start, end in ranges]
1284
 
        coalesce = self.transport._coalesce_offsets
 
1214
        coalesce = t._coalesce_offsets
1285
1215
        coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
1286
 
        code, data = self.transport._get(relpath, coalesced)
 
1216
        code, data = t._get(relpath, coalesced)
1287
1217
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1288
1218
        for start, end in ranges:
1289
1219
            data.seek(start)
1290
1220
            yield data.read(end - start + 1)
1291
1221
 
1292
1222
    def _file_tail(self, relpath, tail_amount):
1293
 
        code, data = self.transport._get(relpath, [], tail_amount)
 
1223
        t = self.get_readonly_transport()
 
1224
        code, data = t._get(relpath, [], tail_amount)
1294
1225
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1295
1226
        data.seek(-tail_amount, 2)
1296
1227
        return data.read(tail_amount)
1315
1246
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1316
1247
    """Test redirection between http servers."""
1317
1248
 
1318
 
    def create_transport_secondary_server(self):
1319
 
        """Create the secondary server redirecting to the primary server"""
1320
 
        new = self.get_readonly_server()
1321
 
 
1322
 
        redirecting = http_utils.HTTPServerRedirecting(
1323
 
            protocol_version=self._protocol_version)
1324
 
        redirecting.redirect_to(new.host, new.port)
1325
 
        return redirecting
 
1249
    scenarios = multiply_scenarios(
 
1250
        vary_by_http_client_implementation(), 
 
1251
        vary_by_http_protocol_version(),
 
1252
        )
1326
1253
 
1327
1254
    def setUp(self):
1328
1255
        super(TestHTTPRedirections, self).setUp()
1330
1257
                                  ('bundle',
1331
1258
                                  '# Bazaar revision bundle v0.9\n#\n')
1332
1259
                                  ],)
1333
 
        # The requests to the old server will be redirected to the new server
1334
 
        self.old_transport = self._transport(self.old_server.get_url())
1335
1260
 
1336
1261
    def test_redirected(self):
1337
 
        self.assertRaises(errors.RedirectRequested, self.old_transport.get, 'a')
1338
 
        t = self._transport(self.new_server.get_url())
1339
 
        self.assertEqual('0123456789', t.get('a').read())
1340
 
 
1341
 
    def test_read_redirected_bundle_from_url(self):
1342
 
        from bzrlib.bundle import read_bundle_from_url
1343
 
        url = self.old_transport.abspath('bundle')
1344
 
        bundle = self.applyDeprecated(deprecated_in((1, 12, 0)),
1345
 
                read_bundle_from_url, url)
1346
 
        # If read_bundle_from_url was successful we get an empty bundle
1347
 
        self.assertEqual([], bundle.revisions)
 
1262
        self.assertRaises(errors.RedirectRequested,
 
1263
                          self.get_old_transport().get, 'a')
 
1264
        self.assertEqual('0123456789', self.get_new_transport().get('a').read())
1348
1265
 
1349
1266
 
1350
1267
class RedirectedRequest(_urllib2_wrappers.Request):
1364
1281
 
1365
1282
 
1366
1283
def install_redirected_request(test):
1367
 
    test.original_class = _urllib2_wrappers.Request
1368
 
    def restore():
1369
 
        _urllib2_wrappers.Request = test.original_class
1370
 
    _urllib2_wrappers.Request = RedirectedRequest
1371
 
    test.addCleanup(restore)
 
1284
    test.overrideAttr(_urllib2_wrappers, 'Request', RedirectedRequest)
 
1285
 
 
1286
 
 
1287
def cleanup_http_redirection_connections(test):
 
1288
    # Some sockets are opened but never seen by _urllib, so we trap them at
 
1289
    # the _urllib2_wrappers level to be able to clean them up.
 
1290
    def socket_disconnect(sock):
 
1291
        try:
 
1292
            sock.shutdown(socket.SHUT_RDWR)
 
1293
            sock.close()
 
1294
        except socket.error:
 
1295
            pass
 
1296
    def connect(connection):
 
1297
        test.http_connect_orig(connection)
 
1298
        test.addCleanup(socket_disconnect, connection.sock)
 
1299
    test.http_connect_orig = test.overrideAttr(
 
1300
        _urllib2_wrappers.HTTPConnection, 'connect', connect)
 
1301
    def connect(connection):
 
1302
        test.https_connect_orig(connection)
 
1303
        test.addCleanup(socket_disconnect, connection.sock)
 
1304
    test.https_connect_orig = test.overrideAttr(
 
1305
        _urllib2_wrappers.HTTPSConnection, 'connect', connect)
1372
1306
 
1373
1307
 
1374
1308
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1385
1319
    -- vila 20070212
1386
1320
    """
1387
1321
 
 
1322
    scenarios = multiply_scenarios(
 
1323
        vary_by_http_client_implementation(), 
 
1324
        vary_by_http_protocol_version(),
 
1325
        )
 
1326
 
1388
1327
    def setUp(self):
1389
1328
        if (features.pycurl.available()
1390
1329
            and self._transport == PyCurlTransport):
1391
1330
            raise tests.TestNotApplicable(
1392
 
                "pycurl doesn't redirect silently annymore")
 
1331
                "pycurl doesn't redirect silently anymore")
1393
1332
        super(TestHTTPSilentRedirections, self).setUp()
1394
1333
        install_redirected_request(self)
 
1334
        cleanup_http_redirection_connections(self)
1395
1335
        self.build_tree_contents([('a','a'),
1396
1336
                                  ('1/',),
1397
1337
                                  ('1/a', 'redirected once'),
1405
1345
                                  ('5/a', 'redirected 5 times'),
1406
1346
                                  ],)
1407
1347
 
1408
 
        self.old_transport = self._transport(self.old_server.get_url())
1409
 
 
1410
 
    def create_transport_secondary_server(self):
1411
 
        """Create the secondary server, redirections are defined in the tests"""
1412
 
        return http_utils.HTTPServerRedirecting(
1413
 
            protocol_version=self._protocol_version)
1414
 
 
1415
1348
    def test_one_redirection(self):
1416
 
        t = self.old_transport
1417
 
 
1418
 
        req = RedirectedRequest('GET', t.abspath('a'))
 
1349
        t = self.get_old_transport()
 
1350
        req = RedirectedRequest('GET', t._remote_path('a'))
1419
1351
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1420
1352
                                       self.new_server.port)
1421
1353
        self.old_server.redirections = \
1422
1354
            [('(.*)', r'%s/1\1' % (new_prefix), 301),]
1423
 
        self.assertEqual('redirected once',t._perform(req).read())
 
1355
        self.assertEqual('redirected once', t._perform(req).read())
1424
1356
 
1425
1357
    def test_five_redirections(self):
1426
 
        t = self.old_transport
1427
 
 
1428
 
        req = RedirectedRequest('GET', t.abspath('a'))
 
1358
        t = self.get_old_transport()
 
1359
        req = RedirectedRequest('GET', t._remote_path('a'))
1429
1360
        old_prefix = 'http://%s:%s' % (self.old_server.host,
1430
1361
                                       self.old_server.port)
1431
1362
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1437
1368
            ('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1438
1369
            ('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1439
1370
            ]
1440
 
        self.assertEqual('redirected 5 times',t._perform(req).read())
 
1371
        self.assertEqual('redirected 5 times', t._perform(req).read())
1441
1372
 
1442
1373
 
1443
1374
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1444
1375
    """Test transport.do_catching_redirections."""
1445
1376
 
 
1377
    scenarios = multiply_scenarios(
 
1378
        vary_by_http_client_implementation(), 
 
1379
        vary_by_http_protocol_version(),
 
1380
        )
 
1381
 
1446
1382
    def setUp(self):
1447
1383
        super(TestDoCatchRedirections, self).setUp()
1448
1384
        self.build_tree_contents([('a', '0123456789'),],)
1449
 
 
1450
 
        self.old_transport = self._transport(self.old_server.get_url())
1451
 
 
1452
 
    def get_a(self, transport):
1453
 
        return transport.get('a')
 
1385
        cleanup_http_redirection_connections(self)
 
1386
 
 
1387
        self.old_transport = self.get_old_transport()
 
1388
 
 
1389
    def get_a(self, t):
 
1390
        return t.get('a')
1454
1391
 
1455
1392
    def test_no_redirection(self):
1456
 
        t = self._transport(self.new_server.get_url())
 
1393
        t = self.get_new_transport()
1457
1394
 
1458
1395
        # We use None for redirected so that we fail if redirected
1459
1396
        self.assertEqual('0123456789',
1463
1400
    def test_one_redirection(self):
1464
1401
        self.redirections = 0
1465
1402
 
1466
 
        def redirected(transport, exception, redirection_notice):
 
1403
        def redirected(t, exception, redirection_notice):
1467
1404
            self.redirections += 1
1468
 
            dir, file = urlutils.split(exception.target)
1469
 
            return self._transport(dir)
 
1405
            redirected_t = t._redirected_to(exception.source, exception.target)
 
1406
            return redirected_t
1470
1407
 
1471
1408
        self.assertEqual('0123456789',
1472
1409
                         transport.do_catching_redirections(
1489
1426
class TestAuth(http_utils.TestCaseWithWebserver):
1490
1427
    """Test authentication scheme"""
1491
1428
 
 
1429
    scenarios = multiply_scenarios(
 
1430
        vary_by_http_client_implementation(),
 
1431
        vary_by_http_protocol_version(),
 
1432
        vary_by_http_auth_scheme(),
 
1433
        )
 
1434
 
1492
1435
    _auth_header = 'Authorization'
1493
1436
    _password_prompt_prefix = ''
1494
1437
    _username_prompt_prefix = ''
1502
1445
                                  ('b', 'contents of b\n'),])
1503
1446
 
1504
1447
    def create_transport_readonly_server(self):
1505
 
        return self._auth_server(protocol_version=self._protocol_version)
 
1448
        server = self._auth_server(protocol_version=self._protocol_version)
 
1449
        server._url_protocol = self._url_protocol
 
1450
        return server
1506
1451
 
1507
1452
    def _testing_pycurl(self):
1508
1453
        # TODO: This is duplicated for lots of the classes in this file
1521
1466
        return url
1522
1467
 
1523
1468
    def get_user_transport(self, user, password):
1524
 
        return self._transport(self.get_user_url(user, password))
 
1469
        t = transport.get_transport(self.get_user_url(user, password))
 
1470
        return t
1525
1471
 
1526
1472
    def test_no_user(self):
1527
1473
        self.server.add_user('joe', 'foo')
1638
1584
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1639
1585
                                            stderr=tests.StringIOWrapper())
1640
1586
        # Create a minimal config file with the right password
1641
 
        conf = config.AuthenticationConfig()
1642
 
        conf._get_config().update(
1643
 
            {'httptest': {'scheme': 'http', 'port': self.server.port,
1644
 
                          'user': user, 'password': password}})
1645
 
        conf._save()
 
1587
        _setup_authentication_config(
 
1588
            scheme='http', 
 
1589
            port=self.server.port,
 
1590
            user=user,
 
1591
            password=password)
1646
1592
        # Issue a request to the server to connect
1647
1593
        self.assertEqual('contents of a\n',t.get('a').read())
1648
1594
        # stdin should have  been left untouched
1650
1596
        # Only one 'Authentication Required' error should occur
1651
1597
        self.assertEqual(1, self.server.auth_required_errors)
1652
1598
 
1653
 
    def test_user_from_auth_conf(self):
1654
 
        if self._testing_pycurl():
1655
 
            raise tests.TestNotApplicable(
1656
 
                'pycurl does not support authentication.conf')
1657
 
        user = 'joe'
1658
 
        password = 'foo'
1659
 
        self.server.add_user(user, password)
1660
 
        # Create a minimal config file with the right password
1661
 
        conf = config.AuthenticationConfig()
1662
 
        conf._get_config().update(
1663
 
            {'httptest': {'scheme': 'http', 'port': self.server.port,
1664
 
                          'user': user, 'password': password}})
1665
 
        conf._save()
1666
 
        t = self.get_user_transport(None, None)
1667
 
        # Issue a request to the server to connect
1668
 
        self.assertEqual('contents of a\n', t.get('a').read())
1669
 
        # Only one 'Authentication Required' error should occur
1670
 
        self.assertEqual(1, self.server.auth_required_errors)
1671
 
 
1672
1599
    def test_changing_nonce(self):
1673
1600
        if self._auth_server not in (http_utils.HTTPDigestAuthServer,
1674
1601
                                     http_utils.ProxyDigestAuthServer):
1690
1617
        # initial 'who are you' and a second 'who are you' with the new nonce)
1691
1618
        self.assertEqual(2, self.server.auth_required_errors)
1692
1619
 
 
1620
    def test_user_from_auth_conf(self):
 
1621
        if self._testing_pycurl():
 
1622
            raise tests.TestNotApplicable(
 
1623
                'pycurl does not support authentication.conf')
 
1624
        user = 'joe'
 
1625
        password = 'foo'
 
1626
        self.server.add_user(user, password)
 
1627
        _setup_authentication_config(
 
1628
            scheme='http', 
 
1629
            port=self.server.port,
 
1630
            user=user,
 
1631
            password=password)
 
1632
        t = self.get_user_transport(None, None)
 
1633
        # Issue a request to the server to connect
 
1634
        self.assertEqual('contents of a\n', t.get('a').read())
 
1635
        # Only one 'Authentication Required' error should occur
 
1636
        self.assertEqual(1, self.server.auth_required_errors)
 
1637
 
 
1638
 
 
1639
def _setup_authentication_config(**kwargs):
 
1640
    conf = config.AuthenticationConfig()
 
1641
    conf._get_config().update({'httptest': kwargs})
 
1642
    conf._save()
 
1643
 
 
1644
 
 
1645
 
 
1646
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
 
1647
    """Unit tests for glue by which urllib2 asks us for authentication"""
 
1648
 
 
1649
    def test_get_user_password_without_port(self):
 
1650
        """We cope if urllib2 doesn't tell us the port.
 
1651
 
 
1652
        See https://bugs.launchpad.net/bzr/+bug/654684
 
1653
        """
 
1654
        user = 'joe'
 
1655
        password = 'foo'
 
1656
        _setup_authentication_config(
 
1657
            scheme='http', 
 
1658
            host='localhost',
 
1659
            user=user,
 
1660
            password=password)
 
1661
        handler = _urllib2_wrappers.HTTPAuthHandler()
 
1662
        got_pass = handler.get_user_password(dict(
 
1663
            user='joe',
 
1664
            protocol='http',
 
1665
            host='localhost',
 
1666
            path='/',
 
1667
            realm='Realm',
 
1668
            ))
 
1669
        self.assertEquals((user, password), got_pass)
1693
1670
 
1694
1671
 
1695
1672
class TestProxyAuth(TestAuth):
1696
1673
    """Test proxy authentication schemes."""
1697
1674
 
 
1675
    scenarios = multiply_scenarios(
 
1676
        vary_by_http_client_implementation(),
 
1677
        vary_by_http_protocol_version(),
 
1678
        vary_by_http_proxy_auth_scheme(),
 
1679
        )
 
1680
 
1698
1681
    _auth_header = 'Proxy-authorization'
1699
1682
    _password_prompt_prefix = 'Proxy '
1700
1683
    _username_prompt_prefix = 'Proxy '
1701
1684
 
1702
1685
    def setUp(self):
1703
1686
        super(TestProxyAuth, self).setUp()
1704
 
        self._old_env = {}
1705
 
        self.addCleanup(self._restore_env)
1706
1687
        # Override the contents to avoid false positives
1707
1688
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1708
1689
                                  ('b', 'not proxied contents of b\n'),
1711
1692
                                  ])
1712
1693
 
1713
1694
    def get_user_transport(self, user, password):
1714
 
        self._install_env({'all_proxy': self.get_user_url(user, password)})
1715
 
        return self._transport(self.server.get_url())
1716
 
 
1717
 
    def _install_env(self, env):
1718
 
        for name, value in env.iteritems():
1719
 
            self._old_env[name] = osutils.set_or_unset_env(name, value)
1720
 
 
1721
 
    def _restore_env(self):
1722
 
        for name, value in self._old_env.iteritems():
1723
 
            osutils.set_or_unset_env(name, value)
 
1695
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
 
1696
        return TestAuth.get_user_transport(self, user, password)
1724
1697
 
1725
1698
    def test_empty_pass(self):
1726
1699
        if self._testing_pycurl():
1745
1718
        self.readfile = StringIO(socket_read_content)
1746
1719
        self.writefile = StringIO()
1747
1720
        self.writefile.close = lambda: None
 
1721
        self.close = lambda: None
1748
1722
 
1749
1723
    def makefile(self, mode='r', bufsize=None):
1750
1724
        if 'r' in mode:
1755
1729
 
1756
1730
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1757
1731
 
 
1732
    scenarios = multiply_scenarios(
 
1733
        vary_by_http_client_implementation(), 
 
1734
        vary_by_http_protocol_version(),
 
1735
        )
 
1736
 
1758
1737
    def setUp(self):
1759
1738
        super(SmartHTTPTunnellingTest, self).setUp()
1760
1739
        # We use the VFS layer as part of HTTP tunnelling tests.
1761
 
        self._captureVar('BZR_NO_SMART_VFS', None)
 
1740
        self.overrideEnv('BZR_NO_SMART_VFS', None)
1762
1741
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
 
1742
        self.http_server = self.get_readonly_server()
1763
1743
 
1764
1744
    def create_transport_readonly_server(self):
1765
 
        return http_utils.HTTPServerWithSmarts(
 
1745
        server = http_utils.HTTPServerWithSmarts(
1766
1746
            protocol_version=self._protocol_version)
 
1747
        server._url_protocol = self._url_protocol
 
1748
        return server
1767
1749
 
1768
1750
    def test_open_bzrdir(self):
1769
1751
        branch = self.make_branch('relpath')
1770
 
        http_server = self.get_readonly_server()
1771
 
        url = http_server.get_url() + 'relpath'
 
1752
        url = self.http_server.get_url() + 'relpath'
1772
1753
        bd = bzrdir.BzrDir.open(url)
 
1754
        self.addCleanup(bd.transport.disconnect)
1773
1755
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1774
1756
 
1775
1757
    def test_bulk_data(self):
1777
1759
        # The 'readv' command in the smart protocol both sends and receives
1778
1760
        # bulk data, so we use that.
1779
1761
        self.build_tree(['data-file'])
1780
 
        http_server = self.get_readonly_server()
1781
 
        http_transport = self._transport(http_server.get_url())
 
1762
        http_transport = transport.get_transport(self.http_server.get_url())
1782
1763
        medium = http_transport.get_smart_medium()
1783
1764
        # Since we provide the medium, the url below will be mostly ignored
1784
1765
        # during the test, as long as the path is '/'.
1792
1773
        post_body = 'hello\n'
1793
1774
        expected_reply_body = 'ok\x012\n'
1794
1775
 
1795
 
        http_server = self.get_readonly_server()
1796
 
        http_transport = self._transport(http_server.get_url())
 
1776
        http_transport = transport.get_transport(self.http_server.get_url())
1797
1777
        medium = http_transport.get_smart_medium()
1798
1778
        response = medium.send_http_smart_request(post_body)
1799
1779
        reply_body = response.read()
1800
1780
        self.assertEqual(expected_reply_body, reply_body)
1801
1781
 
1802
1782
    def test_smart_http_server_post_request_handler(self):
1803
 
        httpd = self.get_readonly_server()._get_httpd()
 
1783
        httpd = self.http_server.server
1804
1784
 
1805
1785
        socket = SampleSocket(
1806
1786
            'POST /.bzr/smart %s \r\n' % self._protocol_version
1838
1818
 
1839
1819
    def test_probe_smart_server(self):
1840
1820
        """Test error handling against server refusing smart requests."""
1841
 
        server = self.get_readonly_server()
1842
 
        t = self._transport(server.get_url())
 
1821
        t = self.get_readonly_transport()
1843
1822
        # No need to build a valid smart request here, the server will not even
1844
1823
        # try to interpret it.
1845
1824
        self.assertRaises(errors.SmartProtocolError,
1846
1825
                          t.get_smart_medium().send_http_smart_request,
1847
1826
                          'whatever')
1848
1827
 
 
1828
 
1849
1829
class Test_redirected_to(tests.TestCase):
1850
1830
 
 
1831
    scenarios = vary_by_http_client_implementation()
 
1832
 
1851
1833
    def test_redirected_to_subdir(self):
1852
1834
        t = self._transport('http://www.example.com/foo')
1853
1835
        r = t._redirected_to('http://www.example.com/foo',
1904
1886
    line.
1905
1887
    """
1906
1888
 
1907
 
    def handle_one_request(self):
 
1889
    def _handle_one_request(self):
1908
1890
        tcs = self.server.test_case_server
1909
1891
        requestline = self.rfile.readline()
1910
1892
        headers = self.MessageClass(self.rfile, 0)
1974
1956
        # We override at class level because constructors may propagate the
1975
1957
        # bound method and render instance overriding ineffective (an
1976
1958
        # alternative would be to define a specific ui factory instead...)
1977
 
        self.orig_report_activity = self._transport._report_activity
1978
 
        self._transport._report_activity = report_activity
1979
 
 
1980
 
    def tearDown(self):
1981
 
        self._transport._report_activity = self.orig_report_activity
1982
 
        self.server.stop_server()
1983
 
        tests.TestCase.tearDown(self)
 
1959
        self.overrideAttr(self._transport, '_report_activity', report_activity)
 
1960
        self.addCleanup(self.server.stop_server)
1984
1961
 
1985
1962
    def get_transport(self):
1986
 
        return self._transport(self.server.get_url())
 
1963
        t = self._transport(self.server.get_url())
 
1964
        # FIXME: Needs cleanup -- vila 20100611
 
1965
        return t
1987
1966
 
1988
1967
    def assertActivitiesMatch(self):
1989
1968
        self.assertEqual(self.server.bytes_read,
2094
2073
'''
2095
2074
        t = self.get_transport()
2096
2075
        # We must send a single line of body bytes, see
2097
 
        # PredefinedRequestHandler.handle_one_request
 
2076
        # PredefinedRequestHandler._handle_one_request
2098
2077
        code, f = t._post('abc def end-of-body\n')
2099
2078
        self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2100
2079
        self.assertActivitiesMatch()
2102
2081
 
2103
2082
class TestActivity(tests.TestCase, TestActivityMixin):
2104
2083
 
 
2084
    scenarios = multiply_scenarios(
 
2085
        vary_by_http_activity(),
 
2086
        vary_by_http_protocol_version(),
 
2087
        )
 
2088
 
2105
2089
    def setUp(self):
2106
 
        tests.TestCase.setUp(self)
2107
 
        self.server = self._activity_server(self._protocol_version)
2108
 
        self.server.start_server()
2109
 
        self.activities = {}
2110
 
        def report_activity(t, bytes, direction):
2111
 
            count = self.activities.get(direction, 0)
2112
 
            count += bytes
2113
 
            self.activities[direction] = count
2114
 
 
2115
 
        # We override at class level because constructors may propagate the
2116
 
        # bound method and render instance overriding ineffective (an
2117
 
        # alternative would be to define a specific ui factory instead...)
2118
 
        self.orig_report_activity = self._transport._report_activity
2119
 
        self._transport._report_activity = report_activity
2120
 
 
2121
 
    def tearDown(self):
2122
 
        self._transport._report_activity = self.orig_report_activity
2123
 
        self.server.stop_server()
2124
 
        tests.TestCase.tearDown(self)
 
2090
        TestActivityMixin.setUp(self)
2125
2091
 
2126
2092
 
2127
2093
class TestNoReportActivity(tests.TestCase, TestActivityMixin):
2128
2094
 
 
2095
    # Unlike TestActivity, we are really testing ReportingFileSocket and
 
2096
    # ReportingSocket, so we don't need all the parametrization. Since
 
2097
    # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
 
2098
    # test them through their use by the transport than directly (that's a
 
2099
    # bit less clean but far more simpler and effective).
 
2100
    _activity_server = ActivityHTTPServer
 
2101
    _protocol_version = 'HTTP/1.1'
 
2102
 
2129
2103
    def setUp(self):
2130
 
        tests.TestCase.setUp(self)
2131
 
        # Unlike TestActivity, we are really testing ReportingFileSocket and
2132
 
        # ReportingSocket, so we don't need all the parametrization. Since
2133
 
        # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
2134
 
        # test them through their use by the transport than directly (that's a
2135
 
        # bit less clean but far more simpler and effective).
2136
 
        self.server = ActivityHTTPServer('HTTP/1.1')
2137
 
        self._transport=_urllib.HttpTransport_urllib
2138
 
 
2139
 
        self.server.start_server()
2140
 
 
2141
 
        # We override at class level because constructors may propagate the
2142
 
        # bound method and render instance overriding ineffective (an
2143
 
        # alternative would be to define a specific ui factory instead...)
2144
 
        self.orig_report_activity = self._transport._report_activity
2145
 
        self._transport._report_activity = None
2146
 
 
2147
 
    def tearDown(self):
2148
 
        self._transport._report_activity = self.orig_report_activity
2149
 
        self.server.stop_server()
2150
 
        tests.TestCase.tearDown(self)
 
2104
        self._transport =_urllib.HttpTransport_urllib
 
2105
        TestActivityMixin.setUp(self)
2151
2106
 
2152
2107
    def assertActivitiesMatch(self):
2153
2108
        # Nothing to check here
2157
2112
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2158
2113
    """Test authentication on the redirected http server."""
2159
2114
 
 
2115
    scenarios = vary_by_http_protocol_version()
 
2116
 
2160
2117
    _auth_header = 'Authorization'
2161
2118
    _password_prompt_prefix = ''
2162
2119
    _username_prompt_prefix = ''
2163
2120
    _auth_server = http_utils.HTTPBasicAuthServer
2164
2121
    _transport = _urllib.HttpTransport_urllib
2165
2122
 
2166
 
    def create_transport_readonly_server(self):
2167
 
        return self._auth_server()
2168
 
 
2169
 
    def create_transport_secondary_server(self):
2170
 
        """Create the secondary server redirecting to the primary server"""
2171
 
        new = self.get_readonly_server()
2172
 
 
2173
 
        redirecting = http_utils.HTTPServerRedirecting()
2174
 
        redirecting.redirect_to(new.host, new.port)
2175
 
        return redirecting
2176
 
 
2177
2123
    def setUp(self):
2178
2124
        super(TestAuthOnRedirected, self).setUp()
2179
2125
        self.build_tree_contents([('a','a'),
2184
2130
                                       self.new_server.port)
2185
2131
        self.old_server.redirections = [
2186
2132
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2187
 
        self.old_transport = self._transport(self.old_server.get_url())
 
2133
        self.old_transport = self.get_old_transport()
2188
2134
        self.new_server.add_user('joe', 'foo')
2189
 
 
2190
 
    def get_a(self, transport):
2191
 
        return transport.get('a')
 
2135
        cleanup_http_redirection_connections(self)
 
2136
 
 
2137
    def create_transport_readonly_server(self):
 
2138
        server = self._auth_server(protocol_version=self._protocol_version)
 
2139
        server._url_protocol = self._url_protocol
 
2140
        return server
 
2141
 
 
2142
    def get_a(self, t):
 
2143
        return t.get('a')
2192
2144
 
2193
2145
    def test_auth_on_redirected_via_do_catching_redirections(self):
2194
2146
        self.redirections = 0
2195
2147
 
2196
 
        def redirected(transport, exception, redirection_notice):
 
2148
        def redirected(t, exception, redirection_notice):
2197
2149
            self.redirections += 1
2198
 
            dir, file = urlutils.split(exception.target)
2199
 
            return self._transport(dir)
 
2150
            redirected_t = t._redirected_to(exception.source, exception.target)
 
2151
            self.addCleanup(redirected_t.disconnect)
 
2152
            return redirected_t
2200
2153
 
2201
2154
        stdout = tests.StringIOWrapper()
2202
2155
        stderr = tests.StringIOWrapper()
2223
2176
                                       self.new_server.port)
2224
2177
        self.old_server.redirections = [
2225
2178
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2226
 
        self.assertEqual('redirected once',t._perform(req).read())
 
2179
        self.assertEqual('redirected once', t._perform(req).read())
2227
2180
        # stdin should be empty
2228
2181
        self.assertEqual('', ui.ui_factory.stdin.readline())
2229
2182
        # stdout should be empty, stderr will contains the prompts