/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
1
# Copyright (C) 2010 Canonical Ltd
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
17
import errno
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
18
import socket
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
19
import SocketServer
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
20
import select
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
21
import sys
22
import threading
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
23
24
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
25
from bzrlib import (
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
26
    osutils,
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
27
    transport,
5017.3.15 by Vincent Ladeuil
Fix missing import.
28
    urlutils,
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
29
    )
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
30
from bzrlib.transport import (
5017.3.20 by Vincent Ladeuil
Move TestingChrootServer to bzrlib.tests.test_server
31
    chroot,
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
32
    pathfilter,
33
    )
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
34
from bzrlib.smart import server
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
35
36
5247.5.17 by Vincent Ladeuil
Add some basic debug tracing controlled by -Ethreads.
37
def debug_threads():
38
    # FIXME: There is a dependency loop between bzrlib.tests and
39
    # bzrlib.tests.test_server that needs to be fixed. In the mean time
40
    # defining this function is enough for our needs. -- vila 20100611
41
    from bzrlib import tests
42
    return 'threads' in tests.selftest_debug_flags
43
44
5017.3.1 by Vincent Ladeuil
Create a tests.test_server.TestServer class out of transport.Server (while retaining the later for some special non-tests usages).
45
class TestServer(transport.Server):
46
    """A Transport Server dedicated to tests.
47
48
    The TestServer interface provides a server for a given transport. We use
49
    these servers as loopback testing tools. For any given transport the
50
    Servers it provides must either allow writing, or serve the contents
51
    of os.getcwdu() at the time start_server is called.
52
53
    Note that these are real servers - they must implement all the things
54
    that we want bzr transports to take advantage of.
55
    """
56
57
    def get_url(self):
58
        """Return a url for this server.
59
60
        If the transport does not represent a disk directory (i.e. it is
61
        a database like svn, or a memory only transport, it should return
62
        a connection to a newly established resource for this Server.
63
        Otherwise it should return a url that will provide access to the path
64
        that was os.getcwdu() when start_server() was called.
65
66
        Subsequent calls will return the same resource.
67
        """
68
        raise NotImplementedError
69
70
    def get_bogus_url(self):
71
        """Return a url for this protocol, that will fail to connect.
72
73
        This may raise NotImplementedError to indicate that this server cannot
74
        provide bogus urls.
75
        """
76
        raise NotImplementedError
77
78
5017.3.6 by Vincent Ladeuil
Fix some fallouts of moving test servers around.
79
class LocalURLServer(TestServer):
5017.3.3 by Vincent Ladeuil
Move LocalURLServer to bzrlib.tests.test_server
80
    """A pretend server for local transports, using file:// urls.
81
82
    Of course no actual server is required to access the local filesystem, so
83
    this just exists to tell the test code how to get to it.
84
    """
85
86
    def start_server(self):
87
        pass
88
89
    def get_url(self):
90
        """See Transport.Server.get_url."""
91
        return urlutils.local_path_to_url('')
92
93
5017.3.6 by Vincent Ladeuil
Fix some fallouts of moving test servers around.
94
class DecoratorServer(TestServer):
5017.3.2 by Vincent Ladeuil
Move DecoratorServer to test_server.py
95
    """Server for the TransportDecorator for testing with.
96
97
    To use this when subclassing TransportDecorator, override override the
98
    get_decorator_class method.
99
    """
100
101
    def start_server(self, server=None):
102
        """See bzrlib.transport.Server.start_server.
103
104
        :server: decorate the urls given by server. If not provided a
105
        LocalServer is created.
106
        """
107
        if server is not None:
108
            self._made_server = False
109
            self._server = server
110
        else:
111
            self._made_server = True
112
            self._server = LocalURLServer()
113
            self._server.start_server()
114
115
    def stop_server(self):
116
        if self._made_server:
117
            self._server.stop_server()
118
119
    def get_decorator_class(self):
120
        """Return the class of the decorators we should be constructing."""
121
        raise NotImplementedError(self.get_decorator_class)
122
123
    def get_url_prefix(self):
124
        """What URL prefix does this decorator produce?"""
125
        return self.get_decorator_class()._get_url_prefix()
126
127
    def get_bogus_url(self):
