~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/HTTPTestUtil.py

  • Committer: Ian Clatworthy
  • Date: 2007-08-13 14:33:10 UTC
  • mto: (2733.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2734.
  • Revision ID: ian.clatworthy@internode.on.net-20070813143310-twhj4la0qnupvze8
Added Quick Start Summary

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
from cStringIO import StringIO
18
18
import errno
 
19
import md5
 
20
from SimpleHTTPServer import SimpleHTTPRequestHandler
19
21
import re
 
22
import sha
20
23
import socket
21
 
import threading
22
24
import time
23
25
import urllib2
24
26
import urlparse
25
27
 
26
 
 
27
 
from bzrlib import (
28
 
    errors,
29
 
    osutils,
30
 
    tests,
31
 
    transport,
32
 
    )
33
 
from bzrlib.smart import (
34
 
    medium,
35
 
    protocol,
36
 
    )
37
 
from bzrlib.tests import http_server
38
 
from bzrlib.transport import chroot
39
 
 
40
 
 
41
 
class HTTPServerWithSmarts(http_server.HttpServer):
 
28
from bzrlib.smart import protocol
 
29
from bzrlib.tests import TestCaseWithTransport
 
30
from bzrlib.tests.HttpServer import (
 
31
    HttpServer,
 
32
    TestingHTTPRequestHandler,
 
33
    )
 
34
from bzrlib.transport import (
 
35
    get_transport,
 
36
    )
 
37
 
 
38
 
 
39
class WallRequestHandler(TestingHTTPRequestHandler):
 
40
    """Whatever request comes in, close the connection"""
 
41
 
 
42
    def handle_one_request(self):
 
43
        """Handle a single HTTP request, by abruptly closing the connection"""
 
44
        self.close_connection = 1
 
45
 
 
46
 
 
47
class BadStatusRequestHandler(TestingHTTPRequestHandler):
 
48
    """Whatever request comes in, returns a bad status"""
 
49
 
 
50
    def parse_request(self):
 
51
        """Fakes handling a single HTTP request, returns a bad status"""
 
52
        ignored = TestingHTTPRequestHandler.parse_request(self)
 
53
        try:
 
54
            self.send_response(0, "Bad status")
 
55
            self.end_headers()
 
56
        except socket.error, e:
 
57
            # We don't want to pollute the test results with
 
58
            # spurious server errors while test succeed. In our
 
59
            # case, it may occur that the test has already read
 
60
            # the 'Bad Status' and closed the socket while we are
 
61
            # still trying to send some headers... So the test is
 
62
            # ok, but if we raise the exception, the output is
 
63
            # dirty. So we don't raise, but we close the
 
64
            # connection, just to be safe :)
 
65
            spurious = [errno.EPIPE,
 
66
                        errno.ECONNRESET,
 
67
                        errno.ECONNABORTED,
 
68
                        ]
 
69
            if (len(e.args) > 0) and (e.args[0] in spurious):
 
70
                self.close_connection = 1
 
71
                pass
 
72
            else:
 
73
                raise
 
74
        return False
 
75
 
 
76
 
 
77
class InvalidStatusRequestHandler(TestingHTTPRequestHandler):
 
78
    """Whatever request comes in, returns am invalid status"""
 
79
 
 
80
    def parse_request(self):
 
81
        """Fakes handling a single HTTP request, returns a bad status"""
 
82
        ignored = TestingHTTPRequestHandler.parse_request(self)
 
83
        self.wfile.write("Invalid status line\r\n")
 
84
        return False
 
85
 
 
86
 
 
87
class BadProtocolRequestHandler(TestingHTTPRequestHandler):
 
88
    """Whatever request comes in, returns a bad protocol version"""
 
89
 
 
90
    def parse_request(self):
 
91
        """Fakes handling a single HTTP request, returns a bad status"""
 
92
        ignored = TestingHTTPRequestHandler.parse_request(self)
 
93
        # Returns an invalid protocol version, but curl just
 
94
        # ignores it and those cannot be tested.
 
95
        self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
 
96
                                           404,
 
97
                                           'Look at my protocol version'))
 
98
        return False
 
99
 
 
100
 
 
101
class ForbiddenRequestHandler(TestingHTTPRequestHandler):
 
