~bzr-pqm/bzr/bzr.dev

2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
1
# Copyright (C) 2005, 2006 Canonical Ltd
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
2
#
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
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.
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
7
#
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
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.
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
12
#
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
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
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
16
1540.3.3 by Martin Pool
Review updates of pycurl transport
17
# FIXME: This test should be repeated for each available http client
18
# implementation; at the moment we have urllib and pycurl.
19
1540.3.22 by Martin Pool
[patch] Add TestCase.assertIsInstance
20
# TODO: Should be renamed to bzrlib.transport.http.tests?
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
21
# TODO: What about renaming to bzrlib.tests.transport.http ?
1540.3.22 by Martin Pool
[patch] Add TestCase.assertIsInstance
22
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
23
import os
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
24
import select
2000.2.2 by John Arbash Meinel
Update the urllib.has test.
25
import socket
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
26
import threading
2000.2.2 by John Arbash Meinel
Update the urllib.has test.
27
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
28
import bzrlib
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
29
from bzrlib import (
30
    errors,
31
    osutils,
32
    urlutils,
33
    )
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
34
from bzrlib.tests import (
35
    TestCase,
36
    TestSkipped,
37
    )
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
38
from bzrlib.tests.HttpServer import (
39
    HttpServer,
40
    HttpServer_PyCurl,
41
    HttpServer_urllib,
42
    )
43
from bzrlib.tests.HTTPTestUtil import (
44
    BadProtocolRequestHandler,
45
    BadStatusRequestHandler,
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
46
    FakeProxyRequestHandler,
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
47
    ForbiddenRequestHandler,
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
48
    HTTPServerRedirecting,
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
49
    InvalidStatusRequestHandler,
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
50
    NoRangeRequestHandler,
51
    SingleRangeRequestHandler,
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
52
    TestCaseWithRedirectedWebserver,
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
53
    TestCaseWithTwoWebservers,
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
54
    TestCaseWithWebserver,
55
    WallRequestHandler,
56
    )
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
57
from bzrlib.transport import (
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
58
    do_catching_redirections,
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
59
    get_transport,
60
    Transport,
61
    )
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
62
from bzrlib.transport.http import (
63
    extract_auth,
64
    HttpTransportBase,
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
65
    _urllib2_wrappers,
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
66
    )
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
67
from bzrlib.transport.http._urllib import HttpTransport_urllib
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
68
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
69
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
70
class FakeManager(object):
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
71
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
72
    def __init__(self):
73
        self.credentials = []
2004.3.1 by vila
Test ConnectionError exceptions.
74
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
75
    def add_password(self, realm, host, username, password):
76
        self.credentials.append([realm, host, username, password])
77
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
78
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
79
class RecordingServer(object):
80
    """A fake HTTP server.
81
    
82
    It records the bytes sent to it, and replies with a 200.
83
    """
84
85
    def __init__(self, expect_body_tail=None):
2018.2.28 by Andrew Bennetts
Changes in response to review: re-use _base_curl, rather than keeping a seperate _post_curl object; add docstring to test_http.RecordingServer, set is_user_error on some new exceptions.
86
        """Constructor.
87
88
        :type expect_body_tail: str
89
        :param expect_body_tail: a reply won't be sent until this string is
90
            received.
91
        """
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
92
        self._expect_body_tail = expect_body_tail
93
        self.host = None
94
        self.port = None
95
        self.received_bytes = ''
96
97
    def setUp(self):
98
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
99
        self._sock.bind(('127.0.0.1', 0))
100
        self.host, self.port = self._sock.getsockname()
101
        self._ready = threading.Event()
102
        self._thread = threading.Thread(target=self._accept_read_and_reply)
103
        self._thread.setDaemon(True)
104
        self._thread.start()
105
        self._ready.wait(5)
106
107
    def _accept_read_and_reply(self):
108
        self._sock.listen(1)
109
        self._ready.set()
110
        self._sock.settimeout(5)
111
        try:
112
            conn, address = self._sock.accept()
113
            # On win32, the accepted connection will be non-blocking to start
114
            # with because we're using settimeout.
115
            conn.setblocking(True)
116
            while not self.received_bytes.endswith(self._expect_body_tail):
117
                self.received_bytes += conn.recv(4096)
118
            conn.sendall('HTTP/1.1 200 OK\r\n')
119
        except socket.timeout:
120
            # Make sure the client isn't stuck waiting for us to e.g. accept.
121
            self._sock.close()
2158.2.1 by v.ladeuil+lp at free
Windows tests cleanup.
122
        except socket.error:
123
            # The client may have already closed the socket.
124
            pass
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
125
126
    def tearDown(self):
127
        try:
128
            self._sock.close()
129
        except socket.error:
130
            # We might have already closed it.  We don't care.
131
            pass
132
        self.host = None
133
        self.port = None
134
135
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
136
class TestWithTransport_pycurl(object):
137
    """Test case to inherit from if pycurl is present"""
138
139
    def _get_pycurl_maybe(self):
140
        try:
141
            from bzrlib.transport.http._pycurl import PyCurlTransport
142
            return PyCurlTransport
143
        except errors.DependencyNotPresent:
144
            raise TestSkipped('pycurl not present')
145
146
    _transport = property(_get_pycurl_maybe)
147
148
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
149
class TestHttpUrls(TestCase):
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
150
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
151
    # TODO: This should be moved to authorization tests once they
152
    # are written.
2004.1.40 by v.ladeuil+lp at free
Fix the race condition again and correct some small typos to be in
153
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
154
    def test_url_parsing(self):
155
        f = FakeManager()
156
        url = extract_auth('http://example.com', f)
157
        self.assertEquals('http://example.com', url)
158
        self.assertEquals(0, len(f.credentials))