128
        """See bzrlib.transport.Server.get_bogus_url."""
129
        return self.get_url_prefix() + self._server.get_bogus_url()
130
131
    def get_url(self):
132
        """See bzrlib.transport.Server.get_url."""
133
        return self.get_url_prefix() + self._server.get_url()
134
135
5017.3.8 by Vincent Ladeuil
Move BrokenRenameServer to bzrlib.tests.test_server
136
class BrokenRenameServer(DecoratorServer):
137
    """Server for the BrokenRenameTransportDecorator for testing with."""
138
139
    def get_decorator_class(self):
140
        from bzrlib.transport import brokenrename
141
        return brokenrename.BrokenRenameTransportDecorator
142
143
5017.3.7 by Vincent Ladeuil
Move FakeNFSServer to bzrlib.tests.test_server
144
class FakeNFSServer(DecoratorServer):
145
    """Server for the FakeNFSTransportDecorator for testing with."""
146
147
    def get_decorator_class(self):
148
        from bzrlib.transport import fakenfs
149
        return fakenfs.FakeNFSTransportDecorator
150
151
5017.3.9 by Vincent Ladeuil
Move FakeVFATServer to bzrlib.tests.test_server
152
class FakeVFATServer(DecoratorServer):
153
    """A server that suggests connections through FakeVFATTransportDecorator
154
155
    For use in testing.
156
    """
157
158
    def get_decorator_class(self):
159
        from bzrlib.transport import fakevfat
5017.3.14 by Vincent Ladeuil
Fix some missing prefixes.
160
        return fakevfat.FakeVFATTransportDecorator
5017.3.9 by Vincent Ladeuil
Move FakeVFATServer to bzrlib.tests.test_server
161
162
5017.3.11 by Vincent Ladeuil
Move LogDecoratorServer to bzrlib.tests.test_server
163
class LogDecoratorServer(DecoratorServer):
164
    """Server for testing."""
165
166
    def get_decorator_class(self):
167
        from bzrlib.transport import log
168
        return log.TransportLogDecorator
169
170
5017.3.12 by Vincent Ladeuil
Move NoSmartTransportServer to bzrlib.tests.test_server
171
class NoSmartTransportServer(DecoratorServer):
172
    """Server for the NoSmartTransportDecorator for testing with."""
173
174
    def get_decorator_class(self):
175
        from bzrlib.transport import nosmart
5017.3.14 by Vincent Ladeuil
Fix some missing prefixes.
176
        return nosmart.NoSmartTransportDecorator
5017.3.12 by Vincent Ladeuil
Move NoSmartTransportServer to bzrlib.tests.test_server
177
178
5017.3.5 by Vincent Ladeuil
Move ReadonlyServer to bzrlib.tests.readonly
179
class ReadonlyServer(DecoratorServer):
180
    """Server for the ReadonlyTransportDecorator for testing with."""
181
182
    def get_decorator_class(self):
183
        from bzrlib.transport import readonly
184
        return readonly.ReadonlyTransportDecorator
185
186
5017.3.10 by Vincent Ladeuil
Move TraceServer to bzrlib.tests.test_server
187
class TraceServer(DecoratorServer):
188
    """Server for the TransportTraceDecorator for testing with."""
189
190
    def get_decorator_class(self):
191
        from bzrlib.transport import trace
5017.3.14 by Vincent Ladeuil
Fix some missing prefixes.
192
        return trace.TransportTraceDecorator
5017.3.10 by Vincent Ladeuil
Move TraceServer to bzrlib.tests.test_server
193
194
5017.3.13 by Vincent Ladeuil
Move UnlistableServer to bzrlib.tests.test_server
195
class UnlistableServer(DecoratorServer):
196
    """Server for the UnlistableTransportDecorator for testing with."""
197
198
    def get_decorator_class(self):
199
        from bzrlib.transport import unlistable
200
        return unlistable.UnlistableTransportDecorator
201
202
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
203
class TestingPathFilteringServer(pathfilter.PathFilteringServer):
204
205
    def __init__(self):
5017.3.20 by Vincent Ladeuil
Move TestingChrootServer to bzrlib.tests.test_server
206
        """TestingPathFilteringServer is not usable until start_server
207
        is called."""
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
208
209
    def start_server(self, backing_server=None):
