~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: John Arbash Meinel
  • Date: 2006-10-11 00:23:23 UTC
  • mfrom: (2070 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2071.
  • Revision ID: john@arbash-meinel.com-20061011002323-82ba88c293d7caff
[merge] bzr.dev 2070

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical
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
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 ?
22
21
 
23
 
import os
24
 
import select
25
22
import socket
26
 
import threading
27
23
 
28
24
import bzrlib
29
 
from bzrlib import errors
30
 
from bzrlib import osutils
31
 
from bzrlib.tests import (
32
 
    TestCase,
33
 
    TestSkipped,
34
 
    )
35
 
from bzrlib.tests.HttpServer import (
36
 
    HttpServer,
37
 
    HttpServer_PyCurl,
38
 
    HttpServer_urllib,
39
 
    )
40
 
from bzrlib.tests.HTTPTestUtil import (
41
 
    BadProtocolRequestHandler,
42
 
    BadStatusRequestHandler,
43
 
    FakeProxyRequestHandler,
44
 
    ForbiddenRequestHandler,
45
 
    InvalidStatusRequestHandler,
46
 
    NoRangeRequestHandler,
47
 
    SingleRangeRequestHandler,
48
 
    TestCaseWithTwoWebservers,
49
 
    TestCaseWithWebserver,
50
 
    WallRequestHandler,
51
 
    )
52
 
from bzrlib.transport import (
53
 
    get_transport,
54
 
    Transport,
55
 
    )
56
 
from bzrlib.transport.http import (
57
 
    extract_auth,
58
 
    HttpTransportBase,
59
 
    )
 
25
from bzrlib.errors import DependencyNotPresent
 
26
from bzrlib.tests import TestCase, TestSkipped
 
27
from bzrlib.transport import Transport
 
28
from bzrlib.transport.http import extract_auth, HttpTransportBase
60
29
from bzrlib.transport.http._urllib import HttpTransport_urllib
61
 
 
62
 
 
63
 
class FakeManager(object):
 
30
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
 
31
 
 
32
 
 
33
class FakeManager (object):
64
34
 
65
35
    def __init__(self):
66
36
        self.credentials = []
67
 
 
 
37
        
68
38
    def add_password(self, realm, host, username, password):
69
39
        self.credentials.append([realm, host, username, password])
70
40
 
71
41
 
72
 
class RecordingServer(object):
73
 
    """A fake HTTP server.
74
 
    
75
 
    It records the bytes sent to it, and replies with a 200.
76
 
    """
77
 
 
78
 
    def __init__(self, expect_body_tail=None):
79
 
        """Constructor.
80
 
 
81
 
        :type expect_body_tail: str
82
 
        :param expect_body_tail: a reply won't be sent until this string is
83
 
            received.
84
 
        """
85
 
        self._expect_body_tail = expect_body_tail
86
 
        self.host = None
87
 
        self.port = None
88
 
        self.received_bytes = ''
89
 
 
90
 
    def setUp(self):
91
 
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
92
 
        self._sock.bind(('127.0.0.1', 0))
93
 
        self.host, self.port = self._sock.getsockname()
94
 
        self._ready = threading.Event()
95
 
        self._thread = threading.Thread(target=self._accept_read_and_reply)
96
 
        self._thread.setDaemon(True)
97
 
        self._thread.start()
98
 
        self._ready.wait(5)
99
 
 
100
 
    def _accept_read_and_reply(self):
101
 
        self._sock.listen(1)
102
 
        self._ready.set()
103
 
        self._sock.settimeout(5)
104
 
        try:
105
 
            conn, address = self._sock.accept()
106
 
            # On win32, the accepted connection will be non-blocking to start
107
 
            # with because we're using settimeout.
108
 
            conn.setblocking(True)
109
 
            while not self.received_bytes.endswith(self._expect_body_tail):
110
 
                self.received_bytes += conn.recv(4096)
111
 
            conn.sendall('HTTP/1.1 200 OK\r\n')
112
 
        except socket.timeout:
113
 
            # Make sure the client isn't stuck waiting for us to e.g. accept.
114
 
            self._sock.close()
115
 
        except socket.error:
116
 
            # The client may have already closed the socket.
117
 
            pass
118
 
 
119
 
    def tearDown(self):
120
 
        try:
121
 
            self._sock.close()
122
 
        except socket.error:
123
 
            # We might have already closed it.  We don't care.
124
 
            pass
125
 
        self.host = None
126
 
        self.port = None
127
 
 
128
 
 
129
 
class TestWithTransport_pycurl(object):
130
 
    """Test case to inherit from if pycurl is present"""
131
 
 
132
 
    def _get_pycurl_maybe(self):
133
 
        try:
134
 
            from bzrlib.transport.http._pycurl import PyCurlTransport
135
 
            return PyCurlTransport
136
 
        except errors.DependencyNotPresent:
137
 
            raise TestSkipped('pycurl not present')
138
 
 
139
 
    _transport = property(_get_pycurl_maybe)
140
 
 
141
 
 
142
42
class TestHttpUrls(TestCase):
143
43
 
144
 
    # TODO: This should be moved to authorization tests once they
145
 
    # are written.
146
 
 
147
44
    def test_url_parsing(self):
148
45
        f = FakeManager()
149
46
        url = extract_auth('http://example.com', f)
152
49
        url = extract_auth('http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
153
50
        self.assertEquals('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
154
51
        self.assertEquals(1, len(f.credentials))
155
 
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'],
156
 
                          f.credentials[0])
157
 
 
158
 
 
159
 
class TestHttpTransportUrls(object):
160
 
    """Test the http urls.
161
 
 
162
 
    This MUST be used by daughter classes that also inherit from
163
 
    TestCase.
164
 
 
165
 
    We can't inherit directly from TestCase or the
166
 
    test framework will try to create an instance which cannot
167
 
    run, its implementation being incomplete.
168
 
    """
169
 
 
 
52
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'], f.credentials[0])
 
53
        
170
54
    def test_abs_url(self):
171
55
        """Construction of absolute http URLs"""
172
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
 
56
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
173
57
        eq = self.assertEqualDiff
174
 
        eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
175
 
        eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
176
 
        eq(t.abspath('.bzr'), 'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
 
58
        eq(t.abspath('.'),
 
59
           'http://bazaar-vcs.org/bzr/bzr.dev')
 
60
        eq(t.abspath('foo/bar'), 
 
61
           'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
 
62
        eq(t.abspath('.bzr'),
 
63
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
177
64
        eq(t.abspath('.bzr/1//2/./3'),
178
65
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
179
66
 
180
67
    def test_invalid_http_urls(self):
181
68
        """Trap invalid construction of urls"""
182
 
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
183
 
        self.assertRaises(ValueError, t.abspath, '.bzr/')
184
 
        t = self._transport('http://http://bazaar-vcs.org/bzr/bzr.dev/')
185
 
        self.assertRaises((errors.InvalidURL, errors.ConnectionError),
186
 
                          t.has, 'foo/bar')
 
69
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
 
70
        self.assertRaises(ValueError,
 
71
            t.abspath,
 
72
            '.bzr/')
187
73
 
188
74
    def test_http_root_urls(self):
189
75
        """Construction of URLs from server root"""
190
 
        t = self._transport('http://bzr.ozlabs.org/')
 
76
        t = HttpTransport_urllib('http://bzr.ozlabs.org/')
191
77
        eq = self.assertEqualDiff
192
78
        eq(t.abspath('.bzr/tree-version'),
193
79
           'http://bzr.ozlabs.org/.bzr/tree-version')
194
80
 
195
81
    def test_http_impl_urls(self):
196
82
        """There are servers which ask for particular clients to connect"""
197
 
        server = self._server()
198
 
        try:
199
 
            server.setUp()
200
 
            url = server.get_url()
201
 
            self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
202
 
        finally:
203
 
            server.tearDown()
204
 
 
205
 
 
206
 
class TestHttpUrls_urllib(TestHttpTransportUrls, TestCase):
207
 
    """Test http urls with urllib"""
208
 
 
209
 
    _transport = HttpTransport_urllib
210
 
    _server = HttpServer_urllib
211
 
    _qualified_prefix = 'http+urllib'
212
 
 
213
 
 
214
 
class TestHttpUrls_pycurl(TestWithTransport_pycurl, TestHttpTransportUrls,
215
 
                          TestCase):
216
 
    """Test http urls with pycurl"""
217
 
 
218
 
    _server = HttpServer_PyCurl
219
 
    _qualified_prefix = 'http+pycurl'
220
 
 
221
 
    # TODO: This should really be moved into another pycurl
222
 
    # specific test. When https tests will be implemented, take
223
 
    # this one into account.
224
 
    def test_pycurl_without_https_support(self):
225
 
        """Test that pycurl without SSL do not fail with a traceback.
226
 
 
227
 
        For the purpose of the test, we force pycurl to ignore
228
 
        https by supplying a fake version_info that do not
229
 
        support it.
230
 
        """
231
 
        try:
232
 
            import pycurl
233
 
        except ImportError:
 
83
        try:
 
84
            from bzrlib.transport.http._pycurl import HttpServer_PyCurl
 
85
            server = HttpServer_PyCurl()
 
86
            try:
 
87
                server.setUp()
 
88
                url = server.get_url()
 
89
                self.assertTrue(url.startswith('http+pycurl://'))
 
90
            finally:
 
91
                server.tearDown()
 
92
        except DependencyNotPresent:
234
93
            raise TestSkipped('pycurl not present')
235
 
        # Now that we have pycurl imported, we can fake its version_info
236
 
        # This was taken from a windows pycurl without SSL
237
 
        # (thanks to bialix)
238
 
        pycurl.version_info = lambda : (2,
239
 
                                        '7.13.2',
240
 
                                        462082,
241
 
                                        'i386-pc-win32',
242
 
                                        2576,
243
 
                                        None,
244
 
                                        0,
245
 
                                        None,
246
 
                                        ('ftp', 'gopher', 'telnet',
247
 
                                         'dict', 'ldap', 'http', 'file'),
248
 
                                        None,
249
 
                                        0,
250
 
                                        None)
251
 
        self.assertRaises(errors.DependencyNotPresent, self._transport,
252
 
                          'https://launchpad.net')
253
 
 
254
 
class TestHttpConnections(object):
255
 
    """Test the http connections.
256
 
 
257
 
    This MUST be used by daughter classes that also inherit from
258
 
    TestCaseWithWebserver.
259
 
 
260
 
    We can't inherit directly from TestCaseWithWebserver or the
261
 
    test framework will try to create an instance which cannot
262
 
    run, its implementation being incomplete.
263
 
    """
264
 
 
265
 
    def setUp(self):
266
 
        TestCaseWithWebserver.setUp(self)
 
94
 
 
95
 
 
96
class TestHttpMixins(object):
 
97
 
 
98
    def _prep_tree(self):
267
99
        self.build_tree(['xxx', 'foo/', 'foo/bar'], line_endings='binary',
268
100
                        transport=self.get_transport())
269
101
 
272
104
        t = self._transport(server.get_url())
273
105
        self.assertEqual(t.has('foo/bar'), True)
274
106
        self.assertEqual(len(server.logs), 1)
275
 
        self.assertContainsRe(server.logs[0],
 
107
        self.assertContainsRe(server.logs[0], 
276
108
            r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
277
109
 
278
110
    def test_http_has_not_found(self):
279
111
        server = self.get_readonly_server()
280
112
        t = self._transport(server.get_url())
281
113
        self.assertEqual(t.has('not-found'), False)
282
 
        self.assertContainsRe(server.logs[1],
 
114
        self.assertContainsRe(server.logs[1], 
283
115
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
284
116
 
285
117
    def test_http_get(self):
291
123
            'contents of foo/bar\n')
292
124
        self.assertEqual(len(server.logs), 1)
293
125
        self.assertTrue(server.logs[0].find(
294
 
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s'
295
 
            % bzrlib.__version__) > -1)
296
 
 
297
 
    def test_get_smart_medium(self):
298
 
        # For HTTP, get_smart_medium should return the transport object.
299
 
        server = self.get_readonly_server()
300
 
        http_transport = self._transport(server.get_url())
301
 
        medium = http_transport.get_smart_medium()
302
 
        self.assertIs(medium, http_transport)
 
126
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s' % bzrlib.__version__) > -1)
 
127
 
 
128
 
 
129
class TestHttpConnections_urllib(TestCaseWithWebserver, TestHttpMixins):
 
130
 
 
131
    _transport = HttpTransport_urllib
 
132
 
 
133
    def setUp(self):
 
134
        TestCaseWithWebserver.setUp(self)
 
135
        self._prep_tree()
303
136
 
304
137
    def test_has_on_bogus_host(self):
305
 
        # Get a free address and don't 'accept' on it, so that we
306
 
        # can be sure there is no http handler there, but set a
307
 
        # reasonable timeout to not slow down tests too much.
308
 
        default_timeout = socket.getdefaulttimeout()
 
138
        import urllib2
 
139
        # Get a random address, so that we can be sure there is no
 
140
        # http handler there.
 
141
        s = socket.socket()
 
142
        s.bind(('localhost', 0))
 
143
        t = self._transport('http://%s:%s/' % s.getsockname())
 
144
        self.assertRaises(urllib2.URLError, t.has, 'foo/bar')
 
145
 
 
146
 
 
147
class TestHttpConnections_pycurl(TestCaseWithWebserver, TestHttpMixins):
 
148
 
 
149
    def _get_pycurl_maybe(self):
309
150
        try:
310
 
            socket.setdefaulttimeout(2)
311
 
            s = socket.socket()
312
 
            s.bind(('localhost', 0))
313
 
            t = self._transport('http://%s:%s/' % s.getsockname())
314
 
            self.assertRaises(errors.ConnectionError, t.has, 'foo/bar')
315
 
        finally:
316
 
            socket.setdefaulttimeout(default_timeout)
317
 
 
318
 
 
319
 
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
320
 
    """Test http connections with urllib"""
321
 
 
322
 
    _transport = HttpTransport_urllib
323
 
 
324
 
 
325
 
 
326
 
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
327
 
                                 TestHttpConnections,
328
 
                                 TestCaseWithWebserver):
329
 
    """Test http connections with pycurl"""
 
151
            from bzrlib.transport.http._pycurl import PyCurlTransport
 
152
            return PyCurlTransport
 
153
        except DependencyNotPresent:
 
154
            raise TestSkipped('pycurl not present')
 
155
 
 
156
    _transport = property(_get_pycurl_maybe)
 
157
 
 
158
    def setUp(self):
 
159
        TestCaseWithWebserver.setUp(self)
 
160
        self._prep_tree()
 
161
 
330
162
 
331
163
 
332
164
class TestHttpTransportRegistration(TestCase):
333
165
    """Test registrations of various http implementations"""
334
166
 
335
167
    def test_http_registered(self):
 
168
        import bzrlib.transport.http._urllib
 
169
        from bzrlib.transport import get_transport
336
170
        # urlllib should always be present
337
171
        t = get_transport('http+urllib://bzr.google.com/')
338
172
        self.assertIsInstance(t, Transport)
339
 
        self.assertIsInstance(t, HttpTransport_urllib)
 
173
        self.assertIsInstance(t, bzrlib.transport.http._urllib.HttpTransport_urllib)
340
174
 
341
175
 
342
176
class TestOffsets(TestCase):
363
197
        self.assertEqual([[10, 12], [22, 26]], ranges)
364
198
 
365
199
 
366
 
class TestPost(object):
367
 
 
368
 
    def _test_post_body_is_received(self, scheme):
369
 
        server = RecordingServer(expect_body_tail='end-of-body')
370
 
        server.setUp()
371
 
        self.addCleanup(server.tearDown)
372
 
        url = '%s://%s:%s/' % (scheme, server.host, server.port)
373
 
        try:
374
 
            http_transport = get_transport(url)
375
 
        except errors.UnsupportedProtocol:
376
 
            raise TestSkipped('%s not available' % scheme)
377
 
        code, response = http_transport._post('abc def end-of-body')
378
 
        self.assertTrue(
379
 
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
380
 
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
381
 
        # The transport should not be assuming that the server can accept
382
 
        # chunked encoding the first time it connects, because HTTP/1.1, so we
383
 
        # check for the literal string.
384
 
        self.assertTrue(
385
 
            server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
386
 
 
387
 
 
388
 
class TestPost_urllib(TestCase, TestPost):
389
 
    """TestPost for urllib implementation"""
390
 
 
391
 
    _transport = HttpTransport_urllib
392
 
 
393
 
    def test_post_body_is_received_urllib(self):
394
 
        self._test_post_body_is_received('http+urllib')
395
 
 
396
 
 
397
 
class TestPost_pycurl(TestWithTransport_pycurl, TestCase, TestPost):
398
 
    """TestPost for pycurl implementation"""
399
 
 
400
 
    def test_post_body_is_received_pycurl(self):
401
 
        self._test_post_body_is_received('http+pycurl')
402
 
 
403
 
 
404
200
class TestRangeHeader(TestCase):
405
201
    """Test range_header method"""
406
202
 
424
220
        self.check_header('0-9,300-5000,-50',
425
221
                          ranges=[(0,9), (300,5000)],
426
222
                          tail=50)
427
 
 
428
 
 
429
 
class TestWallServer(object):
430
 
    """Tests exceptions during the connection phase"""
431
 
 
432
 
    def create_transport_readonly_server(self):
433
 
        return HttpServer(WallRequestHandler)
434
 
 
435
 
    def test_http_has(self):
436
 
        server = self.get_readonly_server()
437
 
        t = self._transport(server.get_url())
438
 
        # Unfortunately httplib (see HTTPResponse._read_status
439
 
        # for details) make no distinction between a closed
440
 
        # socket and badly formatted status line, so we can't
441
 
        # just test for ConnectionError, we have to test
442
 
        # InvalidHttpResponse too.
443
 
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
444
 
                          t.has, 'foo/bar')
445
 
 
446
 
    def test_http_get(self):
447
 
        server = self.get_readonly_server()
448
 
        t = self._transport(server.get_url())
449
 
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
450
 
                          t.get, 'foo/bar')
451
 
 
452
 
 
453
 
class TestWallServer_urllib(TestWallServer, TestCaseWithWebserver):
454
 
    """Tests "wall" server for urllib implementation"""
455
 
 
456
 
    _transport = HttpTransport_urllib
457
 
 
458
 
 
459
 
class TestWallServer_pycurl(TestWithTransport_pycurl,
460
 
                            TestWallServer,
461
 
                            TestCaseWithWebserver):
462
 
    """Tests "wall" server for pycurl implementation"""
463
 
 
464
 
 
465
 
class TestBadStatusServer(object):
466
 
    """Tests bad status from server."""
467
 
 
468
 
    def create_transport_readonly_server(self):
469
 
        return HttpServer(BadStatusRequestHandler)
470
 
 
471
 
    def test_http_has(self):
472
 
        server = self.get_readonly_server()
473
 
        t = self._transport(server.get_url())
474
 
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
475
 
 
476
 
    def test_http_get(self):
477
 
        server = self.get_readonly_server()
478
 
        t = self._transport(server.get_url())
479
 
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
480
 
 
481
 
 
482
 
class TestBadStatusServer_urllib(TestBadStatusServer, TestCaseWithWebserver):
483
 
    """Tests bad status server for urllib implementation"""
484
 
 
485
 
    _transport = HttpTransport_urllib
486
 
 
487
 
 
488
 
class TestBadStatusServer_pycurl(TestWithTransport_pycurl,
489
 
                                 TestBadStatusServer,
490
 
                                 TestCaseWithWebserver):
491
 
    """Tests bad status server for pycurl implementation"""
492
 
 
493
 
 
494
 
class TestInvalidStatusServer(TestBadStatusServer):
495
 
    """Tests invalid status from server.
496
 
 
497
 
    Both implementations raises the same error as for a bad status.
498
 
    """
499
 
 
500
 
    def create_transport_readonly_server(self):
501
 
        return HttpServer(InvalidStatusRequestHandler)
502
 
 
503
 
 
504
 
class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
505
 
                                     TestCaseWithWebserver):
506
 
    """Tests invalid status server for urllib implementation"""
507
 
 
508
 
    _transport = HttpTransport_urllib
509
 
 
510
 
 
511
 
class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
512
 
                                     TestInvalidStatusServer,
513
 
                                     TestCaseWithWebserver):
514
 
    """Tests invalid status server for pycurl implementation"""
515
 
 
516
 
 
517
 
class TestBadProtocolServer(object):
518
 
    """Tests bad protocol from server."""
519
 
 
520
 
    def create_transport_readonly_server(self):
521
 
        return HttpServer(BadProtocolRequestHandler)
522
 
 
523
 
    def test_http_has(self):
524
 
        server = self.get_readonly_server()
525
 
        t = self._transport(server.get_url())
526
 
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
527
 
 
528
 
    def test_http_get(self):
529
 
        server = self.get_readonly_server()
530
 
        t = self._transport(server.get_url())
531
 
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
532
 
 
533
 
 
534
 
class TestBadProtocolServer_urllib(TestBadProtocolServer,
535
 
                                   TestCaseWithWebserver):
536
 
    """Tests bad protocol server for urllib implementation"""
537
 
 
538
 
    _transport = HttpTransport_urllib
539
 
 
540
 
# curl don't check the protocol version
541
 
#class TestBadProtocolServer_pycurl(TestWithTransport_pycurl,
542
 
#                                   TestBadProtocolServer,
543
 
#                                   TestCaseWithWebserver):
544
 
#    """Tests bad protocol server for pycurl implementation"""
545
 
 
546
 
 
547
 
class TestForbiddenServer(object):
548
 
    """Tests forbidden server"""
549
 
 
550
 
    def create_transport_readonly_server(self):
551
 
        return HttpServer(ForbiddenRequestHandler)
552
 
 
553
 
    def test_http_has(self):
554
 
        server = self.get_readonly_server()
555
 
        t = self._transport(server.get_url())
556
 
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
557
 
 
558
 
    def test_http_get(self):
559
 
        server = self.get_readonly_server()
560
 
        t = self._transport(server.get_url())
561
 
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
562
 
 
563
 
 
564
 
class TestForbiddenServer_urllib(TestForbiddenServer, TestCaseWithWebserver):
565
 
    """Tests forbidden server for urllib implementation"""
566
 
 
567
 
    _transport = HttpTransport_urllib
568
 
 
569
 
 
570
 
class TestForbiddenServer_pycurl(TestWithTransport_pycurl,
571
 
                                 TestForbiddenServer,
572
 
                                 TestCaseWithWebserver):
573
 
    """Tests forbidden server for pycurl implementation"""
574
 
 
575
 
 
576
 
class TestRecordingServer(TestCase):
577
 
 
578
 
    def test_create(self):
579
 
        server = RecordingServer(expect_body_tail=None)
580
 
        self.assertEqual('', server.received_bytes)
581
 
        self.assertEqual(None, server.host)
582
 
        self.assertEqual(None, server.port)
583
 
 
584
 
    def test_setUp_and_tearDown(self):
585
 
        server = RecordingServer(expect_body_tail=None)
586
 
        server.setUp()
587
 
        try:
588
 
            self.assertNotEqual(None, server.host)
589
 
            self.assertNotEqual(None, server.port)
590
 
        finally:
591
 
            server.tearDown()
592
 
        self.assertEqual(None, server.host)
593
 
        self.assertEqual(None, server.port)
594
 
 
595
 
    def test_send_receive_bytes(self):
596
 
        server = RecordingServer(expect_body_tail='c')
597
 
        server.setUp()
598
 
        self.addCleanup(server.tearDown)
599
 
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
600
 
        sock.connect((server.host, server.port))
601
 
        sock.sendall('abc')
602
 
        self.assertEqual('HTTP/1.1 200 OK\r\n',
603
 
                         osutils.recv_all(sock, 4096))
604
 
        self.assertEqual('abc', server.received_bytes)
605
 
 
606
 
 
607
 
class TestRangeRequestServer(object):
608
 
    """Tests readv requests against server.
609
 
 
610
 
    This MUST be used by daughter classes that also inherit from
611
 
    TestCaseWithWebserver.
612
 
 
613
 
    We can't inherit directly from TestCaseWithWebserver or the
614
 
    test framework will try to create an instance which cannot
615
 
    run, its implementation being incomplete.
616
 
    """
617
 
 
618
 
    def setUp(self):
619
 
        TestCaseWithWebserver.setUp(self)
620
 
        self.build_tree_contents([('a', '0123456789')],)
621
 
 
622
 
    def test_readv(self):
623
 
        server = self.get_readonly_server()
624
 
        t = self._transport(server.get_url())
625
 
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
626
 
        self.assertEqual(l[0], (0, '0'))
627
 
        self.assertEqual(l[1], (1, '1'))
628
 
        self.assertEqual(l[2], (3, '34'))
629
 
        self.assertEqual(l[3], (9, '9'))
630
 
 
631
 
    def test_readv_out_of_order(self):
632
 
        server = self.get_readonly_server()
633
 
        t = self._transport(server.get_url())
634
 
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
635
 
        self.assertEqual(l[0], (1, '1'))
636
 
        self.assertEqual(l[1], (9, '9'))
637
 
        self.assertEqual(l[2], (0, '0'))
638
 
        self.assertEqual(l[3], (3, '34'))
639
 
 
640
 
    def test_readv_invalid_ranges(self):
641
 
        server = self.get_readonly_server()
642
 
        t = self._transport(server.get_url())
643
 
 
644
 
        # This is intentionally reading off the end of the file
645
 
        # since we are sure that it cannot get there
646
 
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
647
 
                              t.readv, 'a', [(1,1), (8,10)])
648
 
 
649
 
        # This is trying to seek past the end of the file, it should
650
 
        # also raise a special error
651
 
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
652
 
                              t.readv, 'a', [(12,2)])
653
 
 
654
 
 
655
 
class TestSingleRangeRequestServer(TestRangeRequestServer):
656
 
    """Test readv against a server which accept only single range requests"""
657
 
 
658
 
    def create_transport_readonly_server(self):
659
 
        return HttpServer(SingleRangeRequestHandler)
660
 
 
661
 
 
662
 
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
663
 
                                          TestCaseWithWebserver):
664
 
    """Tests single range requests accepting server for urllib implementation"""
665
 
 
666
 
    _transport = HttpTransport_urllib
667
 
 
668
 
 
669
 
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
670
 
                                          TestSingleRangeRequestServer,
671
 
                                          TestCaseWithWebserver):
672
 
    """Tests single range requests accepting server for pycurl implementation"""
673
 
 
674
 
 
675
 
class TestNoRangeRequestServer(TestRangeRequestServer):
676
 
    """Test readv against a server which do not accept range requests"""
677
 
 
678
 
    def create_transport_readonly_server(self):
679
 
        return HttpServer(NoRangeRequestHandler)
680
 
 
681
 
 
682
 
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
683
 
                                      TestCaseWithWebserver):
684
 
    """Tests range requests refusing server for urllib implementation"""
685
 
 
686
 
    _transport = HttpTransport_urllib
687
 
 
688
 
 
689
 
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
690
 
                               TestNoRangeRequestServer,
691
 
                               TestCaseWithWebserver):