1185.50.94 by John Arbash Meinel
Updated web page url to http://bazaar-vcs.org
159
        url = extract_auth('http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
160
        self.assertEquals('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
1185.40.20 by Robey Pointer
allow user:pass@ info in http urls to be used for auth; this should be easily expandable later to use auth config files
161
        self.assertEquals(1, len(f.credentials))
2004.3.1 by vila
Test ConnectionError exceptions.
162
        self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'],
163
                          f.credentials[0])
164
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
165
166
class TestHttpTransportUrls(object):
167
    """Test the http urls.
168
169
    This MUST be used by daughter classes that also inherit from
170
    TestCase.
171
172
    We can't inherit directly from TestCase or the
173
    test framework will try to create an instance which cannot
174
    run, its implementation being incomplete.
175
    """
176
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
177
    def test_abs_url(self):
178
        """Construction of absolute http URLs"""
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
179
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
180
        eq = self.assertEqualDiff
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
181
        eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
182
        eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
183
        eq(t.abspath('.bzr'), 'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
184
        eq(t.abspath('.bzr/1//2/./3'),
1185.50.94 by John Arbash Meinel
Updated web page url to http://bazaar-vcs.org
185
           'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
186
187
    def test_invalid_http_urls(self):
188
        """Trap invalid construction of urls"""
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
189
        t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
190
        self.assertRaises(ValueError, t.abspath, '.bzr/')
191
        t = self._transport('http://http://bazaar-vcs.org/bzr/bzr.dev/')
192
        self.assertRaises((errors.InvalidURL, errors.ConnectionError),
193
                          t.has, 'foo/bar')
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
194
195
    def test_http_root_urls(self):
196
        """Construction of URLs from server root"""
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
197
        t = self._transport('http://bzr.ozlabs.org/')
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
198
        eq = self.assertEqualDiff
199
        eq(t.abspath('.bzr/tree-version'),
200
           'http://bzr.ozlabs.org/.bzr/tree-version')
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
201
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
202
    def test_http_impl_urls(self):
203
        """There are servers which ask for particular clients to connect"""
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
204
        server = self._server()
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
205
        try:
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
206
            server.setUp()
207
            url = server.get_url()
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
208
            self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
209
        finally:
210
            server.tearDown()
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
211
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
212
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
213
class TestHttpUrls_urllib(TestHttpTransportUrls, TestCase):
214
    """Test http urls with urllib"""
215
216
    _transport = HttpTransport_urllib
217
    _server = HttpServer_urllib
218
    _qualified_prefix = 'http+urllib'
219
220
221
class TestHttpUrls_pycurl(TestWithTransport_pycurl, TestHttpTransportUrls,
222
                          TestCase):
223
    """Test http urls with pycurl"""
224
225
    _server = HttpServer_PyCurl
226
    _qualified_prefix = 'http+pycurl'
227
228
    # TODO: This should really be moved into another pycurl
229
    # specific test. When https tests will be implemented, take
230
    # this one into account.
231
    def test_pycurl_without_https_support(self):
232
        """Test that pycurl without SSL do not fail with a traceback.
233
234
        For the purpose of the test, we force pycurl to ignore
235
        https by supplying a fake version_info that do not
236
        support it.
237
        """
238
        try:
239
            import pycurl
240
        except ImportError:
241
            raise TestSkipped('pycurl not present')
242
        # Now that we have pycurl imported, we can fake its version_info
243
        # This was taken from a windows pycurl without SSL
244
        # (thanks to bialix)
245
        pycurl.version_info = lambda : (2,
246
                                        '7.13.2',
247
                                        462082,
248
                                        'i386-pc-win32',
249
                                        2576,
250
                                        None,
251
                                        0,
252
                                        None,
2294.3.2 by Aaron Bentley
Remove trailing whitespace
253
                                        ('ftp', 'gopher', 'telnet',
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
254
                                         'dict', 'ldap', 'http', 'file'),
255
                                        None,
256
                                        0,
257
                                        None)
258
        self.assertRaises(errors.DependencyNotPresent, self._transport,
259
                          'https://launchpad.net')
260
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
261
class TestHttpConnections(object):
262
    """Test the http connections.
263
264
    This MUST be used by daughter classes that also inherit from
265
    TestCaseWithWebserver.
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
266
267
    We can't inherit directly from TestCaseWithWebserver or the
268
    test framework will try to create an instance which cannot
269
    run, its implementation being incomplete.
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
270
    """
271
272
    def setUp(self):
273
        TestCaseWithWebserver.setUp(self)
1540.3.33 by Martin Pool
Fix http tests that were failing to run tearDown when setup got a missing dependency
274
        self.build_tree(['xxx', 'foo/', 'foo/bar'], line_endings='binary',
275
                        transport=self.get_transport())
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
276
277
    def test_http_has(self):
1185.50.84 by John Arbash Meinel
[merge] bzr.dev, cleanup conflicts, fixup http tests for new TestCase layout.
278
        server = self.get_readonly_server()
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
279
        t = self._transport(server.get_url())
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
280
        self.assertEqual(t.has('foo/bar'), True)
1185.50.84 by John Arbash Meinel
[merge] bzr.dev, cleanup conflicts, fixup http tests for new TestCase layout.
281
        self.assertEqual(len(server.logs), 1)
2004.3.1 by vila
Test ConnectionError exceptions.
282
        self.assertContainsRe(server.logs[0],
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
283
            r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
1553.1.5 by James Henstridge
Make HTTP transport has() method do HEAD requests, and update test to
284
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
285
    def test_http_has_not_found(self):
286
        server = self.get_readonly_server()
287
        t = self._transport(server.get_url())
1553.1.5 by James Henstridge
Make HTTP transport has() method do HEAD requests, and update test to
288
        self.assertEqual(t.has('not-found'), False)
2004.3.1 by vila
Test ConnectionError exceptions.
289
        self.assertContainsRe(server.logs[1],
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
290
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
291
292
    def test_http_get(self):
1185.50.84 by John Arbash Meinel
[merge] bzr.dev, cleanup conflicts, fixup http tests for new TestCase layout.
293
        server = self.get_readonly_server()
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
294
        t = self._transport(server.get_url())
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
295
        fp = t.get('foo/bar')
296
        self.assertEqualDiff(
297
            fp.read(),
1553.1.3 by James Henstridge
Make bzrlib.transport.http.HttpServer output referer and user agent as in
298
            'contents of foo/bar\n')
1185.50.84 by John Arbash Meinel
[merge] bzr.dev, cleanup conflicts, fixup http tests for new TestCase layout.
299
        self.assertEqual(len(server.logs), 1)
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
300
        self.assertTrue(server.logs[0].find(
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
301
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s'
302
            % bzrlib.__version__) > -1)
1540.3.33 by Martin Pool
Fix http tests that were failing to run tearDown when setup got a missing dependency
303
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
304
    def test_get_smart_medium(self):
305
        # For HTTP, get_smart_medium should return the transport object.
306
        server = self.get_readonly_server()
307
        http_transport = self._transport(server.get_url())
308
        medium = http_transport.get_smart_medium()
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
309
        self.assertIs(medium, http_transport)
1540.3.33 by Martin Pool
Fix http tests that were failing to run tearDown when setup got a missing dependency
310
2000.2.2 by John Arbash Meinel
Update the urllib.has test.
311
    def test_has_on_bogus_host(self):
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
312
        # Get a free address and don't 'accept' on it, so that we
313
        # can be sure there is no http handler there, but set a
314
        # reasonable timeout to not slow down tests too much.
315
        default_timeout = socket.getdefaulttimeout()
316
        try:
317
            socket.setdefaulttimeout(2)
318
            s = socket.socket()
319
            s.bind(('localhost', 0))
320
            t = self._transport('http://%s:%s/' % s.getsockname())
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
321
            self.assertRaises(errors.ConnectionError, t.has, 'foo/bar')
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
322
        finally:
323
            socket.setdefaulttimeout(default_timeout)
324
1540.3.33 by Martin Pool
Fix http tests that were failing to run tearDown when setup got a missing dependency
325
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
326
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
327
    """Test http connections with urllib"""
328
329
    _transport = HttpTransport_urllib
330
331
332
333
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
334
                                 TestHttpConnections,
335
                                 TestCaseWithWebserver):
336
    """Test http connections with pycurl"""
1540.3.33 by Martin Pool
Fix http tests that were failing to run tearDown when setup got a missing dependency
337
338
1540.3.23 by Martin Pool
Allow urls like http+pycurl://host/ to use a particular impl
339
class TestHttpTransportRegistration(TestCase):
340
    """Test registrations of various http implementations"""
341
342
    def test_http_registered(self):
343
        # urlllib should always be present
344
        t = get_transport('http+urllib://bzr.google.com/')
345
        self.assertIsInstance(t, Transport)
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
346
        self.assertIsInstance(t, HttpTransport_urllib)
1786.1.23 by John Arbash Meinel
Move offset_to_http_ranges back onto HttpTransportBase, clarify tests.
347
348
349
class TestOffsets(TestCase):
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
350
    """Test offsets_to_ranges method"""
1786.1.23 by John Arbash Meinel
Move offset_to_http_ranges back onto HttpTransportBase, clarify tests.
351
352
    def test_offsets_to_ranges_simple(self):
353
        to_range = HttpTransportBase.offsets_to_ranges
1786.1.39 by John Arbash Meinel
Remove the ability to read negative offsets from readv()
354
        ranges = to_range([(10, 1)])
1786.1.23 by John Arbash Meinel
Move offset_to_http_ranges back onto HttpTransportBase, clarify tests.
355
        self.assertEqual([[10, 10]], ranges)
1786.1.39 by John Arbash Meinel
Remove the ability to read negative offsets from readv()
356
357
        ranges = to_range([(0, 1), (1, 1)])
358
        self.assertEqual([[0, 1]], ranges)
359
360
        ranges = to_range([(1, 1), (0, 1)])
361
        self.assertEqual([[0, 1]], ranges)
1786.1.23 by John Arbash Meinel
Move offset_to_http_ranges back onto HttpTransportBase, clarify tests.
362
363
    def test_offset_to_ranges_overlapped(self):
364
        to_range = HttpTransportBase.offsets_to_ranges
365
1786.1.39 by John Arbash Meinel
Remove the ability to read negative offsets from readv()
366
        ranges = to_range([(10, 1), (20, 2), (22, 5)])
367
        self.assertEqual([[10, 10], [20, 26]], ranges)
368
369
        ranges = to_range([(10, 1), (11, 2), (22, 5)])
370
        self.assertEqual([[10, 12], [22, 26]], ranges)
1786.1.23 by John Arbash Meinel
Move offset_to_http_ranges back onto HttpTransportBase, clarify tests.
371
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
372
2158.2.1 by v.ladeuil+lp at free
Windows tests cleanup.
373
class TestPost(object):
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
374
375
    def _test_post_body_is_received(self, scheme):
376
        server = RecordingServer(expect_body_tail='end-of-body')
377
        server.setUp()
378
        self.addCleanup(server.tearDown)
379
        url = '%s://%s:%s/' % (scheme, server.host, server.port)
380
        try:
381
            http_transport = get_transport(url)
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
382
        except errors.UnsupportedProtocol:
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
383
            raise TestSkipped('%s not available' % scheme)
384
        code, response = http_transport._post('abc def end-of-body')
385
        self.assertTrue(
386
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
387
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
388
        # The transport should not be assuming that the server can accept
389
        # chunked encoding the first time it connects, because HTTP/1.1, so we
390
        # check for the literal string.
391
        self.assertTrue(
392
            server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
393
2158.2.1 by v.ladeuil+lp at free
Windows tests cleanup.
394
395
class TestPost_urllib(TestCase, TestPost):
396
    """TestPost for urllib implementation"""
397
398
    _transport = HttpTransport_urllib
399
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
400
    def test_post_body_is_received_urllib(self):
401
        self._test_post_body_is_received('http+urllib')
402
2158.2.1 by v.ladeuil+lp at free
Windows tests cleanup.
403
404
class TestPost_pycurl(TestWithTransport_pycurl, TestCase, TestPost):
405
    """TestPost for pycurl implementation"""
406
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
407
    def test_post_body_is_received_pycurl(self):
408
        self._test_post_body_is_received('http+pycurl')
409
410
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
411
class TestRangeHeader(TestCase):
412
    """Test range_header method"""
413
414
    def check_header(self, value, ranges=[], tail=0):
415
        range_header = HttpTransportBase.range_header
416
        self.assertEqual(value, range_header(ranges, tail))
417
418
    def test_range_header_single(self):
1786.1.36 by John Arbash Meinel
pycurl expects us to just set the range of bytes, not including bytes=
419
        self.check_header('0-9', ranges=[[0,9]])
420
        self.check_header('100-109', ranges=[[100,109]])
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
421
422
    def test_range_header_tail(self):
1786.1.36 by John Arbash Meinel
pycurl expects us to just set the range of bytes, not including bytes=
423
        self.check_header('-10', tail=10)
424
        self.check_header('-50', tail=50)
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
425
426
    def test_range_header_multi(self):
1786.1.36 by John Arbash Meinel
pycurl expects us to just set the range of bytes, not including bytes=
427
        self.check_header('0-9,100-200,300-5000',
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
428
                          ranges=[(0,9), (100, 200), (300,5000)])
429
430
    def test_range_header_mixed(self):
1786.1.36 by John Arbash Meinel
pycurl expects us to just set the range of bytes, not including bytes=
431
        self.check_header('0-9,300-5000,-50',
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
432
                          ranges=[(0,9), (300,5000)],
433
                          tail=50)
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
434
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
435
436
class TestWallServer(object):
437
    """Tests exceptions during the connection phase"""
438
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
439
    def create_transport_readonly_server(self):
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
440
        return HttpServer(WallRequestHandler)
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
441
442
    def test_http_has(self):
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
443
        server = self.get_readonly_server()
2004.3.1 by vila
Test ConnectionError exceptions.
444
        t = self._transport(server.get_url())
2004.1.40 by v.ladeuil+lp at free
Fix the race condition again and correct some small typos to be in
445
        # Unfortunately httplib (see HTTPResponse._read_status
446
        # for details) make no distinction between a closed
447
        # socket and badly formatted status line, so we can't
448
        # just test for ConnectionError, we have to test
449
        # InvalidHttpResponse too.
450
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
451
                          t.has, 'foo/bar')
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
452
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
453
    def test_http_get(self):
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
454
        server = self.get_readonly_server()
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
455
        t = self._transport(server.get_url())
2145.1.1 by mbp at sourcefrog
merge urllib keepalive etc
456
        self.assertRaises((errors.ConnectionError, errors.InvalidHttpResponse),
457
                          t.get, 'foo/bar')
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
458
459
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
460
class TestWallServer_urllib(TestWallServer, TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
461
    """Tests "wall" server for urllib implementation"""
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
462
463
    _transport = HttpTransport_urllib
464
465
466
class TestWallServer_pycurl(TestWithTransport_pycurl,
467
                            TestWallServer,
468
                            TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
469
    """Tests "wall" server for pycurl implementation"""
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
470
471
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
472
class TestBadStatusServer(object):
473
    """Tests bad status from server."""
474
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
475
    def create_transport_readonly_server(self):
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
476
        return HttpServer(BadStatusRequestHandler)
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
477
478
    def test_http_has(self):
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
479
        server = self.get_readonly_server()
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
480
        t = self._transport(server.get_url())
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
481
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
482
483
    def test_http_get(self):
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
484
        server = self.get_readonly_server()
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
485
        t = self._transport(server.get_url())
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
486
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
487
488
489
class TestBadStatusServer_urllib(TestBadStatusServer, TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
490
    """Tests bad status server for urllib implementation"""
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
491
492
    _transport = HttpTransport_urllib
493
494
495
class TestBadStatusServer_pycurl(TestWithTransport_pycurl,
496
                                 TestBadStatusServer,
497
                                 TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
498
    """Tests bad status server for pycurl implementation"""
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
499
500
501
class TestInvalidStatusServer(TestBadStatusServer):
502
    """Tests invalid status from server.
503
504
    Both implementations raises the same error as for a bad status.
505
    """
506
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
507
    def create_transport_readonly_server(self):
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
508
        return HttpServer(InvalidStatusRequestHandler)
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
509
510
511
class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
512
                                     TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
513
    """Tests invalid status server for urllib implementation"""
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
514
515
    _transport = HttpTransport_urllib
516
517
518
class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
519
                                     TestInvalidStatusServer,
520
                                     TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
521
    """Tests invalid status server for pycurl implementation"""
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
522
523
524
class TestBadProtocolServer(object):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
525
    """Tests bad protocol from server."""
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
526
527
    def create_transport_readonly_server(self):
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
528
        return HttpServer(BadProtocolRequestHandler)
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
529
530
    def test_http_has(self):
531
        server = self.get_readonly_server()
532
        t = self._transport(server.get_url())
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
533
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
534
535
    def test_http_get(self):
536
        server = self.get_readonly_server()
537
        t = self._transport(server.get_url())
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
538
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
539
540
541
class TestBadProtocolServer_urllib(TestBadProtocolServer,
542
                                   TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
543
    """Tests bad protocol server for urllib implementation"""
2004.1.19 by v.ladeuil+lp at free
Test protocol version in http responses.
544
545
    _transport = HttpTransport_urllib
546
547
# curl don't check the protocol version
548
#class TestBadProtocolServer_pycurl(TestWithTransport_pycurl,
549
#                                   TestBadProtocolServer,
550
#                                   TestCaseWithWebserver):
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
551
#    """Tests bad protocol server for pycurl implementation"""
552
553
554
class TestForbiddenServer(object):
555
    """Tests forbidden server"""
556
557
    def create_transport_readonly_server(self):
558
        return HttpServer(ForbiddenRequestHandler)
559
560
    def test_http_has(self):
561
        server = self.get_readonly_server()
562
        t = self._transport(server.get_url())
563
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
564
565
    def test_http_get(self):
566
        server = self.get_readonly_server()
567
        t = self._transport(server.get_url())
568
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
569
570
571
class TestForbiddenServer_urllib(TestForbiddenServer, TestCaseWithWebserver):
572
    """Tests forbidden server for urllib implementation"""
573
574
    _transport = HttpTransport_urllib
575
576
577
class TestForbiddenServer_pycurl(TestWithTransport_pycurl,
578
                                 TestForbiddenServer,
579
                                 TestCaseWithWebserver):
580
    """Tests forbidden server for pycurl implementation"""
581
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
582
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
583
class TestRecordingServer(TestCase):
584
585
    def test_create(self):
586
        server = RecordingServer(expect_body_tail=None)
587
        self.assertEqual('', server.received_bytes)
588
        self.assertEqual(None, server.host)
589
        self.assertEqual(None, server.port)
590
591
    def test_setUp_and_tearDown(self):
592
        server = RecordingServer(expect_body_tail=None)
593
        server.setUp()
594
        try:
595
            self.assertNotEqual(None, server.host)
596
            self.assertNotEqual(None, server.port)
597
        finally:
598
            server.tearDown()
599
        self.assertEqual(None, server.host)
600
        self.assertEqual(None, server.port)
601
602
    def test_send_receive_bytes(self):
603
        server = RecordingServer(expect_body_tail='c')
604
        server.setUp()
605
        self.addCleanup(server.tearDown)
606
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
607
        sock.connect((server.host, server.port))
608
        sock.sendall('abc')
609
        self.assertEqual('HTTP/1.1 200 OK\r\n',
2091.1.1 by Martin Pool
Avoid MSG_WAITALL as it doesn't work on Windows
610
                         osutils.recv_all(sock, 4096))
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
611
        self.assertEqual('abc', server.received_bytes)
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
612
613
614
class TestRangeRequestServer(object):
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
615
    """Tests readv requests against server.
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
616
617
    This MUST be used by daughter classes that also inherit from
618
    TestCaseWithWebserver.
619
620
    We can't inherit directly from TestCaseWithWebserver or the
621
    test framework will try to create an instance which cannot
622
    run, its implementation being incomplete.
623
    """
624
625
    def setUp(self):
626
        TestCaseWithWebserver.setUp(self)
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
627
        self.build_tree_contents([('a', '0123456789')],)
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
628
629
    def test_readv(self):
630
        server = self.get_readonly_server()
631
        t = self._transport(server.get_url())
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
632
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
633
        self.assertEqual(l[0], (0, '0'))
634
        self.assertEqual(l[1], (1, '1'))
635
        self.assertEqual(l[2], (3, '34'))
636
        self.assertEqual(l[3], (9, '9'))
637
638
    def test_readv_out_of_order(self):
639
        server = self.get_readonly_server()
640
        t = self._transport(server.get_url())
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
641
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
642
        self.assertEqual(l[0], (1, '1'))
643
        self.assertEqual(l[1], (9, '9'))
644
        self.assertEqual(l[2], (0, '0'))
645
        self.assertEqual(l[3], (3, '34'))
646
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
647
    def test_readv_invalid_ranges(self):
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
648
        server = self.get_readonly_server()
649
        t = self._transport(server.get_url())
650
651
        # This is intentionally reading off the end of the file
652
        # since we are sure that it cannot get there
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
653
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
654
                              t.readv, 'a', [(1,1), (8,10)])
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
655
656
        # This is trying to seek past the end of the file, it should
657
        # also raise a special error
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
658
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
659
                              t.readv, 'a', [(12,2)])
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
660
661
662
class TestSingleRangeRequestServer(TestRangeRequestServer):
663
    """Test readv against a server which accept only single range requests"""
664
665
    def create_transport_readonly_server(self):
666
        return HttpServer(SingleRangeRequestHandler)
667
668
669
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
670
                                          TestCaseWithWebserver):
671
    """Tests single range requests accepting server for urllib implementation"""
672
673
    _transport = HttpTransport_urllib
674
675
676
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
677
                                          TestSingleRangeRequestServer,
678
                                          TestCaseWithWebserver):
679
    """Tests single range requests accepting server for pycurl implementation"""
680
681
682
class TestNoRangeRequestServer(TestRangeRequestServer):
683
    """Test readv against a server which do not accept range requests"""
684
685
    def create_transport_readonly_server(self):
686
        return HttpServer(NoRangeRequestHandler)
687
688
689
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
690
                                      TestCaseWithWebserver):
691
    """Tests range requests refusing server for urllib implementation"""
692
693
    _transport = HttpTransport_urllib
694
695
696
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
697
                               TestNoRangeRequestServer,
698
                               TestCaseWithWebserver):
699
    """Tests range requests refusing server for pycurl implementation"""
700
701
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
702
class TestHttpProxyWhiteBox(TestCase):
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
703
    """Whitebox test proxy http authorization.
704
705
    These tests concern urllib implementation only.
706
    """
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
707
708
    def setUp(self):
709
        TestCase.setUp(self)
710
        self._old_env = {}
711
712
    def tearDown(self):
713
        self._restore_env()
714
715
    def _set_and_capture_env_var(self, name, new_value):
716
        """Set an environment variable, and reset it when finished."""
717
        self._old_env[name] = osutils.set_or_unset_env(name, new_value)
718
719
    def _install_env(self, env):
720
        for name, value in env.iteritems():
721
            self._set_and_capture_env_var(name, value)
722
723
    def _restore_env(self):
724
        for name, value in self._old_env.iteritems():
725
            osutils.set_or_unset_env(name, value)
726
727
    def _proxied_request(self):
728
        from bzrlib.transport.http._urllib2_wrappers import (
729
            ProxyHandler,
730
            Request,
731
            )
732
733
        handler = ProxyHandler()
734
        request = Request('GET','http://baz/buzzle')
735
        handler.set_proxy(request, 'http')
736
        return request
737
738
    def test_empty_user(self):
739
        self._install_env({'http_proxy': 'http://bar.com'})
740
        request = self._proxied_request()
741
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
742
743
    def test_empty_pass(self):
744
        self._install_env({'http_proxy': 'http://joe@bar.com'})
745
        request = self._proxied_request()
746
        self.assertEqual('Basic ' + 'joe:'.encode('base64').strip(),
747
                         request.headers['Proxy-authorization'])
748
    def test_user_pass(self):
749
        self._install_env({'http_proxy': 'http://joe:foo@bar.com'})
750
        request = self._proxied_request()
751
        self.assertEqual('Basic ' + 'joe:foo'.encode('base64').strip(),
752
                         request.headers['Proxy-authorization'])
753
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
754
    def test_invalid_proxy(self):
755
        """A proxy env variable without scheme"""
756
        self._install_env({'http_proxy': 'host:1234'})
757
        self.assertRaises(errors.InvalidURL, self._proxied_request)
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
758
759
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
760
class TestProxyHttpServer(object):
761
    """Tests proxy server.
762
763
    This MUST be used by daughter classes that also inherit from
764
    TestCaseWithTwoWebservers.
765
766
    We can't inherit directly from TestCaseWithTwoWebservers or
767
    the test framework will try to create an instance which
768
    cannot run, its implementation being incomplete.
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
769
770
    Be aware that we do not setup a real proxy here. Instead, we
2167.3.7 by v.ladeuil+lp at free
Typos corrected.
771
    check that the *connection* goes through the proxy by serving
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
772
    different content (the faked proxy server append '-proxied'
773
    to the file names).
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
774
    """
775
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
776
    # FIXME: We don't have an https server available, so we don't
777
    # test https connections.
778
2273.2.1 by v.ladeuil+lp at free
Fix bug #83954.
779
    # FIXME: Once the test suite is better fitted to test
780
    # authorization schemes, test proxy authorizations too (see
781
    # bug #83954).
782
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
783
    def setUp(self):
784
        TestCaseWithTwoWebservers.setUp(self)
785
        self.build_tree_contents([('foo', 'contents of foo\n'),
786
                                  ('foo-proxied', 'proxied contents of foo\n')])
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
787
        # Let's setup some attributes for tests
788
        self.server = self.get_readonly_server()
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
789
        # FIXME: We should not rely on 'localhost' being the hostname
790
        self.proxy_address = 'localhost:%d' % self.server.port
791
        self.no_proxy_host = self.proxy_address
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
792
        # The secondary server is the proxy
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
793
        self.proxy = self.get_secondary_server()
794
        self.proxy_url = self.proxy.get_url()
795
        self._old_env = {}
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
796
797
    def create_transport_secondary_server(self):
798
        """Creates an http server that will serve files with
799
        '-proxied' appended to their names.
800
        """
801
        return HttpServer(FakeProxyRequestHandler)
802
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
803
    def _set_and_capture_env_var(self, name, new_value):
804
        """Set an environment variable, and reset it when finished."""
805
        self._old_env[name] = osutils.set_or_unset_env(name, new_value)
806
807
    def _install_env(self, env):
808
        for name, value in env.iteritems():
809
            self._set_and_capture_env_var(name, value)
810
811
    def _restore_env(self):
812
        for name, value in self._old_env.iteritems():
813
            osutils.set_or_unset_env(name, value)
814
815
    def proxied_in_env(self, env):
816
        self._install_env(env)
817
        url = self.server.get_url()
818
        t = self._transport(url)
819
        try:
820
            self.assertEqual(t.get('foo').read(), 'proxied contents of foo\n')
821
        finally:
822
            self._restore_env()
823
824
    def not_proxied_in_env(self, env):
825
        self._install_env(env)
826
        url = self.server.get_url()
827
        t = self._transport(url)
828
        try:
829
            self.assertEqual(t.get('foo').read(), 'contents of foo\n')
830
        finally:
831
            self._restore_env()
832
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
833
    def test_http_proxy(self):
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
834
        self.proxied_in_env({'http_proxy': self.proxy_url})
835
836
    def test_HTTP_PROXY(self):
837
        self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
838
839
    def test_all_proxy(self):
840
        self.proxied_in_env({'all_proxy': self.proxy_url})
841
842
    def test_ALL_PROXY(self):
843
        self.proxied_in_env({'ALL_PROXY': self.proxy_url})
844
845
    def test_http_proxy_with_no_proxy(self):
846
        self.not_proxied_in_env({'http_proxy': self.proxy_url,
847
                                 'no_proxy': self.no_proxy_host})
848
849
    def test_HTTP_PROXY_with_NO_PROXY(self):
850
        self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
851
                                 'NO_PROXY': self.no_proxy_host})
852
853
    def test_all_proxy_with_no_proxy(self):
854
        self.not_proxied_in_env({'all_proxy': self.proxy_url,
855
                                 'no_proxy': self.no_proxy_host})
856
857
    def test_ALL_PROXY_with_NO_PROXY(self):
858
        self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
859
                                 'NO_PROXY': self.no_proxy_host})
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
860
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
861
    def test_http_proxy_without_scheme(self):
