~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Robert Collins
  • Date: 2007-05-07 16:48:14 UTC
  • mto: This revision was merged to the branch mainline in revision 2485.
  • Revision ID: robertc@robertcollins.net-20070507164814-wpagonutf4b5cf8s
Move HACKING to docs/developers/HACKING and adjust Makefile to accomodate this.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
# implementation; at the moment we have urllib and pycurl.
19
19
 
20
20
# TODO: Should be renamed to bzrlib.transport.http.tests?
 
21
# TODO: What about renaming to bzrlib.tests.transport.http ?
21
22
 
22
 
import errno
 
23
from cStringIO import StringIO
 
24
import os
23
25
import select
24
26
import socket
 
27
import sys
25
28
import threading
26
29
 
27
30
import bzrlib
28
 
from bzrlib.errors import DependencyNotPresent, UnsupportedProtocol
29
 
from bzrlib import osutils
30
 
from bzrlib.tests import TestCase, TestSkipped
31
 
from bzrlib.transport import get_transport, Transport
32
 
from bzrlib.transport.http import extract_auth, HttpTransportBase
 
31
from bzrlib import (
 
32
    errors,
 
33
    osutils,
 
34
    ui,
 
35
    urlutils,
 
36
    )
 
37
from bzrlib.tests import (
 
38
    TestCase,
 
39
    TestUIFactory,
 
40
    TestSkipped,
 
41
    StringIOWrapper,
 
42
    )
 
43
from bzrlib.tests.HttpServer import (
 
44
    HttpServer,
 
45
    HttpServer_PyCurl,
 
46
    HttpServer_urllib,
 
47
    )
 
48
from bzrlib.tests.HTTPTestUtil import (
 
49
    BadProtocolRequestHandler,
 
50
    BadStatusRequestHandler,
 
51
    ForbiddenRequestHandler,
 
52
    HTTPBasicAuthServer,
 
53
    HTTPDigestAuthServer,
 
54
    HTTPServerRedirecting,
 
55
    InvalidStatusRequestHandler,
 
56
    NoRangeRequestHandler,
 
57
    ProxyBasicAuthServer,
 
58
    ProxyDigestAuthServer,
 
59
    ProxyServer,
 
60
    SingleRangeRequestHandler,
 
61
    TestCaseWithRedirectedWebserver,
 
62
    TestCaseWithTwoWebservers,
 
63
    TestCaseWithWebserver,
 
64
    WallRequestHandler,
 
65
    )
 
66
from bzrlib.transport import (
 
67
    do_catching_redirections,
 
68
    get_transport,
 
69
    Transport,
 
70
    )
 
71
from bzrlib.transport.http import (
 
72
    extract_auth,
 
73
    HttpTransportBase,
 
74
    _urllib2_wrappers,
 
75
    )
33
76
from bzrlib.transport.http._urllib import HttpTransport_urllib
34
 
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
 
77
from bzrlib.transport.http._urllib2_wrappers import (
 
78
    PasswordManager,
 
79
    ProxyHandler,
 
80
    Request,
 
81
    )
35
82
 
36
83
 
37
84
class FakeManager(object):
38
85
 
39
86
    def __init__(self):
40
87
        self.credentials = []
41
 
        
 
88
 
42
89
    def add_password(self, realm, host, username, password):
43
90
        self.credentials.append([realm, host, username, password])
44
91
 
86
133
        except socket.timeout:
87
134
            # Make sure the client isn't stuck waiting for us to e.g. accept.
88
135
            self._sock.close()
 
136
        except socket.error:
 
137
            # The client may have already closed the socket.
 
138
            pass
89
139
 
90
140
    def tearDown(self):
91
141
        try:
97
147
        self.port = None
98
148
 
99
149
 
 
150
class TestWithTransport_pycurl(object):
 
151
    """Test case to inherit from if pycurl is present"""
 
152
 
 
153
    def _get_pycurl_maybe(self):
 
154
        try:
 
155
            from bzrlib.transport.http._pycurl import PyCurlTransport
 
156
            return PyCurlTransport
 
157
        except errors.DependencyNotPresent:
 
158
            raise TestSkipped('pycurl not present')
 
159
 
 
160
    _transport = property(_get_pycurl_maybe)
 
161
 
 
162
 
100
163
class TestHttpUrls(TestCase):
101
164
 
 
165
    # TODO: This should be moved to authorization tests once they
 
166
    # are written.
 
167
 
102
168
    def test_url_parsing(self):
103
169
        f = FakeManager()
104
170
        url = extract_auth('http://example.com', f)
