/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http.py

  • Committer: Vincent Ladeuil
  • Date: 2007-02-11 16:06:13 UTC
  • mto: (2323.7.1 redirection)
  • mto: This revision was merged to the branch mainline in revision 2390.
  • Revision ID: v.ladeuil+lp@free.fr-20070211160613-9k1vwo0e1x0si26z
Http redirections are not followed by default. Do not use hints
anymore.

* bzrlib/transport/smart.py:
(SmartTransport.get): Do not use hints.

* bzrlib/transport/sftp.py:
(SFTPTransport.get): Do not use hints.

* bzrlib/transport/memory.py:
(MemoryTransport.get): Do not use hints.

* bzrlib/transport/local.py:
(LocalTransport.get): Do not use hints.

* bzrlib/transport/http/_urllib2_wrappers.py:
(Request.__init__): Redirections are *not* followed by default.

* bzrlib/transport/http/_urllib.py:
(HttpTransport_urllib._get): Do not use hints.

* bzrlib/transport/http/_pycurl.py:
(PyCurlTransport._get): Do not use hints.

* bzrlib/transport/http/__init__.py:
(HttpTransportBase.get, HttpTransportBase._get): Do not use hints.
Fix _get doc anyway.

* bzrlib/transport/ftp.py:
(FtpTransport.get): Do not use hints.

* bzrlib/transport/fakevfat.py:
(FakeVFATTransportDecorator.get): Do not use hints.

* bzrlib/transport/decorator.py
(TransportDecorator.get): Do not use hints.

* bzrlib/transport/chroot.py:
(ChrootTransportDecorator.get): Do not use hints.

* bzrlib/tests/test_transport_hints.py:
Deleted.

* bzrlib/tests/__init__.py:
(test_suite): Do not test hints.

* bzrlib/errors.py:
(UnknownHint): Deleted.

* bzrlib/bzrdir.py:
(BzrDirMetaFormat1.probe_transport): Do not use hints.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
# FIXME: This test should be repeated for each available http client
 
18
# implementation; at the moment we have urllib and pycurl.
 
19
 
 
20
# TODO: Should be renamed to bzrlib.transport.http.tests?
 
21
# TODO: What about renaming to bzrlib.tests.transport.http ?
 
22
 
 
23
import os
 
24
import select
 
25
import socket
 
26
import threading
 
27
 
 
28
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
    HTTPServerRedirecting,
 
46
    InvalidStatusRequestHandler,
 
47
    NoRangeRequestHandler,
 
48
    SingleRangeRequestHandler,
 
49
    TestCaseWithTwoWebservers,
 
50
    TestCaseWithWebserver,
 
51
    WallRequestHandler,
 
52
    )
 
53
from bzrlib.transport import (
 
54
    get_transport,
 
55
    Transport,
 
56
    )
 
57
from bzrlib.transport.http import (
 
58
    extract_auth,
 
59
    HttpTransportBase,
 
60
    )
 
61
from bzrlib.transport.http._urllib import HttpTransport_urllib
 
62
 
 
63
 
 
64
class FakeManager(object):
 
65
 
 
66
    def __init__(self):
 
67
        self.credentials = []
 
68
 
 
69
    def add_password(self, realm, host, username, password):
 
70
        self.credentials.append([realm, host, username, password])
 
71
 
 
72
 
 
73
class RecordingServer(object):
 
74
    """A fake HTTP server.
 
75
    
 
76
    It records the bytes sent to it, and replies with a 200.
 
77
    """
 
78
 
 
79
    def __init__(self, expect_body_tail=None):
 
80
        """Constructor.
 
81
 
 
82
        :type expect_body_tail: str
 
83
        :param expect_body_tail: a reply won't be sent until this string is
 
84
            received.
 
85
        """
 
86
        self._expect_body_tail = expect_body_tail
 
87
        self.host = None
 
88
        self.port = None
 
89
        self.received_bytes = ''
 
90
 
 
91
    def setUp(self):
 
92
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
93
        self._sock.bind(('127.0.0.1', 0))
 
94
        self.host, self.port = self._sock.getsockname()
 
95
        self._ready = threading.Event()
 
96
        self._thread = threading.Thread(target=self._accept_read_and_reply)
 
97
        self._thread.setDaemon(True)
 
98
        self._thread.start()
 
99
        self._ready.wait(5)
 
100
 
 
101
    def _accept_read_and_reply(self):
 
102
        self._sock.listen(1)
 
103
        self._ready.set()
 
