/brz/remove-bazaar

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