862
        self.assertRaises(errors.InvalidURL,
863
                          self.proxied_in_env,
864
                          {'http_proxy': self.proxy_address})
865
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
866
867
class TestProxyHttpServer_urllib(TestProxyHttpServer,
868
                                 TestCaseWithTwoWebservers):
869
    """Tests proxy server for urllib implementation"""
870
871
    _transport = HttpTransport_urllib
872
873
874
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
875
                                 TestProxyHttpServer,
876
                                 TestCaseWithTwoWebservers):
877
    """Tests proxy server for pycurl implementation"""
878
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
879
    def setUp(self):
880
        TestProxyHttpServer.setUp(self)
881
        # Oh my ! pycurl does not check for the port as part of
882
        # no_proxy :-( So we just test the host part
883
        self.no_proxy_host = 'localhost'
884
885
    def test_HTTP_PROXY(self):
886
        # pycurl do not check HTTP_PROXY for security reasons
887
        # (for use in a CGI context that we do not care
888
        # about. Should we ?)
889
        raise TestSkipped()
890
891
    def test_HTTP_PROXY_with_NO_PROXY(self):
892
        raise TestSkipped()
2183.1.1 by Aaron Bentley
Make test HTTP server's range handling more spec-compliant (Vincent Ladeuil)
893
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
894
    def test_http_proxy_without_scheme(self):