107
173
        url = extract_auth('http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
108
174
        self.assertEquals('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
109
175
        self.assertEquals(1, len(f.credentials))
110
 
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'], f.credentials[0])
111
 
        
 
176
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'],
 
177
                          f.credentials[0])
 
178
 
 
179
 
 
180
class TestHttpTransportUrls(object):
 
181
    """Test the http urls.
 
182
 
 
183
    This MUST be used by daughter classes that also inherit from
 
184
    TestCase.
 
185
 
 
186
    We can't inherit directly from TestCase or the
 
187
    test framework will try to create an instance which cannot
 
188
    run, its implementation being incomplete.
 
189
    """
 
190
 
112
191
    def test_abs_url(self):
113
192
        """Construction of absolute http URLs"""
114
 
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
 
193
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
115
194
        eq = self.assertEqualDiff
116
 
        eq(t.abspath('.'),
117
 
           'http://bazaar-vcs.org/bzr/bzr.dev')
118
 
        eq(t.abspath('foo/bar'), 
119
 
           'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
120
 
        eq(t.abspath('.bzr'),
121
 
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
 
195
        eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
 
196
        eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
 
197
        eq(t.abspath('.bzr'), 'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
122
198
        eq(t.abspath('.bzr/1//2/./3'),
123
199
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
124
200
 
125
201
    def test_invalid_http_urls(self):
126
202
        """Trap invalid construction of urls"""
127
 
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
128
 
        self.assertRaises(ValueError,
129
 
            t.abspath,
130
 
            '.bzr/')
 
203
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
204
        self.assertRaises(ValueError, t.abspath, '.bzr/')
 
205
        t = self._transport('http://http://bazaar-vcs.org/bzr/bzr.dev/')
 
206
        self.assertRaises((errors.InvalidURL, errors.ConnectionError),
 
207
                          t.has, 'foo/bar')
131
208
 
132
209
    def test_http_root_urls(self):
133
210
        """Construction of URLs from server root"""
134
 
        t = HttpTransport_urllib('http://bzr.ozlabs.org/')
 
211
        t = self._transport('http://bzr.ozlabs.org/')
135
212
        eq = self.assertEqualDiff
136
213
        eq(t.abspath('.bzr/tree-version'),
137
214
           'http://bzr.ozlabs.org/.bzr/tree-version')
138
215
 
139
216
    def test_http_impl_urls(self):
140
217
        """There are servers which ask for particular clients to connect"""
141
 
        try:
142
 
            from bzrlib.transport.http._pycurl import HttpServer_PyCurl
143
 
            server = HttpServer_PyCurl()
144
 
            try:
145
 
                server.setUp()
146
 
                url = server.get_url()
147
 
                self.assertTrue(url.startswith('http+pycurl://'))
148
 
            finally:
149
 
                server.tearDown()
150
 
        except DependencyNotPresent:
 
218
        server = self._server()
 
219
        try:
 
220
            server.setUp()
 
221
            url = server.get_url()
 
222
            self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
 
223
        finally:
 
224
            server.tearDown()
 
225
 
 
226
 
 
227
class TestHttpUrls_urllib(TestHttpTransportUrls, TestCase):
 
228
    """Test http urls with urllib"""
 
229
 
 
230
    _transport = HttpTransport_urllib
 
231
    _server = HttpServer_urllib
 
232
    _qualified_prefix = 'http+urllib'
 
233
 
 
234
 
 
235
class TestHttpUrls_pycurl(TestWithTransport_pycurl, TestHttpTransportUrls,
 
236
                          TestCase):
 
237
    """Test http urls with pycurl"""
 
238
 
 
239
    _server = HttpServer_PyCurl
 
240
    _qualified_prefix = 'http+pycurl'
 
241
 
 
242
    # TODO: This should really be moved into another pycurl
 
243
    # specific test. When https tests will be implemented, take
 
244
    # this one into account.
 
245
    def test_pycurl_without_https_support(self):
 
246
        """Test that pycurl without SSL do not fail with a traceback.
 
247
 
 
248
        For the purpose of the test, we force pycurl to ignore
 
249
        https by supplying a fake version_info that do not
 
250
        support it.
 
251
        """
 
252
        try:
 
253
            import pycurl
 
254
        except ImportError:
151
255
            raise TestSkipped('pycurl not present')
152
 
 
153
 
 
154
 
class TestHttpMixins(object):
155
 
 
156
 
    def _prep_tree(self):
 
256
        # Now that we have pycurl imported, we can fake its version_info
 
257
        # This was taken from a windows pycurl without SSL
 
258
        # (thanks to bialix)
 
259
        pycurl.version_info = lambda : (2,
 
260
                                        '7.13.2',
 
261
                                        462082,
 
262
                                        'i386-pc-win32',
 
263
                                        2576,
 
264
                                        None,
 
265
                                        0,
 
266
                                        None,
 
267
                                        ('ftp', 'gopher', 'telnet',
 
268
                                         'dict', 'ldap', 'http', 'file'),
 
269
                                        None,
 
270
                                        0,
 
271
                                        None)
 
272
        self.assertRaises(errors.DependencyNotPresent, self._transport,
 
273
                          'https://launchpad.net')
 
274
 
 
275
class TestHttpConnections(object):
 
276
    """Test the http connections.
 
277
 
 
278
    This MUST be used by daughter classes that also inherit from
 
279
    TestCaseWithWebserver.
 
280
 
 
281
    We can't inherit directly from TestCaseWithWebserver or the
 
282
    test framework will try to create an instance which cannot
 
283
    run, its implementation being incomplete.
 
284
    """
 
285
 
 
286
    def setUp(self):
 
287
        TestCaseWithWebserver.setUp(self)
157
288
        self.build_tree(['xxx', 'foo/', 'foo/bar'], line_endings='binary',
158
289
                        transport=self.get_transport())
159
290
 
162
293
        t = self._transport(server.get_url())
163
294
        self.assertEqual(t.has('foo/bar'), True)
164
295
        self.assertEqual(len(server.logs), 1)
165
 
        self.assertContainsRe(server.logs[0], 
 
296
        self.assertContainsRe(server.logs[0],
166
297
            r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
167
298
 
168
299
    def test_http_has_not_found(self):
169
300
        server = self.get_readonly_server()
170
301
        t = self._transport(server.get_url())
171
302
        self.assertEqual(t.has('not-found'), False)
172
 
        self.assertContainsRe(server.logs[1], 
 
303
        self.assertContainsRe(server.logs[1],
173
304
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
174
305
 
175
306
    def test_http_get(self):
181
312
            'contents of foo/bar\n')
182
313
        self.assertEqual(len(server.logs), 1)
183
314
        self.assertTrue(server.logs[0].find(
184
 
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s' % bzrlib.__version__) > -1)
 
315
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s'
 
316
            % bzrlib.__version__) > -1)
185
317
 
186
318
    def test_get_smart_medium(self):
187
319
        # For HTTP, get_smart_medium should return the transport object.
189
321
        http_transport = self._transport(server.get_url())
190
322
        medium = http_transport.get_smart_medium()
191
323
        self.assertIs(medium, http_transport)
192
 
        
193
 
 
194
 
class TestHttpConnections_urllib(TestCaseWithWebserver, TestHttpMixins):
195
 
 
196
 
    _transport = HttpTransport_urllib
197
 
 
198
 
    def setUp(self):
199
 
        TestCaseWithWebserver.setUp(self)
200
 
        self._prep_tree()
201
324
 
202
325
    def test_has_on_bogus_host(self):
203
 
        import urllib2
204
 
        # Get a random address, so that we can be sure there is no
205
 
        # http handler there.
206
 
        s = socket.socket()
207
 
        s.bind(('localhost', 0))
208
 
        t = self._transport('http://%s:%s/' % s.getsockname())
209
 
        self.assertRaises(urllib2.URLError, t.has, 'foo/bar')
210
 
 
211
 
 
212
 
class TestHttpConnections_pycurl(TestCaseWithWebserver, TestHttpMixins):
213
 
 
214
 
    def _get_pycurl_maybe(self):
 
326
        # Get a free address and don't 'accept' on it, so that we
 
327
        # can be sure there is no http handler there, but set a
 
328
        # reasonable timeout to not slow down tests too much.
 
329
        default_timeout = socket.getdefaulttimeout()
215
330
        try:
216
 
            from bzrlib.transport.http._pycurl import PyCurlTransport
217
 
            return PyCurlTransport
218
 
        except DependencyNotPresent:
219
 
            raise TestSkipped('pycurl not present')
220
 
 
221
 
    _transport = property(_get_pycurl_maybe)
222
 
 
223
 
    def setUp(self):
224
 
        TestCaseWithWebserver.setUp(self)
225
 
        self._prep_tree()
 
331
            socket.setdefaulttimeout(2)
 
332
            s = socket.socket()
 
333
            s.bind(('localhost', 0))
 
334
            t = self._transport('http://%s:%s/' % s.getsockname())
 
335
            self.assertRaises(errors.ConnectionError, t.has, 'foo/bar')
 
336
        finally:
 
337
            socket.setdefaulttimeout(default_timeout)
 
338
 
 
339
 
 
340
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
 
341
    """Test http connections with urllib"""
 
342
 
 
343
    _transport = HttpTransport_urllib
 
344
 
 
345
 
 
346
 
 
347
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
 
348
                                 TestHttpConnections,
 
349
                                 TestCaseWithWebserver):
 
350
    """Test http connections with pycurl"""
226
351
 
227
352
 
228
353
class TestHttpTransportRegistration(TestCase):
229
354
    """Test registrations of various http implementations"""
230
355
 
231
356
    def test_http_registered(self):
232
 
        import bzrlib.transport.http._urllib
233
 
        from bzrlib.transport import get_transport
234
357
        # urlllib should always be present
235
358
        t = get_transport('http+urllib://bzr.google.com/')
236
359
        self.assertIsInstance(t, Transport)
237
 
        self.assertIsInstance(t, bzrlib.transport.http._urllib.HttpTransport_urllib)
 
360
        self.assertIsInstance(t, HttpTransport_urllib)
238
361
 
239
362
 
240
363
class TestOffsets(TestCase):
261
384
        self.assertEqual([[10, 12], [22, 26]], ranges)
262
385
 
263
386
 
264
 
class TestPost(TestCase):
 
387
class TestPost(object):
265
388
 
266
389
    def _test_post_body_is_received(self, scheme):
267
390
        server = RecordingServer(expect_body_tail='end-of-body')
270
393
        url = '%s://%s:%s/' % (scheme, server.host, server.port)
271
394
        try:
272
395
            http_transport = get_transport(url)
273
 
        except UnsupportedProtocol:
 
396
        except errors.UnsupportedProtocol:
274
397
            raise TestSkipped('%s not available' % scheme)
275
398
        code, response = http_transport._post('abc def end-of-body')
276
399
        self.assertTrue(
282
405
        self.assertTrue(
283
406
            server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
284
407
 
 
408
 
 
409
class TestPost_urllib(TestCase, TestPost):
 
410
    """TestPost for urllib implementation"""
 
411
 
 
412
    _transport = HttpTransport_urllib
 
413
 
285
414
    def test_post_body_is_received_urllib(self):
286
415
        self._test_post_body_is_received('http+urllib')
287
416
 
 
417
 
 
418
class TestPost_pycurl(TestWithTransport_pycurl, TestCase, TestPost):
 
419
    """TestPost for pycurl implementation"""
 
420
 
288
421
    def test_post_body_is_received_pycurl(self):
289
422
        self._test_post_body_is_received('http+pycurl')
290
423
 
313
446
                          ranges=[(0,9), (300,5000)],
314
447
                          tail=50)
315
448
 
316
 
        
 
449
 
 
450
class TestWallServer(object):
 
451
    """Tests exceptions during the connection phase"""
 
452
 
 
453
    def create_transport_readonly_server(self):
 
454
        return HttpServer(WallRequestHandler)
 
455
 
 
456
    def test_http_has(self):
 
457
        server = self.get_readonly_server()
 
458
        t = self._transport(server.get_url())
 
459
        # Unfortunately httplib (see HTTPResponse._read_status
 
460
        # for details) make no distinction between a closed
 
461
        # socket and badly formatted status line, so we can't
 
462
        # just test for ConnectionError, we have to test
 
463
        # InvalidHttpResponse too.
 
464
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
 
465
                          t.has, 'foo/bar')
 
466
 
 
467
    def test_http_get(self):
 
468
        server = self.get_readonly_server()
 
469
        t = self._transport(server.get_url())
 
470
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
 
471
                          t.get, 'foo/bar')
 
472
 
 
473
 
 
474
class TestWallServer_urllib(TestWallServer, TestCaseWithWebserver):
 
475
    """Tests "wall" server for urllib implementation"""
 
476
 
 
477
    _transport = HttpTransport_urllib
 
478
 
 
479
 
 
480
class TestWallServer_pycurl(TestWithTransport_pycurl,
 
481
                            TestWallServer,
 
482
                            TestCaseWithWebserver):
 
483
    """Tests "wall" server for pycurl implementation"""
 
484
 
 
485
 
 
486
class TestBadStatusServer(object):
 
487
    """Tests bad status from server."""
 
488
 
 
489
    def create_transport_readonly_server(self):
 
490
        return HttpServer(BadStatusRequestHandler)
 
491
 
 
492
    def test_http_has(self):
 
493
        server = self.get_readonly_server()
 
494
        t = self._transport(server.get_url())
 
495
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
496
 
 
497
    def test_http_get(self):
 
498
        server = self.get_readonly_server()
 
499
        t = self._transport(server.get_url())
 
500
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
501
 
 
502
 
 
503
class TestBadStatusServer_urllib(TestBadStatusServer, TestCaseWithWebserver):
 
504
    """Tests bad status server for urllib implementation"""
 
505
 
 
506
    _transport = HttpTransport_urllib
 
507
 
 
508
 
 
509
class TestBadStatusServer_pycurl(TestWithTransport_pycurl,
 
510
                                 TestBadStatusServer,
 
511
                                 TestCaseWithWebserver):
 
512
    """Tests bad status server for pycurl implementation"""
 
513
 
 
514
 
 
515
class TestInvalidStatusServer(TestBadStatusServer):
 
516
    """Tests invalid status from server.
 
517
 
 
518
    Both implementations raises the same error as for a bad status.
 
519
    """
 
520
 
 
521
    def create_transport_readonly_server(self):
 
522
        return HttpServer(InvalidStatusRequestHandler)
 
523
 
 
524
 
 
525
class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
 
526
                                     TestCaseWithWebserver):
 
527
    """Tests invalid status server for urllib implementation"""
 
528
 
 
529
    _transport = HttpTransport_urllib
 
530
 
 
531
 
 
532
class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
 
533
                                     TestInvalidStatusServer,
 
534
                                     TestCaseWithWebserver):
 
535
    """Tests invalid status server for pycurl implementation"""
 
536
 
 
537
 
 
538
class TestBadProtocolServer(object):
 
539
    """Tests bad protocol from server."""
 
540
 
 
541
    def create_transport_readonly_server(self):
 
542
        return HttpServer(BadProtocolRequestHandler)
 
543
 
 
544
    def test_http_has(self):
 
545
        server = self.get_readonly_server()
 
546
        t = self._transport(server.get_url())
 
547
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
548
 
 
549
    def test_http_get(self):
 
550
        server = self.get_readonly_server()
 
551
        t = self._transport(server.get_url())
 
552
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
553
 
 
554
 
 
555
class TestBadProtocolServer_urllib(TestBadProtocolServer,
 
556
                                   TestCaseWithWebserver):
 
557
    """Tests bad protocol server for urllib implementation"""
 
558
 
 
559
    _transport = HttpTransport_urllib
 
560
 
 
561
# curl don't check the protocol version
 
562
#class TestBadProtocolServer_pycurl(TestWithTransport_pycurl,
 
563
#                                   TestBadProtocolServer,
 
564
#                                   TestCaseWithWebserver):
 
565
#    """Tests bad protocol server for pycurl implementation"""
 
566
 
 
567
 
 
568
class TestForbiddenServer(object):
 
569
    """Tests forbidden server"""
 
570
 
 
571
    def create_transport_readonly_server(self):
 
572
        return HttpServer(ForbiddenRequestHandler)
 
573
 
 
574
    def test_http_has(self):
 
575
        server = self.get_readonly_server()
 
576
        t = self._transport(server.get_url())
 
577
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
 
578
 
 
579
    def test_http_get(self):
 
580
        server = self.get_readonly_server()
 
581
        t = self._transport(server.get_url())
 
582
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
 
583
 
 
584
 
 
585
class TestForbiddenServer_urllib(TestForbiddenServer, TestCaseWithWebserver):
 
586
    """Tests forbidden server for urllib implementation"""
 
587
 
 
588
    _transport = HttpTransport_urllib
 
589
 
 
590
 
 
591
class TestForbiddenServer_pycurl(TestWithTransport_pycurl,
 
592
                                 TestForbiddenServer,
 
593
                                 TestCaseWithWebserver):
 
594
    """Tests forbidden server for pycurl implementation"""
 
595
 
 
596
 
317
597
class TestRecordingServer(TestCase):
318
598
 
319
599
    def test_create(self):
343
623
        self.assertEqual('HTTP/1.1 200 OK\r\n',
344
624
                         osutils.recv_all(sock, 4096))
345
625
        self.assertEqual('abc', server.received_bytes)
 
626
 
 
627
 
 
628
class TestRangeRequestServer(object):
 
629
    """Tests readv requests against server.
 
630
 
 
631
    This MUST be used by daughter classes that also inherit from
 
632
    TestCaseWithWebserver.
 
633
 
 
634
    We can't inherit directly from TestCaseWithWebserver or the
 
635
    test framework will try to create an instance which cannot
 
636
    run, its implementation being incomplete.
 
637
    """
 
638
 
 
639
    def setUp(self):
 
640
        TestCaseWithWebserver.setUp(self)
 
641
        self.build_tree_contents([('a', '0123456789')],)
 
642
 
 
643
    def test_readv(self):
 
644
        server = self.get_readonly_server()
 
645
        t = self._transport(server.get_url())
 
646
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
 
647
        self.assertEqual(l[0], (0, '0'))
 
648
        self.assertEqual(l[1], (1, '1'))
 
649
        self.assertEqual(l[2], (3, '34'))
 
650
        self.assertEqual(l[3], (9, '9'))
 
651
 
 
652
    def test_readv_out_of_order(self):
 
653
        server = self.get_readonly_server()
 
654
        t = self._transport(server.get_url())
 
655
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
 
656
        self.assertEqual(l[0], (1, '1'))
 
657
        self.assertEqual(l[1], (9, '9'))
 
658
        self.assertEqual(l[2], (0, '0'))
 
659
        self.assertEqual(l[3], (3, '34'))
 
660
 
 
661
    def test_readv_invalid_ranges(self):
 
662
        server = self.get_readonly_server()
 
663
        t = self._transport(server.get_url())
 
664
 
 
665
        # This is intentionally reading off the end of the file
 
666
        # since we are sure that it cannot get there
 
667
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
 
668
                              t.readv, 'a', [(1,1), (8,10)])
 
669
 
 
670
        # This is trying to seek past the end of the file, it should
 
671
        # also raise a special error
 
672
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
 
673
                              t.readv, 'a', [(12,2)])
 
674
 
 
675
 
 
676
class TestSingleRangeRequestServer(TestRangeRequestServer):
 
677
    """Test readv against a server which accept only single range requests"""
 
678
 
 
679
    def create_transport_readonly_server(self):
 
680
        return HttpServer(SingleRangeRequestHandler)
 
681
 
 
682
 
 
683
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
 
684
                                          TestCaseWithWebserver):
 
685
    """Tests single range requests accepting server for urllib implementation"""
 
686
 
 
687
    _transport = HttpTransport_urllib
 
688
 
 
689
 
 
690
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
 
691
                                          TestSingleRangeRequestServer,
 
692
                                          TestCaseWithWebserver):
 