104
        self._sock.settimeout(5)
 
105
        try:
 
106
            conn, address = self._sock.accept()
 
107
            # On win32, the accepted connection will be non-blocking to start
 
108
            # with because we're using settimeout.
 
109
            conn.setblocking(True)
 
110
            while not self.received_bytes.endswith(self._expect_body_tail):
 
111
                self.received_bytes += conn.recv(4096)
 
112
            conn.sendall('HTTP/1.1 200 OK\r\n')
 
113
        except socket.timeout:
 
114
            # Make sure the client isn't stuck waiting for us to e.g. accept.
 
115
            self._sock.close()
 
116
        except socket.error:
 
117
            # The client may have already closed the socket.
 
118
            pass
 
119
 
 
120
    def tearDown(self):
 
121
        try:
 
122
            self._sock.close()
 
123
        except socket.error:
 
124
            # We might have already closed it.  We don't care.
 
125
            pass
 
126
        self.host = None
 
127
        self.port = None
 
128
 
 
129
 
 
130
class TestHttpUrls(TestCase):
 
131
 
 
132
    # FIXME: Some of these tests should be done for both
 
133
    # implementations
 
134
 
 
135
    def test_url_parsing(self):
 
136
        f = FakeManager()
 
137
        url = extract_auth('http://example.com', f)
 
138
        self.assertEquals('http://example.com', url)
 
139
        self.assertEquals(0, len(f.credentials))
 
