/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':')
7078.10.1 by Jelmer Vernooij
Fix most remaining http tests.
338
                return tcs.authorized(user.decode('ascii'),
339
                                      password.decode('ascii'))
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
340
341
        return False
342
343
    def send_header_auth_reqed(self):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
344
        tcs = self.server.test_case_server
345
        self.send_header(tcs.auth_header_sent,
346
                         'Basic realm="%s"' % tcs.auth_realm)
347
348
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
349
# FIXME: We could send an Authentication-Info header too when
350
# the authentication is succesful
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
351
352
class DigestAuthRequestHandler(AuthRequestHandler):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
353
    """Implements the digest authentication of a request.
354
355
    We need persistence for some attributes and that can't be
356
    achieved here since we get instantiated for each request. We
357
    rely on the DigestAuthServer to take care of them.
358
    """
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
359
360
    def authorized(self):
361
        tcs = self.server.test_case_server
362
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
363
        auth_header = self.headers.get(tcs.auth_header_recv, None)
364
        if auth_header is None:
365
            return False
366
        scheme, auth = auth_header.split(None, 1)
367
        if scheme.lower() == tcs.auth_scheme:
6791.2.3 by Jelmer Vernooij
Fix more imports.
368
            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.
369
370
            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.
371
372
        return False
373
374
    def send_header_auth_reqed(self):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
375
        tcs = self.server.test_case_server
376
        header = 'Digest realm="%s", ' % tcs.auth_realm
2545.2.1 by Vincent Ladeuil
Fix 121889 by working around urllib2 bug.
377
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
378
                                                              'MD5')
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
379
        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.
380
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
381
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
382
class DigestAndBasicAuthRequestHandler(DigestAuthRequestHandler):
383
    """Implements a digest and basic authentication of a request.
384
385
    I.e. the server proposes both schemes and the client should choose the best
386
    one it can handle, which, in that case, should be digest, the only scheme
387
    accepted here.
388
    """
389
390
    def send_header_auth_reqed(self):
391
        tcs = self.server.test_case_server
392
        self.send_header(tcs.auth_header_sent,
393
                         'Basic realm="%s"' % tcs.auth_realm)
394
        header = 'Digest realm="%s", ' % tcs.auth_realm