895
        # pycurl *ignores* invalid proxy env variables. If that
896
        # ever change in the future, this test will fail
897
        # indicating that pycurl do not ignore anymore such
898
        # variables.
899
        self.not_proxied_in_env({'http_proxy': self.proxy_address})
900
2183.1.1 by Aaron Bentley
Make test HTTP server's range handling more spec-compliant (Vincent Ladeuil)
901
2182.2.2 by v.ladeuil+lp at free
Thanks again to Aaron, the http server RFC2616 compliance
902
class TestRanges(object):
903
    """Test the Range header in GET methods..
904
905
    This MUST be used by daughter classes that also inherit from
906
    TestCaseWithWebserver.
907
908
    We can't inherit directly from TestCaseWithWebserver or the
909
    test framework will try to create an instance which cannot
910
    run, its implementation being incomplete.
911
    """
912
913
    def setUp(self):
914
        TestCaseWithWebserver.setUp(self)
915
        self.build_tree_contents([('a', '0123456789')],)
916
        server = self.get_readonly_server()
917
        self.transport = self._transport(server.get_url())
918
919
    def _file_contents(self, relpath, ranges, tail_amount=0):
920
         code, data = self.transport._get(relpath, ranges)
921
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
922
         for start, end in ranges:
923
             data.seek(start)