692
 
    """Tests range requests refusing server for pycurl implementation"""
693
 
 
694
 
 
695
 
class TestHttpProxyWhiteBox(TestCase):
696
 
    """Whitebox test proxy http authorization.
697
 
 
698
 
    These tests concern urllib implementation only.
699
 
    """
700
 
 
701
 
    def setUp(self):
702
 
        TestCase.setUp(self)
703
 
        self._old_env = {}
704
 
 
705
 
    def tearDown(self):
706
 
        self._restore_env()
707
 
 
708
 
    def _set_and_capture_env_var(self, name, new_value):
709
 
        """Set an environment variable, and reset it when finished."""
710
 
        self._old_env[name] = osutils.set_or_unset_env(name, new_value)
711
 
 
712
 
    def _install_env(self, env):
713
 
        for name, value in env.iteritems():
714
 
            self._set_and_capture_env_var(name, value)
715
 
 
716
 
    def _restore_env(self):
717
 
        for name, value in self._old_env.iteritems():
718
 
            osutils.set_or_unset_env(name, value)
719
 
 
720
 
    def _proxied_request(self):
721
 
        from bzrlib.transport.http._urllib2_wrappers import (
722
 
            ProxyHandler,
723
 
            Request,
724
 
            )
725
 
 
726
 
        handler = ProxyHandler()