395
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
396
                                                              'MD5')
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
397
        self.send_header(tcs.auth_header_sent, header)
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
398
399
3111.1.16 by Vincent Ladeuil
Fix more imports.
400
class AuthServer(http_server.HttpServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
401
    """Extends HttpServer with a dictionary of passwords.
402
403
    This is used as a base class for various schemes which should
404
    all use or redefined the associated AuthRequestHandler.
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
405
406
    Note that no users are defined by default, so add_user should
407
    be called before issuing the first request.
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
408
    """
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
409
410
    # The following attributes should be set dy daughter classes
411
    # and are used by AuthRequestHandler.
412
    auth_header_sent = None
413
    auth_header_recv = None
414
    auth_error_code = None
7045.5.4 by Jelmer Vernooij
Fix a few more tests.
415
    auth_realm = u"Thou should not pass"
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
416
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
417
    def __init__(self, request_handler, auth_scheme,
418
                 protocol_version=None):
419
        http_server.HttpServer.__init__(self, request_handler,
420
                                        protocol_version=protocol_version)
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
421
        self.auth_scheme = auth_scheme
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
422
        self.password_of = {}
2420.1.4 by Vincent Ladeuil
Add test checking the number of roundtrips due to 401 or 407 errors.
423
        self.auth_required_errors = 0
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
424
425
    def add_user(self, user, password):
2363.4.12 by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better
426
        """Declare a user with an associated password.
427
428
        password can be empty, use an empty string ('') in that
429
        case, not None.
430
        """
2363.4.8 by Vincent Ladeuil
Implement a basic auth HTTP server, rewrite tests accordingly.
431
        self.password_of[user] = password
432
433
    def authorized(self, user, password):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
434
        """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
435
        expected_password = self.password_of.get(user, None)
436
        return expected_password is not None and password == expected_password
437
438
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
439
# FIXME: There is some code duplication with
2900.2.5 by Vincent Ladeuil
ake ftp aware of authentication config.
440
# _urllib2_wrappers.py.DigestAuthHandler. If that duplication
2420.1.19 by Vincent Ladeuil
Cosmetic changes.
441
# grows, it may require a refactoring. Also, we don't implement
442
# SHA algorithm nor MD5-sess here, but that does not seem worth
443
# it.
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
444
class DigestAuthServer(AuthServer):
445
    """A digest authentication server"""
446
2420.1.16 by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing.
447
    auth_nonce = 'now!'
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
448
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
449
    def __init__(self, request_handler, auth_scheme,
450
                 protocol_version=None):
451
        AuthServer.__init__(self, request_handler, auth_scheme,
452
                            protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
453
454
    def digest_authorized(self, auth, command):
2420.1.16 by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing.
455
        nonce = auth['nonce']
456
        if nonce != self.auth_nonce:
457
            return False
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
458
        realm = auth['realm']
459
        if realm != self.auth_realm:
460
            return False
461
        user = auth['username']
6619.3.1 by Jelmer Vernooij
Apply 2to3 has_key fix.
462
        if user not in self.password_of:
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
463
            return False
464
        algorithm= auth['algorithm']
465
        if algorithm != 'MD5':
466
            return False
467
        qop = auth['qop']
468
        if qop != 'auth':
469
            return False
470
471
        password = self.password_of[user]
472
473
        # Recalculate the response_digest to compare with the one
474
        # sent by the client
7045.5.4 by Jelmer Vernooij
Fix a few more tests.
475
        A1 = ('%s:%s:%s' % (user, realm, password)).encode('utf-8')
476
        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.
477
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
478
        H = lambda x: osutils.md5(x).hexdigest()
7045.5.4 by Jelmer Vernooij
Fix a few more tests.
479
        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.
480
481
        nonce_count = int(auth['nc'], 16)
482
483
        ncvalue = '%08x' % nonce_count
484
485
        cnonce = auth['cnonce']
486
        noncebit = '%s:%s:%s:%s:%s' % (nonce, ncvalue, cnonce, qop, H(A2))
487
        response_digest = KD(H(A1), noncebit)
488
489
        return response_digest == auth['response']
490
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
491
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
492
class HTTPAuthServer(AuthServer):
493
    """An HTTP server requiring authentication"""
494
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
495
    def init_http_auth(self):
496
        self.auth_header_sent = 'WWW-Authenticate'
497
        self.auth_header_recv = 'Authorization'
498
        self.auth_error_code = 401
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
499
500
501
class ProxyAuthServer(AuthServer):
502
    """A proxy server requiring authentication"""
503
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
504
    def init_proxy_auth(self):
505
        self.proxy_requests = True
506
        self.auth_header_sent = 'Proxy-Authenticate'
507
        self.auth_header_recv = 'Proxy-Authorization'
508
        self.auth_error_code = 407
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
509
510
511
class HTTPBasicAuthServer(HTTPAuthServer):
512
    """An HTTP server requiring basic authentication"""
513
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
514
    def __init__(self, protocol_version=None):
515
        HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
516
                                protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
517
        self.init_http_auth()
518
519
520
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
521
    """An HTTP server requiring digest authentication"""
522
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
523
    def __init__(self, protocol_version=None):
524
        DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
525
                                  protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
526
        self.init_http_auth()
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
527
528
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
529
class HTTPBasicAndDigestAuthServer(DigestAuthServer, HTTPAuthServer):
530
    """An HTTP server requiring basic or digest authentication"""
531
532
    def __init__(self, protocol_version=None):
533
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
534
                                  'basicdigest',
535
                                  protocol_version=protocol_version)
536
        self.init_http_auth()
537
        # We really accept Digest only
538
        self.auth_scheme = 'digest'
539
540
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
541
class ProxyBasicAuthServer(ProxyAuthServer):
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
542
    """A proxy server requiring basic authentication"""
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
543
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
544
    def __init__(self, protocol_version=None):
545
        ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
546
                                 protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
547
        self.init_proxy_auth()
548
549
550
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
551
    """A proxy server requiring basic authentication"""
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
552
3111.1.18 by Vincent Ladeuil
Test parametrization for protocol versions achieved. Tests are failing :)
553
    def __init__(self, protocol_version=None):
554
        ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
555
                                 protocol_version=protocol_version)
2420.1.11 by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x.
556
        self.init_proxy_auth()
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
557
558
4307.4.2 by Vincent Ladeuil
Handle servers proposing several authentication schemes.
559
class ProxyBasicAndDigestAuthServer(DigestAuthServer, ProxyAuthServer):
560
    """An proxy server requiring basic or digest authentication"""
561
562
    def __init__(self, protocol_version=None):
563
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
564
                                  'basicdigest',
565
                                  protocol_version=protocol_version)
566
        self.init_proxy_auth()
567
        # We really accept Digest only
568
        self.auth_scheme = 'digest'
569
570