/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2005-2010 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
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
17
from cStringIO import StringIO
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
18
import errno
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
19
import re
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
20
import socket
3111.1.7 by Vincent Ladeuil
Further refactoring.
21
import threading
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
22
import time
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
23
import urllib2
2213.1.1 by v.ladeuil+lp at free
Workaround SimpleHTTPRequestHandler.translate_path limitation in
24
import urlparse
1530.1.14 by Robert Collins
Remove duplicate web server from HTTPTestUtil.
25
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
26
3111.1.16 by Vincent Ladeuil
Fix more imports.
27
from bzrlib import (
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
28
    errors,
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
29
    osutils,
3111.1.16 by Vincent Ladeuil
Fix more imports.
30
    tests,
31
    )
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
32
from bzrlib.smart import medium, protocol
3111.1.16 by Vincent Ladeuil
Fix more imports.
33
from bzrlib.tests import http_server
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
34
from bzrlib.transport import (
35
    chroot,
36
    get_transport,
37
    )
3111.1.16 by Vincent Ladeuil
Fix more imports.
38
39
40
class HTTPServerWithSmarts(http_server.HttpServer):
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
41
    """HTTPServerWithSmarts extends the HttpServer with POST methods that will
42
    trigger a smart server to execute with a transport rooted at the rootdir of
43
    the HTTP server.
44
    """
45
3111.1.25 by Vincent Ladeuil
Fix the smart server failing test and use it against protocol combinations.
46
    def __init__(self, protocol_version=None):
47
        http_server.HttpServer.__init__(self, SmartRequestHandler,
48
                                        protocol_version=protocol_version)
3111.1.16 by Vincent Ladeuil
Fix more imports.
49
50
51
class SmartRequestHandler(http_server.TestingHTTPRequestHandler):
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
52
    """Extend TestingHTTPRequestHandler to support smart client POSTs.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
53
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
54
    XXX: This duplicates a fair bit of the logic in bzrlib.transport.http.wsgi.
55
    """
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
56
57
    def do_POST(self):
58
        """Hand the request off to a smart server instance."""
3606.4.1 by Andrew Bennetts
Fix NotImplementedError when probing for smart protocol via HTTP.
59
        backing = get_transport(self.server.test_case_server._home_dir)
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:
63
            t = get_transport(chroot_server.get_url())
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)
86
        protocol_factory, unused_bytes = medium._get_protocol_factory_for_bytes(
87
            request_bytes)
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
88
        out_buffer = StringIO()
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()
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
    """
133
    def setUp(self):
134
        super(TestCaseWithTwoWebservers, self).setUp()
3111.1.16 by Vincent Ladeuil
Fix more imports.
135
        self.transport_secondary_server = http_server.HttpServer
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
136
        self.__secondary_server = None
137
138
    def create_transport_secondary_server(self):
139
        """Create a transport server from class defined at init.
140
141
        This is mostly a hook for daughter classes.
142
        """
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.
143
        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.
144
            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.
145
        server._url_protocol = self._url_protocol
146
        return server
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
147
148
    def get_secondary_server(self):
149
        """Get the server instance for the secondary transport."""
150
        if self.__secondary_server is None:
151
            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,
152
            self.start_server(self.__secondary_server)
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
153
        return self.__secondary_server
154
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.
155
    def get_secondary_url(self, relpath=None):
156
        base = self.get_secondary_server().get_url()
157
        return self._adjust_url(base, relpath)
158
159
    def get_secondary_transport(self, relpath=None):
160
        t = get_transport(self.get_secondary_url(relpath))
161
        self.assertTrue(t.is_readonly())
162
        return t
163
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
164
3111.1.16 by Vincent Ladeuil
Fix more imports.
165
class ProxyServer(http_server.HttpServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
166
    """A proxy test server for http transports."""
167
168
    proxy_requests = True
2213.1.1 by v.ladeuil+lp at free
Workaround SimpleHTTPRequestHandler.translate_path limitation in
169
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
170
3111.1.16 by Vincent Ladeuil
Fix more imports.
171
class RedirectRequestHandler(http_server.TestingHTTPRequestHandler):
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
172
    """Redirect all request to the specified server"""
173
174
    def parse_request(self):
175
        """Redirect a single HTTP request to another host"""
3111.1.16 by Vincent Ladeuil
Fix more imports.
176
        valid = http_server.TestingHTTPRequestHandler.parse_request(self)
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
177
        if valid:
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
178
            tcs = self.server.test_case_server
179
            code, target = tcs.is_redirected(self.path)
180
            if code is not None and target is not None:
181
                # Redirect as instructed
182
                self.send_response(code)
2164.2.16 by Vincent Ladeuil
Add tests.
183
                self.send_header('Location', target)
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
184
                # We do not send a body
185
                self.send_header('Content-Length', '0')
2164.2.16 by Vincent Ladeuil
Add tests.
186
                self.end_headers()
187
                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
188
            else:
189
                # We leave the parent class serve the request
190
                pass
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
191
        return valid
192
193
3111.1.16 by Vincent Ladeuil
Fix more imports.
194
class HTTPServerRedirecting(http_server.HttpServer):
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
195
    """An HttpServer redirecting to another server """
196
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
197
    def __init__(self, request_handler=RedirectRequestHandler,
198
                 protocol_version=None):
199
        http_server.HttpServer.__init__(self, request_handler,
200
                                        protocol_version=protocol_version)
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
201
        # redirections is a list of tuples (source, target, code)
202
        # - source is a regexp for the paths requested
203
        # - target is a replacement for re.sub describing where
204
        #   the request will be redirected
205
        # - code is the http error code associated to the
206
        #   redirection (301 permanent, 302 temporarry, etc
207
        self.redirections = []
208
209
    def redirect_to(self, host, port):
210
        """Redirect all requests to a specific host:port"""
211
        self.redirections = [('(.*)',
212
                              r'http://%s:%s\1' % (host, port) ,
213
                              301)]
214
215
    def is_redirected(self, path):
216
        """Is the path redirected by this server.
