/brz/remove-bazaar

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