210
        """Setup the Chroot on backing_server."""
211
        if backing_server is not None:
212
            self.backing_transport = transport.get_transport(
213
                backing_server.get_url())
214
        else:
215
            self.backing_transport = transport.get_transport('.')
216
        self.backing_transport.clone('added-by-filter').ensure_base()
217
        self.filter_func = lambda x: 'added-by-filter/' + x
218
        super(TestingPathFilteringServer, self).start_server()
219
5017.3.20 by Vincent Ladeuil
Move TestingChrootServer to bzrlib.tests.test_server
220
    def get_bogus_url(self):
221
        raise NotImplementedError
222
223
224
class TestingChrootServer(chroot.ChrootServer):
225
226
    def __init__(self):
227
        """TestingChrootServer is not usable until start_server is called."""
228
        super(TestingChrootServer, self).__init__(None)
229
230
    def start_server(self, backing_server=None):
231
        """Setup the Chroot on backing_server."""
232
        if backing_server is not None:
233
            self.backing_transport = transport.get_transport(
234
                backing_server.get_url())
235
        else:
236
            self.backing_transport = transport.get_transport('.')
237
        super(TestingChrootServer, self).start_server()
238
239
    def get_bogus_url(self):
240
        raise NotImplementedError
241
5017.3.19 by Vincent Ladeuil
Move TestingPathFilteringServer to bzrlib.tests.test_server
242
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
243
class ThreadWithException(threading.Thread):
244
    """A catching exception thread.
245
246
    If an exception occurs during the thread execution, it's caught and
247
    re-raised when the thread is joined().
248
    """
249
250
    def __init__(self, *args, **kwargs):
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
251
        # There are cases where the calling thread must wait, yet, if an
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
252
        # exception occurs, the event should be set so the caller is not
5247.2.5 by Vincent Ladeuil
Some cleanups.
253
        # blocked. The main example is a calling thread that want to wait for
254
        # the called thread to be in a given state before continuing.
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
255
        try:
256
            event = kwargs.pop('event')
257
        except KeyError:
258
            # If the caller didn't pass a specific event, create our own
259
            event = threading.Event()
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
260
        super(ThreadWithException, self).__init__(*args, **kwargs)
5247.3.10 by Vincent Ladeuil
Test errors during server life.
261
        self.set_event(event)
262
        self.exception = None
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
263
        self.ignored_exceptions = None # see set_ignored_exceptions
5247.3.10 by Vincent Ladeuil
Test errors during server life.
264
5247.5.18 by Vincent Ladeuil
Compatibility with python 2.5 and 2.4 for ThreadWithException.name.
265
    # compatibility thunk for python-2.4 and python-2.5...
5247.5.19 by Vincent Ladeuil
Cough, the compatibility thunk should not be installed for 2.6.
266
    if sys.version_info < (2, 6):
267
        name = property(threading.Thread.getName, threading.Thread.setName)
5247.5.18 by Vincent Ladeuil
Compatibility with python 2.5 and 2.4 for ThreadWithException.name.
268
5247.3.10 by Vincent Ladeuil
Test errors during server life.
269
    def set_event(self, event):
5247.2.5 by Vincent Ladeuil
Some cleanups.
270
        self.ready = event
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
271
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
272
    def set_ignored_exceptions(self, ignored):
273
        """Declare which exceptions will be ignored.
274
275
        :param ignored: Can be either:
276
           - None: all exceptions will be raised,
277
           - an exception class: the instances of this class will be ignored,
278
           - a tuple of exception classes: the instances of any class of the
279
             list will be ignored,
5247.5.6 by Vincent Ladeuil
Just pass the exception object to simplify.
280
           - a callable: that will be passed the exception object
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
281
             and should return True if the exception should be ignored
282
        """
283
        if ignored is None:
284
            self.ignored_exceptions = None
5247.5.6 by Vincent Ladeuil
Just pass the exception object to simplify.
285
        elif isinstance(ignored, (Exception, tuple)):
286
            self.ignored_exceptions = lambda e: isinstance(e, ignored)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
287
        else:
288
            self.ignored_exceptions = ignored
289
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
290
    def run(self):
291
        """Overrides Thread.run to capture any exception."""
