/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
1
# Copyright (C) 2006, 2007 Canonical Ltd
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
import errno
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
18
import httplib
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
19
import os
2146.1.1 by Alexander Belchenko
fixes for test suite: forgotten imports in HttpServer.py
20
import posixpath
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
21
import random
22
import re
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
23
import SimpleHTTPServer
24
import socket
3111.1.3 by Vincent Ladeuil
Rework http test servers classes. Both 1.0 and 1.1 passing tests (1.1 forced manually).
25
import SocketServer
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
26
import sys
27
import threading
28
import time
2146.1.1 by Alexander Belchenko
fixes for test suite: forgotten imports in HttpServer.py
29
import urllib
30
import urlparse
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
31
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
32
from bzrlib import transport
33
from bzrlib.transport import local
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
34
35
36
class WebserverNotAvailable(Exception):
37
    pass
38
39
40
class BadWebserverPath(ValueError):
41
    def __str__(self):
42
        return 'path %s is not in %s' % self.args
43
44
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
45
class TestingHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
2420.1.10 by Vincent Ladeuil
Doc fixes.
46
    """Handles one request.
47
3111.1.21 by Vincent Ladeuil
Add some comments.
48
    A TestingHTTPRequestHandler is instantiated for every request received by
49
    the associated server. Note that 'request' here is inherited from the base
50
    TCPServer class, for the HTTP server it is really a connection which itself
51
    will handle one or several HTTP requests.
2420.1.10 by Vincent Ladeuil
Doc fixes.
52
    """
3111.1.24 by Vincent Ladeuil
Cleanups.
53
    # Default protocol version
54
    protocol_version = 'HTTP/1.1'
55
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
56
    # The Message-like class used to parse the request headers
57
    MessageClass = httplib.HTTPMessage
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
58
3111.1.15 by Vincent Ladeuil
Provide a way to specify the protocol version at the server layer.
59
    def setup(self):
60
        SimpleHTTPServer.SimpleHTTPRequestHandler.setup(self)
3221.9.1 by Robert Collins
* Fix the test HTTPServer to be isolated from chdir calls made while it is
61
        self._cwd = self.server._home_dir
3111.1.15 by Vincent Ladeuil
Provide a way to specify the protocol version at the server layer.
62
        tcs = self.server.test_case_server
63
        if tcs.protocol_version is not None:
64
            # If the test server forced a protocol version, use it
65
            self.protocol_version = tcs.protocol_version
66
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
67
    def log_message(self, format, *args):
2164.2.28 by Vincent Ladeuil
TestingHTTPServer.test_case_server renamed from test_case to avoid confusions.
68
        tcs = self.server.test_case_server
69
        tcs.log('webserver - %s - - [%s] %s "%s" "%s"',
70
                self.address_string(),
71
                self.log_date_time_string(),
72
                format % args,
3111.1.19 by Vincent Ladeuil
Merge back test_http_implementations.pc into test_http.py.
73
                self.headers.get('referer', '-'),
2164.2.28 by Vincent Ladeuil
TestingHTTPServer.test_case_server renamed from test_case to avoid confusions.
74
                self.headers.get('user-agent', '-'))
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
75
76
    def handle_one_request(self):
77
        """Handle a single HTTP request.
78
2831.6.1 by Vincent Ladeuil
Remove some more noise from test suite.
79
        We catch all socket errors occurring when the client close the
80
        connection early to avoid polluting the test results.
81
        """
82
        try:
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
83
            SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
2831.6.1 by Vincent Ladeuil
Remove some more noise from test suite.
84
        except socket.error, e:
3111.1.24 by Vincent Ladeuil
Cleanups.
85
            # Any socket error should close the connection, but some errors are
86
            # due to the client closing early and we don't want to pollute test
3111.1.20 by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant.
87
            # results, so we raise only the others.
88
            self.close_connection = 1
89
            if (len(e.args) == 0
90
                or e.args[0] not in (errno.EPIPE, errno.ECONNRESET,
91
                                     errno.ECONNABORTED, errno.EBADF)):
