/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/http_utils.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
import base64
 
17
from cStringIO import StringIO
 
18
import errno
18
19
import re
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
 
        )
29
 
 
30
 
 
31
 
from .. import (
 
20
import socket
 
21
import threading
 
22
import time
 
23
import urllib2
 
24
import urlparse
 
25
 
 
26
 
 
27
from bzrlib import (
32
28
    errors,
33
29
    osutils,
34
30
    tests,
35
 
    transport,
36
 
    )
37
 
from ..sixish import (
38
 
    BytesIO,
39
 
    )
40
 
from ..bzr.smart import (
41
 
    medium,
42
 
    )
43
 
from . import http_server
44
 
from ..transport import chroot
 
31
    )
 
32
from bzrlib.smart import medium, protocol
 
33
from bzrlib.tests import http_server
 
34
from bzrlib.transport import (
 
35
    chroot,
 
36
    get_transport,
 
37
    )
45
38
 
46
39
 
47
40
class HTTPServerWithSmarts(http_server.HttpServer):
58
51
class SmartRequestHandler(http_server.TestingHTTPRequestHandler):
59
52
    """Extend TestingHTTPRequestHandler to support smart client POSTs.
60
53
 
61
 
    XXX: This duplicates a fair bit of the logic in breezy.transport.http.wsgi.
 
54
    XXX: This duplicates a fair bit of the logic in bzrlib.transport.http.wsgi.
62
55
    """
63
56
 
64
57
    def do_POST(self):
65
58
        """Hand the request off to a smart server instance."""
66
 
        backing = transport.get_transport_from_path(
67
 
            self.server.test_case_server._home_dir)
 
59
        backing = get_transport(self.server.test_case_server._home_dir)
68
60
        chroot_server = chroot.ChrootServer(backing)
69
61
        chroot_server.start_server()
70
62
        try:
71
 
            t = transport.get_transport_from_url(chroot_server.get_url())
 
63
            t = get_transport(chroot_server.get_url())
72
64
            self.do_POST_inner(t)
73
65
        finally:
74
66
            chroot_server.stop_server()
91
83
        # we have to stop early due to error, but we would also have to use the
92
84
        # HTTP trailer facility which may not be widely available.
93
85
        request_bytes = self.rfile.read(data_length)
94
 
        protocol_factory, unused_bytes = (
95
 
            medium._get_protocol_factory_for_bytes(request_bytes))
96
 
        out_buffer = BytesIO()
 
86
        protocol_factory, unused_bytes = medium._get_protocol_factory_for_bytes(
 
87
            request_bytes)
 
88
        out_buffer = StringIO()
97
89
        smart_protocol_request = protocol_factory(t, out_buffer.write, '/')
98
90
        # Perhaps there should be a SmartServerHTTPMedium that takes care of
99
91
        # feeding the bytes in the http request to the smart_protocol_request,
115
107
    backed by regular disk files.
116
108
    """
117
109
 
118
 
    # These attributes can be overriden or parametrized by daughter clasess if
119
 
    # needed, but must exist so that the create_transport_readonly_server()
120
 
    # method (or any method creating an http(s) server) can propagate it.
 
110
    # This can be overriden or parametrized by daughter clasess if needed, but
 
111
    # it must exist so that the create_transport_readonly_server() method can
 
112
    # propagate it.
121
113
    _protocol_version = None
122
 
    _url_protocol = 'http'
123
114
 
124
115
    def setUp(self):
125
116
        super(TestCaseWithWebserver, self).setUp()
126
117
        self.transport_readonly_server = http_server.HttpServer
127
118
 
128
119
    def create_transport_readonly_server(self):
129
 
        server = self.transport_readonly_server(
 
120
        return self.transport_readonly_server(
130
121
            protocol_version=self._protocol_version)
131
 
        server._url_protocol = self._url_protocol
132
 
        return server
133
122
 
134
123
 
135
124
class TestCaseWithTwoWebservers(TestCaseWithWebserver):
138
127
    We set up two webservers to allows various tests involving
139
128
    proxies or redirections from one server to the other.
140
129
    """
141
 
 
142
130
    def setUp(self):
143
131
        super(TestCaseWithTwoWebservers, self).setUp()
144
132
        self.transport_secondary_server = http_server.HttpServer
149
137
 
150
138
        This is mostly a hook for daughter classes.