102
    """Whatever request comes in, returns a 403 code"""
 
103
 
 
104
    def parse_request(self):
 
105
        """Handle a single HTTP request, by replying we cannot handle it"""
 
106
        ignored = TestingHTTPRequestHandler.parse_request(self)
 
107
        self.send_error(403)
 
108
        return False
 
109
 
 
110
 
 
111
class HTTPServerWithSmarts(HttpServer):
42
112
    """HTTPServerWithSmarts extends the HttpServer with POST methods that will
43
113
    trigger a smart server to execute with a transport rooted at the rootdir of
44
114
    the HTTP server.
45
115
    """
46
116
 
47
 
    def __init__(self, protocol_version=None):
48
 
        http_server.HttpServer.__init__(self, SmartRequestHandler,
49
 
                                        protocol_version=protocol_version)
50
 
 
51
 
 
52
 
class SmartRequestHandler(http_server.TestingHTTPRequestHandler):
53
 
    """Extend TestingHTTPRequestHandler to support smart client POSTs.
54
 
 
55
 
    XXX: This duplicates a fair bit of the logic in bzrlib.transport.http.wsgi.
56
 
    """
 
117
    def __init__(self):
 
118
        HttpServer.__init__(self, SmartRequestHandler)
 
119
 
 
120
 
 
121
class SmartRequestHandler(TestingHTTPRequestHandler):
 
122
    """Extend TestingHTTPRequestHandler to support smart client POSTs."""
57
123
 
58
124
    def do_POST(self):
59
125
        """Hand the request off to a smart server instance."""
60
 
        backing = transport.get_transport(
61
 
            self.server.test_case_server._home_dir)
62
 
        chroot_server = chroot.ChrootServer(backing)
63
 
        chroot_server.start_server()
64
 
        try:
65
 
            t = transport.get_transport(chroot_server.get_url())
66
 
            self.do_POST_inner(t)
67
 
        finally:
68
 
            chroot_server.stop_server()
69
 
 
70
 
    def do_POST_inner(self, chrooted_transport):
71
126
        self.send_response(200)
72
127
        self.send_header("Content-type", "application/octet-stream")
73
 
        if not self.path.endswith('.bzr/smart'):
74
 
            raise AssertionError(
75
 
                'POST to path not ending in .bzr/smart: %r' % (self.path,))
76
 
        t = chrooted_transport.clone(self.path[:-len('.bzr/smart')])
77
 
        # if this fails, we should return 400 bad request, but failure is
78
 
        # failure for now - RBC 20060919
79
 
        data_length = int(self.headers['Content-Length'])
 
128
        transport = get_transport(self.server.test_case_server._home_dir)
80
129
        # TODO: We might like to support streaming responses.  1.0 allows no
81
130
        # Content-length in this case, so for integrity we should perform our
82
131
        # own chunking within the stream.
84
133
        # the HTTP chunking as this will allow HTTP persistence safely, even if
85
134
        # we have to stop early due to error, but we would also have to use the
86
135
        # HTTP trailer facility which may not be widely available.
87
 
        request_bytes = self.rfile.read(data_length)
88
 
        protocol_factory, unused_bytes = medium._get_protocol_factory_for_bytes(
89
 
            request_bytes)
90
136
        out_buffer = StringIO()
91
 
        smart_protocol_request = protocol_factory(t, out_buffer.write, '/')
 
137
        smart_protocol_request = protocol.SmartServerRequestProtocolOne(
 
138
                transport, out_buffer.write)
 
139
        # if this fails, we should return 400 bad request, but failure is
 
140
        # failure for now - RBC 20060919
 
141
        data_length = int(self.headers['Content-Length'])
92
142
        # Perhaps there should be a SmartServerHTTPMedium that takes care of
93
143
        # feeding the bytes in the http request to the smart_protocol_request,
94
144
        # but for now it's simpler to just feed the bytes directly.
95
 
        smart_protocol_request.accept_bytes(unused_bytes)
96
 
        if not (smart_protocol_request.next_read_size() == 0):
97
 
            raise errors.SmartProtocolError(
98
 
                "not finished reading, but all data sent to protocol.")
 
145
        smart_protocol_request.accept_bytes(self.rfile.read(data_length))
 