2831.6.1 by Vincent Ladeuil
Remove some more noise from test suite.
92
                raise
93
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
94
    _range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
95
    _tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
96
97
    def parse_ranges(self, ranges_header):
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
98
        """Parse the range header value and returns ranges and tail.
99
100
        RFC2616 14.35 says that syntactically invalid range
101
        specifiers MUST be ignored. In that case, we return 0 for
102
        tail and [] for ranges.
103
        """
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
104
        tail = 0
105
        ranges = []
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
106
        if not ranges_header.startswith('bytes='):
107
            # Syntactically invalid header
108
            return 0, []
109
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
110
        ranges_header = ranges_header[len('bytes='):]
111
        for range_str in ranges_header.split(','):
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
112
            # FIXME: RFC2616 says end is optional and default to file_size
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
113
            range_match = self._range_regexp.match(range_str)
114
            if range_match is not None:
2182.2.2 by v.ladeuil+lp at free
Thanks again to Aaron, the http server RFC2616 compliance
115
                start = int(range_match.group('start'))
116
                end = int(range_match.group('end'))
117
                if start > end:
118
                    # Syntactically invalid range
119
                    return 0, []
120
                ranges.append((start, end))
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
121
            else:
122
                tail_match = self._tail_regexp.match(range_str)
123
                if tail_match is not None:
124
                    tail = int(tail_match.group('tail'))
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
125
                else:
126
                    # Syntactically invalid range
127
                    return 0, []
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
128
        return tail, ranges
129
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
130
    def _header_line_length(self, keyword, value):
131
        header_line = '%s: %s\r\n' % (keyword, value)
132
        return len(header_line)
133
3111.1.23 by Vincent Ladeuil
Make HTTP/1.1 the default implementation reveals one more bug.
134
    def send_head(self):
135
        """Overrides base implementation to work around a bug in python2.5."""
136
        path = self.translate_path(self.path)
137
        if os.path.isdir(path) and not self.path.endswith('/'):
138
            # redirect browser - doing basically what apache does when
139
            # DirectorySlash option is On which is quite common (braindead, but
140
            # common)
141
            self.send_response(301)
142
            self.send_header("Location", self.path + "/")
143
            # Indicates that the body is empty for HTTP/1.1 clients 
144
            self.send_header('Content-Length', '0')
145
            self.end_headers()
146
            return None
147
148
        return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
149
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
150
    def send_range_content(self, file, start, length):
151
        file.seek(start)
152
        self.wfile.write(file.read(length))
153
154
    def get_single_range(self, file, file_size, start, end):
155
        self.send_response(206)
156
        length = end - start + 1
157
        self.send_header('Accept-Ranges', 'bytes')
158
        self.send_header("Content-Length", "%d" % length)
159
160
        self.send_header("Content-Type", 'application/octet-stream')
161
        self.send_header("Content-Range", "bytes %d-%d/%d" % (start,
162
                                                              end,
163
                                                              file_size))
164
        self.end_headers()
165
        self.send_range_content(file, start, length)
166
167
    def get_multiple_ranges(self, file, file_size, ranges):
168
        self.send_response(206)
169
        self.send_header('Accept-Ranges', 'bytes')
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
170
        boundary = '%d' % random.randint(0,0x7FFFFFFF)
171
        self.send_header('Content-Type',
172
                         'multipart/byteranges; boundary=%s' % boundary)
173
        boundary_line = '--%s\r\n' % boundary
174
        # Calculate the Content-Length
175
        content_length = 0
176
        for (start, end) in ranges:
177
            content_length += len(boundary_line)
178
            content_length += self._header_line_length(
179
                'Content-type', 'application/octet-stream')