5247.2.5 by Vincent Ladeuil
Some cleanups.
292
        self.ready.clear()
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
293
        try:
5247.3.19 by Vincent Ladeuil
Fix python-2.4 incompatibility.
294
            try:
295
                super(ThreadWithException, self).run()
296
            except:
297
                self.exception = sys.exc_info()
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
298
        finally:
299
            # Make sure the calling thread is released
5247.2.5 by Vincent Ladeuil
Some cleanups.
300
            self.ready.set()
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
301
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
302
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
303
    def join(self, timeout=5):
5247.2.3 by Vincent Ladeuil
join(timeout=0) is useful to check for an exception without stopping the thread.
304
        """Overrides Thread.join to raise any exception caught.
305
306
307
        Calling join(timeout=0) will raise the caught exception or return None
5247.3.10 by Vincent Ladeuil
Test errors during server life.
308
        if the thread is still alive.
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
309
310
        The default timeout is set to 5 and should expire only when a thread
311
        serving a client connection is hung.
5247.2.3 by Vincent Ladeuil
join(timeout=0) is useful to check for an exception without stopping the thread.
312
        """
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
313
        super(ThreadWithException, self).join(timeout)
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
314
        if self.exception is not None:
5247.2.4 by Vincent Ladeuil
Add an event to ThreadWithException that can be shared with the calling thread.
315
            exc_class, exc_value, exc_tb = self.exception
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
316
            self.exception = None # The exception should be raised only once
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
317
            if (self.ignored_exceptions is None
5247.5.6 by Vincent Ladeuil
Just pass the exception object to simplify.
318
                or not self.ignored_exceptions(exc_value)):
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
319
                # Raise non ignored exceptions
320
                raise exc_class, exc_value, exc_tb
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
321
        if timeout and self.isAlive():
322
            # The timeout expired without joining the thread, the thread is
323
            # therefore stucked and that's a failure as far as the test is
324
            # concerned. We used to hang here.
325
            raise AssertionError('thread %s hung' % (self.name,))
326
327
    def pending_exception(self):
328
        """Raise the caught exception.
329
330
        This does nothing if no exception occurred.
331
        """
332
        self.join(timeout=0)
5247.2.2 by Vincent Ladeuil
Implement a thread that can re-raise exceptions.
333
334
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
335
class TestingTCPServerMixin:
336
    """Mixin to support running SocketServer.TCPServer in a thread.
337
338
    Tests are connecting from the main thread, the server has to be run in a
339
    separate thread.
340
    """
341
5247.3.16 by Vincent Ladeuil
All http tests passing (including https).
342
    # FIXME: sibling_class is a hack -- vila 20100604
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
343
    def __init__(self, sibling_class):
344
        self.sibling_class = sibling_class
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
345
        self.started = threading.Event()
346
        self.serving = threading.Event()
347
        self.stopped = threading.Event()
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
348
        # We collect the resources used by the clients so we can release them
349
        # when shutting down
350
        self.clients = []
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
351
        self.ignored_exceptions = None
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
352
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
353
    def server_bind(self):
354
        # We need to override the SocketServer bind, yet, we still want to use
355
        # it so we need to use the sibling class to call it explicitly
356
        self.sibling_class.server_bind(self)
357
        # The following has been fixed in 2.5 so we need to provide it for
358
        # older python versions.
359
        if sys.version < (2, 5):
360
            self.server_address = self.socket.getsockname()
361
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
362
    def serve(self):
363
        self.serving.set()
364
        self.stopped.clear()
365
        # We are listening and ready to accept connections
366
        self.started.set()
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
367
        try:
368
            while self.serving.isSet():
369
                # Really a connection but the python framework is generic and
370
                # call them requests
371
                self.handle_request()
372
            # Let's close the listening socket
373
            self.server_close()
374
        finally:
375
            self.stopped.set()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
376
5247.5.10 by Vincent Ladeuil
Fix broken test.
377
    def handle_request(self):
378
        """Handle one request.
379
380
        The python version swallows some socket exceptions and we don't use
381
        timeout, so we override it to better control the server behavior.
382
        """
383
        request, client_address = self.get_request()
384
        if self.verify_request(request, client_address):
385
            try:
