~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Martin Pool
  • Date: 2010-02-25 06:17:27 UTC
  • mfrom: (5055 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5057.
  • Revision ID: mbp@sourcefrog.net-20100225061727-4sd9lt0qmdc6087t
merge news

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
32
35
import bzrlib
33
36
from bzrlib import (
34
37
    bzrdir,
35
 
    cethread,
36
38
    config,
37
 
    debug,
38
39
    errors,
39
40
    osutils,
40
41
    remote as _mod_remote,
41
42
    tests,
42
 
    trace,
43
43
    transport,
44
44
    ui,
 
45
    urlutils,
 
46
    )
 
47
from bzrlib.symbol_versioning import (
 
48
    deprecated_in,
45
49
    )
46
50
from bzrlib.tests import (
47
51
    features,
48
52
    http_server,
49
53
    http_utils,
50
 
    test_server,
51
 
    )
52
 
from bzrlib.tests.scenarios import (
53
 
    load_tests_apply_scenarios,
54
 
    multiply_scenarios,
55
54
    )
56
55
from bzrlib.transport import (
57
56
    http,
67
66
    from bzrlib.transport.http._pycurl import PyCurlTransport
68
67
 
69
68
 
70
 
load_tests = load_tests_apply_scenarios
71
 
 
72
 
 
73
 
def vary_by_http_client_implementation():
74
 
    """Test the two libraries we can use, pycurl and urllib."""
 
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
                )))
75
80
    transport_scenarios = [
76
81
        ('urllib', dict(_transport=_urllib.HttpTransport_urllib,
77
82
                        _server=http_server.HttpServer_urllib,
78
 
                        _url_protocol='http+urllib',)),
 
83
                        _qualified_prefix='http+urllib',)),
79
84
        ]
80
85
    if features.pycurl.available():
81
86
        transport_scenarios.append(
82
87
            ('pycurl', dict(_transport=PyCurlTransport,
83
88
                            _server=http_server.HttpServer_PyCurl,
84
 
                            _url_protocol='http+pycurl',)))
85
 
    return transport_scenarios
86
 
 
87
 
 
88
 
def vary_by_http_protocol_version():
89
 
    """Test on http/1.0 and 1.1"""
90
 
    return [
91
 
        ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
92
 
        ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
 
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 = [
 
120
        ('basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
 
121
        ('digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
 
122
        ('basicdigest',
 
123
         dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
93
124
        ]
94
 
 
95
 
 
96
 
def vary_by_http_auth_scheme():
97
 
    scenarios = [
 
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 = [
98
135
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
99
136
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
100
137
        ('basicdigest',
101
 
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
102
 
        ]
103
 
    # Add some attributes common to all scenarios
104
 
    for scenario_id, scenario_dict in scenarios:
105
 
        scenario_dict.update(_auth_header='Authorization',
106
 
                             _username_prompt_prefix='',
107
 
                             _password_prompt_prefix='')
108
 
    return scenarios
109
 
 
110
 
 
111
 
def vary_by_http_proxy_auth_scheme():
112
 
    scenarios = [
113
 
        ('proxy-basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
114
 
        ('proxy-digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
115
 
        ('proxy-basicdigest',
116
 
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
117
 
        ]
118
 
    # Add some attributes common to all scenarios
119
 
    for scenario_id, scenario_dict in scenarios:
120
 
        scenario_dict.update(_auth_header='Proxy-Authorization',
121
 
                             _username_prompt_prefix='Proxy ',
122
 
                             _password_prompt_prefix='Proxy ')
123
 
    return scenarios
124
 
 
125
 
 
126
 
def vary_by_http_activity():
 
138
         dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
 
139
        ]
 
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
                )))
127
149
    activity_scenarios = [
128
150
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
129
 
                            _transport=_urllib.HttpTransport_urllib,)),
 
151
                             _transport=_urllib.HttpTransport_urllib,)),
130
152
        ]
131
 
    if features.HTTPSServerFeature.available():
 
153
    if tests.HTTPSServerFeature.available():
132
154
        activity_scenarios.append(
133
155
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
134
 
                                _transport=_urllib.HttpTransport_urllib,)),)
 
156
                                  _transport=_urllib.HttpTransport_urllib,)),)
135
157
    if features.pycurl.available():
136
158
        activity_scenarios.append(
137
159
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
138
 
                                _transport=PyCurlTransport,)),)
139
 
        if features.HTTPSServerFeature.available():
 
160
                                 _transport=PyCurlTransport,)),)
 
161
        if tests.HTTPSServerFeature.available():
140
162
            from bzrlib.tests import (
141
163
                ssl_certs,
142
164
                )
153
175
 
154
176
            activity_scenarios.append(
155
177
                ('pycurl,https', dict(_activity_server=ActivityHTTPSServer,
156
 
                                    _transport=HTTPS_pycurl_transport,)),)
157
 
    return activity_scenarios
 
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
158
188
 
159
189
 
160
190
class FakeManager(object):
193
223
        self._sock.bind(('127.0.0.1', 0))
194
224
        self.host, self.port = self._sock.getsockname()
195
225
        self._ready = threading.Event()
196
 
        self._thread = test_server.TestThread(
197
 
            sync_event=self._ready, target=self._accept_read_and_reply)
 
226
        self._thread = threading.Thread(target=self._accept_read_and_reply)
 
227
        self._thread.setDaemon(True)
198
228
        self._thread.start()
199
 
        if 'threads' in tests.selftest_debug_flags:
200
 
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
201
 
        self._ready.wait()
 
229
        self._ready.wait(5)
202
230
 
203
231
    def _accept_read_and_reply(self):
204
232
        self._sock.listen(1)
205
233
        self._ready.set()
206
 
        conn, address = self._sock.accept()
207
 
        if self._expect_body_tail is not None:
 
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)
208
240
            while not self.received_bytes.endswith(self._expect_body_tail):
209
241
                self.received_bytes += conn.recv(4096)
210
242
            conn.sendall('HTTP/1.1 200 OK\r\n')
211
 
        try:
 
243
        except socket.timeout:
 
244
            # Make sure the client isn't stuck waiting for us to e.g. accept.
212
245
            self._sock.close()
213
246
        except socket.error:
214
247
            # The client may have already closed the socket.
216
249
 
217
250
    def stop_server(self):
218
251
        try:
219
 
            # Issue a fake connection to wake up the server and allow it to
220
 
            # finish quickly
221
 
            fake_conn = osutils.connect_socket((self.host, self.port))
222
 
            fake_conn.close()
 
252
            self._sock.close()
223
253
        except socket.error:
224
254
            # We might have already closed it.  We don't care.
225
255
            pass
226
256
        self.host = None
227
257
        self.port = None
228
 
        self._thread.join()
229
 
        if 'threads' in tests.selftest_debug_flags:
230
 
            sys.stderr.write('Thread  joined: %s\n' % (self._thread.ident,))
231
258
 
232
259
 
233
260
class TestAuthHeader(tests.TestCase):
269
296
        self.assertEqual('realm="Thou should not pass"', remainder)
270
297
 
271
298
 
272
 
class TestHTTPRangeParsing(tests.TestCase):
273
 
 
274
 
    def setUp(self):
275
 
        super(TestHTTPRangeParsing, self).setUp()
276
 
        # We focus on range  parsing here and ignore everything else
277
 
        class RequestHandler(http_server.TestingHTTPRequestHandler):
278
 
            def setup(self): pass
279
 
            def handle(self): pass
280
 
            def finish(self): pass
281
 
 
282
 
        self.req_handler = RequestHandler(None, None, None)
283
 
 
284
 
    def assertRanges(self, ranges, header, file_size):
285
 
        self.assertEquals(ranges,
286
 
                          self.req_handler._parse_ranges(header, file_size))
287
 
 
288
 
    def test_simple_range(self):
289
 
        self.assertRanges([(0,2)], 'bytes=0-2', 12)
290
 
 
291
 
    def test_tail(self):
292
 
        self.assertRanges([(8, 11)], 'bytes=-4', 12)
293
 
 
294
 
    def test_tail_bigger_than_file(self):
295
 
        self.assertRanges([(0, 11)], 'bytes=-99', 12)
296
 
 
297
 
    def test_range_without_end(self):
298
 
        self.assertRanges([(4, 11)], 'bytes=4-', 12)
299
 
 
300
 
    def test_invalid_ranges(self):
301
 
        self.assertRanges(None, 'bytes=12-22', 12)
302
 
        self.assertRanges(None, 'bytes=1-3,12-22', 12)
303
 
        self.assertRanges(None, 'bytes=-', 12)
304
 
 
305
 
 
306
299
class TestHTTPServer(tests.TestCase):
307
300
    """Test the HTTP servers implementations."""
308
301
 
311
304
 
312
305
            protocol_version = 'HTTP/0.1'
313
306
 
314
 
        self.assertRaises(httplib.UnknownProtocol,
315
 
                          http_server.HttpServer, BogusRequestHandler)
 
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')
316
313
 
317
314
    def test_force_invalid_protocol(self):
318
 
        self.assertRaises(httplib.UnknownProtocol,
319
 
                          http_server.HttpServer, protocol_version='HTTP/0.1')
 
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')
320
321
 
321
322
    def test_server_start_and_stop(self):
322
323
        server = http_server.HttpServer()
323
 
        self.addCleanup(server.stop_server)
324
324
        server.start_server()
325
 
        self.assertTrue(server.server is not None)
