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