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