326
 
        self.assertTrue(server.server.serving is not None)
327
 
        self.assertTrue(server.server.serving)
 
325
        try:
 
326
            self.assertTrue(server._http_running)
 
327
        finally:
 
328
            server.stop_server()
 
329
        self.assertFalse(server._http_running)
328
330
 
329
331
    def test_create_http_server_one_zero(self):
330
332
        class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
333
335
 
334
336
        server = http_server.HttpServer(RequestHandlerOneZero)
335
337
        self.start_server(server)
336
 
        self.assertIsInstance(server.server, http_server.TestingHTTPServer)
 
338
        self.assertIsInstance(server._httpd, http_server.TestingHTTPServer)
337
339
 
338
340
    def test_create_http_server_one_one(self):
339
341
        class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
342
344
 
343
345
        server = http_server.HttpServer(RequestHandlerOneOne)
344
346
        self.start_server(server)
345
 
        self.assertIsInstance(server.server,
 
347
        self.assertIsInstance(server._httpd,
346
348
                              http_server.TestingThreadingHTTPServer)
347
349
 
348
350
    def test_create_http_server_force_one_one(self):
353
355
        server = http_server.HttpServer(RequestHandlerOneZero,
354
356
                                        protocol_version='HTTP/1.1')
355
357
        self.start_server(server)
356
 
        self.assertIsInstance(server.server,
 
358
        self.assertIsInstance(server._httpd,
357
359
                              http_server.TestingThreadingHTTPServer)
358
360
 
359
361
    def test_create_http_server_force_one_zero(self):
364
366
        server = http_server.HttpServer(RequestHandlerOneOne,
365
367
                                        protocol_version='HTTP/1.0')
366
368
        self.start_server(server)
367
 
        self.assertIsInstance(server.server,
 
369
        self.assertIsInstance(server._httpd,
368
370
                              http_server.TestingHTTPServer)
369
371
 
370
372
 
399
401
class TestHttpTransportUrls(tests.TestCase):
400
402
    """Test the http urls."""
401
403
 
402
 
    scenarios = vary_by_http_client_implementation()
403
 
 
404
404
    def test_abs_url(self):
405
405
        """Construction of absolute http URLs"""
406
 
        t = self._transport('http://example.com/bzr/bzr.dev/')
 
406
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
407
407
        eq = self.assertEqualDiff
408
 
        eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
409
 
        eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
410
 
        eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
 
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')
411
411
        eq(t.abspath('.bzr/1//2/./3'),
412
 
           'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
 
412
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
413
413
 
414
414
    def test_invalid_http_urls(self):
415
415
        """Trap invalid construction of urls"""
416
 
        self._transport('http://example.com/bzr/bzr.dev/')
 
416
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
417
417
        self.assertRaises(errors.InvalidURL,
418
418
                          self._transport,
419
 
                          'http://http://example.com/bzr/bzr.dev/')
 
419
                          'http://http://bazaar-vcs.org/bzr/bzr.dev/')
420
420
 
421
421
    def test_http_root_urls(self):
422
422
        """Construction of URLs from server root"""
423
 
        t = self._transport('http://example.com/')
 
423
        t = self._transport('http://bzr.ozlabs.org/')
424
424
        eq = self.assertEqualDiff
425
425
        eq(t.abspath('.bzr/tree-version'),
426
 
           'http://example.com/.bzr/tree-version')
 
426
           'http://bzr.ozlabs.org/.bzr/tree-version')
427
427
 
428
428
    def test_http_impl_urls(self):
429
429
        """There are servers which ask for particular clients to connect"""
431
431
        server.start_server()
432
432
        try:
433
433
            url = server.get_url()
434
 
            self.assertTrue(url.startswith('%s://' % self._url_protocol))
 
434
            self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
435
435
        finally:
436
436
            server.stop_server()
437
437
 
475
475
class TestHTTPConnections(http_utils.TestCaseWithWebserver):
476
476
    """Test the http connections."""
477
477
 
478
 
    scenarios = multiply_scenarios(
479
 
        vary_by_http_client_implementation(),
480
 
        vary_by_http_protocol_version(),
481
 
        )
482
 
 
483
478
    def setUp(self):
484
479
        http_utils.TestCaseWithWebserver.setUp(self)
485
480
        self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
487
482
 
488
483
    def test_http_has(self):
489
484
        server = self.get_readonly_server()
490
 
        t = self.get_readonly_transport()
 
485
        t = self._transport(server.get_url())
491
486
        self.assertEqual(t.has('foo/bar'), True)
492
487
        self.assertEqual(len(server.logs), 1)
493
488
        self.assertContainsRe(server.logs[0],
495
490
 
496
491
    def test_http_has_not_found(self):
497
492
        server = self.get_readonly_server()
498
 
        t = self.get_readonly_transport()
 
493
        t = self._transport(server.get_url())
499
494
        self.assertEqual(t.has('not-found'), False)
500
495
        self.assertContainsRe(server.logs[1],
501
496
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
502
497
 
503
498
    def test_http_get(self):
504
499
        server = self.get_readonly_server()
505
 
        t = self.get_readonly_transport()
 
500
        t = self._transport(server.get_url())
506
501
        fp = t.get('foo/bar')
507
502
        self.assertEqualDiff(
508
503
            fp.read(),
530
525
class TestHttpTransportRegistration(tests.TestCase):
531
526
    """Test registrations of various http implementations"""
532
527
 
533
 
    scenarios = vary_by_http_client_implementation()
534
 
 
535
528
    def test_http_registered(self):
536
 
        t = transport.get_transport_from_url(
537
 
            '%s://foo.com/' % self._url_protocol)
 
529
        t = transport.get_transport('%s://foo.com/' % self._qualified_prefix)
538
530
        self.assertIsInstance(t, transport.Transport)
539
531
        self.assertIsInstance(t, self._transport)
540
532
 
541
533
 
542
534
class TestPost(tests.TestCase):
543
535
 
544
 
    scenarios = multiply_scenarios(
545
 
        vary_by_http_client_implementation(),
546
 
        vary_by_http_protocol_version(),
547
 
        )
548
 
 
549
536
    def test_post_body_is_received(self):
550
537
        server = RecordingServer(expect_body_tail='end-of-body',
551
 
                                 scheme=self._url_protocol)
 
538
            scheme=self._qualified_prefix)
552
539
        self.start_server(server)
553
540
        url = server.get_url()
554
 
        # FIXME: needs a cleanup -- vila 20100611
555
 
        http_transport = transport.get_transport_from_url(url)
 
541
        http_transport = self._transport(url)
556
542
        code, response = http_transport._post('abc def end-of-body')
557
543
        self.assertTrue(
558
544
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
559
545
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
560
 
        self.assertTrue('content-type: application/octet-stream\r'
561
 
                        in server.received_bytes.lower())
562
546
        # The transport should not be assuming that the server can accept
563
547
        # chunked encoding the first time it connects, because HTTP/1.1, so we
564
548
        # check for the literal string.
600
584
    Daughter classes are expected to override _req_handler_class
601
585
    """
602
586
 
603
 
    scenarios = multiply_scenarios(
604
 
        vary_by_http_client_implementation(),
605
 
        vary_by_http_protocol_version(),
606
 
        )
607
 
 
608
587
    # Provide a useful default
609
588
    _req_handler_class = http_server.TestingHTTPRequestHandler
610
589
 
611
590
    def create_transport_readonly_server(self):
612
 
        server = http_server.HttpServer(self._req_handler_class,
613
 
                                        protocol_version=self._protocol_version)
614
 
        server._url_protocol = self._url_protocol
615
 
        return server
 
591
        return http_server.HttpServer(self._req_handler_class,
 
592
                                      protocol_version=self._protocol_version)
616
593
 
617
594
    def _testing_pycurl(self):
618
595
        # TODO: This is duplicated for lots of the classes in this file
623
600
class WallRequestHandler(http_server.TestingHTTPRequestHandler):
624
601
    """Whatever request comes in, close the connection"""
625
602
 
626
 
    def _handle_one_request(self):
 
603
    def handle_one_request(self):
627
604
        """Handle a single HTTP request, by abruptly closing the connection"""
628
605
        self.close_connection = 1
629
606
 
634
611
    _req_handler_class = WallRequestHandler
635
612
 
636
613
    def test_http_has(self):
637
 
        t = self.get_readonly_transport()
 
614
        server = self.get_readonly_server()
 
615
        t = self._transport(server.get_url())
638
616
        # Unfortunately httplib (see HTTPResponse._read_status
639
617
        # for details) make no distinction between a closed
640
618
        # socket and badly formatted status line, so we can't
646
624
                          t.has, 'foo/bar')
647
625
 
648
626
    def test_http_get(self):
649
 
        t = self.get_readonly_transport()
 
627
        server = self.get_readonly_server()
 
628
        t = self._transport(server.get_url())
650
629
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
651
630
                           errors.InvalidHttpResponse),
652
631
                          t.get, 'foo/bar')
669
648
    _req_handler_class = BadStatusRequestHandler
670
649
 
671
650
    def test_http_has(self):
672
 
        t = self.get_readonly_transport()
 
651
        server = self.get_readonly_server()
 
652
        t = self._transport(server.get_url())
673
653
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
674
654
 
675
655
    def test_http_get(self):
676
 
        t = self.get_readonly_transport()
 
656
        server = self.get_readonly_server()
 
657
        t = self._transport(server.get_url())
677
658
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
678
659
 
679
660
 
684
665
        """Fakes handling a single HTTP request, returns a bad status"""
685
666
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
686
667
        self.wfile.write("Invalid status line\r\n")
687
 
        # If we don't close the connection pycurl will hang. Since this is a
688
 
        # stress test we don't *have* to respect the protocol, but we don't
689
 
        # have to sabotage it too much either.
690
 
        self.close_connection = True
691
668
        return False
692
669
 
693
670
 
699
676
 
700
677
    _req_handler_class = InvalidStatusRequestHandler
701
678
 
 
679
    def test_http_has(self):
 
680
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
 
681
            raise tests.KnownFailure(
 
682
                'pycurl hangs if the server send back garbage')
 
683
        super(TestInvalidStatusServer, self).test_http_has()
 
684
 
 
685
    def test_http_get(self):
 
686
        if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
 
687
            raise tests.KnownFailure(
 
688
                'pycurl hangs if the server send back garbage')
 
689
        super(TestInvalidStatusServer, self).test_http_get()
 
690
 
702
691
 
703
692
class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
704
693
    """Whatever request comes in, returns a bad protocol version"""
726
715
        super(TestBadProtocolServer, self).setUp()
727
716
 
728
717
    def test_http_has(self):
729
 
        t = self.get_readonly_transport()
 
718
        server = self.get_readonly_server()
 
719
        t = self._transport(server.get_url())
730
720
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
731
721
 
732
722
    def test_http_get(self):
733
 
        t = self.get_readonly_transport()
 
723
        server = self.get_readonly_server()
 
724
        t = self._transport(server.get_url())
734
725
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
735
726
 
736
727
 
750
741
    _req_handler_class = ForbiddenRequestHandler
751
742
 
752
743
    def test_http_has(self):
753
 
        t = self.get_readonly_transport()
 
744
        server = self.get_readonly_server()
 
745
        t = self._transport(server.get_url())
754
746
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
755
747
 
756
748
    def test_http_get(self):
757
 
        t = self.get_readonly_transport()
 
749
        server = self.get_readonly_server()
 
750
        t = self._transport(server.get_url())
758
751
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
759
752
 
760
753
 
799
792
        self.build_tree_contents([('a', '0123456789')],)
800
793
 
801
794
    def test_readv(self):
802
 
        t = self.get_readonly_transport()
 
795
        server = self.get_readonly_server()
 
796
        t = self._transport(server.get_url())
803
797
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
804
798
        self.assertEqual(l[0], (0, '0'))
805
799
        self.assertEqual(l[1], (1, '1'))
807
801
        self.assertEqual(l[3], (9, '9'))
808
802
 
809
803
    def test_readv_out_of_order(self):
810
 
        t = self.get_readonly_transport()
 
804
        server = self.get_readonly_server()
 
805
        t = self._transport(server.get_url())
811
806
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
812
807
        self.assertEqual(l[0], (1, '1'))
813
808
        self.assertEqual(l[1], (9, '9'))
815
810
        self.assertEqual(l[3], (3, '34'))
816
811
 
817
812
    def test_readv_invalid_ranges(self):
818
 
        t = self.get_readonly_transport()
 
813
        server = self.get_readonly_server()
 
814
        t = self._transport(server.get_url())
819
815
 
820
816
        # This is intentionally reading off the end of the file
821
817
        # since we are sure that it cannot get there
829
825
 
830
826
    def test_readv_multiple_get_requests(self):
831
827
        server = self.get_readonly_server()
832
 
        t = self.get_readonly_transport()
 
828
        t = self._transport(server.get_url())
833
829
        # force transport to issue multiple requests
834
830
        t._max_readv_combine = 1
835
831
        t._max_get_ranges = 1
843
839
 
844
840
    def test_readv_get_max_size(self):
845
841
        server = self.get_readonly_server()
846
 
        t = self.get_readonly_transport()
 
842
        t = self._transport(server.get_url())
847
843
        # force transport to issue multiple requests by limiting the number of
848
844
        # bytes by request. Note that this apply to coalesced offsets only, a
849
845
        # single range will keep its size even if bigger than the limit.
858
854
 
859
855
    def test_complete_readv_leave_pipe_clean(self):
860
856
        server = self.get_readonly_server()
861
 
        t = self.get_readonly_transport()
 
857
        t = self._transport(server.get_url())
862
858
        # force transport to issue multiple requests
863
859
        t._get_max_size = 2
864
 
        list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
 
860
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
865
861
        # The server should have issued 3 requests
866
862
        self.assertEqual(3, server.GET_request_nb)
867
863
        self.assertEqual('0123456789', t.get_bytes('a'))
869
865
 
870
866
    def test_incomplete_readv_leave_pipe_clean(self):
871
867
        server = self.get_readonly_server()
872
 
        t = self.get_readonly_transport()
 
868
        t = self._transport(server.get_url())
873
869
        # force transport to issue multiple requests
874
870
        t._get_max_size = 2
875
871
        # Don't collapse readv results into a list so that we leave unread
944
940
    def get_multiple_ranges(self, file, file_size, ranges):
945
941
        self.send_response(206)
946
942
        self.send_header('Accept-Ranges', 'bytes')
947
 
        # XXX: this is strange; the 'random' name below seems undefined and
948
 
        # yet the tests pass -- mbp 2010-10-11 bug 658773
949
943
        boundary = "%d" % random.randint(0,0x7FFFFFFF)
950
944
        self.send_header("Content-Type",
951
945
                         "multipart/byteranges; boundary=%s" % boundary)
1013
1007
                return
1014
1008
            self.send_range_content(file, start, end - start + 1)
1015
1009
            cur += 1
1016
 
        # Final boundary
 
1010
        # No final boundary
1017
1011
        self.wfile.write(boundary_line)
1018
1012
 
1019
1013
 
1027
1021
 
1028
1022
    def test_readv_with_short_reads(self):
1029
1023
        server = self.get_readonly_server()
1030
 
        t = self.get_readonly_transport()
 
1024
        t = self._transport(server.get_url())
1031
1025
        # Force separate ranges for each offset
1032
1026
        t._bytes_to_read_before_seek = 0
1033
1027
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1048
1042
        # that mode
1049
1043
        self.assertEqual('single', t._range_hint)
1050
1044
 
1051
 
 
1052
 
class TruncatedBeforeBoundaryRequestHandler(
1053
 
    http_server.TestingHTTPRequestHandler):
1054
 
    """Truncation before a boundary, like in bug 198646"""
1055
 
 
1056
 
    _truncated_ranges = 1
1057
 
 
1058
 
    def get_multiple_ranges(self, file, file_size, ranges):
1059
 
        self.send_response(206)
1060
 
        self.send_header('Accept-Ranges', 'bytes')
1061
 
        boundary = 'tagada'
1062
 
        self.send_header('Content-Type',
1063
 
                         'multipart/byteranges; boundary=%s' % boundary)
1064
 
        boundary_line = '--%s\r\n' % boundary
1065
 
        # Calculate the Content-Length
1066
 
        content_length = 0
1067
 
        for (start, end) in ranges:
1068
 
            content_length += len(boundary_line)
1069
 
            content_length += self._header_line_length(
1070
 
                'Content-type', 'application/octet-stream')
1071
 
            content_length += self._header_line_length(
1072
 
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
1073
 
            content_length += len('\r\n') # end headers
1074
 
            content_length += end - start # + 1
1075
 
        content_length += len(boundary_line)
1076
 
        self.send_header('Content-length', content_length)
1077
 
        self.end_headers()
1078
 
 
1079
 
        # Send the multipart body
1080
 
        cur = 0
1081
 
        for (start, end) in ranges:
1082
 
            if cur + self._truncated_ranges >= len(ranges):
1083
 
                # Abruptly ends the response and close the connection
1084
 
                self.close_connection = 1
1085
 
                return
1086
 
            self.wfile.write(boundary_line)
1087
 
            self.send_header('Content-type', 'application/octet-stream')
1088
 
            self.send_header('Content-Range', 'bytes %d-%d/%d'
1089
 
                             % (start, end, file_size))
1090
 
            self.end_headers()
1091
 
            self.send_range_content(file, start, end - start + 1)
1092
 
            cur += 1
1093
 
        # Final boundary
1094
 
        self.wfile.write(boundary_line)
1095
 
 
1096
 
 
1097
 
class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
1098
 
    """Tests the case of bug 198646, disconnecting before a boundary."""
1099
 
 
1100
 
    _req_handler_class = TruncatedBeforeBoundaryRequestHandler
1101
 
 
1102
 
    def setUp(self):
1103
 
        super(TestTruncatedBeforeBoundary, self).setUp()
1104
 
        self.build_tree_contents([('a', '0123456789')],)
1105
 
 
1106
 
    def test_readv_with_short_reads(self):
1107
 
        server = self.get_readonly_server()
1108
 
        t = self.get_readonly_transport()
1109
 
        # Force separate ranges for each offset
1110
 
        t._bytes_to_read_before_seek = 0
1111
 
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1112
 
        self.assertEqual((0, '0'), ireadv.next())
1113
 
        self.assertEqual((2, '2'), ireadv.next())
1114
 
        self.assertEqual((4, '45'), ireadv.next())
1115
 
        self.assertEqual((9, '9'), ireadv.next())
1116
 
 
1117
 
 
1118
1045
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1119
1046
    """Errors out when range specifiers exceed the limit"""
1120
1047
 
1144
1071
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1145
1072
    """Tests readv requests against a server erroring out on too much ranges."""
1146
1073
 
1147
 
    scenarios = multiply_scenarios(
1148
 
        vary_by_http_client_implementation(),
1149
 
        vary_by_http_protocol_version(),
1150
 
        )
1151
 
 
1152
1074
    # Requests with more range specifiers will error out
1153
1075
    range_limit = 3
1154
1076
 
1156
1078
        return LimitedRangeHTTPServer(range_limit=self.range_limit,
1157
1079
                                      protocol_version=self._protocol_version)
1158
1080
 
 
1081
    def get_transport(self):
 
1082
        return self._transport(self.get_readonly_server().get_url())
 
1083
 
1159
1084
    def setUp(self):
1160
1085
        http_utils.TestCaseWithWebserver.setUp(self)
1161
1086
        # We need to manipulate ranges that correspond to real chunks in the
1165
1090
        self.build_tree_contents([('a', content)],)
1166
1091
 
1167
1092
    def test_few_ranges(self):
1168
 
        t = self.get_readonly_transport()
 
1093
        t = self.get_transport()
1169
1094
        l = list(t.readv('a', ((0, 4), (1024, 4), )))
1170
1095
        self.assertEqual(l[0], (0, '0000'))
1171
1096
        self.assertEqual(l[1], (1024, '0001'))
1172
1097
        self.assertEqual(1, self.get_readonly_server().GET_request_nb)
1173
1098
 
1174
1099
    def test_more_ranges(self):
1175
 
        t = self.get_readonly_transport()
 
1100
        t = self.get_transport()
1176
1101
        l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
1177
1102
        self.assertEqual(l[0], (0, '0000'))
1178
1103
        self.assertEqual(l[1], (1024, '0001'))
1189
1114
    Only the urllib implementation is tested here.
1190
1115
    """
1191
1116
 
 
1117
    def setUp(self):
 
1118
        tests.TestCase.setUp(self)
 
1119
        self._old_env = {}
 
1120
 
 
1121
    def tearDown(self):
 
1122
        self._restore_env()
 
1123
        tests.TestCase.tearDown(self)
 
1124
 
 
1125
    def _install_env(self, env):
 
1126
        for name, value in env.iteritems():
 
1127
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
1128
 
 
1129
    def _restore_env(self):
 
1130
        for name, value in self._old_env.iteritems():
 
1131
            osutils.set_or_unset_env(name, value)
 
1132
 
1192
1133
    def _proxied_request(self):
1193
1134
        handler = _urllib2_wrappers.ProxyHandler()
1194
 
        request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
 
1135
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
1195
1136
        handler.set_proxy(request, 'http')
1196
1137
        return request
1197
1138
 
1198
 
    def assertEvaluateProxyBypass(self, expected, host, no_proxy):
1199
 
        handler = _urllib2_wrappers.ProxyHandler()
1200
 
        self.assertEquals(expected,
1201
 
                          handler.evaluate_proxy_bypass(host, no_proxy))
1202
 
 
1203
1139
    def test_empty_user(self):
1204
 
        self.overrideEnv('http_proxy', 'http://bar.com')
1205
 
        request = self._proxied_request()
1206
 
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1207
 
 
1208
 
    def test_user_with_at(self):
1209
 
        self.overrideEnv('http_proxy',
1210
 
                         'http://username@domain:password@proxy_host:1234')
 
1140
        self._install_env({'http_proxy': 'http://bar.com'})
1211
1141
        request = self._proxied_request()
1212
1142
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1213
1143
 
1214
1144
    def test_invalid_proxy(self):
1215
1145
        """A proxy env variable without scheme"""
1216
 
        self.overrideEnv('http_proxy', 'host:1234')
 
1146
        self._install_env({'http_proxy': 'host:1234'})
1217
1147
        self.assertRaises(errors.InvalidURL, self._proxied_request)
1218
1148
 
1219
 
    def test_evaluate_proxy_bypass_true(self):
1220
 
        """The host is not proxied"""
1221
 
        self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
1222
 
        self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
1223
 
 
1224
 
    def test_evaluate_proxy_bypass_false(self):
1225
 
        """The host is proxied"""
1226
 
        self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
1227
 
 
1228
 
    def test_evaluate_proxy_bypass_unknown(self):
1229
 
        """The host is not explicitly proxied"""
1230
 
        self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
1231
 
        self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
1232
 
 
1233
 
    def test_evaluate_proxy_bypass_empty_entries(self):
1234
 
        """Ignore empty entries"""
1235
 
        self.assertEvaluateProxyBypass(None, 'example.com', '')
1236
 
        self.assertEvaluateProxyBypass(None, 'example.com', ',')
1237
 
        self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
1238
 
 
1239
1149
 
1240
1150
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
1241
1151
    """Tests proxy server.
1246
1156
    to the file names).
1247
1157
    """
1248
1158
 
1249
 
    scenarios = multiply_scenarios(
1250
 
        vary_by_http_client_implementation(),
1251
 
        vary_by_http_protocol_version(),
1252
 
        )
1253
 
 
1254
1159
    # FIXME: We don't have an https server available, so we don't
1255
 
    # test https connections. --vila toolongago
 
1160
    # test https connections.
1256
1161
 
1257
1162
    def setUp(self):
1258
1163
        super(TestProxyHttpServer, self).setUp()
1259
 
        self.transport_secondary_server = http_utils.ProxyServer
1260
1164
        self.build_tree_contents([('foo', 'contents of foo\n'),
1261
1165
                                  ('foo-proxied', 'proxied contents of foo\n')])
1262
1166
        # Let's setup some attributes for tests
1263
 
        server = self.get_readonly_server()
1264
 
        self.server_host_port = '%s:%d' % (server.host, server.port)
 
1167
        self.server = self.get_readonly_server()
 
1168
        self.proxy_address = '%s:%d' % (self.server.host, self.server.port)
1265
1169
        if self._testing_pycurl():
1266
1170
            # Oh my ! pycurl does not check for the port as part of
1267
1171
            # no_proxy :-( So we just test the host part
1268
 
            self.no_proxy_host = server.host
 
1172
            self.no_proxy_host = self.server.host
1269
1173
        else:
1270
 
            self.no_proxy_host = self.server_host_port
 
1174
            self.no_proxy_host = self.proxy_address
1271
1175
        # The secondary server is the proxy
1272
 
        self.proxy_url = self.get_secondary_url()
 
1176
        self.proxy = self.get_secondary_server()
 
1177
        self.proxy_url = self.proxy.get_url()
 
1178
        self._old_env = {}
1273
1179
 
1274
1180
    def _testing_pycurl(self):
1275
1181
        # TODO: This is duplicated for lots of the classes in this file
1276
1182
        return (features.pycurl.available()
1277
1183
                and self._transport == PyCurlTransport)
1278
1184
 
1279
 
    def assertProxied(self):
1280
 
        t = self.get_readonly_transport()
1281
 
        self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1282
 
 
1283
 
    def assertNotProxied(self):
1284
 
        t = self.get_readonly_transport()
1285
 
        self.assertEqual('contents of foo\n', t.get('foo').read())
 
1185
    def create_transport_secondary_server(self):
 
1186
        """Creates an http server that will serve files with
 
1187
        '-proxied' appended to their names.
 
1188
        """
 
1189
        return http_utils.ProxyServer(protocol_version=self._protocol_version)
 
1190
 
 
1191
    def _install_env(self, env):
 
1192
        for name, value in env.iteritems():
 
1193
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
1194
 
 
1195
    def _restore_env(self):
 
1196
        for name, value in self._old_env.iteritems():
 
1197
            osutils.set_or_unset_env(name, value)
 
1198
 
 
1199
    def proxied_in_env(self, env):
 
1200
        self._install_env(env)
 
1201
        url = self.server.get_url()
 
1202
        t = self._transport(url)
 
1203
        try:
 
1204
            self.assertEqual('proxied contents of foo\n', t.get('foo').read())
 
1205
        finally:
 
1206
            self._restore_env()
 
1207
 
 
1208
    def not_proxied_in_env(self, env):
 
1209
        self._install_env(env)
 
1210
        url = self.server.get_url()
 
1211
        t = self._transport(url)
 
1212
        try:
 
1213
            self.assertEqual('contents of foo\n', t.get('foo').read())
 
1214
        finally:
 
1215
            self._restore_env()
1286
1216
 
1287
1217
    def test_http_proxy(self):
1288
 
        self.overrideEnv('http_proxy', self.proxy_url)
1289
 
        self.assertProxied()
 
1218
        self.proxied_in_env({'http_proxy': self.proxy_url})
1290
1219
 
1291
1220
    def test_HTTP_PROXY(self):
1292
1221
        if self._testing_pycurl():
1295
1224
            # about. Should we ?)
1296
1225
            raise tests.TestNotApplicable(
1297
1226
                'pycurl does not check HTTP_PROXY for security reasons')
1298
 
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
1299
 
        self.assertProxied()
 
1227
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
1300
1228
 
1301
1229
    def test_all_proxy(self):
1302
 
        self.overrideEnv('all_proxy', self.proxy_url)
1303
 
        self.assertProxied()
 
1230
        self.proxied_in_env({'all_proxy': self.proxy_url})
1304
1231
 
1305
1232
    def test_ALL_PROXY(self):
1306
 
        self.overrideEnv('ALL_PROXY', self.proxy_url)
1307
 
        self.assertProxied()
 
1233
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
1308
1234
 
1309
1235
    def test_http_proxy_with_no_proxy(self):
1310
 
        self.overrideEnv('no_proxy', self.no_proxy_host)
1311
 
        self.overrideEnv('http_proxy', self.proxy_url)
1312
 
        self.assertNotProxied()
 
1236
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
 
1237
                                 'no_proxy': self.no_proxy_host})
1313
1238
 
1314
1239
    def test_HTTP_PROXY_with_NO_PROXY(self):
1315
1240
        if self._testing_pycurl():
1316
1241
            raise tests.TestNotApplicable(
1317
1242
                'pycurl does not check HTTP_PROXY for security reasons')
1318
 
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
1319
 
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
1320
 
        self.assertNotProxied()
 
1243
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
 
1244
                                 'NO_PROXY': self.no_proxy_host})
1321
1245
 
1322
1246
    def test_all_proxy_with_no_proxy(self):
1323
 
        self.overrideEnv('no_proxy', self.no_proxy_host)
1324
 
        self.overrideEnv('all_proxy', self.proxy_url)
1325
 
        self.assertNotProxied()
 
1247
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
 
1248
                                 'no_proxy': self.no_proxy_host})