217
218
        :param path: the requested relative path
219
220
        :returns: a tuple (code, target) if a matching
221
             redirection is found, (None, None) otherwise.
222
        """
223
        code = None
224
        target = None
225
        for (rsource, rtarget, rcode) in self.redirections:
226
            target, match = re.subn(rsource, rtarget, path)
227
            if match:
228
                code = rcode
229
                break # The first match wins
230
            else:
231
                target = None
232
        return code, target
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
233
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
234
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
235
class TestCaseWithRedirectedWebserver(TestCaseWithTwoWebservers):
236
   """A support class providing redirections from one server to another.
237
2164.2.25 by Vincent Ladeuil
Fix typos noticed by Aaron.
238
   We set up two webservers to allows various tests involving
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
239
   redirections.
240
   The 'old' server is redirected to the 'new' server.
241
   """
242
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.
243
   def setUp(self):
244
       super(TestCaseWithRedirectedWebserver, self).setUp()
245
       # The redirections will point to the new server
246
       self.new_server = self.get_readonly_server()
247
       # The requests to the old server will be redirected to the new server
248
       self.old_server = self.get_secondary_server()
249
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
250
   def create_transport_secondary_server(self):
251
       """Create the secondary server redirecting to the primary server"""
252
       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.
253
       redirecting = HTTPServerRedirecting(
254
           protocol_version=self._protocol_version)
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
255
       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.
256
       redirecting._url_protocol = self._url_protocol
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
257
       return redirecting
258
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.
259
   def get_old_url(self, relpath=None):
260
        base = self.old_server.get_url()
261
        return self._adjust_url(base, relpath)
262
263
   def get_old_transport(self, relpath=None):
264
        t = get_transport(self.get_old_url(relpath))
265
        self.assertTrue(t.is_readonly())
266
        return t
267
268
   def get_new_url(self, relpath=None):
269
        base = self.new_server.get_url()
270
        return self._adjust_url(base, relpath)
271
272
   def get_new_transport(self, relpath=None):
273
        t = get_transport(self.get_new_url(relpath))
274
        self.assertTrue(t.is_readonly())
275
        return t
2164.2.22 by Vincent Ladeuil
Take Aaron's review comments into account.
276
277
3111.1.16 by Vincent Ladeuil
Fix more imports.
278
class AuthRequestHandler(http_server.TestingHTTPRequestHandler):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
279
    """Requires an authentication to process requests.
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
280
281
    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.