727
 
        request = Request('GET','http://baz/buzzle')
728
 
        handler.set_proxy(request, 'http')
729
 
        return request
730
 
 
731
 
    def test_empty_user(self):
732
 
        self._install_env({'http_proxy': 'http://bar.com'})
733
 
        request = self._proxied_request()
734
 
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
735
 
 
736
 
    def test_empty_pass(self):
737
 
        self._install_env({'http_proxy': 'http://joe@bar.com'})
738
 
        request = self._proxied_request()
739
 
        self.assertEqual('Basic ' + 'joe:'.encode('base64').strip(),
740
 
                         request.headers['Proxy-authorization'])
741
 
    def test_user_pass(self):
742
 
        self._install_env({'http_proxy': 'http://joe:foo@bar.com'})
743
 
        request = self._proxied_request()
744
 
        self.assertEqual('Basic ' + 'joe:foo'.encode('base64').strip(),
745
 
                         request.headers['Proxy-authorization'])
746
 
 
747
 
    def test_invalid_proxy(self):
748
 
        """A proxy env variable without scheme"""
749
 
        self._install_env({'http_proxy': 'host:1234'})
750
 
        self.assertRaises(errors.InvalidURL, self._proxied_request)
751
 
 
752
 
 
753
 
class TestProxyHttpServer(object):
754
 
    """Tests proxy server.
755
 
 
756
 
    This MUST be used by daughter classes that also inherit from
757
 
    TestCaseWithTwoWebservers.
758
 
 
759
 
    We can't inherit directly from TestCaseWithTwoWebservers or
760
 
    the test framework will try to create an instance which
761
 
    cannot run, its implementation being incomplete.
762
 
 
763
 
    Be aware that we do not setup a real proxy here. Instead, we
764
 
    check that the *connection* goes through the proxy by serving
765
 
    different content (the faked proxy server append '-proxied'
766
 
    to the file names).
767
 
    """
