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):
523
447
"""Start a new thread to process the request."""
524
448
started = threading.Event()
525
449
stopped = threading.Event()
526
t = ThreadWithException(
528
452
name='%s -> %s' % (client_address, self.server_address),
529
target = self.process_request_thread,
530
args = (started, stopped, request, client_address))
453
target=self.process_request_thread,
454
args=(started, stopped, request, client_address))
531
455
# Update the client description
532
456
self.clients.pop()
533
457
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.
458
# Propagate the exception handler since we must use the same one as
459
# TestingTCPServer for connections running in their own threads.
536
460
t.set_ignored_exceptions(self.ignored_exceptions)
539
463
if debug_threads():
540
464
sys.stderr.write('Client thread %s started\n' % (t.name,))
541
465
# If an exception occured during the thread start, it will get raised.
466
# In rare cases, an exception raised during the request processing may
467
# also get caught here (see http://pad.lv/869366)
542
468
t.pending_exception()
544
470
# The following methods are called by the main thread
675
601
def __init__(self, request, client_address, server):
676
602
medium.SmartServerSocketStreamMedium.__init__(
677
603
self, request, server.backing_transport,
678
server.root_client_path)
604
server.root_client_path,
605
timeout=_DEFAULT_TESTING_CLIENT_TIMEOUT)
679
606
request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
680
607
SocketServer.BaseRequestHandler.__init__(self, request, client_address,
683
610
def handle(self):
684
while not self.finished:
685
server_protocol = self._build_protocol()
686
self._serve_one_request(server_protocol)
612
while not self.finished:
613
server_protocol = self._build_protocol()
614
self._serve_one_request(server_protocol)
615
except errors.ConnectionTimeout:
616
# idle connections aren't considered a failure of the server
620
_DEFAULT_TESTING_CLIENT_TIMEOUT = 60.0
689
622
class TestingSmartServer(TestingThreadingTCPServer, server.SmartTCPServer):