~bzr-pqm/bzr/bzr.dev

2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
1
# Copyright (C) 2006, 2007 Canonical Ltd
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for smart transport"""
18
19
# all of this deals with byte strings so this is safe
20
from cStringIO import StringIO
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
21
import os
22
import socket
23
import threading
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
24
import urllib2
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
25
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
26
from bzrlib import (
27
        bzrdir,
28
        errors,
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
29
        osutils,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
30
        tests,
2049.1.1 by Lukáš Lalinský
Windows-speficic smart server transport selftest fixes.
31
        urlutils,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
32
        )
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
33
from bzrlib.smart import (
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
34
        client,
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
35
        medium,
36
        protocol,
37
        request,
38
        server,
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
39
        vfs,
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
40
)
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
41
from bzrlib.tests.HTTPTestUtil import (
42
        HTTPServerWithSmarts,
43
        SmartRequestHandler,
44
        )
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
45
from bzrlib.transport import (
46
        get_transport,
47
        local,
48
        memory,
2400.1.1 by Andrew Bennetts
Rename bzrlib/transport/smart.py to bzrlib/transport/remote.py.
49
        remote,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
50
        )
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
51
from bzrlib.transport.http import SmartClientHTTPMediumRequest
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
52
53
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
54
class StringIOSSHVendor(object):
55
    """A SSH vendor that uses StringIO to buffer writes and answer reads."""
56
57
    def __init__(self, read_from, write_to):
58
        self.read_from = read_from
59
        self.write_to = write_to
60
        self.calls = []
61
62
    def connect_ssh(self, username, password, host, port, command):
63
        self.calls.append(('connect_ssh', username, password, host, port,
64
            command))
65
        return StringIOSSHConnection(self)
66
67
68
class StringIOSSHConnection(object):
69
    """A SSH connection that uses StringIO to buffer writes and answer reads."""
70
71
    def __init__(self, vendor):
72
        self.vendor = vendor
73
    
74
    def close(self):
75
        self.vendor.calls.append(('close', ))
76
        
77
    def get_filelike_channels(self):
78
        return self.vendor.read_from, self.vendor.write_to
79
80
81
82
class SmartClientMediumTests(tests.TestCase):
83
    """Tests for SmartClientMedium.
84
85
    We should create a test scenario for this: we need a server module that
86
    construct the test-servers (like make_loopsocket_and_medium), and the list
87
    of SmartClientMedium classes to test.
88
    """
89
90
    def make_loopsocket_and_medium(self):
91
        """Create a loopback socket for testing, and a medium aimed at it."""
92
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
93
        sock.bind(('127.0.0.1', 0))
94
        sock.listen(1)
95
        port = sock.getsockname()[1]
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
96
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', port)
97
        return sock, client_medium
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
98
99
    def receive_bytes_on_server(self, sock, bytes):
100
        """Accept a connection on sock and read 3 bytes.
101
102
        The bytes are appended to the list bytes.
103
104
        :return: a Thread which is running to do the accept and recv.