693
    """Tests single range requests accepting server for pycurl implementation"""
 
694
 
 
695
 
 
696
class TestNoRangeRequestServer(TestRangeRequestServer):
 
697
    """Test readv against a server which do not accept range requests"""
 
698
 
 
699
    def create_transport_readonly_server(self):
 
700
        return HttpServer(NoRangeRequestHandler)
 
701
 
 
702
 
 
703
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
 
704
                                      TestCaseWithWebserver):
 
705
    """Tests range requests refusing server for urllib implementation"""
 
706
 
 
707
    _transport = HttpTransport_urllib
 
708
 
 
709
 
 
710
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
 
711
                               TestNoRangeRequestServer,
 
712
                               TestCaseWithWebserver):
 
713
    """Tests range requests refusing server for pycurl implementation"""
 
714
 
 
715
 
 
716
class TestHttpProxyWhiteBox(TestCase):
 
717
    """Whitebox test proxy http authorization.
 
718
 
 
719
    Only the urllib implementation is tested here.
 
720
    """
 
721
 
 
722
    def setUp(self):
 
723
        TestCase.setUp(self)
 
724
        self._old_env = {}
 
725
 
 
726
    def tearDown(self):
 
727
        self._restore_env()
 
728
 
 
729
    def _install_env(self, env):
 
