/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_server.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-23 22:06:41 UTC
  • mfrom: (6738 trunk)
  • mto: This revision was merged to the branch mainline in revision 6739.
  • Revision ID: jelmer@jelmer.uk-20170723220641-69eczax9bmv8d6kk
Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import errno
18
 
import http.client as http_client
19
 
import http.server as http_server
 
18
import httplib
20
19
import os
21
20
import posixpath
22
21
import random
23
22
import re
 
23
import SimpleHTTPServer
24
24
import socket
25
 
import sys
26
 
from urllib.parse import urlparse
 
25
import urlparse
27
26
 
28
27
from .. import (
29
28
    osutils,
37
36
        return 'path %s is not in %s' % self.args
38
37
 
39
38
 
40
 
class TestingHTTPRequestHandler(http_server.SimpleHTTPRequestHandler):
 
39
class TestingHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
41
40
    """Handles one request.
42
41
 
43
42
    A TestingHTTPRequestHandler is instantiated for every request received by
49
48
    protocol_version = 'HTTP/1.1'
50
49
 
51
50
    # The Message-like class used to parse the request headers
52
 
    MessageClass = http_client.HTTPMessage
 
51
    MessageClass = httplib.HTTPMessage
53
52
 
54
53
    def setup(self):
55
 
        http_server.SimpleHTTPRequestHandler.setup(self)
 
54
        SimpleHTTPServer.SimpleHTTPRequestHandler.setup(self)
56
55
        self._cwd = self.server._home_dir
57
56
        tcs = self.server.test_case_server
58
57
        if tcs.protocol_version is not None:
95
94
    def send_error(self, code, message=None):
96
95
        """Send and log an error reply.
97
96
 
98
 
        We redefine the python-provided version to be able to set a
 
97
        We redefine the python-provided version to be able to set a 
99
98
        ``Content-Length`` header as some http/1.1 clients complain otherwise
100
99
        (see bug #568421).
101
100
 
119
118
        self.send_header('Connection', 'close')
120
119
        self.end_headers()
121
120
        if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
122
 
            self.wfile.write(content.encode('utf-8'))
 
121
            self.wfile.write(content)
123
122
 
124
123
    def _handle_one_request(self):
125
 
        http_server.SimpleHTTPRequestHandler.handle_one_request(self)
 
124
        SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
126
125
 
127
126
    _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)?$')
128
127
    _tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
187
186
        header_line = '%s: %s\r\n' % (keyword, value)
188
187
        return len(header_line)
189
188
 
 
189
    def send_head(self):
 
190
        """Overrides base implementation to work around a bug in python2.5."""
 
191
        path = self.translate_path(self.path)
 
192
        if os.path.isdir(path) and not self.path.endswith('/'):
 
193
            # redirect browser - doing basically what apache does when
 
194
            # DirectorySlash option is On which is quite common (braindead, but
 
195
            # common)
 
196
            self.send_response(301)
 
197
            self.send_header("Location", self.path + "/")
 
198
            # Indicates that the body is empty for HTTP/1.1 clients
 
199
            self.send_header('Content-Length', '0')
 
200
            self.end_headers()
 
201
            return None
 
202
 
 
203
        return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
 
204
 
190
205
    def send_range_content(self, file, start, length):
191
206
        file.seek(start)
192
207
        self.wfile.write(file.read(length))
207
222
    def get_multiple_ranges(self, file, file_size, ranges):
208
223
        self.send_response(206)
209
224
        self.send_header('Accept-Ranges', 'bytes')
210
 
        boundary = '%d' % random.randint(0, 0x7FFFFFFF)
 
225
        boundary = '%d' % random.randint(0,0x7FFFFFFF)
211
226
        self.send_header('Content-Type',
212
227
                         'multipart/byteranges; boundary=%s' % boundary)
213
 
        boundary_line = b'--%s\r\n' % boundary.encode('ascii')
 
228
        boundary_line = '--%s\r\n' % boundary
214
229
        # Calculate the Content-Length
215
230
        content_length = 0
216
231
        for (start, end) in ranges:
219
234
                'Content-type', 'application/octet-stream')