768
 
 
769
 
    # FIXME: We don't have an https server available, so we don't
770
 
    # test https connections.
771
 
 
772
 
    # FIXME: Once the test suite is better fitted to test
773
 
    # authorization schemes, test proxy authorizations too (see
774
 
    # bug #83954).
775
 
 
776
 
    def setUp(self):
777
 
        TestCaseWithTwoWebservers.setUp(self)
778
 
        self.build_tree_contents([('foo', 'contents of foo\n'),
779
 
                                  ('foo-proxied', 'proxied contents of foo\n')])
780
 
        # Let's setup some attributes for tests
781
 
        self.server = self.get_readonly_server()
782
 
        # FIXME: We should not rely on 'localhost' being the hostname
783
 
        self.proxy_address = 'localhost:%d' % 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 HttpServer(FakeProxyRequestHandler)
795
 
 
796
 
    def _set_and_capture_env_var(self, name, new_value):
797
 
        """Set an environment variable, and reset it when finished."""
798
 
        self._old_env[name] = osutils.set_or_unset_env(name, new_value)
799
 
 
800
 
    def _install_env(self, env):
801
 
        for name, value in env.iteritems():
802
 
            self._set_and_capture_env_var(name, value)
803
 
 
804
 
    def _restore_env(self):
805
 
        for name, value in self._old_env.iteritems():