146
        assert smart_protocol_request.next_read_size() == 0, (
 
147
            "not finished reading, but all data sent to protocol.")
99
148
        self.send_header("Content-Length", str(len(out_buffer.getvalue())))
100
149
        self.end_headers()
101
150
        self.wfile.write(out_buffer.getvalue())
102
151
 
103
152
 
104
 
class TestCaseWithWebserver(tests.TestCaseWithTransport):
 
153
class LimitedRangeRequestHandler(TestingHTTPRequestHandler):
 
154
    """Errors out when range specifiers exceed the limit"""
 
155
 
 
156
    def get_multiple_ranges(self, file, file_size, ranges):
 
157
        """Refuses the multiple ranges request"""
 
158
        tcs = self.server.test_case_server
 
159
        if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
 
160
            file.close()
 
161
            # Emulate apache behavior
 
162
            self.send_error(400, "Bad Request")
 
163
            return
 
164
        return TestingHTTPRequestHandler.get_multiple_ranges(self, file,
 
165
                                                             file_size, ranges)
 
166
 
 
167
    def do_GET(self):
 
168
        tcs = self.server.test_case_server
 
169
        tcs.GET_request_nb += 1
 
170
        return TestingHTTPRequestHandler.do_GET(self)
 
171
 
 
172
 
 
173
class LimitedRangeHTTPServer(HttpServer):
 
174
    """An HttpServer erroring out on requests with too much range specifiers"""
 
175
 
 
176
    def __init__(self, request_handler=LimitedRangeRequestHandler,
 
177
                 range_limit=None):
 
178
        HttpServer.__init__(self, request_handler)
 
179
        self.range_limit = range_limit
 
180
        self.GET_request_nb = 0
 
181
 
 
182
 
 
183
class SingleRangeRequestHandler(TestingHTTPRequestHandler):
 
184
    """Always reply to range request as if they were single.
 
185
 
 
186
    Don't be explicit about it, just to annoy the clients.
 
187
    """
 
188
 
 
189
    def get_multiple_ranges(self, file, file_size, ranges):
 
190
        """Answer as if it was a single range request and ignores the rest"""
 
191
        (start, end) = ranges[0]
 
192
        return self.get_single_range(file, file_size, start, end)
 
193
 
 
194
 
 
195
class SingleOnlyRangeRequestHandler(TestingHTTPRequestHandler):
 
196
    """Only reply to simple range requests, errors out on multiple"""
 
197
 
 
198
    def get_multiple_ranges(self, file, file_size, ranges):
 
199
        """Refuses the multiple ranges request"""
 
200
        if len(ranges) > 1:
 
201
            file.close()
 
202
            self.send_error(416, "Requested range not satisfiable")
 
203
            return
 
204
        (start, end) = ranges[0]
 
205
        return self.get_single_range(file, file_size, start, end)
 
206
 
 
207
 
 
208
class NoRangeRequestHandler(TestingHTTPRequestHandler):
 
209
    """Ignore range requests without notice"""
 
210
 
 
211
    # Just bypass the range handling done by TestingHTTPRequestHandler
 
212
    do_GET = SimpleHTTPRequestHandler.do_GET
 
213
 
 
214
 
 
215
class TestCaseWithWebserver(TestCaseWithTransport):
105
216
    """A support class that provides readonly urls that are http://.
106
217
 
107
218
    This is done by forcing the readonly server to be an http
108
219
    one. This will currently fail if the primary transport is not
109
220
    backed by regular disk files.
110
221
    """
111
 
 
112
 
    # These attributes can be overriden or parametrized by daughter clasess if
113
 
    # needed, but must exist so that the create_transport_readonly_server()
114
 
    # method (or any method creating an http(s) server) can propagate it.
115
 
    _protocol_version = None
116
 
    _url_protocol = 'http'
117
 
 
118
222
    def setUp(self):
119
223
        super(TestCaseWithWebserver, self).setUp()
120
 
        self.transport_readonly_server = http_server.HttpServer
121
 
 
122
 
    def create_transport_readonly_server(self):
123
 
        server = self.transport_readonly_server(
124
 
            protocol_version=self._protocol_version)
125
 
        server._url_protocol = self._url_protocol
126
 
        return server
 
224
        self.transport_readonly_server = HttpServer
127
225
 
128
226
 