386
                self.process_request(request, client_address)
387
            except:
388
                self.handle_error(request, client_address)
389
                self.close_request(request)
390
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
391
    def verify_request(self, request, client_address):
392
        """Verify the request.
393
394
        Return True if we should proceed with this request, False if we should
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
395
        not even touch a single byte in the socket ! This is useful when we
396
        stop the server with a dummy last connection.
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
397
        """
398
        return self.serving.isSet()
399
5247.3.10 by Vincent Ladeuil
Test errors during server life.
400
    def handle_error(self, request, client_address):
401
        # Stop serving and re-raise the last exception seen
402
        self.serving.clear()
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
403
#        self.sibling_class.handle_error(self, request, client_address)
5247.3.10 by Vincent Ladeuil
Test errors during server life.
404
        raise
405
5247.5.7 by Vincent Ladeuil
Factor out socket exception handling during server shutdown.
406
    def ignored_exceptions_during_shutdown(self, e):
407
        if sys.platform == 'win32':
5247.5.8 by Vincent Ladeuil
Thanks for being inconsistently inconsistent.
408
            accepted_errnos = [errno.EBADF, errno.WSAEBADF, errno.WSAENOTCONN,
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
409
                               errno.WSAECONNRESET, errno.WSAESHUTDOWN]
5247.5.7 by Vincent Ladeuil
Factor out socket exception handling during server shutdown.
410
        else:
411
            accepted_errnos = [errno.EBADF, errno.ENOTCONN, errno.ECONNRESET]
412
        if isinstance(e, socket.error) and e[0] in accepted_errnos:
413
            return True
414
        return False
415
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
416
    # The following methods are called by the main thread
417
418
    def stop_client_connections(self):
419
        while self.clients:
420
            c = self.clients.pop()
421
            self.shutdown_client(c)
422
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
423
    def shutdown_socket(self, sock):
424
        """Properly shutdown a socket.
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
425
426
        This should be called only when no other thread is trying to use the
427
        socket.
428
        """
429
        try:
430
            sock.shutdown(socket.SHUT_RDWR)
431
            sock.close()
5247.5.7 by Vincent Ladeuil
Factor out socket exception handling during server shutdown.
432
        except Exception, e:
433
            if self.ignored_exceptions(e):
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
434
                pass
435
            else:
436
                raise
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
437
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
438
    # The following methods are called by the main thread
439
440
    def set_ignored_exceptions(self, thread, ignored_exceptions):
441
        self.ignored_exceptions = ignored_exceptions
442
        thread.set_ignored_exceptions(self.ignored_exceptions)
443
444
    def _pending_exception(self, thread):
445
        """Raise server uncaught exception.
446
447
        Daughter classes can override this if they use daughter threads.
448
        """
449
        thread.pending_exception()
450
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
451
452
class TestingTCPServer(TestingTCPServerMixin, SocketServer.TCPServer):
453
454
    def __init__(self, server_address, request_handler_class):
455
        TestingTCPServerMixin.__init__(self, SocketServer.TCPServer)
456
        SocketServer.TCPServer.__init__(self, server_address,
457
                                        request_handler_class)
458
459
    def get_request(self):
460
        """Get the request and client address from the socket."""
461
        sock, addr = self.sibling_class.get_request(self)
462
        self.clients.append((sock, addr))
463
        return sock, addr
464
465
    # The following methods are called by the main thread
466
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
467
    def shutdown_client(self, client):
468
        sock, addr = client
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
469
        self.shutdown_socket(sock)
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
470
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
471
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
472
class TestingThreadingTCPServer(TestingTCPServerMixin,
473
                                SocketServer.ThreadingTCPServer):
474
475
    def __init__(self, server_address, request_handler_class):
476
        TestingTCPServerMixin.__init__(self, SocketServer.ThreadingTCPServer)
477
        SocketServer.TCPServer.__init__(self, server_address,
478
                                        request_handler_class)
479
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
480
    def get_request (self):
481
        """Get the request and client address from the socket."""
482
        sock, addr = self.sibling_class.get_request(self)
483
        # The thread is not create yet, it will be updated in process_request
484
        self.clients.append((sock, addr, None))
485
        return sock, addr