924
             yield data.read(end - start + 1)
925
926
    def _file_tail(self, relpath, tail_amount):
927
         code, data = self.transport._get(relpath, [], tail_amount)
928
         self.assertTrue(code in (200, 206),'_get returns: %d' % code)
929
         data.seek(-tail_amount + 1, 2)
930
         return data.read(tail_amount)
931
932
    def test_range_header(self):
933
        # Valid ranges
934
        map(self.assertEqual,['0', '234'],
935
            list(self._file_contents('a', [(0,0), (2,4)])),)
936
        # Tail
937
        self.assertEqual('789', self._file_tail('a', 3))
938
        # Syntactically invalid range
939
        self.assertRaises(errors.InvalidRange,
940
                          self.transport._get, 'a', [(4, 3)])
941
        # Semantically invalid range
942
        self.assertRaises(errors.InvalidRange,
943
                          self.transport._get, 'a', [(42, 128)])
944
945
946
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
947
    """Test the Range header in GET methods for urllib implementation"""
948
949
    _transport = HttpTransport_urllib
950
951
952
class TestRanges_pycurl(TestWithTransport_pycurl,
953
                        TestRanges,
954
                        TestCaseWithWebserver):
955
    """Test the Range header in GET methods for pycurl implementation"""
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
956
957
2164.2.16 by Vincent Ladeuil
Add tests.
958
class TestHTTPRedirections(object):
959
    """Test redirection between http servers.
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
960
961
    This MUST be used by daughter classes that also inherit from
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
962
    TestCaseWithRedirectedWebserver.
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
963
964
    We can't inherit directly from TestCaseWithTwoWebservers or the
965
    test framework will try to create an instance which cannot
966
    run, its implementation being incomplete. 
967
    """