282
    only use one authentication scheme (implemented by daughter
283
    classes).
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
284
    """
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
285
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
286
    # The following attributes should be defined in the server
2420.1.10 by Vincent Ladeuil
Doc fixes.
287
    # - auth_header_sent: the header name sent to require auth
288
    # - auth_header_recv: the header received containing auth
289
    # - 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.
290
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
291
    def do_GET(self):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
292
        if self.authorized():
3111.1.16 by Vincent Ladeuil
Fix more imports.
293
            return http_server.TestingHTTPRequestHandler.do_GET(self)
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
294
        else:
295
            # Note that we must update test_case_server *before*
296
            # sending the error or the client may try to read it
297
            # before we have sent the whole error back.
298
            tcs = self.server.test_case_server
299
            tcs.auth_required_errors += 1
300
            self.send_response(tcs.auth_error_code)
301
            self.send_header_auth_reqed()
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
302
            # We do not send a body
303
            self.send_header('Content-Length', '0')
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
304
            self.end_headers()
305
            return
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
306
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
307
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
308
class BasicAuthRequestHandler(AuthRequestHandler):
309
    """Implements the basic authentication of a request"""
310
311
    def authorized(self):
312
        tcs = self.server.test_case_server
313
        if tcs.auth_scheme != 'basic':
314
            return False
315
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
316
        auth_header = self.headers.get(tcs.auth_header_recv, None)
317
        if auth_header:
318
            scheme, raw_auth = auth_header.split(' ', 1)
319
            if scheme.lower() == tcs.auth_scheme:
320
                user, password = raw_auth.decode('base64').split(':')
321
                return tcs.authorized(user, password)
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
322
323
        return False
324
325
    def send_header_auth_reqed(self):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
326
        tcs = self.server.test_case_server
327
        self.send_header(tcs.auth_header_sent,
328
                         'Basic realm="%s"' % tcs.auth_realm)
329
330
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
331
# FIXME: We could send an Authentication-Info header too when
332
# the authentication is succesful
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
333
334
class DigestAuthRequestHandler(AuthRequestHandler):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
335
    """Implements the digest authentication of a request.
336
337
    We need persistence for some attributes and that can't be
338
    achieved here since we get instantiated for each request. We
339
    rely on the DigestAuthServer to take care of them.
340
    """
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
341
342
    def authorized(self):
343
        tcs = self.server.test_case_server
344
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
345
        auth_header = self.headers.get(tcs.auth_header_recv, None)
346
        if auth_header is None:
347
            return False
348
        scheme, auth = auth_header.split(None, 1)
349
        if scheme.lower() == tcs.auth_scheme:
350
            auth_dict = urllib2.parse_keqv_list(urllib2.parse_http_list(auth))
351
352
            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.
353
354
        return False
355
356
    def send_header_auth_reqed(self):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
357
        tcs = self.server.test_case_server
358
        header = 'Digest realm="%s", ' % tcs.auth_realm
2545.2.1 by Vincent Ladeuil
Fix 121889 by working around urllib2 bug.
359
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
360
                                                              'MD5')
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
361
        self.send_header(tcs.auth_header_sent,header)
362
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
363
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
364
class DigestAndBasicAuthRequestHandler(DigestAuthRequestHandler):
365
    """Implements a digest and basic authentication of a request.
366
367
    I.e. the server proposes both schemes and the client should choose the best
368
    one it can handle, which, in that case, should be digest, the only scheme
369
    accepted here.
370
    """
371
372
    def send_header_auth_reqed(self):
373
        tcs = self.server.test_case_server
374
        self.send_header(tcs.auth_header_sent,
375
                         'Basic realm="%s"' % tcs.auth_realm)
376
        header = 'Digest realm="%s", ' % tcs.auth_realm
377
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
378
                                                              'MD5')
379
        self.send_header(tcs.auth_header_sent,header)
380
381
3111.1.16 by Vincent Ladeuil
Fix more imports.
382
class AuthServer(http_server.HttpServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
383
    """Extends HttpServer with a dictionary of passwords.
384
385
    This is used as a base class for various schemes which should
386
    all use or redefined the associated AuthRequestHandler.
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
387
388
    Note that no users are defined by default, so add_user should
389
    be called before issuing the first request.
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
390
    """
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
391
392
    # The following attributes should be set dy daughter classes
393
    # and are used by AuthRequestHandler.
394
    auth_header_sent = None
395
    auth_header_recv = None
396
    auth_error_code = None
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
397
    auth_realm = "Thou should not pass"
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
398
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
399
    def __init__(self, request_handler, auth_scheme,
400
                 protocol_version=None):
401
        http_server.HttpServer.__init__(self, request_handler,
402
                                        protocol_version=protocol_version)
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
403
        self.auth_scheme = auth_scheme
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
404
        self.password_of = {}
2420.1.4 by Vincent Ladeuil
Add test checking the number of roundtrips due to 401 or 407 errors.
405
        self.auth_required_errors = 0
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
406
407
    def add_user(self, user, password):
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
408
        """Declare a user with an associated password.
409
410
        password can be empty, use an empty string ('') in that
411
        case, not None.
412
        """
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
413
        self.password_of[user] = password
414
415
    def authorized(self, user, password):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
416
        """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
417
        expected_password = self.password_of.get(user, None)
418
        return expected_password is not None and password == expected_password
