~bzr-pqm/bzr/bzr.dev

5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2010, 2011 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
19
import SocketServer
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
20
import threading
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
21
6175.1.1 by John Arbash Meinel
Close the request when we don't process it.
22
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
23
from bzrlib import (
24
    osutils,
25
    tests,
26
    )
27
from bzrlib.tests import test_server
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
28
from bzrlib.tests.scenarios import load_tests_apply_scenarios
29
30
31
load_tests = load_tests_apply_scenarios
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
32
33
6175.1.5 by John Arbash Meinel
Suppress ConnectionTimeout as a server-side exception.
34
def portable_socket_pair():
35
    """Return a pair of TCP sockets connected to each other.
36
37
    Unlike socket.socketpair, this should work on Windows.
38
    """
39
    listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
40
    listen_sock.bind(('127.0.0.1', 0))
41
    listen_sock.listen(1)
42
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
43
    client_sock.connect(listen_sock.getsockname())
44
    server_sock, addr = listen_sock.accept()
45
    listen_sock.close()
46
    return server_sock, client_sock
47
48
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
49
class TCPClient(object):
50
51
    def __init__(self):
52
        self.sock = None
53
54
    def connect(self, addr):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
55
        if self.sock is not None:
56
            raise AssertionError('Already connected to %r'
57
                                 % (self.sock.getsockname(),))
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
58
        self.sock = osutils.connect_socket(addr)
59
60
    def disconnect(self):
61
        if self.sock is not None:
5247.3.10 by Vincent Ladeuil
Test errors during server life.
62
            try:
63
                self.sock.shutdown(socket.SHUT_RDWR)
64
                self.sock.close()
65
            except socket.error, e:
66
                if e[0] in (errno.EBADF, errno.ENOTCONN):
67
                    # Right, the socket is already down
68
                    pass
69
                else:
70
                    raise
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
71
            self.sock = None
72
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
73
    def write(self, s):
74
        return self.sock.sendall(s)
75
76
    def read(self, bufsize=4096):
77
        return self.sock.recv(bufsize)
78
79
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
80
class TCPConnectionHandler(SocketServer.BaseRequestHandler):
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
81
82
    def handle(self):
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
83
        self.done = False
84
        self.handle_connection()
85
        while not self.done:
86
            self.handle_connection()
87
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
88
    def readline(self):
89
        # TODO: We should be buffering any extra data sent, etc. However, in
90
        #       practice, we don't send extra content, so we haven't bothered
91
        #       to implement it yet.
92
        req = self.request.recv(4096)
93
        # An empty string is allowed, to indicate the end of the connection
94
        if not req or (req.endswith('\n') and req.count('\n') == 1):
95
            return req
96
        raise ValueError('[%r] not a simple line' % (req,))
97
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
98
    def handle_connection(self):
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
99
        req = self.readline()
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
100
        if not req:
101
            self.done = True
102
        elif req == 'ping\n':
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
103
            self.request.sendall('pong\n')
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
104
        else:
105
            raise ValueError('[%s] not understood' % req)
5247.3.8 by Vincent Ladeuil
Start implementing a TCP server running in its own thread (using
106
5247.3.13 by Vincent Ladeuil
Really test against a threading server and properly shutdown socket and threads.
107
108
class TestTCPServerInAThread(tests.TestCase):
109
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
110
    scenarios = [
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
111
        (name, {'server_class': getattr(test_server, name)})
112
        for name in
113
        ('TestingTCPServer', 'TestingThreadingTCPServer')]
114
5247.3.14 by Vincent Ladeuil
Use a proper load_tests.
115
    # Set by load_tests()
116
    server_class = None
5247.3.11 by Vincent Ladeuil
Start implementing the threading variants.
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)
139
        self.assertEquals(client.sock.getsockname(), conn[1])
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)
5247.3.9 by Vincent Ladeuil
Ensure a simple dialog can occur between a client and a server.
158
        self.assertEquals('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())
194
        except socket.error, 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):
205
        sync = threading.Event()
206
        sync.clear()
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
207
        class FailToRespond(Exception):
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
208
            pass
209
210
        class FailingDuringResponseHandler(TCPConnectionHandler):
211
212
            def handle_connection(self):
6175.1.2 by John Arbash Meinel
Change the TCPConnectionHandler to inherit from BaseRequestHandler.
213
                req = self.readline()
5652.1.2 by Vincent Ladeuil
Use clearer names.
214
                threading.currentThread().set_sync_event(sync)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