968
969
    def create_transport_secondary_server(self):
970
        """Create the secondary server redirecting to the primary server"""
2164.2.16 by Vincent Ladeuil
Add tests.
971
        new = self.get_readonly_server()
972
973
        redirecting = HTTPServerRedirecting()
974
        redirecting.redirect_to(new.host, new.port)
975
        return redirecting
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
976
977
    def setUp(self):
2164.2.16 by Vincent Ladeuil
Add tests.
978
        super(TestHTTPRedirections, self).setUp()
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
979
        self.build_tree_contents([('a', '0123456789'),
980
                                  ('bundle',
981
                                  '# Bazaar revision bundle v0.9\n#\n')
982
                                  ],)
983
984
        self.old_transport = self._transport(self.old_server.get_url())
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
985
986
    def test_redirected(self):
2164.2.16 by Vincent Ladeuil
Add tests.
987
        self.assertRaises(errors.RedirectRequested, self.old_transport.get, 'a')
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
988
        t = self._transport(self.new_server.get_url())
989
        self.assertEqual('0123456789', t.get('a').read())
990
991
    def test_read_redirected_bundle_from_url(self):
992
        from bzrlib.bundle import read_bundle_from_url
993
        url = self.old_transport.abspath('bundle')
994
        bundle = read_bundle_from_url(url)
