101
88
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
91
_range_regexp = re.compile(r'^(?P<start>\d+)-(?P<end>\d+)$')
143
92
_tail_regexp = re.compile(r'^-(?P<tail>\d+)$')
305
254
if len(ranges) == 1:
306
255
(start, end) = ranges[0]
307
self.get_single_range(f, file_size, start, end)
256
self.get_single_range(file, file_size, start, end)
309
self.get_multiple_ranges(f, file_size, ranges)
258
self.get_multiple_ranges(file, file_size, ranges)
312
261
def translate_path(self, path):
313
262
"""Translate a /-separated PATH to the local filename syntax.
369
318
self.test_case_server = test_case_server
370
319
self._home_dir = test_case_server._home_dir
373
class TestingHTTPServer(test_server.TestingTCPServer, TestingHTTPServerMixin):
322
"""Called to clean-up the server.
324
Since the server may be (surely is, even) in a blocking listen, we
325
shutdown its socket before closing it.
327
# Note that is this executed as part of the implicit tear down in the
328
# main thread while the server runs in its own thread. The clean way
329
# to tear down the server is to instruct him to stop accepting
330
# connections and wait for the current connection(s) to end
331
# naturally. To end the connection naturally, the http transports
332
# should close their socket when they do not need to talk to the
333
# server anymore. This happens naturally during the garbage collection
334
# phase of the test transport objetcs (the server clients), so we
335
# don't have to worry about them. So, for the server, we must tear
336
# down here, from the main thread, when the test have ended. Note
337
# that since the server is in a blocking operation and since python
338
# use select internally, shutting down the socket is reliable and
341
self.socket.shutdown(socket.SHUT_RDWR)
342
except socket.error, e:
343
# WSAENOTCONN (10057) 'Socket is not connected' is harmless on
344
# windows (occurs before the first connection attempt
347
# 'Socket is not connected' can also occur on OSX, with a
348
# "regular" ENOTCONN (when something went wrong during test case
349
# setup leading to self.setUp() *not* being called but
350
# self.tearDown() still being called -- vila20081106
351
if not len(e.args) or e.args[0] not in (errno.ENOTCONN, 10057):
353
# Let the server properly close the socket
357
class TestingHTTPServer(SocketServer.TCPServer, TestingHTTPServerMixin):
375
359
def __init__(self, server_address, request_handler_class,
376
360
test_case_server):
377
test_server.TestingTCPServer.__init__(self, server_address,
378
request_handler_class)
379
361
TestingHTTPServerMixin.__init__(self, test_case_server)
382
class TestingThreadingHTTPServer(test_server.TestingThreadingTCPServer,
362
SocketServer.TCPServer.__init__(self, server_address,
363
request_handler_class)
366
class TestingThreadingHTTPServer(SocketServer.ThreadingTCPServer,
383
367
TestingHTTPServerMixin):
384
368
"""A threading HTTP test server for HTTP 1.1.
387
371
server, we need an independent connection for each of them. We achieve that
388
372
by spawning a new thread for each connection.
390
375
def __init__(self, server_address, request_handler_class,
391
376
test_case_server):
392
test_server.TestingThreadingTCPServer.__init__(self, server_address,
393
request_handler_class)
394
377
TestingHTTPServerMixin.__init__(self, test_case_server)
397
class HttpServer(test_server.TestingTCPServerInAThread):
378
SocketServer.ThreadingTCPServer.__init__(self, server_address,
379
request_handler_class)
380
# Decides how threads will act upon termination of the main
381
# process. This is prophylactic as we should not leave the threads
383
self.daemon_threads = True
386
class HttpServer(transport.Server):
398
387
"""A test server for http transports.
400
389
Subclasses can provide a specific request handler.
422
411
:param protocol_version: if specified, will override the protocol
423
412
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)
414
transport.Server.__init__(self)
415
self.request_handler = request_handler
438
416
self.host = 'localhost'
440
super(HttpServer, self).__init__((self.host, self.port),
443
self.protocol_version = proto_vers
419
self.protocol_version = protocol_version
444
420
# Allows tests to verify number of GET requests issued
445
421
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)
423
def create_httpd(self, serv_cls, rhandler_cls):
424
return serv_cls((self.host, self.port), self.request_handler, self)
427
return "%s(%s:%s)" % \
428
(self.__class__.__name__, self.host, self.port)
430
def _get_httpd(self):
431
if self._httpd is None:
432
rhandler = self.request_handler
433
# Depending on the protocol version, we will create the approriate
435
if self.protocol_version is None:
436
# Use the request handler one
437
proto_vers = rhandler.protocol_version
439
# Use our own, it will be used to override the request handler
441
proto_vers = self.protocol_version
442
# Create the appropriate server for the required protocol
443
serv_cls = self.http_server_class.get(proto_vers, None)
445
raise httplib.UnknownProtocol(proto_vers)
447
self._httpd = self.create_httpd(serv_cls, rhandler)
448
host, self.port = self._httpd.socket.getsockname()
451
def _http_start(self):
452
"""Server thread main entry point. """
453
self._http_running = False
456
httpd = self._get_httpd()
457
self._http_base_url = '%s://%s:%s/' % (self._url_protocol,
458
self.host, self.port)
459
self._http_running = True
461
# Whatever goes wrong, we save the exception for the main
462
# thread. Note that since we are running in a thread, no signal
463
# can be received, so we don't care about KeyboardInterrupt.
464
self._http_exception = sys.exc_info()
466
# Release the lock or the main thread will block and the whole
468
self._http_starting.release()
470
# From now on, exceptions are taken care of by the
471
# SocketServer.BaseServer or the request handler.
472
while self._http_running:
474
# Really an HTTP connection but the python framework is generic
475
# and call them requests
476
httpd.handle_request()
477
except socket.timeout:
479
except (socket.error, select.error), e:
480
if e[0] == errno.EBADF:
481
# Starting with python-2.6, handle_request may raise socket
482
# or select exceptions when the server is shut down (as we
453
488
def _get_remote_url(self, path):
454
489
path_parts = path.split(os.path.sep)
476
511
# 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)):
513
if not (backing_transport_server is None or \
514
isinstance(backing_transport_server, local.LocalURLServer)):
481
515
raise AssertionError(
482
"HTTPServer currently assumes local transport, got %s" %
516
"HTTPServer currently assumes local transport, got %s" % \
483
517
backing_transport_server)
484
518
self._home_dir = os.getcwdu()
485
519
self._local_path_parts = self._home_dir.split(os.path.sep)
520
self._http_base_url = None
522
# Create the server thread
523
self._http_starting = threading.Lock()
524
self._http_starting.acquire()
525
self._http_thread = threading.Thread(target=self._http_start)
526
self._http_thread.setDaemon(True)
527
self._http_exception = None
528
self._http_thread.start()
530
# Wait for the server thread to start (i.e release the lock)
531
self._http_starting.acquire()
533
if self._http_exception is not None:
534
# Something went wrong during server start
535
exc_class, exc_value, exc_tb = self._http_exception
536
raise exc_class, exc_value, exc_tb
537
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)
541
"""See bzrlib.transport.Server.tearDown."""
542
self._httpd.tearDown()
543
self._http_running = False
544
# We don't need to 'self._http_thread.join()' here since the thread is
545
# a daemonic one and will be garbage collected anyway. Joining just
546
# slows us down for no added benefit.
492
548
def get_url(self):
493
549
"""See bzrlib.transport.Server.get_url."""