~bzr-pqm/bzr/bzr.dev

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