486
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
487
    def process_request_thread(self, started, stopped, request, client_address):
488
        started.set()
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
489
        SocketServer.ThreadingTCPServer.process_request_thread(
490
            self, request, client_address)
491
        self.close_request(request)
492
        stopped.set()
493
494
    def process_request(self, request, client_address):
495
        """Start a new thread to process the request."""
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
496
        started = threading.Event()
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
497
        stopped = threading.Event()
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
498
        t = ThreadWithException(
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
499
            event=stopped,
5247.5.17 by Vincent Ladeuil
Add some basic debug tracing controlled by -Ethreads.
500
            name='%s -> %s' % (client_address, self.server_address),
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
501
            target = self.process_request_thread,
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
502
            args = (started, stopped, request, client_address))
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
503
        # Update the client description
504
        self.clients.pop()
505
        self.clients.append((request, client_address, t))
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
506
        # Propagate the exception handler since we must the same one for
507
        # connections running in their own threads than TestingTCPServer.
508
        t.set_ignored_exceptions(self.ignored_exceptions)
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
509
        t.start()
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
510
        started.wait()
5247.5.17 by Vincent Ladeuil
Add some basic debug tracing controlled by -Ethreads.
511
        if debug_threads():
512
            print 'Client thread %s started' % (t.name,)
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
513
        # If an exception occured during the thread start, it will get raised.
514
        t.pending_exception()
515
516
    # The following methods are called by the main thread
517
518
    def shutdown_client(self, client):
5247.5.2 by Vincent Ladeuil
Cosmetic change.
519
        sock, addr, connection_thread = client
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
520
        self.shutdown_socket(sock)
5247.5.2 by Vincent Ladeuil
Cosmetic change.
521
        if connection_thread is not None:
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
522
            # The thread has been created only if the request is processed but
523
            # after the connection is inited. This could happen during server
524
            # shutdown. If an exception occurred in the thread it will be
525
            # re-raised
5247.5.17 by Vincent Ladeuil
Add some basic debug tracing controlled by -Ethreads.
526
            if debug_threads():
527
                print 'Client thread %s will be joined' % (
528
                    connection_thread.name,)
5247.5.2 by Vincent Ladeuil
Cosmetic change.
529
            connection_thread.join()
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
530
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
531
    def set_ignored_exceptions(self, thread, ignored_exceptions):
532
        TestingTCPServerMixin.set_ignored_exceptions(self, thread,
533
                                                     ignored_exceptions)
534
        for sock, addr, connection_thread in self.clients:
535
            if connection_thread is not None:
536
                connection_thread.set_ignored_exceptions(
537
                    self.ignored_exceptions)
538
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
539
    def _pending_exception(self, thread):
540
        for sock, addr, connection_thread in self.clients:
541
            if connection_thread is not None:
542
                connection_thread.pending_exception()
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
543
        TestingTCPServerMixin._pending_exception(self, thread)
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
544
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
545
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
546
class TestingTCPServerInAThread(transport.Server):
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
547
    """A server in a thread that re-raise thread exceptions."""
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
548
549
    def __init__(self, server_address, server_class, request_handler_class):
550
        self.server_class = server_class
551
        self.request_handler_class = request_handler_class
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
552
        self.host, self.port = server_address
5247.3.10 by Vincent Ladeuil
Test errors during server life.
553
        self.server = None
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
554
        self._server_thread = None
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
555
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
556
    def __repr__(self):
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
557
        return "%s(%s:%s)" % (self.__class__.__name__, self.host, self.port)
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
558
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
559
    def create_server(self):
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
560
        return self.server_class((self.host, self.port),
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
561
                                 self.request_handler_class)
562
563
    def start_server(self):
564
        self.server = self.create_server()
565
        self._server_thread = ThreadWithException(
5247.5.17 by Vincent Ladeuil
Add some basic debug tracing controlled by -Ethreads.
566
            event=self.server.started,
567
            target=self.run_server)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
568
        self._server_thread.start()
569
        # Wait for the server thread to start (i.e release the lock)
570
        self.server.started.wait()
571
        # Get the real address, especially the port
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
572
        self.host, self.port = self.server.server_address
5247.5.18 by Vincent Ladeuil
Compatibility with python 2.5 and 2.4 for ThreadWithException.name.
573
        self._server_thread.name = self.server.server_address
