/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: Robert Collins
  • Date: 2010-05-05 00:05:29 UTC
  • mto: This revision was merged to the branch mainline in revision 5206.
  • Revision ID: robertc@robertcollins.net-20100505000529-ltmllyms5watqj5u
Make 'pydoc bzrlib.tests.build_tree_shape' useful.

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,
114
106
    one. This will currently fail if the primary transport is not
115
107
    backed by regular disk files.
116
108
    """
117
 
 
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.
121
 
    _protocol_version = None
122
 
    _url_protocol = 'http'
123
 
 
124
109
    def setUp(self):
125
110
        super(TestCaseWithWebserver, self).setUp()
126
111
        self.transport_readonly_server = http_server.HttpServer
127
112
 
128
 
    def create_transport_readonly_server(self):
129
 
        server = self.transport_readonly_server(
130
 
            protocol_version=self._protocol_version)
131
 
        server._url_protocol = self._url_protocol
132
 
        return server
133
 
 
134
113
 
135
114
class TestCaseWithTwoWebservers(TestCaseWithWebserver):
136
115
    """A support class providing readonly urls on two servers that are http://.
138
117
    We set up two webservers to allows various tests involving
139
118
    proxies or redirections from one server to the other.
140
119
    """
141
 
 
142
120
    def setUp(self):
143
121
        super(TestCaseWithTwoWebservers, self).setUp()
144
122
        self.transport_secondary_server = http_server.HttpServer
149
127
 
150
128
        This is mostly a hook for daughter classes.
151
129
        """
152
 
        server = self.transport_secondary_server(
153
 
            protocol_version=self._protocol_version)
154
 
        server._url_protocol = self._url_protocol
155
 
        return server
 
130
        return self.transport_secondary_server()
156
131
 
157
132
    def get_secondary_server(self):
158
133
        """Get the server instance for the secondary transport."""
161
136
            self.start_server(self.__secondary_server)
162
137
        return self.__secondary_server
163
138
 
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
139
 
174
140
class ProxyServer(http_server.HttpServer):
175
141
    """A proxy test server for http transports."""
193
159
                # We do not send a body
194
160
                self.send_header('Content-Length', '0')
195
161
                self.end_headers()
196
 
                return False  # The job is done
 
162
                return False # The job is done
197
163
            else:
198
164
                # We leave the parent class serve the request
199
165
                pass
218
184
    def redirect_to(self, host, port):
219
185
        """Redirect all requests to a specific host:port"""
220
186
        self.redirections = [('(.*)',
221
 
                              r'http://%s:%s\1' % (host, port),
 
187
                              r'http://%s:%s\1' % (host, port) ,
222
188
                              301)]
223
189
 
224
190
    def is_redirected(self, path):
232
198
        code = None
233
199
        target = None
234
200
        for (rsource, rtarget, rcode) in self.redirections:
235
 
            target, match = re.subn(rsource, rtarget, path, count=1)
 
201
            target, match = re.subn(rsource, rtarget, path)
236
202
            if match:
237
203
                code = rcode
238
 
                break  # The first match wins
 
204
                break # The first match wins
239
205
            else:
240
206
                target = None
241
207
        return code, target
242
208
 
243
209
 
244
210
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
 
211
   """A support class providing redirections from one server to another.
 
212
 
 
213
   We set up two webservers to allows various tests involving
 
214
   redirections.
 
215
   The 'old' server is redirected to the 'new' server.
 
216
   """
 
217
 
 
218
   def create_transport_secondary_server(self):
 
219
       """Create the secondary server redirecting to the primary server"""
 
220
       new = self.get_readonly_server()
 
221
       redirecting = HTTPServerRedirecting()
 
222
       redirecting.redirect_to(new.host, new.port)
 
223
       return redirecting
 
224
 
 
225
   def setUp(self):
 
226
       super(TestCaseWithRedirectedWebserver, self).setUp()
 
227
       # The redirections will point to the new server
 
228
       self.new_server = self.get_readonly_server()
 