105
        """
106
        def _receive_bytes_on_server():
107
            connection, address = sock.accept()
2091.1.1 by Martin Pool
Avoid MSG_WAITALL as it doesn't work on Windows
108
            bytes.append(osutils.recv_all(connection, 3))
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
109
            connection.close()
110
        t = threading.Thread(target=_receive_bytes_on_server)
111
        t.start()
112
        return t
113
    
114
    def test_construct_smart_stream_medium_client(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
115
        # make a new instance of the common base for Stream-like Mediums.
116
        # this just ensures that the constructor stays parameter-free which
117
        # is important for reuse : some subclasses will dynamically connect,
118
        # others are always on, etc.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
119
        client_medium = medium.SmartClientStreamMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
120
121
    def test_construct_smart_client_medium(self):
122
        # the base client medium takes no parameters
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
123
        client_medium = medium.SmartClientMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
124
    
125
    def test_construct_smart_simple_pipes_client_medium(self):
126
        # the SimplePipes client medium takes two pipes:
127
        # readable pipe, writeable pipe.
128
        # Constructing one should just save these and do nothing.
129
        # We test this by passing in None.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
130
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
131
        
132
    def test_simple_pipes_client_request_type(self):
133
        # SimplePipesClient should use SmartClientStreamMediumRequest's.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
134
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
135
        request = client_medium.get_request()
136
        self.assertIsInstance(request, medium.SmartClientStreamMediumRequest)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
137
138
    def test_simple_pipes_client_get_concurrent_requests(self):
139
        # the simple_pipes client does not support pipelined requests:
140
        # but it does support serial requests: we construct one after 
141
        # another is finished. This is a smoke test testing the integration
142
        # of the SmartClientStreamMediumRequest and the SmartClientStreamMedium
143
        # classes - as the sibling classes share this logic, they do not have
144
        # explicit tests for this.
145
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
146
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
147
        request = client_medium.get_request()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
148
        request.finished_writing()
149
        request.finished_reading()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
150
        request2 = client_medium.get_request()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
151
        request2.finished_writing()
152
        request2.finished_reading()
153
154
    def test_simple_pipes_client__accept_bytes_writes_to_writable(self):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
155
        # accept_bytes writes to the writeable pipe.
156
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
157
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
158
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
159
        self.assertEqual('abc', output.getvalue())
160
    
161
    def test_simple_pipes_client_disconnect_does_nothing(self):
162
        # calling disconnect does nothing.
163
        input = StringIO()
164
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
165
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
166
        # send some bytes to ensure disconnecting after activity still does not
167
        # close.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
168
        client_medium._accept_bytes('abc')
169
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
170
        self.assertFalse(input.closed)
171
        self.assertFalse(output.closed)
172
173
    def test_simple_pipes_client_accept_bytes_after_disconnect(self):
174
        # calling disconnect on the client does not alter the pipe that
175
        # accept_bytes writes to.
176
        input = StringIO()
177
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
178
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
179
        client_medium._accept_bytes('abc')
180
        client_medium.disconnect()
181
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
182
        self.assertFalse(input.closed)
183
        self.assertFalse(output.closed)
184
        self.assertEqual('abcabc', output.getvalue())
185
    
186
    def test_simple_pipes_client_ignores_disconnect_when_not_connected(self):
187
        # Doing a disconnect on a new (and thus unconnected) SimplePipes medium
188
        # does nothing.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
189
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
190
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
191
192
    def test_simple_pipes_client_can_always_read(self):
193
        # SmartSimplePipesClientMedium is never disconnected, so read_bytes
194
        # always tries to read from the underlying pipe.
195
        input = StringIO('abcdef')
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
196
        client_medium = medium.SmartSimplePipesClientMedium(input, None)
197
        self.assertEqual('abc', client_medium.read_bytes(3))
198
        client_medium.disconnect()
199
        self.assertEqual('def', client_medium.read_bytes(3))
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
200
        
201
    def test_simple_pipes_client_supports__flush(self):
202
        # invoking _flush on a SimplePipesClient should flush the output 
203
        # pipe. We test this by creating an output pipe that records
204
        # flush calls made to it.
205
        from StringIO import StringIO # get regular StringIO
206
        input = StringIO()
207
        output = StringIO()
208
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
209
        def logging_flush(): flush_calls.append('flush')
210
        output.flush = logging_flush
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
211
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
212
        # this call is here to ensure we only flush once, not on every
213
        # _accept_bytes call.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
214
        client_medium._accept_bytes('abc')
215
        client_medium._flush()
216
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
217
        self.assertEqual(['flush'], flush_calls)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
218
219
    def test_construct_smart_ssh_client_medium(self):
220
        # the SSH client medium takes:
221
        # host, port, username, password, vendor
222
        # Constructing one should just save these and do nothing.
223
        # we test this by creating a empty bound socket and constructing
224
        # a medium.
225
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
226
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
227
        unopened_port = sock.getsockname()[1]
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
228
        # having vendor be invalid means that if it tries to connect via the
229
        # vendor it will blow up.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
230
        client_medium = medium.SmartSSHClientMedium('127.0.0.1', unopened_port,
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
231
            username=None, password=None, vendor="not a vendor")
232
        sock.close()
233
234
    def test_ssh_client_connects_on_first_use(self):
235
        # The only thing that initiates a connection from the medium is giving
236
        # it bytes.
237
        output = StringIO()
238
        vendor = StringIOSSHVendor(StringIO(), output)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
239
        client_medium = medium.SmartSSHClientMedium(
240
            'a hostname', 'a port', 'a username', 'a password', vendor)
241
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
242
        self.assertEqual('abc', output.getvalue())
243
        self.assertEqual([('connect_ssh', 'a username', 'a password',
244
            'a hostname', 'a port',
245
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes'])],
246
            vendor.calls)
247
    
248
    def test_ssh_client_changes_command_when_BZR_REMOTE_PATH_is_set(self):
249
        # The only thing that initiates a connection from the medium is giving
250
        # it bytes.
251
        output = StringIO()
252
        vendor = StringIOSSHVendor(StringIO(), output)
253
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
254
        def cleanup_environ():
255
            osutils.set_or_unset_env('BZR_REMOTE_PATH', orig_bzr_remote_path)
256
        self.addCleanup(cleanup_environ)
257
        os.environ['BZR_REMOTE_PATH'] = 'fugly'
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
258
        client_medium = medium.SmartSSHClientMedium('a hostname', 'a port', 'a username',
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
259
            'a password', vendor)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
260
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
261
        self.assertEqual('abc', output.getvalue())
262
        self.assertEqual([('connect_ssh', 'a username', 'a password',
263
            'a hostname', 'a port',
264
            ['fugly', 'serve', '--inet', '--directory=/', '--allow-writes'])],
265
            vendor.calls)
266
    
267
    def test_ssh_client_disconnect_does_so(self):
268
        # calling disconnect should disconnect both the read_from and write_to
269
        # file-like object it from the ssh connection.
270
        input = StringIO()
271
        output = StringIO()
272
        vendor = StringIOSSHVendor(input, output)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
273
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
274
        client_medium._accept_bytes('abc')
275
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
276
        self.assertTrue(input.closed)
277
        self.assertTrue(output.closed)
278
        self.assertEqual([
279
            ('connect_ssh', None, None, 'a hostname', None,
280
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
281
            ('close', ),
282
            ],
283
            vendor.calls)
284
285
    def test_ssh_client_disconnect_allows_reconnection(self):
286
        # calling disconnect on the client terminates the connection, but should
287
        # not prevent additional connections occuring.
288
        # we test this by initiating a second connection after doing a
289
        # disconnect.
290
        input = StringIO()
291
        output = StringIO()
292
        vendor = StringIOSSHVendor(input, output)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
293
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
294
        client_medium._accept_bytes('abc')
295
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
296
        # the disconnect has closed output, so we need a new output for the
297
        # new connection to write to.
298
        input2 = StringIO()
299
        output2 = StringIO()
300
        vendor.read_from = input2
301
        vendor.write_to = output2
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
302
        client_medium._accept_bytes('abc')
303
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
304
        self.assertTrue(input.closed)
305
        self.assertTrue(output.closed)
306
        self.assertTrue(input2.closed)
307
        self.assertTrue(output2.closed)
308
        self.assertEqual([
309
            ('connect_ssh', None, None, 'a hostname', None,
310
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
311
            ('close', ),
312
            ('connect_ssh', None, None, 'a hostname', None,
313
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
314
            ('close', ),
315
            ],
316
            vendor.calls)
317
    
318
    def test_ssh_client_ignores_disconnect_when_not_connected(self):
319
        # Doing a disconnect on a new (and thus unconnected) SSH medium
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
320
        # does not fail.  It's ok to disconnect an unconnected medium.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
321
        client_medium = medium.SmartSSHClientMedium(None)
322
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
323
324
    def test_ssh_client_raises_on_read_when_not_connected(self):
325
        # Doing a read on a new (and thus unconnected) SSH medium raises
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
326
        # MediumNotConnected.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
327
        client_medium = medium.SmartSSHClientMedium(None)
328
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
329
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
330
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
331
    def test_ssh_client_supports__flush(self):
332
        # invoking _flush on a SSHClientMedium should flush the output 
333
        # pipe. We test this by creating an output pipe that records
334
        # flush calls made to it.
335
        from StringIO import StringIO # get regular StringIO
336
        input = StringIO()
337
        output = StringIO()
338
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
339
        def logging_flush(): flush_calls.append('flush')
340
        output.flush = logging_flush
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
341
        vendor = StringIOSSHVendor(input, output)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
342
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
343
        # this call is here to ensure we only flush once, not on every
344
        # _accept_bytes call.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
345
        client_medium._accept_bytes('abc')
346
        client_medium._flush()
347
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
348
        self.assertEqual(['flush'], flush_calls)
349
        
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
350
    def test_construct_smart_tcp_client_medium(self):
351
        # the TCP client medium takes a host and a port.  Constructing it won't
352
        # connect to anything.
353
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
354
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
355
        unopened_port = sock.getsockname()[1]
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
356
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', unopened_port)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
357
        sock.close()
358
359
    def test_tcp_client_connects_on_first_use(self):
360
        # The only thing that initiates a connection from the medium is giving
361
        # it bytes.
362
        sock, medium = self.make_loopsocket_and_medium()
363
        bytes = []
364
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
365
        medium.accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
366
        t.join()
367
        sock.close()
368
        self.assertEqual(['abc'], bytes)
369
    
370
    def test_tcp_client_disconnect_does_so(self):
371
        # calling disconnect on the client terminates the connection.
372
        # we test this by forcing a short read during a socket.MSG_WAITALL
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
373
        # call: write 2 bytes, try to read 3, and then the client disconnects.
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
374
        sock, medium = self.make_loopsocket_and_medium()
375
        bytes = []
376
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
377
        medium.accept_bytes('ab')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
378
        medium.disconnect()
379
        t.join()
380
        sock.close()
381
        self.assertEqual(['ab'], bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
382
        # now disconnect again: this should not do anything, if disconnection
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
383
        # really did disconnect.
384
        medium.disconnect()
385
    
386
    def test_tcp_client_ignores_disconnect_when_not_connected(self):
387
        # Doing a disconnect on a new (and thus unconnected) TCP medium
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
388
        # does not fail.  It's ok to disconnect an unconnected medium.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
389
        client_medium = medium.SmartTCPClientMedium(None, None)
390
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
391
392
    def test_tcp_client_raises_on_read_when_not_connected(self):
393
        # Doing a read on a new (and thus unconnected) TCP medium raises
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
394
        # MediumNotConnected.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
395
        client_medium = medium.SmartTCPClientMedium(None, None)
396
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
397
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
398
399
    def test_tcp_client_supports__flush(self):
400
        # invoking _flush on a TCPClientMedium should do something useful.
401
        # RBC 20060922 not sure how to test/tell in this case.
402
        sock, medium = self.make_loopsocket_and_medium()
403
        bytes = []
404
        t = self.receive_bytes_on_server(sock, bytes)
405
        # try with nothing buffered
406
        medium._flush()
407
        medium._accept_bytes('ab')
408
        # and with something sent.
409
        medium._flush()
410
        medium.disconnect()
411
        t.join()
412
        sock.close()
413
        self.assertEqual(['ab'], bytes)
414
        # now disconnect again : this should not do anything, if disconnection
415
        # really did disconnect.
416
        medium.disconnect()
417
418
419
class TestSmartClientStreamMediumRequest(tests.TestCase):
420
    """Tests the for SmartClientStreamMediumRequest.
421
    
422
    SmartClientStreamMediumRequest is a helper for the three stream based 
423
    mediums: TCP, SSH, SimplePipes, so we only test it once, and then test that
424
    those three mediums implement the interface it expects.