574
        if debug_threads():
575
            print 'Server thread %s started' % (self._server_thread.name,)
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
576
        # If an exception occured during the server start, it will get raised,
577
        # otherwise, the server is blocked on its accept() call.
578
        self._server_thread.pending_exception()
5247.3.10 by Vincent Ladeuil
Test errors during server life.
579
        # From now on, we'll use a different event to ensure the server can set
580
        # its exception
581
        self._server_thread.set_event(self.server.stopped)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
582
583
    def run_server(self):
584
        self.server.serve()
585
586
    def stop_server(self):
587
        if self.server is None:
588
            return
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
589
        try:
590
            # The server has been started successfully, shut it down now.  As
5247.5.10 by Vincent Ladeuil
Fix broken test.
591
            # soon as we stop serving, no more connection are accepted except
592
            # one to get out of the blocking listen.
5247.5.7 by Vincent Ladeuil
Factor out socket exception handling during server shutdown.
593
            self.set_ignored_exceptions(
594
                self.server.ignored_exceptions_during_shutdown)
5247.5.10 by Vincent Ladeuil
Fix broken test.
595
            self.server.serving.clear()
5247.5.17 by Vincent Ladeuil
Add some basic debug tracing controlled by -Ethreads.
596
            if debug_threads():
597
                print 'Server thread %s will be joined' % (
598
                    self._server_thread.name,)
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
599
            # The server is listening for a last connection, let's give it:
600
            last_conn = None
601
            try:
5247.3.15 by Vincent Ladeuil
All http tests passing, https failing.
602
                last_conn = osutils.connect_socket((self.host, self.port))
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
603
            except socket.error, e:
604
                # But ignore connection errors as the point is to unblock the
605
                # server thread, it may happen that it's not blocked or even
606
                # not started.
607
                pass
608
            # We start shutting down the client while the server itself is
609
            # shutting down.
610
            self.server.stop_client_connections()
611
            # Now we wait for the thread running self.server.serve() to finish
612
            self.server.stopped.wait()
613
            if last_conn is not None:
614
                # Close the last connection without trying to use it. The
615
                # server will not process a single byte on that socket to avoid
616
                # complications (SSL starts with a handshake for example).
617
                last_conn.close()
5247.3.10 by Vincent Ladeuil
Test errors during server life.
618
            # Check for any exception that could have occurred in the server
619
            # thread
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
620
            try:
621
                self._server_thread.join()
622
            except Exception, e:
623
                if self.server.ignored_exceptions(e):
624
                    pass
625
                else:
626
                    raise
5247.3.10 by Vincent Ladeuil
Test errors during server life.
627
        finally:
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
628
            # Make sure we can be called twice safely, note that this means
629
            # that we will raise a single exception even if several occurred in
630
            # the various threads involved.
5247.3.10 by Vincent Ladeuil
Test errors during server life.
631
            self.server = None
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
632
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
633
    def set_ignored_exceptions(self, ignored_exceptions):
634
        """Install an exception handler for the server."""
635
        self.server.set_ignored_exceptions(self._server_thread,
636
                                           ignored_exceptions)
637
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
638
    def pending_exception(self):
639
        """Raise uncaught exception in the server."""
640
        self.server._pending_exception(self._server_thread)
641
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
642
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
643
class SmartTCPServer_for_testing(server.SmartTCPServer):
644
    """Server suitable for use by transport tests.
645
646
    This server is backed by the process's cwd.
647
    """
648
649
    def __init__(self, thread_name_suffix=''):
650
        super(SmartTCPServer_for_testing, self).__init__(None)
651
        self.client_path_extra = None
652
        self.thread_name_suffix = thread_name_suffix
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
653
        # We collect the sockets/threads used by the clients so we can
654
        # close/join them when shutting down
655
        self.clients = []
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
656
657
    def get_backing_transport(self, backing_transport_server):
658
        """Get a backing transport from a server we are decorating."""
659
        return transport.get_transport(backing_transport_server.get_url())
660
661
    def start_server(self, backing_transport_server=None,
662
              client_path_extra='/extra/'):
