~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
        )
2018.5.21 by Andrew Bennetts
Move bzrlib.transport.smart to bzrlib.smart
33
from bzrlib.smart import (
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
34
        client,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
35
        medium,
36
        protocol,
2432.4.3 by Robert Collins
Refactor the HPSS Response code to take SmartServerResponse rather than args and body.
37
        request as _mod_request,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
38
        server,
39
        vfs,
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
        )
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
45
from bzrlib.tests.test_smart import TestCaseWithSmartMedium
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
46
from bzrlib.transport import (
47
        get_transport,
48
        local,
49
        memory,
2400.1.1 by Andrew Bennetts
Rename bzrlib/transport/smart.py to bzrlib/transport/remote.py.
50
        remote,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
51
        )
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
52
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
53
54
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
55
class StringIOSSHVendor(object):
56
    """A SSH vendor that uses StringIO to buffer writes and answer reads."""
57
58
    def __init__(self, read_from, write_to):
59
        self.read_from = read_from
60
        self.write_to = write_to
61
        self.calls = []
62
63
    def connect_ssh(self, username, password, host, port, command):
64
        self.calls.append(('connect_ssh', username, password, host, port,
65
            command))
66
        return StringIOSSHConnection(self)
67
68
69
class StringIOSSHConnection(object):
70
    """A SSH connection that uses StringIO to buffer writes and answer reads."""
71
72
    def __init__(self, vendor):
73
        self.vendor = vendor
74
    
75
    def close(self):
76
        self.vendor.calls.append(('close', ))
77
        
78
    def get_filelike_channels(self):
79
        return self.vendor.read_from, self.vendor.write_to
80
81
82
83
class SmartClientMediumTests(tests.TestCase):
84
    """Tests for SmartClientMedium.
85
86
    We should create a test scenario for this: we need a server module that
87
    construct the test-servers (like make_loopsocket_and_medium), and the list
88
    of SmartClientMedium classes to test.
89
    """
90
91
    def make_loopsocket_and_medium(self):
92
        """Create a loopback socket for testing, and a medium aimed at it."""
93
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
94
        sock.bind(('127.0.0.1', 0))
95
        sock.listen(1)
96
        port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
97
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', port)
98
        return sock, client_medium
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
99
100
    def receive_bytes_on_server(self, sock, bytes):
101
        """Accept a connection on sock and read 3 bytes.
102
103
        The bytes are appended to the list bytes.
104
105
        :return: a Thread which is running to do the accept and recv.
106
        """
107
        def _receive_bytes_on_server():
108
            connection, address = sock.accept()
2091.1.1 by Martin Pool
Avoid MSG_WAITALL as it doesn't work on Windows
109
            bytes.append(osutils.recv_all(connection, 3))
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
110
            connection.close()
111
        t = threading.Thread(target=_receive_bytes_on_server)
112
        t.start()
113
        return t
114
    
115
    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.
116
        # make a new instance of the common base for Stream-like Mediums.
117
        # this just ensures that the constructor stays parameter-free which
118
        # is important for reuse : some subclasses will dynamically connect,
119
        # others are always on, etc.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
120
        client_medium = medium.SmartClientStreamMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
121
122
    def test_construct_smart_client_medium(self):
123
        # the base client medium takes no parameters
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
124
        client_medium = medium.SmartClientMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
125
    
126
    def test_construct_smart_simple_pipes_client_medium(self):
127
        # the SimplePipes client medium takes two pipes:
128
        # readable pipe, writeable pipe.
129
        # Constructing one should just save these and do nothing.
130
        # We test this by passing in None.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
131
        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.
132
        
133
    def test_simple_pipes_client_request_type(self):
134
        # SimplePipesClient should use SmartClientStreamMediumRequest's.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
135
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
136
        request = client_medium.get_request()
137
        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.
138
139
    def test_simple_pipes_client_get_concurrent_requests(self):
140
        # the simple_pipes client does not support pipelined requests:
141
        # but it does support serial requests: we construct one after 
142
        # another is finished. This is a smoke test testing the integration
143
        # of the SmartClientStreamMediumRequest and the SmartClientStreamMedium
144
        # classes - as the sibling classes share this logic, they do not have
145
        # explicit tests for this.
146
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
147
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
148
        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.
149
        request.finished_writing()
150
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
151
        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.
152
        request2.finished_writing()
153
        request2.finished_reading()
154
155
    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.
156
        # accept_bytes writes to the writeable pipe.
157
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
158
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
159
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
160
        self.assertEqual('abc', output.getvalue())
161
    
162
    def test_simple_pipes_client_disconnect_does_nothing(self):
163
        # calling disconnect does nothing.
164
        input = StringIO()
165
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
166
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
167
        # send some bytes to ensure disconnecting after activity still does not
168
        # close.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
169
        client_medium._accept_bytes('abc')
170
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
171
        self.assertFalse(input.closed)
172
        self.assertFalse(output.closed)
173
174
    def test_simple_pipes_client_accept_bytes_after_disconnect(self):
175
        # calling disconnect on the client does not alter the pipe that
176
        # accept_bytes writes to.
177
        input = StringIO()
178
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
179
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
180
        client_medium._accept_bytes('abc')
181
        client_medium.disconnect()
182
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
183
        self.assertFalse(input.closed)
184
        self.assertFalse(output.closed)
185
        self.assertEqual('abcabc', output.getvalue())
186
    
187
    def test_simple_pipes_client_ignores_disconnect_when_not_connected(self):
188
        # Doing a disconnect on a new (and thus unconnected) SimplePipes medium
189
        # does nothing.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
190
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
191
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
192
193
    def test_simple_pipes_client_can_always_read(self):
194
        # SmartSimplePipesClientMedium is never disconnected, so read_bytes
195
        # always tries to read from the underlying pipe.
196
        input = StringIO('abcdef')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
197
        client_medium = medium.SmartSimplePipesClientMedium(input, None)
198
        self.assertEqual('abc', client_medium.read_bytes(3))
199
        client_medium.disconnect()
200
        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.
201
        
202
    def test_simple_pipes_client_supports__flush(self):
203
        # invoking _flush on a SimplePipesClient should flush the output 
204
        # pipe. We test this by creating an output pipe that records
205
        # flush calls made to it.
206
        from StringIO import StringIO # get regular StringIO
207
        input = StringIO()
208
        output = StringIO()
209
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
210
        def logging_flush(): flush_calls.append('flush')
211
        output.flush = logging_flush
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
212
        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.
213
        # this call is here to ensure we only flush once, not on every
214
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
215
        client_medium._accept_bytes('abc')
216
        client_medium._flush()
217
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
218
        self.assertEqual(['flush'], flush_calls)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
219
220
    def test_construct_smart_ssh_client_medium(self):
221
        # the SSH client medium takes:
222
        # host, port, username, password, vendor
223
        # Constructing one should just save these and do nothing.
224
        # we test this by creating a empty bound socket and constructing
225
        # a medium.
226
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
227
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
228
        unopened_port = sock.getsockname()[1]
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
229
        # having vendor be invalid means that if it tries to connect via the
230
        # vendor it will blow up.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
231
        client_medium = medium.SmartSSHClientMedium('127.0.0.1', unopened_port,
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
232
            username=None, password=None, vendor="not a vendor",
233
            bzr_remote_path='bzr')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
234
        sock.close()
235
236
    def test_ssh_client_connects_on_first_use(self):
237
        # The only thing that initiates a connection from the medium is giving
238
        # it bytes.
239
        output = StringIO()