140
        url = extract_auth('http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
 
141
        self.assertEquals('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
 
142
        self.assertEquals(1, len(f.credentials))
 
143
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'],
 
144
                          f.credentials[0])
 
145
 
 
146
    def test_abs_url(self):
 
147
        """Construction of absolute http URLs"""
 
148
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
 
149
        eq = self.assertEqualDiff
 
150
        eq(t.abspath('.'),
 
151
           'http://bazaar-vcs.org/bzr/bzr.dev')
 
152
        eq(t.abspath('foo/bar'),
 
153
           'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
 
154
        eq(t.abspath('.bzr'),
 
155
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
 
156
        eq(t.abspath('.bzr/1//2/./3'),
 
157
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
 
158
 
 
159
    def test_invalid_http_urls(self):
 
160
        """Trap invalid construction of urls"""
 
161
        t = HttpTransport_urllib('http://bazaar-vcs.org/bzr/bzr.dev/')
 
162
        self.assertRaises(ValueError,
 
163
            t.abspath,
 
164
            '.bzr/')
 
165
        t = HttpTransport_urllib('http://http://bazaar-vcs.org/bzr/bzr.dev/')
 
166
        self.assertRaises(errors.InvalidURL, t.has, 'foo/bar')
 
167
 
 
168
    def test_http_root_urls(self):
 
169
        """Construction of URLs from server root"""
 
170
        t = HttpTransport_urllib('http://bzr.ozlabs.org/')
 
171
        eq = self.assertEqualDiff
 
172
        eq(t.abspath('.bzr/tree-version'),
 
173
           'http://bzr.ozlabs.org/.bzr/tree-version')
 
174
 
 
175
    def test_http_impl_urls(self):
 
176
        """There are servers which ask for particular clients to connect"""
 
177
        server = HttpServer_PyCurl()
 
178
        try:
 
179
            server.setUp()
 
180
            url = server.get_url()
 
181
            self.assertTrue(url.startswith('http+pycurl://'))
 
182
        finally:
 
183
            server.tearDown()
 
184
 
 
185
 
 
186
class TestHttpConnections(object):
 
187
    """Test the http connections.
 
188
 
 
189
    This MUST be used by daughter classes that also inherit from
 
190
    TestCaseWithWebserver.
 
191
 
 
192
    We can't inherit directly from TestCaseWithWebserver or the
 
193
    test framework will try to create an instance which cannot
 
194
    run, its implementation being incomplete.
 
195
    """
 
196
 
 
197
    def setUp(self):
 
198
        TestCaseWithWebserver.setUp(self)
 
199
        self.build_tree(['xxx', 'foo/', 'foo/bar'], line_endings='binary',
 
200
                        transport=self.get_transport())
 
201
 
 
202
    def test_http_has(self):
 
203
        server = self.get_readonly_server()
 
204
        t = self._transport(server.get_url())
 
205
        self.assertEqual(t.has('foo/bar'), True)
 
206
        self.assertEqual(len(server.logs), 1)
 
207
        self.assertContainsRe(server.logs[0],
 
208
            r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
 
209
 
 
210
    def test_http_has_not_found(self):
 
211
        server = self.get_readonly_server()
 
212
        t = self._transport(server.get_url())
 
213
        self.assertEqual(t.has('not-found'), False)
 
214
        self.assertContainsRe(server.logs[1],
 
215
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
 
216
 
 
217
    def test_http_get(self):
 
218
        server = self.get_readonly_server()
 
219
        t = self._transport(server.get_url())
 
220
        fp = t.get('foo/bar')
 
221
        self.assertEqualDiff(
 
222
            fp.read(),
 
223
            'contents of foo/bar\n')
 
224
        self.assertEqual(len(server.logs), 1)
 
225
        self.assertTrue(server.logs[0].find(
 
226
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s'
 
227
            % bzrlib.__version__) > -1)
 
228
 
 
229
    def test_get_smart_medium(self):
 
230
        # For HTTP, get_smart_medium should return the transport object.
 
231
        server = self.get_readonly_server()
 
232
        http_transport = self._transport(server.get_url())
 
233
        medium = http_transport.get_smart_medium()
 
234
        self.assertIs(medium, http_transport)
 
235
 
 
236
    def test_has_on_bogus_host(self):
 
237
        # Get a free address and don't 'accept' on it, so that we
 
238
        # can be sure there is no http handler there, but set a
 
239
        # reasonable timeout to not slow down tests too much.
 
240
        default_timeout = socket.getdefaulttimeout()
 
241
        try:
 
242
            socket.setdefaulttimeout(2)
 
243
            s = socket.socket()
 
244
            s.bind(('localhost', 0))
 
245
            t = self._transport('http://%s:%s/' % s.getsockname())
 
246
            self.assertRaises(errors.ConnectionError, t.has, 'foo/bar')
 
247
        finally:
 
248
            socket.setdefaulttimeout(default_timeout)
 
249
 
 
250
 
 
251
class TestWithTransport_pycurl(object):
 
252
    """Test case to inherit from if pycurl is present"""
 
253
 
 
254
    def _get_pycurl_maybe(self):
 
255
        try:
 
256
            from bzrlib.transport.http._pycurl import PyCurlTransport
 
257
            return PyCurlTransport
 
258
        except errors.DependencyNotPresent:
 
259
            raise TestSkipped('pycurl not present')
 
260
 
 
261
    _transport = property(_get_pycurl_maybe)
 
262
 
 
263
 
 
264
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
 
265
    """Test http connections with urllib"""
 
266
 
 
267
    _transport = HttpTransport_urllib
 
268
 
 
269
 
 
270
 
 
271
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
 
272
                                 TestHttpConnections,
 
273
                                 TestCaseWithWebserver):
 
274
    """Test http connections with pycurl"""
 
275
 
 
276
 
 
277
class TestHttpTransportRegistration(TestCase):
 
278
    """Test registrations of various http implementations"""
 
279
 
 
280
    def test_http_registered(self):
 
281
        # urlllib should always be present
 
282
        t = get_transport('http+urllib://bzr.google.com/')
 
283
        self.assertIsInstance(t, Transport)
 
284
        self.assertIsInstance(t, HttpTransport_urllib)
 
285
 
 
286
 
 
287
class TestOffsets(TestCase):
 
288
    """Test offsets_to_ranges method"""
 
289
 
 
290
    def test_offsets_to_ranges_simple(self):
 
291
        to_range = HttpTransportBase.offsets_to_ranges
 
292
        ranges = to_range([(10, 1)])
 
293
        self.assertEqual([[10, 10]], ranges)
 
294
 
 
295
        ranges = to_range([(0, 1), (1, 1)])
 
296
        self.assertEqual([[0, 1]], ranges)
 
297
 
 
298
        ranges = to_range([(1, 1), (0, 1)])
 
299
        self.assertEqual([[0, 1]], ranges)
 
300
 
 
301
    def test_offset_to_ranges_overlapped(self):
 
302
        to_range = HttpTransportBase.offsets_to_ranges
 
303
 
 
304
        ranges = to_range([(10, 1), (20, 2), (22, 5)])
 
305
        self.assertEqual([[10, 10], [20, 26]], ranges)
 
306
 
 
307
        ranges = to_range([(10, 1), (11, 2), (22, 5)])
 
308
        self.assertEqual([[10, 12], [22, 26]], ranges)
 
309
 
 
310
 
 
311
class TestPost(object):
 
312
 
 
313
    def _test_post_body_is_received(self, scheme):
 
314
        server = RecordingServer(expect_body_tail='end-of-body')
 
315
        server.setUp()
 
316
        self.addCleanup(server.tearDown)
 
317
        url = '%s://%s:%s/' % (scheme, server.host, server.port)
 
318
        try:
 
319
            http_transport = get_transport(url)
 
320
        except errors.UnsupportedProtocol:
 
321
            raise TestSkipped('%s not available' % scheme)
 
322
        code, response = http_transport._post('abc def end-of-body')
 
323
        self.assertTrue(
 
324
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
 
325
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
 
326
        # The transport should not be assuming that the server can accept
 
327
        # chunked encoding the first time it connects, because HTTP/1.1, so we
 
328
        # check for the literal string.
 
329
        self.assertTrue(
 
330
            server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
 
331
 
 
332
 
 
333
class TestPost_urllib(TestCase, TestPost):
 
334
    """TestPost for urllib implementation"""
 
335
 
 
336
    _transport = HttpTransport_urllib
 
337
 
 
338
    def test_post_body_is_received_urllib(self):
 
339
        self._test_post_body_is_received('http+urllib')
 
340
 
 
341
 
 
342
class TestPost_pycurl(TestWithTransport_pycurl, TestCase, TestPost):
 
343
    """TestPost for pycurl implementation"""
 
344
 
 
345
    def test_post_body_is_received_pycurl(self):
 
346
        self._test_post_body_is_received('http+pycurl')
 
347
 
 
348
 
 
349
class TestRangeHeader(TestCase):
 
350
    """Test range_header method"""
 
351
 
 
352
    def check_header(self, value, ranges=[], tail=0):
 
353
        range_header = HttpTransportBase.range_header
 
354
        self.assertEqual(value, range_header(ranges, tail))
 
355
 
 
356
    def test_range_header_single(self):
 
357
        self.check_header('0-9', ranges=[[0,9]])
 
358
        self.check_header('100-109', ranges=[[100,109]])
 
359
 
 
360
    def test_range_header_tail(self):
 
361
        self.check_header('-10', tail=10)
 
362
        self.check_header('-50', tail=50)
 
363
 
 
364
    def test_range_header_multi(self):
 
365
        self.check_header('0-9,100-200,300-5000',
 
366
                          ranges=[(0,9), (100, 200), (300,5000)])
 
367
 
 
368
    def test_range_header_mixed(self):
 
369
        self.check_header('0-9,300-5000,-50',
 
370
                          ranges=[(0,9), (300,5000)],
 
371
                          tail=50)
 
372
 
 
373
 
 
374
class TestWallServer(object):
 
375
    """Tests exceptions during the connection phase"""
 
376
 
 
377
    def create_transport_readonly_server(self):
 
378
        return HttpServer(WallRequestHandler)
 
379
 
 
380
    def test_http_has(self):
 
381
        server = self.get_readonly_server()
 
382
        t = self._transport(server.get_url())
 
383
        # Unfortunately httplib (see HTTPResponse._read_status
 
384
        # for details) make no distinction between a closed
 
385
        # socket and badly formatted status line, so we can't
 
386
        # just test for ConnectionError, we have to test
 
387
        # InvalidHttpResponse too.
 
388
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
 
389
                          t.has, 'foo/bar')
 
390
 
 
391
    def test_http_get(self):
 
392
        server = self.get_readonly_server()
 
393
        t = self._transport(server.get_url())
 
394
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
 
395
                          t.get, 'foo/bar')
 
396
 
 
397
 
 
398
class TestWallServer_urllib(TestWallServer, TestCaseWithWebserver):
 
399
    """Tests "wall" server for urllib implementation"""
 
400
 
 
401
    _transport = HttpTransport_urllib
 
402
 
 
403
 
 
404
class TestWallServer_pycurl(TestWithTransport_pycurl,
 
405
                            TestWallServer,
 
406
                            TestCaseWithWebserver):
 
407
    """Tests "wall" server for pycurl implementation"""
 
408
 
 
409
 
 
410
class TestBadStatusServer(object):
 
411
    """Tests bad status from server."""
 
412
 
 
413
    def create_transport_readonly_server(self):
 
414
        return HttpServer(BadStatusRequestHandler)
 
415
 
 
416
    def test_http_has(self):
 
417
        server = self.get_readonly_server()
 
418
        t = self._transport(server.get_url())
 
419
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
420
 
 
421
    def test_http_get(self):
 
422
        server = self.get_readonly_server()
 
423
        t = self._transport(server.get_url())
 
424
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
425
 
 
426
 
 
427
class TestBadStatusServer_urllib(TestBadStatusServer, TestCaseWithWebserver):
 
428
    """Tests bad status server for urllib implementation"""
 
429
 
 
430
    _transport = HttpTransport_urllib
 
431
 
 
432
 
 
433
class TestBadStatusServer_pycurl(TestWithTransport_pycurl,
 
434
                                 TestBadStatusServer,
 
435
                                 TestCaseWithWebserver):
 
436
    """Tests bad status server for pycurl implementation"""
 
437
 
 
438
 
 
439
class TestInvalidStatusServer(TestBadStatusServer):
 
440
    """Tests invalid status from server.
 
441
 
 
442
    Both implementations raises the same error as for a bad status.
 
443
    """
 
444
 
 
445
    def create_transport_readonly_server(self):
 
446
        return HttpServer(InvalidStatusRequestHandler)
 
447
 
 
448
 
 
449
class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
 
450
                                     TestCaseWithWebserver):
 
451
    """Tests invalid status server for urllib implementation"""
 
452
 
 
453
    _transport = HttpTransport_urllib
 
454
 
 
455
 
 
456
class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
 
457
                                     TestInvalidStatusServer,
 
458
                                     TestCaseWithWebserver):
 
459
    """Tests invalid status server for pycurl implementation"""
 
460
 
 
461
 
 
462
class TestBadProtocolServer(object):
 
463
    """Tests bad protocol from server."""
 
464
 
 
465
    def create_transport_readonly_server(self):
 
466
        return HttpServer(BadProtocolRequestHandler)
 
467
 
 
468
    def test_http_has(self):
 
469
        server = self.get_readonly_server()
 
470
        t = self._transport(server.get_url())
 
471
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
 
472
 
 
473
    def test_http_get(self):
 
474
        server = self.get_readonly_server()
 
475
        t = self._transport(server.get_url())
 
476
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
 
477
 
 
478
 
 
479
class TestBadProtocolServer_urllib(TestBadProtocolServer,
 
480
                                   TestCaseWithWebserver):
 
481
    """Tests bad protocol server for urllib implementation"""
 
482
 
 
483
    _transport = HttpTransport_urllib
 
484
 
 
485
# curl don't check the protocol version
 
486
#class TestBadProtocolServer_pycurl(TestWithTransport_pycurl,
 
487
#                                   TestBadProtocolServer,
 
488
#                                   TestCaseWithWebserver):
 
489
#    """Tests bad protocol server for pycurl implementation"""
 
490
 
 
491
 
 
492
class TestForbiddenServer(object):
 
493
    """Tests forbidden server"""
 
494
 
 
495
    def create_transport_readonly_server(self):
 
496
        return HttpServer(ForbiddenRequestHandler)
 
497
 
 
498
    def test_http_has(self):
 
499
        server = self.get_readonly_server()
 
500
        t = self._transport(server.get_url())
 
501
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
 
502
 
 
503
    def test_http_get(self):
 
504
        server = self.get_readonly_server()
 
505
        t = self._transport(server.get_url())
 
506
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
 
507
 
 
508
 
 
509
class TestForbiddenServer_urllib(TestForbiddenServer, TestCaseWithWebserver):
 
510
    """Tests forbidden server for urllib implementation"""
 
511
 
 
512
    _transport = HttpTransport_urllib
 
513
 
 
514
 
 
515
class TestForbiddenServer_pycurl(TestWithTransport_pycurl,
 
516
                                 TestForbiddenServer,
 
517
                                 TestCaseWithWebserver):
 
518
    """Tests forbidden server for pycurl implementation"""
 
519
 
 
520
 
 
521
class TestRecordingServer(TestCase):
 
522
 
 
523
    def test_create(self):
 
524
        server = RecordingServer(expect_body_tail=None)
 
525
        self.assertEqual('', server.received_bytes)
 
526
        self.assertEqual(None, server.host)
 
527
        self.assertEqual(None, server.port)
 
528
 
 
529
    def test_setUp_and_tearDown(self):
 
530
        server = RecordingServer(expect_body_tail=None)
 
531
        server.setUp()
 
532
        try:
 
533
            self.assertNotEqual(None, server.host)
 
534
            self.assertNotEqual(None, server.port)
 
535
        finally:
 
536
            server.tearDown()
 
537
        self.assertEqual(None, server.host)
 
538
        self.assertEqual(None, server.port)
 
539
 
 
540
    def test_send_receive_bytes(self):
 
541
        server = RecordingServer(expect_body_tail='c')
 
542
        server.setUp()
 
543
        self.addCleanup(server.tearDown)
 
544
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
545
        sock.connect((server.host, server.port))
 
546
        sock.sendall('abc')
 
547
        self.assertEqual('HTTP/1.1 200 OK\r\n',
 
548
                         osutils.recv_all(sock, 4096))
 
549
        self.assertEqual('abc', server.received_bytes)
 
550
 
 
551
 
 
552
class TestRangeRequestServer(object):
 
553
    """Tests readv requests against server.
 
554
 
 
555
    This MUST be used by daughter classes that also inherit from
 
556
    TestCaseWithWebserver.
 
557
 
 
558
    We can't inherit directly from TestCaseWithWebserver or the
 
559
    test framework will try to create an instance which cannot
 
560
    run, its implementation being incomplete.
 
561
    """
 
562
 
 
563
    def setUp(self):
 
564
        TestCaseWithWebserver.setUp(self)
 
565
        self.build_tree_contents([('a', '0123456789')],)
 
566
 
 
567
    def test_readv(self):
 
568
        server = self.get_readonly_server()
 
569
        t = self._transport(server.get_url())
 
570
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
 
571
        self.assertEqual(l[0], (0, '0'))
 
572
        self.assertEqual(l[1], (1, '1'))
 
573
        self.assertEqual(l[2], (3, '34'))
 
574
        self.assertEqual(l[3], (9, '9'))
 
575
 
 
576
    def test_readv_out_of_order(self):
 
577
        server = self.get_readonly_server()
 
578
        t = self._transport(server.get_url())
 
579
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
 
580
        self.assertEqual(l[0], (1, '1'))
 
581
        self.assertEqual(l[1], (9, '9'))
 
582
        self.assertEqual(l[2], (0, '0'))
 
583
        self.assertEqual(l[3], (3, '34'))
 
584
 
 
585
    def test_readv_invalid_ranges(self):
 
586
        server = self.get_readonly_server()
 
587
        t = self._transport(server.get_url())
 
588
 
 
589
        # This is intentionally reading off the end of the file
 
590
        # since we are sure that it cannot get there
 
591
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
 
592
                              t.readv, 'a', [(1,1), (8,10)])
 
593
 
 
594
        # This is trying to seek past the end of the file, it should
 
595
        # also raise a special error
 
596
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
 
597
                              t.readv, 'a', [(12,2)])
 
598
 
 
599
 
 
600
class TestSingleRangeRequestServer(TestRangeRequestServer):
 
601
    """Test readv against a server which accept only single range requests"""
 
602
 
 
603
    def create_transport_readonly_server(self):
 
604
        return HttpServer(SingleRangeRequestHandler)
 
605
 
 
606
 
 
607
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
 
608
                                          TestCaseWithWebserver):
 