229
       # The requests to the old server will be redirected
 
230
       self.old_server = self.get_secondary_server()
285
231
 
286
232
 
287
233
class AuthRequestHandler(http_server.TestingHTTPRequestHandler):
297
243
    # - auth_header_recv: the header received containing auth
298
244
    # - auth_error_code: the error code to indicate auth required
299
245
 
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
246
    def do_GET(self):
314
247
        if self.authorized():
315
248
            return http_server.TestingHTTPRequestHandler.do_GET(self)
316
249
        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()
 
250
            # Note that we must update test_case_server *before*
 
251
            # sending the error or the client may try to read it
 
252
            # before we have sent the whole error back.
 
253
            tcs = self.server.test_case_server
 
254
            tcs.auth_required_errors += 1
 
255
            self.send_response(tcs.auth_error_code)
 
256
            self.send_header_auth_reqed()
 
257
            # We do not send a body
 
258
            self.send_header('Content-Length', '0')
 
259
            self.end_headers()
 
260
            return
324
261
 
325
262
 
326
263
class BasicAuthRequestHandler(AuthRequestHandler):
335
272
        if auth_header:
336
273
            scheme, raw_auth = auth_header.split(' ', 1)
337
274
            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'))
 
275
                user, password = raw_auth.decode('base64').split(':')
 
276
                return tcs.authorized(user, password)
341
277
 
342
278
        return False
343
279
 
366
302
            return False
367
303
        scheme, auth = auth_header.split(None, 1)
368
304
        if scheme.lower() == tcs.auth_scheme:
369
 
            auth_dict = parse_keqv_list(parse_http_list(auth))
 
305
            auth_dict = urllib2.parse_keqv_list(urllib2.parse_http_list(auth))
370
306
 
371
307
            return tcs.digest_authorized(auth_dict, self.command)
372
308
 
377
313
        header = 'Digest realm="%s", ' % tcs.auth_realm
378
314
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
379
315
                                                              'MD5')
380
 
        self.send_header(tcs.auth_header_sent, header)
 
316
        self.send_header(tcs.auth_header_sent,header)
381
317
 
382
318
 
383
319
class DigestAndBasicAuthRequestHandler(DigestAuthRequestHandler):
395
331
        header = 'Digest realm="%s", ' % tcs.auth_realm
396
332
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
397
333
                                                              'MD5')
398
 
        self.send_header(tcs.auth_header_sent, header)
 
334
        self.send_header(tcs.auth_header_sent,header)
399
335
 
400
336
 
401
337
class AuthServer(http_server.HttpServer):
413
349
    auth_header_sent = None
414
350
    auth_header_recv = None
415
351
    auth_error_code = None
416
 
    auth_realm = u"Thou should not pass"
 
352
    auth_realm = "Thou should not pass"
417
353
 
418
354
    def __init__(self, request_handler, auth_scheme,
419
355
                 protocol_version=None):
460
396
        if realm != self.auth_realm:
461
397
            return False
462
398
        user = auth['username']
463
 
        if user not in self.password_of:
 
399
        if not self.password_of.has_key(user):
464
400
            return False
465
 
        algorithm = auth['algorithm']
 
401
        algorithm= auth['algorithm']
466
402
        if algorithm != 'MD5':
467
403
            return False
468
404
        qop = auth['qop']
473
409
 
474
410
        # Recalculate the response_digest to compare with the one
475
411
        # 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'))
 
412
        A1 = '%s:%s:%s' % (user, realm, password)
 
413
        A2 = '%s:%s' % (command, auth['uri'])
 
414
 
 
415
        H = lambda x: osutils.md5(x).hexdigest()
 
416
        KD = lambda secret, data: H("%s:%s" % (secret, data))
484
417
 
485
418
        nonce_count = int(auth['nc'], 16)
486
419
 
570
503
        self.init_proxy_auth()
571
504
        # We really accept Digest only
572
505
        self.auth_scheme = 'digest'
 
506
 
 
507