806
 
            osutils.set_or_unset_env(name, value)
807
 
 
808
 
    def proxied_in_env(self, env):
809
 
        self._install_env(env)
810
 
        url = self.server.get_url()
811
 
        t = self._transport(url)
812
 
        try:
813
 
            self.assertEqual(t.get('foo').read(), 'proxied contents of foo\n')
814
 
        finally:
815
 
            self._restore_env()
816
 
 
817
 
    def not_proxied_in_env(self, env):
818
 
        self._install_env(env)
819
 
        url = self.server.get_url()
820
 
        t = self._transport(url)
821
 
        try:
822
 
            self.assertEqual(t.get('foo').read(), 'contents of foo\n')
823
 
        finally:
824
 
            self._restore_env()
825
 
 
826
 
    def test_http_proxy(self):
827
 
        self.proxied_in_env({'http_proxy': self.proxy_url})
828
 
 
829
 
    def test_HTTP_PROXY(self):
830
 
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
831
 
 
832
 
    def test_all_proxy(self):
833
 
        self.proxied_in_env({'all_proxy': self.proxy_url})
834
 
 
835
 
    def test_ALL_PROXY(self):
836
 
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
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_HTTP_PROXY_with_NO_PROXY(self):
843
 
        self.not_proxied_in_env({'HTTP_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_ALL_PROXY_with_NO_PROXY(self):
851
 
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
852
 
                                 'NO_PROXY': self.no_proxy_host})