609
    """Tests single range requests accepting server for urllib implementation"""
 
610
 
 
611
    _transport = HttpTransport_urllib
 
612
 
 
613
 
 
614
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
 
615
                                          TestSingleRangeRequestServer,
 
616
                                          TestCaseWithWebserver):
 
617
    """Tests single range requests accepting server for pycurl implementation"""
 
618
 
 
619
 
 
620
class TestNoRangeRequestServer(TestRangeRequestServer):
 
621
    """Test readv against a server which do not accept range requests"""
 
622
 
 
623
    def create_transport_readonly_server(self):
 
624
        return HttpServer(NoRangeRequestHandler)
 
625
 
 
626
 
 
627
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
 
628
                                      TestCaseWithWebserver):
 
629
    """Tests range requests refusing server for urllib implementation"""
 
630
 
 
631
    _transport = HttpTransport_urllib
 
632
 
 
633
 
 
634
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
 
635
                               TestNoRangeRequestServer,
 
636
                               TestCaseWithWebserver):
 
637
    """Tests range requests refusing server for pycurl implementation"""
 
638
 
 
639
 
 
640
class TestProxyHttpServer(object):
 
641
    """Tests proxy server.
 
642
 
 
643
    This MUST be used by daughter classes that also inherit from
 
644
    TestCaseWithTwoWebservers.
 
645
 
 
646
    We can't inherit directly from TestCaseWithTwoWebservers or
 
647
    the test framework will try to create an instance which
 
648
    cannot run, its implementation being incomplete.
 
649
 
 
650
    Be aware that we do not setup a real proxy here. Instead, we
 
651
    check that the *connection* goes through the proxy by serving
 
652
    different content (the faked proxy server append '-proxied'
 
653
    to the file names).
 
654
    """
 