663
        """Set up server for testing.
664
665
        :param backing_transport_server: backing server to use.  If not
666
            specified, a LocalURLServer at the current working directory will
667
            be used.
668
        :param client_path_extra: a path segment starting with '/' to append to
669
            the root URL for this server.  For instance, a value of '/foo/bar/'
670
            will mean the root of the backing transport will be published at a
671
            URL like `bzr://127.0.0.1:nnnn/foo/bar/`, rather than
672
            `bzr://127.0.0.1:nnnn/`.  Default value is `extra`, so that tests
673
            by default will fail unless they do the necessary path translation.
674
        """
675
        if not client_path_extra.startswith('/'):
676
            raise ValueError(client_path_extra)
677
        from bzrlib.transport.chroot import ChrootServer
678
        if backing_transport_server is None:
679
            backing_transport_server = LocalURLServer()
680
        self.chroot_server = ChrootServer(
681
            self.get_backing_transport(backing_transport_server))
682
        self.chroot_server.start_server()
683
        self.backing_transport = transport.get_transport(
684
            self.chroot_server.get_url())
685
        self.root_client_path = self.client_path_extra = client_path_extra
686
        self.start_background_thread(self.thread_name_suffix)
687
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
688
    def serve_conn(self, conn, thread_name_suffix):
689
        conn_thread = super(SmartTCPServer_for_testing, self).serve_conn(
690
            conn, thread_name_suffix)
691
        self.clients.append((conn, conn_thread))
692
        return conn_thread
693
694
    def shutdown_client(self, client_socket):
695
        """Properly shutdown a client socket.
696
697
        Under some circumstances (as in bug #383920), we need to force the
698
        shutdown as python delays it until gc occur otherwise and the client
699
        may hang.
700
701
        This should be called only when no other thread is trying to use the
702
        socket.
703
        """
704
        try:
705
            # The request process has been completed, the thread is about to
706
            # die, let's shutdown the socket if we can.
707
            client_socket.shutdown(socket.SHUT_RDWR)
708
        except (socket.error, select.error), e:
709
            if e[0] in (errno.EBADF, errno.ENOTCONN):
710
                # Right, the socket is already down
711
                pass
712
            else:
713
                raise
714
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
715
    def stop_server(self):
716
        self.stop_background_thread()
5247.1.1 by Vincent Ladeuil
Merge previous attempt into current trunk
717
        # Let's close all our pending clients too
718
        for sock, thread in self.clients:
719
            self.shutdown_client(sock)
720
            thread.join()
721
            del thread
722
        self.clients = []
5017.3.18 by Vincent Ladeuil
Move SmartTCPServer_for_testing and friends to bzrlib.tests.test_server
723
        self.chroot_server.stop_server()
724
725
    def get_url(self):
726
        url = super(SmartTCPServer_for_testing, self).get_url()
727
        return url[:-1] + self.client_path_extra
728
729
    def get_bogus_url(self):
730
        """Return a URL which will fail to connect"""
731
        return 'bzr://127.0.0.1:1/'
732
733
734
class ReadonlySmartTCPServer_for_testing(SmartTCPServer_for_testing):
735
    """Get a readonly server for testing."""
736
737
    def get_backing_transport(self, backing_transport_server):
738
        """Get a backing transport from a server we are decorating."""
739
        url = 'readonly+' + backing_transport_server.get_url()
740
        return transport.get_transport(url)
741
742
743
class SmartTCPServer_for_testing_v2_only(SmartTCPServer_for_testing):
744
    """A variation of SmartTCPServer_for_testing that limits the client to
745
    using RPCs in protocol v2 (i.e. bzr <= 1.5).
746
    """
747
748
    def get_url(self):
749
        url = super(SmartTCPServer_for_testing_v2_only, self).get_url()
750
        url = 'bzr-v2://' + url[len('bzr://'):]
751
        return url
752
753
754
class ReadonlySmartTCPServer_for_testing_v2_only(
755
    SmartTCPServer_for_testing_v2_only):
756
    """Get a readonly server for testing."""
757
758
    def get_backing_transport(self, backing_transport_server):
759
        """Get a backing transport from a server we are decorating."""
760
        url = 'readonly+' + backing_transport_server.get_url()
761
        return transport.get_transport(url)
762
763
764
765