730
        for name, value in env.iteritems():
 
731
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
732
 
 
733
    def _restore_env(self):
 
734
        for name, value in self._old_env.iteritems():
 
735
            osutils.set_or_unset_env(name, value)
 
736
 
 
737
    def _proxied_request(self):
 
738
        handler = ProxyHandler(PasswordManager())
 
739
        request = Request('GET','http://baz/buzzle')
 
740
        handler.set_proxy(request, 'http')
 
741
        return request
 
742
 
 
743
    def test_empty_user(self):
 
744
        self._install_env({'http_proxy': 'http://bar.com'})
 
745
        request = self._proxied_request()
 
746
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
 
747
 
 
748
    def test_invalid_proxy(self):
 
749
        """A proxy env variable without scheme"""
 
750
        self._install_env({'http_proxy': 'host:1234'})
 
751
        self.assertRaises(errors.InvalidURL, self._proxied_request)
 
752
 
 
753
 
 
754
class TestProxyHttpServer(object):
 
755
    """Tests proxy server.
 
756
 
 
757
    This MUST be used by daughter classes that also inherit from
 
758
    TestCaseWithTwoWebservers.
 
759
 
 
760
    We can't inherit directly from TestCaseWithTwoWebservers or
 
761
    the test framework will try to create an instance which
 
762
    cannot run, its implementation being incomplete.
 
763
 
 
764
    Be aware that we do not setup a real proxy here. Instead, we
 
765
    check that the *connection* goes through the proxy by serving
 
766
    different content (the faked proxy server append '-proxied'
 
767
    to the file names).
 
768
    """
 