995
        # If read_bundle_from_url was successful we get an empty bundle
996
        self.assertEqual([], bundle.revisions)
2164.2.16 by Vincent Ladeuil
Add tests.
997
998
999
class TestHTTPRedirections_urllib(TestHTTPRedirections,
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
1000
                                  TestCaseWithRedirectedWebserver):
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
1001
    """Tests redirections for urllib implementation"""
1002
1003
    _transport = HttpTransport_urllib
1004
2164.2.16 by Vincent Ladeuil
Add tests.
1005
1006
1007
class TestHTTPRedirections_pycurl(TestWithTransport_pycurl,
1008
                                  TestHTTPRedirections,
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
1009
                                  TestCaseWithRedirectedWebserver):
2164.2.16 by Vincent Ladeuil
Add tests.
1010
    """Tests redirections for pycurl implementation"""
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
1011
1012
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
1013
class RedirectedRequest(_urllib2_wrappers.Request):
1014
    """Request following redirections"""
1015
1016
    init_orig = _urllib2_wrappers.Request.__init__
1017
1018
    def __init__(self, method, url, *args, **kwargs):
1019
        RedirectedRequest.init_orig(self, method, url, args, kwargs)
1020
        self.follow_redirections = True
1021
1022
1023
class TestHTTPSilentRedirections_urllib(TestCaseWithRedirectedWebserver):
1024
    """Test redirections provided by urllib.
1025
1026
    http implementations do not redirect silently anymore (they
1027
    do not redirect at all in fact). The mechanism is still in
1028
    place at the _urllib2_wrappers.Request level and these tests
1029
    exercise it.
1030
1031
    For the pycurl implementation
1032
    the redirection have been deleted as we may deprecate pycurl
1033
    and I have no place to keep a working implementation.
1034
    -- vila 20070212
1035
    """