180
            content_length += self._header_line_length(
181
                'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
182
            content_length += len('\r\n') # end headers
183
            content_length += end - start # + 1
184
        content_length += len(boundary_line)
185
        self.send_header('Content-length', content_length)
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
186
        self.end_headers()
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
187
188
        # Send the multipart body
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
189
        for (start, end) in ranges:
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
190
            self.wfile.write(boundary_line)
191
            self.send_header('Content-type', 'application/octet-stream')
192
            self.send_header('Content-Range', 'bytes %d-%d/%d'
193
                             % (start, end, file_size))
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
194
            self.end_headers()
195
            self.send_range_content(file, start, end - start + 1)
3059.2.12 by Vincent Ladeuil
Spiv review feedback.
196
        # Final boundary
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
197
        self.wfile.write(boundary_line)
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
198
199
    def do_GET(self):
200
        """Serve a GET request.
201
202
        Handles the Range header.
203
        """
3052.3.2 by Vincent Ladeuil
Add tests and fix trivial bugs and other typos.
204
        # Update statistics
205
        self.server.test_case_server.GET_request_nb += 1
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
206
207
        path = self.translate_path(self.path)
208
        ranges_header_value = self.headers.get('Range')
209
        if ranges_header_value is None or os.path.isdir(path):
210
            # Let the mother class handle most cases
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
211
            return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
212
213
        try:
214
            # Always read in binary mode. Opening files in text
215
            # mode may cause newline translations, making the
216
            # actual size of the content transmitted *less* than
217
            # the content-length!
218
            file = open(path, 'rb')
219
        except IOError:
220
            self.send_error(404, "File not found")
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
221
            return
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
222
223
        file_size = os.fstat(file.fileno())[6]
224
        tail, ranges = self.parse_ranges(ranges_header_value)
225
        # Normalize tail into ranges
226
        if tail != 0:
227
            ranges.append((file_size - tail, file_size))
228
2182.2.2 by v.ladeuil+lp at free
Thanks again to Aaron, the http server RFC2616 compliance
229
        self._satisfiable_ranges = True
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
230
        if len(ranges) == 0:
2182.2.2 by v.ladeuil+lp at free
Thanks again to Aaron, the http server RFC2616 compliance
231
            self._satisfiable_ranges = False
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
232
        else:
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
233
            def check_range(range_specifier):
234
                start, end = range_specifier
2182.2.2 by v.ladeuil+lp at free
Thanks again to Aaron, the http server RFC2616 compliance
235
                # RFC2616 14.35, ranges are invalid if start >= file_size
236
                if start >= file_size:
237
                    self._satisfiable_ranges = False # Side-effect !
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
238
                    return 0, 0
239
                # RFC2616 14.35, end values should be truncated
240
                # to file_size -1 if they exceed it
241
                end = min(end, file_size - 1)
242
                return start, end
243
244
            ranges = map(check_range, ranges)
245
2182.2.2 by v.ladeuil+lp at free
Thanks again to Aaron, the http server RFC2616 compliance
246
        if not self._satisfiable_ranges:
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
247
            # RFC2616 14.16 and 14.35 says that when a server
248
            # encounters unsatisfiable range specifiers, it
249
            # SHOULD return a 416.
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
250
            file.close()
2182.2.1 by v.ladeuil+lp at free
Aaron was right. Thanks to him, the http server RFC2616 compliance
251
            # FIXME: We SHOULD send a Content-Range header too,
252
            # but the implementation of send_error does not
253
            # allows that. So far.
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
254
            self.send_error(416, "Requested range not satisfiable")
255
            return
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
256
257
        if len(ranges) == 1:
258
            (start, end) = ranges[0]
259
            self.get_single_range(file, file_size, start, end)
260
        else:
261
            self.get_multiple_ranges(file, file_size, ranges)
262
        file.close()
263
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
264
    def translate_path(self, path):
265
        """Translate a /-separated PATH to the local filename syntax.
266
267
        If the server requires it, proxy the path before the usual translation
268
        """
269
        if self.server.test_case_server.proxy_requests:
270
            # We need to act as a proxy and accept absolute urls,
271
            # which SimpleHTTPRequestHandler (parent) is not
272
            # ready for. So we just drop the protocol://host:port
273
            # part in front of the request-url (because we know
274
            # we would not forward the request to *another*
275
            # proxy).
276
277
            # So we do what SimpleHTTPRequestHandler.translate_path
278
            # do beginning with python 2.4.3: abandon query
279
            # parameters, scheme, host port, etc (which ensure we
280
            # provide the right behaviour on all python versions).
281
            path = urlparse.urlparse(path)[2]
282
            # And now, we can apply *our* trick to proxy files
283
            path += '-proxied'
284
285
        return self._translate_path(path)
286
287
    def _translate_path(self, path):
3221.9.1 by Robert Collins
* Fix the test HTTPServer to be isolated from chdir calls made while it is
288
        """Translate a /-separated PATH to the local filename syntax.
289
290
        Components that mean special things to the local file system
291
        (e.g. drive or directory names) are ignored.  (XXX They should
292
        probably be diagnosed.)
293
294
        Override from python standard library to stop it calling os.getcwd()
295
        """
296
        # abandon query parameters
297
        path = urlparse.urlparse(path)[2]
298
        path = posixpath.normpath(urllib.unquote(path))
299
        words = path.split('/')
300
        words = filter(None, words)
301
        path = self._cwd
302
        for word in words:
303
            drive, word = os.path.splitdrive(word)
304
            head, word = os.path.split(word)
305
            if word in (os.curdir, os.pardir): continue
306
            path = os.path.join(path, word)
307
        return path
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
308
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
309
    if sys.platform == 'win32':
310
        # On win32 you cannot access non-ascii filenames without
311
        # decoding them into unicode first.
312
        # However, under Linux, you can access bytestream paths
313
        # without any problems. If this function was always active
314
        # it would probably break tests when LANG=C was set
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
315
        def _translate_path(self, path):
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
316
            """Translate a /-separated PATH to the local filename syntax.
317
318
            For bzr, all url paths are considered to be utf8 paths.
319
            On Linux, you can access these paths directly over the bytestream
320
            request, but on win32, you must decode them, and access them
321
            as Unicode files.
322
            """
323
            # abandon query parameters
324
            path = urlparse.urlparse(path)[2]
325
            path = posixpath.normpath(urllib.unquote(path))
326
            path = path.decode('utf-8')
327
            words = path.split('/')
328
            words = filter(None, words)
3221.9.1 by Robert Collins
* Fix the test HTTPServer to be isolated from chdir calls made while it is
329
            path = self._cwd
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
330
            for word in words:
331
                drive, word = os.path.splitdrive(word)
332
                head, word = os.path.split(word)
333
                if word in (os.curdir, os.pardir): continue
334
                path = os.path.join(path, word)
335
            return path
336
337
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
338
class TestingHTTPServerMixin:
339
340
    def __init__(self, test_case_server):
2164.2.28 by Vincent Ladeuil
TestingHTTPServer.test_case_server renamed from test_case to avoid confusions.
341
        # test_case_server can be used to communicate between the
2164.2.29 by Vincent Ladeuil
Test the http redirection at the request level even if it's not
342
        # tests and the server (or the request handler and the
343
        # server), allowing dynamic behaviors to be defined from
344
        # the tests cases.
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
345
        self.test_case_server = test_case_server
3221.9.1 by Robert Collins
* Fix the test HTTPServer to be isolated from chdir calls made while it is
346
        self._home_dir = test_case_server._home_dir
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
347
348
    def tearDown(self):
3111.1.3 by Vincent Ladeuil
Rework http test servers classes. Both 1.0 and 1.1 passing tests (1.1 forced manually).
349
         """Called to clean-up the server.
350
 
351
         Since the server may be (surely is, even) in a blocking listen, we
352
         shutdown its socket before closing it.
353
         """
354
         # Note that is this executed as part of the implicit tear down in the
355
         # main thread while the server runs in its own thread. The clean way
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
356
         # to tear down the server is to instruct him to stop accepting
357
         # connections and wait for the current connection(s) to end
358
         # naturally. To end the connection naturally, the http transports
359
         # should close their socket when they do not need to talk to the
360
         # server anymore. This happens naturally during the garbage collection
361
         # phase of the test transport objetcs (the server clients), so we
362
         # don't have to worry about them.  So, for the server, we must tear
363
         # down here, from the main thread, when the test have ended.  Note
364
         # that since the server is in a blocking operation and since python
365
         # use select internally, shutting down the socket is reliable and
366
         # relatively clean.
3111.1.31 by Vincent Ladeuil
Review feeback.
367
         try:
368
             self.socket.shutdown(socket.SHUT_RDWR)
369
         except socket.error, e:
370
             # WSAENOTCONN (10057) 'Socket is not connected' is harmless on
371
             # windows (occurs before the first connection attempt
372
             # vila--20071230)
373
             if not len(e.args) or e.args[0] != 10057:
374
                 raise
3111.1.3 by Vincent Ladeuil
Rework http test servers classes. Both 1.0 and 1.1 passing tests (1.1 forced manually).
375
         # Let the server properly close the socket
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
376
         self.server_close()
3111.1.3 by Vincent Ladeuil
Rework http test servers classes. Both 1.0 and 1.1 passing tests (1.1 forced manually).
377
3221.9.1 by Robert Collins
* Fix the test HTTPServer to be isolated from chdir calls made while it is
378
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
379
class TestingHTTPServer(SocketServer.TCPServer, TestingHTTPServerMixin):
3111.1.3 by Vincent Ladeuil
Rework http test servers classes. Both 1.0 and 1.1 passing tests (1.1 forced manually).
380
3111.1.30 by Vincent Ladeuil
Update NEWS. Some cosmetic changes.
381
    def __init__(self, server_address, request_handler_class,
382
                 test_case_server):
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
383
        TestingHTTPServerMixin.__init__(self, test_case_server)
384
        SocketServer.TCPServer.__init__(self, server_address,
385
                                        request_handler_class)
386
387
388
class TestingThreadingHTTPServer(SocketServer.ThreadingTCPServer,
389
                                 TestingHTTPServerMixin):
3111.1.3 by Vincent Ladeuil
Rework http test servers classes. Both 1.0 and 1.1 passing tests (1.1 forced manually).
390
    """A threading HTTP test server for HTTP 1.1.
391
392
    Since tests can initiate several concurrent connections to the same http
393
    server, we need an independent connection for each of them. We achieve that
394
    by spawning a new thread for each connection.
395
    """
396
3111.1.30 by Vincent Ladeuil
Update NEWS. Some cosmetic changes.
397
    def __init__(self, server_address, request_handler_class,
398
                 test_case_server):
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
399
        TestingHTTPServerMixin.__init__(self, test_case_server)
400
        SocketServer.ThreadingTCPServer.__init__(self, server_address,
401
                                                 request_handler_class)
3111.1.3 by Vincent Ladeuil
Rework http test servers classes. Both 1.0 and 1.1 passing tests (1.1 forced manually).
402
        # Decides how threads will act upon termination of the main
403
        # process. This is prophylactic as we should not leave the threads
404
        # lying around.
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
405
        self.daemon_threads = True
2953.2.1 by Vincent Ladeuil
Fix #158972 by not using timeout for HttpServer.
406
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
407
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
408
class HttpServer(transport.Server):
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
409
    """A test server for http transports.
410
411
    Subclasses can provide a specific request handler.
412
    """
413
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
414
    # The real servers depending on the protocol
415
    http_server_class = {'HTTP/1.0': TestingHTTPServer,
416
                         'HTTP/1.1': TestingThreadingHTTPServer,
417
                         }
418
2420.1.9 by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth.
419
    # Whether or not we proxy the requests (see
420
    # TestingHTTPRequestHandler.translate_path).
421
    proxy_requests = False
422
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
423
    # used to form the url that connects to this server
424
    _url_protocol = 'http'
425
3111.1.15 by Vincent Ladeuil
Provide a way to specify the protocol version at the server layer.
426
    def __init__(self, request_handler=TestingHTTPRequestHandler,
427
                 protocol_version=None):
428
        """Constructor.
429
430
        :param request_handler: a class that will be instantiated to handle an
431
            http connection (one or several requests).
432
433
        :param protocol_version: if specified, will override the protocol
434
            version of the request handler.
435
        """
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
436
        transport.Server.__init__(self)
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
437
        self.request_handler = request_handler
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
438
        self.host = 'localhost'
439
        self.port = 0
440
        self._httpd = None
3111.1.15 by Vincent Ladeuil
Provide a way to specify the protocol version at the server layer.
441
        self.protocol_version = protocol_version
3052.3.2 by Vincent Ladeuil
Add tests and fix trivial bugs and other typos.
442
        # Allows tests to verify number of GET requests issued
443
        self.GET_request_nb = 0
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
444
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
445
    def _get_httpd(self):
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
446
        if self._httpd is None:
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
447
            rhandler = self.request_handler
3111.1.21 by Vincent Ladeuil
Add some comments.
448
            # Depending on the protocol version, we will create the approriate
449
            # server
3111.1.15 by Vincent Ladeuil
Provide a way to specify the protocol version at the server layer.
450
            if self.protocol_version is None:
3111.1.21 by Vincent Ladeuil
Add some comments.
451
                # Use the request handler one
3111.1.15 by Vincent Ladeuil
Provide a way to specify the protocol version at the server layer.
452
                proto_vers = rhandler.protocol_version
453
            else:
3111.1.21 by Vincent Ladeuil
Add some comments.
454
                # Use our own, it will be used to override the request handler
455
                # one too.
3111.1.15 by Vincent Ladeuil
Provide a way to specify the protocol version at the server layer.
456
                proto_vers = self.protocol_version
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
457
            # Create the appropriate server for the required protocol
458
            serv_cls = self.http_server_class.get(proto_vers, None)
459
            if serv_cls is None:
460
                raise httplib.UnknownProtocol(proto_vers)
461
            else:
462
                self._httpd = serv_cls((self.host, self.port), rhandler, self)
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
463
            host, self.port = self._httpd.socket.getsockname()
464
        return self._httpd
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
465
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
466
    def _http_start(self):
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
467
        """Server thread main entry point. """
468
        self._http_running = False
469
        try:
470
            try:
471
                httpd = self._get_httpd()
472
                self._http_base_url = '%s://%s:%s/' % (self._url_protocol,
473
                                                       self.host, self.port)
474
                self._http_running = True
475
            except:
476
                # Whatever goes wrong, we save the exception for the main
477
                # thread. Note that since we are running in a thread, no signal
478
                # can be received, so we don't care about KeyboardInterrupt.
479
                self._http_exception = sys.exc_info()
480
        finally:
481
            # Release the lock or the main thread will block and the whole
482
            # process will hang.
483
            self._http_starting.release()
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
484
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
485
        # From now on, exceptions are taken care of by the
486
        # SocketServer.BaseServer or the request handler.
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
487
        while self._http_running:
488
            try:
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
489
                # Really an HTTP connection but the python framework is generic
490
                # and call them requests
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
491
                httpd.handle_request()
492
            except socket.timeout:
493
                pass
494
495
    def _get_remote_url(self, path):
496
        path_parts = path.split(os.path.sep)
497
        if os.path.isabs(path):
498
            if path_parts[:len(self._local_path_parts)] != \
499
                   self._local_path_parts:
500
                raise BadWebserverPath(path, self.test_dir)
501
            remote_path = '/'.join(path_parts[len(self._local_path_parts):])
502
        else:
503
            remote_path = '/'.join(path_parts)
504
505
        return self._http_base_url + remote_path
506
507
    def log(self, format, *args):
508
        """Capture Server log output."""
509
        self.logs.append(format % args)
510
2381.1.2 by Robert Collins
Fixup the test changes made for hpss to be clean and self contained.
511
    def setUp(self, backing_transport_server=None):
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
512
        """See bzrlib.transport.Server.setUp.
513
        
2381.1.2 by Robert Collins
Fixup the test changes made for hpss to be clean and self contained.
514
        :param backing_transport_server: The transport that requests over this
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
515
            protocol should be forwarded to. Note that this is currently not
2381.1.2 by Robert Collins
Fixup the test changes made for hpss to be clean and self contained.
516
            supported for HTTP.
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
517
        """
2381.1.2 by Robert Collins
Fixup the test changes made for hpss to be clean and self contained.
518
        # XXX: TODO: make the server back onto vfs_server rather than local
519
        # disk.
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
520
        if not (backing_transport_server is None or \
521
                isinstance(backing_transport_server, local.LocalURLServer)):
522
            raise AssertionError(
523
                "HTTPServer currently assumes local transport, got %s" % \
524
                backing_transport_server)
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
525
        self._home_dir = os.getcwdu()
526
        self._local_path_parts = self._home_dir.split(os.path.sep)
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
527
        self._http_base_url = None
528
3111.1.6 by Vincent Ladeuil
Begin refactoring test_http.py into parameterized tests.
529
        # Create the server thread
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
530
        self._http_starting = threading.Lock()
531
        self._http_starting.acquire()
532
        self._http_thread = threading.Thread(target=self._http_start)
533
        self._http_thread.setDaemon(True)
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
534
        self._http_exception = None
2167.3.5 by v.ladeuil+lp at free
Tests for proxies, covering #74759.
535
        self._http_thread.start()
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
536
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
537
        # Wait for the server thread to start (i.e release the lock)
538
        self._http_starting.acquire()
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
539
540
        if self._http_exception is not None:
3111.1.21 by Vincent Ladeuil
Add some comments.
541
            # Something went wrong during server start
3111.1.4 by Vincent Ladeuil
Select the server depending on the request handler protocol. Add tests.
542
            exc_class, exc_value, exc_tb = self._http_exception
543
            raise exc_class, exc_value, exc_tb
2167.3.6 by v.ladeuil+lp at free
Take John's comments into account and add more tests.
544
        self._http_starting.release()
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
545
        self.logs = []
546
547
    def tearDown(self):
548
        """See bzrlib.transport.Server.tearDown."""
3111.1.22 by Vincent Ladeuil
Rework TestingHTTPServer classes, fix test bug.
549
        self._httpd.tearDown()
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
550
        self._http_running = False
3111.1.28 by Vincent Ladeuil
Fix the multi-ranges http server and add tests.
551
        # We don't need to 'self._http_thread.join()' here since the thread is
552
        # a daemonic one and will be garbage collected anyway. Joining just
553
        # slows us down for no added benefit.
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
554
555
    def get_url(self):
556
        """See bzrlib.transport.Server.get_url."""
557
        return self._get_remote_url(self._home_dir)
558
559
    def get_bogus_url(self):
560
        """See bzrlib.transport.Server.get_bogus_url."""
561
        # this is chosen to try to prevent trouble with proxies, weird dns,
562
        # etc
3111.1.2 by Vincent Ladeuil
Preparatory cleanup.
563
        return self._url_protocol + '://127.0.0.1:1/'
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
564
565
566
class HttpServer_urllib(HttpServer):
567
    """Subclass of HttpServer that gives http+urllib urls.
568
569
    This is for use in testing: connections to this server will always go
570
    through urllib where possible.
571
    """
572
573
    # urls returned by this server should require the urllib client impl
574
    _url_protocol = 'http+urllib'
575
576
577
class HttpServer_PyCurl(HttpServer):
578
    """Subclass of HttpServer that gives http+pycurl urls.
579
580
    This is for use in testing: connections to this server will always go
581
    through pycurl where possible.
582
    """
583
584
    # We don't care about checking the pycurl availability as
585
    # this server will be required only when pycurl is present
586
587
    # urls returned by this server should require the pycurl client impl
588
    _url_protocol = 'http+pycurl'