/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2005-2011 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.1.18 by Robert Collins
Lalo Martins remotebranch patch
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.1.18 by Robert Collins
Lalo Martins remotebranch patch
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.1.18 by Robert Collins
Lalo Martins remotebranch patch
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.1.18 by Robert Collins
Lalo Martins remotebranch patch
16
7045.5.4 by Jelmer Vernooij
Fix a few more tests.
17
import base64
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
18
import re
6791.2.3 by Jelmer Vernooij
Fix more imports.
19
try:
20
    from urllib.request import (
21
        parse_http_list,
22
        parse_keqv_list,
23
        )
24
except ImportError:  # python < 3
25
    from urllib2 import (
26
        parse_http_list,
27
        parse_keqv_list,
28
        )
1530.1.14 by Robert Collins
Remove duplicate web server from HTTPTestUtil.
29
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
30
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
31
from .. import (
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
32
    errors,
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
33
    osutils,
3111.1.16 by Vincent Ladeuil
Fix more imports.
34
    tests,
5247.3.31 by Vincent Ladeuil
Cleanup some imports in http_utils.py.
35
    transport,
36
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
37
from ..sixish import (
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
38
    BytesIO,
39
    )
6670.4.16 by Jelmer Vernooij
Move smart to breezy.bzr.
40
from ..bzr.smart import (
5247.3.31 by Vincent Ladeuil
Cleanup some imports in http_utils.py.
41
    medium,
42
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
43
from . import http_server
44
from ..transport import chroot
3111.1.16 by Vincent Ladeuil
Fix more imports.
45
46
47
class HTTPServerWithSmarts(http_server.HttpServer):
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
48
    """HTTPServerWithSmarts extends the HttpServer with POST methods that will
49
    trigger a smart server to execute with a transport rooted at the rootdir of
50
    the HTTP server.
51
    """
52
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
53
    def __init__(self, protocol_version=None):
54
        http_server.HttpServer.__init__(self, SmartRequestHandler,
55
                                        protocol_version=protocol_version)
3111.1.16 by Vincent Ladeuil
Fix more imports.
56
57
58
class SmartRequestHandler(http_server.TestingHTTPRequestHandler):
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
59
    """Extend TestingHTTPRequestHandler to support smart client POSTs.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
60
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
61
    XXX: This duplicates a fair bit of the logic in breezy.transport.http.wsgi.
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
62
    """
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
63
64
    def do_POST(self):
65
        """Hand the request off to a smart server instance."""
6039.1.5 by Jelmer Vernooij
Add get_transport_from_url and get_transport_from_path functions.
66
        backing = transport.get_transport_from_path(
5247.3.31 by Vincent Ladeuil
Cleanup some imports in http_utils.py.
67
            self.server.test_case_server._home_dir)
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
68
        chroot_server = chroot.ChrootServer(backing)
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
69
        chroot_server.start_server()
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
70
        try:
6039.1.5 by Jelmer Vernooij
Add get_transport_from_url and get_transport_from_path functions.
71
            t = transport.get_transport_from_url(chroot_server.get_url())
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
72
            self.do_POST_inner(t)
73
        finally:
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
74
            chroot_server.stop_server()
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
75
76
    def do_POST_inner(self, chrooted_transport):
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
77
        self.send_response(200)
78
        self.send_header("Content-type", "application/octet-stream")
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
79
        if not self.path.endswith('.bzr/smart'):
80
            raise AssertionError(
81
                'POST to path not ending in .bzr/smart: %r' % (self.path,))
82
        t = chrooted_transport.clone(self.path[:-len('.bzr/smart')])
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
83
        # if this fails, we should return 400 bad request, but failure is
84
        # failure for now - RBC 20060919
85
        data_length = int(self.headers['Content-Length'])
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
86
        # TODO: We might like to support streaming responses.  1.0 allows no
87
        # Content-length in this case, so for integrity we should perform our
88
        # own chunking within the stream.
89
        # 1.1 allows chunked responses, and in this case we could chunk using
90
        # the HTTP chunking as this will allow HTTP persistence safely, even if
91
        # we have to stop early due to error, but we would also have to use the
92
        # HTTP trailer facility which may not be widely available.
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
93
        request_bytes = self.rfile.read(data_length)
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
94
        protocol_factory, unused_bytes = (
95
            medium._get_protocol_factory_for_bytes(request_bytes))
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
96
        out_buffer = BytesIO()
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
97
        smart_protocol_request = protocol_factory(t, out_buffer.write, '/')
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
98
        # Perhaps there should be a SmartServerHTTPMedium that takes care of
99
        # feeding the bytes in the http request to the smart_protocol_request,
100
        # but for now it's simpler to just feed the bytes directly.
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
101
        smart_protocol_request.accept_bytes(unused_bytes)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
102
        if not (smart_protocol_request.next_read_size() == 0):
103
            raise errors.SmartProtocolError(
104
                "not finished reading, but all data sent to protocol.")
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
105
        self.send_header("Content-Length", str(len(out_buffer.getvalue())))
106
        self.end_headers()
107
        self.wfile.write(out_buffer.getvalue())
108
109
3111.1.16 by Vincent Ladeuil
Fix more imports.
110
class TestCaseWithWebserver(tests.TestCaseWithTransport):
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
111
    """A support class that provides readonly urls that are http://.
112
2004.3.3 by vila
Better (but still incomplete) design for bogus servers.
113
    This is done by forcing the readonly server to be an http
114
    one. This will currently fail if the primary transport is not
115
    backed by regular disk files.
1185.1.18 by Robert Collins
Lalo Martins remotebranch patch
116
    """
5273.1.4 by Vincent Ladeuil
The default http protocol version wasn't properly defined and as such not respected by some parametrized tests.
117
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.
118
    # These attributes can be overriden or parametrized by daughter clasess if
119
    # needed, but must exist so that the create_transport_readonly_server()
5247.6.6 by Vincent Ladeuil
Closing the connection is what pycurl was waiting for.
120
    # method (or any method creating an http(s) server) can propagate it.
5273.1.4 by Vincent Ladeuil
The default http protocol version wasn't properly defined and as such not respected by some parametrized tests.
121
    _protocol_version = None
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.
122
    _url_protocol = 'http'
5273.1.4 by Vincent Ladeuil
The default http protocol version wasn't properly defined and as such not respected by some parametrized tests.
123
1185.1.18 by Robert Collins
Lalo Martins remotebranch patch
124
    def setUp(self):
1530.1.14 by Robert Collins
Remove duplicate web server from HTTPTestUtil.
125
        super(TestCaseWithWebserver, self).setUp()
3111.1.16 by Vincent Ladeuil
Fix more imports.
126
        self.transport_readonly_server = http_server.HttpServer
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
127
5273.1.4 by Vincent Ladeuil
The default http protocol version wasn't properly defined and as such not respected by some parametrized tests.
128
    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.
129
        server = self.transport_readonly_server(
5273.1.4 by Vincent Ladeuil
The default http protocol version wasn't properly defined and as such not respected by some parametrized tests.
130
            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.
131
        server._url_protocol = self._url_protocol
132
        return server
5273.1.4 by Vincent Ladeuil
The default http protocol version wasn't properly defined and as such not respected by some parametrized tests.
133
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
134
135
class TestCaseWithTwoWebservers(TestCaseWithWebserver):
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
136
    """A support class providing readonly urls on two servers that are http://.
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
137
2164.2.25 by Vincent Ladeuil
Fix typos noticed by Aaron.
138
    We set up two webservers to allows various tests involving
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
139
    proxies or redirections from one server to the other.
140
    """
7143.15.2 by Jelmer Vernooij
Run autopep8.
141
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
142
    def setUp(self):
143
        super(TestCaseWithTwoWebservers, self).setUp()
3111.1.16 by Vincent Ladeuil
Fix more imports.
144
        self.transport_secondary_server = http_server.HttpServer
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
145
        self.__secondary_server = None
146
147
    def create_transport_secondary_server(self):
148
        """Create a transport server from class defined at init.
149
150
        This is mostly a hook for daughter classes.
151
        """
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.
152
        server = self.transport_secondary_server(
5273.1.4 by Vincent Ladeuil
The default http protocol version wasn't properly defined and as such not respected by some parametrized tests.
153
            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.
154
        server._url_protocol = self._url_protocol
155
        return server
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
156
157
    def get_secondary_server(self):
158
        """Get the server instance for the secondary transport."""
159
        if self.__secondary_server is None:
160
            self.__secondary_server = self.create_transport_secondary_server()
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
161
            self.start_server(self.__secondary_server)
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
162
        return self.__secondary_server
163
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.
164
    def get_secondary_url(self, relpath=None):
165
        base = self.get_secondary_server().get_url()
166
        return self._adjust_url(base, relpath)
167
168
    def get_secondary_transport(self, relpath=None):
6039.1.5 by Jelmer Vernooij
Add get_transport_from_url and get_transport_from_path functions.
169
        t = transport.get_transport_from_url(self.get_secondary_url(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.
170
        self.assertTrue(t.is_readonly())
171
        return t
172
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
173
3111.1.16 by Vincent Ladeuil
Fix more imports.
174
class ProxyServer(http_server.HttpServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
175
    """A proxy test server for http transports."""
176
177
    proxy_requests = True
2213.1.1 by v.ladeuil+lp at free
Workaround SimpleHTTPRequestHandler.translate_path limitation in
178
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
179
3111.1.16 by Vincent Ladeuil
Fix more imports.
180
class RedirectRequestHandler(http_server.TestingHTTPRequestHandler):
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
181
    """Redirect all request to the specified server"""
182
183
    def parse_request(self):
184
        """Redirect a single HTTP request to another host"""
3111.1.16 by Vincent Ladeuil
Fix more imports.
185
        valid = http_server.TestingHTTPRequestHandler.parse_request(self)
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
186
        if valid:
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
187
            tcs = self.server.test_case_server
188
            code, target = tcs.is_redirected(self.path)
189
            if code is not None and target is not None:
190
                # Redirect as instructed
191
                self.send_response(code)
2164.2.16 by Vincent Ladeuil
Add tests.
192
                self.send_header('Location', target)
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
193
                # We do not send a body
194
                self.send_header('Content-Length', '0')
2164.2.16 by Vincent Ladeuil
Add tests.
195
                self.end_headers()
7143.15.2 by Jelmer Vernooij
Run autopep8.
196
                return False  # The job is done
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
197
            else:
198
                # We leave the parent class serve the request
199
                pass
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
200
        return valid
201
202
3111.1.16 by Vincent Ladeuil
Fix more imports.
203
class HTTPServerRedirecting(http_server.HttpServer):
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
204
    """An HttpServer redirecting to another server """
205
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
206
    def __init__(self, request_handler=RedirectRequestHandler,
207
                 protocol_version=None):
208
        http_server.HttpServer.__init__(self, request_handler,
209
                                        protocol_version=protocol_version)
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
210
        # redirections is a list of tuples (source, target, code)
211
        # - source is a regexp for the paths requested
212
        # - target is a replacement for re.sub describing where
213
        #   the request will be redirected
214
        # - code is the http error code associated to the
215
        #   redirection (301 permanent, 302 temporarry, etc
216
        self.redirections = []
217
218
    def redirect_to(self, host, port):
219
        """Redirect all requests to a specific host:port"""
220
        self.redirections = [('(.*)',
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
221
                              r'http://%s:%s\1' % (host, port),
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
222
                              301)]
223
224
    def is_redirected(self, path):
225
        """Is the path redirected by this server.
226
227
        :param path: the requested relative path
228
229
        :returns: a tuple (code, target) if a matching
230
             redirection is found, (None, None) otherwise.
231
        """
232
        code = None
233
        target = None
234
        for (rsource, rtarget, rcode) in self.redirections:
7206.3.10 by Jelmer Vernooij
Fix two more tests.
235
            target, match = re.subn(rsource, rtarget, path, count=1)
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
236
            if match:
237
                code = rcode
7143.15.2 by Jelmer Vernooij
Run autopep8.
238
                break  # The first match wins
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
239
            else:
240
                target = None
241
        return code, target
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
242
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
243
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
244
class TestCaseWithRedirectedWebserver(TestCaseWithTwoWebservers):
7143.15.2 by Jelmer Vernooij
Run autopep8.
245
    """A support class providing redirections from one server to another.
246
247
    We set up two webservers to allows various tests involving
248
    redirections.
249
    The 'old' server is redirected to the 'new' server.
250
    """
251
252
    def setUp(self):
253
        super(TestCaseWithRedirectedWebserver, self).setUp()
254
        # The redirections will point to the new server
255
        self.new_server = self.get_readonly_server()
256
        # The requests to the old server will be redirected to the new server
257
        self.old_server = self.get_secondary_server()
258
259
    def create_transport_secondary_server(self):
260
        """Create the secondary server redirecting to the primary server"""
261
        new = self.get_readonly_server()
262
        redirecting = HTTPServerRedirecting(
263
            protocol_version=self._protocol_version)
264
        redirecting.redirect_to(new.host, new.port)
265
        redirecting._url_protocol = self._url_protocol
266
        return redirecting
267
268
    def get_old_url(self, relpath=None):
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.
269
        base = self.old_server.get_url()
270
        return self._adjust_url(base, relpath)
271
7143.15.2 by Jelmer Vernooij
Run autopep8.
272
    def get_old_transport(self, relpath=None):
6039.1.5 by Jelmer Vernooij
Add get_transport_from_url and get_transport_from_path functions.
273
        t = transport.get_transport_from_url(self.get_old_url(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.
274
        self.assertTrue(t.is_readonly())
275
        return t
276
7143.15.2 by Jelmer Vernooij
Run autopep8.
277
    def get_new_url(self, relpath=None):
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.
278
        base = self.new_server.get_url()
279
        return self._adjust_url(base, relpath)
280
7143.15.2 by Jelmer Vernooij
Run autopep8.
281
    def get_new_transport(self, relpath=None):
6039.1.5 by Jelmer Vernooij
Add get_transport_from_url and get_transport_from_path functions.
282
        t = transport.get_transport_from_url(self.get_new_url(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.
283
        self.assertTrue(t.is_readonly())
284
        return t
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
285
286
3111.1.16 by Vincent Ladeuil
Fix more imports.
287
class AuthRequestHandler(http_server.TestingHTTPRequestHandler):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
288
    """Requires an authentication to process requests.
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
289
290
    This is intended to be used with a server that always and
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
291
    only use one authentication scheme (implemented by daughter
292
    classes).
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
293
    """
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
294
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
295
    # The following attributes should be defined in the server
2420.1.10 by Vincent Ladeuil
Doc fixes.
296
    # - auth_header_sent: the header name sent to require auth
297
    # - auth_header_recv: the header received containing auth
298
    # - auth_error_code: the error code to indicate auth required
2420.1.2 by Vincent Ladeuil
Define tests for http proxy basic authentication. They fail.
299
5957.2.2 by Vincent Ladeuil
Allows HEAD to be used in tests while still requiring authentication.
300
    def _require_authentication(self):
301
        # Note that we must update test_case_server *before*
302
        # sending the error or the client may try to read it
303
        # before we have sent the whole error back.
304
        tcs = self.server.test_case_server
305
        tcs.auth_required_errors += 1
306
        self.send_response(tcs.auth_error_code)
307
        self.send_header_auth_reqed()
308
        # We do not send a body
309
        self.send_header('Content-Length', '0')
310
        self.end_headers()
311
        return
312
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
313
    def do_GET(self):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
314
        if self.authorized():
3111.1.16 by Vincent Ladeuil
Fix more imports.
315
            return http_server.TestingHTTPRequestHandler.do_GET(self)
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
316
        else:
5957.2.2 by Vincent Ladeuil
Allows HEAD to be used in tests while still requiring authentication.
317
            return self._require_authentication()
318
319
    def do_HEAD(self):
320
        if self.authorized():
321
            return http_server.TestingHTTPRequestHandler.do_HEAD(self)
322
        else:
323
            return self._require_authentication()
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
324
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
325
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
326
class BasicAuthRequestHandler(AuthRequestHandler):
327
    """Implements the basic authentication of a request"""
328
329
    def authorized(self):
330
        tcs = self.server.test_case_server
331
        if tcs.auth_scheme != 'basic':
332
            return False
333
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
334
        auth_header = self.headers.get(tcs.auth_header_recv, None)
335
        if auth_header:
336
            scheme, raw_auth = auth_header.split(' ', 1)
337
            if scheme.lower() == tcs.auth_scheme:
7045.5.4 by Jelmer Vernooij
Fix a few more tests.
338
                user, password = base64.b64decode(raw_auth).split(b':')
7078.10.1 by Jelmer Vernooij
Fix most remaining http tests.
339
                return tcs.authorized(user.decode('ascii'),
340
                                      password.decode('ascii'))
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
341
342
        return False
343
344
    def send_header_auth_reqed(self):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
345
        tcs = self.server.test_case_server
346
        self.send_header(tcs.auth_header_sent,
347
                         'Basic realm="%s"' % tcs.auth_realm)
348
349
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
350
# FIXME: We could send an Authentication-Info header too when
351
# the authentication is succesful
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
352
353
class DigestAuthRequestHandler(AuthRequestHandler):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
354
    """Implements the digest authentication of a request.
355
356
    We need persistence for some attributes and that can't be
357
    achieved here since we get instantiated for each request. We
358
    rely on the DigestAuthServer to take care of them.
359
    """
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
360
361
    def authorized(self):
362
        tcs = self.server.test_case_server
363
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
364
        auth_header = self.headers.get(tcs.auth_header_recv, None)
365
        if auth_header is None:
366
            return False
367
        scheme, auth = auth_header.split(None, 1)
368
        if scheme.lower() == tcs.auth_scheme:
6791.2.3 by Jelmer Vernooij
Fix more imports.
369
            auth_dict = parse_keqv_list(parse_http_list(auth))
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
370
371
            return tcs.digest_authorized(auth_dict, self.command)
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
372
373
        return False
374
375
    def send_header_auth_reqed(self):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
376
        tcs = self.server.test_case_server
377
        header = 'Digest realm="%s", ' % tcs.auth_realm
2545.2.1 by Vincent Ladeuil
Fix 121889 by working around urllib2 bug.
378
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
379
                                                              'MD5')
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
380
        self.send_header(tcs.auth_header_sent, header)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
381
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
382
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
383
class DigestAndBasicAuthRequestHandler(DigestAuthRequestHandler):
384
    """Implements a digest and basic authentication of a request.
385
386
    I.e. the server proposes both schemes and the client should choose the best
387
    one it can handle, which, in that case, should be digest, the only scheme
388
    accepted here.
389
    """
390
391
    def send_header_auth_reqed(self):
392
        tcs = self.server.test_case_server
393
        self.send_header(tcs.auth_header_sent,
394
                         'Basic realm="%s"' % tcs.auth_realm)
395
        header = 'Digest realm="%s", ' % tcs.auth_realm
396
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
397
                                                              'MD5')
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
398
        self.send_header(tcs.auth_header_sent, header)
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
399
400
3111.1.16 by Vincent Ladeuil
Fix more imports.
401
class AuthServer(http_server.HttpServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
402
    """Extends HttpServer with a dictionary of passwords.
403
404
    This is used as a base class for various schemes which should
405
    all use or redefined the associated AuthRequestHandler.
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
406
407
    Note that no users are defined by default, so add_user should
408
    be called before issuing the first request.
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
409
    """
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
410
411
    # The following attributes should be set dy daughter classes
412
    # and are used by AuthRequestHandler.
413
    auth_header_sent = None
414
    auth_header_recv = None
415
    auth_error_code = None
7045.5.4 by Jelmer Vernooij
Fix a few more tests.
416
    auth_realm = u"Thou should not pass"
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
417
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
418
    def __init__(self, request_handler, auth_scheme,
419
                 protocol_version=None):
420
        http_server.HttpServer.__init__(self, request_handler,
421
                                        protocol_version=protocol_version)
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
422
        self.auth_scheme = auth_scheme
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
423
        self.password_of = {}
2420.1.4 by Vincent Ladeuil
Add test checking the number of roundtrips due to 401 or 407 errors.
424
        self.auth_required_errors = 0
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
425
426
    def add_user(self, user, password):
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
427
        """Declare a user with an associated password.
428
429
        password can be empty, use an empty string ('') in that
430
        case, not None.
431
        """
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
432
        self.password_of[user] = password
433
434
    def authorized(self, user, password):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
435
        """Check that the given user provided the right password"""
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
436
        expected_password = self.password_of.get(user, None)
437
        return expected_password is not None and password == expected_password
438
439
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
440
# FIXME: There is some code duplication with
2900.2.5 by Vincent Ladeuil
ake ftp aware of authentication config.
441
# _urllib2_wrappers.py.DigestAuthHandler. If that duplication
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
442
# grows, it may require a refactoring. Also, we don't implement
443
# SHA algorithm nor MD5-sess here, but that does not seem worth
444
# it.
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
445
class DigestAuthServer(AuthServer):
446
    """A digest authentication server"""
447
2420.1.16 by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing.
448
    auth_nonce = 'now!'
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
449
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
450
    def __init__(self, request_handler, auth_scheme,
451
                 protocol_version=None):
452
        AuthServer.__init__(self, request_handler, auth_scheme,
453
                            protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
454
455
    def digest_authorized(self, auth, command):
2420.1.16 by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing.
456
        nonce = auth['nonce']
457
        if nonce != self.auth_nonce:
458
            return False
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
459
        realm = auth['realm']
460
        if realm != self.auth_realm:
461
            return False
462
        user = auth['username']
6619.3.1 by Jelmer Vernooij
Apply 2to3 has_key fix.
463
        if user not in self.password_of:
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
464
            return False
7143.15.2 by Jelmer Vernooij
Run autopep8.
465
        algorithm = auth['algorithm']
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
466
        if algorithm != 'MD5':
467
            return False
468
        qop = auth['qop']
469
        if qop != 'auth':
470
            return False
471
472
        password = self.password_of[user]
473
474
        # Recalculate the response_digest to compare with the one
475
        # sent by the client
7045.5.4 by Jelmer Vernooij
Fix a few more tests.
476
        A1 = ('%s:%s:%s' % (user, realm, password)).encode('utf-8')
477
        A2 = ('%s:%s' % (command, auth['uri'])).encode('utf-8')
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
478
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
479
        def H(x):
480
            return osutils.md5(x).hexdigest()
7143.15.2 by Jelmer Vernooij
Run autopep8.
481
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
482
        def KD(secret, data):
483
            return H(("%s:%s" % (secret, data)).encode('utf-8'))
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
484
485
        nonce_count = int(auth['nc'], 16)
486
487
        ncvalue = '%08x' % nonce_count
488
489
        cnonce = auth['cnonce']
490
        noncebit = '%s:%s:%s:%s:%s' % (nonce, ncvalue, cnonce, qop, H(A2))
491
        response_digest = KD(H(A1), noncebit)
492
493
        return response_digest == auth['response']
494
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
495
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
496
class HTTPAuthServer(AuthServer):
497
    """An HTTP server requiring authentication"""
498
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
499
    def init_http_auth(self):
500
        self.auth_header_sent = 'WWW-Authenticate'
501
        self.auth_header_recv = 'Authorization'
502
        self.auth_error_code = 401
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
503
504
505
class ProxyAuthServer(AuthServer):
506
    """A proxy server requiring authentication"""
507
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
508
    def init_proxy_auth(self):
509
        self.proxy_requests = True
510
        self.auth_header_sent = 'Proxy-Authenticate'
511
        self.auth_header_recv = 'Proxy-Authorization'
512
        self.auth_error_code = 407
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
513
514
515
class HTTPBasicAuthServer(HTTPAuthServer):
516
    """An HTTP server requiring basic authentication"""
517
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
518
    def __init__(self, protocol_version=None):
519
        HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
520
                                protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
521
        self.init_http_auth()
522
523
524
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
525
    """An HTTP server requiring digest authentication"""
526
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
527
    def __init__(self, protocol_version=None):
528
        DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
529
                                  protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
530
        self.init_http_auth()
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
531
532
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
533
class HTTPBasicAndDigestAuthServer(DigestAuthServer, HTTPAuthServer):
534
    """An HTTP server requiring basic or digest authentication"""
535
536
    def __init__(self, protocol_version=None):
537
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
538
                                  'basicdigest',
539
                                  protocol_version=protocol_version)
540
        self.init_http_auth()
541
        # We really accept Digest only
542
        self.auth_scheme = 'digest'
543
544
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
545
class ProxyBasicAuthServer(ProxyAuthServer):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
546
    """A proxy server requiring basic authentication"""
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
547
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
548
    def __init__(self, protocol_version=None):
549
        ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
550
                                 protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
551
        self.init_proxy_auth()
552
553
554
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
555
    """A proxy server requiring basic authentication"""
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
556
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
557
    def __init__(self, protocol_version=None):
558
        ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
559
                                 protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
560
        self.init_proxy_auth()
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
561
562
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
563
class ProxyBasicAndDigestAuthServer(DigestAuthServer, ProxyAuthServer):
564
    """An proxy server requiring basic or digest authentication"""
565
566
    def __init__(self, protocol_version=None):
567
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
568
                                  'basicdigest',
569
                                  protocol_version=protocol_version)
570
        self.init_proxy_auth()
571
        # We really accept Digest only
572
        self.auth_scheme = 'digest'