151
139
        """
152
 
        server = self.transport_secondary_server(
 
140
        return self.transport_secondary_server(
153
141
            protocol_version=self._protocol_version)
154
 
        server._url_protocol = self._url_protocol
155
 
        return server
156
142
 
157
143
    def get_secondary_server(self):
158
144
        """Get the server instance for the secondary transport."""
161
147
            self.start_server(self.__secondary_server)
162
148
        return self.__secondary_server
163
149
 
164
 
    def get_secondary_url(self, relpath=None):
165
 
        base = self.get_secondary_server().get_url()
166
 
        return self._adjust_url(base, relpath)
167
 
 
168
 
    def get_secondary_transport(self, relpath=None):
169
 
        t = transport.get_transport_from_url(self.get_secondary_url(relpath))
170
 
        self.assertTrue(t.is_readonly())
171
 
        return t
172
 
 
173
150
 
174
151
class ProxyServer(http_server.HttpServer):
175
152
    """A proxy test server for http transports."""
193
170
                # We do not send a body
194
171
                self.send_header('Content-Length', '0')
195
172
                self.end_headers()
196
 
                return False  # The job is done
 
173
                return False # The job is done
197
174
            else:
198
175
                # We leave the parent class serve the request
199
176
                pass
218
195
    def redirect_to(self, host, port):
219
196
        """Redirect all requests to a specific host:port"""
220
197
        self.redirections = [('(.*)',
221
 
                              r'http://%s:%s\1' % (host, port),
 
198
                              r'http://%s:%s\1' % (host, port) ,
222
199
                              301)]
223
200
 
224
201
    def is_redirected(self, path):
232
209
        code = None
233
210
        target = None
234
211
        for (rsource, rtarget, rcode) in self.redirections:
235
 
            target, match = re.subn(rsource, rtarget, path, count=1)
 
212
            target, match = re.subn(rsource, rtarget, path)
236
213
            if match:
237
214
                code = rcode
238
 
                break  # The first match wins
 
215
                break # The first match wins
239
216
            else:
240
217
                target = None
241
218
        return code, target
242
219
 
243
220
 
244
221
class TestCaseWithRedirectedWebserver(TestCaseWithTwoWebservers):
245
 
    """A support class providing redirections from one server to another.
246
 
 
247
 
    We set up two webservers to allows various tests involving
248
 
    redirections.
249
 
    The 'old' server is redirected to the 'new' server.
250
 
    """
251
 
 
252
 
    def setUp(self):
253
 
        super(TestCaseWithRedirectedWebserver, self).setUp()
254
 
        # The redirections will point to the new server
255
 
        self.new_server = self.get_readonly_server()
256
 
        # The requests to the old server will be redirected to the new server
257
 
        self.old_server = self.get_secondary_server()
258
 
 
259
 
    def create_transport_secondary_server(self):
260
 
        """Create the secondary server redirecting to the primary server"""
261
 
        new = self.get_readonly_server()
262
 
        redirecting = HTTPServerRedirecting(
263
 
            protocol_version=self._protocol_version)
264
 
        redirecting.redirect_to(new.host, new.port)
265
 
        redirecting._url_protocol = self._url_protocol
266
 
        return redirecting
267
 
 
268
 
    def get_old_url(self, relpath=None):
269
 
        base = self.old_server.get_url()
270
 
        return self._adjust_url(base, relpath)
271
 
 
272
 
    def get_old_transport(self, relpath=None):
273
 
        t = transport.get_transport_from_url(self.get_old_url(relpath))
274
 
        self.assertTrue(t.is_readonly())
275
 
        return t
276
 
 
277
 
    def get_new_url(self, relpath=None):
278
 
        base = self.new_server.get_url()
279
 
        return self._adjust_url(base, relpath)
280
 
 
281
 
    def get_new_transport(self, relpath=None):
282
 
        t = transport.get_transport_from_url(self.get_new_url(relpath))
283
 
        self.assertTrue(t.is_readonly())
284
 
        return t
 
222
   """A support class providing redirections from one server to another.
 
223
 
 
224
   We set up two webservers to allows various tests involving
 
225
   redirections.
 
226
   The 'old' server is redirected to the 'new' server.
 