1036
1037
    _transport = HttpTransport_urllib
1038
1039
    def setUp(self):
1040
        super(TestHTTPSilentRedirections_urllib, self).setUp()
1041
        self.setup_redirected_request()
1042
        self.addCleanup(self.cleanup_redirected_request)
1043
        self.build_tree_contents([('a','a'),
1044
                                  ('1/',),
1045
                                  ('1/a', 'redirected once'),
1046
                                  ('2/',),
1047
                                  ('2/a', 'redirected twice'),
1048
                                  ('3/',),
1049
                                  ('3/a', 'redirected thrice'),
1050
                                  ('4/',),
1051
                                  ('4/a', 'redirected 4 times'),
1052
                                  ('5/',),
1053
                                  ('5/a', 'redirected 5 times'),
1054
                                  ],)
1055
1056
        self.old_transport = self._transport(self.old_server.get_url())
1057
1058
    def setup_redirected_request(self):
1059
        self.original_class = _urllib2_wrappers.Request
1060
        _urllib2_wrappers.Request = RedirectedRequest
1061
1062
    def cleanup_redirected_request(self):
1063
        _urllib2_wrappers.Request = self.original_class
1064
1065
    def create_transport_secondary_server(self):
1066
        """Create the secondary server, redirections are defined in the tests"""
1067
        return HTTPServerRedirecting()
1068
1069
    def test_one_redirection(self):
1070
        t = self.old_transport
1071
1072
        req = RedirectedRequest('GET', t.abspath('a'))
1073
        req.follow_redirections = True
1074
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1075
                                       self.new_server.port)
1076
        self.old_server.redirections = \
1077
            [('(.*)', r'%s/1\1' % (new_prefix), 301),]
1078
        self.assertEquals('redirected once',t._perform(req).read())
1079
1080
    def test_five_redirections(self):
1081
        t = self.old_transport
1082
1083
        req = RedirectedRequest('GET', t.abspath('a'))
1084
        req.follow_redirections = True
1085
        old_prefix = 'http://%s:%s' % (self.old_server.host,
1086
                                       self.old_server.port)
1087
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1088
                                       self.new_server.port)
1089
        self.old_server.redirections = \
1090
            [('/1(.*)', r'%s/2\1' % (old_prefix), 302),
1091
             ('/2(.*)', r'%s/3\1' % (old_prefix), 303),
1092
             ('/3(.*)', r'%s/4\1' % (old_prefix), 307),
1093
             ('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1094
             ('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1095
             ]
1096
        self.assertEquals('redirected 5 times',t._perform(req).read())
1097
1098
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
1099
class TestDoCatchRedirections(TestCaseWithRedirectedWebserver):
1100
    """Test transport.do_catching_redirections.
1101
1102
    We arbitrarily choose to use urllib transports
1103
    """
1104
1105
    _transport = HttpTransport_urllib
1106
1107
    def setUp(self):
1108
        super(TestDoCatchRedirections, self).setUp()
1109
        self.build_tree_contents([('a', '0123456789'),],)
1110
1111
        self.old_transport = self._transport(self.old_server.get_url())
1112
1113
    def get_a(self, transport):
1114
        return transport.get('a')
1115
1116
    def test_no_redirection(self):
1117
        t = self._transport(self.new_server.get_url())
1118
1119
        # We use None for redirected so that we fail if redirected
1120
        self.assertEquals('0123456789',
1121
                          do_catching_redirections(self.get_a, t, None).read())
1122
1123
    def test_one_redirection(self):
1124
        self.redirections = 0
1125
1126
        def redirected(transport, exception, redirection_notice):
1127
            self.redirections += 1
1128
            dir, file = urlutils.split(exception.target)
1129
            return self._transport(dir)
1130
1131
        self.assertEquals('0123456789',
1132
                          do_catching_redirections(self.get_a,
1133
                                                   self.old_transport,
1134
                                                   redirected
1135
                                                   ).read())
1136
        self.assertEquals(1, self.redirections)
1137
1138
    def test_redirection_loop(self):
1139
1140
        def redirected(transport, exception, redirection_notice):
1141
            # By using the redirected url as a base dir for the
1142
            # *old* transport, we create a loop: a => a/a =>
1143
            # a/a/a
1144
            return self.old_transport.clone(exception.target)
1145
1146
        self.assertRaises(errors.TooManyRedirections, do_catching_redirections,
1147
                          self.get_a, self.old_transport, redirected)