1326
1249
 
1327
1250
    def test_ALL_PROXY_with_NO_PROXY(self):
1328
 
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
1329
 
        self.overrideEnv('ALL_PROXY', self.proxy_url)
1330
 
        self.assertNotProxied()
 
1251
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
 
1252
                                 'NO_PROXY': self.no_proxy_host})
1331
1253
 
1332
1254
    def test_http_proxy_without_scheme(self):
1333
 
        self.overrideEnv('http_proxy', self.server_host_port)
1334
1255
        if self._testing_pycurl():
1335
1256
            # pycurl *ignores* invalid proxy env variables. If that ever change
1336
1257
            # in the future, this test will fail indicating that pycurl do not
1337
1258
            # ignore anymore such variables.
1338
 
            self.assertNotProxied()
 
1259
            self.not_proxied_in_env({'http_proxy': self.proxy_address})
1339
1260
        else:
1340
 
            self.assertRaises(errors.InvalidURL, self.assertProxied)
 
1261
            self.assertRaises(errors.InvalidURL,
 
1262
                              self.proxied_in_env,
 
1263
                              {'http_proxy': self.proxy_address})
1341
1264
 
1342
1265
 
1343
1266
class TestRanges(http_utils.TestCaseWithWebserver):
1344
1267
    """Test the Range header in GET methods."""
