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