240
        vendor = StringIOSSHVendor(StringIO(), output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
241
        client_medium = medium.SmartSSHClientMedium(
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
242
            'a hostname', 'a port', 'a username', 'a password', vendor, 'bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
243
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
244
        self.assertEqual('abc', output.getvalue())
245
        self.assertEqual([('connect_ssh', 'a username', 'a password',
246
            'a hostname', 'a port',
247
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes'])],
248
            vendor.calls)
249
    
250
    def test_ssh_client_changes_command_when_BZR_REMOTE_PATH_is_set(self):
251
        # The only thing that initiates a connection from the medium is giving
252
        # it bytes.
253
        output = StringIO()
254
        vendor = StringIOSSHVendor(StringIO(), output)
255
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
256
        def cleanup_environ():
257
            osutils.set_or_unset_env('BZR_REMOTE_PATH', orig_bzr_remote_path)
258
        self.addCleanup(cleanup_environ)
259
        os.environ['BZR_REMOTE_PATH'] = 'fugly'
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
260
        client_medium = self.callDeprecated(
261
            ['bzr_remote_path is required as of bzr 0.92'],
262
            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.
263
            'a password', vendor)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
264
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
265
        self.assertEqual('abc', output.getvalue())
266
        self.assertEqual([('connect_ssh', 'a username', 'a password',
267
            'a hostname', 'a port',
268
            ['fugly', 'serve', '--inet', '--directory=/', '--allow-writes'])],
269
            vendor.calls)
270
    
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
271
    def test_ssh_client_changes_command_when_bzr_remote_path_passed(self):
272
        # The only thing that initiates a connection from the medium is giving
273
        # it bytes.
274
        output = StringIO()
275
        vendor = StringIOSSHVendor(StringIO(), output)
276
        client_medium = medium.SmartSSHClientMedium('a hostname', 'a port',
277
            'a username', 'a password', vendor, bzr_remote_path='fugly')
278
        client_medium._accept_bytes('abc')
279
        self.assertEqual('abc', output.getvalue())
280
        self.assertEqual([('connect_ssh', 'a username', 'a password',
281
            'a hostname', 'a port',
282
            ['fugly', 'serve', '--inet', '--directory=/', '--allow-writes'])],
283
            vendor.calls)
284
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
285
    def test_ssh_client_disconnect_does_so(self):
286
        # calling disconnect should disconnect both the read_from and write_to
287
        # file-like object it from the ssh connection.
288
        input = StringIO()
289
        output = StringIO()
290
        vendor = StringIOSSHVendor(input, output)
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
291
        client_medium = medium.SmartSSHClientMedium('a hostname',
292
                                                    vendor=vendor,
293
                                                    bzr_remote_path='bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
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
        self.assertTrue(input.closed)
297
        self.assertTrue(output.closed)
298
        self.assertEqual([
299
            ('connect_ssh', None, None, 'a hostname', None,
300
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
301
            ('close', ),
302
            ],
303
            vendor.calls)
304
305
    def test_ssh_client_disconnect_allows_reconnection(self):
306
        # calling disconnect on the client terminates the connection, but should
307
        # not prevent additional connections occuring.
308
        # we test this by initiating a second connection after doing a
309
        # disconnect.
310
        input = StringIO()
311
        output = StringIO()
312
        vendor = StringIOSSHVendor(input, output)
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
313
        client_medium = medium.SmartSSHClientMedium('a hostname',
314
            vendor=vendor, bzr_remote_path='bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
315
        client_medium._accept_bytes('abc')
316
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
317
        # the disconnect has closed output, so we need a new output for the
318
        # new connection to write to.
319
        input2 = StringIO()
320
        output2 = StringIO()
321
        vendor.read_from = input2
322
        vendor.write_to = output2
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
323
        client_medium._accept_bytes('abc')
324
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
325
        self.assertTrue(input.closed)
326
        self.assertTrue(output.closed)
327
        self.assertTrue(input2.closed)
328
        self.assertTrue(output2.closed)
329
        self.assertEqual([
330
            ('connect_ssh', None, None, 'a hostname', None,
331
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
332
            ('close', ),
333
            ('connect_ssh', None, None, 'a hostname', None,
334
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
335
            ('close', ),
336
            ],
337
            vendor.calls)
338
    
339
    def test_ssh_client_ignores_disconnect_when_not_connected(self):
340
        # 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.
341
        # does not fail.  It's ok to disconnect an unconnected medium.
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
342
        client_medium = medium.SmartSSHClientMedium(None,
343
                                                    bzr_remote_path='bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
344
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
345
346
    def test_ssh_client_raises_on_read_when_not_connected(self):
347
        # 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.
348
        # MediumNotConnected.
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
349
        client_medium = medium.SmartSSHClientMedium(None,
350
                                                    bzr_remote_path='bzr')
351
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes,
352
                          0)
353
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes,
354
                          1)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
355
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
356
    def test_ssh_client_supports__flush(self):
357
        # invoking _flush on a SSHClientMedium should flush the output 
358
        # pipe. We test this by creating an output pipe that records
359
        # flush calls made to it.
360
        from StringIO import StringIO # get regular StringIO
361
        input = StringIO()
362
        output = StringIO()
363
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
364
        def logging_flush(): flush_calls.append('flush')
365
        output.flush = logging_flush
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
366
        vendor = StringIOSSHVendor(input, output)
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
367
        client_medium = medium.SmartSSHClientMedium('a hostname',
368
                                                    vendor=vendor,
369
                                                    bzr_remote_path='bzr')
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
370
        # this call is here to ensure we only flush once, not on every
371
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
372
        client_medium._accept_bytes('abc')
373
        client_medium._flush()
374
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
375
        self.assertEqual(['flush'], flush_calls)
376
        
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
377
    def test_construct_smart_tcp_client_medium(self):
378
        # the TCP client medium takes a host and a port.  Constructing it won't
379
        # connect to anything.
380
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
381
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
382
        unopened_port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
383
        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.
384
        sock.close()
385
386
    def test_tcp_client_connects_on_first_use(self):
387
        # The only thing that initiates a connection from the medium is giving
388
        # it bytes.
389
        sock, medium = self.make_loopsocket_and_medium()
390
        bytes = []
391
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
392
        medium.accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
393
        t.join()
394
        sock.close()
395
        self.assertEqual(['abc'], bytes)
396
    
397
    def test_tcp_client_disconnect_does_so(self):
398
        # calling disconnect on the client terminates the connection.
399
        # 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.
400
        # 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.
401
        sock, medium = self.make_loopsocket_and_medium()
402
        bytes = []
403
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
404
        medium.accept_bytes('ab')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
405
        medium.disconnect()
406
        t.join()
407
        sock.close()
408
        self.assertEqual(['ab'], bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
409
        # 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.
410
        # really did disconnect.
411
        medium.disconnect()
412
    
413
    def test_tcp_client_ignores_disconnect_when_not_connected(self):
414
        # 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.
415
        # does not fail.  It's ok to disconnect an unconnected medium.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
416
        client_medium = medium.SmartTCPClientMedium(None, None)
417
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
418
419
    def test_tcp_client_raises_on_read_when_not_connected(self):
420
        # 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.
421
        # MediumNotConnected.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
422
        client_medium = medium.SmartTCPClientMedium(None, None)
423
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
424
        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.
425
426
    def test_tcp_client_supports__flush(self):
427
        # invoking _flush on a TCPClientMedium should do something useful.
428
        # RBC 20060922 not sure how to test/tell in this case.
429
        sock, medium = self.make_loopsocket_and_medium()
430
        bytes = []
431
        t = self.receive_bytes_on_server(sock, bytes)
432
        # try with nothing buffered
433
        medium._flush()
434
        medium._accept_bytes('ab')
435
        # and with something sent.
436
        medium._flush()
437
        medium.disconnect()
438
        t.join()
439
        sock.close()
440
        self.assertEqual(['ab'], bytes)
441
        # now disconnect again : this should not do anything, if disconnection
442
        # really did disconnect.
443
        medium.disconnect()
444
445
446
class TestSmartClientStreamMediumRequest(tests.TestCase):
447
    """Tests the for SmartClientStreamMediumRequest.
448
    
449
    SmartClientStreamMediumRequest is a helper for the three stream based 
450
    mediums: TCP, SSH, SimplePipes, so we only test it once, and then test that
451
    those three mediums implement the interface it expects.
452
    """
453
454
    def test_accept_bytes_after_finished_writing_errors(self):
455
        # calling accept_bytes after calling finished_writing raises 
456
        # WritingCompleted to prevent bad assumptions on stream environments
457
        # breaking the needs of message-based environments.
458
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
459
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
460
        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.
461
        request.finished_writing()
462
        self.assertRaises(errors.WritingCompleted, request.accept_bytes, None)
463
464
    def test_accept_bytes(self):
465
        # accept bytes should invoke _accept_bytes on the stream medium.
466
        # we test this by using the SimplePipes medium - the most trivial one
467
        # and checking that the pipes get the data.
468
        input = StringIO()
469
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
470
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
471
        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.
472
        request.accept_bytes('123')
473
        request.finished_writing()
474
        request.finished_reading()
475
        self.assertEqual('', input.getvalue())
476
        self.assertEqual('123', output.getvalue())
477
478
    def test_construct_sets_stream_request(self):
479
        # constructing a SmartClientStreamMediumRequest on a StreamMedium sets
480
        # the current request to the new SmartClientStreamMediumRequest
481
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
482
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
483
        request = medium.SmartClientStreamMediumRequest(client_medium)
484
        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.
485
486
    def test_construct_while_another_request_active_throws(self):
487
        # constructing a SmartClientStreamMediumRequest on a StreamMedium with
488
        # a non-None _current_request raises TooManyConcurrentRequests.
489
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
490
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
491
        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.
492
        self.assertRaises(errors.TooManyConcurrentRequests,
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
493
            medium.SmartClientStreamMediumRequest, client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
494
495
    def test_finished_read_clears_current_request(self):
496
        # calling finished_reading clears the current request from the requests
497
        # medium
498
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
499
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
500
        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.
501
        request.finished_writing()
502
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
503
        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.
504
505
    def test_finished_read_before_finished_write_errors(self):
506
        # calling finished_reading before calling finished_writing triggers a
507
        # WritingNotComplete error.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
508
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
509
        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.
510
        self.assertRaises(errors.WritingNotComplete, request.finished_reading)
511
        
512
    def test_read_bytes(self):
513
        # read bytes should invoke _read_bytes on the stream medium.
514
        # we test this by using the SimplePipes medium - the most trivial one
515
        # and checking that the data is supplied. Its possible that a 
516
        # faulty implementation could poke at the pipe variables them selves,
517
        # but we trust that this will be caught as it will break the integration
518
        # smoke tests.
519
        input = StringIO('321')
520
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
521
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
522
        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.
523
        request.finished_writing()
524
        self.assertEqual('321', request.read_bytes(3))
525
        request.finished_reading()
526
        self.assertEqual('', input.read())
527
        self.assertEqual('', output.getvalue())
528
529
    def test_read_bytes_before_finished_write_errors(self):
530
        # calling read_bytes before calling finished_writing triggers a
531
        # WritingNotComplete error because the Smart protocol is designed to be
532
        # compatible with strict message based protocols like HTTP where the
533
        # request cannot be submitted until the writing has completed.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
534
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
535
        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.
536
        self.assertRaises(errors.WritingNotComplete, request.read_bytes, None)
537
538
    def test_read_bytes_after_finished_reading_errors(self):
539
        # calling read_bytes after calling finished_reading raises 
540
        # ReadingCompleted to prevent bad assumptions on stream environments
541
        # breaking the needs of message-based environments.
542
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
543
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
544
        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.
545
        request.finished_writing()
546
        request.finished_reading()
547
        self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
548
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
549
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
550
class RemoteTransportTests(TestCaseWithSmartMedium):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
551
552
    def test_plausible_url(self):
553
        self.assert_(self.get_url().startswith('bzr://'))
554
555
    def test_probe_transport(self):
556
        t = self.get_transport()
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
557
        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
558
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
559
    def test_get_medium_from_transport(self):
560
        """Remote transport has a medium always, which it can return."""
561
        t = self.get_transport()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
562
        client_medium = t.get_smart_medium()
563
        self.assertIsInstance(client_medium, medium.SmartClientMedium)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
564
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
565
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
566
class ErrorRaisingProtocol(object):
567
568
    def __init__(self, exception):
569
        self.exception = exception
570
571
    def next_read_size(self):
572
        raise self.exception
573
574
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
575
class SampleRequest(object):
576
    
577
    def __init__(self, expected_bytes):
578
        self.accepted_bytes = ''
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
579
        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.
580
        self.expected_bytes = expected_bytes
581
        self.excess_buffer = ''
582
583
    def accept_bytes(self, bytes):
584
        self.accepted_bytes += bytes
585
        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.
586
            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.
587
            self.excess_buffer = self.accepted_bytes[len(self.expected_bytes):]
588
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
589
    def next_read_size(self):
590
        if self._finished_reading:
591
            return 0
592
        else:
593
            return 1
594
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
595
596
class TestSmartServerStreamMedium(tests.TestCase):
597
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
598
    def setUp(self):
599
        super(TestSmartServerStreamMedium, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
600
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
601
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
602
    def portable_socket_pair(self):
603
        """Return a pair of TCP sockets connected to each other.
604
        
605
        Unlike socket.socketpair, this should work on Windows.
606
        """
607
        listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
608
        listen_sock.bind(('127.0.0.1', 0))
609
        listen_sock.listen(1)
610
        client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
611
        client_sock.connect(listen_sock.getsockname())
612
        server_sock, addr = listen_sock.accept()
613
        listen_sock.close()
614
        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
615
    
616
    def test_smart_query_version(self):
617
        """Feed a canned query version to a server"""
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
618
        # 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
619
        to_server = StringIO('hello\n')
620
        from_server = StringIO()
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
621
        transport = local.LocalTransport(urlutils.local_path_to_url('/'))
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
622
        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.
623
            to_server, from_server, transport)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
624
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
625
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
626
        server._serve_one_request(smart_protocol)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
627
        self.assertEqual('ok\0012\n',
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
628
                         from_server.getvalue())
629
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
630
    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
631
        transport = memory.MemoryTransport('memory:///')
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
632
        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
633
        to_server = StringIO('get\001./testfile\n')
634
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
635
        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.
636
            to_server, from_server, transport)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
637
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
638
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
639
        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
640
        self.assertEqual('ok\n'
641
                         '17\n'
642
                         'contents\nof\nfile\n'
643
                         'done\n',
644
                         from_server.getvalue())
645
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
646
    def test_response_to_canned_get_of_utf8(self):
647
        # wire-to-wire, using the whole stack, with a UTF-8 filename.
648
        transport = memory.MemoryTransport('memory:///')
649
        utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')
650
        transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
651
        to_server = StringIO('get\001' + utf8_filename + '\n')
652
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
653
        server = medium.SmartServerPipeStreamMedium(
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
654
            to_server, from_server, transport)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
655
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
656
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
657
        server._serve_one_request(smart_protocol)
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
658
        self.assertEqual('ok\n'
659
                         '17\n'
660
                         'contents\nof\nfile\n'
661
                         'done\n',
662
                         from_server.getvalue())
663
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
664
    def test_pipe_like_stream_with_bulk_data(self):
665
        sample_request_bytes = 'command\n9\nbulk datadone\n'
666
        to_server = StringIO(sample_request_bytes)
667
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
668
        server = medium.SmartServerPipeStreamMedium(
669
            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.
670
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
671
        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.
672
        self.assertEqual('', from_server.getvalue())
673
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
674
        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.
675
676
    def test_socket_stream_with_bulk_data(self):
677
        sample_request_bytes = 'command\n9\nbulk datadone\n'
678
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
679
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
680
            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.
681
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
682
        client_sock.sendall(sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
683
        server._serve_one_request(sample_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
684
        server_sock.close()
685
        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.
686
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
687
        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.
688
689
    def test_pipe_like_stream_shutdown_detection(self):
690
        to_server = StringIO('')
691
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
692
        server = medium.SmartServerPipeStreamMedium(to_server, from_server, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
693
        server._serve_one_request(SampleRequest('x'))
694
        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.
695
        
696
    def test_socket_stream_shutdown_detection(self):
697
        server_sock, client_sock = self.portable_socket_pair()
698
        client_sock.close()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
699
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
700
            server_sock, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
701
        server._serve_one_request(SampleRequest('x'))
702
        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.
703
        
704
    def test_pipe_like_stream_with_two_requests(self):
705
        # If two requests are read in one go, then two calls to
706
        # _serve_one_request should still process both of them as if they had
707
        # been received seperately.
708
        sample_request_bytes = 'command\n'
709
        to_server = StringIO(sample_request_bytes * 2)
710
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
711
        server = medium.SmartServerPipeStreamMedium(
712
            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.
713
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
714
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
715
        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.
716
        self.assertEqual('', from_server.getvalue())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
717
        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.
718
        # Make a new protocol, call _serve_one_request with it to collect the
719
        # second request.
720
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
721
        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.
722
        self.assertEqual('', from_server.getvalue())
723
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
724
        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.
725
        
726
    def test_socket_stream_with_two_requests(self):
727
        # If two requests are read in one go, then two calls to
728
        # _serve_one_request should still process both of them as if they had
729
        # been received seperately.
730
        sample_request_bytes = 'command\n'
731
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
732
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
733
            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.
734
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
735
        # Put two whole requests on the wire.
736
        client_sock.sendall(sample_request_bytes * 2)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
737
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
738
        self.assertEqual(0, first_protocol.next_read_size())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
739
        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.
740
        # Make a new protocol, call _serve_one_request with it to collect the
741
        # second request.
742
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
743
        stream_still_open = server._serve_one_request(second_protocol)
744
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
745
        self.assertFalse(server.finished)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
746
        server_sock.close()
747
        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.
748
749
    def test_pipe_like_stream_error_handling(self):
750
        # Use plain python StringIO so we can monkey-patch the close method to
751
        # not discard the contents.
752
        from StringIO import StringIO
753
        to_server = StringIO('')
754
        from_server = StringIO()
755
        self.closed = False
756
        def close():
757
            self.closed = True
758
        from_server.close = close
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
759
        server = medium.SmartServerPipeStreamMedium(
760
            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.
761
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
762
        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.
763
        self.assertEqual('', from_server.getvalue())
764
        self.assertTrue(self.closed)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
765
        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.
766
        
767
    def test_socket_stream_error_handling(self):
768
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
769
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
770
            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.
771
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
772
        server._serve_one_request(fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
773
        # recv should not block, because the other end of the socket has been
774
        # closed.
775
        self.assertEqual('', client_sock.recv(1))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
776
        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.
777
        
778
    def test_pipe_like_stream_keyboard_interrupt_handling(self):
779
        to_server = StringIO('')
780
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
781
        server = medium.SmartServerPipeStreamMedium(
782
            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.
783
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
784
        self.assertRaises(
785
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
786
        self.assertEqual('', from_server.getvalue())
787
788
    def test_socket_stream_keyboard_interrupt_handling(self):
789
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
790
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
791
            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.
792
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
793
        self.assertRaises(
794
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
795
        server_sock.close()
796
        self.assertEqual('', client_sock.recv(1))
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
797
798
    def build_protocol_pipe_like(self, bytes):
799
        to_server = StringIO(bytes)
800
        from_server = StringIO()
801
        server = medium.SmartServerPipeStreamMedium(
802
            to_server, from_server, None)
803
        return server._build_protocol()
804
805
    def build_protocol_socket(self, bytes):
806
        server_sock, client_sock = self.portable_socket_pair()
807
        server = medium.SmartServerSocketStreamMedium(
808
            server_sock, None)
809
        client_sock.sendall(bytes)
810
        client_sock.close()
811
        return server._build_protocol()
812
813
    def assertProtocolOne(self, server_protocol):
814
        # Use assertIs because assertIsInstance will wrongly pass
815
        # SmartServerRequestProtocolTwo (because it subclasses
816
        # SmartServerRequestProtocolOne).
817
        self.assertIs(
818
            type(server_protocol), protocol.SmartServerRequestProtocolOne)
819
820
    def assertProtocolTwo(self, server_protocol):
821
        self.assertIsInstance(
822
            server_protocol, protocol.SmartServerRequestProtocolTwo)
823
824
    def test_pipe_like_build_protocol_empty_bytes(self):
825
        # Any empty request (i.e. no bytes) is detected as protocol version one.
826
        server_protocol = self.build_protocol_pipe_like('')
827
        self.assertProtocolOne(server_protocol)
828
        
829
    def test_socket_like_build_protocol_empty_bytes(self):
830
        # Any empty request (i.e. no bytes) is detected as protocol version one.
831
        server_protocol = self.build_protocol_socket('')
832
        self.assertProtocolOne(server_protocol)
833
834
    def test_pipe_like_build_protocol_non_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
835
        # A request that doesn't start with "bzr request 2\n" is version one.
836
        server_protocol = self.build_protocol_pipe_like('abc\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
837
        self.assertProtocolOne(server_protocol)
838
839
    def test_socket_build_protocol_non_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
840
        # A request that doesn't start with "bzr request 2\n" is version one.
841
        server_protocol = self.build_protocol_socket('abc\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
842
        self.assertProtocolOne(server_protocol)
843
844
    def test_pipe_like_build_protocol_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
845
        # A request that starts with "bzr request 2\n" is version two.
846
        server_protocol = self.build_protocol_pipe_like('bzr request 2\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
847
        self.assertProtocolTwo(server_protocol)
848
849
    def test_socket_build_protocol_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
850
        # A request that starts with "bzr request 2\n" is version two.
851
        server_protocol = self.build_protocol_socket('bzr request 2\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
852
        self.assertProtocolTwo(server_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
853
        
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
854
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
855
class TestSmartTCPServer(tests.TestCase):
856
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
857
    def test_get_error_unexpected(self):
858
        """Error reported by server with no specific representation"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
859
        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
860
        class FlakyTransport(object):
2376.3.3 by Robert Collins
Fix all smart_transport tests.
861
            base = 'a_url'
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
862
            def external_url(self):
863
                return self.base
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.
864
            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
865
                raise Exception("some random exception from inside server")
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
866
        smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
867
        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
868
        try:
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
869
            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
870
            try:
871
                transport.get('something')
872
            except errors.TransportError, e:
873
                self.assertContainsRe(str(e), 'some random exception')
874
            else:
875
                self.fail("get did not raise expected error")
2018.5.171 by Andrew Bennetts
Disconnect RemoteTransports in some tests to avoid tripping up test_strace with leftover threads from previous tests.
876
            transport.disconnect()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
877
        finally:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
878
            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
879
880
881
class SmartTCPTests(tests.TestCase):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
882
    """Tests for connection/end to end behaviour using the TCP server.
883
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
884
    All of these tests are run with a server running on another thread serving
885
    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`.
886
887
    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
888
    """
889
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
890
    def setUpServer(self, readonly=False, backing_transport=None):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
891
        """Setup the server.
892
893
        :param readonly: Create a readonly server.
894
        """
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
895
        if not backing_transport:
896
            self.backing_transport = memory.MemoryTransport()
897
        else:
898
            self.backing_transport = backing_transport
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
899
        if readonly:
900
            self.real_backing_transport = self.backing_transport
901
            self.backing_transport = get_transport("readonly+" + self.backing_transport.abspath('.'))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
902
        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
903
        self.server.start_background_thread()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
904
        self.transport = remote.RemoteTCPTransport(self.server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
905
        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
906
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
907
    def tearDownServer(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
908
        if getattr(self, 'transport', None):
909
            self.transport.disconnect()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
910
            del self.transport
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
911
        if getattr(self, 'server', None):
912
            self.server.stop_background_thread()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
913
            del self.server
914
915
916
class TestServerSocketUsage(SmartTCPTests):
917
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.
918
    def test_server_setup_teardown(self):
919
        """It should be safe to teardown the server with no requests."""
920
        self.setUpServer()
921
        server = self.server
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
922
        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.
923
        self.tearDownServer()
924
        self.assertRaises(errors.ConnectionError, transport.has, '.')
925
926
    def test_server_closes_listening_sock_on_shutdown_after_request(self):
2370.4.2 by Robert Collins
Review feedback.
927
        """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
928
        self.setUpServer()
2370.4.2 by Robert Collins
Review feedback.
929
        server = self.server
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
930
        self.transport.has('.')
931
        self.tearDownServer()
932
        # if the listening socket has closed, we should get a BADFD error
933
        # when connecting, rather than a hang.
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
934
        transport = remote.RemoteTCPTransport(server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
935
        self.assertRaises(errors.ConnectionError, transport.has, '.')
936
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
937
938
class WritableEndToEndTests(SmartTCPTests):
939
    """Client to server tests that require a writable transport."""
940
941
    def setUp(self):
942
        super(WritableEndToEndTests, self).setUp()
943
        self.setUpServer()
944
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
945
    def test_start_tcp_server(self):
946
        url = self.server.get_url()
947
        self.assertContainsRe(url, r'^bzr://127\.0\.0\.1:[0-9]{2,}/')
948
949
    def test_smart_transport_has(self):
950
        """Checking for file existence over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
951
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
952
        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
953
        self.assertTrue(self.transport.has("foo"))
954
        self.assertFalse(self.transport.has("non-foo"))
955
956
    def test_smart_transport_get(self):
957
        """Read back a file over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
958
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
959
        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
960
        fp = self.transport.get("foo")
961
        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`.
962
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
963
    def test_get_error_enoent(self):
964
        """Error reported from server getting nonexistent file."""
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
965
        # The path in a raised NoSuchFile exception should be the precise path
966
        # asked for by the client. This gives meaningful and unsurprising errors
967
        # for users.
2402.1.2 by Andrew Bennetts
Deal with review comments.
968
        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
969
        try:
1752.2.80 by Andrew Bennetts
Some urlquoting fixes, a double-slash fix, a fix for an incorrect test (still failing though) and remove an apparently obsolete comment.
970
            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
971
        except errors.NoSuchFile, e:
1752.2.80 by Andrew Bennetts
Some urlquoting fixes, a double-slash fix, a fix for an incorrect test (still failing though) and remove an apparently obsolete comment.
972
            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
973
        else:
974
            self.fail("get did not raise expected error")
975
976
    def test_simple_clone_conn(self):
977
        """Test that cloning reuses the same connection."""
978
        # we create a real connection not a loopback one, but it will use the
979
        # same server and pipes
1752.2.74 by Andrew Bennetts
Make SmartTransport.clone return the right class, and move connection sharing into clone from __init__.
980
        conn2 = self.transport.clone('.')
2485.8.54 by Vincent Ladeuil
Refactor medium uses by making a distinction betweem shared and real medium.
981
        self.assertIs(self.transport.get_smart_medium(),
982
                      conn2.get_smart_medium())
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
983
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)
984
    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
985
        self.assertEquals('/foo/bar',
986
                          self.transport._remote_path('foo/bar'))
987
988
    def test_clone_changes_base(self):
989
        """Cloning transport produces one with a new base location"""
990
        conn2 = self.transport.clone('subdir')
991
        self.assertEquals(self.transport.base + 'subdir/',
992
                          conn2.base)
993
994
    def test_open_dir(self):
995
        """Test changing directory"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
996
        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
997
        transport = self.transport
998
        self.backing_transport.mkdir('toffee')
999
        self.backing_transport.mkdir('toffee/apple')
1000
        self.assertEquals('/toffee', transport._remote_path('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
1001
        toffee_trans = transport.clone('toffee')
1002
        # Check that each transport has only the contents of its directory
1003
        # directly visible. If state was being held in the wrong object, it's
1004
        # conceivable that cloning a transport would alter the state of the
1005
        # cloned-from transport.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1006
        self.assertTrue(transport.has('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
1007
        self.assertFalse(toffee_trans.has('toffee'))
1008
        self.assertFalse(transport.has('apple'))
1009
        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
1010
1011
    def test_open_bzrdir(self):
1012
        """Open an existing bzrdir over smart transport"""
1013
        transport = self.transport
1014
        t = self.backing_transport
1015
        bzrdir.BzrDirFormat.get_default_format().initialize_on_transport(t)
1016
        result_dir = bzrdir.BzrDir.open_containing_from_transport(transport)
1017
1018
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1019
class ReadOnlyEndToEndTests(SmartTCPTests):
1020
    """Tests from the client to the server using a readonly backing transport."""
1021
1022
    def test_mkdir_error_readonly(self):
1023
        """TransportNotPossible should be preserved from the backing transport."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
1024
        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`.
1025
        self.setUpServer(readonly=True)
1026
        self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
1027
            'foo')
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1028
1029
1030
class TestServerHooks(SmartTCPTests):
1031
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
1032
    def capture_server_call(self, backing_urls, public_url):
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1033
        """Record a server_started|stopped hook firing."""
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
1034
        self.hook_calls.append((backing_urls, public_url))
1035
1036
    def test_server_started_hook_memory(self):
1037
        """The server_started hook fires when the server is started."""
1038
        self.hook_calls = []
1039
        server.SmartTCPServer.hooks.install_hook('server_started',
1040
            self.capture_server_call)
1041
        self.setUpServer()
1042
        # at this point, the server will be starting a thread up.
1043
        # there is no indicator at the moment, so bodge it by doing a request.
1044
        self.transport.has('.')
1045
        # The default test server uses MemoryTransport and that has no external
1046
        # url:
1047
        self.assertEqual([([self.backing_transport.base], self.transport.base)],
1048
            self.hook_calls)
1049
1050
    def test_server_started_hook_file(self):
1051
        """The server_started hook fires when the server is started."""
1052
        self.hook_calls = []
1053
        server.SmartTCPServer.hooks.install_hook('server_started',
1054
            self.capture_server_call)
1055
        self.setUpServer(backing_transport=get_transport("."))
1056
        # at this point, the server will be starting a thread up.
1057
        # there is no indicator at the moment, so bodge it by doing a request.
1058
        self.transport.has('.')
1059
        # The default test server uses MemoryTransport and that has no external
1060
        # url:
1061
        self.assertEqual([([
1062
            self.backing_transport.base, self.backing_transport.external_url()],
1063
             self.transport.base)],
1064
            self.hook_calls)
1065
1066
    def test_server_stopped_hook_simple_memory(self):
1067
        """The server_stopped hook fires when the server is stopped."""
1068
        self.hook_calls = []
1069
        server.SmartTCPServer.hooks.install_hook('server_stopped',
1070
            self.capture_server_call)
1071
        self.setUpServer()
1072
        result = [([self.backing_transport.base], self.transport.base)]
1073
        # check the stopping message isn't emitted up front.
1074
        self.assertEqual([], self.hook_calls)
1075
        # nor after a single message
1076
        self.transport.has('.')
1077
        self.assertEqual([], self.hook_calls)
1078
        # clean up the server
1079
        self.tearDownServer()
1080
        # now it should have fired.
1081
        self.assertEqual(result, self.hook_calls)
1082
1083
    def test_server_stopped_hook_simple_file(self):
1084
        """The server_stopped hook fires when the server is stopped."""
1085
        self.hook_calls = []
1086
        server.SmartTCPServer.hooks.install_hook('server_stopped',
1087
            self.capture_server_call)
1088
        self.setUpServer(backing_transport=get_transport("."))
1089
        result = [(
1090
            [self.backing_transport.base, self.backing_transport.external_url()]
1091
            , 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.
1092
        # check the stopping message isn't emitted up front.
1093
        self.assertEqual([], self.hook_calls)
1094
        # nor after a single message
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1095
        self.transport.has('.')
1096
        self.assertEqual([], self.hook_calls)
1097
        # clean up the server
1098
        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.
1099
        # now it should have fired.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1100
        self.assertEqual(result, self.hook_calls)
1101
1102
# TODO: test that when the server suffers an exception that it calls the
1103
# server-stopped hook.
1104
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1105
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1106
class SmartServerCommandTests(tests.TestCaseWithTransport):
1107
    """Tests that call directly into the command objects, bypassing the network
1108
    and the request dispatching.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1109
1110
    Note: these tests are rudimentary versions of the command object tests in
1111
    test_remote.py.
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1112
    """
1113
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1114
    def test_hello(self):
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1115
        cmd = _mod_request.HelloRequest(None, '/')
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1116
        response = cmd.execute()
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1117
        self.assertEqual(('ok', '2'), response.args)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1118
        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
1119
        
1120
    def test_get_bundle(self):
1121
        from bzrlib.bundle import serializer
1122
        wt = self.make_branch_and_tree('.')
1910.19.13 by Andrew Bennetts
Address various review comments.
1123
        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
1124
        wt.add('hello')
1910.19.13 by Andrew Bennetts
Address various review comments.
1125
        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
1126
        
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1127
        cmd = _mod_request.GetBundleRequest(self.get_transport(), '/')
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1128
        response = cmd.execute('.', rev_id)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1129
        bundle = serializer.read_bundle(StringIO(response.body))
1130
        self.assertEqual((), response.args)
1131
1132
1133
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
1134
    """Test that call directly into the handler logic, bypassing the network."""
1135
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1136
    def setUp(self):
1137
        super(SmartServerRequestHandlerTests, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
1138
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1139
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1140
    def build_handler(self, transport):
1141
        """Returns a handler for the commands in protocol version one."""
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1142
        return _mod_request.SmartServerRequestHandler(
1143
            transport, _mod_request.request_handlers, '/')
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1144
1145
    def test_construct_request_handler(self):
1146
        """Constructing a request handler should be easy and set defaults."""
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1147
        handler = _mod_request.SmartServerRequestHandler(None, commands=None,
1148
                root_client_path='/')
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1149
        self.assertFalse(handler.finished_reading)
1150
1151
    def test_hello(self):
1152
        handler = self.build_handler(None)
1153
        handler.dispatch_command('hello', ())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1154
        self.assertEqual(('ok', '2'), handler.response.args)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1155
        self.assertEqual(None, handler.response.body)
1156
        
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1157
    def test_disable_vfs_handler_classes_via_environment(self):
2018.5.140 by Andrew Bennetts
Merge from bzr.dev
1158
        # VFS handler classes will raise an error from "execute" if
1159
        # BZR_NO_SMART_VFS is set.
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1160
        handler = vfs.HasRequest(None, '/')
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1161
        # set environment variable after construction to make sure it's
1162
        # examined.
2018.5.140 by Andrew Bennetts
Merge from bzr.dev
1163
        # Note that we can safely clobber BZR_NO_SMART_VFS here, because setUp
1164
        # has called _captureVar, so it will be restored to the right state
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1165
        # afterwards.
2402.1.2 by Andrew Bennetts
Deal with review comments.
1166
        os.environ['BZR_NO_SMART_VFS'] = ''
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1167
        self.assertRaises(errors.DisabledMethod, handler.execute)
1168
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1169
    def test_readonly_exception_becomes_transport_not_possible(self):
1170
        """The response for a read-only error is ('ReadOnlyError')."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1171
        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`.
1172
        # send a mkdir for foo, with no explicit mode - should fail.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1173
        handler.dispatch_command('mkdir', ('foo', ''))
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1174
        # and the failure should be an explicit ReadOnlyError
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1175
        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`.
1176
        # XXX: TODO: test that other TransportNotPossible errors are
1177
        # presented as TransportNotPossible - not possible to do that
1178
        # until I figure out how to trigger that relatively cleanly via
1179
        # the api. RBC 20060918
1180
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1181
    def test_hello_has_finished_body_on_dispatch(self):
1182
        """The 'hello' command should set finished_reading."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1183
        handler = self.build_handler(None)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1184
        handler.dispatch_command('hello', ())
1185
        self.assertTrue(handler.finished_reading)
1186
        self.assertNotEqual(None, handler.response)
1187
1188
    def test_put_bytes_non_atomic(self):
1189
        """'put_...' should set finished_reading after reading the bytes."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1190
        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
1191
        handler.dispatch_command('put_non_atomic', ('a-file', '', 'F', ''))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1192
        self.assertFalse(handler.finished_reading)
1193
        handler.accept_body('1234')
1194
        self.assertFalse(handler.finished_reading)
1195
        handler.accept_body('5678')
1196
        handler.end_of_body()
1197
        self.assertTrue(handler.finished_reading)
1198
        self.assertEqual(('ok', ), handler.response.args)
1199
        self.assertEqual(None, handler.response.body)
1200
        
1201
    def test_readv_accept_body(self):
1202
        """'readv' should set finished_reading after reading offsets."""
1203
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1204
        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
1205
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1206
        self.assertFalse(handler.finished_reading)
1207
        handler.accept_body('2,')
1208
        self.assertFalse(handler.finished_reading)
1209
        handler.accept_body('3')
1210
        handler.end_of_body()
1211
        self.assertTrue(handler.finished_reading)
1212
        self.assertEqual(('readv', ), handler.response.args)
1213
        # co - nte - nt of a-file is the file contents we are extracting from.
1214
        self.assertEqual('nte', handler.response.body)
1215
1216
    def test_readv_short_read_response_contents(self):
1217
        """'readv' when a short read occurs sets the response appropriately."""
1218
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1219
        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
1220
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1221
        # read beyond the end of the file.
1222
        handler.accept_body('100,1')
1223
        handler.end_of_body()
1224
        self.assertTrue(handler.finished_reading)
2692.1.8 by Andrew Bennetts
Fix trivial test failure.
1225
        self.assertEqual(('ShortReadvError', './a-file', '100', '1', '0'),
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1226
            handler.response.args)
1227
        self.assertEqual(None, handler.response.body)
1228
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
1229
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1230
class RemoteTransportRegistration(tests.TestCase):
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
1231
1232
    def test_registration(self):
1233
        t = get_transport('bzr+ssh://example.com/path')
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
1234
        self.assertIsInstance(t, remote.RemoteSSHTransport)
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
1235
        self.assertEqual('example.com', t._host)
1236
2814.2.2 by Martin Pool
merge bzr+https patch from johnf and add a basic test
1237
    def test_bzr_https(self):
1238
        # https://bugs.launchpad.net/bzr/+bug/128456
1239
        t = get_transport('bzr+https://example.com/path')
1240
        self.assertIsInstance(t, remote.RemoteHTTPTransport)
1241
        self.assertStartsWith(
1242
            t._http_transport.base,
1243
            'https://')
1244
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
1245
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1246
class TestRemoteTransport(tests.TestCase):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1247
        
1248
    def test_use_connection_factory(self):
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1249
        # 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.
1250
        input = StringIO("ok\n3\nbardone\n")
1251
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1252
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1253
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1254
            'bzr://localhost/', medium=client_medium)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1255
1256
        # 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.
1257
        # method is called.  No data should have been sent, or read.
1258
        self.assertEqual(0, input.tell())
1259
        self.assertEqual('', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1260
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1261
        # Now call a method that should result in a single request : as the
1262
        # transport makes its own protocol instances, we check on the wire.
1263
        # XXX: TODO: give the transport a protocol factory, which can make
1264
        # an instrumented protocol for us.
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1265
        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.
1266
        # only the needed data should have been sent/received.
1267
        self.assertEqual(13, input.tell())
1268
        self.assertEqual('get\x01/foo\n', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1269
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1270
    def test__translate_error_readonly(self):
1271
        """Sending a ReadOnlyError to _translate_error raises TransportNotPossible."""
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1272
        client_medium = medium.SmartClientMedium()
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1273
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1274
            'bzr://localhost/', medium=client_medium)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1275
        self.assertRaises(errors.TransportNotPossible,
1276
            transport._translate_error, ("ReadOnlyError", ))
1277
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1278
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1279
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.
1280
    """A smart server which is backed by memory and saves its write requests."""
1281
1282
    def __init__(self, write_output_list):
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1283
        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.
1284
        self._write_output_list = write_output_list
1285
1286
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1287
class TestSmartProtocol(tests.TestCase):
1288
    """Base class for smart protocol tests.
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.
1289
1290
    Each test case gets a smart_server and smart_client created during setUp().
1291
1292
    It is planned that the client can be called with self.call_client() giving
1293
    it an expected server response, which will be fed into it when it tries to
1294
    read. Likewise, self.call_server will call a servers method with a canned
1295
    serialised client request. Output done by the client or server for these
1296
    calls will be captured to self.to_server and self.to_client. Each element
1297
    in the list is a write call from the client or server respectively.
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1298
1299
    Subclasses can override client_protocol_class and server_protocol_class.
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.
1300
    """
1301
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1302
    client_protocol_class = None
1303
    server_protocol_class = None
1304
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.
1305
    def setUp(self):
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1306
        super(TestSmartProtocol, self).setUp()
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
1307
        # XXX: self.server_to_client doesn't seem to be used.  If so,
1308
        # InstrumentedServerProtocol is redundant too.
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1309
        self.server_to_client = []
1310
        self.to_server = StringIO()
1311
        self.to_client = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1312
        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.
1313
            self.to_server)
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1314
        self.client_protocol = self.client_protocol_class(self.client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1315
        self.smart_server = InstrumentedServerProtocol(self.server_to_client)
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1316
        self.smart_server_request = _mod_request.SmartServerRequestHandler(
1317
            None, _mod_request.request_handlers, root_client_path='/')
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.
1318
1319
    def assertOffsetSerialisation(self, expected_offsets, expected_serialised,
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1320
        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.
1321
        """Check that smart (de)serialises offsets as expected.
1322
        
1323
        We check both serialisation and deserialisation at the same time
1324
        to ensure that the round tripping cannot skew: both directions should
1325
        be as expected.
1326
        
1327
        :param expected_offsets: a readv offset list.
1328
        :param expected_seralised: an expected serial form of the offsets.
1329
        """
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1330
        # XXX: '_deserialise_offsets' should be a method of the
1331
        # SmartServerRequestProtocol in future.
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1332
        readv_cmd = vfs.ReadvRequest(None, '/')
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1333
        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.
1334
        self.assertEqual(expected_offsets, offsets)
1335
        serialised = client._serialise_offsets(offsets)
1336
        self.assertEqual(expected_serialised, serialised)
1337
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1338
    def build_protocol_waiting_for_body(self):
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1339
        out_stream = StringIO()
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1340
        smart_protocol = self.server_protocol_class(None, out_stream.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1341
        smart_protocol.has_dispatched = True
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1342
        smart_protocol.request = self.smart_server_request
2018.5.7 by Andrew Bennetts
Simplify dispatch_command.
1343
        class FakeCommand(object):
1344
            def do_body(cmd, body_bytes):
1345
                self.end_received = True
1346
                self.assertEqual('abcdefg', body_bytes)
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1347
                return _mod_request.SuccessfulSmartServerResponse(('ok', ))
2018.5.12 by Andrew Bennetts
Rename SmartServerRequestHandler's command attribute to _command; it's private.
1348
        smart_protocol.request._command = FakeCommand()
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1349
        # Call accept_bytes to make sure that internal state like _body_decoder
1350
        # is initialised.  This test should probably be given a clearer
1351
        # interface to work with that will not cause this inconsistency.
1352
        #   -- Andrew Bennetts, 2006-09-28
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1353
        smart_protocol.accept_bytes('')
1354
        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.
1355
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1356
    def assertServerToClientEncoding(self, expected_bytes, expected_tuple,
1357
            input_tuples):
1358
        """Assert that each input_tuple serialises as expected_bytes, and the
1359
        bytes deserialise as expected_tuple.
1360
        """
1361
        # check the encoding of the server for all input_tuples matches
1362
        # expected bytes
1363
        for input_tuple in input_tuples:
1364
            server_output = StringIO()
1365
            server_protocol = self.server_protocol_class(
1366
                None, server_output.write)
2432.4.4 by Robert Collins
Merge hpss-protocol2.
1367
            server_protocol._send_response(
1368
                _mod_request.SuccessfulSmartServerResponse(input_tuple))
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1369
            self.assertEqual(expected_bytes, server_output.getvalue())
1370
        # check the decoding of the client smart_protocol from expected_bytes:
1371
        input = StringIO(expected_bytes)
1372
        output = StringIO()
1373
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1374
        request = client_medium.get_request()
1375
        smart_protocol = self.client_protocol_class(request)
1376
        smart_protocol.call('foo')
1377
        self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
1378
1379
2621.3.1 by Andrew Bennetts
Log errors from the smart server in the trace file, to make debugging test failures (and live failures!) easier.
1380
class CommonSmartProtocolTestMixin(object):
1381
1382
    def test_errors_are_logged(self):
1383
        """If an error occurs during testing, it is logged to the test log."""
1384
        out_stream = StringIO()
1385
        smart_protocol = self.server_protocol_class(None, out_stream.write)
1386
        # This triggers a "bad request" error.
1387
        smart_protocol.accept_bytes('abc\n')
1388
        test_log = self._get_log(keep_log_file=True)
1389
        self.assertContainsRe(test_log, 'Traceback')
1390
        self.assertContainsRe(test_log, 'SmartProtocolError')
1391
1392
1393
class TestSmartProtocolOne(TestSmartProtocol, CommonSmartProtocolTestMixin):
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1394
    """Tests for the smart protocol version one."""
1395
1396
    client_protocol_class = protocol.SmartClientRequestProtocolOne
1397
    server_protocol_class = protocol.SmartServerRequestProtocolOne
1398
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1399
    def test_construct_version_one_server_protocol(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1400
        smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
1401
        self.assertEqual('', smart_protocol.excess_buffer)
1402
        self.assertEqual('', smart_protocol.in_buffer)
1403
        self.assertFalse(smart_protocol.has_dispatched)
1404
        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.
1405
1406
    def test_construct_version_one_client_protocol(self):
1407
        # we can construct a client protocol from a client medium request
1408
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1409
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1410
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1411
        client_protocol = protocol.SmartClientRequestProtocolOne(request)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1412
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.
1413
    def test_server_offset_serialisation(self):
1414
        """The Smart protocol serialises offsets as a comma and \n string.
1415
1416
        We check a number of boundary cases are as expected: empty, one offset,
1417
        one with the order of reads not increasing (an out of order read), and
1418
        one that should coalesce.
1419
        """
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1420
        self.assertOffsetSerialisation([], '', self.client_protocol)
1421
        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.
1422
        self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1423
            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.
1424
        self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1425
            '1,2\n3,4\n100,200', self.client_protocol)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1426
2930.1.2 by Ian Clatworthy
Review feedback from poolie and spiv
1427
    def test_connection_closed_reporting(self):
2930.1.1 by Ian Clatworthy
error msg instead of assert when connection over bzr+ssh fails (#115601)
1428
        input = StringIO()
1429
        output = StringIO()
1430
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1431
        request = client_medium.get_request()
1432
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1433
        smart_protocol.call('hello')
2930.1.2 by Ian Clatworthy
Review feedback from poolie and spiv
1434
        ex = self.assertRaises(errors.ConnectionReset, 
1435
            smart_protocol.read_response_tuple)
1436
        self.assertEqual("Connection closed: "
1437
            "please check connectivity and permissions "
1438
            "(and try -Dhpss if further diagnosis is required)", str(ex))
2930.1.1 by Ian Clatworthy
error msg instead of assert when connection over bzr+ssh fails (#115601)
1439
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1440
    def test_accept_bytes_of_bad_request_to_protocol(self):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1441
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1442
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1443
            None, out_stream.write)
1444
        smart_protocol.accept_bytes('abc')
1445
        self.assertEqual('abc', smart_protocol.in_buffer)
1446
        smart_protocol.accept_bytes('\n')
1447
        self.assertEqual(
1448
            "error\x01Generic bzr smart protocol error: bad request 'abc'\n",
1449
            out_stream.getvalue())
1450
        self.assertTrue(smart_protocol.has_dispatched)
1451
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1452
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1453
    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.
1454
        protocol = self.build_protocol_waiting_for_body()
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1455
        self.assertEqual(6, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1456
        protocol.accept_bytes('7\nabc')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1457
        self.assertEqual(9, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1458
        protocol.accept_bytes('defgd')
1459
        protocol.accept_bytes('one\n')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1460
        self.assertEqual(0, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1461
        self.assertTrue(self.end_received)
1462
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1463
    def test_accept_request_and_body_all_at_once(self):
2402.1.2 by Andrew Bennetts
Deal with review comments.
1464
        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.
1465
        mem_transport = memory.MemoryTransport()
1466
        mem_transport.put_bytes('foo', 'abcdefghij')
1467
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1468
        smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
1469
                out_stream.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1470
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1471
        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.
1472
        self.assertEqual('readv\n3\ndefdone\n', out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1473
        self.assertEqual('', smart_protocol.excess_buffer)
1474
        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.
1475
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1476
    def test_accept_excess_bytes_are_preserved(self):
1477
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1478
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1479
            None, out_stream.write)
1480
        smart_protocol.accept_bytes('hello\nhello\n')
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1481
        self.assertEqual("ok\x012\n", out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1482
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1483
        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.
1484
1485
    def test_accept_excess_bytes_after_body(self):
1486
        protocol = self.build_protocol_waiting_for_body()
1487
        protocol.accept_bytes('7\nabcdefgdone\nX')
1488
        self.assertTrue(self.end_received)
1489
        self.assertEqual("X", protocol.excess_buffer)
1490
        self.assertEqual("", protocol.in_buffer)
1491
        protocol.accept_bytes('Y')
1492
        self.assertEqual("XY", protocol.excess_buffer)
1493
        self.assertEqual("", protocol.in_buffer)
1494
1495
    def test_accept_excess_bytes_after_dispatch(self):
1496
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1497
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1498
            None, out_stream.write)
1499
        smart_protocol.accept_bytes('hello\n')
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1500
        self.assertEqual("ok\x012\n", out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1501
        smart_protocol.accept_bytes('hel')
1502
        self.assertEqual("hel", smart_protocol.excess_buffer)
1503
        smart_protocol.accept_bytes('lo\n')
1504
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1505
        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.
1506
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1507
    def test__send_response_sets_finished_reading(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1508
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1509
            None, lambda x: None)
1510
        self.assertEqual(1, smart_protocol.next_read_size())
2432.4.3 by Robert Collins
Refactor the HPSS Response code to take SmartServerResponse rather than args and body.
1511
        smart_protocol._send_response(
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1512
            _mod_request.SuccessfulSmartServerResponse(('x',)))
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1513
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1514
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1515
    def test__send_response_errors_with_base_response(self):
1516
        """Ensure that only the Successful/Failed subclasses are used."""
1517
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1518
            None, lambda x: None)
1519
        self.assertRaises(AttributeError, smart_protocol._send_response,
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1520
            _mod_request.SmartServerResponse(('x',)))
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1521
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1522
    def test_query_version(self):
1523
        """query_version on a SmartClientProtocolOne should return a number.
1524
        
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1525
        The protocol provides the query_version because the domain level clients
1526
        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.
1527
        """
1528
        # What we really want to test here is that SmartClientProtocolOne calls
1529
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1530
        # 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.
1531
        # the error if the response is a non-understood version.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1532
        input = StringIO('ok\x012\n')
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1533
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1534
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1535
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1536
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1537
        self.assertEqual(2, smart_protocol.query_version())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1538
1539
    def test_client_call_empty_response(self):
1540
        # protocol.call() can get back an empty tuple as a response. This occurs
1541
        # when the parsed line is an empty line, and results in a tuple with
1542
        # one element - an empty string.
1543
        self.assertServerToClientEncoding('\n', ('', ), [(), ('', )])
1544
1545
    def test_client_call_three_element_response(self):
1546
        # protocol.call() can get back tuples of other lengths. A three element
1547
        # tuple should be unpacked as three strings.
1548
        self.assertServerToClientEncoding('a\x01b\x0134\n', ('a', 'b', '34'),
1549
            [('a', 'b', '34')])
1550
1551
    def test_client_call_with_body_bytes_uploads(self):
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1552
        # 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.
1553
        # wire.
1554
        expected_bytes = "foo\n7\nabcdefgdone\n"
1555
        input = StringIO("\n")
1556
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1557
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1558
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1559
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1560
        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.
1561
        self.assertEqual(expected_bytes, output.getvalue())
1562
1563
    def test_client_call_with_body_readv_array(self):
1564
        # protocol.call_with_upload should encode the readv array and then
1565
        # length-prefix the bytes onto the wire.
1566
        expected_bytes = "foo\n7\n1,2\n5,6done\n"
1567
        input = StringIO("\n")
1568
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1569
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1570
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1571
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1572
        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.
1573
        self.assertEqual(expected_bytes, output.getvalue())
1574
1575
    def test_client_read_body_bytes_all(self):
1576
        # read_body_bytes should decode the body bytes from the wire into
1577
        # a response.
1578
        expected_bytes = "1234567"
1579
        server_bytes = "ok\n7\n1234567done\n"
1580
        input = StringIO(server_bytes)
1581
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1582
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1583
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1584
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1585
        smart_protocol.call('foo')
1586
        smart_protocol.read_response_tuple(True)
1587
        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.
1588
1589
    def test_client_read_body_bytes_incremental(self):
1590
        # test reading a few bytes at a time from the body
1591
        # 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.
1592
        # 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.
1593
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1594
        # that.
1595
        expected_bytes = "1234567"
1596
        server_bytes = "ok\n7\n1234567done\n"
1597
        input = StringIO(server_bytes)
1598
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1599
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1600
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1601
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1602
        smart_protocol.call('foo')
1603
        smart_protocol.read_response_tuple(True)
1604
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1605
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1606
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1607
        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.
1608
1609
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1610
        # cancelling the expected body needs to finish the request, but not
1611
        # read any more bytes.
1612
        expected_bytes = "1234567"
1613
        server_bytes = "ok\n7\n1234567done\n"
1614
        input = StringIO(server_bytes)
1615
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1616
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1617
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1618
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1619
        smart_protocol.call('foo')
1620
        smart_protocol.read_response_tuple(True)
1621
        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.
1622
        self.assertEqual(3, input.tell())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1623
        self.assertRaises(
1624
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1625
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1626
2621.3.1 by Andrew Bennetts
Log errors from the smart server in the trace file, to make debugging test failures (and live failures!) easier.
1627
class TestSmartProtocolTwo(TestSmartProtocol, CommonSmartProtocolTestMixin):
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1628
    """Tests for the smart protocol version two.
1629
1630
    This test case is mostly the same as TestSmartProtocolOne.
1631
    """
1632
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1633
    client_protocol_class = protocol.SmartClientRequestProtocolTwo
1634
    server_protocol_class = protocol.SmartServerRequestProtocolTwo
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1635
1636
    def test_construct_version_two_server_protocol(self):
1637
        smart_protocol = protocol.SmartServerRequestProtocolTwo(None, None)
1638
        self.assertEqual('', smart_protocol.excess_buffer)
1639
        self.assertEqual('', smart_protocol.in_buffer)
1640
        self.assertFalse(smart_protocol.has_dispatched)
1641
        self.assertEqual(1, smart_protocol.next_read_size())
1642
1643
    def test_construct_version_two_client_protocol(self):
1644
        # we can construct a client protocol from a client medium request
1645
        output = StringIO()
1646
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1647
        request = client_medium.get_request()
1648
        client_protocol = protocol.SmartClientRequestProtocolTwo(request)
1649
1650
    def test_server_offset_serialisation(self):
1651
        """The Smart protocol serialises offsets as a comma and \n string.
1652
1653
        We check a number of boundary cases are as expected: empty, one offset,
1654
        one with the order of reads not increasing (an out of order read), and
1655
        one that should coalesce.
1656
        """
1657
        self.assertOffsetSerialisation([], '', self.client_protocol)
1658
        self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1659
        self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1660
            self.client_protocol)
1661
        self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1662
            '1,2\n3,4\n100,200', self.client_protocol)
1663
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1664
    def assertBodyStreamSerialisation(self, expected_serialisation,
1665
                                      body_stream):
1666
        """Assert that body_stream is serialised as expected_serialisation."""
1667
        out_stream = StringIO()
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1668
        protocol._send_stream(body_stream, out_stream.write)
2748.4.4 by Andrew Bennetts
Extract a _send_chunks function to make testing easier.
1669
        self.assertEqual(expected_serialisation, out_stream.getvalue())
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1670
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1671
    def assertBodyStreamRoundTrips(self, body_stream):
1672
        """Assert that body_stream is the same after being serialised and
1673
        deserialised.
1674
        """
1675
        out_stream = StringIO()
1676
        protocol._send_stream(body_stream, out_stream.write)
1677
        decoder = protocol.ChunkedBodyDecoder()
1678
        decoder.accept_bytes(out_stream.getvalue())
1679
        decoded_stream = list(iter(decoder.read_next_chunk, None))
1680
        self.assertEqual(body_stream, decoded_stream)
1681
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1682
    def test_body_stream_serialisation_empty(self):
1683
        """A body_stream with no bytes can be serialised."""
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1684
        self.assertBodyStreamSerialisation('chunked\nEND\n', [])
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1685
        self.assertBodyStreamRoundTrips([])
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1686
1687
    def test_body_stream_serialisation(self):
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1688
        stream = ['chunk one', 'chunk two', 'chunk three']
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1689
        self.assertBodyStreamSerialisation(
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1690
            'chunked\n' + '9\nchunk one' + '9\nchunk two' + 'b\nchunk three' +
1691
            'END\n',
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1692
            stream)
1693
        self.assertBodyStreamRoundTrips(stream)
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1694
1695
    def test_body_stream_with_empty_element_serialisation(self):
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
1696
        """A body stream can include ''.
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1697
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
1698
        The empty string can be transmitted like any other string.
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1699
        """
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1700
        stream = ['', 'chunk']
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
1701
        self.assertBodyStreamSerialisation(
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1702
            'chunked\n' + '0\n' + '5\nchunk' + 'END\n', stream)
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1703
        self.assertBodyStreamRoundTrips(stream)
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1704
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1705
    def test_body_stream_error_serialistion(self):
1706
        stream = ['first chunk',
2692.1.2 by Andrew Bennetts
Merge from bzr.dev.
1707
                  _mod_request.FailedSmartServerResponse(
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1708
                      ('FailureName', 'failure arg'))]
1709
        expected_bytes = (
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1710
            'chunked\n' + 'b\nfirst chunk' +
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1711
            'ERR\n' + 'b\nFailureName' + 'b\nfailure arg' +
1712
            'END\n')
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1713
        self.assertBodyStreamSerialisation(expected_bytes, stream)
2748.4.10 by Andrew Bennetts
Fix chunking serialisation to be current with the latest changes to the protocol, and improve the tests to make it harder to have them desynchronised.
1714
        self.assertBodyStreamRoundTrips(stream)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1715
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1716
    def test_accept_bytes_of_bad_request_to_protocol(self):
1717
        out_stream = StringIO()
1718
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1719
            None, out_stream.write)
1720
        smart_protocol.accept_bytes('abc')
1721
        self.assertEqual('abc', smart_protocol.in_buffer)
1722
        smart_protocol.accept_bytes('\n')
1723
        self.assertEqual(
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1724
            protocol.RESPONSE_VERSION_TWO +
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1725
            "failed\nerror\x01Generic bzr smart protocol error: bad request 'abc'\n",
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1726
            out_stream.getvalue())
1727
        self.assertTrue(smart_protocol.has_dispatched)
1728
        self.assertEqual(0, smart_protocol.next_read_size())
1729
1730
    def test_accept_body_bytes_to_protocol(self):
1731
        protocol = self.build_protocol_waiting_for_body()
1732
        self.assertEqual(6, protocol.next_read_size())
1733
        protocol.accept_bytes('7\nabc')
1734
        self.assertEqual(9, protocol.next_read_size())
1735
        protocol.accept_bytes('defgd')
1736
        protocol.accept_bytes('one\n')
1737
        self.assertEqual(0, protocol.next_read_size())
1738
        self.assertTrue(self.end_received)
1739
1740
    def test_accept_request_and_body_all_at_once(self):
1741
        self._captureVar('BZR_NO_SMART_VFS', None)
1742
        mem_transport = memory.MemoryTransport()
1743
        mem_transport.put_bytes('foo', 'abcdefghij')
1744
        out_stream = StringIO()
1745
        smart_protocol = protocol.SmartServerRequestProtocolTwo(mem_transport,
1746
                out_stream.write)
1747
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1748
        self.assertEqual(0, smart_protocol.next_read_size())
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1749
        self.assertEqual(protocol.RESPONSE_VERSION_TWO +
1750
                         'success\nreadv\n3\ndefdone\n',
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1751
                         out_stream.getvalue())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1752
        self.assertEqual('', smart_protocol.excess_buffer)
1753
        self.assertEqual('', smart_protocol.in_buffer)
1754
1755
    def test_accept_excess_bytes_are_preserved(self):
1756
        out_stream = StringIO()
1757
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1758
            None, out_stream.write)
1759
        smart_protocol.accept_bytes('hello\nhello\n')
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1760
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + "success\nok\x012\n",
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1761
                         out_stream.getvalue())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1762
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1763
        self.assertEqual("", smart_protocol.in_buffer)
1764
1765
    def test_accept_excess_bytes_after_body(self):
1766
        # The excess bytes look like the start of another request.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1767
        server_protocol = self.build_protocol_waiting_for_body()
1768
        server_protocol.accept_bytes(
1769
            '7\nabcdefgdone\n' + protocol.RESPONSE_VERSION_TWO)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1770
        self.assertTrue(self.end_received)
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1771
        self.assertEqual(protocol.RESPONSE_VERSION_TWO,
1772
                         server_protocol.excess_buffer)
1773
        self.assertEqual("", server_protocol.in_buffer)
1774
        server_protocol.accept_bytes('Y')
1775
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + "Y",
1776
                         server_protocol.excess_buffer)
1777
        self.assertEqual("", server_protocol.in_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1778
1779
    def test_accept_excess_bytes_after_dispatch(self):
1780
        out_stream = StringIO()
1781
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1782
            None, out_stream.write)
1783
        smart_protocol.accept_bytes('hello\n')
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1784
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + "success\nok\x012\n",
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1785
                         out_stream.getvalue())
1786
        smart_protocol.accept_bytes(protocol.REQUEST_VERSION_TWO + 'hel')
1787
        self.assertEqual(protocol.REQUEST_VERSION_TWO + "hel",
1788
                         smart_protocol.excess_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1789
        smart_protocol.accept_bytes('lo\n')
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1790
        self.assertEqual(protocol.REQUEST_VERSION_TWO + "hello\n",
1791
                         smart_protocol.excess_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1792
        self.assertEqual("", smart_protocol.in_buffer)
1793
1794
    def test__send_response_sets_finished_reading(self):
1795
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1796
            None, lambda x: None)
1797
        self.assertEqual(1, smart_protocol.next_read_size())
2432.4.3 by Robert Collins
Refactor the HPSS Response code to take SmartServerResponse rather than args and body.
1798
        smart_protocol._send_response(
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1799
            _mod_request.SuccessfulSmartServerResponse(('x',)))
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1800
        self.assertEqual(0, smart_protocol.next_read_size())
1801
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1802
    def test__send_response_with_body_stream_sets_finished_reading(self):
1803
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1804
            None, lambda x: None)
1805
        self.assertEqual(1, smart_protocol.next_read_size())
1806
        smart_protocol._send_response(
2692.1.2 by Andrew Bennetts
Merge from bzr.dev.
1807
            _mod_request.SuccessfulSmartServerResponse(('x',), body_stream=[]))
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1808
        self.assertEqual(0, smart_protocol.next_read_size())
1809
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1810
    def test__send_response_errors_with_base_response(self):
1811
        """Ensure that only the Successful/Failed subclasses are used."""
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1812
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1813
            None, lambda x: None)
1814
        self.assertRaises(AttributeError, smart_protocol._send_response,
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1815
            _mod_request.SmartServerResponse(('x',)))
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1816
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1817
    def test__send_response_includes_failure_marker(self):
1818
        """FailedSmartServerResponse have 'failed\n' after the version."""
1819
        out_stream = StringIO()
1820
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1821
            None, out_stream.write)
1822
        smart_protocol._send_response(
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1823
            _mod_request.FailedSmartServerResponse(('x',)))
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1824
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'failed\nx\n',
1825
                         out_stream.getvalue())
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1826
1827
    def test__send_response_includes_success_marker(self):
1828
        """SuccessfulSmartServerResponse have 'success\n' after the version."""
1829
        out_stream = StringIO()
1830
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1831
            None, out_stream.write)
1832
        smart_protocol._send_response(
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
1833
            _mod_request.SuccessfulSmartServerResponse(('x',)))
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1834
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'success\nx\n',
1835
                         out_stream.getvalue())
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1836
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1837
    def test_query_version(self):
1838
        """query_version on a SmartClientProtocolTwo should return a number.
1839
        
1840
        The protocol provides the query_version because the domain level clients
1841
        may all need to be able to probe for capabilities.
1842
        """
1843
        # What we really want to test here is that SmartClientProtocolTwo calls
1844
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1845
        # response of tuple-encoded (ok, 1).  Also, seperately we should test
1846
        # the error if the response is a non-understood version.
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1847
        input = StringIO(protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n')
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1848
        output = StringIO()
1849
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1850
        request = client_medium.get_request()
1851
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1852
        self.assertEqual(2, smart_protocol.query_version())
1853
1854
    def test_client_call_empty_response(self):
1855
        # protocol.call() can get back an empty tuple as a response. This occurs
1856
        # when the parsed line is an empty line, and results in a tuple with
1857
        # one element - an empty string.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1858
        self.assertServerToClientEncoding(
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1859
            protocol.RESPONSE_VERSION_TWO + 'success\n\n', ('', ), [(), ('', )])
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1860
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1861
    def test_client_call_three_element_response(self):
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1862
        # protocol.call() can get back tuples of other lengths. A three element
1863
        # tuple should be unpacked as three strings.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1864
        self.assertServerToClientEncoding(
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1865
            protocol.RESPONSE_VERSION_TWO + 'success\na\x01b\x0134\n',
1866
            ('a', 'b', '34'),
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1867
            [('a', 'b', '34')])
1868
1869
    def test_client_call_with_body_bytes_uploads(self):
1870
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
1871
        # wire.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1872
        expected_bytes = protocol.REQUEST_VERSION_TWO + "foo\n7\nabcdefgdone\n"
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1873
        input = StringIO("\n")
1874
        output = StringIO()
1875
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1876
        request = client_medium.get_request()
1877
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1878
        smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
1879
        self.assertEqual(expected_bytes, output.getvalue())
1880
1881
    def test_client_call_with_body_readv_array(self):
1882
        # protocol.call_with_upload should encode the readv array and then
1883
        # length-prefix the bytes onto the wire.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1884
        expected_bytes = protocol.REQUEST_VERSION_TWO+"foo\n7\n1,2\n5,6done\n"
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1885
        input = StringIO("\n")
1886
        output = StringIO()
1887
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1888
        request = client_medium.get_request()
1889
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1890
        smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1891
        self.assertEqual(expected_bytes, output.getvalue())
1892
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1893
    def test_client_read_response_tuple_sets_response_status(self):
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1894
        server_bytes = protocol.RESPONSE_VERSION_TWO + "success\nok\n"
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1895
        input = StringIO(server_bytes)
1896
        output = StringIO()
1897
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1898
        request = client_medium.get_request()
1899
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1900
        smart_protocol.call('foo')
1901
        smart_protocol.read_response_tuple(False)
1902
        self.assertEqual(True, smart_protocol.response_status)
1903
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1904
    def test_client_read_body_bytes_all(self):
1905
        # read_body_bytes should decode the body bytes from the wire into
1906
        # a response.
1907
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1908
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1909
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1910
        input = StringIO(server_bytes)
1911
        output = StringIO()
1912
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1913
        request = client_medium.get_request()
1914
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1915
        smart_protocol.call('foo')
1916
        smart_protocol.read_response_tuple(True)
1917
        self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
1918
1919
    def test_client_read_body_bytes_incremental(self):
1920
        # test reading a few bytes at a time from the body
1921
        # XXX: possibly we should test dribbling the bytes into the stringio
1922
        # to make the state machine work harder: however, as we use the
1923
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1924
        # that.
1925
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1926
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1927
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1928
        input = StringIO(server_bytes)
1929
        output = StringIO()
1930
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1931
        request = client_medium.get_request()
1932
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1933
        smart_protocol.call('foo')
1934
        smart_protocol.read_response_tuple(True)
1935
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1936
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1937
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1938
        self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
1939
1940
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1941
        # cancelling the expected body needs to finish the request, but not
1942
        # read any more bytes.
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1943
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1944
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1945
        input = StringIO(server_bytes)
1946
        output = StringIO()
1947
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1948
        request = client_medium.get_request()
1949
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1950
        smart_protocol.call('foo')
1951
        smart_protocol.read_response_tuple(True)
1952
        smart_protocol.cancel_read_body()
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1953
        self.assertEqual(len(protocol.RESPONSE_VERSION_TWO + 'success\nok\n'),
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1954
                         input.tell())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1955
        self.assertRaises(
1956
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
1957
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1958
    def test_streamed_body_bytes(self):
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1959
        body_header = 'chunked\n'
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1960
        two_body_chunks = "4\n1234" + "3\n567"
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
1961
        body_terminator = "END\n"
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1962
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1963
                        "success\nok\n" + body_header + two_body_chunks +
1964
                        body_terminator)
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
1965
        input = StringIO(server_bytes)
1966
        output = StringIO()
1967
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1968
        request = client_medium.get_request()
1969
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1970
        smart_protocol.call('foo')
1971
        smart_protocol.read_response_tuple(True)
1972
        stream = smart_protocol.read_streamed_body()
1973
        self.assertEqual(['1234', '567'], list(stream))
1974
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1975
    def test_read_streamed_body_error(self):
1976
        """When a stream is interrupted by an error..."""
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1977
        body_header = 'chunked\n'
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1978
        a_body_chunk = '4\naaaa'
2748.4.6 by Andrew Bennetts
Use chunks for stream errors, rather than the response tuple format.
1979
        err_signal = 'ERR\n'
1980
        err_chunks = 'a\nerror arg1' + '4\narg2'
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
1981
        finish = 'END\n'
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
1982
        body = body_header + a_body_chunk + err_signal + err_chunks + finish
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1983
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
2748.4.6 by Andrew Bennetts
Use chunks for stream errors, rather than the response tuple format.
1984
                        "success\nok\n" + body)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1985
        input = StringIO(server_bytes)
1986
        output = StringIO()
1987
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1988
        smart_request = client_medium.get_request()
1989
        smart_protocol = protocol.SmartClientRequestProtocolTwo(smart_request)
1990
        smart_protocol.call('foo')
1991
        smart_protocol.read_response_tuple(True)
1992
        expected_chunks = [
1993
            'aaaa',
2692.1.2 by Andrew Bennetts
Merge from bzr.dev.
1994
            _mod_request.FailedSmartServerResponse(('error arg1', 'arg2'))]
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
1995
        stream = smart_protocol.read_streamed_body()
1996
        self.assertEqual(expected_chunks, list(stream))
1997
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1998
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1999
class TestSmartClientUnicode(tests.TestCase):
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
2000
    """_SmartClient tests for unicode arguments.
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
2001
2002
    Unicode arguments to call_with_body_bytes are not correct (remote method
2003
    names, arguments, and bodies must all be expressed as byte strings), but
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
2004
    _SmartClient should gracefully reject them, rather than getting into a
2005
    broken state that prevents future correct calls from working.  That is, it
2006
    should be possible to issue more requests on the medium afterwards, rather
2007
    than allowing one bad call to call_with_body_bytes to cause later calls to
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
2008
    mysteriously fail with TooManyConcurrentRequests.
2009
    """
2010
2011
    def assertCallDoesNotBreakMedium(self, method, args, body):
2012
        """Call a medium with the given method, args and body, then assert that
2013
        the medium is left in a sane state, i.e. is capable of allowing further
2014
        requests.
2015
        """
2016
        input = StringIO("\n")
2017
        output = StringIO()
2018
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
2019
        smart_client = client._SmartClient(client_medium)
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
2020
        self.assertRaises(TypeError,
2021
            smart_client.call_with_body_bytes, method, args, body)
2022
        self.assertEqual("", output.getvalue())
2023
        self.assertEqual(None, client_medium._current_request)
2024
2025
    def test_call_with_body_bytes_unicode_method(self):
2026
        self.assertCallDoesNotBreakMedium(u'method', ('args',), 'body')
2027
2028
    def test_call_with_body_bytes_unicode_args(self):
2029
        self.assertCallDoesNotBreakMedium('method', (u'args',), 'body')
2414.1.2 by Andrew Bennetts
Deal with review comments.
2030
        self.assertCallDoesNotBreakMedium('method', ('arg1', u'arg2'), 'body')
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
2031
2032
    def test_call_with_body_bytes_unicode_body(self):
2033
        self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
2034
2035
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2036
class LengthPrefixedBodyDecoder(tests.TestCase):
2037
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
2038
    # XXX: TODO: make accept_reading_trailer invoke translate_response or 
2039
    # something similar to the ProtocolBase method.
2040
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2041
    def test_construct(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
2042
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2043
        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.
2044
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2045
        self.assertEqual('', decoder.read_pending_data())
2046
        self.assertEqual('', decoder.unused_data)
2047
2048
    def test_accept_bytes(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
2049
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2050
        decoder.accept_bytes('')
2051
        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.
2052
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2053
        self.assertEqual('', decoder.read_pending_data())
2054
        self.assertEqual('', decoder.unused_data)
2055
        decoder.accept_bytes('7')
2056
        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.
2057
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2058
        self.assertEqual('', decoder.read_pending_data())
2059
        self.assertEqual('', decoder.unused_data)
2060
        decoder.accept_bytes('\na')
2061
        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.
2062
        self.assertEqual(11, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2063
        self.assertEqual('a', decoder.read_pending_data())
2064
        self.assertEqual('', decoder.unused_data)
2065
        decoder.accept_bytes('bcdefgd')
2066
        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.
2067
        self.assertEqual(4, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2068
        self.assertEqual('bcdefg', decoder.read_pending_data())
2069
        self.assertEqual('', decoder.unused_data)
2070
        decoder.accept_bytes('one')
2071
        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.
2072
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2073
        self.assertEqual('', decoder.read_pending_data())
2074
        self.assertEqual('', decoder.unused_data)
2075
        decoder.accept_bytes('\nblarg')
2076
        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.
2077
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2078
        self.assertEqual('', decoder.read_pending_data())
2079
        self.assertEqual('blarg', decoder.unused_data)
2080
        
2081
    def test_accept_bytes_all_at_once_with_excess(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
2082
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2083
        decoder.accept_bytes('1\nadone\nunused')
2084
        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.
2085
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2086
        self.assertEqual('a', decoder.read_pending_data())
2087
        self.assertEqual('unused', decoder.unused_data)
2088
2089
    def test_accept_bytes_exact_end_of_body(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
2090
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2091
        decoder.accept_bytes('1\na')
2092
        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.
2093
        self.assertEqual(5, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2094
        self.assertEqual('a', decoder.read_pending_data())
2095
        self.assertEqual('', decoder.unused_data)
2096
        decoder.accept_bytes('done\n')
2097
        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.
2098
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2099
        self.assertEqual('', decoder.read_pending_data())
2100
        self.assertEqual('', decoder.unused_data)
2101
2102
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2103
class TestChunkedBodyDecoder(tests.TestCase):
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2104
    """Tests for ChunkedBodyDecoder.
2105
    
2106
    This is the body decoder used for protocol version two.
2107
    """
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2108
2109
    def test_construct(self):
2110
        decoder = protocol.ChunkedBodyDecoder()
2111
        self.assertFalse(decoder.finished_reading)
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2112
        self.assertEqual(8, decoder.next_read_size())
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2113
        self.assertEqual(None, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2114
        self.assertEqual('', decoder.unused_data)
2115
2116
    def test_empty_content(self):
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2117
        """'chunked\nEND\n' is the complete encoding of a zero-length body.
2118
        """
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2119
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2120
        decoder.accept_bytes('chunked\n')
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2121
        decoder.accept_bytes('END\n')
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2122
        self.assertTrue(decoder.finished_reading)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2123
        self.assertEqual(None, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2124
        self.assertEqual('', decoder.unused_data)
2125
2126
    def test_one_chunk(self):
2127
        """A body in a single chunk is decoded correctly."""
2128
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2129
        decoder.accept_bytes('chunked\n')
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2130
        chunk_length = 'f\n'
2131
        chunk_content = '123456789abcdef'
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2132
        finish = 'END\n'
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2133
        decoder.accept_bytes(chunk_length + chunk_content + finish)
2134
        self.assertTrue(decoder.finished_reading)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2135
        self.assertEqual(chunk_content, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2136
        self.assertEqual('', decoder.unused_data)
2137
        
2138
    def test_incomplete_chunk(self):
2139
        """When there are less bytes in the chunk than declared by the length,
2140
        then we haven't finished reading yet.
2141
        """
2142
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2143
        decoder.accept_bytes('chunked\n')
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2144
        chunk_length = '8\n'
2145
        three_bytes = '123'
2146
        decoder.accept_bytes(chunk_length + three_bytes)
2147
        self.assertFalse(decoder.finished_reading)
2148
        self.assertEqual(
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2149
            5 + 4, decoder.next_read_size(),
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2150
            "The next_read_size hint should be the number of missing bytes in "
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2151
            "this chunk plus 4 (the length of the end-of-body marker: "
2152
            "'END\\n')")
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2153
        self.assertEqual(None, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2154
2155
    def test_incomplete_length(self):
2156
        """A chunk length hasn't been read until a newline byte has been read.
2157
        """
2158
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2159
        decoder.accept_bytes('chunked\n')
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2160
        decoder.accept_bytes('9')
2161
        self.assertEqual(
2162
            1, decoder.next_read_size(),
2163
            "The next_read_size hint should be 1, because we don't know the "
2164
            "length yet.")
2165
        decoder.accept_bytes('\n')
2166
        self.assertEqual(
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2167
            9 + 4, decoder.next_read_size(),
2168
            "The next_read_size hint should be the length of the chunk plus 4 "
2169
            "(the length of the end-of-body marker: 'END\\n')")
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2170
        self.assertFalse(decoder.finished_reading)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2171
        self.assertEqual(None, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2172
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2173
    def test_two_chunks(self):
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2174
        """Content from multiple chunks is concatenated."""
2175
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2176
        decoder.accept_bytes('chunked\n')
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2177
        chunk_one = '3\naaa'
2178
        chunk_two = '5\nbbbbb'
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2179
        finish = 'END\n'
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2180
        decoder.accept_bytes(chunk_one + chunk_two + finish)
2181
        self.assertTrue(decoder.finished_reading)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2182
        self.assertEqual('aaa', decoder.read_next_chunk())
2183
        self.assertEqual('bbbbb', decoder.read_next_chunk())
2184
        self.assertEqual(None, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2185
        self.assertEqual('', decoder.unused_data)
2186
2187
    def test_excess_bytes(self):
2188
        """Bytes after the chunked body are reported as unused bytes."""
2189
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2190
        decoder.accept_bytes('chunked\n')
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2191
        chunked_body = "5\naaaaaEND\n"
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2192
        excess_bytes = "excess bytes"
2193
        decoder.accept_bytes(chunked_body + excess_bytes)
2194
        self.assertTrue(decoder.finished_reading)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2195
        self.assertEqual('aaaaa', decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2196
        self.assertEqual(excess_bytes, decoder.unused_data)
2197
        self.assertEqual(
2198
            1, decoder.next_read_size(),
2199
            "next_read_size hint should be 1 when finished_reading.")
2200
2201
    def test_multidigit_length(self):
2202
        """Lengths in the chunk prefixes can have multiple digits."""
2203
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2204
        decoder.accept_bytes('chunked\n')
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2205
        length = 0x123
2206
        chunk_prefix = hex(length) + '\n'
2207
        chunk_bytes = 'z' * length
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2208
        finish = 'END\n'
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2209
        decoder.accept_bytes(chunk_prefix + chunk_bytes + finish)
2210
        self.assertTrue(decoder.finished_reading)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2211
        self.assertEqual(chunk_bytes, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2212
2213
    def test_byte_at_a_time(self):
2214
        """A complete body fed to the decoder one byte at a time should not
2215
        confuse the decoder.  That is, it should give the same result as if the
2216
        bytes had been received in one batch.
2217
2218
        This test is the same as test_one_chunk apart from the way accept_bytes
2219
        is called.
2220
        """
2221
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2222
        decoder.accept_bytes('chunked\n')
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2223
        chunk_length = 'f\n'
2224
        chunk_content = '123456789abcdef'
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2225
        finish = 'END\n'
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2226
        for byte in (chunk_length + chunk_content + finish):
2227
            decoder.accept_bytes(byte)
2228
        self.assertTrue(decoder.finished_reading)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2229
        self.assertEqual(chunk_content, decoder.read_next_chunk())
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2230
        self.assertEqual('', decoder.unused_data)
2231
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2232
    def test_read_pending_data_resets(self):
2233
        """read_pending_data does not return the same bytes twice."""
2234
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2235
        decoder.accept_bytes('chunked\n')
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2236
        chunk_one = '3\naaa'
2237
        chunk_two = '3\nbbb'
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2238
        finish = 'END\n'
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2239
        decoder.accept_bytes(chunk_one)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2240
        self.assertEqual('aaa', decoder.read_next_chunk())
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2241
        decoder.accept_bytes(chunk_two)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2242
        self.assertEqual('bbb', decoder.read_next_chunk())
2243
        self.assertEqual(None, decoder.read_next_chunk())
2244
2245
    def test_decode_error(self):
2246
        decoder = protocol.ChunkedBodyDecoder()
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2247
        decoder.accept_bytes('chunked\n')
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2248
        chunk_one = 'b\nfirst chunk'
2748.4.6 by Andrew Bennetts
Use chunks for stream errors, rather than the response tuple format.
2249
        error_signal = 'ERR\n'
2250
        error_chunks = '5\npart1' + '5\npart2'
2748.4.7 by Andrew Bennetts
Change the end-of-body marker to something clearer than a zero-length chunk.
2251
        finish = 'END\n'
2748.4.6 by Andrew Bennetts
Use chunks for stream errors, rather than the response tuple format.
2252
        decoder.accept_bytes(chunk_one + error_signal + error_chunks + finish)
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2253
        self.assertTrue(decoder.finished_reading)
2254
        self.assertEqual('first chunk', decoder.read_next_chunk())
2692.1.2 by Andrew Bennetts
Merge from bzr.dev.
2255
        expected_failure = _mod_request.FailedSmartServerResponse(
2748.4.6 by Andrew Bennetts
Use chunks for stream errors, rather than the response tuple format.
2256
            ('part1', 'part2'))
2748.4.5 by Andrew Bennetts
Allow an error to interrupt (and terminate) a streamed response body.
2257
        self.assertEqual(expected_failure, decoder.read_next_chunk())
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2258
2748.4.16 by Andrew Bennetts
Tweaks suggested by review.
2259
    def test_bad_header(self):
2260
        """accept_bytes raises a SmartProtocolError if a chunked body does not
2261
        start with the right header.
2262
        """
2263
        decoder = protocol.ChunkedBodyDecoder()
2264
        self.assertRaises(
2265
            errors.SmartProtocolError, decoder.accept_bytes, 'bad header\n')
2266
2748.4.1 by Andrew Bennetts
Implement a ChunkedBodyDecoder.
2267
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2268
class TestSuccessfulSmartServerResponse(tests.TestCase):
2269
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2270
    def test_construct_no_body(self):
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
2271
        response = _mod_request.SuccessfulSmartServerResponse(('foo', 'bar'))
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2272
        self.assertEqual(('foo', 'bar'), response.args)
2273
        self.assertEqual(None, response.body)
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2274
2275
    def test_construct_with_body(self):
2692.1.2 by Andrew Bennetts
Merge from bzr.dev.
2276
        response = _mod_request.SuccessfulSmartServerResponse(('foo', 'bar'),
2277
                                                              'bytes')
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2278
        self.assertEqual(('foo', 'bar'), response.args)
2279
        self.assertEqual('bytes', response.body)
2280
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2281
    def test_construct_with_body_stream(self):
2282
        bytes_iterable = ['abc']
2692.1.2 by Andrew Bennetts
Merge from bzr.dev.
2283
        response = _mod_request.SuccessfulSmartServerResponse(
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2284
            ('foo', 'bar'), body_stream=bytes_iterable)
2285
        self.assertEqual(('foo', 'bar'), response.args)
2286
        self.assertEqual(bytes_iterable, response.body_stream)
2287
2288
    def test_construct_rejects_body_and_body_stream(self):
2289
        """'body' and 'body_stream' are mutually exclusive."""
2290
        self.assertRaises(
2291
            errors.BzrError,
2692.1.2 by Andrew Bennetts
Merge from bzr.dev.
2292
            _mod_request.SuccessfulSmartServerResponse, (), 'body', ['stream'])
2748.4.2 by Andrew Bennetts
Add protocol (version two) support for streaming bodies (using chunking) in responses.
2293
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2294
    def test_is_successful(self):
2295
        """is_successful should return True for SuccessfulSmartServerResponse."""
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
2296
        response = _mod_request.SuccessfulSmartServerResponse(('error',))
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2297
        self.assertEqual(True, response.is_successful())
2298
2299
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
2300
class TestFailedSmartServerResponse(tests.TestCase):
2301
2302
    def test_construct(self):
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
2303
        response = _mod_request.FailedSmartServerResponse(('foo', 'bar'))
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
2304
        self.assertEqual(('foo', 'bar'), response.args)
2305
        self.assertEqual(None, response.body)
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
2306
        response = _mod_request.FailedSmartServerResponse(('foo', 'bar'), 'bytes')
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
2307
        self.assertEqual(('foo', 'bar'), response.args)
2308
        self.assertEqual('bytes', response.body)
2309
2310
    def test_is_successful(self):
2311
        """is_successful should return False for FailedSmartServerResponse."""
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
2312
        response = _mod_request.FailedSmartServerResponse(('error',))
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
2313
        self.assertEqual(False, response.is_successful())
2314
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2315
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2316
class FakeHTTPMedium(object):
2317
    def __init__(self):
2318
        self.written_request = None
2319
        self._current_request = None
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
2320
    def send_http_smart_request(self, bytes):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2321
        self.written_request = bytes
2322
        return None
2323
2324
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2325
class HTTPTunnellingSmokeTest(tests.TestCaseWithTransport):
2326
    
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
2327
    def setUp(self):
2328
        super(HTTPTunnellingSmokeTest, self).setUp()
2329
        # We use the VFS layer as part of HTTP tunnelling tests.
2402.1.2 by Andrew Bennetts
Deal with review comments.
2330
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
2331
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2332
    def _test_bulk_data(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2333
        # We should be able to send and receive bulk data in a single message.
2334
        # The 'readv' command in the smart protocol both sends and receives bulk
2335
        # data, so we use that.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2336
        self.build_tree(['data-file'])
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2337
        self.transport_readonly_server = HTTPServerWithSmarts
2338
2339
        http_transport = self.get_readonly_transport()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
2340
        medium = http_transport.get_smart_medium()
2900.2.16 by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password).
2341
        # Since we provide the medium, the url below will be mostly ignored
2342
        # during the test, as long as the path is '/'.
2343
        remote_transport = remote.RemoteTransport('bzr://fake_host/',
2344
                                                  medium=medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2345
        self.assertEqual(
2346
            [(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
2347
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2348
    def test_bulk_data_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
2349
        try:
2350
            self._test_bulk_data('http+pycurl')
2351
        except errors.UnsupportedProtocol, e:
2352
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2353
    
2354
    def test_bulk_data_urllib(self):
2355
        self._test_bulk_data('http+urllib')
2356
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2357
    def test_smart_http_medium_request_accept_bytes(self):
2358
        medium = FakeHTTPMedium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
2359
        request = SmartClientHTTPMediumRequest(medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2360
        request.accept_bytes('abc')
2361
        request.accept_bytes('def')
2362
        self.assertEqual(None, medium.written_request)
2363
        request.finished_writing()
2364
        self.assertEqual('abcdef', medium.written_request)
2365
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2366
    def _test_http_send_smart_request(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2367
        http_server = HTTPServerWithSmarts()
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2368
        http_server._url_protocol = url_protocol
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2369
        http_server.setUp(self.get_vfs_only_server())
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2370
        self.addCleanup(http_server.tearDown)
2371
2372
        post_body = 'hello\n'
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
2373
        expected_reply_body = 'ok\x012\n'
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2374
2375
        http_transport = get_transport(http_server.get_url())
2376
        medium = http_transport.get_smart_medium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
2377
        response = medium.send_http_smart_request(post_body)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2378
        reply_body = response.read()
2379
        self.assertEqual(expected_reply_body, reply_body)
2380
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2381
    def test_http_send_smart_request_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
2382
        try:
2383
            self._test_http_send_smart_request('http+pycurl')
2384
        except errors.UnsupportedProtocol, e:
2385
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2386
2387
    def test_http_send_smart_request_urllib(self):
2388
        self._test_http_send_smart_request('http+urllib')
2389
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2390
    def test_http_server_with_smarts(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2391
        self.transport_readonly_server = HTTPServerWithSmarts
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2392
2393
        post_body = 'hello\n'
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
2394
        expected_reply_body = 'ok\x012\n'
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2395
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2396
        smart_server_url = self.get_readonly_url('.bzr/smart')
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2397
        reply = urllib2.urlopen(smart_server_url, post_body).read()
2398
2399
        self.assertEqual(expected_reply_body, reply)
2400
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2401
    def test_smart_http_server_post_request_handler(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2402
        self.transport_readonly_server = HTTPServerWithSmarts
2403
        httpd = self.get_readonly_server()._get_httpd()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2404
2405
        socket = SampleSocket(
2406
            'POST /.bzr/smart HTTP/1.0\r\n'
2407
            # HTTP/1.0 posts must have a Content-Length.
2408
            'Content-Length: 6\r\n'
2409
            '\r\n'
2410
            'hello\n')
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
2411
        # Beware: the ('localhost', 80) below is the
2412
        # client_address parameter, but we don't have one because
2164.2.14 by v.ladeuil+lp at free
Typo in comment
2413
        # 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.
2414
        # address. The test framework never uses this client
2415
        # address, so far...
2416
        request_handler = SmartRequestHandler(socket, ('localhost', 80), httpd)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2417
        response = socket.writefile.getvalue()
2418
        self.assertStartsWith(response, 'HTTP/1.0 200 ')
2419
        # This includes the end of the HTTP headers, and all the body.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
2420
        expected_end_of_response = '\r\n\r\nok\x012\n'
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2421
        self.assertEndsWith(response, expected_end_of_response)
2422
2423
2424
class SampleSocket(object):
2425
    """A socket-like object for use in testing the HTTP request handler."""
2426
    
2427
    def __init__(self, socket_read_content):
2428
        """Constructs a sample socket.
2429
2430
        :param socket_read_content: a byte sequence
2431
        """
2432
        # Use plain python StringIO so we can monkey-patch the close method to
2433
        # not discard the contents.
2434
        from StringIO import StringIO
2435
        self.readfile = StringIO(socket_read_content)
2436
        self.writefile = StringIO()
2437
        self.writefile.close = lambda: None
2438
        
2439
    def makefile(self, mode='r', bufsize=None):
2440
        if 'r' in mode:
2441
            return self.readfile
2442
        else:
2443
            return self.writefile
2444
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
2445
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2446
class RemoteHTTPTransportTestCase(tests.TestCase):
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2447
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2448
    def test_remote_path_after_clone_child(self):
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2449
        # If a user enters "bzr+http://host/foo", we want to sent all smart
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2450
        # requests for child URLs of that to the original URL.  i.e., we want to
2451
        # POST to "bzr+http://host/foo/.bzr/smart" and never something like
2452
        # "bzr+http://host/foo/.bzr/branch/.bzr/smart".  So, a cloned
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2453
        # RemoteHTTPTransport remembers the initial URL, and adjusts the relpaths
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2454
        # it sends in smart requests accordingly.
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2455
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/path')
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2456
        new_transport = base_transport.clone('child_dir')
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2457
        self.assertEqual(base_transport._http_transport,
2458
                         new_transport._http_transport)
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2459
        self.assertEqual('child_dir/foo', new_transport._remote_path('foo'))
2460
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2461
    def test_remote_path_after_clone_parent(self):
2462
        # However, accessing a parent directory should go direct to the parent's
2463
        # URL.  We don't send relpaths like "../foo" in smart requests.
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2464
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/path1/path2')
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2465
        new_transport = base_transport.clone('..')
2466
        self.assertEqual('foo', new_transport._remote_path('foo'))
2467
        new_transport = base_transport.clone('../')
2468
        self.assertEqual('foo', new_transport._remote_path('foo'))
2469
        new_transport = base_transport.clone('../abc')
2470
        self.assertEqual('foo', new_transport._remote_path('foo'))
2471
        # "abc/../.." should be equivalent to ".."
2472
        new_transport = base_transport.clone('abc/../..')
2473
        self.assertEqual('foo', new_transport._remote_path('foo'))
2474
2466.3.1 by Andrew Bennetts
Normalise URLs in RemoteHTTPTransport before doing URL calculations to fix bad results.
2475
    def test_remote_path_unnormal_base(self):
2476
        # If the transport's base isn't normalised, the _remote_path should
2477
        # still be calculated correctly.
2478
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/%7Ea/b')
2479
        self.assertEqual('c', base_transport._remote_path('c'))
2480
2481
    def test_clone_unnormal_base(self):
2482
        # If the transport's base isn't normalised, cloned transports should
2483
        # still work correctly.
2484
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/%7Ea/b')
2485
        new_transport = base_transport.clone('c')
2486
        self.assertEqual('bzr+http://host/%7Ea/b/c/', new_transport.base)
2487
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2488
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
2489
# TODO: Client feature that does get_bundle and then installs that into a
2490
# branch; this can be used in place of the regular pull/fetch operation when
2491
# coming from a smart server.
2492
#
2493
# TODO: Eventually, want to do a 'branch' command by fetching the whole
2494
# history as one big bundle.  How?  
2495
#
2496
# The branch command does 'br_from.sprout', which tries to preserve the same
2497
# format.  We don't necessarily even want that.  
2498
#
2499
# It might be simpler to handle cmd_pull first, which does a simpler fetch()
2500
# operation from one branch into another.  It already has some code for
2501
# pulling from a bundle, which it does by trying to see if the destination is
2502
# a bundle file.  So it seems the logic for pull ought to be:
2503
# 
2504
#  - if it's a smart server, get a bundle from there and install that
2505
#  - if it's a bundle, install that
2506
#  - if it's a branch, pull from there
2507
#
2508
# Getting a bundle from a smart server is a bit different from reading a
2509
# bundle from a URL:
2510
#
2511
#  - we can reasonably remember the URL we last read from 
2512
#  - you can specify a revision number to pull, and we need to pass it across
2513
#    to the server as a limit on what will be requested
2514
#
2515
# TODO: Given a URL, determine whether it is a smart server or not (or perhaps
2516
# otherwise whether it's a bundle?)  Should this be a property or method of
2517
# the transport?  For the ssh protocol, we always know it's a smart server.
2518
# For http, we potentially need to probe.  But if we're explicitly given
2519
# bzr+http:// then we can skip that for now.