1345
1268
 
1346
 
    scenarios = multiply_scenarios(
1347
 
        vary_by_http_client_implementation(),
1348
 
        vary_by_http_protocol_version(),
1349
 
        )
1350
 
 
1351
1269
    def setUp(self):
1352
1270
        http_utils.TestCaseWithWebserver.setUp(self)
1353
1271
        self.build_tree_contents([('a', '0123456789')],)
 
1272
        server = self.get_readonly_server()
 
1273
        self.transport = self._transport(server.get_url())
1354
1274
 
1355
1275
    def create_transport_readonly_server(self):
1356
1276
        return http_server.HttpServer(protocol_version=self._protocol_version)
1357
1277
 
1358
1278
    def _file_contents(self, relpath, ranges):
1359
 
        t = self.get_readonly_transport()
1360
1279
        offsets = [ (start, end - start + 1) for start, end in ranges]
1361
 
        coalesce = t._coalesce_offsets
 
1280
        coalesce = self.transport._coalesce_offsets
1362
1281
        coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
1363
 
        code, data = t._get(relpath, coalesced)
 
1282
        code, data = self.transport._get(relpath, coalesced)
1364
1283
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1365
1284
        for start, end in ranges:
1366
1285
            data.seek(start)
1367
1286
            yield data.read(end - start + 1)
