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