769
 
 
770
    # FIXME: We don't have an https server available, so we don't
 
771
    # test https connections.
 
772
 
 
773
    # FIXME: Once the test suite is better fitted to test
 
774
    # authorization schemes, test proxy authorizations too (see
 
775
    # bug #83954).
 
776
 
 
777
    def setUp(self):
 
778
        TestCaseWithTwoWebservers.setUp(self)
 
779
        self.build_tree_contents([('foo', 'contents of foo\n'),
 
780
                                  ('foo-proxied', 'proxied contents of foo\n')])
 
781
        # Let's setup some attributes for tests
 
782
        self.server = self.get_readonly_server()
 
783
        self.proxy_address = '%s:%d' % (self.server.host, self.server.port)
 
784
        self.no_proxy_host = self.proxy_address
 
785
        # The secondary server is the proxy
 
786
        self.proxy = self.get_secondary_server()
 
787
        self.proxy_url = self.proxy.get_url()
 
788
        self._old_env = {}
 
789
 
 
790
    def create_transport_secondary_server(self):
 
791
        """Creates an http server that will serve files with
 
792
        '-proxied' appended to their names.
 
793
        """
 
794
        return ProxyServer()
 
795
 
 
796
    def _install_env(self, env):
 
797
        for name, value in env.iteritems():
 
798
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
799
 
 
800
    def _restore_env(self):
 
801
        for name, value in self._old_env.iteritems():
 
802
            osutils.set_or_unset_env(name, value)
 
803
 
 
804
    def proxied_in_env(self, env):
 
805
        self._install_env(env)
 
806
        url = self.server.get_url()
 
807
        t = self._transport(url)
 
808
        try:
 
809
            self.assertEqual(t.get('foo').read(), 'proxied contents of foo\n')
 
810
        finally:
 
811
            self._restore_env()
 
812
 
 
813
    def not_proxied_in_env(self, env):
 
814
        self._install_env(env)
 
815
        url = self.server.get_url()
 
816
        t = self._transport(url)
 
817
        try:
 
818
            self.assertEqual(t.get('foo').read(), 'contents of foo\n')
 
819
        finally:
 
820
            self._restore_env()
 
821
 
 
822
    def test_http_proxy(self):
 
823
        self.proxied_in_env({'http_proxy': self.proxy_url})
 
824
 
 
825
    def test_HTTP_PROXY(self):
 
826
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
 
827
 
 
828
    def test_all_proxy(self):
 
829
        self.proxied_in_env({'all_proxy': self.proxy_url})
 
830
 
 
831
    def test_ALL_PROXY(self):
 
832
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
 
833
 
 
834
    def test_http_proxy_with_no_proxy(self):
 
835
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
 
836
                                 'no_proxy': self.no_proxy_host})
 
837
 
 
838
    def test_HTTP_PROXY_with_NO_PROXY(self):
 
839
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
 
840
                                 'NO_PROXY': self.no_proxy_host})
 
841
 
 
842
    def test_all_proxy_with_no_proxy(self):
 
843
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
 
844
                                 'no_proxy': self.no_proxy_host})
 
845
 
 
846
    def test_ALL_PROXY_with_NO_PROXY(self):
 
847
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
 
848
                                 'NO_PROXY': self.no_proxy_host})
 
849
 
 
850
    def test_http_proxy_without_scheme(self):
 
851
        self.assertRaises(errors.InvalidURL,
 
852
                          self.proxied_in_env,
 
853
                          {'http_proxy': self.proxy_address})
 
854
 
 
855
 
 
856
class TestProxyHttpServer_urllib(TestProxyHttpServer,
 
857
                                 TestCaseWithTwoWebservers):
 
858
    """Tests proxy server for urllib implementation"""
 
859
 
 
860
    _transport = HttpTransport_urllib
 
861
 
 
862
 
 
863
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
 
864
                                 TestProxyHttpServer,
 
865
                                 TestCaseWithTwoWebservers):
 
866
    """Tests proxy server for pycurl implementation"""
 
867
 
 
868
    def setUp(self):
 
869
        TestProxyHttpServer.setUp(self)
 
870
        # Oh my ! pycurl does not check for the port as part of
 
871
        # no_proxy :-( So we just test the host part
 
872
        self.no_proxy_host = 'localhost'
 
873
 
 
874
    def test_HTTP_PROXY(self):
 
875
        # pycurl do not check HTTP_PROXY for security reasons
 
876
        # (for use in a CGI context that we do not care
 
877
        # about. Should we ?)
 
878
        raise TestSkipped()
 
879
 
 
880
    def test_HTTP_PROXY_with_NO_PROXY(self):
 
881
        raise TestSkipped()
 
882
 
 
883
    def test_http_proxy_without_scheme(self):
 
884
        # pycurl *ignores* invalid proxy env variables. If that
 
885
        # ever change in the future, this test will fail
 
886
        # indicating that pycurl do not ignore anymore such
 
887
        # variables.
 
888
        self.not_proxied_in_env({'http_proxy': self.proxy_address})
 
889
 
 
890
 
 
891
class TestRanges(object):
 
892
    """Test the Range header in GET methods..
 
893
 
 
894
    This MUST be used by daughter classes that also inherit from
 
895
    TestCaseWithWebserver.
 
896
 
 
897
    We can't inherit directly from TestCaseWithWebserver or the
 
898
    test framework will try to create an instance which cannot
 
899
    run, its implementation being incomplete.
 
900
    """
 
901
 
 
902
    def setUp(self):
 