1368
1287
 
1369
1288
    def _file_tail(self, relpath, tail_amount):
1370
 
        t = self.get_readonly_transport()
1371
 
        code, data = t._get(relpath, [], tail_amount)
 
1289
        code, data = self.transport._get(relpath, [], tail_amount)
1372
1290
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1373
1291
        data.seek(-tail_amount, 2)
1374
1292
        return data.read(tail_amount)
1393
1311
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1394
1312
    """Test redirection between http servers."""
1395
1313
 
1396
 
    scenarios = multiply_scenarios(
1397
 
        vary_by_http_client_implementation(),
1398
 
        vary_by_http_protocol_version(),
1399
 
        )
 
1314
    def create_transport_secondary_server(self):
 
1315
        """Create the secondary server redirecting to the primary server"""
 
1316
        new = self.get_readonly_server()
 
1317
 
 
1318
        redirecting = http_utils.HTTPServerRedirecting(
 
1319
            protocol_version=self._protocol_version)
 
1320
        redirecting.redirect_to(new.host, new.port)
 
1321
        return redirecting
1400
1322
 
1401
1323
    def setUp(self):
1402
1324
        super(TestHTTPRedirections, self).setUp()
1404
1326
                                  ('bundle',
1405
1327
                                  '# Bazaar revision bundle v0.9\n#\n')
1406
1328
                                  ],)
 
1329
        # The requests to the old server will be redirected to the new server
 
1330
        self.old_transport = self._transport(self.old_server.get_url())
1407
1331
 
1408
1332
    def test_redirected(self):
1409
 
        self.assertRaises(errors.RedirectRequested,
1410
 
                          self.get_old_transport().get, 'a')
1411
 
        self.assertEqual('0123456789', self.get_new_transport().get('a').read())
 
1333
        self.assertRaises(errors.RedirectRequested, self.old_transport.get, 'a')
 
1334
        t = self._transport(self.new_server.get_url())
 
1335
        self.assertEqual('0123456789', t.get('a').read())
 
1336
 
 
1337
    def test_read_redirected_bundle_from_url(self):
 
1338
        from bzrlib.bundle import read_bundle_from_url
 
1339
        url = self.old_transport.abspath('bundle')
 
1340
        bundle = self.applyDeprecated(deprecated_in((1, 12, 0)),
 
1341
                read_bundle_from_url, url)
 
1342
        # If read_bundle_from_url was successful we get an empty bundle
 
1343
        self.assertEqual([], bundle.revisions)
1412
1344
 
1413
1345
 
1414
1346
class RedirectedRequest(_urllib2_wrappers.Request):
1431
1363
    test.overrideAttr(_urllib2_wrappers, 'Request', RedirectedRequest)
1432
1364
 
1433
1365
 
1434
 
def cleanup_http_redirection_connections(test):
1435
 
    # Some sockets are opened but never seen by _urllib, so we trap them at
1436
 
    # the _urllib2_wrappers level to be able to clean them up.
1437
 
    def socket_disconnect(sock):
1438
 
        try:
1439
 
            sock.shutdown(socket.SHUT_RDWR)
1440
 
            sock.close()
1441
 
        except socket.error:
1442
 
            pass
1443
 
    def connect(connection):
1444
 
        test.http_connect_orig(connection)
1445
 
        test.addCleanup(socket_disconnect, connection.sock)
1446
 
    test.http_connect_orig = test.overrideAttr(
1447
 
        _urllib2_wrappers.HTTPConnection, 'connect', connect)
1448
 
    def connect(connection):
1449
 
        test.https_connect_orig(connection)
1450
 
        test.addCleanup(socket_disconnect, connection.sock)
1451
 
    test.https_connect_orig = test.overrideAttr(
1452
 
        _urllib2_wrappers.HTTPSConnection, 'connect', connect)
1453
 
 
1454
 
 
1455
1366
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1456
1367
    """Test redirections.
1457
1368
 
1466
1377
    -- vila 20070212
1467
1378
    """
1468
1379
 
1469
 
    scenarios = multiply_scenarios(
1470
 
        vary_by_http_client_implementation(),
1471
 
        vary_by_http_protocol_version(),
1472
 
        )
1473
 
 
1474
1380
    def setUp(self):
1475
1381
        if (features.pycurl.available()
1476
1382
            and self._transport == PyCurlTransport):
1477
1383
            raise tests.TestNotApplicable(
1478
 
                "pycurl doesn't redirect silently anymore")
 
1384
                "pycurl doesn't redirect silently annymore")
1479
1385
        super(TestHTTPSilentRedirections, self).setUp()
1480
1386
        install_redirected_request(self)
1481
 
        cleanup_http_redirection_connections(self)
1482
1387
        self.build_tree_contents([('a','a'),
1483
1388
                                  ('1/',),
1484
1389
                                  ('1/a', 'redirected once'),
1492
1397
                                  ('5/a', 'redirected 5 times'),
1493
1398
                                  ],)
1494
1399
 
 
1400
        self.old_transport = self._transport(self.old_server.get_url())
 
1401
 
 
1402
    def create_transport_secondary_server(self):
 
1403
        """Create the secondary server, redirections are defined in the tests"""
 
1404
        return http_utils.HTTPServerRedirecting(
 
1405
            protocol_version=self._protocol_version)
 
1406
 
1495
1407
    def test_one_redirection(self):
1496
 
        t = self.get_old_transport()
1497
 
        req = RedirectedRequest('GET', t._remote_path('a'))
 
1408
        t = self.old_transport
 
1409
 
 
1410
        req = RedirectedRequest('GET', t.abspath('a'))
1498
1411
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1499
1412
                                       self.new_server.port)
1500
1413
        self.old_server.redirections = \
1501
1414
            [('(.*)', r'%s/1\1' % (new_prefix), 301),]
