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