129
227
class TestCaseWithTwoWebservers(TestCaseWithWebserver):
134
232
    """
135
233
    def setUp(self):
136
234
        super(TestCaseWithTwoWebservers, self).setUp()
137
 
        self.transport_secondary_server = http_server.HttpServer
 
235
        self.transport_secondary_server = HttpServer
138
236
        self.__secondary_server = None
139
237
 
140
238
    def create_transport_secondary_server(self):
142
240
 
143
241
        This is mostly a hook for daughter classes.
144
242
        """
145
 
        server = self.transport_secondary_server(
146
 
            protocol_version=self._protocol_version)
147
 
        server._url_protocol = self._url_protocol
148
 
        return server
 
243
        return self.transport_secondary_server()
149
244
 
150
245
    def get_secondary_server(self):
151
246
        """Get the server instance for the secondary transport."""
152
247
        if self.__secondary_server is None:
153
248
            self.__secondary_server = self.create_transport_secondary_server()
154
 
            self.start_server(self.__secondary_server)
 
249
            self.__secondary_server.setUp()
 
250
            self.addCleanup(self.__secondary_server.tearDown)
155
251
        return self.__secondary_server
156
252
 
157
 
    def get_secondary_url(self, relpath=None):
158
 
        base = self.get_secondary_server().get_url()
159
 
        return self._adjust_url(base, relpath)
160
 
 
161
 
    def get_secondary_transport(self, relpath=None):
162
 
        t = transport.get_transport(self.get_secondary_url(relpath))
163
 
        self.assertTrue(t.is_readonly())
164
 
        return t
165
 
 
166
 
 
167
 
class ProxyServer(http_server.HttpServer):
 
253
 
 
254
class ProxyServer(HttpServer):
168
255
    """A proxy test server for http transports."""
169
256
 
170
257
    proxy_requests = True
171
258
 
172
259
 
173
 
class RedirectRequestHandler(http_server.TestingHTTPRequestHandler):
 
260
class RedirectRequestHandler(TestingHTTPRequestHandler):
174
261
    """Redirect all request to the specified server"""
175
262
 
176
263
    def parse_request(self):
177
264
        """Redirect a single HTTP request to another host"""
178
 
        valid = http_server.TestingHTTPRequestHandler.parse_request(self)
 
265
        valid = TestingHTTPRequestHandler.parse_request(self)
179
266
        if valid:
180
267
            tcs = self.server.test_case_server
181
268
            code, target = tcs.is_redirected(self.path)
183
270
                # Redirect as instructed
184
271
                self.send_response(code)
185
272
                self.send_header('Location', target)
186
 
                # We do not send a body
187
 
                self.send_header('Content-Length', '0')
188
273
                self.end_headers()
189
274
                return False # The job is done
190
275
            else:
193
278
        return valid
194
279
 
195
280
 
196
 
class HTTPServerRedirecting(http_server.HttpServer):
 
281
class HTTPServerRedirecting(HttpServer):
197
282
    """An HttpServer redirecting to another server """
198
283
 
199
 
    def __init__(self, request_handler=RedirectRequestHandler,
200
 
                 protocol_version=None):
201
 
        http_server.HttpServer.__init__(self, request_handler,
202
 
                                        protocol_version=protocol_version)
 
284
    def __init__(self, request_handler=RedirectRequestHandler):
 
285
        HttpServer.__init__(self, request_handler)
203
286
        # redirections is a list of tuples (source, target, code)
204
287
        # - source is a regexp for the paths requested
205
288
        # - target is a replacement for re.sub describing where
242
325
   The 'old' server is redirected to the 'new' server.