1502
 
        self.assertEqual('redirected once', t._perform(req).read())
 
1415
        self.assertEqual('redirected once',t._perform(req).read())
1503
1416
 
1504
1417
    def test_five_redirections(self):
1505
 
        t = self.get_old_transport()
1506
 
        req = RedirectedRequest('GET', t._remote_path('a'))
 
1418
        t = self.old_transport
 
1419
 
 
1420
        req = RedirectedRequest('GET', t.abspath('a'))
1507
1421
        old_prefix = 'http://%s:%s' % (self.old_server.host,
1508
1422
                                       self.old_server.port)
1509
1423
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1515
1429
            ('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1516
1430
            ('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1517
1431
            ]
1518
 
        self.assertEqual('redirected 5 times', t._perform(req).read())
 
1432
        self.assertEqual('redirected 5 times',t._perform(req).read())
1519
1433
 
1520
1434
 
1521
1435
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1522
1436
    """Test transport.do_catching_redirections."""
1523
1437
 
1524
 
    scenarios = multiply_scenarios(
1525
 
        vary_by_http_client_implementation(),
1526
 
        vary_by_http_protocol_version(),
1527
 
        )
1528
 
 
1529
1438
    def setUp(self):
1530
1439
        super(TestDoCatchRedirections, self).setUp()
1531
1440
        self.build_tree_contents([('a', '0123456789'),],)
1532
 
        cleanup_http_redirection_connections(self)
1533
 
 
1534
 
        self.old_transport = self.get_old_transport()
1535
 
 
1536
 
    def get_a(self, t):
1537
 
        return t.get('a')
 
1441
 
 
1442
        self.old_transport = self._transport(self.old_server.get_url())
 
1443
 
 
1444
    def get_a(self, transport):
 
1445
        return transport.get('a')
1538
1446
 
1539
1447
    def test_no_redirection(self):
1540
 
        t = self.get_new_transport()
 
1448
        t = self._transport(self.new_server.get_url())
1541
1449
 
1542
1450
        # We use None for redirected so that we fail if redirected
1543
1451
        self.assertEqual('0123456789',
1547
1455
    def test_one_redirection(self):
1548
1456
        self.redirections = 0
1549
1457
 
1550
 
        def redirected(t, exception, redirection_notice):
 
1458
        def redirected(transport, exception, redirection_notice):
1551
1459
            self.redirections += 1
1552
 
            redirected_t = t._redirected_to(exception.source, exception.target)
1553
 
            return redirected_t
 
1460
            dir, file = urlutils.split(exception.target)
 
1461
            return self._transport(dir)
1554
1462
 
1555
1463
        self.assertEqual('0123456789',
1556
1464
                         transport.do_catching_redirections(
1570
1478
                          self.get_a, self.old_transport, redirected)
1571
1479
 
1572
1480
 
1573
 
def _setup_authentication_config(**kwargs):
1574
 
    conf = config.AuthenticationConfig()
1575
 
    conf._get_config().update({'httptest': kwargs})
1576
 
    conf._save()
1577
 
 
1578
 
 
1579
 
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
1580
 
    """Unit tests for glue by which urllib2 asks us for authentication"""
1581
 
 
1582
 
    def test_get_user_password_without_port(self):
1583
 
        """We cope if urllib2 doesn't tell us the port.
1584
 
 
1585
 
        See https://bugs.launchpad.net/bzr/+bug/654684
1586
 
        """
1587
 
        user = 'joe'
1588
 
        password = 'foo'
1589
 
        _setup_authentication_config(scheme='http', host='localhost',
1590
 
                                     user=user, password=password)
1591
 
        handler = _urllib2_wrappers.HTTPAuthHandler()
1592
 
        got_pass = handler.get_user_password(dict(
1593
 
            user='joe',
1594
 
            protocol='http',
1595
 
            host='localhost',
1596
 
            path='/',
1597
 
            realm='Realm',
1598
 
            ))
1599
 
        self.assertEquals((user, password), got_pass)
1600
 
 
1601
 
 
1602
1481
class TestAuth(http_utils.TestCaseWithWebserver):
1603
1482
    """Test authentication scheme"""
1604
1483
 
1605
 
    scenarios = multiply_scenarios(
1606
 
        vary_by_http_client_implementation(),
1607
 
        vary_by_http_protocol_version(),
1608
 
        vary_by_http_auth_scheme(),
1609
 
        )
 
1484
    _auth_header = 'Authorization'
 
1485
    _password_prompt_prefix = ''
 
1486
    _username_prompt_prefix = ''
 
1487
    # Set by load_tests
 
1488
    _auth_server = None
1610
1489
 
1611
1490
    def setUp(self):
1612
1491
        super(TestAuth, self).setUp()
1615
1494
                                  ('b', 'contents of b\n'),])
1616
1495
 
1617
1496
    def create_transport_readonly_server(self):
1618
 
        server = self._auth_server(protocol_version=self._protocol_version)
1619
 
        server._url_protocol = self._url_protocol
1620
 
        return server
 
1497
        return self._auth_server(protocol_version=self._protocol_version)
1621
1498
 
1622
1499
    def _testing_pycurl(self):
1623
1500
        # TODO: This is duplicated for lots of the classes in this file
1636
1513
        return url
1637
1514
 
1638
1515
    def get_user_transport(self, user, password):
1639
 
        t = transport.get_transport_from_url(
1640
 
            self.get_user_url(user, password))
1641
 
        return t
 
1516
        return self._transport(self.get_user_url(user, password))
1642
1517
 
1643
1518
    def test_no_user(self):
1644
1519
        self.server.add_user('joe', 'foo')
1755
1630
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
1756
1631
                                            stderr=tests.StringIOWrapper())
1757
1632
        # Create a minimal config file with the right password
1758
 
        _setup_authentication_config(scheme='http', port=self.server.port,
1759
 
                                     user=user, password=password)
 
1633
        conf = config.AuthenticationConfig()
 
1634
        conf._get_config().update(
 
1635
            {'httptest': {'scheme': 'http', 'port': self.server.port,
 
1636
                          'user': user, 'password': password}})
 
1637
        conf._save()
1760
1638
        # Issue a request to the server to connect
1761
1639
        self.assertEqual('contents of a\n',t.get('a').read())
1762
1640
        # stdin should have  been left untouched
1764
1642
        # Only one 'Authentication Required' error should occur
1765
1643
        self.assertEqual(1, self.server.auth_required_errors)
1766
1644
 
 
1645
    def test_user_from_auth_conf(self):
 
1646
        if self._testing_pycurl():
 
1647
            raise tests.TestNotApplicable(
 
1648
                'pycurl does not support authentication.conf')
 
1649
        user = 'joe'
 
1650
        password = 'foo'
 
1651
        self.server.add_user(user, password)
 
1652
        # Create a minimal config file with the right password
 
1653
        conf = config.AuthenticationConfig()
 
1654
        conf._get_config().update(
 
1655
            {'httptest': {'scheme': 'http', 'port': self.server.port,
 
1656
                          'user': user, 'password': password}})
 
1657
        conf._save()
 
1658
        t = self.get_user_transport(None, None)
 
1659
        # Issue a request to the server to connect
 
1660
        self.assertEqual('contents of a\n', t.get('a').read())
 
1661
        # Only one 'Authentication Required' error should occur
 
1662
        self.assertEqual(1, self.server.auth_required_errors)
 
1663
 
1767
1664
    def test_changing_nonce(self):
1768
1665
        if self._auth_server not in (http_utils.HTTPDigestAuthServer,
1769
1666
                                     http_utils.ProxyDigestAuthServer):
1770
1667
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1771
1668
        if self._testing_pycurl():