425
    """
426
427
    def test_accept_bytes_after_finished_writing_errors(self):
428
        # calling accept_bytes after calling finished_writing raises 
429
        # WritingCompleted to prevent bad assumptions on stream environments
430
        # breaking the needs of message-based environments.
431
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
432
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
433
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
434
        request.finished_writing()
435
        self.assertRaises(errors.WritingCompleted, request.accept_bytes, None)
436
437
    def test_accept_bytes(self):
438
        # accept bytes should invoke _accept_bytes on the stream medium.
439
        # we test this by using the SimplePipes medium - the most trivial one
440
        # and checking that the pipes get the data.
441
        input = StringIO()
442
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
443
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
444
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
445
        request.accept_bytes('123')
446
        request.finished_writing()
447
        request.finished_reading()
448
        self.assertEqual('', input.getvalue())
449
        self.assertEqual('123', output.getvalue())
450
451
    def test_construct_sets_stream_request(self):
452
        # constructing a SmartClientStreamMediumRequest on a StreamMedium sets
453
        # the current request to the new SmartClientStreamMediumRequest
454
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
455
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
456
        request = medium.SmartClientStreamMediumRequest(client_medium)
457
        self.assertIs(client_medium._current_request, request)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
458
459
    def test_construct_while_another_request_active_throws(self):
460
        # constructing a SmartClientStreamMediumRequest on a StreamMedium with
461
        # a non-None _current_request raises TooManyConcurrentRequests.
462
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
463
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
464
        client_medium._current_request = "a"
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
465
        self.assertRaises(errors.TooManyConcurrentRequests,
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
466
            medium.SmartClientStreamMediumRequest, client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
467
468
    def test_finished_read_clears_current_request(self):
469
        # calling finished_reading clears the current request from the requests
470
        # medium
471
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
472
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
473
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
474
        request.finished_writing()
475
        request.finished_reading()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
476
        self.assertEqual(None, client_medium._current_request)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
477
478
    def test_finished_read_before_finished_write_errors(self):
479
        # calling finished_reading before calling finished_writing triggers a
480
        # WritingNotComplete error.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
481
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
482
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
483
        self.assertRaises(errors.WritingNotComplete, request.finished_reading)
484
        
485
    def test_read_bytes(self):
486
        # read bytes should invoke _read_bytes on the stream medium.
487
        # we test this by using the SimplePipes medium - the most trivial one
488
        # and checking that the data is supplied. Its possible that a 
489
        # faulty implementation could poke at the pipe variables them selves,
490
        # but we trust that this will be caught as it will break the integration
491
        # smoke tests.
492
        input = StringIO('321')
493
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
494
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
495
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
496
        request.finished_writing()
497
        self.assertEqual('321', request.read_bytes(3))
498
        request.finished_reading()
499
        self.assertEqual('', input.read())
500
        self.assertEqual('', output.getvalue())
501
502
    def test_read_bytes_before_finished_write_errors(self):
503
        # calling read_bytes before calling finished_writing triggers a
504
        # WritingNotComplete error because the Smart protocol is designed to be
505
        # compatible with strict message based protocols like HTTP where the
506
        # request cannot be submitted until the writing has completed.
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
507
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
508
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
509
        self.assertRaises(errors.WritingNotComplete, request.read_bytes, None)
510
511
    def test_read_bytes_after_finished_reading_errors(self):
512
        # calling read_bytes after calling finished_reading raises 
513
        # ReadingCompleted to prevent bad assumptions on stream environments
514
        # breaking the needs of message-based environments.
515
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
516
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
517
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
518
        request.finished_writing()
519
        request.finished_reading()
520
        self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
521
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
522
523
class RemoteTransportTests(tests.TestCaseWithTransport):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
524
525
    def setUp(self):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
526
        super(RemoteTransportTests, self).setUp()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
527
        # We're allowed to set  the transport class here, so that we don't use
528
        # the default or a parameterized class, but rather use the
529
        # TestCaseWithTransport infrastructure to set up a smart server and
530
        # transport.
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
531
        self.transport_server = server.SmartTCPServer_for_testing
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
532
533
    def test_plausible_url(self):
534
        self.assert_(self.get_url().startswith('bzr://'))
535
536
    def test_probe_transport(self):
537
        t = self.get_transport()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
538
        self.assertIsInstance(t, remote.RemoteTransport)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
539
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
540
    def test_get_medium_from_transport(self):
541
        """Remote transport has a medium always, which it can return."""
542
        t = self.get_transport()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
543
        smart_medium = t.get_smart_medium()
544
        self.assertIsInstance(smart_medium, medium.SmartClientMedium)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
545
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
546
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
547
class ErrorRaisingProtocol(object):
548
549
    def __init__(self, exception):
550
        self.exception = exception
551
552
    def next_read_size(self):
553
        raise self.exception
554
555
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
556
class SampleRequest(object):
557
    
558
    def __init__(self, expected_bytes):
559
        self.accepted_bytes = ''
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
560
        self._finished_reading = False
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
561
        self.expected_bytes = expected_bytes
562
        self.excess_buffer = ''
563
564
    def accept_bytes(self, bytes):
565
        self.accepted_bytes += bytes
566
        if self.accepted_bytes.startswith(self.expected_bytes):
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
567
            self._finished_reading = True
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
568
            self.excess_buffer = self.accepted_bytes[len(self.expected_bytes):]
569
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
570
    def next_read_size(self):
571
        if self._finished_reading:
572
            return 0
573
        else:
574
            return 1
575
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
576
577
class TestSmartServerStreamMedium(tests.TestCase):
578
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
579
    def setUp(self):
580
        super(TestSmartServerStreamMedium, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
581
        self._captureVar('BZR_NO_SMART_VFS', None)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
582
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
583
    def portable_socket_pair(self):
584
        """Return a pair of TCP sockets connected to each other.
585
        
586
        Unlike socket.socketpair, this should work on Windows.
587
        """
588
        listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
589
        listen_sock.bind(('127.0.0.1', 0))
590
        listen_sock.listen(1)
591
        client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
592
        client_sock.connect(listen_sock.getsockname())
593
        server_sock, addr = listen_sock.accept()
594
        listen_sock.close()
595
        return server_sock, client_sock
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
596
    
597
    def test_smart_query_version(self):
598
        """Feed a canned query version to a server"""
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
599
        # wire-to-wire, using the whole stack
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
600
        to_server = StringIO('hello\n')
601
        from_server = StringIO()
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
602
        transport = local.LocalTransport(urlutils.local_path_to_url('/'))
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
603
        server = medium.SmartServerPipeStreamMedium(
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
604
            to_server, from_server, transport)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
605
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
606
                from_server.write)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
607
        server._serve_one_request(smart_protocol)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
608
        self.assertEqual('ok\0011\n',
609
                         from_server.getvalue())
610
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
611
    def test_response_to_canned_get(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
612
        transport = memory.MemoryTransport('memory:///')
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
613
        transport.put_bytes('testfile', 'contents\nof\nfile\n')
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
614
        to_server = StringIO('get\001./testfile\n')
615
        from_server = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
616
        server = medium.SmartServerPipeStreamMedium(
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
617
            to_server, from_server, transport)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
618
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
619
                from_server.write)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
620
        server._serve_one_request(smart_protocol)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
621
        self.assertEqual('ok\n'
622
                         '17\n'
623
                         'contents\nof\nfile\n'
624
                         'done\n',
625
                         from_server.getvalue())
626
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
627
    def test_response_to_canned_get_of_utf8(self):
628
        # wire-to-wire, using the whole stack, with a UTF-8 filename.
629
        transport = memory.MemoryTransport('memory:///')
630
        utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')
631
        transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
632
        to_server = StringIO('get\001' + utf8_filename + '\n')
633
        from_server = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
634
        server = medium.SmartServerPipeStreamMedium(
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
635
            to_server, from_server, transport)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
636
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
637
                from_server.write)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
638
        server._serve_one_request(smart_protocol)
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
639
        self.assertEqual('ok\n'
640
                         '17\n'
641
                         'contents\nof\nfile\n'
642
                         'done\n',
643
                         from_server.getvalue())
644
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
645
    def test_pipe_like_stream_with_bulk_data(self):
646
        sample_request_bytes = 'command\n9\nbulk datadone\n'
647
        to_server = StringIO(sample_request_bytes)
648
        from_server = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
649
        server = medium.SmartServerPipeStreamMedium(
650
            to_server, from_server, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
651
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
652
        server._serve_one_request(sample_protocol)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
653
        self.assertEqual('', from_server.getvalue())
654
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
655
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
656
657
    def test_socket_stream_with_bulk_data(self):
658
        sample_request_bytes = 'command\n9\nbulk datadone\n'
659
        server_sock, client_sock = self.portable_socket_pair()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
660
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
661
            server_sock, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
662
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
663
        client_sock.sendall(sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
664
        server._serve_one_request(sample_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
665
        server_sock.close()
666
        self.assertEqual('', client_sock.recv(1))
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
667
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
668
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
669
670
    def test_pipe_like_stream_shutdown_detection(self):
671
        to_server = StringIO('')
672
        from_server = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
673
        server = medium.SmartServerPipeStreamMedium(to_server, from_server, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
674
        server._serve_one_request(SampleRequest('x'))
675
        self.assertTrue(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
676
        
677
    def test_socket_stream_shutdown_detection(self):
678
        server_sock, client_sock = self.portable_socket_pair()
679
        client_sock.close()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
680
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
681
            server_sock, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
682
        server._serve_one_request(SampleRequest('x'))
683
        self.assertTrue(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
684
        
685
    def test_pipe_like_stream_with_two_requests(self):
686
        # If two requests are read in one go, then two calls to
687
        # _serve_one_request should still process both of them as if they had
688
        # been received seperately.
689
        sample_request_bytes = 'command\n'
690
        to_server = StringIO(sample_request_bytes * 2)
691
        from_server = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
692
        server = medium.SmartServerPipeStreamMedium(
693
            to_server, from_server, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
694
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
695
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
696
        self.assertEqual(0, first_protocol.next_read_size())
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
697
        self.assertEqual('', from_server.getvalue())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
698
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
699
        # Make a new protocol, call _serve_one_request with it to collect the
700
        # second request.
701
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
702
        server._serve_one_request(second_protocol)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
703
        self.assertEqual('', from_server.getvalue())
704
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
705
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
706
        
707
    def test_socket_stream_with_two_requests(self):
708
        # If two requests are read in one go, then two calls to
709
        # _serve_one_request should still process both of them as if they had
710
        # been received seperately.
711
        sample_request_bytes = 'command\n'
712
        server_sock, client_sock = self.portable_socket_pair()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
713
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
714
            server_sock, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
715
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
716
        # Put two whole requests on the wire.
717
        client_sock.sendall(sample_request_bytes * 2)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
718
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
719
        self.assertEqual(0, first_protocol.next_read_size())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
720
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
721
        # Make a new protocol, call _serve_one_request with it to collect the
722
        # second request.
723
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
724
        stream_still_open = server._serve_one_request(second_protocol)
725
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
726
        self.assertFalse(server.finished)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
727
        server_sock.close()
728
        self.assertEqual('', client_sock.recv(1))
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
729
730
    def test_pipe_like_stream_error_handling(self):
731
        # Use plain python StringIO so we can monkey-patch the close method to
732
        # not discard the contents.
733
        from StringIO import StringIO
734
        to_server = StringIO('')
735
        from_server = StringIO()
736
        self.closed = False
737
        def close():
738
            self.closed = True
739
        from_server.close = close
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
740
        server = medium.SmartServerPipeStreamMedium(
741
            to_server, from_server, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
742
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
743
        server._serve_one_request(fake_protocol)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
744
        self.assertEqual('', from_server.getvalue())
745
        self.assertTrue(self.closed)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
746
        self.assertTrue(server.finished)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
747
        
748
    def test_socket_stream_error_handling(self):
749
        # Use plain python StringIO so we can monkey-patch the close method to
750
        # not discard the contents.
751
        from StringIO import StringIO
752
        server_sock, client_sock = self.portable_socket_pair()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
753
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
754
            server_sock, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
755
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
756
        server._serve_one_request(fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
757
        # recv should not block, because the other end of the socket has been
758
        # closed.
759
        self.assertEqual('', client_sock.recv(1))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
760
        self.assertTrue(server.finished)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
761
        
762
    def test_pipe_like_stream_keyboard_interrupt_handling(self):
763
        # Use plain python StringIO so we can monkey-patch the close method to
764
        # not discard the contents.
765
        to_server = StringIO('')
766
        from_server = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
767
        server = medium.SmartServerPipeStreamMedium(
768
            to_server, from_server, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
769
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
770
        self.assertRaises(
771
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
772
        self.assertEqual('', from_server.getvalue())
773
774
    def test_socket_stream_keyboard_interrupt_handling(self):
775
        server_sock, client_sock = self.portable_socket_pair()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
776
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
777
            server_sock, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
778
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
779
        self.assertRaises(
780
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
781
        server_sock.close()
782
        self.assertEqual('', client_sock.recv(1))
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
783
        
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
784
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
785
class TestSmartTCPServer(tests.TestCase):
786
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
787
    def test_get_error_unexpected(self):
788
        """Error reported by server with no specific representation"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