655
 
 
656
    # FIXME: We don't have an https server available, so we don't
 
657
    # test https connections.
 
658
 
 
659
    def setUp(self):
 
660
        TestCaseWithTwoWebservers.setUp(self)
 
661
        self.build_tree_contents([('foo', 'contents of foo\n'),
 
662
                                  ('foo-proxied', 'proxied contents of foo\n')])
 
663
        # Let's setup some attributes for tests
 
664
        self.server = self.get_readonly_server()
 
665
        self.no_proxy_host = 'localhost:%d' % self.server.port
 
666
        # The secondary server is the proxy
 
667
        self.proxy = self.get_secondary_server()
 
668
        self.proxy_url = self.proxy.get_url()
 
669
        self._old_env = {}
 
670
 
 
671
    def create_transport_secondary_server(self):
 
672
        """Creates an http server that will serve files with
 
673
        '-proxied' appended to their names.
 
674
        """
 
675
        return HttpServer(FakeProxyRequestHandler)
 
676
 
 
677
    def _set_and_capture_env_var(self, name, new_value):
 
678
        """Set an environment variable, and reset it when finished."""
 
679
        self._old_env[name] = osutils.set_or_unset_env(name, new_value)
 
680
 
 
681
    def _install_env(self, env):
 
682
        for name, value in env.iteritems():
 
