233
235
def start_server(self, backing_server=None):
234
236
"""Setup the Chroot on backing_server."""
235
237
if backing_server is not None:
236
self.backing_transport = transport.get_transport(
238
self.backing_transport = transport.get_transport_from_url(
237
239
backing_server.get_url())
239
self.backing_transport = transport.get_transport('.')
241
self.backing_transport = transport.get_transport_from_path('.')
240
242
super(TestingChrootServer, self).start_server()
242
244
def get_bogus_url(self):
243
245
raise NotImplementedError
246
class ThreadWithException(threading.Thread):
247
"""A catching exception thread.
249
If an exception occurs during the thread execution, it's caught and
250
re-raised when the thread is joined().
253
def __init__(self, *args, **kwargs):
254
# There are cases where the calling thread must wait, yet, if an
255
# exception occurs, the event should be set so the caller is not
256
# blocked. The main example is a calling thread that want to wait for
257
# the called thread to be in a given state before continuing.
259
event = kwargs.pop('event')
261
# If the caller didn't pass a specific event, create our own
262
event = threading.Event()
263
super(ThreadWithException, self).__init__(*args, **kwargs)
264
self.set_ready_event(event)
265
self.exception = None
266
self.ignored_exceptions = None # see set_ignored_exceptions
268
# compatibility thunk for python-2.4 and python-2.5...
269
if sys.version_info < (2, 6):
270
name = property(threading.Thread.getName, threading.Thread.setName)
272
def set_ready_event(self, event):
273
"""Set the ``ready`` event used to synchronize exception catching.
275
When the thread uses an event to synchronize itself with another thread
276
(setting it when the other thread can wake up from a ``wait`` call),
277
the event must be set after catching an exception or the other thread
280
Some threads require multiple events and should set the relevant one
285
def set_ignored_exceptions(self, ignored):
286
"""Declare which exceptions will be ignored.
288
:param ignored: Can be either:
289
- None: all exceptions will be raised,
290
- an exception class: the instances of this class will be ignored,
291
- a tuple of exception classes: the instances of any class of the
292
list will be ignored,
293
- a callable: that will be passed the exception object
294
and should return True if the exception should be ignored
297
self.ignored_exceptions = None
298
elif isinstance(ignored, (Exception, tuple)):
299
self.ignored_exceptions = lambda e: isinstance(e, ignored)
301
self.ignored_exceptions = ignored
304
"""Overrides Thread.run to capture any exception."""
308
super(ThreadWithException, self).run()
310
self.exception = sys.exc_info()
312
# Make sure the calling thread is released
248
class TestThread(cethread.CatchingExceptionThread):
316
250
def join(self, timeout=5):
317
"""Overrides Thread.join to raise any exception caught.
320
Calling join(timeout=0) will raise the caught exception or return None
321
if the thread is still alive.
251
"""Overrides to use a default timeout.
323
253
The default timeout is set to 5 and should expire only when a thread
324
254
serving a client connection is hung.
326
super(ThreadWithException, self).join(timeout)
327
if self.exception is not None:
328
exc_class, exc_value, exc_tb = self.exception
329
self.exception = None # The exception should be raised only once
330
if (self.ignored_exceptions is None
331
or not self.ignored_exceptions(exc_value)):
332
# Raise non ignored exceptions
333
raise exc_class, exc_value, exc_tb
256
super(TestThread, self).join(timeout)
334
257
if timeout and self.isAlive():
335
258
# The timeout expired without joining the thread, the thread is
336
259
# therefore stucked and that's a failure as far as the test is
418
334
# The following can be used for debugging purposes, it will display the
419
335
# exception and the traceback just when it occurs instead of waiting
420
336
# for the thread to be joined.
422
337
# SocketServer.BaseServer.handle_error(self, request, client_address)
339
# We call close_request manually, because we are going to raise an
340
# exception. The SocketServer implementation calls:
343
# But because we raise the exception, close_request will never be
344
# triggered. This helps client not block waiting for a response when
345
# the server gets an exception.
346
self.close_request(request)
425
349
def ignored_exceptions_during_shutdown(self, e):
505
429
SocketServer.ThreadingTCPServer.__init__(self, server_address,
506
430
request_handler_class)
508
def get_request (self):
432
def get_request(self):
509
433
"""Get the request and client address from the socket."""
510
434
sock, addr = TestingTCPServerMixin.get_request(self)
511
# The thread is not create yet, it will be updated in process_request
435
# The thread is not created yet, it will be updated in process_request
512
436
self.clients.append((sock, addr, None))
513
437
return sock, addr
515
def process_request_thread(self, started, stopped, request, client_address):
439
def process_request_thread(self, started, detached, stopped,
440
request, client_address):
442
# We will be on our own once the server tells us we're detached
517
444
SocketServer.ThreadingTCPServer.process_request_thread(
518
445
self, request, client_address)
519
446
self.close_request(request)
522
449
def process_request(self, request, client_address):
523
450
"""Start a new thread to process the request."""
524
451
started = threading.Event()
452
detached = threading.Event()
525
453
stopped = threading.Event()
526
t = ThreadWithException(
528
456
name='%s -> %s' % (client_address, self.server_address),
529
457
target = self.process_request_thread,
530
args = (started, stopped, request, client_address))
458
args = (started, detached, stopped, request, client_address))
531
459
# Update the client description
532
460
self.clients.pop()
533
461
self.clients.append((request, client_address, t))
534
# Propagate the exception handler since we must use the same one for
535
# connections running in their own threads than TestingTCPServer.
462
# Propagate the exception handler since we must use the same one as
463
# TestingTCPServer for connections running in their own threads.
536
464
t.set_ignored_exceptions(self.ignored_exceptions)
540
sys.stderr.write('Client thread %s started\n' % (t.name,))
541
467
# If an exception occured during the thread start, it will get raised.
542
468
t.pending_exception()
470
sys.stderr.write('Client thread %s started\n' % (t.name,))
471
# Tell the thread, it's now on its own for exception handling.
544
474
# The following methods are called by the main thread
675
605
def __init__(self, request, client_address, server):
676
606
medium.SmartServerSocketStreamMedium.__init__(
677
607
self, request, server.backing_transport,
678
server.root_client_path)
608
server.root_client_path,
609
timeout=_DEFAULT_TESTING_CLIENT_TIMEOUT)
679
610
request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
680
611
SocketServer.BaseRequestHandler.__init__(self, request, client_address,
683
614
def handle(self):
684
while not self.finished:
685
server_protocol = self._build_protocol()
686
self._serve_one_request(server_protocol)
616
while not self.finished:
617
server_protocol = self._build_protocol()
618
self._serve_one_request(server_protocol)
619
except errors.ConnectionTimeout:
620
# idle connections aren't considered a failure of the server
624
_DEFAULT_TESTING_CLIENT_TIMEOUT = 60.0
689
626
class TestingSmartServer(TestingThreadingTCPServer, server.SmartTCPServer):