/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 breezy/tests/http_utils.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
 
from cStringIO import StringIO
18
 
import errno
 
17
import base64
19
18
import re
20
 
import socket
21
 
import threading
22
 
import time
23
 
import urllib2
24
 
import urlparse
25
 
 
26
 
 
27
 
from bzrlib import (
 
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 (
28
32
    errors,
29
33
    osutils,
30
34
    tests,
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
 
    )
 
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
38
45
 
39
46
 
40
47
class HTTPServerWithSmarts(http_server.HttpServer):
51
58
class SmartRequestHandler(http_server.TestingHTTPRequestHandler):
52
59
    """Extend TestingHTTPRequestHandler to support smart client POSTs.
53
60
 
54
 
    XXX: This duplicates a fair bit of the logic in bzrlib.transport.http.wsgi.
 
61
    XXX: This duplicates a fair bit of the logic in breezy.transport.http.wsgi.
55
62
    """
56
63
 
57
64
    def do_POST(self):
58
65
        """Hand the request off to a smart server instance."""
59
 
        backing = get_transport(self.server.test_case_server._home_dir)
 
66
        backing = transport.get_transport_from_path(
 
67
            self.server.test_case_server._home_dir)
60
68
        chroot_server = chroot.ChrootServer(backing)
61
69
        chroot_server.start_server()
62
70
        try:
63
 
            t = get_transport(chroot_server.get_url())
 
71
            t = transport.get_transport_from_url(chroot_server.get_url())
64
72
            self.do_POST_inner(t)
65
73
        finally:
66
74
            chroot_server.stop_server()
83
91
        # we have to stop early due to error, but we would also have to use the
84
92
        # HTTP trailer facility which may not be widely available.
85
93
        request_bytes = self.rfile.read(data_length)
86
 
        protocol_factory, unused_bytes = medium._get_protocol_factory_for_bytes(
87
 
            request_bytes)
88
 
        out_buffer = StringIO()
 
94
        protocol_factory, unused_bytes = (
 
95
            medium._get_protocol_factory_for_bytes(request_bytes))
 
96
        out_buffer = BytesIO()
89
97
        smart_protocol_request = protocol_factory(t, out_buffer.write, '/')
90
98
        # Perhaps there should be a SmartServerHTTPMedium that takes care of
91
99
        # feeding the bytes in the http request to the smart_protocol_request,
106
114
    one. This will currently fail if the primary transport is not
107
115
    backed by regular disk files.
108
116
    """
 
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
 
109
124
    def setUp(self):
110
125
        super(TestCaseWithWebserver, self).setUp()
111
126
        self.transport_readonly_server = http_server.HttpServer
112
127
 
 
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
 
113
134
 
114
135
class TestCaseWithTwoWebservers(TestCaseWithWebserver):
115
136
    """A support class providing readonly urls on two servers that are http://.
117
138
    We set up two webservers to allows various tests involving
118
139
    proxies or redirections from one server to the other.
119
140
    """
 
141
 
120
142
    def setUp(self):
121
143
        super(TestCaseWithTwoWebservers, self).setUp()
122
144
        self.transport_secondary_server = http_server.HttpServer
127
149
 
128
150
        This is mostly a hook for daughter classes.
129
151
        """
130
 
        return self.transport_secondary_server()
 
152
        server = self.transport_secondary_server(
 
153
            protocol_version=self._protocol_version)
 
154
        server._url_protocol = self._url_protocol
 
155
        return server
131
156
 
132
157
    def get_secondary_server(self):
133
158
        """Get the server instance for the secondary transport."""
136
161
            self.start_server(self.__secondary_server)
137
162
        return self.__secondary_server
138
163
 
 
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
 
139
173
 
140
174
class ProxyServer(http_server.HttpServer):
141
175
    """A proxy test server for http transports."""
159
193
                # We do not send a body
160
194
                self.send_header('Content-Length', '0')
161
195
                self.end_headers()
162
 
                return False # The job is done
 
196
                return False  # The job is done
163
197
            else:
164
198
                # We leave the parent class serve the request
165
199
                pass
184
218
    def redirect_to(self, host, port):
185
219
        """Redirect all requests to a specific host:port"""
186
220
        self.redirections = [('(.*)',
187
 
                              r'http://%s:%s\1' % (host, port) ,
 
221
                              r'http://%s:%s\1' % (host, port),
188
222
                              301)]
189
223
 
190
224
    def is_redirected(self, path):
198
232
        code = None
199
233
        target = None
200
234
        for (rsource, rtarget, rcode) in self.redirections:
201
 
            target, match = re.subn(rsource, rtarget, path)
 
235
            target, match = re.subn(rsource, rtarget, path, count=1)
202
236
            if match:
203
237
                code = rcode
204
 
                break # The first match wins
 
238
                break  # The first match wins
205
239
            else:
206
240
                target = None
207
241
        return code, target
208
242
 
209
243
 
210
244
class TestCaseWithRedirectedWebserver(TestCaseWithTwoWebservers):
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()
 
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
231
285
 
232
286
 
233
287
class AuthRequestHandler(http_server.TestingHTTPRequestHandler):
243
297
    # - auth_header_recv: the header received containing auth
244
298
    # - auth_error_code: the error code to indicate auth required
245
299
 
 
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
 
246
313
    def do_GET(self):
247
314
        if self.authorized():
248
315
            return http_server.TestingHTTPRequestHandler.do_GET(self)
249
316
        else:
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
 
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()
261
324
 
262
325
 
263
326
class BasicAuthRequestHandler(AuthRequestHandler):
272
335
        if auth_header:
273
336
            scheme, raw_auth = auth_header.split(' ', 1)
274
337
            if scheme.lower() == tcs.auth_scheme:
275
 
                user, password = raw_auth.decode('base64').split(':')
276
 
                return tcs.authorized(user, password)
 
338
                user, password = base64.b64decode(raw_auth).split(b':')
 
339
                return tcs.authorized(user.decode('ascii'),
 
340
                                      password.decode('ascii'))
277
341
 
278
342
        return False
279
343
 
302
366
            return False
303
367
        scheme, auth = auth_header.split(None, 1)
304
368
        if scheme.lower() == tcs.auth_scheme:
305
 
            auth_dict = urllib2.parse_keqv_list(urllib2.parse_http_list(auth))
 
369
            auth_dict = parse_keqv_list(parse_http_list(auth))
306
370
 
307
371
            return tcs.digest_authorized(auth_dict, self.command)
308
372
 
313
377
        header = 'Digest realm="%s", ' % tcs.auth_realm
314
378
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
315
379
                                                              'MD5')