683
            self._set_and_capture_env_var(name, value)
 
684
 
 
685
    def _restore_env(self):
 
686
        for name, value in self._old_env.iteritems():
 
687
            osutils.set_or_unset_env(name, value)
 
688
 
 
689
    def proxied_in_env(self, env):
 
690
        self._install_env(env)
 
691
        url = self.server.get_url()
 
692
        t = self._transport(url)
 
693
        try:
 
694
            self.assertEqual(t.get('foo').read(), 'proxied contents of foo\n')
 
695
        finally:
 
696
            self._restore_env()
 
697
 
 
698
    def not_proxied_in_env(self, env):
 
699
        self._install_env(env)
 
700
        url = self.server.get_url()
 
701
        t = self._transport(url)
 
702
        try:
 
703
            self.assertEqual(t.get('foo').read(), 'contents of foo\n')
 
704
        finally:
 
705
            self._restore_env()
 
706
 
 
707
    def test_http_proxy(self):
 
708
        self.proxied_in_env({'http_proxy': self.proxy_url})
 
709
 
 
710
    def test_HTTP_PROXY(self):
 
711
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
 
712
 
 
713
    def test_all_proxy(self):
 
714
        self.proxied_in_env({'all_proxy': self.proxy_url})
 
715
 
 
716
    def test_ALL_PROXY(self):
 