789
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
790
        class FlakyTransport(object):
2376.3.3 by Robert Collins
Fix all smart_transport tests.
791
            base = 'a_url'
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
792
            def get_bytes(self, path):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
793
                raise Exception("some random exception from inside server")
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
794
        smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
795
        smart_server.start_background_thread()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
796
        try:
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
797
            transport = remote.RemoteTCPTransport(smart_server.get_url())
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
798
            try:
799
                transport.get('something')
800
            except errors.TransportError, e:
801
                self.assertContainsRe(str(e), 'some random exception')
802
            else:
803
                self.fail("get did not raise expected error")
804
        finally:
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
805
            smart_server.stop_background_thread()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
806
807
808
class SmartTCPTests(tests.TestCase):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
809
    """Tests for connection/end to end behaviour using the TCP server.
810
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
811
    All of these tests are run with a server running on another thread serving
812
    a MemoryTransport, and a connection to it already open.
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
813
814
    the server is obtained by calling self.setUpServer(readonly=False).
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
815
    """
816
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
817
    def setUpServer(self, readonly=False):
818
        """Setup the server.
819
820
        :param readonly: Create a readonly server.
821
        """
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
822
        self.backing_transport = memory.MemoryTransport()
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
823
        if readonly:
824
            self.real_backing_transport = self.backing_transport
825
            self.backing_transport = get_transport("readonly+" + self.backing_transport.abspath('.'))
2400.1.2 by Andrew Bennetts
Move SmartTCPServer classes into bzrlib/smart/server.py
826
        self.server = server.SmartTCPServer(self.backing_transport)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
827
        self.server.start_background_thread()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