903
        TestCaseWithWebserver.setUp(self)
 
904
        self.build_tree_contents([('a', '0123456789')],)
 
905
        server = self.get_readonly_server()
 
906
        self.transport = self._transport(server.get_url())
 
907
 
 
908
    def _file_contents(self, relpath, ranges, tail_amount=0):
 
909
         code, data = self.transport._get(relpath, ranges)
 
910
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
 
911
         for start, end in ranges:
 
912
             data.seek(start)
 
913
             yield data.read(end - start + 1)
 
914
 
 
915
    def _file_tail(self, relpath, tail_amount):
 
916
         code, data = self.transport._get(relpath, [], tail_amount)
 
917
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
 
918
         data.seek(-tail_amount + 1, 2)
 
919
         return data.read(tail_amount)
 
920
 
 
921
    def test_range_header(self):
 
922
        # Valid ranges
 
923
        map(self.assertEqual,['0', '234'],
 
924
            list(self._file_contents('a', [(0,0), (2,4)])),)
 
925
        # Tail
 
926
        self.assertEqual('789', self._file_tail('a', 3))
 
927
        # Syntactically invalid range
 
928
        self.assertRaises(errors.InvalidRange,
 
929
                          self.transport._get, 'a', [(4, 3)])
 
930
        # Semantically invalid range
 
931
        self.assertRaises(errors.InvalidRange,
 
932
                          self.transport._get, 'a', [(42, 128)])
 
933
 
 
934
 
 
935
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
 
936
    """Test the Range header in GET methods for urllib implementation"""
 
937
 
 
938
    _transport = HttpTransport_urllib
 
939
 
 
940
 
 
941
class TestRanges_pycurl(TestWithTransport_pycurl,
 
942
                        TestRanges,
 
943
                        TestCaseWithWebserver):
 
944
    """Test the Range header in GET methods for pycurl implementation"""
 
945
 
 
946
 
 
947
class TestHTTPRedirections(object):
 
948
    """Test redirection between http servers.
 
949
 
 
950
    This MUST be used by daughter classes that also inherit from
 
951
    TestCaseWithRedirectedWebserver.
 
952
 
 
953
    We can't inherit directly from TestCaseWithTwoWebservers or the
 
954
    test framework will try to create an instance which cannot
 
955
    run, its implementation being incomplete. 
 
956
    """
 
957
 
 
958
    def create_transport_secondary_server(self):
 
959
        """Create the secondary server redirecting to the primary server"""
 
960
        new = self.get_readonly_server()
 
961
 
 
962
        redirecting = HTTPServerRedirecting()
 
963
        redirecting.redirect_to(new.host, new.port)
 
964
        return redirecting
 
965
 
 
966
    def setUp(self):
 
967
        super(TestHTTPRedirections, self).setUp()
 
968
        self.build_tree_contents([('a', '0123456789'),
 
969
                                  ('bundle',
 
970
                                  '# Bazaar revision bundle v0.9\n#\n')
 
971
                                  ],)
 
972
 
 
973
        self.old_transport = self._transport(self.old_server.get_url())
 
974
 
 
975
    def test_redirected(self):
 
976
        self.assertRaises(errors.RedirectRequested, self.old_transport.get, 'a')
 
977
        t = self._transport(self.new_server.get_url())
 
978
        self.assertEqual('0123456789', t.get('a').read())
 
979
 
 
980
    def test_read_redirected_bundle_from_url(self):
 
981
        from bzrlib.bundle import read_bundle_from_url
 
982
        url = self.old_transport.abspath('bundle')
 
983
        bundle = read_bundle_from_url(url)
 
984
        # If read_bundle_from_url was successful we get an empty bundle
 
985
        self.assertEqual([], bundle.revisions)
 
986
 
 
987
 
 
988
class TestHTTPRedirections_urllib(TestHTTPRedirections,
 
989
                                  TestCaseWithRedirectedWebserver):
 
990
    """Tests redirections for urllib implementation"""
 
991
 
 
992
    _transport = HttpTransport_urllib
 
993
 
 
994
 
 
995
 
 
996
class TestHTTPRedirections_pycurl(TestWithTransport_pycurl,
 
997
                                  TestHTTPRedirections,
 
998
                                  TestCaseWithRedirectedWebserver):
 
999
    """Tests redirections for pycurl implementation"""
 
1000
 
 
1001
 
 
1002
class RedirectedRequest(Request):
 
1003
    """Request following redirections"""
 
1004
 
 
1005
    init_orig = Request.__init__
 
1006
 
 
1007
    def __init__(self, method, url, *args, **kwargs):
 
1008
        RedirectedRequest.init_orig(self, method, url, args, kwargs)
 
1009
        self.follow_redirections = True
 
1010
 
 
1011
 
 
1012
class TestHTTPSilentRedirections_urllib(TestCaseWithRedirectedWebserver):
 
1013
    """Test redirections provided by urllib.
 
1014
 
 
1015
    http implementations do not redirect silently anymore (they
 
1016
    do not redirect at all in fact). The mechanism is still in
 
1017
    place at the _urllib2_wrappers.Request level and these tests
 
1018
    exercise it.
 
1019
 
 
1020
    For the pycurl implementation
 
1021
    the redirection have been deleted as we may deprecate pycurl
 
1022
    and I have no place to keep a working implementation.
 
1023
    -- vila 20070212
 
1024
    """
 
1025
 
 
1026
    _transport = HttpTransport_urllib
 
1027
 
 
1028
    def setUp(self):
 
1029
        super(TestHTTPSilentRedirections_urllib, self).setUp()
 
1030
        self.setup_redirected_request()
 
1031
        self.addCleanup(self.cleanup_redirected_request)
 
1032
        self.build_tree_contents([('a','a'),
 
1033
                                  ('1/',),
 
1034
                                  ('1/a', 'redirected once'),
 
1035
                                  ('2/',),
 
1036
                                  ('2/a', 'redirected twice'),
 
1037
                                  ('3/',),
 
1038
                                  ('3/a', 'redirected thrice'),
 
1039
                                  ('4/',),
 
1040
                                  ('4/a', 'redirected 4 times'),
 
1041
                                  ('5/',),
 
1042
                                  ('5/a', 'redirected 5 times'),
 
1043
                                  ],)
 
1044
 
 
1045
        self.old_transport = self._transport(self.old_server.get_url())
 
1046
 
 
1047
    def setup_redirected_request(self):
 
1048
        self.original_class = _urllib2_wrappers.Request
 
1049
        _urllib2_wrappers.Request = RedirectedRequest
 
1050
 
 
1051
    def cleanup_redirected_request(self):
 
1052
        _urllib2_wrappers.Request = self.original_class
 
