~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Patch Queue Manager
  • Date: 2011-10-14 16:54:26 UTC
  • mfrom: (6216.1.1 remove-this-file)
  • Revision ID: pqm@pqm.ubuntu.com-20111014165426-tjix4e6idryf1r2z
(jelmer) Remove an accidentally committed .THIS file. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

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