828
        self.transport = remote.RemoteTCPTransport(self.server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
829
        self.addCleanup(self.tearDownServer)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
830
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
831
    def tearDownServer(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
832
        if getattr(self, 'transport', None):
833
            self.transport.disconnect()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
834
            del self.transport
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
835
        if getattr(self, 'server', None):
836
            self.server.stop_background_thread()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
837
            del self.server
838
839
840
class TestServerSocketUsage(SmartTCPTests):
841
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
842
    def test_server_setup_teardown(self):
843
        """It should be safe to teardown the server with no requests."""
844
        self.setUpServer()
845
        server = self.server
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
846
        transport = remote.RemoteTCPTransport(self.server.get_url())
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
847
        self.tearDownServer()
848
        self.assertRaises(errors.ConnectionError, transport.has, '.')
849
850
    def test_server_closes_listening_sock_on_shutdown_after_request(self):
2370.4.2 by Robert Collins
Review feedback.
851
        """The server should close its listening socket when it's stopped."""
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
852
        self.setUpServer()
2370.4.2 by Robert Collins
Review feedback.
853
        server = self.server
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
854
        self.transport.has('.')
855
        self.tearDownServer()
856
        # if the listening socket has closed, we should get a BADFD error
857
        # when connecting, rather than a hang.
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
858
        transport = remote.RemoteTCPTransport(server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
859
        self.assertRaises(errors.ConnectionError, transport.has, '.')
860
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
861
862
class WritableEndToEndTests(SmartTCPTests):
863
    """Client to server tests that require a writable transport."""
864
865
    def setUp(self):
866
        super(WritableEndToEndTests, self).setUp()
867
        self.setUpServer()
868
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
869
    def test_start_tcp_server(self):
870
        url = self.server.get_url()
871
        self.assertContainsRe(url, r'^bzr://127\.0\.0\.1:[0-9]{2,}/')
872
873
    def test_smart_transport_has(self):
874
        """Checking for file existence over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
875
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
876
        self.backing_transport.put_bytes("foo", "contents of foo\n")
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
877
        self.assertTrue(self.transport.has("foo"))
878
        self.assertFalse(self.transport.has("non-foo"))
879
880
    def test_smart_transport_get(self):
881
        """Read back a file over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
882
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
883
        self.backing_transport.put_bytes("foo", "contents\nof\nfoo\n")
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
884
        fp = self.transport.get("foo")
885
        self.assertEqual('contents\nof\nfoo\n', fp.read())
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
886
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
887
    def test_get_error_enoent(self):
888
        """Error reported from server getting nonexistent file."""
1910.19.3 by Andrew Bennetts
Add SSH support.
889
        # The path in a raised NoSuchFile exception should be the precise path
890
        # asked for by the client. This gives meaningful and unsurprising errors
891
        # for users.
2402.1.2 by Andrew Bennetts
Deal with review comments.
892
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
893
        try:
1910.19.3 by Andrew Bennetts
Add SSH support.
894
            self.transport.get('not%20a%20file')
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
895
        except errors.NoSuchFile, e:
1910.19.3 by Andrew Bennetts
Add SSH support.
896
            self.assertEqual('not%20a%20file', e.path)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
897
        else:
898
            self.fail("get did not raise expected error")
899
900
    def test_simple_clone_conn(self):
901
        """Test that cloning reuses the same connection."""
902
        # we create a real connection not a loopback one, but it will use the
903
        # same server and pipes
1910.19.3 by Andrew Bennetts
Add SSH support.
904
        conn2 = self.transport.clone('.')
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
905
        self.assertIs(self.transport._medium, conn2._medium)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
906
1910.19.12 by Andrew Bennetts
Activate a disabled test, rename another test to be consistent with what it's testing. (Andrew Bennetts, Robert Collins)
907
    def test__remote_path(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
908
        self.assertEquals('/foo/bar',
909
                          self.transport._remote_path('foo/bar'))
910
911
    def test_clone_changes_base(self):
912
        """Cloning transport produces one with a new base location"""
913
        conn2 = self.transport.clone('subdir')
914
        self.assertEquals(self.transport.base + 'subdir/',
915
                          conn2.base)
916
917
    def test_open_dir(self):
918
        """Test changing directory"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
919
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
920
        transport = self.transport
921
        self.backing_transport.mkdir('toffee')
922
        self.backing_transport.mkdir('toffee/apple')
923
        self.assertEquals('/toffee', transport._remote_path('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
924
        toffee_trans = transport.clone('toffee')
925
        # Check that each transport has only the contents of its directory
926
        # directly visible. If state was being held in the wrong object, it's
927
        # conceivable that cloning a transport would alter the state of the
928
        # cloned-from transport.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
929
        self.assertTrue(transport.has('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
930
        self.assertFalse(toffee_trans.has('toffee'))
931
        self.assertFalse(transport.has('apple'))
932
        self.assertTrue(toffee_trans.has('apple'))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
933
934
    def test_open_bzrdir(self):
935
        """Open an existing bzrdir over smart transport"""
936
        transport = self.transport
937
        t = self.backing_transport
938
        bzrdir.BzrDirFormat.get_default_format().initialize_on_transport(t)
939
        result_dir = bzrdir.BzrDir.open_containing_from_transport(transport)
940
941
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
942
class ReadOnlyEndToEndTests(SmartTCPTests):
943
    """Tests from the client to the server using a readonly backing transport."""
944
945
    def test_mkdir_error_readonly(self):
946
        """TransportNotPossible should be preserved from the backing transport."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
947
        self._captureVar('BZR_NO_SMART_VFS', None)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
948
        self.setUpServer(readonly=True)
949
        self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
950
            'foo')
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
951
952
953
class TestServerHooks(SmartTCPTests):
954
955
    def capture_server_call(self, backing_url, public_url):
956
        """Record a server_started|stopped hook firing."""
957
        self.hook_calls.append((backing_url, public_url))
958
959
    def test_server_started_hook(self):
960
        """The server_started hook fires when the server is started."""
961
        self.hook_calls = []
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
962
        server.SmartTCPServer.hooks.install_hook('server_started',
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
963
            self.capture_server_call)
964
        self.setUpServer()
965
        # at this point, the server will be starting a thread up.
966
        # there is no indicator at the moment, so bodge it by doing a request.
967
        self.transport.has('.')
968
        self.assertEqual([(self.backing_transport.base, self.transport.base)],
969
            self.hook_calls)
970
971
    def test_server_stopped_hook_simple(self):
972
        """The server_stopped hook fires when the server is stopped."""
973
        self.hook_calls = []
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
974
        server.SmartTCPServer.hooks.install_hook('server_stopped',
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
975
            self.capture_server_call)
976
        self.setUpServer()
977
        result = [(self.backing_transport.base, self.transport.base)]
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
978
        # check the stopping message isn't emitted up front.
979
        self.assertEqual([], self.hook_calls)
980
        # nor after a single message
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
981
        self.transport.has('.')
982
        self.assertEqual([], self.hook_calls)
983
        # clean up the server
984
        self.tearDownServer()
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
985
        # now it should have fired.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
986
        self.assertEqual(result, self.hook_calls)
987
988
# TODO: test that when the server suffers an exception that it calls the
989
# server-stopped hook.
990
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
991
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
992
class SmartServerCommandTests(tests.TestCaseWithTransport):
993
    """Tests that call directly into the command objects, bypassing the network
994
    and the request dispatching.
995
    """
996
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
997
    def test_hello(self):
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
998
        cmd = request.HelloRequest(None)
999
        response = cmd.execute()
1000
        self.assertEqual(('ok', '1'), response.args)
1001
        self.assertEqual(None, response.body)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1002
        
1003
    def test_get_bundle(self):
1004
        from bzrlib.bundle import serializer
1005
        wt = self.make_branch_and_tree('.')
1910.19.13 by Andrew Bennetts
Address various review comments.
1006
        self.build_tree_contents([('hello', 'hello world')])
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1007
        wt.add('hello')
1910.19.13 by Andrew Bennetts
Address various review comments.
1008
        rev_id = wt.commit('add hello')
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1009
        
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1010
        cmd = request.GetBundleRequest(self.get_transport())
1011
        response = cmd.execute('.', rev_id)
1012
        bundle = serializer.read_bundle(StringIO(response.body))
1013
        self.assertEqual((), response.args)
1014
1015
1016
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
1017
    """Test that call directly into the handler logic, bypassing the network."""
1018
1019
    def setUp(self):
1020
        super(SmartServerRequestHandlerTests, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
1021
        self._captureVar('BZR_NO_SMART_VFS', None)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1022
1023
    def build_handler(self, transport):
1024
        """Returns a handler for the commands in protocol version one."""
1025
        return request.SmartServerRequestHandler(transport, request.request_handlers)
1026
1027
    def test_construct_request_handler(self):
1028
        """Constructing a request handler should be easy and set defaults."""
1029
        handler = request.SmartServerRequestHandler(None, None)
1030
        self.assertFalse(handler.finished_reading)
1031
1032
    def test_hello(self):
1033
        handler = self.build_handler(None)
1034
        handler.dispatch_command('hello', ())
1035
        self.assertEqual(('ok', '1'), handler.response.args)
1036
        self.assertEqual(None, handler.response.body)
1037
        
1038
    def test_disable_vfs_handler_classes_via_environment(self):
2402.1.2 by Andrew Bennetts
Deal with review comments.
1039
        # VFS handler classes will raise an error from "execute" if BZR_NO_SMART_VFS
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1040
        # is set.
1041
        handler = vfs.HasRequest(None)
1042
        # set environment variable after construction to make sure it's
1043
        # examined.
2402.1.2 by Andrew Bennetts
Deal with review comments.
1044
        # Note that we can safely clobber BZR_NO_SMART_VFS here, because setUp has
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1045
        # called _captureVar, so it will be restored to the right state
1046
        # afterwards.
2402.1.2 by Andrew Bennetts
Deal with review comments.
1047
        os.environ['BZR_NO_SMART_VFS'] = ''
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1048
        self.assertRaises(errors.DisabledMethod, handler.execute)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1049
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1050
    def test_readonly_exception_becomes_transport_not_possible(self):
1051
        """The response for a read-only error is ('ReadOnlyError')."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1052
        handler = self.build_handler(self.get_readonly_transport())
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1053
        # send a mkdir for foo, with no explicit mode - should fail.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1054
        handler.dispatch_command('mkdir', ('foo', ''))
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1055
        # and the failure should be an explicit ReadOnlyError
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1056
        self.assertEqual(("ReadOnlyError", ), handler.response.args)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1057
        # XXX: TODO: test that other TransportNotPossible errors are
1058
        # presented as TransportNotPossible - not possible to do that
1059
        # until I figure out how to trigger that relatively cleanly via
1060
        # the api. RBC 20060918
1061
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1062
    def test_hello_has_finished_body_on_dispatch(self):
1063
        """The 'hello' command should set finished_reading."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1064
        handler = self.build_handler(None)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1065
        handler.dispatch_command('hello', ())
1066
        self.assertTrue(handler.finished_reading)
1067
        self.assertNotEqual(None, handler.response)
1068
1069
    def test_put_bytes_non_atomic(self):
1070
        """'put_...' should set finished_reading after reading the bytes."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1071
        handler = self.build_handler(self.get_transport())
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
1072
        handler.dispatch_command('put_non_atomic', ('a-file', '', 'F', ''))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1073
        self.assertFalse(handler.finished_reading)
1074
        handler.accept_body('1234')
1075
        self.assertFalse(handler.finished_reading)
1076
        handler.accept_body('5678')
1077
        handler.end_of_body()
1078
        self.assertTrue(handler.finished_reading)
1079
        self.assertEqual(('ok', ), handler.response.args)
1080
        self.assertEqual(None, handler.response.body)
1081
        
1082
    def test_readv_accept_body(self):
1083
        """'readv' should set finished_reading after reading offsets."""
1084
        self.build_tree(['a-file'])
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1085
        handler = self.build_handler(self.get_readonly_transport())
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
1086
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1087
        self.assertFalse(handler.finished_reading)
1088
        handler.accept_body('2,')
1089
        self.assertFalse(handler.finished_reading)
1090
        handler.accept_body('3')
1091
        handler.end_of_body()
1092
        self.assertTrue(handler.finished_reading)
1093
        self.assertEqual(('readv', ), handler.response.args)
1094
        # co - nte - nt of a-file is the file contents we are extracting from.
1095
        self.assertEqual('nte', handler.response.body)
1096
1097
    def test_readv_short_read_response_contents(self):
1098
        """'readv' when a short read occurs sets the response appropriately."""
1099
        self.build_tree(['a-file'])
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1100
        handler = self.build_handler(self.get_readonly_transport())
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
1101
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1102
        # read beyond the end of the file.
1103
        handler.accept_body('100,1')
1104
        handler.end_of_body()
1105
        self.assertTrue(handler.finished_reading)
1106
        self.assertEqual(('ShortReadvError', 'a-file', '100', '1', '0'),
1107
            handler.response.args)
1108
        self.assertEqual(None, handler.response.body)
1109
1910.19.3 by Andrew Bennetts
Add SSH support.
1110
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1111
class RemoteTransportRegistration(tests.TestCase):
1910.19.3 by Andrew Bennetts
Add SSH support.
1112
1113
    def test_registration(self):
1114
        t = get_transport('bzr+ssh://example.com/path')
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
1115
        self.assertIsInstance(t, remote.RemoteSSHTransport)
1910.19.3 by Andrew Bennetts
Add SSH support.
1116
        self.assertEqual('example.com', t._host)
1117
1118
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1119
class TestRemoteTransport(tests.TestCase):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1120
        
1121
    def test_use_connection_factory(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1122
        # We want to be able to pass a client as a parameter to RemoteTransport.
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1123
        input = StringIO("ok\n3\nbardone\n")
1124
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1125
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
1126
        transport = remote.RemoteTransport(
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1127
            'bzr://localhost/', medium=client_medium)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1128
1129
        # We want to make sure the client is used when the first remote
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1130
        # method is called.  No data should have been sent, or read.
1131
        self.assertEqual(0, input.tell())
1132
        self.assertEqual('', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1133
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1134
        # Now call a method that should result in a single request : as the
1135
        # transport makes its own protocol instances, we check on the wire.
1136
        # XXX: TODO: give the transport a protocol factory, which can make
1137
        # an instrumented protocol for us.
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1138
        self.assertEqual('bar', transport.get_bytes('foo'))
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1139
        # only the needed data should have been sent/received.
1140
        self.assertEqual(13, input.tell())
1141
        self.assertEqual('get\x01/foo\n', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1142
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1143
    def test__translate_error_readonly(self):
1144
        """Sending a ReadOnlyError to _translate_error raises TransportNotPossible."""
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1145
        client_medium = medium.SmartClientMedium()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
1146
        transport = remote.RemoteTransport(
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1147
            'bzr://localhost/', medium=client_medium)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1148
        self.assertRaises(errors.TransportNotPossible,
1149
            transport._translate_error, ("ReadOnlyError", ))
1150
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1151
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1152
class InstrumentedServerProtocol(medium.SmartServerStreamMedium):
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1153
    """A smart server which is backed by memory and saves its write requests."""
1154
1155
    def __init__(self, write_output_list):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1156
        medium.SmartServerStreamMedium.__init__(self, memory.MemoryTransport())
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1157
        self._write_output_list = write_output_list
1158
1159
1160
class TestSmartProtocol(tests.TestCase):
1161
    """Tests for the smart protocol.
1162
1163
    Each test case gets a smart_server and smart_client created during setUp().
1164
1165
    It is planned that the client can be called with self.call_client() giving
1166
    it an expected server response, which will be fed into it when it tries to
1167
    read. Likewise, self.call_server will call a servers method with a canned
1168
    serialised client request. Output done by the client or server for these
1169
    calls will be captured to self.to_server and self.to_client. Each element
1170
    in the list is a write call from the client or server respectively.
1171
    """
1172
1173
    def setUp(self):
1174
        super(TestSmartProtocol, self).setUp()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1175
        # XXX: self.server_to_client doesn't seem to be used.  If so,
1176
        # InstrumentedServerProtocol is redundant too.
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1177
        self.server_to_client = []
1178
        self.to_server = StringIO()
1179
        self.to_client = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1180
        self.client_medium = medium.SmartSimplePipesClientMedium(self.to_client,
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1181
            self.to_server)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1182
        self.client_protocol = protocol.SmartClientRequestProtocolOne(
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1183
            self.client_medium)
1184
        self.smart_server = InstrumentedServerProtocol(self.server_to_client)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1185
        self.smart_server_request = request.SmartServerRequestHandler(
1186
            None, request.request_handlers)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1187
1188
    def assertOffsetSerialisation(self, expected_offsets, expected_serialised,
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1189
        client):
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1190
        """Check that smart (de)serialises offsets as expected.
1191
        
1192
        We check both serialisation and deserialisation at the same time
1193
        to ensure that the round tripping cannot skew: both directions should
1194
        be as expected.
1195
        
1196
        :param expected_offsets: a readv offset list.
1197
        :param expected_seralised: an expected serial form of the offsets.
1198
        """
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1199
        # XXX: '_deserialise_offsets' should be a method of the
1200
        # SmartServerRequestProtocol in future.
1201
        readv_cmd = vfs.ReadvRequest(None)
1202
        offsets = readv_cmd._deserialise_offsets(expected_serialised)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1203
        self.assertEqual(expected_offsets, offsets)
1204
        serialised = client._serialise_offsets(offsets)
1205
        self.assertEqual(expected_serialised, serialised)
1206
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1207
    def build_protocol_waiting_for_body(self):
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1208
        out_stream = StringIO()
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1209
        smart_protocol = protocol.SmartServerRequestProtocolOne(None,
1210
                out_stream.write)
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1211
        smart_protocol.has_dispatched = True
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1212
        smart_protocol.request = self.smart_server_request
1213
        class FakeCommand(object):
1214
            def do_body(cmd, body_bytes):
1215
                self.end_received = True
1216
                self.assertEqual('abcdefg', body_bytes)
1217
                return request.SmartServerResponse(('ok', ))
1218
        smart_protocol.request._command = FakeCommand()
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1219
        # Call accept_bytes to make sure that internal state like _body_decoder
1220
        # is initialised.  This test should probably be given a clearer
1221
        # interface to work with that will not cause this inconsistency.
1222
        #   -- Andrew Bennetts, 2006-09-28
1223
        smart_protocol.accept_bytes('')
1224
        return smart_protocol
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1225
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1226
    def test_construct_version_one_server_protocol(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1227
        smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
1228
        self.assertEqual('', smart_protocol.excess_buffer)
1229
        self.assertEqual('', smart_protocol.in_buffer)
1230
        self.assertFalse(smart_protocol.has_dispatched)
1231
        self.assertEqual(1, smart_protocol.next_read_size())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1232
1233
    def test_construct_version_one_client_protocol(self):
1234
        # we can construct a client protocol from a client medium request
1235
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1236
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1237
        request = client_medium.get_request()
1238
        client_protocol = protocol.SmartClientRequestProtocolOne(request)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1239
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1240
    def test_server_offset_serialisation(self):
1241
        """The Smart protocol serialises offsets as a comma and \n string.
1242
1243
        We check a number of boundary cases are as expected: empty, one offset,
1244
        one with the order of reads not increasing (an out of order read), and
1245
        one that should coalesce.
1246
        """
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1247
        self.assertOffsetSerialisation([], '', self.client_protocol)
1248
        self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1249
        self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1250
            self.client_protocol)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1251
        self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1252
            '1,2\n3,4\n100,200', self.client_protocol)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1253
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1254
    def test_accept_bytes_of_bad_request_to_protocol(self):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1255
        out_stream = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1256
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1257
            None, out_stream.write)
1258
        smart_protocol.accept_bytes('abc')
1259
        self.assertEqual('abc', smart_protocol.in_buffer)
1260
        smart_protocol.accept_bytes('\n')
1261
        self.assertEqual(
1262
            "error\x01Generic bzr smart protocol error: bad request 'abc'\n",
1263
            out_stream.getvalue())
1264
        self.assertTrue(smart_protocol.has_dispatched)
1265
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1266
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1267
    def test_accept_body_bytes_to_protocol(self):
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1268
        protocol = self.build_protocol_waiting_for_body()
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1269
        self.assertEqual(6, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1270
        protocol.accept_bytes('7\nabc')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1271
        self.assertEqual(9, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1272
        protocol.accept_bytes('defgd')
1273
        protocol.accept_bytes('one\n')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1274
        self.assertEqual(0, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1275
        self.assertTrue(self.end_received)
1276
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1277
    def test_accept_request_and_body_all_at_once(self):
2402.1.2 by Andrew Bennetts
Deal with review comments.
1278
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1279
        mem_transport = memory.MemoryTransport()
1280
        mem_transport.put_bytes('foo', 'abcdefghij')
1281
        out_stream = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1282
        smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
1283
                out_stream.write)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1284
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1285
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1286
        self.assertEqual('readv\n3\ndefdone\n', out_stream.getvalue())
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1287
        self.assertEqual('', smart_protocol.excess_buffer)
1288
        self.assertEqual('', smart_protocol.in_buffer)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1289
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1290
    def test_accept_excess_bytes_are_preserved(self):
1291
        out_stream = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1292
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1293
            None, out_stream.write)
1294
        smart_protocol.accept_bytes('hello\nhello\n')
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1295
        self.assertEqual("ok\x011\n", out_stream.getvalue())
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1296
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1297
        self.assertEqual("", smart_protocol.in_buffer)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1298
1299
    def test_accept_excess_bytes_after_body(self):
1300
        protocol = self.build_protocol_waiting_for_body()
1301
        protocol.accept_bytes('7\nabcdefgdone\nX')
1302
        self.assertTrue(self.end_received)
1303
        self.assertEqual("X", protocol.excess_buffer)
1304
        self.assertEqual("", protocol.in_buffer)
1305
        protocol.accept_bytes('Y')
1306
        self.assertEqual("XY", protocol.excess_buffer)
1307
        self.assertEqual("", protocol.in_buffer)
1308
1309
    def test_accept_excess_bytes_after_dispatch(self):
1310
        out_stream = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1311
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1312
            None, out_stream.write)
1313
        smart_protocol.accept_bytes('hello\n')
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1314
        self.assertEqual("ok\x011\n", out_stream.getvalue())
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1315
        smart_protocol.accept_bytes('hel')
1316
        self.assertEqual("hel", smart_protocol.excess_buffer)
1317
        smart_protocol.accept_bytes('lo\n')
1318
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1319
        self.assertEqual("", smart_protocol.in_buffer)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1320
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1321
    def test__send_response_sets_finished_reading(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1322
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1323
            None, lambda x: None)
1324
        self.assertEqual(1, smart_protocol.next_read_size())
1325
        smart_protocol._send_response(('x',))
1326
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1327
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1328
    def test_query_version(self):
1329
        """query_version on a SmartClientProtocolOne should return a number.
1330
        
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1331
        The protocol provides the query_version because the domain level clients
1332
        may all need to be able to probe for capabilities.
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1333
        """
1334
        # What we really want to test here is that SmartClientProtocolOne calls
1335
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1336
        # response of tuple-encoded (ok, 1).  Also, seperately we should test
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1337
        # the error if the response is a non-understood version.
1338
        input = StringIO('ok\x011\n')
1339
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1340
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1341
        request = client_medium.get_request()
1342
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1343
        self.assertEqual(1, smart_protocol.query_version())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1344
1345
    def assertServerToClientEncoding(self, expected_bytes, expected_tuple,
1346
            input_tuples):
1347
        """Assert that each input_tuple serialises as expected_bytes, and the
1348
        bytes deserialise as expected_tuple.
1349
        """
1350
        # check the encoding of the server for all input_tuples matches
1351
        # expected bytes
1352
        for input_tuple in input_tuples:
1353
            server_output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1354
            server_protocol = protocol.SmartServerRequestProtocolOne(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
1355
                None, server_output.write)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1356
            server_protocol._send_response(input_tuple)
1357
            self.assertEqual(expected_bytes, server_output.getvalue())
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1358
        # check the decoding of the client smart_protocol from expected_bytes:
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1359
        input = StringIO(expected_bytes)
1360
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1361
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1362
        request = client_medium.get_request()
1363
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1364
        smart_protocol.call('foo')
1365
        self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1366
1367
    def test_client_call_empty_response(self):
1368
        # protocol.call() can get back an empty tuple as a response. This occurs
1369
        # when the parsed line is an empty line, and results in a tuple with
1370
        # one element - an empty string.
1371
        self.assertServerToClientEncoding('\n', ('', ), [(), ('', )])
1372
1373
    def test_client_call_three_element_response(self):
1374
        # protocol.call() can get back tuples of other lengths. A three element
1375
        # tuple should be unpacked as three strings.
1376
        self.assertServerToClientEncoding('a\x01b\x0134\n', ('a', 'b', '34'),
1377
            [('a', 'b', '34')])
1378
1379
    def test_client_call_with_body_bytes_uploads(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1380
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1381
        # wire.
1382
        expected_bytes = "foo\n7\nabcdefgdone\n"
1383
        input = StringIO("\n")
1384
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1385
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1386
        request = client_medium.get_request()
1387
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1388
        smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1389
        self.assertEqual(expected_bytes, output.getvalue())
1390
1391
    def test_client_call_with_body_readv_array(self):
1392
        # protocol.call_with_upload should encode the readv array and then
1393
        # length-prefix the bytes onto the wire.
1394
        expected_bytes = "foo\n7\n1,2\n5,6done\n"
1395
        input = StringIO("\n")
1396
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1397
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1398
        request = client_medium.get_request()
1399
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1400
        smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1401
        self.assertEqual(expected_bytes, output.getvalue())
1402
1403
    def test_client_read_body_bytes_all(self):
1404
        # read_body_bytes should decode the body bytes from the wire into
1405
        # a response.
1406
        expected_bytes = "1234567"
1407
        server_bytes = "ok\n7\n1234567done\n"
1408
        input = StringIO(server_bytes)
1409
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1410
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1411
        request = client_medium.get_request()
1412
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1413
        smart_protocol.call('foo')
1414
        smart_protocol.read_response_tuple(True)
1415
        self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1416
1417
    def test_client_read_body_bytes_incremental(self):
1418
        # test reading a few bytes at a time from the body
1419
        # XXX: possibly we should test dribbling the bytes into the stringio
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1420
        # to make the state machine work harder: however, as we use the
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1421
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1422
        # that.
1423
        expected_bytes = "1234567"
1424
        server_bytes = "ok\n7\n1234567done\n"
1425
        input = StringIO(server_bytes)
1426
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1427
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1428
        request = client_medium.get_request()
1429
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1430
        smart_protocol.call('foo')
1431
        smart_protocol.read_response_tuple(True)
1432
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1433
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1434
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1435
        self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1436
1437
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1438
        # cancelling the expected body needs to finish the request, but not
1439
        # read any more bytes.
1440
        expected_bytes = "1234567"
1441
        server_bytes = "ok\n7\n1234567done\n"
1442
        input = StringIO(server_bytes)
1443
        output = StringIO()
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1444
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1445
        request = client_medium.get_request()
1446
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1447
        smart_protocol.call('foo')
1448
        smart_protocol.read_response_tuple(True)
1449
        smart_protocol.cancel_read_body()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1450
        self.assertEqual(3, input.tell())
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1451
        self.assertRaises(
1452
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1453
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1454
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
1455
class TestSmartClientUnicode(tests.TestCase):
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1456
    """_SmartClient tests for unicode arguments.
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
1457
1458
    Unicode arguments to call_with_body_bytes are not correct (remote method
1459
    names, arguments, and bodies must all be expressed as byte strings), but
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1460
    _SmartClient should gracefully reject them, rather than getting into a
1461
    broken state that prevents future correct calls from working.  That is, it
1462
    should be possible to issue more requests on the medium afterwards, rather
1463
    than allowing one bad call to call_with_body_bytes to cause later calls to
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
1464
    mysteriously fail with TooManyConcurrentRequests.
1465
    """
1466
1467
    def assertCallDoesNotBreakMedium(self, method, args, body):
1468
        """Call a medium with the given method, args and body, then assert that
1469
        the medium is left in a sane state, i.e. is capable of allowing further
1470
        requests.
1471
        """
1472
        input = StringIO("\n")
1473
        output = StringIO()
1474
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1475
        smart_client = client._SmartClient(client_medium)
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
1476
        self.assertRaises(TypeError,
1477
            smart_client.call_with_body_bytes, method, args, body)
1478
        self.assertEqual("", output.getvalue())
1479
        self.assertEqual(None, client_medium._current_request)
1480
1481
    def test_call_with_body_bytes_unicode_method(self):
1482
        self.assertCallDoesNotBreakMedium(u'method', ('args',), 'body')
1483
1484
    def test_call_with_body_bytes_unicode_args(self):
1485
        self.assertCallDoesNotBreakMedium('method', (u'args',), 'body')
2414.1.2 by Andrew Bennetts
Deal with review comments.
1486
        self.assertCallDoesNotBreakMedium('method', ('arg1', u'arg2'), 'body')
2414.1.1 by Andrew Bennetts
Add some unicode-related tests from the hpss branch, and a few other nits (also from the hpss branch).
1487
1488
    def test_call_with_body_bytes_unicode_body(self):
1489
        self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
1490
1491
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1492
class LengthPrefixedBodyDecoder(tests.TestCase):
1493
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1494
    # XXX: TODO: make accept_reading_trailer invoke translate_response or 
1495
    # something similar to the ProtocolBase method.
1496
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1497
    def test_construct(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1498
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1499
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1500
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1501
        self.assertEqual('', decoder.read_pending_data())
1502
        self.assertEqual('', decoder.unused_data)
1503
1504
    def test_accept_bytes(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1505
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1506
        decoder.accept_bytes('')
1507
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1508
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1509
        self.assertEqual('', decoder.read_pending_data())
1510
        self.assertEqual('', decoder.unused_data)
1511
        decoder.accept_bytes('7')
1512
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1513
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1514
        self.assertEqual('', decoder.read_pending_data())
1515
        self.assertEqual('', decoder.unused_data)
1516
        decoder.accept_bytes('\na')
1517
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1518
        self.assertEqual(11, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1519
        self.assertEqual('a', decoder.read_pending_data())
1520
        self.assertEqual('', decoder.unused_data)
1521
        decoder.accept_bytes('bcdefgd')
1522
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1523
        self.assertEqual(4, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1524
        self.assertEqual('bcdefg', decoder.read_pending_data())
1525
        self.assertEqual('', decoder.unused_data)
1526
        decoder.accept_bytes('one')
1527
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1528
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1529
        self.assertEqual('', decoder.read_pending_data())
1530
        self.assertEqual('', decoder.unused_data)
1531
        decoder.accept_bytes('\nblarg')
1532
        self.assertTrue(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1533
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1534
        self.assertEqual('', decoder.read_pending_data())
1535
        self.assertEqual('blarg', decoder.unused_data)
1536
        
1537
    def test_accept_bytes_all_at_once_with_excess(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1538
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1539
        decoder.accept_bytes('1\nadone\nunused')
1540
        self.assertTrue(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1541
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1542
        self.assertEqual('a', decoder.read_pending_data())
1543
        self.assertEqual('unused', decoder.unused_data)
1544
1545
    def test_accept_bytes_exact_end_of_body(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1546
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1547
        decoder.accept_bytes('1\na')
1548
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1549
        self.assertEqual(5, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1550
        self.assertEqual('a', decoder.read_pending_data())
1551
        self.assertEqual('', decoder.unused_data)
1552
        decoder.accept_bytes('done\n')
1553
        self.assertTrue(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1554
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1555
        self.assertEqual('', decoder.read_pending_data())
1556
        self.assertEqual('', decoder.unused_data)
1557
1558
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1559
class FakeHTTPMedium(object):
1560
    def __init__(self):
1561
        self.written_request = None
1562
        self._current_request = None
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1563
    def send_http_smart_request(self, bytes):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1564
        self.written_request = bytes
1565
        return None
1566
1567
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1568
class HTTPTunnellingSmokeTest(tests.TestCaseWithTransport):
1569
    
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1570
    def setUp(self):
1571
        super(HTTPTunnellingSmokeTest, self).setUp()
1572
        # We use the VFS layer as part of HTTP tunnelling tests.
2402.1.2 by Andrew Bennetts
Deal with review comments.
1573
        self._captureVar('BZR_NO_SMART_VFS', None)
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1574
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1575
    def _test_bulk_data(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1576
        # We should be able to send and receive bulk data in a single message.
1577
        # The 'readv' command in the smart protocol both sends and receives bulk
1578
        # data, so we use that.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1579
        self.build_tree(['data-file'])
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1580
        self.transport_readonly_server = HTTPServerWithSmarts
1581
1582
        http_transport = self.get_readonly_transport()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1583
        medium = http_transport.get_smart_medium()
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1584
        #remote_transport = RemoteTransport('fake_url', medium)
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
1585
        remote_transport = remote.RemoteTransport('/', medium=medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1586
        self.assertEqual(
1587
            [(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1588
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1589
    def test_bulk_data_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1590
        try:
1591
            self._test_bulk_data('http+pycurl')
1592
        except errors.UnsupportedProtocol, e:
1593
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1594
    
1595
    def test_bulk_data_urllib(self):
1596
        self._test_bulk_data('http+urllib')
1597
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1598
    def test_smart_http_medium_request_accept_bytes(self):
1599
        medium = FakeHTTPMedium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1600
        request = SmartClientHTTPMediumRequest(medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1601
        request.accept_bytes('abc')
1602
        request.accept_bytes('def')
1603
        self.assertEqual(None, medium.written_request)
1604
        request.finished_writing()
1605
        self.assertEqual('abcdef', medium.written_request)
1606
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1607
    def _test_http_send_smart_request(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1608
        http_server = HTTPServerWithSmarts()
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1609
        http_server._url_protocol = url_protocol
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1610
        http_server.setUp(self.get_vfs_only_server())
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1611
        self.addCleanup(http_server.tearDown)
1612
1613
        post_body = 'hello\n'
1614
        expected_reply_body = 'ok\x011\n'
1615
1616
        http_transport = get_transport(http_server.get_url())
1617
        medium = http_transport.get_smart_medium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1618
        response = medium.send_http_smart_request(post_body)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1619
        reply_body = response.read()
1620
        self.assertEqual(expected_reply_body, reply_body)
1621
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1622
    def test_http_send_smart_request_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1623
        try:
1624
            self._test_http_send_smart_request('http+pycurl')
1625
        except errors.UnsupportedProtocol, e:
1626
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1627
1628
    def test_http_send_smart_request_urllib(self):
1629
        self._test_http_send_smart_request('http+urllib')
1630
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1631
    def test_http_server_with_smarts(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1632
        self.transport_readonly_server = HTTPServerWithSmarts
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1633
1634
        post_body = 'hello\n'
1635
        expected_reply_body = 'ok\x011\n'
1636
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1637
        smart_server_url = self.get_readonly_url('.bzr/smart')
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1638
        reply = urllib2.urlopen(smart_server_url, post_body).read()
1639
1640
        self.assertEqual(expected_reply_body, reply)
1641
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1642
    def test_smart_http_server_post_request_handler(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1643
        self.transport_readonly_server = HTTPServerWithSmarts
1644
        httpd = self.get_readonly_server()._get_httpd()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1645
1646
        socket = SampleSocket(
1647
            'POST /.bzr/smart HTTP/1.0\r\n'
1648
            # HTTP/1.0 posts must have a Content-Length.
1649
            'Content-Length: 6\r\n'
1650
            '\r\n'
1651
            'hello\n')
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
1652
        # Beware: the ('localhost', 80) below is the
1653
        # client_address parameter, but we don't have one because
2164.2.14 by v.ladeuil+lp at free
Typo in comment
1654
        # we have defined a socket which is not bound to an
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
1655
        # address. The test framework never uses this client
1656
        # address, so far...
1657
        request_handler = SmartRequestHandler(socket, ('localhost', 80), httpd)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1658
        response = socket.writefile.getvalue()
1659
        self.assertStartsWith(response, 'HTTP/1.0 200 ')
1660
        # This includes the end of the HTTP headers, and all the body.
1661
        expected_end_of_response = '\r\n\r\nok\x011\n'
1662
        self.assertEndsWith(response, expected_end_of_response)
1663
1664
1665
class SampleSocket(object):
1666
    """A socket-like object for use in testing the HTTP request handler."""
1667
    
1668
    def __init__(self, socket_read_content):
1669
        """Constructs a sample socket.
1670
1671
        :param socket_read_content: a byte sequence
1672
        """
1673
        # Use plain python StringIO so we can monkey-patch the close method to
1674
        # not discard the contents.
1675
        from StringIO import StringIO
1676
        self.readfile = StringIO(socket_read_content)
1677
        self.writefile = StringIO()
1678
        self.writefile.close = lambda: None
1679
        
1680
    def makefile(self, mode='r', bufsize=None):
1681
        if 'r' in mode:
1682
            return self.readfile
1683
        else:
1684
            return self.writefile
1685
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1686
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1687
# TODO: Client feature that does get_bundle and then installs that into a
1688
# branch; this can be used in place of the regular pull/fetch operation when
1689
# coming from a smart server.
1690
#
1691
# TODO: Eventually, want to do a 'branch' command by fetching the whole
1692
# history as one big bundle.  How?  
1693
#
1694
# The branch command does 'br_from.sprout', which tries to preserve the same
1695
# format.  We don't necessarily even want that.  
1696
#
1697
# It might be simpler to handle cmd_pull first, which does a simpler fetch()
1698
# operation from one branch into another.  It already has some code for
1699
# pulling from a bundle, which it does by trying to see if the destination is
1700
# a bundle file.  So it seems the logic for pull ought to be:
1701
# 
1702
#  - if it's a smart server, get a bundle from there and install that
1703
#  - if it's a bundle, install that
1704
#  - if it's a branch, pull from there
1705
#
1706
# Getting a bundle from a smart server is a bit different from reading a
1707
# bundle from a URL:
1708
#
1709
#  - we can reasonably remember the URL we last read from 
1710
#  - you can specify a revision number to pull, and we need to pass it across
1711
#    to the server as a limit on what will be requested
1712
#
1713
# TODO: Given a URL, determine whether it is a smart server or not (or perhaps
1714
# otherwise whether it's a bundle?)  Should this be a property or method of
1715
# the transport?  For the ssh protocol, we always know it's a smart server.
1716
# For http, we potentially need to probe.  But if we're explicitly given
1717
# bzr+http:// then we can skip that for now.