215
                raise FailToRespond()
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
216
217
        server = self.get_server(
218
            connection_handler_class=FailingDuringResponseHandler)
219
        client = self.get_client()
5247.3.21 by Vincent Ladeuil
Merge propagate-exceptions into http-leaks
220
        client.connect((server.host, server.port))
5247.5.3 by Vincent Ladeuil
Fix exception raising only once for a given ThreadWithException.
221
        client.write('ping\n')
222
        sync.wait()
6175.1.4 by John Arbash Meinel
Add more checks that the server closes the connection properly.
223
        self.assertEqual('', client.read()) # connection closed
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
224
        self.assertRaises(FailToRespond, server.pending_exception)
225
226
    def test_exception_swallowed_while_serving(self):
227
        sync = threading.Event()
228
        sync.clear()
229
        class CantServe(Exception):
230
            pass
231
232
        class FailingWhileServingConnectionHandler(TCPConnectionHandler):
233
234
            def handle(self):
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
235
                # We want to sync with the thread that is serving the
236
                # connection.
5652.1.2 by Vincent Ladeuil
Use clearer names.
237
                threading.currentThread().set_sync_event(sync)
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
238
                raise CantServe()
239
240
        server = self.get_server(
241
            connection_handler_class=FailingWhileServingConnectionHandler)
242
        # Install the exception swallower
243
        server.set_ignored_exceptions(CantServe)
244
        client = self.get_client()
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
245
        # Connect to the server so the exception is raised there
5247.3.21 by Vincent Ladeuil
Merge propagate-exceptions into http-leaks
246
        client.connect((server.host, server.port))
5247.5.9 by Vincent Ladeuil
Use a better sync for test_exception_swallowed_while_serving test.
247
        # Wait for the exception to propagate.
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
248
        sync.wait()
6175.1.4 by John Arbash Meinel
Add more checks that the server closes the connection properly.
249
        self.assertEqual('', client.read()) # connection closed
5247.5.4 by Vincent Ladeuil
Implement an execption handling mechanism that can be injected in ThreadWithException.
250
        # The connection wasn't served properly but the exception should have
251
        # been swallowed.
252
        server.pending_exception()
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
253
6175.1.1 by John Arbash Meinel
Close the request when we don't process it.
254
    def test_handle_request_closes_if_it_doesnt_process(self):
255
        server = self.get_server()
256
        client = self.get_client()
257
        server.server.serving = False
258
        client.connect((server.host, server.port))
259
        self.assertEqual('', client.read())
260
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
261
262
class TestTestingSmartServer(tests.TestCase):
263
264
    def test_sets_client_timeout(self):
265
        server = test_server.TestingSmartServer(('localhost', 0), None, None,
266
            root_client_path='/no-such-client/path')
267
        self.assertEqual(test_server._DEFAULT_TESTING_CLIENT_TIMEOUT,
268
                         server._client_timeout)
269
        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.
270
        h = server._make_handler(sock)
6133.4.23 by John Arbash Meinel
Setup the TestingSmartServer to set a default client timeout,
271
        self.assertEqual(test_server._DEFAULT_TESTING_CLIENT_TIMEOUT,
272
                         h._client_timeout)
6175.1.5 by John Arbash Meinel
Suppress ConnectionTimeout as a server-side exception.
273
274
275
class FakeServer(object):
276
    """Minimal implementation to pass to TestingSmartConnectionHandler"""
277
    backing_transport = None
278
    root_client_path = '/'
279
280
281
class TestTestingSmartConnectionHandler(tests.TestCase):
282
283
    def test_connection_timeout_suppressed(self):
284
        self.overrideAttr(test_server, '_DEFAULT_TESTING_CLIENT_TIMEOUT', 0.01)
285
        s = FakeServer()
286
        server_sock, client_sock = portable_socket_pair()
287
        # This should timeout quickly, but not generate an exception.
288
        handler = test_server.TestingSmartConnectionHandler(server_sock,
289
            server_sock.getpeername(), s)
290
291
    def test_connection_shutdown_while_serving_no_error(self):
292
        s = FakeServer()
293
        server_sock, client_sock = portable_socket_pair()
294
        class ShutdownConnectionHandler(
295
            test_server.TestingSmartConnectionHandler):
296
297
            def _build_protocol(self):
298
                self.finished = True
299
                return super(ShutdownConnectionHandler, self)._build_protocol()
300
        # This should trigger shutdown after the entering _build_protocol, and
301
        # we should exit cleanly, without raising an exception.
302
        handler = ShutdownConnectionHandler(server_sock,
303
            server_sock.getpeername(), s)