227
   """
 
228
 
 
229
   def create_transport_secondary_server(self):
 
230
       """Create the secondary server redirecting to the primary server"""
 
231
       new = self.get_readonly_server()
 
232
       redirecting = HTTPServerRedirecting(
 
233
           protocol_version=self._protocol_version)
 
234
       redirecting.redirect_to(new.host, new.port)
 
235
       return redirecting
 
236
 
 
237
   def setUp(self):
 
238
       super(TestCaseWithRedirectedWebserver, self).setUp()
 
239
       # The redirections will point to the new server
 
240
       self.new_server = self.get_readonly_server()
 
241
       # The requests to the old server will be redirected
 
242
       self.old_server = self.get_secondary_server()
285
243
 
286
244
 
287
245
class AuthRequestHandler(http_server.TestingHTTPRequestHandler):
297
255
    # - auth_header_recv: the header received containing auth
298
256
    # - auth_error_code: the error code to indicate auth required
299
257
 
300
 
    def _require_authentication(self):
301
 
        # Note that we must update test_case_server *before*
302
 
        # sending the error or the client may try to read it
303
 
        # before we have sent the whole error back.
304
 
        tcs = self.server.test_case_server
305
 
        tcs.auth_required_errors += 1
306
 
        self.send_response(tcs.auth_error_code)
307
 
        self.send_header_auth_reqed()
308
 
        # We do not send a body
309
 
        self.send_header('Content-Length', '0')
310
 
        self.end_headers()
311
 
        return
312
 
 
313
258
    def do_GET(self):
314
259
        if self.authorized():
315
260
            return http_server.TestingHTTPRequestHandler.do_GET(self)
316
261
        else:
317
 
            return self._require_authentication()
318
 
 
319
 
    def do_HEAD(self):
320
 
        if self.authorized():
321
 
            return http_server.TestingHTTPRequestHandler.do_HEAD(self)
322
 
        else:
323
 
            return self._require_authentication()
 
262
            # Note that we must update test_case_server *before*
 
263
            # sending the error or the client may try to read it
 
264
            # before we have sent the whole error back.
 
265
            tcs = self.server.test_case_server
 
266
            tcs.auth_required_errors += 1
 
267
            self.send_response(tcs.auth_error_code)
 
268
            self.send_header_auth_reqed()
 
269
            # We do not send a body
 
270
            self.send_header('Content-Length', '0')
 
271
            self.end_headers()
 
272
            return
324
273
 
325
274
 
326
275
class BasicAuthRequestHandler(AuthRequestHandler):
335
284
        if auth_header:
336
285
            scheme, raw_auth = auth_header.split(' ', 1)
337
286
            if scheme.lower() == tcs.auth_scheme:
338
 
                user, password = base64.b64decode(raw_auth).split(b':')
339
 
                return tcs.authorized(user.decode('ascii'),
340
 
                                      password.decode('ascii'))
 
287
                user, password = raw_auth.decode('base64').split(':')
 
288
                return tcs.authorized(user, password)
341
289
 
342
290
        return False
343
291
 
366
314
            return False
367
315
        scheme, auth = auth_header.split(None, 1)
368
316
        if scheme.lower() == tcs.auth_scheme:
369
 
            auth_dict = parse_keqv_list(parse_http_list(auth))
 
317
            auth_dict = urllib2.parse_keqv_list(urllib2.parse_http_list(auth))
370
318
 
371
319
            return tcs.digest_authorized(auth_dict, self.command)
372
320
 
377
325
        header = 'Digest realm="%s", ' % tcs.auth_realm
378
326
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
379
327
                                                              'MD5')
380
 
        self.send_header(tcs.auth_header_sent, header)
 
328
        self.send_header(tcs.auth_header_sent,header)
381
329
 
382
330
 
383
331
class DigestAndBasicAuthRequestHandler(DigestAuthRequestHandler):
395
343
        header = 'Digest realm="%s", ' % tcs.auth_realm
396
344
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
397
345
                                                              'MD5')
398
 
        self.send_header(tcs.auth_header_sent, header)
 
346
        self.send_header(tcs.auth_header_sent,header)
399
347
 
400
348
 
401
349
class AuthServer(http_server.HttpServer):
413
361
    auth_header_sent = None
414
362
    auth_header_recv = None
415
363
    auth_error_code = None
416
 
    auth_realm = u"Thou should not pass"
 
364
    auth_realm = "Thou should not pass"
417
365
 
418
366
    def __init__(self, request_handler, auth_scheme,
419
367
                 protocol_version=None):
460
408
        if realm != self.auth_realm:
461
409
            return False
462
410
        user = auth['username']
463
 
        if user not in self.password_of:
 
411
        if not self.password_of.has_key(user):
464
412
            return False
465
 
        algorithm = auth['algorithm']
 
413
        algorithm= auth['algorithm']
466
414
        if algorithm != 'MD5':
467
415
            return False
468
416
        qop = auth['qop']
473
421
 
474
422
        # Recalculate the response_digest to compare with the one
475
423
        # sent by the client
476
 
        A1 = ('%s:%s:%s' % (user, realm, password)).encode('utf-8')
477
 
        A2 = ('%s:%s' % (command, auth['uri'])).encode('utf-8')
478
 
 
479
 
        def H(x):
480
 
            return osutils.md5(x).hexdigest()
481
 
 
482
 
        def KD(secret, data):
483
 
            return H(("%s:%s" % (secret, data)).encode('utf-8'))
 
424
        A1 = '%s:%s:%s' % (user, realm, password)
 
425
        A2 = '%s:%s' % (command, auth['uri'])
 
426
 
 
427
        H = lambda x: osutils.md5(x).hexdigest()
 
428
        KD = lambda secret, data: H("%s:%s" % (secret, data))
484
429
 
485
430
        nonce_count = int(auth['nc'], 16)
486
431
 
570
515
        self.init_proxy_auth()
571
516
        # We really accept Digest only
572
517
        self.auth_scheme = 'digest'
 
518
 
 
519