1053
 
 
1054
    def create_transport_secondary_server(self):
 
1055
        """Create the secondary server, redirections are defined in the tests"""
 
1056
        return HTTPServerRedirecting()
 
1057
 
 
1058
    def test_one_redirection(self):
 
1059
        t = self.old_transport
 
1060
 
 
1061
        req = RedirectedRequest('GET', t.abspath('a'))
 
1062
        req.follow_redirections = True
 
1063
        new_prefix = 'http://%s:%s' % (self.new_server.host,
 
1064
                                       self.new_server.port)
 
1065
        self.old_server.redirections = \
 
1066
            [('(.*)', r'%s/1\1' % (new_prefix), 301),]
 
1067
        self.assertEquals('redirected once',t._perform(req).read())
 
1068
 
 
1069
    def test_five_redirections(self):
 
1070
        t = self.old_transport
 
1071
 
 
1072
        req = RedirectedRequest('GET', t.abspath('a'))
 
1073
        req.follow_redirections = True
 
1074
        old_prefix = 'http://%s:%s' % (self.old_server.host,
 
1075
                                       self.old_server.port)
 
1076
        new_prefix = 'http://%s:%s' % (self.new_server.host,
 
1077
                                       self.new_server.port)
 
1078
        self.old_server.redirections = \
 
1079
            [('/1(.*)', r'%s/2\1' % (old_prefix), 302),
 
1080
             ('/2(.*)', r'%s/3\1' % (old_prefix), 303),
 
1081
             ('/3(.*)', r'%s/4\1' % (old_prefix), 307),
 
1082
             ('/4(.*)', r'%s/5\1' % (new_prefix), 301),
 
1083
             ('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
 
1084
             ]
 
1085
        self.assertEquals('redirected 5 times',t._perform(req).read())
 
1086
 
 
1087
 
 
1088
class TestDoCatchRedirections(TestCaseWithRedirectedWebserver):
 
1089
    """Test transport.do_catching_redirections.
 
1090
 
 
1091
    We arbitrarily choose to use urllib transports
 
1092
    """
 
1093
 
 
1094
    _transport = HttpTransport_urllib
 
1095
 
 
1096
    def setUp(self):
 
1097
        super(TestDoCatchRedirections, self).setUp()
 
1098
        self.build_tree_contents([('a', '0123456789'),],)
 
1099
 
 
1100
        self.old_transport = self._transport(self.old_server.get_url())
 
1101
 
 
1102
    def get_a(self, transport):
 
1103
        return transport.get('a')
 
1104
 
 
1105
    def test_no_redirection(self):
 
1106
        t = self._transport(self.new_server.get_url())
 
1107
 
 
1108
        # We use None for redirected so that we fail if redirected
 
1109
        self.assertEquals('0123456789',
 
1110
                          do_catching_redirections(self.get_a, t, None).read())
 
1111
 
 
1112
    def test_one_redirection(self):
 
1113
        self.redirections = 0
 
1114
 
 
1115
        def redirected(transport, exception, redirection_notice):
 
1116
            self.redirections += 1
 
1117
            dir, file = urlutils.split(exception.target)
 
1118
            return self._transport(dir)
 
1119
 
 
1120
        self.assertEquals('0123456789',
 
1121
                          do_catching_redirections(self.get_a,
 
1122
                                                   self.old_transport,
 
1123
                                                   redirected
 
1124
                                                   ).read())
 
1125
        self.assertEquals(1, self.redirections)
 
1126
 
 
1127
    def test_redirection_loop(self):
 
1128
 
 
1129
        def redirected(transport, exception, redirection_notice):
 
1130
            # By using the redirected url as a base dir for the
 
1131
            # *old* transport, we create a loop: a => a/a =>
 
1132
            # a/a/a
 
1133
            return self.old_transport.clone(exception.target)
 
1134
 
 
1135
        self.assertRaises(errors.TooManyRedirections, do_catching_redirections,
 
1136
                          self.get_a, self.old_transport, redirected)
 
1137
 
 
1138
 
 
1139
class TestAuth(object):
 
1140
    """Test some authentication scheme specified by daughter class.
 
1141
 
 
1142
    This MUST be used by daughter classes that also inherit from
 
1143
    either TestCaseWithWebserver or TestCaseWithTwoWebservers.
 
1144
    """
 
1145
 
 
1146
    def setUp(self):
 
1147
        """Set up the test environment
 
1148
 
 
1149
        Daughter classes should set up their own environment
 
1150
        (including self.server) and explicitely call this
 
1151
        method. This is needed because we want to reuse the same
 
1152
        tests for proxy and no-proxy accesses which have
 
1153
        different ways of setting self.server.
 
1154
        """
 
1155
        self.build_tree_contents([('a', 'contents of a\n'),
 
1156
                                  ('b', 'contents of b\n'),])
 
1157
        self.old_factory = ui.ui_factory
 
1158
        self.old_stdout = sys.stdout
 
1159
        sys.stdout = StringIOWrapper()
 
1160
        self.addCleanup(self.restoreUIFactory)
 
1161
 
 
1162
    def restoreUIFactory(self):
 
1163
        ui.ui_factory = self.old_factory
 
1164
        sys.stdout = self.old_stdout
 
1165
 
 
1166
    def get_user_url(self, user=None, password=None):
 
1167
        """Build an url embedding user and password"""
 
1168
        url = '%s://' % self.server._url_protocol
 
1169
        if user is not None:
 
1170
            url += user
 
1171
            if password is not None:
 
1172
                url += ':' + password
 
1173
            url += '@'
 
1174
        url += '%s:%s/' % (self.server.host, self.server.port)
 
1175
        return url
 
1176
 
 
1177
    def test_no_user(self):
 
1178
        self.server.add_user('joe', 'foo')
 
1179
        t = self.get_user_transport()
 
1180
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'a')
 
1181
        # Only one 'Authentication Required' error should occur
 
1182
        self.assertEqual(1, self.server.auth_required_errors)
 
1183
 
 
1184
    def test_empty_pass(self):
 
1185
        self.server.add_user('joe', '')
 
1186
        t = self.get_user_transport('joe', '')
 
1187
        self.assertEqual('contents of a\n', t.get('a').read())
 
1188
        # Only one 'Authentication Required' error should occur
 
1189
        self.assertEqual(1, self.server.auth_required_errors)
 
1190
 
 
1191
    def test_user_pass(self):
 
1192
        self.server.add_user('joe', 'foo')
 
1193
        t = self.get_user_transport('joe', 'foo')
 
1194
        self.assertEqual('contents of a\n', t.get('a').read())
 
1195
        # Only one 'Authentication Required' error should occur
 