1772
 
            self.knownFailure(
 
1669
            raise tests.KnownFailure(
1773
1670
                'pycurl does not handle a nonce change')
1774
1671
        self.server.add_user('joe', 'foo')
1775
1672
        t = self.get_user_transport('joe', 'foo')
1785
1682
        # initial 'who are you' and a second 'who are you' with the new nonce)
1786
1683
        self.assertEqual(2, self.server.auth_required_errors)
1787
1684
 
1788
 
    def test_user_from_auth_conf(self):
1789
 
        if self._testing_pycurl():
1790
 
            raise tests.TestNotApplicable(
1791
 
                'pycurl does not support authentication.conf')
1792
 
        user = 'joe'
1793
 
        password = 'foo'
1794
 
        self.server.add_user(user, password)
1795
 
        _setup_authentication_config(scheme='http', port=self.server.port,
1796
 
                                     user=user, password=password)
1797
 
        t = self.get_user_transport(None, None)
1798
 
        # Issue a request to the server to connect
1799
 
        self.assertEqual('contents of a\n', t.get('a').read())
1800
 
        # Only one 'Authentication Required' error should occur
1801
 
        self.assertEqual(1, self.server.auth_required_errors)
1802
 
 
1803
 
    def test_no_credential_leaks_in_log(self):
1804
 
        self.overrideAttr(debug, 'debug_flags', set(['http']))
1805
 
        user = 'joe'
1806
 
        password = 'very-sensitive-password'
1807
 
        self.server.add_user(user, password)
1808
 
        t = self.get_user_transport(user, password)
1809
 
        # Capture the debug calls to mutter
1810
 
        self.mutters = []
1811
 
        def mutter(*args):
1812
 
            lines = args[0] % args[1:]
1813
 
            # Some calls output multiple lines, just split them now since we
1814
 
            # care about a single one later.
1815
 
            self.mutters.extend(lines.splitlines())
1816
 
        self.overrideAttr(trace, 'mutter', mutter)
1817
 
        # Issue a request to the server to connect
1818
 
        self.assertEqual(True, t.has('a'))
1819
 
        # Only one 'Authentication Required' error should occur
1820
 
        self.assertEqual(1, self.server.auth_required_errors)
1821
 
        # Since the authentification succeeded, there should be a corresponding
1822
 
        # debug line
1823
 
        sent_auth_headers = [line for line in self.mutters
1824
 
                             if line.startswith('> %s' % (self._auth_header,))]
1825
 
        self.assertLength(1, sent_auth_headers)
1826
 
        self.assertStartsWith(sent_auth_headers[0],
1827
 
                              '> %s: <masked>' % (self._auth_header,))
1828
1685
 
1829
1686
 
1830
1687
class TestProxyAuth(TestAuth):
1831
 
    """Test proxy authentication schemes.
1832
 
 
1833
 
    This inherits from TestAuth to tweak the setUp and filter some failing
1834
 
    tests.
1835
 
    """
1836
 
 
1837
 
    scenarios = multiply_scenarios(
1838
 
        vary_by_http_client_implementation(),
1839
 
        vary_by_http_protocol_version(),
1840
 
        vary_by_http_proxy_auth_scheme(),
1841
 
        )
 
1688
    """Test proxy authentication schemes."""
 
1689
 
 
1690
    _auth_header = 'Proxy-authorization'
 
1691
    _password_prompt_prefix = 'Proxy '
 
1692
    _username_prompt_prefix = 'Proxy '
1842
1693
 
1843
1694
    def setUp(self):
1844
1695
        super(TestProxyAuth, self).setUp()
 
1696
        self._old_env = {}
 
1697
        self.addCleanup(self._restore_env)
1845
1698
        # Override the contents to avoid false positives
1846
1699
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1847
1700
                                  ('b', 'not proxied contents of b\n'),
1850
1703
                                  ])
1851
1704
 
1852
1705
    def get_user_transport(self, user, password):
1853
 
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
1854
 
        return TestAuth.get_user_transport(self, user, password)
 
1706
        self._install_env({'all_proxy': self.get_user_url(user, password)})
 
1707
        return self._transport(self.server.get_url())
 
1708
 
 
1709
    def _install_env(self, env):
 
1710
        for name, value in env.iteritems():
 
1711
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
1712
 
 
1713
    def _restore_env(self):
 
1714
        for name, value in self._old_env.iteritems():
 
1715
            osutils.set_or_unset_env(name, value)
1855
1716
 
1856
1717
    def test_empty_pass(self):
1857
1718
        if self._testing_pycurl():
1858
1719
            import pycurl
1859
1720
            if pycurl.version_info()[1] < '7.16.0':