717
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
 
718
 
 
719
    def test_http_proxy_with_no_proxy(self):
 
720
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
 
721
                                 'no_proxy': self.no_proxy_host})
 
722
 
 
723
    def test_HTTP_PROXY_with_NO_PROXY(self):
 
724
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
 
725
                                 'NO_PROXY': self.no_proxy_host})
 
726
 
 
727
    def test_all_proxy_with_no_proxy(self):
 
728
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
 
729
                                 'no_proxy': self.no_proxy_host})
 
730
 
 
731
    def test_ALL_PROXY_with_NO_PROXY(self):
 
732
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
 
733
                                 'NO_PROXY': self.no_proxy_host})
 
734
 
 
735
 
 
736
class TestProxyHttpServer_urllib(TestProxyHttpServer,
 
737
                                 TestCaseWithTwoWebservers):
 
738
    """Tests proxy server for urllib implementation"""
 
739
 
 
740
    _transport = HttpTransport_urllib
 
741
 
 
742
 
 
743
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
 
744
                                 TestProxyHttpServer,
 
745
                                 TestCaseWithTwoWebservers):
 
746
    """Tests proxy server for pycurl implementation"""
 
747
 
 
748
    def setUp(self):
 
749
        TestProxyHttpServer.setUp(self)
 
750
        # Oh my ! pycurl does not check for the port as part of
 
751
        # no_proxy :-( So we just test the host part
 
752
        self.no_proxy_host = 'localhost'
 
753
 
 
754
    def test_HTTP_PROXY(self):
 
755
        # pycurl do not check HTTP_PROXY for security reasons
 
756
        # (for use in a CGI context that we do not care
 
