/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
1
# Copyright (C) 2010, 2011, 2016 Canonical Ltd
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
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.10 by Vincent Ladeuil
Test errors during server life.
17
import errno
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
18
import socket
6621.25.1 by Martin
Adapt to SocketServer module name change
19
try:
20
    import socketserver
21
except ImportError:
22
    import SocketServer as socketserver
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
23
import threading
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
24
6175.1.1 by John Arbash Meinel
Close the request when we don't process it.
25
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
26
from breezy import (
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
27
    osutils,
28
    tests,
29
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
30
from breezy.tests import test_server
31
from breezy.tests.scenarios import load_tests_apply_scenarios
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
32
33
34
load_tests = load_tests_apply_scenarios
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
35
36
6175.1.5 by John Arbash Meinel
Suppress ConnectionTimeout as a server-side exception.
37
def portable_socket_pair():
38
    """Return a pair of TCP sockets connected to each other.
39
40
    Unlike socket.socketpair, this should work on Windows.
41
    """
42
    listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
43
    listen_sock.bind(('127.0.0.1', 0))
44
    listen_sock.listen(1)
45
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
46
    client_sock.connect(listen_sock.getsockname())
47
    server_sock, addr = listen_sock.accept()
48
    listen_sock.close()
49
    return server_sock, client_sock
50
51
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
52
class TCPClient(object):
53
54
    def __init__(self):
55
        self.sock = None
56
57
    def connect(self, addr):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
58
        if self.sock is not None:
59
            raise AssertionError('Already connected to %r'
60
                                 % (self.sock.getsockname(),))
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
61
        self.sock = osutils.connect_socket(addr)
62
63
    def disconnect(self):
64
        if self.sock is not None:
5247.3.10 by Vincent Ladeuil
Test errors during server life.
65
            try:
66
                self.sock.shutdown(socket.SHUT_RDWR)
67
                self.sock.close()
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
68
            except socket.error as e:
5247.3.10 by Vincent Ladeuil
Test errors during server life.
69
                if e[0] in (errno.EBADF, errno.ENOTCONN):
70
                    # Right, the socket is already down
71
                    pass
72
                else:
73
                    raise
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
74
            self.sock = None
75
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
76
    def write(self, s):
77
        return self.sock.sendall(s)
78
79
    def read(self, bufsize=4096):
80
        return self.sock.recv(bufsize)
81
82
6621.25.1 by Martin
Adapt to SocketServer module name change
83
class TCPConnectionHandler(socketserver.BaseRequestHandler):
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
84
85
    def handle(self):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
86
        self.done = False
87
        self.handle_connection()
88
        while not self.done:
89
            self.handle_connection()
90
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
91
    def readline(self):
92
        # TODO: We should be buffering any extra data sent, etc. However, in
93
        #       practice, we don't send extra content, so we haven't bothered
94
        #       to implement it yet.
95
        req = self.request.recv(4096)
96
        # An empty string is allowed, to indicate the end of the connection
97
        if not req or (req.endswith('\n') and req.count('\n') == 1):
98
            return req
99
        raise ValueError('[%r] not a simple line' % (req,))
100
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
101
    def handle_connection(self):
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
102
        req = self.readline()
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
103
        if not req:
104
            self.done = True
105
        elif req == 'ping\n':
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
106
            self.request.sendall('pong\n')
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
107
        else:
108
            raise ValueError('[%s] not understood' % req)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
109
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
110
111
class TestTCPServerInAThread(tests.TestCase):
112
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
113
    scenarios = [
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
114
        (name, {'server_class': getattr(test_server, name)})
115
        for name in
116
        ('TestingTCPServer', 'TestingThreadingTCPServer')]
117
5247.3.10 by Vincent Ladeuil
Test errors during server life.
118
    def get_server(self, server_class=None, connection_handler_class=None):
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
119
        if server_class is not None:
120
            self.server_class = server_class
5247.3.10 by Vincent Ladeuil
Test errors during server life.
121
        if connection_handler_class is None:
122
            connection_handler_class = TCPConnectionHandler
6175.1.1 by John Arbash Meinel
Close the request when we don't process it.
123
        server = test_server.TestingTCPServerInAThread(
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
124
            ('localhost', 0), self.server_class, connection_handler_class)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
125
        server.start_server()
126
        self.addCleanup(server.stop_server)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
127
        return server
128
129
    def get_client(self):
130
        client = TCPClient()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
131
        self.addCleanup(client.disconnect)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
132
        return client
133
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
134
    def get_server_connection(self, server, conn_rank):
135
        return server.server.clients[conn_rank]
136
137
    def assertClientAddr(self, client, server, conn_rank):
138
        conn = self.get_server_connection(server, conn_rank)
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
139
        self.assertEqual(client.sock.getsockname(), conn[1])
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
140
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
141
    def test_start_stop(self):
142
        server = self.get_server()
143
        client = self.get_client()
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
144
        server.stop_server()
145
        # since the server doesn't accept connections anymore attempting to
146
        # connect should fail
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
147
        client = self.get_client()
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
148
        self.assertRaises(socket.error,
149
                          client.connect, (server.host, server.port))
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
150
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
151
    def test_client_talks_server_respond(self):
152
        server = self.get_server()
153
        client = self.get_client()
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
154
        client.connect((server.host, server.port))
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
155
        self.assertIs(None, client.write('ping\n'))
156
        resp = client.read()
5247.3.12 by Vincent Ladeuil
Spawn a thread for each connection from a client.
157
        self.assertClientAddr(client, server, 0)
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
158
        self.assertEqual('pong\n', resp)
5247.3.10 by Vincent Ladeuil
Test errors during server life.
159
160
    def test_server_fails_to_start(self):
161
        class CantStart(Exception):
162
            pass
163
164
        class CantStartServer(test_server.TestingTCPServer):
165
166
            def server_bind(self):
167
                raise CantStart()
168
169
        # The exception is raised in the main thread
170
        self.assertRaises(CantStart,
171
                          self.get_server, server_class=CantStartServer)
172
5247.5.10 by Vincent Ladeuil
Fix broken test.
173
    def test_server_fails_while_serving_or_stopping(self):
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
174
        class CantConnect(Exception):
5247.3.10 by Vincent Ladeuil
Test errors during server life.
175
            pass
176
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
177
        class FailingConnectionHandler(TCPConnectionHandler):
5247.3.10 by Vincent Ladeuil
Test errors during server life.
178
179
            def handle(self):
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
180
                raise CantConnect()
5247.3.10 by Vincent Ladeuil
Test errors during server life.
181
182
        server = self.get_server(
183
            connection_handler_class=FailingConnectionHandler)
184
        # The server won't fail until a client connect
185
        client = self.get_client()
5247.3.18 by Vincent Ladeuil
Fix some fallouts from previous fixes, all tests passing (no more http leaks).
186
        client.connect((server.host, server.port))
6175.1.1 by John Arbash Meinel
Close the request when we don't process it.
187
        # We make sure the server wants to handle a request, but the request is
188
        # guaranteed to fail. However, the server should make sure that the
189
        # connection gets closed, and stop_server should then raise the
190
        # original exception.
191
        client.write('ping\n')
5247.3.10 by Vincent Ladeuil
Test errors during server life.
192
        try:
6175.1.1 by John Arbash Meinel
Close the request when we don't process it.
193
            self.assertEqual('', client.read())
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
194
        except socket.error as e:
6175.1.3 by John Arbash Meinel
Restrict the socket errors we will suppress.
195
            # On Windows, failing during 'handle' means we get
196
            # 'forced-close-of-connection'. Possibly because we haven't
197
            # processed the write request before we close the socket.
198
            WSAECONNRESET = 10054
199
            if e.errno in (WSAECONNRESET,):
200
                pass
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
201
        # Now the server has raised the exception in its own thread
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
202
        self.assertRaises(CantConnect, server.stop_server)
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
203
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
204
    def test_server_crash_while_responding(self):
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
205
        # We want to ensure the exception has been caught
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
206
        caught = threading.Event()
207
        caught.clear()
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
208
        # The thread that will serve the client, this needs to be an attribute
209
        # so the handler below can modify it when it's executed (it's
210
        # instantiated when the request is processed)
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
211
        self.connection_thread = None
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
212
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
213
        class FailToRespond(Exception):
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
214
            pass
215
216
        class FailingDuringResponseHandler(TCPConnectionHandler):
217
6015.42.5 by Vincent Ladeuil
More comments after review discussions.
218
            # We use 'request' instead of 'self' below because the test matters
219
            # more and we need a container to properly set connection_thread.
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
220
            def handle_connection(request):
6205.1.1 by John Arbash Meinel
Merge bzr-2.4 into bzr.dev, resolve the small conflicts.
221
                req = request.readline()
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
222
                # Capture the thread and make it use 'caught' so we can wait on
6015.42.5 by Vincent Ladeuil
More comments after review discussions.
223
                # the event that will be set when the exception is caught. We
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
224
                # also capture the thread to know where to look.
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
225
                self.connection_thread = threading.currentThread()
226
                self.connection_thread.set_sync_event(caught)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
227
                raise FailToRespond()
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
228
229
        server = self.get_server(
230
            connection_handler_class=FailingDuringResponseHandler)
231
        client = self.get_client()
5247.3.21 by Vincent Ladeuil
Merge propagate-exceptions into http-leaks
232
        client.connect((server.host, server.port))
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
233
        client.write('ping\n')
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
234
        # Wait for the exception to be caught
235
        caught.wait()
6175.1.4 by John Arbash Meinel
Add more checks that the server closes the connection properly.
236
        self.assertEqual('', client.read()) # connection closed
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
237
        # Check that the connection thread did catch the exception,
238
        # http://pad.lv/869366 was wrongly checking the server thread which
239
        # works for TestingTCPServer where the connection is handled in the
6015.42.6 by Vincent Ladeuil
Ensures that the connection thread is detached from the server thread before handling the connection.
240
        # same thread than the server one but was racy for
6015.42.7 by Vincent Ladeuil
Fix typo.
241
        # TestingThreadingTCPServer. Since the connection thread detaches
6015.42.6 by Vincent Ladeuil
Ensures that the connection thread is detached from the server thread before handling the connection.
242
        # itself before handling the request, we are guaranteed that the
243
        # exception won't leak into the server thread anymore.
244
        self.assertRaises(FailToRespond,
245
                          self.connection_thread.pending_exception)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
246
247
    def test_exception_swallowed_while_serving(self):
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
248
        # We need to ensure the exception has been caught
249
        caught = threading.Event()
250
        caught.clear()
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
251
        # The thread that will serve the client, this needs to be an attribute
252
        # so the handler below can access it when it's executed (it's
253
        # instantiated when the request is processed)
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
254
        self.connection_thread = None
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
255
        class CantServe(Exception):
256
            pass
257
258
        class FailingWhileServingConnectionHandler(TCPConnectionHandler):
259
6015.42.5 by Vincent Ladeuil
More comments after review discussions.
260
            # We use 'request' instead of 'self' below because the test matters
261
            # more and we need a container to properly set connection_thread.
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
262
            def handle(request):
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
263
                # Capture the thread and make it use 'caught' so we can wait on
6015.42.5 by Vincent Ladeuil
More comments after review discussions.
264
                # the event that will be set when the exception is caught. We
6015.42.4 by Vincent Ladeuil
Less code, more explanations.
265
                # also capture the thread to know where to look.
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
266
                self.connection_thread = threading.currentThread()
267
                self.connection_thread.set_sync_event(caught)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
268
                raise CantServe()
269
270
        server = self.get_server(
271
            connection_handler_class=FailingWhileServingConnectionHandler)
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
272
        self.assertEqual(True, server.server.serving)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
273
        # Install the exception swallower
274
        server.set_ignored_exceptions(CantServe)
275
        client = self.get_client()
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
276
        # Connect to the server so the exception is raised there
5247.3.21 by Vincent Ladeuil
Merge propagate-exceptions into http-leaks
277
        client.connect((server.host, server.port))
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
278
        # Wait for the exception to be caught
279
        caught.wait()
6175.1.4 by John Arbash Meinel
Add more checks that the server closes the connection properly.
280
        self.assertEqual('', client.read()) # connection closed
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
281
        # The connection wasn't served properly but the exception should have
6015.42.1 by Vincent Ladeuil
Fix a naughty race in test_server_crash_while_responding
282
        # been swallowed (see test_server_crash_while_responding remark about
283
        # http://pad.lv/869366 explaining why we can't check the server thread
6015.42.2 by Vincent Ladeuil
One race can hide another... the exception may pass from the connection thread to the server thread so both need to be checked, with care.
284
        # here). More precisely, the exception *has* been caught and captured
285
        # but it is cleared when joining the thread (or trying to acquire the
286
        # exception) and as such won't propagate to the server thread.
6015.42.5 by Vincent Ladeuil
More comments after review discussions.
287
        self.assertIs(None, self.connection_thread.pending_exception())
288
        self.assertIs(None, server.pending_exception())
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
289
6175.1.1 by John Arbash Meinel
Close the request when we don't process it.
290
    def test_handle_request_closes_if_it_doesnt_process(self):
291
        server = self.get_server()
292
        client = self.get_client()
293
        server.server.serving = False
294
        client.connect((server.host, server.port))
295
        self.assertEqual('', client.read())
296
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
297
298
class TestTestingSmartServer(tests.TestCase):
299
300
    def test_sets_client_timeout(self):
301
        server = test_server.TestingSmartServer(('localhost', 0), None, None,
302
            root_client_path='/no-such-client/path')
303
        self.assertEqual(test_server._DEFAULT_TESTING_CLIENT_TIMEOUT,
304
                         server._client_timeout)
305
        sock = socket.socket()
6133.4.24 by John Arbash Meinel
Change the name from _create_handler to _make_handler, to match similar code in the file.
306
        h = server._make_handler(sock)
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
307
        self.assertEqual(test_server._DEFAULT_TESTING_CLIENT_TIMEOUT,
308
                         h._client_timeout)
6175.1.5 by John Arbash Meinel
Suppress ConnectionTimeout as a server-side exception.
309
310
311
class FakeServer(object):
312
    """Minimal implementation to pass to TestingSmartConnectionHandler"""
313
    backing_transport = None
314
    root_client_path = '/'
315
316
317
class TestTestingSmartConnectionHandler(tests.TestCase):
318
319
    def test_connection_timeout_suppressed(self):
320
        self.overrideAttr(test_server, '_DEFAULT_TESTING_CLIENT_TIMEOUT', 0.01)
321
        s = FakeServer()
322
        server_sock, client_sock = portable_socket_pair()
323
        # This should timeout quickly, but not generate an exception.
324
        handler = test_server.TestingSmartConnectionHandler(server_sock,
325
            server_sock.getpeername(), s)
326
327
    def test_connection_shutdown_while_serving_no_error(self):
328
        s = FakeServer()
329
        server_sock, client_sock = portable_socket_pair()
330
        class ShutdownConnectionHandler(
331
            test_server.TestingSmartConnectionHandler):
332
333
            def _build_protocol(self):
334
                self.finished = True
335
                return super(ShutdownConnectionHandler, self)._build_protocol()
336
        # This should trigger shutdown after the entering _build_protocol, and
337
        # we should exit cleanly, without raising an exception.
338
        handler = ShutdownConnectionHandler(server_sock,
339
            server_sock.getpeername(), s)