1860
 
                self.knownFailure(
 
1721
                raise tests.KnownFailure(
1861
1722
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1862
1723
        super(TestProxyAuth, self).test_empty_pass()
1863
1724
 
1876
1737
        self.readfile = StringIO(socket_read_content)
1877
1738
        self.writefile = StringIO()
1878
1739
        self.writefile.close = lambda: None
1879
 
        self.close = lambda: None
1880
1740
 
1881
1741
    def makefile(self, mode='r', bufsize=None):
1882
1742
        if 'r' in mode:
1887
1747
 
1888
1748
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1889
1749
 
1890
 
    scenarios = multiply_scenarios(
1891
 
        vary_by_http_client_implementation(),
1892
 
        vary_by_http_protocol_version(),
1893
 
        )
1894
 
 
1895
1750
    def setUp(self):
1896
1751
        super(SmartHTTPTunnellingTest, self).setUp()
1897
1752
        # We use the VFS layer as part of HTTP tunnelling tests.
1898
 
        self.overrideEnv('BZR_NO_SMART_VFS', None)
 
1753
        self._captureVar('BZR_NO_SMART_VFS', None)
1899
1754
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1900
 
        self.http_server = self.get_readonly_server()
1901
1755
 
1902
1756
    def create_transport_readonly_server(self):
1903
 
        server = http_utils.HTTPServerWithSmarts(
 
1757
        return http_utils.HTTPServerWithSmarts(
1904
1758
            protocol_version=self._protocol_version)
1905
 
        server._url_protocol = self._url_protocol
1906
 
        return server
1907
1759
 
1908
1760
    def test_open_bzrdir(self):
1909
1761
        branch = self.make_branch('relpath')
1910
 
        url = self.http_server.get_url() + 'relpath'
 
1762
        http_server = self.get_readonly_server()
 
1763
        url = http_server.get_url() + 'relpath'
1911
1764
        bd = bzrdir.BzrDir.open(url)
1912
 
        self.addCleanup(bd.transport.disconnect)
1913
1765
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1914
1766
 
1915
1767
    def test_bulk_data(self):
1917
1769
        # The 'readv' command in the smart protocol both sends and receives
1918
1770
        # bulk data, so we use that.
1919
1771
        self.build_tree(['data-file'])
1920
 
        http_transport = transport.get_transport_from_url(
1921
 
            self.http_server.get_url())
 
1772
        http_server = self.get_readonly_server()
 
1773
        http_transport = self._transport(http_server.get_url())
1922
1774
        medium = http_transport.get_smart_medium()
1923
1775
        # Since we provide the medium, the url below will be mostly ignored
1924
1776
        # during the test, as long as the path is '/'.
1932
1784
        post_body = 'hello\n'
1933
1785
        expected_reply_body = 'ok\x012\n'
1934
1786
 
1935
 
        http_transport = transport.get_transport_from_url(
1936
 
            self.http_server.get_url())
 
1787
        http_server = self.get_readonly_server()
 
1788
        http_transport = self._transport(http_server.get_url())
1937
1789
        medium = http_transport.get_smart_medium()
1938
1790
        response = medium.send_http_smart_request(post_body)
1939
1791
        reply_body = response.read()
1940
1792
        self.assertEqual(expected_reply_body, reply_body)
1941
1793
 
1942
1794
    def test_smart_http_server_post_request_handler(self):
1943
 
        httpd = self.http_server.server
 
1795
        httpd = self.get_readonly_server()._get_httpd()
1944
1796
 
1945
1797
        socket = SampleSocket(
1946
1798
            'POST /.bzr/smart %s \r\n' % self._protocol_version
1978
1830
 
1979
1831
    def test_probe_smart_server(self):
1980
1832
        """Test error handling against server refusing smart requests."""
1981
 
        t = self.get_readonly_transport()
 
1833
        server = self.get_readonly_server()
 
1834
        t = self._transport(server.get_url())
1982
1835
        # No need to build a valid smart request here, the server will not even
1983
1836
        # try to interpret it.
1984
1837
        self.assertRaises(errors.SmartProtocolError,
1985
1838
                          t.get_smart_medium().send_http_smart_request,
1986
1839
                          'whatever')
1987
1840
 
1988
 
 
1989
1841
class Test_redirected_to(tests.TestCase):
1990
1842
 
1991
 
    scenarios = vary_by_http_client_implementation()
1992
 
 
1993
1843
    def test_redirected_to_subdir(self):
1994
1844
        t = self._transport('http://www.example.com/foo')
1995
1845
        r = t._redirected_to('http://www.example.com/foo',
1997
1847
        self.assertIsInstance(r, type(t))
1998
1848
        # Both transports share the some connection
1999
1849
        self.assertEqual(t._get_connection(), r._get_connection())
2000
 
        self.assertEquals('http://www.example.com/foo/subdir/', r.base)
2001
1850
 
2002
1851
    def test_redirected_to_self_with_slash(self):
2003
1852
        t = self._transport('http://www.example.com/foo')
2014
1863
        r = t._redirected_to('http://www.example.com/foo',
2015
1864
                             'http://foo.example.com/foo/subdir')
2016
1865
        self.assertIsInstance(r, type(t))
2017
 
        self.assertEquals('http://foo.example.com/foo/subdir/',
2018
 
            r.external_url())
2019
1866
 
2020
1867
    def test_redirected_to_same_host_sibling_protocol(self):
2021
1868
        t = self._transport('http://www.example.com/foo')
2022
1869
        r = t._redirected_to('http://www.example.com/foo',
2023
1870
                             'https://www.example.com/foo')
2024
1871
        self.assertIsInstance(r, type(t))
2025
 
        self.assertEquals('https://www.example.com/foo/',
2026
 
            r.external_url())
2027
1872
 
2028
1873
    def test_redirected_to_same_host_different_protocol(self):
2029
1874
        t = self._transport('http://www.example.com/foo')
2030
1875
        r = t._redirected_to('http://www.example.com/foo',
2031
1876
                             'ftp://www.example.com/foo')
2032
1877
        self.assertNotEquals(type(r), type(t))
2033
 
        self.assertEquals('ftp://www.example.com/foo/', r.external_url())
2034
 
 
2035
 
    def test_redirected_to_same_host_specific_implementation(self):
2036
 
        t = self._transport('http://www.example.com/foo')
2037
 
        r = t._redirected_to('http://www.example.com/foo',
2038
 
                             'https+urllib://www.example.com/foo')
2039
 
        self.assertEquals('https://www.example.com/foo/', r.external_url())
2040
1878
 
2041
1879
    def test_redirected_to_different_host_same_user(self):
2042
1880
        t = self._transport('http://joe@www.example.com/foo')
2043
1881
        r = t._redirected_to('http://www.example.com/foo',
2044
1882
                             'https://foo.example.com/foo')
2045
1883
        self.assertIsInstance(r, type(t))
2046
 
        self.assertEqual(t._parsed_url.user, r._parsed_url.user)
2047
 
        self.assertEquals('https://joe@foo.example.com/foo/', r.external_url())
 
1884
        self.assertEqual(t._user, r._user)
2048
1885
 
2049
1886
 
2050
1887
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
2059
1896
    line.
2060
1897
    """
2061
1898
 
2062
 
    def _handle_one_request(self):
 
1899
    def handle_one_request(self):
2063
1900
        tcs = self.server.test_case_server
2064
1901
        requestline = self.rfile.readline()
2065
1902
        headers = self.MessageClass(self.rfile, 0)
2103
1940
    pass
2104
1941
 
2105
1942
 
2106
 
if features.HTTPSServerFeature.available():
 
1943
if tests.HTTPSServerFeature.available():
2107
1944
    from bzrlib.tests import https_server
2108
1945
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
2109
1946
        pass
2120
1957
        tests.TestCase.setUp(self)
2121
1958
        self.server = self._activity_server(self._protocol_version)
2122
1959
        self.server.start_server()
2123
 
        _activities = {} # Don't close over self and create a cycle
 
1960
        self.activities = {}
2124
1961
        def report_activity(t, bytes, direction):
2125
 
            count = _activities.get(direction, 0)
 
1962
            count = self.activities.get(direction, 0)
2126
1963
            count += bytes
2127
 
            _activities[direction] = count
2128
 
        self.activities = _activities
 
1964
            self.activities[direction] = count
2129
1965
 
2130
1966
        # We override at class level because constructors may propagate the
2131
1967
        # bound method and render instance overriding ineffective (an
2132
1968
        # alternative would be to define a specific ui factory instead...)
2133
 
        self.overrideAttr(self._transport, '_report_activity', report_activity)
2134
 
        self.addCleanup(self.server.stop_server)
 
1969
        self.orig_report_activity = self._transport._report_activity
 
1970
        self._transport._report_activity = report_activity
 
1971
 
 
1972
    def tearDown(self):
 
1973
        self._transport._report_activity = self.orig_report_activity
 
1974
        self.server.stop_server()
 
1975
        tests.TestCase.tearDown(self)
2135
1976
 
2136
1977
    def get_transport(self):
2137
 
        t = self._transport(self.server.get_url())
2138
 
        # FIXME: Needs cleanup -- vila 20100611
2139
 
        return t
 
1978
        return self._transport(self.server.get_url())
2140
1979
 
2141
1980
    def assertActivitiesMatch(self):
2142
1981
        self.assertEqual(self.server.bytes_read,
2247
2086
'''
2248
2087
        t = self.get_transport()
2249
2088
        # We must send a single line of body bytes, see
2250
 
        # PredefinedRequestHandler._handle_one_request
 
2089
        # PredefinedRequestHandler.handle_one_request
2251
2090
        code, f = t._post('abc def end-of-body\n')
2252
2091
        self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2253
2092
        self.assertActivitiesMatch()
2255
2094
 
2256
2095
class TestActivity(tests.TestCase, TestActivityMixin):
2257
2096
 
2258
 
    scenarios = multiply_scenarios(
2259
 
        vary_by_http_activity(),
2260
 
        vary_by_http_protocol_version(),
2261
 
        )
2262
 
 
2263
2097
    def setUp(self):
2264
 
        TestActivityMixin.setUp(self)
 
2098
        tests.TestCase.setUp(self)
 
2099
        self.server = self._activity_server(self._protocol_version)
 
2100
        self.server.start_server()
 
2101
        self.activities = {}
 
2102
        def report_activity(t, bytes, direction):
 
2103
            count = self.activities.get(direction, 0)
 
2104
            count += bytes
 
2105
            self.activities[direction] = count
 
2106
 
 
2107
        # We override at class level because constructors may propagate the
 
2108
        # bound method and render instance overriding ineffective (an
 
2109
        # alternative would be to define a specific ui factory instead...)
 
2110
        self.orig_report_activity = self._transport._report_activity
 
2111
        self._transport._report_activity = report_activity
 
2112
 
 
2113
    def tearDown(self):
 
2114
        self._transport._report_activity = self.orig_report_activity
 
2115
        self.server.stop_server()
 
2116
        tests.TestCase.tearDown(self)
2265
2117
 
2266
2118
 
2267
2119
class TestNoReportActivity(tests.TestCase, TestActivityMixin):
2268
2120
 
2269
 
    # Unlike TestActivity, we are really testing ReportingFileSocket and
2270
 
    # ReportingSocket, so we don't need all the parametrization. Since
2271
 
    # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
2272
 
    # test them through their use by the transport than directly (that's a
2273
 
    # bit less clean but far more simpler and effective).
2274
 
    _activity_server = ActivityHTTPServer
2275
 
    _protocol_version = 'HTTP/1.1'
2276
 
 
2277
2121
    def setUp(self):
2278
 
        self._transport =_urllib.HttpTransport_urllib
2279
 
        TestActivityMixin.setUp(self)
 
2122
        tests.TestCase.setUp(self)
 
2123
        # Unlike TestActivity, we are really testing ReportingFileSocket and
 
2124
        # ReportingSocket, so we don't need all the parametrization. Since
 
2125
        # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
 
2126
        # test them through their use by the transport than directly (that's a
 
2127
        # bit less clean but far more simpler and effective).
 
2128
        self.server = ActivityHTTPServer('HTTP/1.1')
 
2129
        self._transport=_urllib.HttpTransport_urllib
 
2130
 
 
2131
        self.server.start_server()
 
2132
 
 
2133
        # We override at class level because constructors may propagate the
 
2134
        # bound method and render instance overriding ineffective (an
 
2135
        # alternative would be to define a specific ui factory instead...)
 
2136
        self.orig_report_activity = self._transport._report_activity
 
2137
        self._transport._report_activity = None
 
2138
 
 
2139
    def tearDown(self):
 
2140
        self._transport._report_activity = self.orig_report_activity
 
2141
        self.server.stop_server()
 
2142
        tests.TestCase.tearDown(self)
2280
2143
 
2281
2144
    def assertActivitiesMatch(self):
2282
2145
        # Nothing to check here
2286
2149
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2287
2150
    """Test authentication on the redirected http server."""
2288
2151
 
2289
 
    scenarios = vary_by_http_protocol_version()
2290
 
 
2291
2152
    _auth_header = 'Authorization'
2292
2153
    _password_prompt_prefix = ''
2293
2154
    _username_prompt_prefix = ''
2294
2155
    _auth_server = http_utils.HTTPBasicAuthServer
2295
2156
    _transport = _urllib.HttpTransport_urllib
2296
2157
 
 
2158
    def create_transport_readonly_server(self):
 
2159
        return self._auth_server()
 
2160
 
 
2161
    def create_transport_secondary_server(self):
 
2162
        """Create the secondary server redirecting to the primary server"""
 
2163
        new = self.get_readonly_server()
 
2164
 
 
2165
        redirecting = http_utils.HTTPServerRedirecting()
 
2166
        redirecting.redirect_to(new.host, new.port)
 
2167
        return redirecting
 
2168
 
2297
2169
    def setUp(self):
2298
2170
        super(TestAuthOnRedirected, self).setUp()
2299
2171
        self.build_tree_contents([('a','a'),
2304
2176
                                       self.new_server.port)
2305
2177
        self.old_server.redirections = [
2306
2178
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2307
 
        self.old_transport = self.get_old_transport()
 
2179
        self.old_transport = self._transport(self.old_server.get_url())
2308
2180
        self.new_server.add_user('joe', 'foo')
2309
 
        cleanup_http_redirection_connections(self)
2310
 
 
2311
 
    def create_transport_readonly_server(self):
2312
 
        server = self._auth_server(protocol_version=self._protocol_version)
2313
 
        server._url_protocol = self._url_protocol
2314
 
        return server
2315
 
 
2316
 
    def get_a(self, t):
2317
 
        return t.get('a')
 
2181
 
 
2182
    def get_a(self, transport):
 
2183
        return transport.get('a')
2318
2184
 
2319
2185
    def test_auth_on_redirected_via_do_catching_redirections(self):
2320
2186
        self.redirections = 0
2321
2187
 
2322
 
        def redirected(t, exception, redirection_notice):
 
2188
        def redirected(transport, exception, redirection_notice):
2323
2189
            self.redirections += 1
2324
 
            redirected_t = t._redirected_to(exception.source, exception.target)
2325
 
            self.addCleanup(redirected_t.disconnect)
2326
 
            return redirected_t
 
2190
            dir, file = urlutils.split(exception.target)
 
2191
            return self._transport(dir)
2327
2192
 
2328
2193
        stdout = tests.StringIOWrapper()
2329
2194
        stderr = tests.StringIOWrapper()
2350
2215
                                       self.new_server.port)
2351
2216
        self.old_server.redirections = [
2352
2217
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
2353
 
        self.assertEqual('redirected once', t._perform(req).read())
 
2218
        self.assertEqual('redirected once',t._perform(req).read())
2354
2219
        # stdin should be empty
2355
2220
        self.assertEqual('', ui.ui_factory.stdin.readline())
2356
2221
        # stdout should be empty, stderr will contains the prompts
2357
2222
        self.assertEqual('', stdout.getvalue())
2358
2223
 
 
2224