757
        # about. Should we ?)
 
758
        raise TestSkipped()
 
759
 
 
760
    def test_HTTP_PROXY_with_NO_PROXY(self):
 
761
        raise TestSkipped()
 
762
 
 
763
 
 
764
class TestRanges(object):
 
765
    """Test the Range header in GET methods..
 
766
 
 
767
    This MUST be used by daughter classes that also inherit from
 
768
    TestCaseWithWebserver.
 
769
 
 
770
    We can't inherit directly from TestCaseWithWebserver or the
 
771
    test framework will try to create an instance which cannot
 
772
    run, its implementation being incomplete.
 
773
    """
 
774
 
 
775
    def setUp(self):
 
776
        TestCaseWithWebserver.setUp(self)
 
777
        self.build_tree_contents([('a', '0123456789')],)
 
778
        server = self.get_readonly_server()
 
779
        self.transport = self._transport(server.get_url())
 
780
 
 
781
    def _file_contents(self, relpath, ranges, tail_amount=0):
 
782
         code, data = self.transport._get(relpath, ranges)
 
783
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
 
784
         for start, end in ranges:
 
785
             data.seek(start)
 
786
             yield data.read(end - start + 1)
 
787
 
 
788
    def _file_tail(self, relpath, tail_amount):
 
789
         code, data = self.transport._get(relpath, [], tail_amount)
 
790
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
 
791
         data.seek(-tail_amount + 1, 2)
 
792
         return data.read(tail_amount)
 
793
 
 
794
    def test_range_header(self):
 
795
        # Valid ranges
 
796
        map(self.assertEqual,['0', '234'],
 
797
            list(self._file_contents('a', [(0,0), (2,4)])),)
 
798
        # Tail
 
799
        self.assertEqual('789', self._file_tail('a', 3))
 
800
        # Syntactically invalid range
 
801
        self.assertRaises(errors.InvalidRange,
 
802
                          self.transport._get, 'a', [(4, 3)])
 
803
        # Semantically invalid range
 
804
        self.assertRaises(errors.InvalidRange,
 
805
                          self.transport._get, 'a', [(42, 128)])
 
806
 
 
807
 
 
808
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
 
809
    """Test the Range header in GET methods for urllib implementation"""
 
810
 
 
811
    _transport = HttpTransport_urllib
 
812
 
 
813
 
 
814
class TestRanges_pycurl(TestWithTransport_pycurl,
 
815
                        TestRanges,
 
816
                        TestCaseWithWebserver):
 
817
    """Test the Range header in GET methods for pycurl implementation"""
 
818
 
 
819
 
 
820
class TestRedirections(object):
 
821
    """Test redirection from the 'old' server to the 'new' server.
 
822
 
 
823
    This MUST be used by daughter classes that also inherit from
 
824
    TestCaseWithTwoWebservers.
 
825
 
 
826
    We can't inherit directly from TestCaseWithTwoWebservers or the
 
827
    test framework will try to create an instance which cannot
 
828
    run, its implementation being incomplete. 
 
829
    """
 
830
 
 
831
    def create_transport_secondary_server(self):
 
832
        """Create the secondary server redirecting to the primary server"""
 
833
        new = self.get_new_server()
 
834
 
 
835
        return HTTPServerRedirecting(new.host, new.port)
 
836
 
 
837
    def get_old_server(self):
 
838
        """The redirected server"""
 
839
        return self.get_secondary_server()
 
840
 
 
841
    def get_new_server(self):
 
842
        """The redirected to server"""
 
843
        return self.get_readonly_server()
 
844
 
 
845
    def setUp(self):
 
846
        super(TestRedirections, self).setUp()
 
847
        new = self.get_new_server()
 
848
        old = self.get_old_server()
 
849
 
 
850
        self.build_tree_contents([('a', '0123456789')],)
 
851
 
 
852
        self.new_transport = self._transport(new.get_url())
 
853
        self.old_transport = self._transport(old.get_url())
 
854
 
 
855
    def test_redirected(self):
 
856
        old = self.old_transport
 
857
        self.assertRaises(errors.RedirectRequested, old.get, 'a')
 
858
 
 
859
    def test_redirection_loop(self):
 
860
        pass
 
861
 
 
862
 
 
863
class TestRedirections_urllib(TestRedirections,
 
864
                              TestCaseWithTwoWebservers):
 
865
    """Tests redirections for urllib implementation"""
 
866
 
 
867
    _transport = HttpTransport_urllib
 
868