243
326
   """
244
327
 
 
328
   def create_transport_secondary_server(self):
 
329
       """Create the secondary server redirecting to the primary server"""
 
330
       new = self.get_readonly_server()
 
331
       redirecting = HTTPServerRedirecting()
 
332
       redirecting.redirect_to(new.host, new.port)
 
333
       return redirecting
 
334
 
245
335
   def setUp(self):
246
336
       super(TestCaseWithRedirectedWebserver, self).setUp()
247
337
       # The redirections will point to the new server
248
338
       self.new_server = self.get_readonly_server()
249
 
       # The requests to the old server will be redirected to the new server
 
339
       # The requests to the old server will be redirected
250
340
       self.old_server = self.get_secondary_server()
251
341
 
252
 
   def create_transport_secondary_server(self):
253
 
       """Create the secondary server redirecting to the primary server"""
254
 
       new = self.get_readonly_server()
255
 
       redirecting = HTTPServerRedirecting(
256
 
           protocol_version=self._protocol_version)
257
 
       redirecting.redirect_to(new.host, new.port)
258
 
       redirecting._url_protocol = self._url_protocol
259
 
       return redirecting
260
 
 
261
 
   def get_old_url(self, relpath=None):
262
 
        base = self.old_server.get_url()
263
 
        return self._adjust_url(base, relpath)
264
 
 
265
 
   def get_old_transport(self, relpath=None):
266
 
        t = transport.get_transport(self.get_old_url(relpath))
267
 
        self.assertTrue(t.is_readonly())
268
 
        return t
269
 
 
270
 
   def get_new_url(self, relpath=None):
271
 
        base = self.new_server.get_url()
272
 
        return self._adjust_url(base, relpath)
273
 
 
274
 
   def get_new_transport(self, relpath=None):
275
 
        t = transport.get_transport(self.get_new_url(relpath))
276
 
        self.assertTrue(t.is_readonly())
277
 
        return t
278
 
 
279
 
 
280
 
class AuthRequestHandler(http_server.TestingHTTPRequestHandler):
 
342
 
 
343
class AuthRequestHandler(TestingHTTPRequestHandler):
281
344
    """Requires an authentication to process requests.
282
345
 
283
346
    This is intended to be used with a server that always and
292
355
 
293
356
    def do_GET(self):
294
357
        if self.authorized():
295
 
            return http_server.TestingHTTPRequestHandler.do_GET(self)
 
358
            return TestingHTTPRequestHandler.do_GET(self)
296
359
        else:
297
360
            # Note that we must update test_case_server *before*
298
361
            # sending the error or the client may try to read it
301
364
            tcs.auth_required_errors += 1
302
365
            self.send_response(tcs.auth_error_code)
303
366
            self.send_header_auth_reqed()
304
 
            # We do not send a body
305
 
            self.send_header('Content-Length', '0')
306
367
            self.end_headers()
307
368
            return
308
369
 
343
404
 
344
405
    def authorized(self):
345
406
        tcs = self.server.test_case_server
 
407
        if tcs.auth_scheme != 'digest':
 
408
            return False
346
409
 
347
410
        auth_header = self.headers.get(tcs.auth_header_recv, None)
348
411
        if auth_header is None:
363
426
        self.send_header(tcs.auth_header_sent,header)
364
427
 
365
428
 
366
 
class DigestAndBasicAuthRequestHandler(DigestAuthRequestHandler):
367
 
    """Implements a digest and basic authentication of a request.
368
 
 
369
 
    I.e. the server proposes both schemes and the client should choose the best
370
 
    one it can handle, which, in that case, should be digest, the only scheme
371
 
    accepted here.
372
 
    """
373
 
 
374
 
    def send_header_auth_reqed(self):
375
 
        tcs = self.server.test_case_server
376
 
        self.send_header(tcs.auth_header_sent,
377
 
                         'Basic realm="%s"' % tcs.auth_realm)
378
 
        header = 'Digest realm="%s", ' % tcs.auth_realm
379
 
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
380
 
                                                              'MD5')
381
 
        self.send_header(tcs.auth_header_sent,header)
382
 
 
383
 
 
384
 
class AuthServer(http_server.HttpServer):
 