220
235
            content_length += self._header_line_length(
221
236
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
222
 
            content_length += len('\r\n')  # end headers
 
237
            content_length += len('\r\n') # end headers
223
238
            content_length += end - start + 1
224
239
        content_length += len(boundary_line)
225
240
        self.send_header('Content-length', content_length)
248
263
        ranges_header_value = self.headers.get('Range')
249
264
        if ranges_header_value is None or os.path.isdir(path):
250
265
            # Let the mother class handle most cases
251
 
            return http_server.SimpleHTTPRequestHandler.do_GET(self)
 
266
            return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
252
267
 
253
268
        try:
254
269
            # Always read in binary mode. Opening files in text
297
312
            # do beginning with python 2.4.3: abandon query
298
313
            # parameters, scheme, host port, etc (which ensure we
299
314
            # provide the right behaviour on all python versions).
300
 
            path = urlparse(path)[2]
 
315
            path = urlparse.urlparse(path)[2]
301
316
            # And now, we can apply *our* trick to proxy files
302
317
            path += '-proxied'
303
318
 
315
330
        Override from python standard library to stop it calling os.getcwd()
316
331
        """
317
332
        # abandon query parameters
318
 
        path = urlparse(path)[2]
 
333
        path = urlparse.urlparse(path)[2]
319
334
        path = posixpath.normpath(urlutils.unquote(path))
 
335
        path = path.decode('utf-8')
320
336
        words = path.split('/')
321
337
        path = self._cwd
322
338
        for num, word in enumerate(w for w in words if w):
323
339
            if num == 0:
324
340
                drive, word = os.path.splitdrive(word)
325
341
            head, word = os.path.split(word)
326
 
            if word in (os.curdir, os.pardir):
327
 
                continue
 
342
            if word in (os.curdir, os.pardir): continue
328
343
            path = os.path.join(path, word)
329
344
        return path
330
345
 
357
372
    server, we need an independent connection for each of them. We achieve that
358
373
    by spawning a new thread for each connection.
359
374
    """
360
 
 
361
375
    def __init__(self, server_address, request_handler_class,
362
376
                 test_case_server):
363
377
        test_server.TestingThreadingTCPServer.__init__(self, server_address,
405
419
        # Get the appropriate server class for the required protocol
406
420
        serv_cls = self.http_server_class.get(proto_vers, None)
407
421
        if serv_cls is None:
408
 
            raise http_client.UnknownProtocol(proto_vers)
 
422
            raise httplib.UnknownProtocol(proto_vers)
409
423
        self.host = 'localhost'
410
424
        self.port = 0
411
425
        super(HttpServer, self).__init__((self.host, self.port),
425
439
        path_parts = path.split(os.path.sep)
426
440
        if os.path.isabs(path):
427
441
            if path_parts[:len(self._local_path_parts)] != \
428
 
                    self._local_path_parts:
 
442
                   self._local_path_parts:
429
443
                raise BadWebserverPath(path, self.test_dir)
430
444
            remote_path = '/'.join(path_parts[len(self._local_path_parts):])
431
445
        else:
469
483
        # this is chosen to try to prevent trouble with proxies, weird dns,
470
484
        # etc
471
485
        return self._url_protocol + '://127.0.0.1:1/'
 
486
 
 
487
 
 
488
class HttpServer_urllib(HttpServer):
 
489
    """Subclass of HttpServer that gives http+urllib urls.
 
490
 
 
491
    This is for use in testing: connections to this server will always go
 
492
    through urllib where possible.
 
493
    """
 
494
 
 
495
    # urls returned by this server should require the urllib client impl
 
496
    _url_protocol = 'http+urllib'