853
 
 
854
 
    def test_http_proxy_without_scheme(self):
855
 
        self.assertRaises(errors.InvalidURL,
856
 
                          self.proxied_in_env,
857
 
                          {'http_proxy': self.proxy_address})
858
 
 
859
 
 
860
 
class TestProxyHttpServer_urllib(TestProxyHttpServer,
861
 
                                 TestCaseWithTwoWebservers):
862
 
    """Tests proxy server for urllib implementation"""
863
 
 
864
 
    _transport = HttpTransport_urllib
865
 
 
866
 
 
867
 
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
868
 
                                 TestProxyHttpServer,
869
 
                                 TestCaseWithTwoWebservers):
870
 
    """Tests proxy server for pycurl implementation"""
871
 
 
872
 
    def setUp(self):
873
 
        TestProxyHttpServer.setUp(self)
874
 
        # Oh my ! pycurl does not check for the port as part of
875
 
        # no_proxy :-( So we just test the host part
876
 
        self.no_proxy_host = 'localhost'
877
 
 
878
 
    def test_HTTP_PROXY(self):
879
 
        # pycurl do not check HTTP_PROXY for security reasons
880
 
        # (for use in a CGI context that we do not care
881
 
        # about. Should we ?)
882
 
        raise TestSkipped()
883
 
 
884
 
    def test_HTTP_PROXY_with_NO_PROXY(self):