429
class AuthServer(HttpServer):
385
430
    """Extends HttpServer with a dictionary of passwords.
386
431
 
387
432
    This is used as a base class for various schemes which should
398
443
    auth_error_code = None
399
444
    auth_realm = "Thou should not pass"
400
445
 
401
 
    def __init__(self, request_handler, auth_scheme,
402
 
                 protocol_version=None):
403
 
        http_server.HttpServer.__init__(self, request_handler,
404
 
                                        protocol_version=protocol_version)
 
446
    def __init__(self, request_handler, auth_scheme):
 
447
        HttpServer.__init__(self, request_handler)
405
448
        self.auth_scheme = auth_scheme
406
449
        self.password_of = {}
407
450
        self.auth_required_errors = 0
421
464
 
422
465
 
423
466
# FIXME: There is some code duplication with
424
 
# _urllib2_wrappers.py.DigestAuthHandler. If that duplication
 
467
# _urllib2_wrappers.py.DigestAuthHandler. If that duplciation
425
468
# grows, it may require a refactoring. Also, we don't implement
426
469
# SHA algorithm nor MD5-sess here, but that does not seem worth
427
470
# it.
430
473
 
431
474
    auth_nonce = 'now!'
432
475
 
433
 
    def __init__(self, request_handler, auth_scheme,
434
 
                 protocol_version=None):
435
 
        AuthServer.__init__(self, request_handler, auth_scheme,
436
 
                            protocol_version=protocol_version)
 
476
    def __init__(self, request_handler, auth_scheme):
 
477
        AuthServer.__init__(self, request_handler, auth_scheme)
437
478
 
438
479
    def digest_authorized(self, auth, command):
439
480
        nonce = auth['nonce']
459
500
        A1 = '%s:%s:%s' % (user, realm, password)
460
501
        A2 = '%s:%s' % (command, auth['uri'])
461
502
 
462
 
        H = lambda x: osutils.md5(x).hexdigest()
 
503
        H = lambda x: md5.new(x).hexdigest()
463
504
        KD = lambda secret, data: H("%s:%s" % (secret, data))
464
505
 
465
506
        nonce_count = int(auth['nc'], 16)
472
513
 
473
514
        return response_digest == auth['response']
474
515
 
475
 
 
476
516
class HTTPAuthServer(AuthServer):
477
517
    """An HTTP server requiring authentication"""
478
518
 
495
535
class HTTPBasicAuthServer(HTTPAuthServer):
496
536
    """An HTTP server requiring basic authentication"""
497
537
 
498
 
    def __init__(self, protocol_version=None):
499
 
        HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
500
 
                                protocol_version=protocol_version)
 
538
    def __init__(self):
 
539
        HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
501
540
        self.init_http_auth()
502
541
 
503
542
 
504
543
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
505
544
    """An HTTP server requiring digest authentication"""
506
545
 
507
 
    def __init__(self, protocol_version=None):
508
 
        DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
509
 
                                  protocol_version=protocol_version)
510
 
        self.init_http_auth()
511
 
 
512
 
 
513
 
class HTTPBasicAndDigestAuthServer(DigestAuthServer, HTTPAuthServer):
514
 
    """An HTTP server requiring basic or digest authentication"""
515
 
 
516
 
    def __init__(self, protocol_version=None):
517
 
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
518
 
                                  'basicdigest',
519
 
                                  protocol_version=protocol_version)
520
 
        self.init_http_auth()
521
 
        # We really accept Digest only
522
 
        self.auth_scheme = 'digest'
 
546
    def __init__(self):
 
547
        DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
 
548
        self.init_http_auth()
523
549
 
524
550
 
525
551
class ProxyBasicAuthServer(ProxyAuthServer):
526
552
    """A proxy server requiring basic authentication"""
527
553
 
528
 
    def __init__(self, protocol_version=None):
529
 
        ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic',
530
 
                                 protocol_version=protocol_version)
 
554
    def __init__(self):
 
555
        ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
531
556
        self.init_proxy_auth()
532
557
 
533
558
 
534
559
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
535
560
    """A proxy server requiring basic authentication"""
536
561
 
537
 
    def __init__(self, protocol_version=None):
538
 
        ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest',
539
 
                                 protocol_version=protocol_version)
540
 
        self.init_proxy_auth()
541
 
 
542
 
 
543
 
class ProxyBasicAndDigestAuthServer(DigestAuthServer, ProxyAuthServer):
544
 
    """An proxy server requiring basic or digest authentication"""
545
 
 
546
 
    def __init__(self, protocol_version=None):
547
 
        DigestAuthServer.__init__(self, DigestAndBasicAuthRequestHandler,
548
 
                                  'basicdigest',
549
 
                                  protocol_version=protocol_version)
550
 
        self.init_proxy_auth()
551
 
        # We really accept Digest only
552
 
        self.auth_scheme = 'digest'
 
562
    def __init__(self):
 
563
        ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
 
564
        self.init_proxy_auth()
553
565
 
554
566