~bzr-pqm/bzr/bzr.dev

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