/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2005-2011 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.16.68 by Martin Pool
- http url fixes suggested by Robey Pointer, and tests
16
3111.1.30 by Vincent Ladeuil
Update NEWS. Some cosmetic changes.
17
"""Tests for HTTP implementations.
3111.1.10 by Vincent Ladeuil
Finish http parameterization, 24 auth tests failing for pycurl (not
18
3111.1.30 by Vincent Ladeuil
Update NEWS. Some cosmetic changes.
19
This module defines a load_tests() method that parametrize tests classes for
20
transport implementation, http protocol versions and authentication schemes.
3111.1.10 by Vincent Ladeuil
Finish http parameterization, 24 auth tests failing for pycurl (not
21
"""
1540.3.3 by Martin Pool
Review updates of pycurl transport
22
1540.3.22 by Martin Pool
[patch] Add TestCase.assertIsInstance
23
# TODO: Should be renamed to bzrlib.transport.http.tests?
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
24
# TODO: What about renaming to bzrlib.tests.transport.http ?
1540.3.22 by Martin Pool
[patch] Add TestCase.assertIsInstance
25
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
26
import httplib
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
27
import SimpleHTTPServer
2000.2.2 by John Arbash Meinel
Update the urllib.has test.
28
import socket
2420.1.20 by Vincent Ladeuil
Fix test failure on pqm.
29
import sys
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
30
import threading
2000.2.2 by John Arbash Meinel
Update the urllib.has test.
31
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
32
import bzrlib
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
33
from bzrlib import (
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
34
    bzrdir,
2900.2.6 by Vincent Ladeuil
Make http aware of authentication config.
35
    config,
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
36
    errors,
37
    osutils,
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
38
    remote as _mod_remote,
3052.3.2 by Vincent Ladeuil
Add tests and fix trivial bugs and other typos.
39
    tests,
5652.1.1 by Vincent Ladeuil
Split ThreadWithException out of the tests hierarchy.
40
    thread,
3111.1.10 by Vincent Ladeuil
Finish http parameterization, 24 auth tests failing for pycurl (not
41
    transport,
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
42
    ui,
3995.2.2 by Martin Pool
Cope with read_bundle_from_url deprecation in test_http
43
    )
3102.1.1 by Vincent Ladeuil
Rename bzrlib/test/HTTPTestUtils.py to bzrlib/tests/http_utils.py and fix
44
from bzrlib.tests import (
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
45
    features,
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
46
    http_server,
3111.1.7 by Vincent Ladeuil
Further refactoring.
47
    http_utils,
5247.2.5 by Vincent Ladeuil
Some cleanups.
48
    test_server,
3102.1.1 by Vincent Ladeuil
Rename bzrlib/test/HTTPTestUtils.py to bzrlib/tests/http_utils.py and fix
49
    )
5462.3.15 by Martin Pool
Turn variations into scenario lists
50
from bzrlib.tests.scenarios import (
5462.3.21 by Martin Pool
Rename to load_tests_apply_scenarios
51
    load_tests_apply_scenarios,
5462.3.15 by Martin Pool
Turn variations into scenario lists
52
    multiply_scenarios,
5462.3.2 by Martin Pool
Split variations code to bzrlib.tests.variations
53
    )
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
54
from bzrlib.transport import (
55
    http,
56
    remote,
57
    )
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
58
from bzrlib.transport.http import (
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
59
    _urllib,
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
60
    _urllib2_wrappers,
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
61
    )
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
62
63
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
64
if features.pycurl.available():
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
65
    from bzrlib.transport.http._pycurl import PyCurlTransport
66
67
5462.3.21 by Martin Pool
Rename to load_tests_apply_scenarios
68
load_tests = load_tests_apply_scenarios
5462.3.15 by Martin Pool
Turn variations into scenario lists
69
70
71
def vary_by_http_client_implementation():
5462.3.1 by Martin Pool
Split out generation of scenario lists into TestVariations
72
    """Test the two libraries we can use, pycurl and urllib."""
5462.3.15 by Martin Pool
Turn variations into scenario lists
73
    transport_scenarios = [
74
        ('urllib', dict(_transport=_urllib.HttpTransport_urllib,
75
                        _server=http_server.HttpServer_urllib,
76
                        _url_protocol='http+urllib',)),
77
        ]
78
    if features.pycurl.available():
79
        transport_scenarios.append(
80
            ('pycurl', dict(_transport=PyCurlTransport,
81
                            _server=http_server.HttpServer_PyCurl,
82
                            _url_protocol='http+pycurl',)))
83
    return transport_scenarios
84
85
86
def vary_by_http_protocol_version():
5462.3.1 by Martin Pool
Split out generation of scenario lists into TestVariations
87
    """Test on http/1.0 and 1.1"""
5462.3.15 by Martin Pool
Turn variations into scenario lists
88
    return [
89
        ('HTTP/1.0',  dict(_protocol_version='HTTP/1.0')),
90
        ('HTTP/1.1',  dict(_protocol_version='HTTP/1.1')),
91
        ]
92
93
94
def vary_by_http_proxy_auth_scheme():
95
    return [
96
        ('basic', dict(_auth_server=http_utils.ProxyBasicAuthServer)),
97
        ('digest', dict(_auth_server=http_utils.ProxyDigestAuthServer)),
98
        ('basicdigest',
99
            dict(_auth_server=http_utils.ProxyBasicAndDigestAuthServer)),
100
        ]
101
102
103
def vary_by_http_auth_scheme():
104
    return [
105
        ('basic', dict(_auth_server=http_utils.HTTPBasicAuthServer)),
106
        ('digest', dict(_auth_server=http_utils.HTTPDigestAuthServer)),
107
        ('basicdigest',
108
            dict(_auth_server=http_utils.HTTPBasicAndDigestAuthServer)),
109
        ]
110
111
112
def vary_by_http_activity():
113
    activity_scenarios = [
114
        ('urllib,http', dict(_activity_server=ActivityHTTPServer,
115
                            _transport=_urllib.HttpTransport_urllib,)),
116
        ]
117
    if tests.HTTPSServerFeature.available():
118
        activity_scenarios.append(
119
            ('urllib,https', dict(_activity_server=ActivityHTTPSServer,
120
                                _transport=_urllib.HttpTransport_urllib,)),)
121
    if features.pycurl.available():
122
        activity_scenarios.append(
123
            ('pycurl,http', dict(_activity_server=ActivityHTTPServer,
124
                                _transport=PyCurlTransport,)),)
5462.3.1 by Martin Pool
Split out generation of scenario lists into TestVariations
125
        if tests.HTTPSServerFeature.available():
5462.3.15 by Martin Pool
Turn variations into scenario lists
126
            from bzrlib.tests import (
127
                ssl_certs,
128
                )
129
            # FIXME: Until we have a better way to handle self-signed
130
            # certificates (like allowing them in a test specific
131
            # authentication.conf for example), we need some specialized pycurl
132
            # transport for tests.
133
            class HTTPS_pycurl_transport(PyCurlTransport):
134
135
                def __init__(self, base, _from_transport=None):
136
                    super(HTTPS_pycurl_transport, self).__init__(
137
                        base, _from_transport)
138
                    self.cabundle = str(ssl_certs.build_path('ca.crt'))
139
140
            activity_scenarios.append(
141
                ('pycurl,https', dict(_activity_server=ActivityHTTPSServer,
142
                                    _transport=HTTPS_pycurl_transport,)),)
143
    return activity_scenarios
5462.3.1 by Martin Pool
Split out generation of scenario lists into TestVariations
144
145
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
146
class FakeManager(object):
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
147
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
148
    def __init__(self):
149
        self.credentials = []
2004.3.1 by vila
Test ConnectionError exceptions.
150
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
151
    def add_password(self, realm, host, username, password):
152
        self.credentials.append([realm, host, username, password])
153
1553.1.2 by James Henstridge
Add a test to make sure the user-agent header is being sent correctly.
154
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
155
class RecordingServer(object):
156
    """A fake HTTP server.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
157
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
158
    It records the bytes sent to it, and replies with a 200.
159
    """
160
4691.2.1 by Robert Collins
Add stronger test isolation by interception BzrDir.open and checking the thing being opened is known to the test suite.
161
    def __init__(self, expect_body_tail=None, scheme=''):
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
162
        """Constructor.
163
164
        :type expect_body_tail: str
165
        :param expect_body_tail: a reply won't be sent until this string is
166
            received.
167
        """
168
        self._expect_body_tail = expect_body_tail
169
        self.host = None
170
        self.port = None
171
        self.received_bytes = ''
4691.2.1 by Robert Collins
Add stronger test isolation by interception BzrDir.open and checking the thing being opened is known to the test suite.
172
        self.scheme = scheme
173
174
    def get_url(self):
175
        return '%s://%s:%s/' % (self.scheme, self.host, self.port)
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
176
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
177
    def start_server(self):
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
178
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
179
        self._sock.bind(('127.0.0.1', 0))
180
        self.host, self.port = self._sock.getsockname()
181
        self._ready = threading.Event()
5652.1.1 by Vincent Ladeuil
Split ThreadWithException out of the tests hierarchy.
182
        self._thread = test_server.TestThread(
5652.1.2 by Vincent Ladeuil
Use clearer names.
183
            sync_event=self._ready, target=self._accept_read_and_reply)
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
184
        self._thread.start()
4731.2.9 by Vincent Ladeuil
Implement a new -Ethreads to better track the leaks.
185
        if 'threads' in tests.selftest_debug_flags:
5247.5.29 by Vincent Ladeuil
Fixed as per jam's review.
186
            sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
4731.2.4 by Vincent Ladeuil
No more leaks in http tests.
187
        self._ready.wait()
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
188
189
    def _accept_read_and_reply(self):
190
        self._sock.listen(1)
191
        self._ready.set()
5247.2.5 by Vincent Ladeuil
Some cleanups.
192
        conn, address = self._sock.accept()
193
        if self._expect_body_tail is not None:
194
            while not self.received_bytes.endswith(self._expect_body_tail):
195
                self.received_bytes += conn.recv(4096)
196
            conn.sendall('HTTP/1.1 200 OK\r\n')
4731.2.4 by Vincent Ladeuil
No more leaks in http tests.
197
        try:
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
198
            self._sock.close()
199
        except socket.error:
200
            # The client may have already closed the socket.
201
            pass
202
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
203
    def stop_server(self):
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
204
        try:
4731.2.4 by Vincent Ladeuil
No more leaks in http tests.
205
            # Issue a fake connection to wake up the server and allow it to
206
            # finish quickly
5247.3.7 by Vincent Ladeuil
Provide connect_socket (socket.create_connection) for pythons older than 2.6.
207
            fake_conn = osutils.connect_socket((self.host, self.port))
4731.2.4 by Vincent Ladeuil
No more leaks in http tests.
208
            fake_conn.close()
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
209
        except socket.error:
210
            # We might have already closed it.  We don't care.
211
            pass
212
        self.host = None
213
        self.port = None
4731.2.4 by Vincent Ladeuil
No more leaks in http tests.
214
        self._thread.join()
4731.2.9 by Vincent Ladeuil
Implement a new -Ethreads to better track the leaks.
215
        if 'threads' in tests.selftest_debug_flags:
5247.5.29 by Vincent Ladeuil
Fixed as per jam's review.
216
            sys.stderr.write('Thread  joined: %s\n' % (self._thread.ident,))
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
217
218
4050.2.2 by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers.
219
class TestAuthHeader(tests.TestCase):
220
4284.1.1 by Vincent Ladeuil
Fix wrong realm extraction in http basic authentication (reported
221
    def parse_header(self, header, auth_handler_class=None):
222
        if auth_handler_class is None:
223
            auth_handler_class = _urllib2_wrappers.AbstractAuthHandler
224
        self.auth_handler =  auth_handler_class()
225
        return self.auth_handler._parse_auth_header(header)
4050.2.2 by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers.
226
227
    def test_empty_header(self):
228
        scheme, remainder = self.parse_header('')
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
229
        self.assertEqual('', scheme)
4050.2.2 by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers.
230
        self.assertIs(None, remainder)
231
232
    def test_negotiate_header(self):
233
        scheme, remainder = self.parse_header('Negotiate')
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
234
        self.assertEqual('negotiate', scheme)
4050.2.2 by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers.
235
        self.assertIs(None, remainder)
236
237
    def test_basic_header(self):
238
        scheme, remainder = self.parse_header(
239
            'Basic realm="Thou should not pass"')
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
240
        self.assertEqual('basic', scheme)
241
        self.assertEqual('realm="Thou should not pass"', remainder)
4050.2.2 by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers.
242
4284.1.1 by Vincent Ladeuil
Fix wrong realm extraction in http basic authentication (reported
243
    def test_basic_extract_realm(self):
244
        scheme, remainder = self.parse_header(
245
            'Basic realm="Thou should not pass"',
246
            _urllib2_wrappers.BasicAuthHandler)
247
        match, realm = self.auth_handler.extract_realm(remainder)
248
        self.assertTrue(match is not None)
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
249
        self.assertEqual('Thou should not pass', realm)
4284.1.1 by Vincent Ladeuil
Fix wrong realm extraction in http basic authentication (reported
250
4050.2.2 by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers.
251
    def test_digest_header(self):
252
        scheme, remainder = self.parse_header(
253
            'Digest realm="Thou should not pass"')
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
254
        self.assertEqual('digest', scheme)
255
        self.assertEqual('realm="Thou should not pass"', remainder)
4050.2.2 by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers.
256
257
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
258
class TestHTTPServer(tests.TestCase):
259
    """Test the HTTP servers implementations."""
260
261
    def test_invalid_protocol(self):
262
        class BogusRequestHandler(http_server.TestingHTTPRequestHandler):
263
264
            protocol_version = 'HTTP/0.1'
265
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
266
        self.assertRaises(httplib.UnknownProtocol,
267
                          http_server.HttpServer, BogusRequestHandler)
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
268
3111.1.17 by Vincent Ladeuil
Add tests for the protocol version parameter.
269
    def test_force_invalid_protocol(self):
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
270
        self.assertRaises(httplib.UnknownProtocol,
271
                          http_server.HttpServer, protocol_version='HTTP/0.1')
3111.1.17 by Vincent Ladeuil
Add tests for the protocol version parameter.
272
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
273
    def test_server_start_and_stop(self):
274
        server = http_server.HttpServer()
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
275
        self.addCleanup(server.stop_server)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
276
        server.start_server()
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
277
        self.assertTrue(server.server is not None)
278
        self.assertTrue(server.server.serving is not None)
5247.6.7 by Vincent Ladeuil
Merge propagate-exceptions into http-leaks resolving conflicts
279
        self.assertTrue(server.server.serving)
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
280
281
    def test_create_http_server_one_zero(self):
282
        class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
283
284
            protocol_version = 'HTTP/1.0'
285
286
        server = http_server.HttpServer(RequestHandlerOneZero)
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
287
        self.start_server(server)
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
288
        self.assertIsInstance(server.server, http_server.TestingHTTPServer)
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
289
290
    def test_create_http_server_one_one(self):
291
        class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
292
293
            protocol_version = 'HTTP/1.1'
294
295
        server = http_server.HttpServer(RequestHandlerOneOne)
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
296
        self.start_server(server)
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
297
        self.assertIsInstance(server.server,
3111.1.17 by Vincent Ladeuil
Add tests for the protocol version parameter.
298
                              http_server.TestingThreadingHTTPServer)
299
300
    def test_create_http_server_force_one_one(self):
301
        class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
302
303
            protocol_version = 'HTTP/1.0'
304
305
        server = http_server.HttpServer(RequestHandlerOneZero,
306
                                        protocol_version='HTTP/1.1')
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
307
        self.start_server(server)
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
308
        self.assertIsInstance(server.server,
3111.1.17 by Vincent Ladeuil
Add tests for the protocol version parameter.
309
                              http_server.TestingThreadingHTTPServer)
310
311
    def test_create_http_server_force_one_zero(self):
312
        class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
313
314
            protocol_version = 'HTTP/1.1'
315
316
        server = http_server.HttpServer(RequestHandlerOneOne,
317
                                        protocol_version='HTTP/1.0')
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
318
        self.start_server(server)
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
319
        self.assertIsInstance(server.server,
3111.1.17 by Vincent Ladeuil
Add tests for the protocol version parameter.
320
                              http_server.TestingHTTPServer)
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
321
322
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
323
class TestWithTransport_pycurl(object):
324
    """Test case to inherit from if pycurl is present"""
325
326
    def _get_pycurl_maybe(self):
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
327
        self.requireFeature(features.pycurl)
328
        return PyCurlTransport
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
329
330
    _transport = property(_get_pycurl_maybe)
331
332
3052.3.2 by Vincent Ladeuil
Add tests and fix trivial bugs and other typos.
333
class TestHttpUrls(tests.TestCase):
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
334
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
335
    # TODO: This should be moved to authorization tests once they
336
    # are written.
2004.1.40 by v.ladeuil+lp at free
Fix the race condition again and correct some small typos to be in
337
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
338
    def test_url_parsing(self):
339
        f = FakeManager()
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
340
        url = http.extract_auth('http://example.com', f)
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
341
        self.assertEqual('http://example.com', url)
342
        self.assertEqual(0, len(f.credentials))
3111.1.30 by Vincent Ladeuil
Update NEWS. Some cosmetic changes.
343
        url = http.extract_auth(
4988.4.2 by Martin Pool
Change url to canonical.com or wiki, plus some doc improvements in passing
344
            'http://user:pass@example.com/bzr/bzr.dev', f)
345
        self.assertEqual('http://example.com/bzr/bzr.dev', url)
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
346
        self.assertEqual(1, len(f.credentials))
4988.4.2 by Martin Pool
Change url to canonical.com or wiki, plus some doc improvements in passing
347
        self.assertEqual([None, 'example.com', 'user', 'pass'],
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
348
                         f.credentials[0])
2004.3.1 by vila
Test ConnectionError exceptions.
349
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
350
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
351
class TestHttpTransportUrls(tests.TestCase):
352
    """Test the http urls."""
353
5462.3.15 by Martin Pool
Turn variations into scenario lists
354
    scenarios = vary_by_http_client_implementation()
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
355
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
356
    def test_abs_url(self):
357
        """Construction of absolute http URLs"""
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
358
        t = self._transport('http://example.com/bzr/bzr.dev/')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
359
        eq = self.assertEqualDiff
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
360
        eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
361
        eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
362
        eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
363
        eq(t.abspath('.bzr/1//2/./3'),
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
364
           'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
365
366
    def test_invalid_http_urls(self):
367
        """Trap invalid construction of urls"""
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
368
        self._transport('http://example.com/bzr/bzr.dev/')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
369
        self.assertRaises(errors.InvalidURL,
370
                          self._transport,
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
371
                          'http://http://example.com/bzr/bzr.dev/')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
372
373
    def test_http_root_urls(self):
374
        """Construction of URLs from server root"""
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
375
        t = self._transport('http://example.com/')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
376
        eq = self.assertEqualDiff
377
        eq(t.abspath('.bzr/tree-version'),
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
378
           'http://example.com/.bzr/tree-version')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
379
380
    def test_http_impl_urls(self):
381
        """There are servers which ask for particular clients to connect"""
382
        server = self._server()
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
383
        server.start_server()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
384
        try:
385
            url = server.get_url()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
386
            self.assertTrue(url.startswith('%s://' % self._url_protocol))
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
387
        finally:
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
388
            server.stop_server()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
389
390
3111.1.9 by Vincent Ladeuil
Most refactoring regarding parameterization for urllib/pycurl and custom
391
class TestHttps_pycurl(TestWithTransport_pycurl, tests.TestCase):
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
392
393
    # TODO: This should really be moved into another pycurl
394
    # specific test. When https tests will be implemented, take
395
    # this one into account.
396
    def test_pycurl_without_https_support(self):
397
        """Test that pycurl without SSL do not fail with a traceback.
398
399
        For the purpose of the test, we force pycurl to ignore
400
        https by supplying a fake version_info that do not
401
        support it.
402
        """
4913.2.13 by John Arbash Meinel
Finish the pycurl feature.
403
        self.requireFeature(features.pycurl)
4926.1.1 by Vincent Ladeuil
Fix ModuleFeature() side-effect.
404
        # Import the module locally now that we now it's available.
405
        pycurl = features.pycurl.module
3111.1.14 by Vincent Ladeuil
Fix test leakage.
406
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
407
        self.overrideAttr(pycurl, 'version_info',
408
                          # Fake the pycurl version_info This was taken from
409
                          # a windows pycurl without SSL (thanks to bialix)
410
                          lambda : (2,
411
                                    '7.13.2',
412
                                    462082,
413
                                    'i386-pc-win32',
414
                                    2576,
415
                                    None,
416
                                    0,
417
                                    None,
418
                                    ('ftp', 'gopher', 'telnet',
419
                                     'dict', 'ldap', 'http', 'file'),
420
                                    None,
421
                                    0,
422
                                    None))
4926.1.1 by Vincent Ladeuil
Fix ModuleFeature() side-effect.
423
        self.assertRaises(errors.DependencyNotPresent, self._transport,
424
                          'https://launchpad.net')
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
425
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
426
427
class TestHTTPConnections(http_utils.TestCaseWithWebserver):
428
    """Test the http connections."""
429
5462.3.15 by Martin Pool
Turn variations into scenario lists
430
    scenarios = multiply_scenarios(
431
        vary_by_http_client_implementation(), 
432
        vary_by_http_protocol_version(),
433
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
434
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
435
    def setUp(self):
436
        http_utils.TestCaseWithWebserver.setUp(self)
437
        self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
438
                        transport=self.get_transport())
439
440
    def test_http_has(self):
441
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
442
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
443
        self.assertEqual(t.has('foo/bar'), True)
444
        self.assertEqual(len(server.logs), 1)
445
        self.assertContainsRe(server.logs[0],
446
            r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
447
448
    def test_http_has_not_found(self):
449
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
450
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
451
        self.assertEqual(t.has('not-found'), False)
452
        self.assertContainsRe(server.logs[1],
453
            r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
454
455
    def test_http_get(self):
456
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
457
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
458
        fp = t.get('foo/bar')
459
        self.assertEqualDiff(
460
            fp.read(),
461
            'contents of foo/bar\n')
462
        self.assertEqual(len(server.logs), 1)
463
        self.assertTrue(server.logs[0].find(
464
            '"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s'
465
            % bzrlib.__version__) > -1)
466
467
    def test_has_on_bogus_host(self):
468
        # Get a free address and don't 'accept' on it, so that we
469
        # can be sure there is no http handler there, but set a
470
        # reasonable timeout to not slow down tests too much.
471
        default_timeout = socket.getdefaulttimeout()
472
        try:
473
            socket.setdefaulttimeout(2)
474
            s = socket.socket()
475
            s.bind(('localhost', 0))
476
            t = self._transport('http://%s:%s/' % s.getsockname())
477
            self.assertRaises(errors.ConnectionError, t.has, 'foo/bar')
478
        finally:
479
            socket.setdefaulttimeout(default_timeout)
480
481
482
class TestHttpTransportRegistration(tests.TestCase):
483
    """Test registrations of various http implementations"""
484
5462.3.15 by Martin Pool
Turn variations into scenario lists
485
    scenarios = vary_by_http_client_implementation()
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
486
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
487
    def test_http_registered(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
488
        t = transport.get_transport('%s://foo.com/' % self._url_protocol)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
489
        self.assertIsInstance(t, transport.Transport)
490
        self.assertIsInstance(t, self._transport)
491
492
493
class TestPost(tests.TestCase):
494
5462.3.15 by Martin Pool
Turn variations into scenario lists
495
    scenarios = multiply_scenarios(
496
        vary_by_http_client_implementation(), 
497
        vary_by_http_protocol_version(),
498
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
499
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
500
    def test_post_body_is_received(self):
4691.2.1 by Robert Collins
Add stronger test isolation by interception BzrDir.open and checking the thing being opened is known to the test suite.
501
        server = RecordingServer(expect_body_tail='end-of-body',
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
502
                                 scheme=self._url_protocol)
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
503
        self.start_server(server)
4691.2.1 by Robert Collins
Add stronger test isolation by interception BzrDir.open and checking the thing being opened is known to the test suite.
504
        url = server.get_url()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
505
        # FIXME: needs a cleanup -- vila 20100611
506
        http_transport = transport.get_transport(url)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
507
        code, response = http_transport._post('abc def end-of-body')
508
        self.assertTrue(
509
            server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
510
        self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
5514.1.1 by Vincent Ladeuil
Correctly set the Content-Type header when POSTing.
511
        self.assertTrue('content-type: application/octet-stream\r'
512
                        in server.received_bytes.lower())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
513
        # The transport should not be assuming that the server can accept
514
        # chunked encoding the first time it connects, because HTTP/1.1, so we
515
        # check for the literal string.
516
        self.assertTrue(
517
            server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
518
519
3052.3.2 by Vincent Ladeuil
Add tests and fix trivial bugs and other typos.
520
class TestRangeHeader(tests.TestCase):
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
521
    """Test range_header method"""
522
523
    def check_header(self, value, ranges=[], tail=0):
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
524
        offsets = [ (start, end - start + 1) for start, end in ranges]
3111.1.10 by Vincent Ladeuil
Finish http parameterization, 24 auth tests failing for pycurl (not
525
        coalesce = transport.Transport._coalesce_offsets
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
526
        coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
527
        range_header = http.HttpTransportBase._range_header
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
528
        self.assertEqual(value, range_header(coalesced, tail))
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
529
530
    def test_range_header_single(self):
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
531
        self.check_header('0-9', ranges=[(0,9)])
532
        self.check_header('100-109', ranges=[(100,109)])
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
533
534
    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=
535
        self.check_header('-10', tail=10)
536
        self.check_header('-50', tail=50)
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
537
538
    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=
539
        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
540
                          ranges=[(0,9), (100, 200), (300,5000)])
541
542
    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=
543
        self.check_header('0-9,300-5000,-50',
1786.1.28 by John Arbash Meinel
Update and add tests for the HttpTransportBase.range_header
544
                          ranges=[(0,9), (300,5000)],
545
                          tail=50)
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
546
2004.1.15 by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests.
547
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
548
class TestSpecificRequestHandler(http_utils.TestCaseWithWebserver):
549
    """Tests a specific request handler.
550
3111.1.31 by Vincent Ladeuil
Review feeback.
551
    Daughter classes are expected to override _req_handler_class
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
552
    """
553
5462.3.15 by Martin Pool
Turn variations into scenario lists
554
    scenarios = multiply_scenarios(
555
        vary_by_http_client_implementation(), 
556
        vary_by_http_protocol_version(),
557
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
558
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
559
    # Provide a useful default
560
    _req_handler_class = http_server.TestingHTTPRequestHandler
561
562
    def create_transport_readonly_server(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
563
        server = http_server.HttpServer(self._req_handler_class,
564
                                        protocol_version=self._protocol_version)
565
        server._url_protocol = self._url_protocol
566
        return server
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
567
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
568
    def _testing_pycurl(self):
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
569
        # TODO: This is duplicated for lots of the classes in this file
570
        return (features.pycurl.available()
571
                and self._transport == PyCurlTransport)
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
572
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
573
574
class WallRequestHandler(http_server.TestingHTTPRequestHandler):
575
    """Whatever request comes in, close the connection"""
576
4731.2.3 by Vincent Ladeuil
Reduce the leaking http tests from ~200 to ~5.
577
    def _handle_one_request(self):
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
578
        """Handle a single HTTP request, by abruptly closing the connection"""
579
        self.close_connection = 1
580
581
582
class TestWallServer(TestSpecificRequestHandler):
583
    """Tests exceptions during the connection phase"""
584
585
    _req_handler_class = WallRequestHandler
586
587
    def test_http_has(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
588
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
589
        # Unfortunately httplib (see HTTPResponse._read_status
590
        # for details) make no distinction between a closed
591
        # socket and badly formatted status line, so we can't
592
        # just test for ConnectionError, we have to test
4628.1.2 by Vincent Ladeuil
More complete fix.
593
        # InvalidHttpResponse too. And pycurl may raise ConnectionReset
594
        # instead of ConnectionError too.
595
        self.assertRaises(( errors.ConnectionError, errors.ConnectionReset,
596
                            errors.InvalidHttpResponse),
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
597
                          t.has, 'foo/bar')
598
599
    def test_http_get(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
600
        t = self.get_readonly_transport()
4628.1.2 by Vincent Ladeuil
More complete fix.
601
        self.assertRaises((errors.ConnectionError, errors.ConnectionReset,
602
                           errors.InvalidHttpResponse),
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
603
                          t.get, 'foo/bar')
604
605
606
class BadStatusRequestHandler(http_server.TestingHTTPRequestHandler):
607
    """Whatever request comes in, returns a bad status"""
608
609
    def parse_request(self):
610
        """Fakes handling a single HTTP request, returns a bad status"""
611
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
612
        self.send_response(0, "Bad status")
613
        self.close_connection = 1
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
614
        return False
615
616
617
class TestBadStatusServer(TestSpecificRequestHandler):
618
    """Tests bad status from server."""
619
620
    _req_handler_class = BadStatusRequestHandler
621
622
    def test_http_has(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
623
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
624
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
625
626
    def test_http_get(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
627
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
628
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
629
630
631
class InvalidStatusRequestHandler(http_server.TestingHTTPRequestHandler):
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
632
    """Whatever request comes in, returns an invalid status"""
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
633
634
    def parse_request(self):
635
        """Fakes handling a single HTTP request, returns a bad status"""
636
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
637
        self.wfile.write("Invalid status line\r\n")
5247.6.6 by Vincent Ladeuil
Closing the connection is what pycurl was waiting for.
638
        # If we don't close the connection pycurl will hang. Since this is a
639
        # stress test we don't *have* to respect the protocol, but we don't
640
        # have to sabotage it too much either.
641
        self.close_connection = True
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
642
        return False
643
644
645
class TestInvalidStatusServer(TestBadStatusServer):
646
    """Tests invalid status from server.
647
648
    Both implementations raises the same error as for a bad status.
649
    """
650
651
    _req_handler_class = InvalidStatusRequestHandler
652
653
654
class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
655
    """Whatever request comes in, returns a bad protocol version"""
656
657
    def parse_request(self):
658
        """Fakes handling a single HTTP request, returns a bad status"""
659
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
660
        # Returns an invalid protocol version, but curl just
661
        # ignores it and those cannot be tested.
662
        self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
663
                                           404,
664
                                           'Look at my protocol version'))
665
        return False
666
667
668
class TestBadProtocolServer(TestSpecificRequestHandler):
669
    """Tests bad protocol from server."""
670
671
    _req_handler_class = BadProtocolRequestHandler
672
673
    def setUp(self):
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
674
        if self._testing_pycurl():
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
675
            raise tests.TestNotApplicable(
676
                "pycurl doesn't check the protocol version")
677
        super(TestBadProtocolServer, self).setUp()
678
679
    def test_http_has(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
680
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
681
        self.assertRaises(errors.InvalidHttpResponse, t.has, 'foo/bar')
682
683
    def test_http_get(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
684
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
685
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'foo/bar')
686
687
688
class ForbiddenRequestHandler(http_server.TestingHTTPRequestHandler):
689
    """Whatever request comes in, returns a 403 code"""
690
691
    def parse_request(self):
692
        """Handle a single HTTP request, by replying we cannot handle it"""
693
        ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
694
        self.send_error(403)
695
        return False
696
697
698
class TestForbiddenServer(TestSpecificRequestHandler):
699
    """Tests forbidden server"""
700
701
    _req_handler_class = ForbiddenRequestHandler
702
703
    def test_http_has(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
704
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
705
        self.assertRaises(errors.TransportError, t.has, 'foo/bar')
706
707
    def test_http_get(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
708
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
709
        self.assertRaises(errors.TransportError, t.get, 'foo/bar')
710
711
3052.3.2 by Vincent Ladeuil
Add tests and fix trivial bugs and other typos.
712
class TestRecordingServer(tests.TestCase):
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
713
714
    def test_create(self):
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
715
        server = RecordingServer(expect_body_tail=None)
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
716
        self.assertEqual('', server.received_bytes)
717
        self.assertEqual(None, server.host)
718
        self.assertEqual(None, server.port)
719
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
720
    def test_setUp_and_stop(self):
3111.1.29 by Vincent Ladeuil
Cancel RecordingServer move, that was useless.
721
        server = RecordingServer(expect_body_tail=None)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
722
        server.start_server()
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
723
        try:
724
            self.assertNotEqual(None, server.host)
725
            self.assertNotEqual(None, server.port)
726
        finally:
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
727
            server.stop_server()
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
728
        self.assertEqual(None, server.host)
729
        self.assertEqual(None, server.port)
730
731
    def test_send_receive_bytes(self):
4691.2.1 by Robert Collins
Add stronger test isolation by interception BzrDir.open and checking the thing being opened is known to the test suite.
732
        server = RecordingServer(expect_body_tail='c', scheme='http')
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
733
        self.start_server(server)
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
734
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
735
        sock.connect((server.host, server.port))
5011.3.11 by Andrew Bennetts
Consolidate changes, try to minimise unnecessary changes and tidy up those that kept.
736
        sock.sendall('abc')
737
        self.assertEqual('HTTP/1.1 200 OK\r\n',
738
                         osutils.recv_all(sock, 4096))
2018.2.9 by Andrew Bennetts
(Andrew Bennetts, Robert Collins) Add test_http.RecordingServer, and use it to
739
        self.assertEqual('abc', server.received_bytes)
2004.1.29 by v.ladeuil+lp at free
New tests for http range requests handling.
740
741
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
742
class TestRangeRequestServer(TestSpecificRequestHandler):
743
    """Tests readv requests against server.
744
745
    We test against default "normal" server.
746
    """
747
748
    def setUp(self):
749
        super(TestRangeRequestServer, self).setUp()
750
        self.build_tree_contents([('a', '0123456789')],)
751
752
    def test_readv(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
753
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
754
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
755
        self.assertEqual(l[0], (0, '0'))
756
        self.assertEqual(l[1], (1, '1'))
757
        self.assertEqual(l[2], (3, '34'))
758
        self.assertEqual(l[3], (9, '9'))
759
760
    def test_readv_out_of_order(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
761
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
762
        l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
763
        self.assertEqual(l[0], (1, '1'))
764
        self.assertEqual(l[1], (9, '9'))
765
        self.assertEqual(l[2], (0, '0'))
766
        self.assertEqual(l[3], (3, '34'))
767
768
    def test_readv_invalid_ranges(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
769
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
770
771
        # This is intentionally reading off the end of the file
772
        # since we are sure that it cannot get there
773
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
774
                              t.readv, 'a', [(1,1), (8,10)])
775
776
        # This is trying to seek past the end of the file, it should
777
        # also raise a special error
778
        self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
779
                              t.readv, 'a', [(12,2)])
780
781
    def test_readv_multiple_get_requests(self):
782
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
783
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
784
        # force transport to issue multiple requests
785
        t._max_readv_combine = 1
786
        t._max_get_ranges = 1
787
        l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
788
        self.assertEqual(l[0], (0, '0'))
789
        self.assertEqual(l[1], (1, '1'))
790
        self.assertEqual(l[2], (3, '34'))
791
        self.assertEqual(l[3], (9, '9'))
792
        # The server should have issued 4 requests
793
        self.assertEqual(4, server.GET_request_nb)
794
795
    def test_readv_get_max_size(self):
796
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
797
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
798
        # force transport to issue multiple requests by limiting the number of
799
        # bytes by request. Note that this apply to coalesced offsets only, a
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
800
        # single range will keep its size even if bigger than the limit.
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
801
        t._get_max_size = 2
802
        l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
803
        self.assertEqual(l[0], (0, '0'))
804
        self.assertEqual(l[1], (1, '1'))
805
        self.assertEqual(l[2], (2, '2345'))
806
        self.assertEqual(l[3], (6, '6789'))
807
        # The server should have issued 3 requests
808
        self.assertEqual(3, server.GET_request_nb)
809
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
810
    def test_complete_readv_leave_pipe_clean(self):
811
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
812
        t = self.get_readonly_transport()
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
813
        # force transport to issue multiple requests
814
        t._get_max_size = 2
5462.3.16 by Martin Pool
pyflakes cleanups
815
        list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
816
        # The server should have issued 3 requests
817
        self.assertEqual(3, server.GET_request_nb)
818
        self.assertEqual('0123456789', t.get_bytes('a'))
819
        self.assertEqual(4, server.GET_request_nb)
820
821
    def test_incomplete_readv_leave_pipe_clean(self):
822
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
823
        t = self.get_readonly_transport()
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
824
        # force transport to issue multiple requests
825
        t._get_max_size = 2
826
        # Don't collapse readv results into a list so that we leave unread
827
        # bytes on the socket
828
        ireadv = iter(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
829
        self.assertEqual((0, '0'), ireadv.next())
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
830
        # The server should have issued one request so far
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
831
        self.assertEqual(1, server.GET_request_nb)
832
        self.assertEqual('0123456789', t.get_bytes('a'))
833
        # get_bytes issued an additional request, the readv pending ones are
834
        # lost
835
        self.assertEqual(2, server.GET_request_nb)
836
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
837
838
class SingleRangeRequestHandler(http_server.TestingHTTPRequestHandler):
839
    """Always reply to range request as if they were single.
840
841
    Don't be explicit about it, just to annoy the clients.
842
    """
843
844
    def get_multiple_ranges(self, file, file_size, ranges):
845
        """Answer as if it was a single range request and ignores the rest"""
846
        (start, end) = ranges[0]
847
        return self.get_single_range(file, file_size, start, end)
848
849
850
class TestSingleRangeRequestServer(TestRangeRequestServer):
851
    """Test readv against a server which accept only single range requests"""
852
853
    _req_handler_class = SingleRangeRequestHandler
854
855
856
class SingleOnlyRangeRequestHandler(http_server.TestingHTTPRequestHandler):
857
    """Only reply to simple range requests, errors out on multiple"""
858
859
    def get_multiple_ranges(self, file, file_size, ranges):
860
        """Refuses the multiple ranges request"""
861
        if len(ranges) > 1:
862
            file.close()
863
            self.send_error(416, "Requested range not satisfiable")
864
            return
865
        (start, end) = ranges[0]
866
        return self.get_single_range(file, file_size, start, end)
867
868
869
class TestSingleOnlyRangeRequestServer(TestRangeRequestServer):
870
    """Test readv against a server which only accept single range requests"""
871
872
    _req_handler_class = SingleOnlyRangeRequestHandler
873
874
875
class NoRangeRequestHandler(http_server.TestingHTTPRequestHandler):
876
    """Ignore range requests without notice"""
877
878
    def do_GET(self):
879
        # Update the statistics
880
        self.server.test_case_server.GET_request_nb += 1
881
        # Just bypass the range handling done by TestingHTTPRequestHandler
882
        return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
883
884
885
class TestNoRangeRequestServer(TestRangeRequestServer):
886
    """Test readv against a server which do not accept range requests"""
887
888
    _req_handler_class = NoRangeRequestHandler
889
890
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
891
class MultipleRangeWithoutContentLengthRequestHandler(
892
    http_server.TestingHTTPRequestHandler):
893
    """Reply to multiple range requests without content length header."""
894
895
    def get_multiple_ranges(self, file, file_size, ranges):
896
        self.send_response(206)
897
        self.send_header('Accept-Ranges', 'bytes')
5462.3.16 by Martin Pool
pyflakes cleanups
898
        # XXX: this is strange; the 'random' name below seems undefined and
5462.3.19 by Martin Pool
Mention bug 658773 in comment
899
        # yet the tests pass -- mbp 2010-10-11 bug 658773
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
900
        boundary = "%d" % random.randint(0,0x7FFFFFFF)
901
        self.send_header("Content-Type",
902
                         "multipart/byteranges; boundary=%s" % boundary)
903
        self.end_headers()
904
        for (start, end) in ranges:
905
            self.wfile.write("--%s\r\n" % boundary)
906
            self.send_header("Content-type", 'application/octet-stream')
907
            self.send_header("Content-Range", "bytes %d-%d/%d" % (start,
908
                                                                  end,
909
                                                                  file_size))
910
            self.end_headers()
911
            self.send_range_content(file, start, end - start + 1)
912
        # Final boundary
913
        self.wfile.write("--%s\r\n" % boundary)
914
915
916
class TestMultipleRangeWithoutContentLengthServer(TestRangeRequestServer):
917
918
    _req_handler_class = MultipleRangeWithoutContentLengthRequestHandler
919
3146.3.2 by Vincent Ladeuil
Fix #179368 by keeping the current range hint on ShortReadvErrors.
920
921
class TruncatedMultipleRangeRequestHandler(
922
    http_server.TestingHTTPRequestHandler):
923
    """Reply to multiple range requests truncating the last ones.
924
925
    This server generates responses whose Content-Length describes all the
926
    ranges, but fail to include the last ones leading to client short reads.
927
    This has been observed randomly with lighttpd (bug #179368).
928
    """
929
930
    _truncated_ranges = 2
931
932
    def get_multiple_ranges(self, file, file_size, ranges):
933
        self.send_response(206)
934
        self.send_header('Accept-Ranges', 'bytes')
935
        boundary = 'tagada'
936
        self.send_header('Content-Type',
937
                         'multipart/byteranges; boundary=%s' % boundary)
938
        boundary_line = '--%s\r\n' % boundary
939
        # Calculate the Content-Length
940
        content_length = 0
941
        for (start, end) in ranges:
942
            content_length += len(boundary_line)
943
            content_length += self._header_line_length(
944
                'Content-type', 'application/octet-stream')
945
            content_length += self._header_line_length(
946
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
947
            content_length += len('\r\n') # end headers
948
            content_length += end - start # + 1
949
        content_length += len(boundary_line)
950
        self.send_header('Content-length', content_length)
951
        self.end_headers()
952
953
        # Send the multipart body
954
        cur = 0
955
        for (start, end) in ranges:
956
            self.wfile.write(boundary_line)
957
            self.send_header('Content-type', 'application/octet-stream')
958
            self.send_header('Content-Range', 'bytes %d-%d/%d'
959
                             % (start, end, file_size))
960
            self.end_headers()
961
            if cur + self._truncated_ranges >= len(ranges):
962
                # Abruptly ends the response and close the connection
963
                self.close_connection = 1
964
                return
965
            self.send_range_content(file, start, end - start + 1)
966
            cur += 1
5504.4.1 by Vincent Ladeuil
Fix http test spurious failures and get rid of some useless messages in log.
967
        # Final boundary
3146.3.2 by Vincent Ladeuil
Fix #179368 by keeping the current range hint on ShortReadvErrors.
968
        self.wfile.write(boundary_line)
969
970
971
class TestTruncatedMultipleRangeServer(TestSpecificRequestHandler):
972
973
    _req_handler_class = TruncatedMultipleRangeRequestHandler
974
975
    def setUp(self):
976
        super(TestTruncatedMultipleRangeServer, self).setUp()
977
        self.build_tree_contents([('a', '0123456789')],)
978
979
    def test_readv_with_short_reads(self):
980
        server = self.get_readonly_server()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
981
        t = self.get_readonly_transport()
3146.3.2 by Vincent Ladeuil
Fix #179368 by keeping the current range hint on ShortReadvErrors.
982
        # Force separate ranges for each offset
983
        t._bytes_to_read_before_seek = 0
984
        ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
985
        self.assertEqual((0, '0'), ireadv.next())
986
        self.assertEqual((2, '2'), ireadv.next())
987
        if not self._testing_pycurl():
988
            # Only one request have been issued so far (except for pycurl that
989
            # try to read the whole response at once)
990
            self.assertEqual(1, server.GET_request_nb)
991
        self.assertEqual((4, '45'), ireadv.next())
992
        self.assertEqual((9, '9'), ireadv.next())
993
        # Both implementations issue 3 requests but:
994
        # - urllib does two multiple (4 ranges, then 2 ranges) then a single
995
        #   range,
996
        # - pycurl does two multiple (4 ranges, 4 ranges) then a single range
997
        self.assertEqual(3, server.GET_request_nb)
998
        # Finally the client have tried a single range request and stays in
999
        # that mode
1000
        self.assertEqual('single', t._range_hint)
1001
5504.4.1 by Vincent Ladeuil
Fix http test spurious failures and get rid of some useless messages in log.
1002
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1003
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1004
    """Errors out when range specifiers exceed the limit"""
1005
1006
    def get_multiple_ranges(self, file, file_size, ranges):
1007
        """Refuses the multiple ranges request"""
1008
        tcs = self.server.test_case_server
1009
        if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
1010
            file.close()
1011
            # Emulate apache behavior
1012
            self.send_error(400, "Bad Request")
1013
            return
1014
        return http_server.TestingHTTPRequestHandler.get_multiple_ranges(
1015
            self, file, file_size, ranges)
1016
1017
1018
class LimitedRangeHTTPServer(http_server.HttpServer):
1019
    """An HttpServer erroring out on requests with too much range specifiers"""
1020
1021
    def __init__(self, request_handler=LimitedRangeRequestHandler,
1022
                 protocol_version=None,
1023
                 range_limit=None):
1024
        http_server.HttpServer.__init__(self, request_handler,
1025
                                        protocol_version=protocol_version)
1026
        self.range_limit = range_limit
1027
1028
1029
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1030
    """Tests readv requests against a server erroring out on too much ranges."""
1031
5462.3.15 by Martin Pool
Turn variations into scenario lists
1032
    scenarios = multiply_scenarios(
1033
        vary_by_http_client_implementation(), 
1034
        vary_by_http_protocol_version(),
1035
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1036
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
1037
    # Requests with more range specifiers will error out
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1038
    range_limit = 3
1039
1040
    def create_transport_readonly_server(self):
1041
        return LimitedRangeHTTPServer(range_limit=self.range_limit,
1042
                                      protocol_version=self._protocol_version)
1043
1044
    def setUp(self):
1045
        http_utils.TestCaseWithWebserver.setUp(self)
1046
        # We need to manipulate ranges that correspond to real chunks in the
1047
        # response, so we build a content appropriately.
1048
        filler = ''.join(['abcdefghij' for x in range(102)])
1049
        content = ''.join(['%04d' % v + filler for v in range(16)])
1050
        self.build_tree_contents([('a', content)],)
1051
1052
    def test_few_ranges(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1053
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1054
        l = list(t.readv('a', ((0, 4), (1024, 4), )))
1055
        self.assertEqual(l[0], (0, '0000'))
1056
        self.assertEqual(l[1], (1024, '0001'))
1057
        self.assertEqual(1, self.get_readonly_server().GET_request_nb)
1058
1059
    def test_more_ranges(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1060
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1061
        l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
1062
        self.assertEqual(l[0], (0, '0000'))
1063
        self.assertEqual(l[1], (1024, '0001'))
1064
        self.assertEqual(l[2], (4096, '0004'))
1065
        self.assertEqual(l[3], (8192, '0008'))
1066
        # The server will refuse to serve the first request (too much ranges),
3199.1.2 by Vincent Ladeuil
Fix two more leaked log files.
1067
        # a second request will succeed.
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1068
        self.assertEqual(2, self.get_readonly_server().GET_request_nb)
1069
1070
3052.3.2 by Vincent Ladeuil
Add tests and fix trivial bugs and other typos.
1071
class TestHttpProxyWhiteBox(tests.TestCase):
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
1072
    """Whitebox test proxy http authorization.
1073
2420.1.3 by Vincent Ladeuil
Implement http proxy basic authentication.
1074
    Only the urllib implementation is tested here.
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
1075
    """
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
1076
1077
    def _proxied_request(self):
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
1078
        handler = _urllib2_wrappers.ProxyHandler()
1079
        request = _urllib2_wrappers.Request('GET','http://baz/buzzle')
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
1080
        handler.set_proxy(request, 'http')
1081
        return request
1082
5639.2.2 by Vincent Ladeuil
Add tests and comments to clarify the feature.
1083
    def assertEvaluateProxyBypass(self, expected, host, no_proxy):
1084
        handler = _urllib2_wrappers.ProxyHandler()
1085
        self.assertEquals(expected,
1086
                          handler.evaluate_proxy_bypass(host, no_proxy))
1087
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
1088
    def test_empty_user(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1089
        self.overrideEnv('http_proxy', 'http://bar.com')
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
1090
        request = self._proxied_request()
1091
        self.assertFalse(request.headers.has_key('Proxy-authorization'))
1092
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
1093
    def test_invalid_proxy(self):
1094
        """A proxy env variable without scheme"""
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1095
        self.overrideEnv('http_proxy', 'host:1234')
2298.7.1 by Vincent Ladeuil
Fix bug #87765: proxy env variables without scheme should cause
1096
        self.assertRaises(errors.InvalidURL, self._proxied_request)
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
1097
5639.2.2 by Vincent Ladeuil
Add tests and comments to clarify the feature.
1098
    def test_evaluate_proxy_bypass_true(self):
1099
        """The host is not proxied"""
1100
        self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
1101
        self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
1102
1103
    def test_evaluate_proxy_bypass_false(self):
1104
        """The host is proxied"""
1105
        self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
1106
1107
    def test_evaluate_proxy_bypass_unknown(self):
1108
        """The host is not explicitly proxied"""
1109
        self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
1110
        self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
1111
1112
    def test_evaluate_proxy_bypass_empty_entries(self):
1113
        """Ignore empty entries"""
1114
        self.assertEvaluateProxyBypass(None, 'example.com', '')
1115
        self.assertEvaluateProxyBypass(None, 'example.com', ',')
1116
        self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
5639.2.1 by Martin Pool
Empty entries in the ``NO_PROXY`` variable are no longer treated as matching every host.
1117
2273.2.2 by v.ladeuil+lp at free
Really fix bug #83954, with tests.
1118
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1119
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
1120
    """Tests proxy server.
1121
1122
    Be aware that we do not setup a real proxy here. Instead, we
1123
    check that the *connection* goes through the proxy by serving
1124
    different content (the faked proxy server append '-proxied'
1125
    to the file names).
1126
    """
1127
5462.3.15 by Martin Pool
Turn variations into scenario lists
1128
    scenarios = multiply_scenarios(
1129
        vary_by_http_client_implementation(), 
1130
        vary_by_http_protocol_version(),
1131
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1132
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1133
    # FIXME: We don't have an https server available, so we don't
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1134
    # test https connections. --vila toolongago
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1135
1136
    def setUp(self):
1137
        super(TestProxyHttpServer, self).setUp()
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1138
        self.transport_secondary_server = http_utils.ProxyServer
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1139
        self.build_tree_contents([('foo', 'contents of foo\n'),
1140
                                  ('foo-proxied', 'proxied contents of foo\n')])
1141
        # Let's setup some attributes for tests
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1142
        server = self.get_readonly_server()
1143
        self.server_host_port = '%s:%d' % (server.host, server.port)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1144
        if self._testing_pycurl():
1145
            # Oh my ! pycurl does not check for the port as part of
1146
            # no_proxy :-( So we just test the host part
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1147
            self.no_proxy_host = server.host
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1148
        else:
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1149
            self.no_proxy_host = self.server_host_port
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1150
        # The secondary server is the proxy
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1151
        self.proxy_url = self.get_secondary_url()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1152
1153
    def _testing_pycurl(self):
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
1154
        # TODO: This is duplicated for lots of the classes in this file
1155
        return (features.pycurl.available()
1156
                and self._transport == PyCurlTransport)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1157
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1158
    def assertProxied(self):
1159
        t = self.get_readonly_transport()
1160
        self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1161
1162
    def assertNotProxied(self):
1163
        t = self.get_readonly_transport()
1164
        self.assertEqual('contents of foo\n', t.get('foo').read())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1165
1166
    def test_http_proxy(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1167
        self.overrideEnv('http_proxy', self.proxy_url)
1168
        self.assertProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1169
1170
    def test_HTTP_PROXY(self):
1171
        if self._testing_pycurl():
1172
            # pycurl does not check HTTP_PROXY for security reasons
1173
            # (for use in a CGI context that we do not care
1174
            # about. Should we ?)
1175
            raise tests.TestNotApplicable(
1176
                'pycurl does not check HTTP_PROXY for security reasons')
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1177
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
1178
        self.assertProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1179
1180
    def test_all_proxy(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1181
        self.overrideEnv('all_proxy', self.proxy_url)
1182
        self.assertProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1183
1184
    def test_ALL_PROXY(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1185
        self.overrideEnv('ALL_PROXY', self.proxy_url)
1186
        self.assertProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1187
1188
    def test_http_proxy_with_no_proxy(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1189
        self.overrideEnv('no_proxy', self.no_proxy_host)
1190
        self.overrideEnv('http_proxy', self.proxy_url)
1191
        self.assertNotProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1192
1193
    def test_HTTP_PROXY_with_NO_PROXY(self):
1194
        if self._testing_pycurl():
1195
            raise tests.TestNotApplicable(
1196
                'pycurl does not check HTTP_PROXY for security reasons')
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1197
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
1198
        self.overrideEnv('HTTP_PROXY', self.proxy_url)
1199
        self.assertNotProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1200
1201
    def test_all_proxy_with_no_proxy(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1202
        self.overrideEnv('no_proxy', self.no_proxy_host)
1203
        self.overrideEnv('all_proxy', self.proxy_url)
1204
        self.assertNotProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1205
1206
    def test_ALL_PROXY_with_NO_PROXY(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1207
        self.overrideEnv('NO_PROXY', self.no_proxy_host)
1208
        self.overrideEnv('ALL_PROXY', self.proxy_url)
1209
        self.assertNotProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1210
1211
    def test_http_proxy_without_scheme(self):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1212
        self.overrideEnv('http_proxy', self.server_host_port)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1213
        if self._testing_pycurl():
1214
            # pycurl *ignores* invalid proxy env variables. If that ever change
1215
            # in the future, this test will fail indicating that pycurl do not
1216
            # ignore anymore such variables.
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1217
            self.assertNotProxied()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1218
        else:
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1219
            self.assertRaises(errors.InvalidURL, self.assertProxied)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1220
1221
1222
class TestRanges(http_utils.TestCaseWithWebserver):
1223
    """Test the Range header in GET methods."""
1224
5462.3.15 by Martin Pool
Turn variations into scenario lists
1225
    scenarios = multiply_scenarios(
1226
        vary_by_http_client_implementation(), 
1227
        vary_by_http_protocol_version(),
1228
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1229
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1230
    def setUp(self):
1231
        http_utils.TestCaseWithWebserver.setUp(self)
1232
        self.build_tree_contents([('a', '0123456789')],)
1233
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
1234
    def create_transport_readonly_server(self):
1235
        return http_server.HttpServer(protocol_version=self._protocol_version)
1236
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1237
    def _file_contents(self, relpath, ranges):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1238
        t = self.get_readonly_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1239
        offsets = [ (start, end - start + 1) for start, end in ranges]
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1240
        coalesce = t._coalesce_offsets
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1241
        coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1242
        code, data = t._get(relpath, coalesced)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1243
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1244
        for start, end in ranges:
1245
            data.seek(start)
1246
            yield data.read(end - start + 1)
1247
1248
    def _file_tail(self, relpath, tail_amount):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1249
        t = self.get_readonly_transport()
1250
        code, data = t._get(relpath, [], tail_amount)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1251
        self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1252
        data.seek(-tail_amount, 2)
1253
        return data.read(tail_amount)
1254
1255
    def test_range_header(self):
1256
        # Valid ranges
1257
        map(self.assertEqual,['0', '234'],
1258
            list(self._file_contents('a', [(0,0), (2,4)])),)
1259
1260
    def test_range_header_tail(self):
1261
        self.assertEqual('789', self._file_tail('a', 3))
1262
1263
    def test_syntactically_invalid_range_header(self):
1264
        self.assertListRaises(errors.InvalidHttpRange,
1265
                          self._file_contents, 'a', [(4, 3)])
1266
1267
    def test_semantically_invalid_range_header(self):
1268
        self.assertListRaises(errors.InvalidHttpRange,
1269
                          self._file_contents, 'a', [(42, 128)])
1270
1271
1272
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1273
    """Test redirection between http servers."""
1274
5462.3.15 by Martin Pool
Turn variations into scenario lists
1275
    scenarios = multiply_scenarios(
1276
        vary_by_http_client_implementation(), 
1277
        vary_by_http_protocol_version(),
1278
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1279
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1280
    def setUp(self):
1281
        super(TestHTTPRedirections, self).setUp()
1282
        self.build_tree_contents([('a', '0123456789'),
1283
                                  ('bundle',
1284
                                  '# Bazaar revision bundle v0.9\n#\n')
1285
                                  ],)
1286
1287
    def test_redirected(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1288
        self.assertRaises(errors.RedirectRequested,
1289
                          self.get_old_transport().get, 'a')
1290
        self.assertEqual('0123456789', self.get_new_transport().get('a').read())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1291
1292
1293
class RedirectedRequest(_urllib2_wrappers.Request):
1294
    """Request following redirections. """
1295
1296
    init_orig = _urllib2_wrappers.Request.__init__
1297
1298
    def __init__(self, method, url, *args, **kwargs):
1299
        """Constructor.
1300
1301
        """
1302
        # Since the tests using this class will replace
1303
        # _urllib2_wrappers.Request, we can't just call the base class __init__
1304
        # or we'll loop.
4208.3.2 by Andrew Bennetts
Fix one test failure in test_http under Python 2.7a0.
1305
        RedirectedRequest.init_orig(self, method, url, *args, **kwargs)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1306
        self.follow_redirections = True
1307
1308
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
1309
def install_redirected_request(test):
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
1310
    test.overrideAttr(_urllib2_wrappers, 'Request', RedirectedRequest)
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
1311
1312
5247.2.26 by Vincent Ladeuil
Fix http redirection socket leaks.
1313
def cleanup_http_redirection_connections(test):
1314
    # Some sockets are opened but never seen by _urllib, so we trap them at
1315
    # the _urllib2_wrappers level to be able to clean them up.
1316
    def socket_disconnect(sock):
1317
        try:
1318
            sock.shutdown(socket.SHUT_RDWR)
1319
            sock.close()
1320
        except socket.error:
1321
            pass
1322
    def connect(connection):
1323
        test.http_connect_orig(connection)
1324
        test.addCleanup(socket_disconnect, connection.sock)
1325
    test.http_connect_orig = test.overrideAttr(
1326
        _urllib2_wrappers.HTTPConnection, 'connect', connect)
1327
    def connect(connection):
1328
        test.https_connect_orig(connection)
1329
        test.addCleanup(socket_disconnect, connection.sock)
1330
    test.https_connect_orig = test.overrideAttr(
1331
        _urllib2_wrappers.HTTPSConnection, 'connect', connect)
1332
1333
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1334
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1335
    """Test redirections.
1336
1337
    http implementations do not redirect silently anymore (they
1338
    do not redirect at all in fact). The mechanism is still in
1339
    place at the _urllib2_wrappers.Request level and these tests
1340
    exercise it.
1341
1342
    For the pycurl implementation
1343
    the redirection have been deleted as we may deprecate pycurl
1344
    and I have no place to keep a working implementation.
1345
    -- vila 20070212
1346
    """
1347
5462.3.15 by Martin Pool
Turn variations into scenario lists
1348
    scenarios = multiply_scenarios(
1349
        vary_by_http_client_implementation(), 
1350
        vary_by_http_protocol_version(),
1351
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1352
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1353
    def setUp(self):
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
1354
        if (features.pycurl.available()
1355
            and self._transport == PyCurlTransport):
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1356
            raise tests.TestNotApplicable(
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1357
                "pycurl doesn't redirect silently anymore")
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1358
        super(TestHTTPSilentRedirections, self).setUp()
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
1359
        install_redirected_request(self)
5247.2.26 by Vincent Ladeuil
Fix http redirection socket leaks.
1360
        cleanup_http_redirection_connections(self)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1361
        self.build_tree_contents([('a','a'),
1362
                                  ('1/',),
1363
                                  ('1/a', 'redirected once'),
1364
                                  ('2/',),
1365
                                  ('2/a', 'redirected twice'),
1366
                                  ('3/',),
1367
                                  ('3/a', 'redirected thrice'),
1368
                                  ('4/',),
1369
                                  ('4/a', 'redirected 4 times'),
1370
                                  ('5/',),
1371
                                  ('5/a', 'redirected 5 times'),
1372
                                  ],)
1373
1374
    def test_one_redirection(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1375
        t = self.get_old_transport()
1376
        req = RedirectedRequest('GET', t._remote_path('a'))
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1377
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1378
                                       self.new_server.port)
1379
        self.old_server.redirections = \
1380
            [('(.*)', r'%s/1\1' % (new_prefix), 301),]
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1381
        self.assertEqual('redirected once', t._perform(req).read())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1382
1383
    def test_five_redirections(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1384
        t = self.get_old_transport()
1385
        req = RedirectedRequest('GET', t._remote_path('a'))
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1386
        old_prefix = 'http://%s:%s' % (self.old_server.host,
1387
                                       self.old_server.port)
1388
        new_prefix = 'http://%s:%s' % (self.new_server.host,
1389
                                       self.new_server.port)
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
1390
        self.old_server.redirections = [
1391
            ('/1(.*)', r'%s/2\1' % (old_prefix), 302),
1392
            ('/2(.*)', r'%s/3\1' % (old_prefix), 303),
1393
            ('/3(.*)', r'%s/4\1' % (old_prefix), 307),
1394
            ('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1395
            ('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1396
            ]
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1397
        self.assertEqual('redirected 5 times', t._perform(req).read())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1398
1399
1400
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1401
    """Test transport.do_catching_redirections."""
1402
5462.3.15 by Martin Pool
Turn variations into scenario lists
1403
    scenarios = multiply_scenarios(
1404
        vary_by_http_client_implementation(), 
1405
        vary_by_http_protocol_version(),
1406
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1407
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1408
    def setUp(self):
1409
        super(TestDoCatchRedirections, self).setUp()
1410
        self.build_tree_contents([('a', '0123456789'),],)
5247.2.26 by Vincent Ladeuil
Fix http redirection socket leaks.
1411
        cleanup_http_redirection_connections(self)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1412
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1413
        self.old_transport = self.get_old_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1414
5247.3.31 by Vincent Ladeuil
Cleanup some imports in http_utils.py.
1415
    def get_a(self, t):
1416
        return t.get('a')
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1417
1418
    def test_no_redirection(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1419
        t = self.get_new_transport()
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1420
1421
        # We use None for redirected so that we fail if redirected
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1422
        self.assertEqual('0123456789',
1423
                         transport.do_catching_redirections(
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1424
                self.get_a, t, None).read())
1425
1426
    def test_one_redirection(self):
1427
        self.redirections = 0
1428
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1429
        def redirected(t, exception, redirection_notice):
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1430
            self.redirections += 1
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1431
            redirected_t = t._redirected_to(exception.source, exception.target)
1432
            return redirected_t
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1433
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1434
        self.assertEqual('0123456789',
1435
                         transport.do_catching_redirections(
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1436
                self.get_a, self.old_transport, redirected).read())
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1437
        self.assertEqual(1, self.redirections)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1438
1439
    def test_redirection_loop(self):
1440
1441
        def redirected(transport, exception, redirection_notice):
1442
            # By using the redirected url as a base dir for the
1443
            # *old* transport, we create a loop: a => a/a =>
1444
            # a/a/a
1445
            return self.old_transport.clone(exception.target)
1446
1447
        self.assertRaises(errors.TooManyRedirections,
1448
                          transport.do_catching_redirections,
1449
                          self.get_a, self.old_transport, redirected)
1450
1451
1452
class TestAuth(http_utils.TestCaseWithWebserver):
1453
    """Test authentication scheme"""
1454
5462.3.15 by Martin Pool
Turn variations into scenario lists
1455
    scenarios = multiply_scenarios(
1456
        vary_by_http_client_implementation(),
1457
        vary_by_http_protocol_version(),
1458
        vary_by_http_auth_scheme(),
1459
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1460
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1461
    _auth_header = 'Authorization'
1462
    _password_prompt_prefix = ''
4222.3.12 by Jelmer Vernooij
Check that the HTTP transport prompts for usernames.
1463
    _username_prompt_prefix = ''
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
1464
    # Set by load_tests
1465
    _auth_server = None
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1466
1467
    def setUp(self):
1468
        super(TestAuth, self).setUp()
1469
        self.server = self.get_readonly_server()
1470
        self.build_tree_contents([('a', 'contents of a\n'),
1471
                                  ('b', 'contents of b\n'),])
1472
1473
    def create_transport_readonly_server(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1474
        server = self._auth_server(protocol_version=self._protocol_version)
1475
        server._url_protocol = self._url_protocol
1476
        return server
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1477
1478
    def _testing_pycurl(self):
4913.2.11 by John Arbash Meinel
Convert a bunch more features over to ModuleAvailableFeature
1479
        # TODO: This is duplicated for lots of the classes in this file
1480
        return (features.pycurl.available()
1481
                and self._transport == PyCurlTransport)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1482
3910.2.4 by Vincent Ladeuil
Fixed as per John's review.
1483
    def get_user_url(self, user, password):
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1484
        """Build an url embedding user and password"""
1485
        url = '%s://' % self.server._url_protocol
1486
        if user is not None:
1487
            url += user
1488
            if password is not None:
1489
                url += ':' + password
1490
            url += '@'
1491
        url += '%s:%s/' % (self.server.host, self.server.port)
1492
        return url
1493
3910.2.4 by Vincent Ladeuil
Fixed as per John's review.
1494
    def get_user_transport(self, user, password):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1495
        t = transport.get_transport(self.get_user_url(user, password))
1496
        return t
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1497
1498
    def test_no_user(self):
1499
        self.server.add_user('joe', 'foo')
3910.2.4 by Vincent Ladeuil
Fixed as per John's review.
1500
        t = self.get_user_transport(None, None)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1501
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'a')
1502
        # Only one 'Authentication Required' error should occur
1503
        self.assertEqual(1, self.server.auth_required_errors)
1504
1505
    def test_empty_pass(self):
1506
        self.server.add_user('joe', '')
1507
        t = self.get_user_transport('joe', '')
1508
        self.assertEqual('contents of a\n', t.get('a').read())
1509
        # Only one 'Authentication Required' error should occur
1510
        self.assertEqual(1, self.server.auth_required_errors)
1511
1512
    def test_user_pass(self):
1513
        self.server.add_user('joe', 'foo')
1514
        t = self.get_user_transport('joe', 'foo')
1515
        self.assertEqual('contents of a\n', t.get('a').read())
1516
        # Only one 'Authentication Required' error should occur
1517
        self.assertEqual(1, self.server.auth_required_errors)
1518
1519
    def test_unknown_user(self):
1520
        self.server.add_user('joe', 'foo')
1521
        t = self.get_user_transport('bill', 'foo')
1522
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'a')
1523
        # Two 'Authentication Required' errors should occur (the
1524
        # initial 'who are you' and 'I don't know you, who are
1525
        # you').
1526
        self.assertEqual(2, self.server.auth_required_errors)
1527
1528
    def test_wrong_pass(self):
1529
        self.server.add_user('joe', 'foo')
1530
        t = self.get_user_transport('joe', 'bar')
1531
        self.assertRaises(errors.InvalidHttpResponse, t.get, 'a')
1532
        # Two 'Authentication Required' errors should occur (the
1533
        # initial 'who are you' and 'this is not you, who are you')
1534
        self.assertEqual(2, self.server.auth_required_errors)
1535
4222.3.12 by Jelmer Vernooij
Check that the HTTP transport prompts for usernames.
1536
    def test_prompt_for_username(self):
1537
        if self._testing_pycurl():
1538
            raise tests.TestNotApplicable(
1539
                'pycurl cannot prompt, it handles auth by embedding'
1540
                ' user:pass in urls only')
1541
1542
        self.server.add_user('joe', 'foo')
1543
        t = self.get_user_transport(None, None)
1544
        stdout = tests.StringIOWrapper()
4368.3.1 by Vincent Ladeuil
Use stderr for UI prompt to address bug #376582.
1545
        stderr = tests.StringIOWrapper()
1546
        ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
1547
                                            stdout=stdout, stderr=stderr)
4222.3.12 by Jelmer Vernooij
Check that the HTTP transport prompts for usernames.
1548
        self.assertEqual('contents of a\n',t.get('a').read())
1549
        # stdin should be empty
1550
        self.assertEqual('', ui.ui_factory.stdin.readline())
4368.3.1 by Vincent Ladeuil
Use stderr for UI prompt to address bug #376582.
1551
        stderr.seek(0)
4222.3.12 by Jelmer Vernooij
Check that the HTTP transport prompts for usernames.
1552
        expected_prompt = self._expected_username_prompt(t._unqualified_scheme)
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1553
        self.assertEqual(expected_prompt, stderr.read(len(expected_prompt)))
1554
        self.assertEqual('', stdout.getvalue())
4222.3.12 by Jelmer Vernooij
Check that the HTTP transport prompts for usernames.
1555
        self._check_password_prompt(t._unqualified_scheme, 'joe',
4368.3.1 by Vincent Ladeuil
Use stderr for UI prompt to address bug #376582.
1556
                                    stderr.readline())
4284.1.2 by Vincent Ladeuil
Delete spurious space.
1557
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1558
    def test_prompt_for_password(self):
1559
        if self._testing_pycurl():
1560
            raise tests.TestNotApplicable(
1561
                'pycurl cannot prompt, it handles auth by embedding'
1562
                ' user:pass in urls only')
1563
1564
        self.server.add_user('joe', 'foo')
1565
        t = self.get_user_transport('joe', None)
1566
        stdout = tests.StringIOWrapper()
4368.3.1 by Vincent Ladeuil
Use stderr for UI prompt to address bug #376582.
1567
        stderr = tests.StringIOWrapper()
1568
        ui.ui_factory = tests.TestUIFactory(stdin='foo\n',
1569
                                            stdout=stdout, stderr=stderr)
1570
        self.assertEqual('contents of a\n', t.get('a').read())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1571
        # stdin should be empty
1572
        self.assertEqual('', ui.ui_factory.stdin.readline())
1573
        self._check_password_prompt(t._unqualified_scheme, 'joe',
4368.3.1 by Vincent Ladeuil
Use stderr for UI prompt to address bug #376582.
1574
                                    stderr.getvalue())
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1575
        self.assertEqual('', stdout.getvalue())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1576
        # And we shouldn't prompt again for a different request
1577
        # against the same transport.
1578
        self.assertEqual('contents of b\n',t.get('b').read())
1579
        t2 = t.clone()
1580
        # And neither against a clone
1581
        self.assertEqual('contents of b\n',t2.get('b').read())
1582
        # Only one 'Authentication Required' error should occur
1583
        self.assertEqual(1, self.server.auth_required_errors)
1584
1585
    def _check_password_prompt(self, scheme, user, actual_prompt):
1586
        expected_prompt = (self._password_prompt_prefix
1587
                           + ("%s %s@%s:%d, Realm: '%s' password: "
1588
                              % (scheme.upper(),
1589
                                 user, self.server.host, self.server.port,
1590
                                 self.server.auth_realm)))
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1591
        self.assertEqual(expected_prompt, actual_prompt)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1592
4222.3.12 by Jelmer Vernooij
Check that the HTTP transport prompts for usernames.
1593
    def _expected_username_prompt(self, scheme):
1594
        return (self._username_prompt_prefix
1595
                + "%s %s:%d, Realm: '%s' username: " % (scheme.upper(),
1596
                                 self.server.host, self.server.port,
1597
                                 self.server.auth_realm))
1598
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1599
    def test_no_prompt_for_password_when_using_auth_config(self):
1600
        if self._testing_pycurl():
1601
            raise tests.TestNotApplicable(
1602
                'pycurl does not support authentication.conf'
1603
                ' since it cannot prompt')
1604
1605
        user =' joe'
1606
        password = 'foo'
1607
        stdin_content = 'bar\n'  # Not the right password
1608
        self.server.add_user(user, password)
1609
        t = self.get_user_transport(user, None)
1610
        ui.ui_factory = tests.TestUIFactory(stdin=stdin_content,
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
1611
                                            stderr=tests.StringIOWrapper())
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1612
        # Create a minimal config file with the right password
5484.2.1 by Martin Pool
Add failing test for AbstractAuthHandler and bug 654684
1613
        _setup_authentication_config(
1614
            scheme='http', 
1615
            port=self.server.port,
1616
            user=user,
1617
            password=password)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1618
        # Issue a request to the server to connect
1619
        self.assertEqual('contents of a\n',t.get('a').read())
1620
        # stdin should have  been left untouched
1621
        self.assertEqual(stdin_content, ui.ui_factory.stdin.readline())
1622
        # Only one 'Authentication Required' error should occur
1623
        self.assertEqual(1, self.server.auth_required_errors)
1624
3111.1.26 by Vincent Ladeuil
Re-add a test lost in refactoring.
1625
    def test_changing_nonce(self):
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
1626
        if self._auth_server not in (http_utils.HTTPDigestAuthServer,
1627
                                     http_utils.ProxyDigestAuthServer):
1628
            raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
3111.1.26 by Vincent Ladeuil
Re-add a test lost in refactoring.
1629
        if self._testing_pycurl():
1630
            raise tests.KnownFailure(
1631
                'pycurl does not handle a nonce change')
1632
        self.server.add_user('joe', 'foo')
1633
        t = self.get_user_transport('joe', 'foo')
1634
        self.assertEqual('contents of a\n', t.get('a').read())
1635
        self.assertEqual('contents of b\n', t.get('b').read())
1636
        # Only one 'Authentication Required' error should have
1637
        # occured so far
1638
        self.assertEqual(1, self.server.auth_required_errors)
1639
        # The server invalidates the current nonce
1640
        self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1641
        self.assertEqual('contents of a\n', t.get('a').read())
1642
        # Two 'Authentication Required' errors should occur (the
1643
        # initial 'who are you' and a second 'who are you' with the new nonce)
1644
        self.assertEqual(2, self.server.auth_required_errors)
1645
5484.2.1 by Martin Pool
Add failing test for AbstractAuthHandler and bug 654684
1646
    def test_user_from_auth_conf(self):
1647
        if self._testing_pycurl():
1648
            raise tests.TestNotApplicable(
1649
                'pycurl does not support authentication.conf')
1650
        user = 'joe'
1651
        password = 'foo'
1652
        self.server.add_user(user, password)
1653
        _setup_authentication_config(
1654
            scheme='http', 
1655
            port=self.server.port,
1656
            user=user,
1657
            password=password)
1658
        t = self.get_user_transport(None, None)
1659
        # Issue a request to the server to connect
1660
        self.assertEqual('contents of a\n', t.get('a').read())
1661
        # Only one 'Authentication Required' error should occur
1662
        self.assertEqual(1, self.server.auth_required_errors)
1663
1664
1665
def _setup_authentication_config(**kwargs):
1666
    conf = config.AuthenticationConfig()
1667
    conf._get_config().update({'httptest': kwargs})
1668
    conf._save()
1669
1670
1671
5484.2.2 by Martin Pool
Cope gracefully if urllib2 doesn't tell us the port number in the authentication callback
1672
class TestUrllib2AuthHandler(tests.TestCaseWithTransport):
5484.2.1 by Martin Pool
Add failing test for AbstractAuthHandler and bug 654684
1673
    """Unit tests for glue by which urllib2 asks us for authentication"""
1674
1675
    def test_get_user_password_without_port(self):
1676
        """We cope if urllib2 doesn't tell us the port.
1677
1678
        See https://bugs.launchpad.net/bzr/+bug/654684
1679
        """
1680
        user = 'joe'
1681
        password = 'foo'
1682
        _setup_authentication_config(
1683
            scheme='http', 
1684
            host='localhost',
1685
            user=user,
1686
            password=password)
5484.2.2 by Martin Pool
Cope gracefully if urllib2 doesn't tell us the port number in the authentication callback
1687
        handler = _urllib2_wrappers.HTTPAuthHandler()
5484.2.1 by Martin Pool
Add failing test for AbstractAuthHandler and bug 654684
1688
        got_pass = handler.get_user_password(dict(
5484.2.2 by Martin Pool
Cope gracefully if urllib2 doesn't tell us the port number in the authentication callback
1689
            user='joe',
5484.2.1 by Martin Pool
Add failing test for AbstractAuthHandler and bug 654684
1690
            protocol='http',
1691
            host='localhost',
1692
            path='/',
1693
            realm='Realm',
1694
            ))
5484.2.2 by Martin Pool
Cope gracefully if urllib2 doesn't tell us the port number in the authentication callback
1695
        self.assertEquals((user, password), got_pass)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1696
1697
1698
class TestProxyAuth(TestAuth):
1699
    """Test proxy authentication schemes."""
1700
5462.3.15 by Martin Pool
Turn variations into scenario lists
1701
    scenarios = multiply_scenarios(
1702
        vary_by_http_client_implementation(),
1703
        vary_by_http_protocol_version(),
1704
        vary_by_http_proxy_auth_scheme(),
1705
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1706
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1707
    _auth_header = 'Proxy-authorization'
4222.3.12 by Jelmer Vernooij
Check that the HTTP transport prompts for usernames.
1708
    _password_prompt_prefix = 'Proxy '
1709
    _username_prompt_prefix = 'Proxy '
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1710
1711
    def setUp(self):
1712
        super(TestProxyAuth, self).setUp()
1713
        # Override the contents to avoid false positives
1714
        self.build_tree_contents([('a', 'not proxied contents of a\n'),
1715
                                  ('b', 'not proxied contents of b\n'),
1716
                                  ('a-proxied', 'contents of a\n'),
1717
                                  ('b-proxied', 'contents of b\n'),
1718
                                  ])
1719
3910.2.4 by Vincent Ladeuil
Fixed as per John's review.
1720
    def get_user_transport(self, user, password):
5570.3.10 by Vincent Ladeuil
Rework the http tests with overrideEnv.
1721
        self.overrideEnv('all_proxy', self.get_user_url(user, password))
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1722
        return TestAuth.get_user_transport(self, user, password)
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
1723
1724
    def test_empty_pass(self):
1725
        if self._testing_pycurl():
1726
            import pycurl
1727
            if pycurl.version_info()[1] < '7.16.0':
1728
                raise tests.KnownFailure(
1729
                    'pycurl < 7.16.0 does not handle empty proxy passwords')
1730
        super(TestProxyAuth, self).test_empty_pass()
1731
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1732
1733
class SampleSocket(object):
1734
    """A socket-like object for use in testing the HTTP request handler."""
1735
1736
    def __init__(self, socket_read_content):
1737
        """Constructs a sample socket.
1738
1739
        :param socket_read_content: a byte sequence
1740
        """
1741
        # Use plain python StringIO so we can monkey-patch the close method to
1742
        # not discard the contents.
1743
        from StringIO import StringIO
1744
        self.readfile = StringIO(socket_read_content)
1745
        self.writefile = StringIO()
1746
        self.writefile.close = lambda: None
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
1747
        self.close = lambda: None
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1748
1749
    def makefile(self, mode='r', bufsize=None):
1750
        if 'r' in mode:
1751
            return self.readfile
1752
        else:
1753
            return self.writefile
1754
1755
1756
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1757
5462.3.15 by Martin Pool
Turn variations into scenario lists
1758
    scenarios = multiply_scenarios(
1759
        vary_by_http_client_implementation(), 
1760
        vary_by_http_protocol_version(),
1761
        )
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1762
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1763
    def setUp(self):
1764
        super(SmartHTTPTunnellingTest, self).setUp()
1765
        # We use the VFS layer as part of HTTP tunnelling tests.
5570.3.6 by Vincent Ladeuil
Get rid of all _captureVar() calls, no test failures, pfew.
1766
        self.overrideEnv('BZR_NO_SMART_VFS', None)
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1767
        self.transport_readonly_server = http_utils.HTTPServerWithSmarts
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1768
        self.http_server = self.get_readonly_server()
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1769
1770
    def create_transport_readonly_server(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1771
        server = http_utils.HTTPServerWithSmarts(
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1772
            protocol_version=self._protocol_version)
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1773
        server._url_protocol = self._url_protocol
1774
        return server
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1775
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
1776
    def test_open_bzrdir(self):
1777
        branch = self.make_branch('relpath')
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1778
        url = self.http_server.get_url() + 'relpath'
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
1779
        bd = bzrdir.BzrDir.open(url)
5247.2.28 by Vincent Ladeuil
Some more cleanups spotted on windows.
1780
        self.addCleanup(bd.transport.disconnect)
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
1781
        self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1782
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1783
    def test_bulk_data(self):
1784
        # We should be able to send and receive bulk data in a single message.
1785
        # The 'readv' command in the smart protocol both sends and receives
1786
        # bulk data, so we use that.
1787
        self.build_tree(['data-file'])
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1788
        http_transport = transport.get_transport(self.http_server.get_url())
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1789
        medium = http_transport.get_smart_medium()
1790
        # Since we provide the medium, the url below will be mostly ignored
1791
        # during the test, as long as the path is '/'.
1792
        remote_transport = remote.RemoteTransport('bzr://fake_host/',
1793
                                                  medium=medium)
1794
        self.assertEqual(
1795
            [(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1796
1797
    def test_http_send_smart_request(self):
1798
1799
        post_body = 'hello\n'
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
1800
        expected_reply_body = 'ok\x012\n'
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1801
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1802
        http_transport = transport.get_transport(self.http_server.get_url())
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1803
        medium = http_transport.get_smart_medium()
1804
        response = medium.send_http_smart_request(post_body)
1805
        reply_body = response.read()
1806
        self.assertEqual(expected_reply_body, reply_body)
1807
1808
    def test_smart_http_server_post_request_handler(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1809
        httpd = self.http_server.server
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1810
1811
        socket = SampleSocket(
1812
            'POST /.bzr/smart %s \r\n' % self._protocol_version
1813
            # HTTP/1.1 posts must have a Content-Length (but it doesn't hurt
1814
            # for 1.0)
1815
            + 'Content-Length: 6\r\n'
1816
            '\r\n'
1817
            'hello\n')
1818
        # Beware: the ('localhost', 80) below is the
1819
        # client_address parameter, but we don't have one because
1820
        # we have defined a socket which is not bound to an
1821
        # address. The test framework never uses this client
1822
        # address, so far...
1823
        request_handler = http_utils.SmartRequestHandler(socket,
1824
                                                         ('localhost', 80),
1825
                                                         httpd)
1826
        response = socket.writefile.getvalue()
1827
        self.assertStartsWith(response, '%s 200 ' % self._protocol_version)
1828
        # This includes the end of the HTTP headers, and all the body.
3245.4.59 by Andrew Bennetts
Various tweaks in response to Martin's review.
1829
        expected_end_of_response = '\r\n\r\nok\x012\n'
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
1830
        self.assertEndsWith(response, expected_end_of_response)
1831
1832
3430.3.4 by Vincent Ladeuil
Of course we can write tests !
1833
class ForbiddenRequestHandler(http_server.TestingHTTPRequestHandler):
1834
    """No smart server here request handler."""
1835
1836
    def do_POST(self):
1837
        self.send_error(403, "Forbidden")
1838
1839
1840
class SmartClientAgainstNotSmartServer(TestSpecificRequestHandler):
1841
    """Test smart client behaviour against an http server without smarts."""
1842
1843
    _req_handler_class = ForbiddenRequestHandler
1844
1845
    def test_probe_smart_server(self):
1846
        """Test error handling against server refusing smart requests."""
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1847
        t = self.get_readonly_transport()
3430.3.4 by Vincent Ladeuil
Of course we can write tests !
1848
        # No need to build a valid smart request here, the server will not even
1849
        # try to interpret it.
1850
        self.assertRaises(errors.SmartProtocolError,
5599.3.6 by John Arbash Meinel
Revert the medium check, since it isn't necessary, and vila is worried about confusing people.
1851
                          t.get_smart_medium().send_http_smart_request,
1852
                          'whatever')
3430.3.4 by Vincent Ladeuil
Of course we can write tests !
1853
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
1854
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1855
class Test_redirected_to(tests.TestCase):
1856
5462.3.15 by Martin Pool
Turn variations into scenario lists
1857
    scenarios = vary_by_http_client_implementation()
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
1858
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1859
    def test_redirected_to_subdir(self):
1860
        t = self._transport('http://www.example.com/foo')
3878.4.5 by Vincent Ladeuil
Don't use the exception as a parameter for _redirected_to.
1861
        r = t._redirected_to('http://www.example.com/foo',
1862
                             'http://www.example.com/foo/subdir')
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1863
        self.assertIsInstance(r, type(t))
1864
        # Both transports share the some connection
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1865
        self.assertEqual(t._get_connection(), r._get_connection())
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1866
3878.4.3 by Vincent Ladeuil
Fix bug #303959 by returning a transport based on the same url
1867
    def test_redirected_to_self_with_slash(self):
1868
        t = self._transport('http://www.example.com/foo')
3878.4.5 by Vincent Ladeuil
Don't use the exception as a parameter for _redirected_to.
1869
        r = t._redirected_to('http://www.example.com/foo',
1870
                             'http://www.example.com/foo/')
3878.4.3 by Vincent Ladeuil
Fix bug #303959 by returning a transport based on the same url
1871
        self.assertIsInstance(r, type(t))
1872
        # Both transports share the some connection (one can argue that we
1873
        # should return the exact same transport here, but that seems
1874
        # overkill).
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1875
        self.assertEqual(t._get_connection(), r._get_connection())
3878.4.3 by Vincent Ladeuil
Fix bug #303959 by returning a transport based on the same url
1876
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1877
    def test_redirected_to_host(self):
1878
        t = self._transport('http://www.example.com/foo')
3878.4.5 by Vincent Ladeuil
Don't use the exception as a parameter for _redirected_to.
1879
        r = t._redirected_to('http://www.example.com/foo',
1880
                             'http://foo.example.com/foo/subdir')
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1881
        self.assertIsInstance(r, type(t))
1882
1883
    def test_redirected_to_same_host_sibling_protocol(self):
1884
        t = self._transport('http://www.example.com/foo')
3878.4.5 by Vincent Ladeuil
Don't use the exception as a parameter for _redirected_to.
1885
        r = t._redirected_to('http://www.example.com/foo',
1886
                             'https://www.example.com/foo')
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1887
        self.assertIsInstance(r, type(t))
1888
1889
    def test_redirected_to_same_host_different_protocol(self):
1890
        t = self._transport('http://www.example.com/foo')
3878.4.5 by Vincent Ladeuil
Don't use the exception as a parameter for _redirected_to.
1891
        r = t._redirected_to('http://www.example.com/foo',
1892
                             'ftp://www.example.com/foo')
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1893
        self.assertNotEquals(type(r), type(t))
1894
1895
    def test_redirected_to_different_host_same_user(self):
1896
        t = self._transport('http://joe@www.example.com/foo')
3878.4.5 by Vincent Ladeuil
Don't use the exception as a parameter for _redirected_to.
1897
        r = t._redirected_to('http://www.example.com/foo',
1898
                             'https://foo.example.com/foo')
3878.4.2 by Vincent Ladeuil
Fix bug #265070 by providing a finer sieve for accepted redirections.
1899
        self.assertIsInstance(r, type(t))
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
1900
        self.assertEqual(t._user, r._user)
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1901
1902
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1903
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
1904
    """Request handler for a unique and pre-defined request.
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1905
1906
    The only thing we care about here is how many bytes travel on the wire. But
1907
    since we want to measure it for a real http client, we have to send it
1908
    correct responses.
1909
1910
    We expect to receive a *single* request nothing more (and we won't even
1911
    check what request it is, we just measure the bytes read until an empty
1912
    line.
1913
    """
1914
4731.2.3 by Vincent Ladeuil
Reduce the leaking http tests from ~200 to ~5.
1915
    def _handle_one_request(self):
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1916
        tcs = self.server.test_case_server
1917
        requestline = self.rfile.readline()
1918
        headers = self.MessageClass(self.rfile, 0)
1919
        # We just read: the request, the headers, an empty line indicating the
1920
        # end of the headers.
1921
        bytes_read = len(requestline)
1922
        for line in headers.headers:
1923
            bytes_read += len(line)
1924
        bytes_read += len('\r\n')
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1925
        if requestline.startswith('POST'):
1926
            # The body should be a single line (or we don't know where it ends
1927
            # and we don't want to issue a blocking read)
1928
            body = self.rfile.readline()
1929
            bytes_read += len(body)
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1930
        tcs.bytes_read = bytes_read
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1931
1932
        # We set the bytes written *before* issuing the write, the client is
1933
        # supposed to consume every produced byte *before* checking that value.
3945.1.7 by Vincent Ladeuil
Test against https.
1934
1935
        # Doing the oppposite may lead to test failure: we may be interrupted
1936
        # after the write but before updating the value. The client can then
1937
        # continue and read the value *before* we can update it. And yes,
1938
        # this has been observed -- vila 20090129
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1939
        tcs.bytes_written = len(tcs.canned_response)
1940
        self.wfile.write(tcs.canned_response)
1941
1942
1943
class ActivityServerMixin(object):
1944
1945
    def __init__(self, protocol_version):
1946
        super(ActivityServerMixin, self).__init__(
1947
            request_handler=PredefinedRequestHandler,
1948
            protocol_version=protocol_version)
1949
        # Bytes read and written by the server
1950
        self.bytes_read = 0
1951
        self.bytes_written = 0
1952
        self.canned_response = None
1953
1954
1955
class ActivityHTTPServer(ActivityServerMixin, http_server.HttpServer):
1956
    pass
1957
1958
1959
if tests.HTTPSServerFeature.available():
1960
    from bzrlib.tests import https_server
1961
    class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
1962
        pass
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1963
1964
4776.2.1 by Vincent Ladeuil
Support no activity report on http sockets.
1965
class TestActivityMixin(object):
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1966
    """Test socket activity reporting.
1967
1968
    We use a special purpose server to control the bytes sent and received and
1969
    be able to predict the activity on the client socket.
1970
    """
1971
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1972
    def setUp(self):
1973
        tests.TestCase.setUp(self)
1974
        self.server = self._activity_server(self._protocol_version)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
1975
        self.server.start_server()
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1976
        self.activities = {}
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1977
        def report_activity(t, bytes, direction):
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1978
            count = self.activities.get(direction, 0)
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1979
            count += bytes
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1980
            self.activities[direction] = count
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
1981
1982
        # We override at class level because constructors may propagate the
1983
        # bound method and render instance overriding ineffective (an
4031.3.1 by Frank Aspell
Fixing various typos
1984
        # alternative would be to define a specific ui factory instead...)
4986.2.6 by Martin Pool
Clean up test_http setUp methods
1985
        self.overrideAttr(self._transport, '_report_activity', report_activity)
1986
        self.addCleanup(self.server.stop_server)
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1987
1988
    def get_transport(self):
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
1989
        t = self._transport(self.server.get_url())
1990
        # FIXME: Needs cleanup -- vila 20100611
1991
        return t
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
1992
1993
    def assertActivitiesMatch(self):
1994
        self.assertEqual(self.server.bytes_read,
1995
                         self.activities.get('write', 0), 'written bytes')
1996
        self.assertEqual(self.server.bytes_written,
1997
                         self.activities.get('read', 0), 'read bytes')
1998
1999
    def test_get(self):
2000
        self.server.canned_response = '''HTTP/1.1 200 OK\r
2001
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
2002
Server: Apache/2.0.54 (Fedora)\r
2003
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
2004
ETag: "56691-23-38e9ae00"\r
2005
Accept-Ranges: bytes\r
2006
Content-Length: 35\r
2007
Connection: close\r
2008
Content-Type: text/plain; charset=UTF-8\r
2009
\r
2010
Bazaar-NG meta directory, format 1
2011
'''
2012
        t = self.get_transport()
3945.1.5 by Vincent Ladeuil
Start implementing http activity reporting at socket level.
2013
        self.assertEqual('Bazaar-NG meta directory, format 1\n',
2014
                         t.get('foo/bar').read())
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
2015
        self.assertActivitiesMatch()
2016
2017
    def test_has(self):
2018
        self.server.canned_response = '''HTTP/1.1 200 OK\r
2019
Server: SimpleHTTP/0.6 Python/2.5.2\r
2020
Date: Thu, 29 Jan 2009 20:21:47 GMT\r
2021
Content-type: application/octet-stream\r
2022
Content-Length: 20\r
2023
Last-Modified: Thu, 29 Jan 2009 20:21:47 GMT\r
2024
\r
2025
'''
2026
        t = self.get_transport()
2027
        self.assertTrue(t.has('foo/bar'))
2028
        self.assertActivitiesMatch()
2029
2030
    def test_readv(self):
2031
        self.server.canned_response = '''HTTP/1.1 206 Partial Content\r
2032
Date: Tue, 11 Jul 2006 04:49:48 GMT\r
2033
Server: Apache/2.0.54 (Fedora)\r
2034
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
2035
ETag: "238a3c-16ec2-805c5540"\r
2036
Accept-Ranges: bytes\r
2037
Content-Length: 1534\r
2038
Connection: close\r
2039
Content-Type: multipart/byteranges; boundary=418470f848b63279b\r
2040
\r
2041
\r
2042
--418470f848b63279b\r
2043
Content-type: text/plain; charset=UTF-8\r
2044
Content-range: bytes 0-254/93890\r
2045
\r
2046
mbp@sourcefrog.net-20050309040815-13242001617e4a06
2047
mbp@sourcefrog.net-20050309040929-eee0eb3e6d1e7627
2048
mbp@sourcefrog.net-20050309040957-6cad07f466bb0bb8
2049
mbp@sourcefrog.net-20050309041501-c840e09071de3b67
2050
mbp@sourcefrog.net-20050309044615-c24a3250be83220a
2051
\r
2052
--418470f848b63279b\r
2053
Content-type: text/plain; charset=UTF-8\r
2054
Content-range: bytes 1000-2049/93890\r
2055
\r
2056
40-fd4ec249b6b139ab
2057
mbp@sourcefrog.net-20050311063625-07858525021f270b
2058
mbp@sourcefrog.net-20050311231934-aa3776aff5200bb9
2059
mbp@sourcefrog.net-20050311231953-73aeb3a131c3699a
2060
mbp@sourcefrog.net-20050311232353-f5e33da490872c6a
2061
mbp@sourcefrog.net-20050312071639-0a8f59a34a024ff0
2062
mbp@sourcefrog.net-20050312073432-b2c16a55e0d6e9fb
2063
mbp@sourcefrog.net-20050312073831-a47c3335ece1920f
2064
mbp@sourcefrog.net-20050312085412-13373aa129ccbad3
2065
mbp@sourcefrog.net-20050313052251-2bf004cb96b39933
2066
mbp@sourcefrog.net-20050313052856-3edd84094687cb11
2067
mbp@sourcefrog.net-20050313053233-e30a4f28aef48f9d
2068
mbp@sourcefrog.net-20050313053853-7c64085594ff3072
2069
mbp@sourcefrog.net-20050313054757-a86c3f5871069e22
2070
mbp@sourcefrog.net-20050313061422-418f1f73b94879b9
2071
mbp@sourcefrog.net-20050313120651-497bd231b19df600
2072
mbp@sourcefrog.net-20050314024931-eae0170ef25a5d1a
2073
mbp@sourcefrog.net-20050314025438-d52099f915fe65fc
2074
mbp@sourcefrog.net-20050314025539-637a636692c055cf
2075
mbp@sourcefrog.net-20050314025737-55eb441f430ab4ba
2076
mbp@sourcefrog.net-20050314025901-d74aa93bb7ee8f62
2077
mbp@source\r
2078
--418470f848b63279b--\r
2079
'''
2080
        t = self.get_transport()
2081
        # Remember that the request is ignored and that the ranges below
2082
        # doesn't have to match the canned response.
2083
        l = list(t.readv('/foo/bar', ((0, 255), (1000, 1050))))
2084
        self.assertEqual(2, len(l))
2085
        self.assertActivitiesMatch()
2086
2087
    def test_post(self):
2088
        self.server.canned_response = '''HTTP/1.1 200 OK\r
2089
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
2090
Server: Apache/2.0.54 (Fedora)\r
2091
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
2092
ETag: "56691-23-38e9ae00"\r
2093
Accept-Ranges: bytes\r
2094
Content-Length: 35\r
2095
Connection: close\r
2096
Content-Type: text/plain; charset=UTF-8\r
2097
\r
2098
lalala whatever as long as itsssss
2099
'''
2100
        t = self.get_transport()
2101
        # We must send a single line of body bytes, see
4731.2.3 by Vincent Ladeuil
Reduce the leaking http tests from ~200 to ~5.
2102
        # PredefinedRequestHandler._handle_one_request
3945.1.8 by Vincent Ladeuil
Add more tests, fix pycurl double handling, revert previous tracking.
2103
        code, f = t._post('abc def end-of-body\n')
2104
        self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2105
        self.assertActivitiesMatch()
4776.2.1 by Vincent Ladeuil
Support no activity report on http sockets.
2106
2107
2108
class TestActivity(tests.TestCase, TestActivityMixin):
2109
5462.3.15 by Martin Pool
Turn variations into scenario lists
2110
    scenarios = multiply_scenarios(
2111
        vary_by_http_activity(),
2112
        vary_by_http_protocol_version(),
2113
        )
5462.3.11 by Martin Pool
Delete test_http.load_tests, and just specify variations per test
2114
4776.2.1 by Vincent Ladeuil
Support no activity report on http sockets.
2115
    def setUp(self):
4986.2.6 by Martin Pool
Clean up test_http setUp methods
2116
        TestActivityMixin.setUp(self)
4776.2.1 by Vincent Ladeuil
Support no activity report on http sockets.
2117
2118
2119
class TestNoReportActivity(tests.TestCase, TestActivityMixin):
2120
4986.2.6 by Martin Pool
Clean up test_http setUp methods
2121
    # Unlike TestActivity, we are really testing ReportingFileSocket and
2122
    # ReportingSocket, so we don't need all the parametrization. Since
2123
    # ReportingFileSocket and ReportingSocket are wrappers, it's easier to
2124
    # test them through their use by the transport than directly (that's a
2125
    # bit less clean but far more simpler and effective).
2126
    _activity_server = ActivityHTTPServer
2127
    _protocol_version = 'HTTP/1.1'
2128
4776.2.1 by Vincent Ladeuil
Support no activity report on http sockets.
2129
    def setUp(self):
4986.2.6 by Martin Pool
Clean up test_http setUp methods
2130
        self._transport =_urllib.HttpTransport_urllib
2131
        TestActivityMixin.setUp(self)
4776.2.1 by Vincent Ladeuil
Support no activity report on http sockets.
2132
2133
    def assertActivitiesMatch(self):
2134
        # Nothing to check here
2135
        pass
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2136
2137
2138
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2139
    """Test authentication on the redirected http server."""
2140
5462.3.15 by Martin Pool
Turn variations into scenario lists
2141
    scenarios = vary_by_http_protocol_version()
5462.3.9 by Martin Pool
List variations within the test classes, rather than in load_tests
2142
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2143
    _auth_header = 'Authorization'
2144
    _password_prompt_prefix = ''
2145
    _username_prompt_prefix = ''
2146
    _auth_server = http_utils.HTTPBasicAuthServer
2147
    _transport = _urllib.HttpTransport_urllib
2148
2149
    def setUp(self):
2150
        super(TestAuthOnRedirected, self).setUp()
2151
        self.build_tree_contents([('a','a'),
2152
                                  ('1/',),
2153
                                  ('1/a', 'redirected once'),
2154
                                  ],)
2155
        new_prefix = 'http://%s:%s' % (self.new_server.host,
2156
                                       self.new_server.port)
2157
        self.old_server.redirections = [
2158
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
2159
        self.old_transport = self.get_old_transport()
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2160
        self.new_server.add_user('joe', 'foo')
5247.2.26 by Vincent Ladeuil
Fix http redirection socket leaks.
2161
        cleanup_http_redirection_connections(self)
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2162
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
2163
    def create_transport_readonly_server(self):
2164
        server = self._auth_server(protocol_version=self._protocol_version)
2165
        server._url_protocol = self._url_protocol
2166
        return server
2167
2168
    def get_a(self, t):
2169
        return t.get('a')
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2170
2171
    def test_auth_on_redirected_via_do_catching_redirections(self):
2172
        self.redirections = 0
2173
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
2174
        def redirected(t, exception, redirection_notice):
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2175
            self.redirections += 1
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
2176
            redirected_t = t._redirected_to(exception.source, exception.target)
5247.2.28 by Vincent Ladeuil
Some more cleanups spotted on windows.
2177
            self.addCleanup(redirected_t.disconnect)
5247.3.29 by Vincent Ladeuil
Fix a bunch of tests that were building transport objects explicitely instead of relying on self.get_transport() leading to many wrong http implementation objects being tested.
2178
            return redirected_t
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2179
2180
        stdout = tests.StringIOWrapper()
2181
        stderr = tests.StringIOWrapper()
2182
        ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2183
                                            stdout=stdout, stderr=stderr)
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
2184
        self.assertEqual('redirected once',
2185
                         transport.do_catching_redirections(
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2186
                self.get_a, self.old_transport, redirected).read())
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
2187
        self.assertEqual(1, self.redirections)
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2188
        # stdin should be empty
2189
        self.assertEqual('', ui.ui_factory.stdin.readline())
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
2190
        # stdout should be empty, stderr will contains the prompts
2191
        self.assertEqual('', stdout.getvalue())
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2192
2193
    def test_auth_on_redirected_via_following_redirections(self):
2194
        self.new_server.add_user('joe', 'foo')
2195
        stdout = tests.StringIOWrapper()
2196
        stderr = tests.StringIOWrapper()
2197
        ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2198
                                            stdout=stdout, stderr=stderr)
2199
        t = self.old_transport
2200
        req = RedirectedRequest('GET', t.abspath('a'))
2201
        new_prefix = 'http://%s:%s' % (self.new_server.host,
2202
                                       self.new_server.port)
2203
        self.old_server.redirections = [
2204
            ('(.*)', r'%s/1\1' % (new_prefix), 301),]
5247.2.28 by Vincent Ladeuil
Some more cleanups spotted on windows.
2205
        self.assertEqual('redirected once', t._perform(req).read())
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2206
        # stdin should be empty
2207
        self.assertEqual('', ui.ui_factory.stdin.readline())
4795.4.6 by Vincent Ladeuil
Fixed as per John's review.
2208
        # stdout should be empty, stderr will contains the prompts
2209
        self.assertEqual('', stdout.getvalue())
4795.4.5 by Vincent Ladeuil
Make sure all redirection code paths can handle authentication.
2210
2211