885
 
        raise TestSkipped()
886
 
 
887
 
    def test_http_proxy_without_scheme(self):
888
 
        # pycurl *ignores* invalid proxy env variables. If that
889
 
        # ever change in the future, this test will fail
890
 
        # indicating that pycurl do not ignore anymore such
891
 
        # variables.
892
 
        self.not_proxied_in_env({'http_proxy': self.proxy_address})
893
 
 
894
 
 
895
 
class TestRanges(object):
896
 
    """Test the Range header in GET methods..
897
 
 
898
 
    This MUST be used by daughter classes that also inherit from
899
 
    TestCaseWithWebserver.
900
 
 
901
 
    We can't inherit directly from TestCaseWithWebserver or the
902
 
    test framework will try to create an instance which cannot
903
 
    run, its implementation being incomplete.
904
 
    """
905
 
 
906
 
    def setUp(self):
907
 
        TestCaseWithWebserver.setUp(self)
908
 
        self.build_tree_contents([('a', '0123456789')],)
909
 
        server = self.get_readonly_server()
910
 
        self.transport = self._transport(server.get_url())
911
 
 
912
 
    def _file_contents(self, relpath, ranges, tail_amount=0):
913
 
         code, data = self.transport._get(relpath, ranges)
914
 
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
915
 
         for start, end in ranges:
916
 
             data.seek(start)
917
 
             yield data.read(end - start + 1)
918
 
 
919
 
    def _file_tail(self, relpath, tail_amount):
920
 
         code, data = self.transport._get(relpath, [], tail_amount)
921
 
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
922
 
         data.seek(-tail_amount + 1, 2)
923
 
         return data.read(tail_amount)
924
 
 
925
 
    def test_range_header(self):
926
 
        # Valid ranges
927
 
        map(self.assertEqual,['0', '234'],
928
 
            list(self._file_contents('a', [(0,0), (2,4)])),)
929
 
        # Tail
930
 
        self.assertEqual('789', self._file_tail('a', 3))
931
 
        # Syntactically invalid range
932
 
        self.assertRaises(errors.InvalidRange,
933
 
                          self.transport._get, 'a', [(4, 3)])
934
 
        # Semantically invalid range
935
 
        self.assertRaises(errors.InvalidRange,
936
 
                          self.transport._get, 'a', [(42, 128)])
937
 
 
938
 
 
939
 
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
940
 
    """Test the Range header in GET methods for urllib implementation"""
941
 
 
942
 
    _transport = HttpTransport_urllib
943
 
 
944
 
 
945
 
class TestRanges_pycurl(TestWithTransport_pycurl,
946
 
                        TestRanges,
947
 
                        TestCaseWithWebserver):
948
 
    """Test the Range header in GET methods for pycurl implementation"""