419
420
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
421
# FIXME: There is some code duplication with
2900.2.5 by Vincent Ladeuil
ake ftp aware of authentication config.
422
# _urllib2_wrappers.py.DigestAuthHandler. If that duplication
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
423
# grows, it may require a refactoring. Also, we don't implement
424
# SHA algorithm nor MD5-sess here, but that does not seem worth
425
# it.
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
426
class DigestAuthServer(AuthServer):
427
    """A digest authentication server"""
428
2420.1.16 by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing.
429
    auth_nonce = 'now!'
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
430
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
431
    def __init__(self, request_handler, auth_scheme,
432
                 protocol_version=None):
433
        AuthServer.__init__(self, request_handler, auth_scheme,
434
                            protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
435
436
    def digest_authorized(self, auth, command):
2420.1.16 by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing.
437
        nonce = auth['nonce']
438
        if nonce != self.auth_nonce:
439
            return False
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
440
        realm = auth['realm']
441
        if realm != self.auth_realm:
442
            return False
443
        user = auth['username']
444
        if not self.password_of.has_key(user):
445
            return False
446
        algorithm= auth['algorithm']
447
        if algorithm != 'MD5':
448
            return False
449
        qop = auth['qop']
450
        if qop != 'auth':
451
            return False
452
453
        password = self.password_of[user]
454
455
        # Recalculate the response_digest to compare with the one
456
        # sent by the client
457
        A1 = '%s:%s:%s' % (user, realm, password)
458
        A2 = '%s:%s' % (command, auth['uri'])
459
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
460
        H = lambda x: osutils.md5(x).hexdigest()
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
461
        KD = lambda secret, data: H("%s:%s" % (secret, data))
462
463
        nonce_count = int(auth['nc'], 16)
464
465
        ncvalue = '%08x' % nonce_count
466
467
        cnonce = auth['cnonce']
468
        noncebit = '%s:%s:%s:%s:%s' % (nonce, ncvalue, cnonce, qop, H(A2))
469
        response_digest = KD(H(A1), noncebit)
470
471
        return response_digest == auth['response']
472
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
473
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
474
class HTTPAuthServer(AuthServer):
475
    """An HTTP server requiring authentication"""
476
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
477
    def init_http_auth(self):
478
        self.auth_header_sent = 'WWW-Authenticate'
479
        self.auth_header_recv = 'Authorization'
480
        self.auth_error_code = 401
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
481
482
483
class ProxyAuthServer(AuthServer):
484
    """A proxy server requiring authentication"""
485
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
486
    def init_proxy_auth(self):
487
        self.proxy_requests = True
488
        self.auth_header_sent = 'Proxy-Authenticate'
489
        self.auth_header_recv = 'Proxy-Authorization'
490
        self.auth_error_code = 407
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
491
492
493
class HTTPBasicAuthServer(HTTPAuthServer):
494
    """An HTTP server requiring basic authentication"""
495
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
496
    def __init__(self, protocol_version=None):
497
        HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
498
                                protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
499
        self.init_http_auth()
500
501
502
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
503
    """An HTTP server requiring digest authentication"""
504
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
505
    def __init__(self, protocol_version=None):
506
        DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
507
                                  protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
508
        self.init_http_auth()
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
509
510
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
511
class HTTPBasicAndDigestAuthServer(DigestAuthServer, HTTPAuthServer):
512
    """An HTTP server requiring basic or digest authentication"""
513
514
    def __init__(self, protocol_version=None):
515
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
516
                                  'basicdigest',
517
                                  protocol_version=protocol_version)
518
        self.init_http_auth()
519
        # We really accept Digest only
520
        self.auth_scheme = 'digest'
521
522
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
523
class ProxyBasicAuthServer(ProxyAuthServer):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
524
    """A proxy server requiring basic authentication"""
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
525
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
526
    def __init__(self, protocol_version=None):
527
        ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
528
                                 protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
529
        self.init_proxy_auth()
530
531
532
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
533
    """A proxy server requiring basic authentication"""
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
534
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
535
    def __init__(self, protocol_version=None):
536
        ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
537
                                 protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
538
        self.init_proxy_auth()
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
539
540
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
541
class ProxyBasicAndDigestAuthServer(DigestAuthServer, ProxyAuthServer):
542
    """An proxy server requiring basic or digest authentication"""
543
544
    def __init__(self, protocol_version=None):
545
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
546
                                  'basicdigest',
547
                                  protocol_version=protocol_version)
548
        self.init_proxy_auth()
549
        # We really accept Digest only
550
        self.auth_scheme = 'digest'
551
552