1196
        self.assertEqual(1, self.server.auth_required_errors)
 
1197
 
 
1198
    def test_unknown_user(self):
 
1199
        self.server.add_user('joe', 'foo')
 
1200
        t = self.get_user_transport('bill', 'foo')
 
1201
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'a')
 
1202
        # Two 'Authentication Required' errors should occur (the
 
1203
        # initial 'who are you' and 'I don't know you, who are
 
1204
        # you').
 
1205
        self.assertEqual(2, self.server.auth_required_errors)
 
1206
 
 
1207
    def test_wrong_pass(self):
 
1208
        self.server.add_user('joe', 'foo')
 
1209
        t = self.get_user_transport('joe', 'bar')
 
1210
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'a')
 
1211
        # Two 'Authentication Required' errors should occur (the
 
1212
        # initial 'who are you' and 'this is not you, who are you')
 
1213
        self.assertEqual(2, self.server.auth_required_errors)
 
1214
 
 
1215
    def test_prompt_for_password(self):
 
1216
        self.server.add_user('joe', 'foo')
 
1217
        t = self.get_user_transport('joe', None)
 
1218
        ui.ui_factory = TestUIFactory(stdin='foo\n')
 
1219
        self.assertEqual('contents of a\n',t.get('a').read())
 
1220
        # stdin should be empty
 
1221
        self.assertEqual('', ui.ui_factory.stdin.readline())
 
1222
        # And we shouldn't prompt again for a different request
 
1223
        # against the same transport.
 
1224
        self.assertEqual('contents of b\n',t.get('b').read())
 
1225
        t2 = t.clone()
 
1226
        # And neither against a clone
 
1227
        self.assertEqual('contents of b\n',t2.get('b').read())
 
1228
        # Only one 'Authentication Required' error should occur
 
1229
        self.assertEqual(1, self.server.auth_required_errors)
 
1230
 
 
1231
 
 
1232
class TestHTTPAuth(TestAuth):
 
1233
    """Test HTTP authentication schemes.
 
1234
 
 
1235
    Daughter classes MUST inherit from TestCaseWithWebserver too.
 
1236
    """
 
1237
 
 
1238
    _auth_header = 'Authorization'
 
1239
 
 
1240
    def setUp(self):
 
1241
        TestCaseWithWebserver.setUp(self)
 
1242
        self.server = self.get_readonly_server()
 
1243
        TestAuth.setUp(self)
 
1244
 
 
1245
    def get_user_transport(self, user=None, password=None):
 
1246
        return self._transport(self.get_user_url(user, password))
 
1247
 
 
1248
 
 
1249
class TestProxyAuth(TestAuth):
 
1250
    """Test proxy authentication schemes.
 
1251
 
 
1252
    Daughter classes MUST also inherit from TestCaseWithWebserver.
 
1253
    """
 
1254
    _auth_header = 'Proxy-authorization'
 
1255
 
 
1256
    def setUp(self):
 
1257
        TestCaseWithWebserver.setUp(self)
 
1258
        self.server = self.get_readonly_server()
 
1259
        self._old_env = {}
 
1260
        self.addCleanup(self._restore_env)
 
1261
        TestAuth.setUp(self)
 
1262
        # Override the contents to avoid false positives
 
1263
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
 
1264
                                  ('b', 'not proxied contents of b\n'),
 
1265
                                  ('a-proxied', 'contents of a\n'),
 
1266
                                  ('b-proxied', 'contents of b\n'),
 
1267
                                  ])
 
1268
 
 
1269
    def get_user_transport(self, user=None, password=None):
 
1270
        self._install_env({'all_proxy': self.get_user_url(user, password)})
 
1271
        return self._transport(self.server.get_url())
 
1272
 
 
1273
    def _install_env(self, env):
 
1274
        for name, value in env.iteritems():
 
1275
            self._old_env[name] = osutils.set_or_unset_env(name, value)
 
1276
 
 
1277
    def _restore_env(self):
 
1278
        for name, value in self._old_env.iteritems():
 
1279
            osutils.set_or_unset_env(name, value)
 
1280
 
 
1281
 
 
1282
class TestHTTPBasicAuth(TestHTTPAuth, TestCaseWithWebserver):
 
1283
    """Test http basic authentication scheme"""
 
1284
 
 
1285
    _transport = HttpTransport_urllib
 
1286
 
 
1287
    def create_transport_readonly_server(self):
 
1288
        return HTTPBasicAuthServer()
 
1289
 
 
1290
 
 
1291
class TestHTTPProxyBasicAuth(TestProxyAuth, TestCaseWithWebserver):
 
1292
    """Test proxy basic authentication scheme"""
 
1293
 
 
1294
    _transport = HttpTransport_urllib
 
1295
 
 
1296
    def create_transport_readonly_server(self):
 
1297
        return ProxyBasicAuthServer()
 
1298
 
 
1299
 
 
1300
class TestDigestAuth(object):
 
1301
    """Digest Authentication specific tests"""
 
1302
 
 
1303
    def test_changing_nonce(self):
 
1304
        self.server.add_user('joe', 'foo')
 
1305
        t = self.get_user_transport('joe', 'foo')
 
1306
        self.assertEqual('contents of a\n', t.get('a').read())
 
1307
        self.assertEqual('contents of b\n', t.get('b').read())
 
1308
        # Only one 'Authentication Required' error should have
 
1309
        # occured so far
 
1310
        self.assertEqual(1, self.server.auth_required_errors)
 
1311
        # The server invalidates the current nonce
 
1312
        self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
 
1313
        self.assertEqual('contents of a\n', t.get('a').read())
 
1314
        # Two 'Authentication Required' errors should occur (the
 
1315
        # initial 'who are you' and a second 'who are you' with the new nonce)
 
1316
        self.assertEqual(2, self.server.auth_required_errors)
 
1317
 
 
1318
 
 
1319
class TestHTTPDigestAuth(TestHTTPAuth, TestDigestAuth, TestCaseWithWebserver):
 
1320
    """Test http digest authentication scheme"""
 
1321
 
 
1322
    _transport = HttpTransport_urllib
 
1323
 
 
1324
    def create_transport_readonly_server(self):
 
1325
        return HTTPDigestAuthServer()
 
1326
 
 
1327
 
 
1328
class TestHTTPProxyDigestAuth(TestProxyAuth, TestDigestAuth,
 
1329
                              TestCaseWithWebserver):
 
1330
    """Test proxy digest authentication scheme"""
 
1331
 
 
1332
    _transport = HttpTransport_urllib
 
1333
 
 
1334
    def create_transport_readonly_server(self):
 
1335
        return ProxyDigestAuthServer()
 
1336