316
 
        self.send_header(tcs.auth_header_sent,header)
 
380
        self.send_header(tcs.auth_header_sent, header)
317
381
 
318
382
 
319
383
class DigestAndBasicAuthRequestHandler(DigestAuthRequestHandler):
331
395
        header = 'Digest realm="%s", ' % tcs.auth_realm
332
396
        header += 'nonce="%s", algorithm="%s", qop="auth"' % (tcs.auth_nonce,
333
397
                                                              'MD5')
334
 
        self.send_header(tcs.auth_header_sent,header)
 
398
        self.send_header(tcs.auth_header_sent, header)
335
399
 
336
400
 
337
401
class AuthServer(http_server.HttpServer):
349
413
    auth_header_sent = None
350
414
    auth_header_recv = None
351
415
    auth_error_code = None
352
 
    auth_realm = "Thou should not pass"
 
416
    auth_realm = u"Thou should not pass"
353
417
 
354
418
    def __init__(self, request_handler, auth_scheme,
355
419
                 protocol_version=None):
396
460
        if realm != self.auth_realm:
397
461
            return False
398
462
        user = auth['username']
399
 
        if not self.password_of.has_key(user):
 
463
        if user not in self.password_of:
400
464
            return False
401
 
        algorithm= auth['algorithm']
 
465
        algorithm = auth['algorithm']
402
466
        if algorithm != 'MD5':
403
467
            return False
404
468
        qop = auth['qop']
409
473
 
410
474
        # Recalculate the response_digest to compare with the one
411
475
        # sent by the client
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))
 
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'))
417
484
 
418
485
        nonce_count = int(auth['nc'], 16)
419
486
 
503
570
        self.init_proxy_auth()
504
571
        # We really accept Digest only
505
572
        self.auth_scheme = 'digest'
506
 
 
507