13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
75
72
self.headers.get('referer', '-'),
76
73
self.headers.get('user-agent', '-'))
79
SimpleHTTPServer.SimpleHTTPRequestHandler.handle(self)
80
# Some client (pycurl, I'm looking at you) are more picky than others
81
# and require that the socket itself is closed
82
# (SocketServer.StreamRequestHandler only close the two associated
84
self.connection.close()
86
75
def handle_one_request(self):
87
76
"""Handle a single HTTP request.
101
90
errno.ECONNABORTED, errno.EBADF)):
104
error_content_type = 'text/plain'
105
error_message_format = '''\
106
Error code: %(code)s.
107
Message: %(message)s.
110
def send_error(self, code, message=None):
111
"""Send and log an error reply.
113
We redefine the python-provided version to be able to set a
114
``Content-Length`` header as some http/1.1 clients complain otherwise
117
:param code: The HTTP error code.
119
:param message: The explanation of the error code, Defaults to a short
125
message = self.responses[code][0]
128
self.log_error("code %d, message %s", code, message)
129
content = (self.error_message_format %
130
{'code': code, 'message': message})
131
self.send_response(code, message)
132
self.send_header("Content-Type", self.error_content_type)
133
self.send_header("Content-Length", "%d" % len(content))
134
self.send_header('Connection', 'close')
136
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
137
self.wfile.write(content)
139
def _handle_one_request(self):
140
SimpleHTTPServer.SimpleHTTPRequestHandler.handle_one_request(self)
142
93
_range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
143
94
_tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
228
179
content_length += self._header_line_length(
229
180
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
230
181
content_length += len('\r\n') # end headers
231
content_length += end - start + 1
182
content_length += end - start # + 1
232
183
content_length += len(boundary_line)
233
184
self.send_header('Content-length', content_length)
234
185
self.end_headers()
305
256
if len(ranges) == 1:
306
257
(start, end) = ranges[0]
307
self.get_single_range(f, file_size, start, end)
258
self.get_single_range(file, file_size, start, end)
309
self.get_multiple_ranges(f, file_size, ranges)
260
self.get_multiple_ranges(file, file_size, ranges)
312
263
def translate_path(self, path):
313
264
"""Translate a /-separated PATH to the local filename syntax.
333
284
return self._translate_path(path)
335
286
def _translate_path(self, path):
336
"""Translate a /-separated PATH to the local filename syntax.
338
Note that we're translating http URLs here, not file URLs.
339
The URL root location is the server's startup directory.
340
Components that mean special things to the local file system
341
(e.g. drive or directory names) are ignored. (XXX They should
342
probably be diagnosed.)
344
Override from python standard library to stop it calling os.getcwd()
346
# abandon query parameters
347
path = urlparse.urlparse(path)[2]
348
path = posixpath.normpath(urllib.unquote(path))
349
path = path.decode('utf-8')
350
words = path.split('/')
351
words = filter(None, words)
353
for num, word in enumerate(words):
287
return SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(
290
if sys.platform == 'win32':
291
# On win32 you cannot access non-ascii filenames without
292
# decoding them into unicode first.
293
# However, under Linux, you can access bytestream paths
294
# without any problems. If this function was always active
295
# it would probably break tests when LANG=C was set
296
def _translate_path(self, path):
297
"""Translate a /-separated PATH to the local filename syntax.
299
For bzr, all url paths are considered to be utf8 paths.
300
On Linux, you can access these paths directly over the bytestream
301
request, but on win32, you must decode them, and access them
304
# abandon query parameters
305
path = urlparse.urlparse(path)[2]
306
path = posixpath.normpath(urllib.unquote(path))
307
path = path.decode('utf-8')
308
words = path.split('/')
309
words = filter(None, words)
355
312
drive, word = os.path.splitdrive(word)
356
head, word = os.path.split(word)
357
if word in (os.curdir, os.pardir): continue
358
path = os.path.join(path, word)
313
head, word = os.path.split(word)
314
if word in (os.curdir, os.pardir): continue
315
path = os.path.join(path, word)
362
319
class TestingHTTPServerMixin:
367
324
# server), allowing dynamic behaviors to be defined from
368
325
# the tests cases.
369
326
self.test_case_server = test_case_server
370
self._home_dir = test_case_server._home_dir
373
class TestingHTTPServer(test_server.TestingTCPServer, TestingHTTPServerMixin):
329
"""Called to clean-up the server.
331
Since the server may be (surely is, even) in a blocking listen, we
332
shutdown its socket before closing it.
334
# Note that is this executed as part of the implicit tear down in the
335
# main thread while the server runs in its own thread. The clean way
336
# to tear down the server is to instruct him to stop accepting
337
# connections and wait for the current connection(s) to end
338
# naturally. To end the connection naturally, the http transports
339
# should close their socket when they do not need to talk to the
340
# server anymore. This happens naturally during the garbage collection
341
# phase of the test transport objetcs (the server clients), so we
342
# don't have to worry about them. So, for the server, we must tear
343
# down here, from the main thread, when the test have ended. Note
344
# that since the server is in a blocking operation and since python
345
# use select internally, shutting down the socket is reliable and
348
self.socket.shutdown(socket.SHUT_RDWR)
349
except socket.error, e:
350
# WSAENOTCONN (10057) 'Socket is not connected' is harmless on
351
# windows (occurs before the first connection attempt
353
if not len(e.args) or e.args[0] != 10057:
355
# Let the server properly close the socket
358
class TestingHTTPServer(SocketServer.TCPServer, TestingHTTPServerMixin):
375
360
def __init__(self, server_address, request_handler_class,
376
361
test_case_server):
377
test_server.TestingTCPServer.__init__(self, server_address,
378
request_handler_class)
379
362
TestingHTTPServerMixin.__init__(self, test_case_server)
382
class TestingThreadingHTTPServer(test_server.TestingThreadingTCPServer,
363
SocketServer.TCPServer.__init__(self, server_address,
364
request_handler_class)
367
class TestingThreadingHTTPServer(SocketServer.ThreadingTCPServer,
383
368
TestingHTTPServerMixin):
384
369
"""A threading HTTP test server for HTTP 1.1.
387
372
server, we need an independent connection for each of them. We achieve that
388
373
by spawning a new thread for each connection.
390
376
def __init__(self, server_address, request_handler_class,
391
377
test_case_server):
392
test_server.TestingThreadingTCPServer.__init__(self, server_address,
393
request_handler_class)
394
378
TestingHTTPServerMixin.__init__(self, test_case_server)
397
class HttpServer(test_server.TestingTCPServerInAThread):
379
SocketServer.ThreadingTCPServer.__init__(self, server_address,
380
request_handler_class)
381
# Decides how threads will act upon termination of the main
382
# process. This is prophylactic as we should not leave the threads
384
self.daemon_threads = True
387
class HttpServer(transport.Server):
398
388
"""A test server for http transports.
400
390
Subclasses can provide a specific request handler.
422
412
:param protocol_version: if specified, will override the protocol
423
413
version of the request handler.
425
# Depending on the protocol version, we will create the approriate
427
if protocol_version is None:
428
# Use the request handler one
429
proto_vers = request_handler.protocol_version
431
# Use our own, it will be used to override the request handler
433
proto_vers = protocol_version
434
# Get the appropriate server class for the required protocol
435
serv_cls = self.http_server_class.get(proto_vers, None)
437
raise httplib.UnknownProtocol(proto_vers)
415
transport.Server.__init__(self)
416
self.request_handler = request_handler
438
417
self.host = 'localhost'
440
super(HttpServer, self).__init__((self.host, self.port),
443
self.protocol_version = proto_vers
420
self.protocol_version = protocol_version
444
421
# Allows tests to verify number of GET requests issued
445
422
self.GET_request_nb = 0
446
self._http_base_url = None
449
def create_server(self):
450
return self.server_class(
451
(self.host, self.port), self.request_handler_class, self)
424
def _get_httpd(self):
425
if self._httpd is None:
426
rhandler = self.request_handler
427
# Depending on the protocol version, we will create the approriate
429
if self.protocol_version is None:
430
# Use the request handler one
431
proto_vers = rhandler.protocol_version
433
# Use our own, it will be used to override the request handler
435
proto_vers = self.protocol_version
436
# Create the appropriate server for the required protocol
437
serv_cls = self.http_server_class.get(proto_vers, None)
439
raise httplib.UnknownProtocol(proto_vers)
441
self._httpd = serv_cls((self.host, self.port), rhandler, self)
442
host, self.port = self._httpd.socket.getsockname()
445
def _http_start(self):
446
"""Server thread main entry point. """
447
self._http_running = False
450
httpd = self._get_httpd()
451
self._http_base_url = '%s://%s:%s/' % (self._url_protocol,
452
self.host, self.port)
453
self._http_running = True
455
# Whatever goes wrong, we save the exception for the main
456
# thread. Note that since we are running in a thread, no signal
457
# can be received, so we don't care about KeyboardInterrupt.
458
self._http_exception = sys.exc_info()
460
# Release the lock or the main thread will block and the whole
462
self._http_starting.release()
464
# From now on, exceptions are taken care of by the
465
# SocketServer.BaseServer or the request handler.
466
while self._http_running:
468
# Really an HTTP connection but the python framework is generic
469
# and call them requests
470
httpd.handle_request()
471
except socket.timeout:
453
474
def _get_remote_url(self, path):
454
475
path_parts = path.split(os.path.sep)
466
487
"""Capture Server log output."""
467
488
self.logs.append(format % args)
469
def start_server(self, backing_transport_server=None):
470
"""See bzrlib.transport.Server.start_server.
490
def setUp(self, backing_transport_server=None):
491
"""See bzrlib.transport.Server.setUp.
472
493
:param backing_transport_server: The transport that requests over this
473
494
protocol should be forwarded to. Note that this is currently not
474
495
supported for HTTP.
476
497
# XXX: TODO: make the server back onto vfs_server rather than local
478
if not (backing_transport_server is None
479
or isinstance(backing_transport_server,
480
test_server.LocalURLServer)):
481
raise AssertionError(
482
"HTTPServer currently assumes local transport, got %s" %
483
backing_transport_server)
499
assert backing_transport_server is None or \
500
isinstance(backing_transport_server, local.LocalURLServer), \
501
"HTTPServer currently assumes local transport, got %s" % \
502
backing_transport_server
484
503
self._home_dir = os.getcwdu()
485
504
self._local_path_parts = self._home_dir.split(os.path.sep)
505
self._http_base_url = None
507
# Create the server thread
508
self._http_starting = threading.Lock()
509
self._http_starting.acquire()
510
self._http_thread = threading.Thread(target=self._http_start)
511
self._http_thread.setDaemon(True)
512
self._http_exception = None
513
self._http_thread.start()
515
# Wait for the server thread to start (i.e release the lock)
516
self._http_starting.acquire()
518
if self._http_exception is not None:
519
# Something went wrong during server start
520
exc_class, exc_value, exc_tb = self._http_exception
521
raise exc_class, exc_value, exc_tb
522
self._http_starting.release()
488
super(HttpServer, self).start_server()
489
self._http_base_url = '%s://%s:%s/' % (
490
self._url_protocol, self.host, self.port)
526
"""See bzrlib.transport.Server.tearDown."""
527
self._httpd.tearDown()
528
self._http_running = False
529
# We don't need to 'self._http_thread.join()' here since the thread is
530
# a daemonic one and will be garbage collected anyway. Joining just
531
# slows us down for no added benefit.
492
533
def get_url(self):
493
534
"""See bzrlib.transport.Server.get_url."""