/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
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,
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
37
        request,
2432.4.3 by Robert Collins
Refactor the HPSS Response code to take SmartServerResponse rather than args and body.
38
        request as _mod_request,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
39
        server,
40
        vfs,
41
)
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
42
from bzrlib.tests.HTTPTestUtil import (
43
        HTTPServerWithSmarts,
44
        SmartRequestHandler,
45
        )
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
46
from bzrlib.tests.test_smart import TestCaseWithSmartMedium
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
47
from bzrlib.transport import (
48
        get_transport,
49
        local,
50
        memory,
2400.1.1 by Andrew Bennetts
Rename bzrlib/transport/smart.py to bzrlib/transport/remote.py.
51
        remote,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
52
        )
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
53
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
54
55
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
56
class StringIOSSHVendor(object):
57
    """A SSH vendor that uses StringIO to buffer writes and answer reads."""
58
59
    def __init__(self, read_from, write_to):
60
        self.read_from = read_from
61
        self.write_to = write_to
62
        self.calls = []
63
64
    def connect_ssh(self, username, password, host, port, command):
65
        self.calls.append(('connect_ssh', username, password, host, port,
66
            command))
67
        return StringIOSSHConnection(self)
68
69
70
class StringIOSSHConnection(object):
71
    """A SSH connection that uses StringIO to buffer writes and answer reads."""
72
73
    def __init__(self, vendor):
74
        self.vendor = vendor
75
    
76
    def close(self):
77
        self.vendor.calls.append(('close', ))
78
        
79
    def get_filelike_channels(self):
80
        return self.vendor.read_from, self.vendor.write_to
81
82
83
84
class SmartClientMediumTests(tests.TestCase):
85
    """Tests for SmartClientMedium.
86
87
    We should create a test scenario for this: we need a server module that
88
    construct the test-servers (like make_loopsocket_and_medium), and the list
89
    of SmartClientMedium classes to test.
90
    """
91
92
    def make_loopsocket_and_medium(self):
93
        """Create a loopback socket for testing, and a medium aimed at it."""
94
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
95
        sock.bind(('127.0.0.1', 0))
96
        sock.listen(1)
97
        port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
98
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', port)
99
        return sock, client_medium
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
100
101
    def receive_bytes_on_server(self, sock, bytes):
102
        """Accept a connection on sock and read 3 bytes.
103
104
        The bytes are appended to the list bytes.
105
106
        :return: a Thread which is running to do the accept and recv.
107
        """
108
        def _receive_bytes_on_server():
109
            connection, address = sock.accept()
2091.1.1 by Martin Pool
Avoid MSG_WAITALL as it doesn't work on Windows
110
            bytes.append(osutils.recv_all(connection, 3))
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
111
            connection.close()
112
        t = threading.Thread(target=_receive_bytes_on_server)
113
        t.start()
114
        return t
115
    
116
    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.
117
        # make a new instance of the common base for Stream-like Mediums.
118
        # this just ensures that the constructor stays parameter-free which
119
        # is important for reuse : some subclasses will dynamically connect,
120
        # others are always on, etc.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
121
        client_medium = medium.SmartClientStreamMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
122
123
    def test_construct_smart_client_medium(self):
124
        # the base client medium takes no parameters
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
125
        client_medium = medium.SmartClientMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
126
    
127
    def test_construct_smart_simple_pipes_client_medium(self):
128
        # the SimplePipes client medium takes two pipes:
129
        # readable pipe, writeable pipe.
130
        # Constructing one should just save these and do nothing.
131
        # We test this by passing in None.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
132
        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.
133
        
134
    def test_simple_pipes_client_request_type(self):
135
        # SimplePipesClient should use SmartClientStreamMediumRequest's.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
136
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
137
        request = client_medium.get_request()
138
        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.
139
140
    def test_simple_pipes_client_get_concurrent_requests(self):
141
        # the simple_pipes client does not support pipelined requests:
142
        # but it does support serial requests: we construct one after 
143
        # another is finished. This is a smoke test testing the integration
144
        # of the SmartClientStreamMediumRequest and the SmartClientStreamMedium
145
        # classes - as the sibling classes share this logic, they do not have
146
        # explicit tests for this.
147
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
148
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
149
        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.
150
        request.finished_writing()
151
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
152
        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.
153
        request2.finished_writing()
154
        request2.finished_reading()
155
156
    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.
157
        # accept_bytes writes to the writeable pipe.
158
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
159
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
160
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
161
        self.assertEqual('abc', output.getvalue())
162
    
163
    def test_simple_pipes_client_disconnect_does_nothing(self):
164
        # calling disconnect does nothing.
165
        input = StringIO()
166
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
167
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
168
        # send some bytes to ensure disconnecting after activity still does not
169
        # close.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
170
        client_medium._accept_bytes('abc')
171
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
172
        self.assertFalse(input.closed)
173
        self.assertFalse(output.closed)
174
175
    def test_simple_pipes_client_accept_bytes_after_disconnect(self):
176
        # calling disconnect on the client does not alter the pipe that
177
        # accept_bytes writes to.
178
        input = StringIO()
179
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
180
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
181
        client_medium._accept_bytes('abc')
182
        client_medium.disconnect()
183
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
184
        self.assertFalse(input.closed)
185
        self.assertFalse(output.closed)
186
        self.assertEqual('abcabc', output.getvalue())
187
    
188
    def test_simple_pipes_client_ignores_disconnect_when_not_connected(self):
189
        # Doing a disconnect on a new (and thus unconnected) SimplePipes medium
190
        # does nothing.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
191
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
192
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
193
194
    def test_simple_pipes_client_can_always_read(self):
195
        # SmartSimplePipesClientMedium is never disconnected, so read_bytes
196
        # always tries to read from the underlying pipe.
197
        input = StringIO('abcdef')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
198
        client_medium = medium.SmartSimplePipesClientMedium(input, None)
199
        self.assertEqual('abc', client_medium.read_bytes(3))
200
        client_medium.disconnect()
201
        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.
202
        
203
    def test_simple_pipes_client_supports__flush(self):
204
        # invoking _flush on a SimplePipesClient should flush the output 
205
        # pipe. We test this by creating an output pipe that records
206
        # flush calls made to it.
207
        from StringIO import StringIO # get regular StringIO
208
        input = StringIO()
209
        output = StringIO()
210
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
211
        def logging_flush(): flush_calls.append('flush')
212
        output.flush = logging_flush
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
213
        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.
214
        # this call is here to ensure we only flush once, not on every
215
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
216
        client_medium._accept_bytes('abc')
217
        client_medium._flush()
218
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
219
        self.assertEqual(['flush'], flush_calls)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
220
221
    def test_construct_smart_ssh_client_medium(self):
222
        # the SSH client medium takes:
223
        # host, port, username, password, vendor
224
        # Constructing one should just save these and do nothing.
225
        # we test this by creating a empty bound socket and constructing
226
        # a medium.
227
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
228
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
229
        unopened_port = sock.getsockname()[1]
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
230
        # having vendor be invalid means that if it tries to connect via the
231
        # vendor it will blow up.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
232
        client_medium = medium.SmartSSHClientMedium('127.0.0.1', unopened_port,
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
233
            username=None, password=None, vendor="not a vendor",
234
            bzr_remote_path='bzr')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
235
        sock.close()
236
237
    def test_ssh_client_connects_on_first_use(self):
238
        # The only thing that initiates a connection from the medium is giving
239
        # it bytes.
240
        output = StringIO()
241
        vendor = StringIOSSHVendor(StringIO(), output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
242
        client_medium = medium.SmartSSHClientMedium(
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
243
            '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.
244
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
245
        self.assertEqual('abc', output.getvalue())
246
        self.assertEqual([('connect_ssh', 'a username', 'a password',
247
            'a hostname', 'a port',
248
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes'])],
249
            vendor.calls)
250
    
251
    def test_ssh_client_changes_command_when_BZR_REMOTE_PATH_is_set(self):
252
        # The only thing that initiates a connection from the medium is giving
253
        # it bytes.
254
        output = StringIO()
255
        vendor = StringIOSSHVendor(StringIO(), output)
256
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
257
        def cleanup_environ():
258
            osutils.set_or_unset_env('BZR_REMOTE_PATH', orig_bzr_remote_path)
259
        self.addCleanup(cleanup_environ)
260
        os.environ['BZR_REMOTE_PATH'] = 'fugly'
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
261
        client_medium = self.callDeprecated(
262
            ['bzr_remote_path is required as of bzr 0.92'],
263
            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.
264
            'a password', vendor)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
265
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
266
        self.assertEqual('abc', output.getvalue())
267
        self.assertEqual([('connect_ssh', 'a username', 'a password',
268
            'a hostname', 'a port',
269
            ['fugly', 'serve', '--inet', '--directory=/', '--allow-writes'])],
270
            vendor.calls)
271
    
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
272
    def test_ssh_client_changes_command_when_bzr_remote_path_passed(self):
273
        # The only thing that initiates a connection from the medium is giving
274
        # it bytes.
275
        output = StringIO()
276
        vendor = StringIOSSHVendor(StringIO(), output)
277
        client_medium = medium.SmartSSHClientMedium('a hostname', 'a port',
278
            'a username', 'a password', vendor, bzr_remote_path='fugly')
279
        client_medium._accept_bytes('abc')
280
        self.assertEqual('abc', output.getvalue())
281
        self.assertEqual([('connect_ssh', 'a username', 'a password',
282
            'a hostname', 'a port',
283
            ['fugly', 'serve', '--inet', '--directory=/', '--allow-writes'])],
284
            vendor.calls)
285
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
286
    def test_ssh_client_disconnect_does_so(self):
287
        # calling disconnect should disconnect both the read_from and write_to
288
        # file-like object it from the ssh connection.
289
        input = StringIO()
290
        output = StringIO()
291
        vendor = StringIOSSHVendor(input, output)
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
292
        client_medium = medium.SmartSSHClientMedium('a hostname',
293
                                                    vendor=vendor,
294
                                                    bzr_remote_path='bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
295
        client_medium._accept_bytes('abc')
296
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
297
        self.assertTrue(input.closed)
298
        self.assertTrue(output.closed)
299
        self.assertEqual([
300
            ('connect_ssh', None, None, 'a hostname', None,
301
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
302
            ('close', ),
303
            ],
304
            vendor.calls)
305
306
    def test_ssh_client_disconnect_allows_reconnection(self):
307
        # calling disconnect on the client terminates the connection, but should
308
        # not prevent additional connections occuring.
309
        # we test this by initiating a second connection after doing a
310
        # disconnect.
311
        input = StringIO()
312
        output = StringIO()
313
        vendor = StringIOSSHVendor(input, output)
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
314
        client_medium = medium.SmartSSHClientMedium('a hostname',
315
            vendor=vendor, bzr_remote_path='bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
316
        client_medium._accept_bytes('abc')
317
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
318
        # the disconnect has closed output, so we need a new output for the
319
        # new connection to write to.
320
        input2 = StringIO()
321
        output2 = StringIO()
322
        vendor.read_from = input2
323
        vendor.write_to = output2
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
324
        client_medium._accept_bytes('abc')
325
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
326
        self.assertTrue(input.closed)
327
        self.assertTrue(output.closed)
328
        self.assertTrue(input2.closed)
329
        self.assertTrue(output2.closed)
330
        self.assertEqual([
331
            ('connect_ssh', None, None, 'a hostname', None,
332
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
333
            ('close', ),
334
            ('connect_ssh', None, None, 'a hostname', None,
335
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
336
            ('close', ),
337
            ],
338
            vendor.calls)
339
    
340
    def test_ssh_client_ignores_disconnect_when_not_connected(self):
341
        # 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.
342
        # does not fail.  It's ok to disconnect an unconnected medium.
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
343
        client_medium = medium.SmartSSHClientMedium(None,
344
                                                    bzr_remote_path='bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
345
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
346
347
    def test_ssh_client_raises_on_read_when_not_connected(self):
348
        # 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.
349
        # MediumNotConnected.
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
350
        client_medium = medium.SmartSSHClientMedium(None,
351
                                                    bzr_remote_path='bzr')
352
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes,
353
                          0)
354
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes,
355
                          1)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
356
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
357
    def test_ssh_client_supports__flush(self):
358
        # invoking _flush on a SSHClientMedium should flush the output 
359
        # pipe. We test this by creating an output pipe that records
360
        # flush calls made to it.
361
        from StringIO import StringIO # get regular StringIO
362
        input = StringIO()
363
        output = StringIO()
364
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
365
        def logging_flush(): flush_calls.append('flush')
366
        output.flush = logging_flush
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
367
        vendor = StringIOSSHVendor(input, output)
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
368
        client_medium = medium.SmartSSHClientMedium('a hostname',
369
                                                    vendor=vendor,
370
                                                    bzr_remote_path='bzr')
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
371
        # this call is here to ensure we only flush once, not on every
372
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
373
        client_medium._accept_bytes('abc')
374
        client_medium._flush()
375
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
376
        self.assertEqual(['flush'], flush_calls)
377
        
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
378
    def test_construct_smart_tcp_client_medium(self):
379
        # the TCP client medium takes a host and a port.  Constructing it won't
380
        # connect to anything.
381
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
382
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
383
        unopened_port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
384
        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.
385
        sock.close()
386
387
    def test_tcp_client_connects_on_first_use(self):
388
        # The only thing that initiates a connection from the medium is giving
389
        # it bytes.
390
        sock, medium = self.make_loopsocket_and_medium()
391
        bytes = []
392
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
393
        medium.accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
394
        t.join()
395
        sock.close()
396
        self.assertEqual(['abc'], bytes)
397
    
398
    def test_tcp_client_disconnect_does_so(self):
399
        # calling disconnect on the client terminates the connection.
400
        # 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.
401
        # 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.
402
        sock, medium = self.make_loopsocket_and_medium()
403
        bytes = []
404
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
405
        medium.accept_bytes('ab')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
406
        medium.disconnect()
407
        t.join()
408
        sock.close()
409
        self.assertEqual(['ab'], bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
410
        # 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.
411
        # really did disconnect.
412
        medium.disconnect()
413
    
414
    def test_tcp_client_ignores_disconnect_when_not_connected(self):
415
        # 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.
416
        # 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.
417
        client_medium = medium.SmartTCPClientMedium(None, None)
418
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
419
420
    def test_tcp_client_raises_on_read_when_not_connected(self):
421
        # 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.
422
        # MediumNotConnected.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
423
        client_medium = medium.SmartTCPClientMedium(None, None)
424
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
425
        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.
426
427
    def test_tcp_client_supports__flush(self):
428
        # invoking _flush on a TCPClientMedium should do something useful.
429
        # RBC 20060922 not sure how to test/tell in this case.
430
        sock, medium = self.make_loopsocket_and_medium()
431
        bytes = []
432
        t = self.receive_bytes_on_server(sock, bytes)
433
        # try with nothing buffered
434
        medium._flush()
435
        medium._accept_bytes('ab')
436
        # and with something sent.
437
        medium._flush()
438
        medium.disconnect()
439
        t.join()
440
        sock.close()
441
        self.assertEqual(['ab'], bytes)
442
        # now disconnect again : this should not do anything, if disconnection
443
        # really did disconnect.
444
        medium.disconnect()
445
446
447
class TestSmartClientStreamMediumRequest(tests.TestCase):
448
    """Tests the for SmartClientStreamMediumRequest.
449
    
450
    SmartClientStreamMediumRequest is a helper for the three stream based 
451
    mediums: TCP, SSH, SimplePipes, so we only test it once, and then test that
452
    those three mediums implement the interface it expects.
453
    """
454
455
    def test_accept_bytes_after_finished_writing_errors(self):
456
        # calling accept_bytes after calling finished_writing raises 
457
        # WritingCompleted to prevent bad assumptions on stream environments
458
        # breaking the needs of message-based environments.
459
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
460
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
461
        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.
462
        request.finished_writing()
463
        self.assertRaises(errors.WritingCompleted, request.accept_bytes, None)
464
465
    def test_accept_bytes(self):
466
        # accept bytes should invoke _accept_bytes on the stream medium.
467
        # we test this by using the SimplePipes medium - the most trivial one
468
        # and checking that the pipes get the data.
469
        input = StringIO()
470
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
471
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
472
        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.
473
        request.accept_bytes('123')
474
        request.finished_writing()
475
        request.finished_reading()
476
        self.assertEqual('', input.getvalue())
477
        self.assertEqual('123', output.getvalue())
478
479
    def test_construct_sets_stream_request(self):
480
        # constructing a SmartClientStreamMediumRequest on a StreamMedium sets
481
        # the current request to the new SmartClientStreamMediumRequest
482
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
483
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
484
        request = medium.SmartClientStreamMediumRequest(client_medium)
485
        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.
486
487
    def test_construct_while_another_request_active_throws(self):
488
        # constructing a SmartClientStreamMediumRequest on a StreamMedium with
489
        # a non-None _current_request raises TooManyConcurrentRequests.
490
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
491
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
492
        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.
493
        self.assertRaises(errors.TooManyConcurrentRequests,
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
494
            medium.SmartClientStreamMediumRequest, client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
495
496
    def test_finished_read_clears_current_request(self):
497
        # calling finished_reading clears the current request from the requests
498
        # medium
499
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
500
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
501
        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.
502
        request.finished_writing()
503
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
504
        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.
505
506
    def test_finished_read_before_finished_write_errors(self):
507
        # calling finished_reading before calling finished_writing triggers a
508
        # WritingNotComplete error.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
509
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
510
        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.
511
        self.assertRaises(errors.WritingNotComplete, request.finished_reading)
512
        
513
    def test_read_bytes(self):
514
        # read bytes should invoke _read_bytes on the stream medium.
515
        # we test this by using the SimplePipes medium - the most trivial one
516
        # and checking that the data is supplied. Its possible that a 
517
        # faulty implementation could poke at the pipe variables them selves,
518
        # but we trust that this will be caught as it will break the integration
519
        # smoke tests.
520
        input = StringIO('321')
521
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
522
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
523
        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.
524
        request.finished_writing()
525
        self.assertEqual('321', request.read_bytes(3))
526
        request.finished_reading()
527
        self.assertEqual('', input.read())
528
        self.assertEqual('', output.getvalue())
529
530
    def test_read_bytes_before_finished_write_errors(self):
531
        # calling read_bytes before calling finished_writing triggers a
532
        # WritingNotComplete error because the Smart protocol is designed to be
533
        # compatible with strict message based protocols like HTTP where the
534
        # request cannot be submitted until the writing has completed.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
535
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
536
        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.
537
        self.assertRaises(errors.WritingNotComplete, request.read_bytes, None)
538
539
    def test_read_bytes_after_finished_reading_errors(self):
540
        # calling read_bytes after calling finished_reading raises 
541
        # ReadingCompleted to prevent bad assumptions on stream environments
542
        # breaking the needs of message-based environments.
543
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
544
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
545
        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.
546
        request.finished_writing()
547
        request.finished_reading()
548
        self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
549
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
550
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
551
class RemoteTransportTests(TestCaseWithSmartMedium):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
552
553
    def test_plausible_url(self):
554
        self.assert_(self.get_url().startswith('bzr://'))
555
556
    def test_probe_transport(self):
557
        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)
558
        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
559
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
560
    def test_get_medium_from_transport(self):
561
        """Remote transport has a medium always, which it can return."""
562
        t = self.get_transport()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
563
        client_medium = t.get_smart_medium()
564
        self.assertIsInstance(client_medium, medium.SmartClientMedium)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
565
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
566
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
567
class ErrorRaisingProtocol(object):
568
569
    def __init__(self, exception):
570
        self.exception = exception
571
572
    def next_read_size(self):
573
        raise self.exception
574
575
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
576
class SampleRequest(object):
577
    
578
    def __init__(self, expected_bytes):
579
        self.accepted_bytes = ''
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
580
        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.
581
        self.expected_bytes = expected_bytes
582
        self.excess_buffer = ''
583
584
    def accept_bytes(self, bytes):
585
        self.accepted_bytes += bytes
586
        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.
587
            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.
588
            self.excess_buffer = self.accepted_bytes[len(self.expected_bytes):]
589
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
590
    def next_read_size(self):
591
        if self._finished_reading:
592
            return 0
593
        else:
594
            return 1
595
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
596
597
class TestSmartServerStreamMedium(tests.TestCase):
598
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)
599
    def setUp(self):
600
        super(TestSmartServerStreamMedium, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
601
        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)
602
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
603
    def portable_socket_pair(self):
604
        """Return a pair of TCP sockets connected to each other.
605
        
606
        Unlike socket.socketpair, this should work on Windows.
607
        """
608
        listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
609
        listen_sock.bind(('127.0.0.1', 0))
610
        listen_sock.listen(1)
611
        client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
612
        client_sock.connect(listen_sock.getsockname())
613
        server_sock, addr = listen_sock.accept()
614
        listen_sock.close()
615
        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
616
    
617
    def test_smart_query_version(self):
618
        """Feed a canned query version to a server"""
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
619
        # 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
620
        to_server = StringIO('hello\n')
621
        from_server = StringIO()
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
622
        transport = local.LocalTransport(urlutils.local_path_to_url('/'))
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
623
        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.
624
            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
625
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
626
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
627
        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.
628
        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
629
                         from_server.getvalue())
630
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
631
    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
632
        transport = memory.MemoryTransport('memory:///')
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
633
        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
634
        to_server = StringIO('get\001./testfile\n')
635
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
636
        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.
637
            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
638
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
639
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
640
        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
641
        self.assertEqual('ok\n'
642
                         '17\n'
643
                         'contents\nof\nfile\n'
644
                         'done\n',
645
                         from_server.getvalue())
646
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
647
    def test_response_to_canned_get_of_utf8(self):
648
        # wire-to-wire, using the whole stack, with a UTF-8 filename.
649
        transport = memory.MemoryTransport('memory:///')
650
        utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')
651
        transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
652
        to_server = StringIO('get\001' + utf8_filename + '\n')
653
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
654
        server = medium.SmartServerPipeStreamMedium(
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
655
            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
656
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
657
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
658
        server._serve_one_request(smart_protocol)
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
659
        self.assertEqual('ok\n'
660
                         '17\n'
661
                         'contents\nof\nfile\n'
662
                         'done\n',
663
                         from_server.getvalue())
664
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
665
    def test_pipe_like_stream_with_bulk_data(self):
666
        sample_request_bytes = 'command\n9\nbulk datadone\n'
667
        to_server = StringIO(sample_request_bytes)
668
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
669
        server = medium.SmartServerPipeStreamMedium(
670
            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.
671
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
672
        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.
673
        self.assertEqual('', from_server.getvalue())
674
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
675
        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.
676
677
    def test_socket_stream_with_bulk_data(self):
678
        sample_request_bytes = 'command\n9\nbulk datadone\n'
679
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
680
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
681
            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.
682
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
683
        client_sock.sendall(sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
684
        server._serve_one_request(sample_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
685
        server_sock.close()
686
        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.
687
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
688
        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.
689
690
    def test_pipe_like_stream_shutdown_detection(self):
691
        to_server = StringIO('')
692
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
693
        server = medium.SmartServerPipeStreamMedium(to_server, from_server, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
694
        server._serve_one_request(SampleRequest('x'))
695
        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.
696
        
697
    def test_socket_stream_shutdown_detection(self):
698
        server_sock, client_sock = self.portable_socket_pair()
699
        client_sock.close()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
700
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
701
            server_sock, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
702
        server._serve_one_request(SampleRequest('x'))
703
        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.
704
        
705
    def test_pipe_like_stream_with_two_requests(self):
706
        # If two requests are read in one go, then two calls to
707
        # _serve_one_request should still process both of them as if they had
708
        # been received seperately.
709
        sample_request_bytes = 'command\n'
710
        to_server = StringIO(sample_request_bytes * 2)
711
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
712
        server = medium.SmartServerPipeStreamMedium(
713
            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.
714
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
715
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
716
        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.
717
        self.assertEqual('', from_server.getvalue())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
718
        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.
719
        # Make a new protocol, call _serve_one_request with it to collect the
720
        # second request.
721
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
722
        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.
723
        self.assertEqual('', from_server.getvalue())
724
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
725
        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.
726
        
727
    def test_socket_stream_with_two_requests(self):
728
        # If two requests are read in one go, then two calls to
729
        # _serve_one_request should still process both of them as if they had
730
        # been received seperately.
731
        sample_request_bytes = 'command\n'
732
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
733
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
734
            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.
735
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
736
        # Put two whole requests on the wire.
737
        client_sock.sendall(sample_request_bytes * 2)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
738
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
739
        self.assertEqual(0, first_protocol.next_read_size())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
740
        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.
741
        # Make a new protocol, call _serve_one_request with it to collect the
742
        # second request.
743
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
744
        stream_still_open = server._serve_one_request(second_protocol)
745
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
746
        self.assertFalse(server.finished)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
747
        server_sock.close()
748
        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.
749
750
    def test_pipe_like_stream_error_handling(self):
751
        # Use plain python StringIO so we can monkey-patch the close method to
752
        # not discard the contents.
753
        from StringIO import StringIO
754
        to_server = StringIO('')
755
        from_server = StringIO()
756
        self.closed = False
757
        def close():
758
            self.closed = True
759
        from_server.close = close
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
760
        server = medium.SmartServerPipeStreamMedium(
761
            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.
762
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
763
        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.
764
        self.assertEqual('', from_server.getvalue())
765
        self.assertTrue(self.closed)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
766
        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.
767
        
768
    def test_socket_stream_error_handling(self):
769
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
770
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
771
            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.
772
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
773
        server._serve_one_request(fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
774
        # recv should not block, because the other end of the socket has been
775
        # closed.
776
        self.assertEqual('', client_sock.recv(1))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
777
        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.
778
        
779
    def test_pipe_like_stream_keyboard_interrupt_handling(self):
780
        to_server = StringIO('')
781
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
782
        server = medium.SmartServerPipeStreamMedium(
783
            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.
784
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
785
        self.assertRaises(
786
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
787
        self.assertEqual('', from_server.getvalue())
788
789
    def test_socket_stream_keyboard_interrupt_handling(self):
790
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
791
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
792
            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.
793
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
794
        self.assertRaises(
795
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
796
        server_sock.close()
797
        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.
798
799
    def build_protocol_pipe_like(self, bytes):
800
        to_server = StringIO(bytes)
801
        from_server = StringIO()
802
        server = medium.SmartServerPipeStreamMedium(
803
            to_server, from_server, None)
804
        return server._build_protocol()
805
806
    def build_protocol_socket(self, bytes):
807
        server_sock, client_sock = self.portable_socket_pair()
808
        server = medium.SmartServerSocketStreamMedium(
809
            server_sock, None)
810
        client_sock.sendall(bytes)
811
        client_sock.close()
812
        return server._build_protocol()
813
814
    def assertProtocolOne(self, server_protocol):
815
        # Use assertIs because assertIsInstance will wrongly pass
816
        # SmartServerRequestProtocolTwo (because it subclasses
817
        # SmartServerRequestProtocolOne).
818
        self.assertIs(
819
            type(server_protocol), protocol.SmartServerRequestProtocolOne)
820
821
    def assertProtocolTwo(self, server_protocol):
822
        self.assertIsInstance(
823
            server_protocol, protocol.SmartServerRequestProtocolTwo)
824
825
    def test_pipe_like_build_protocol_empty_bytes(self):
826
        # Any empty request (i.e. no bytes) is detected as protocol version one.
827
        server_protocol = self.build_protocol_pipe_like('')
828
        self.assertProtocolOne(server_protocol)
829
        
830
    def test_socket_like_build_protocol_empty_bytes(self):
831
        # Any empty request (i.e. no bytes) is detected as protocol version one.
832
        server_protocol = self.build_protocol_socket('')
833
        self.assertProtocolOne(server_protocol)
834
835
    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.
836
        # A request that doesn't start with "bzr request 2\n" is version one.
837
        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.
838
        self.assertProtocolOne(server_protocol)
839
840
    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.
841
        # A request that doesn't start with "bzr request 2\n" is version one.
842
        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.
843
        self.assertProtocolOne(server_protocol)
844
845
    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.
846
        # A request that starts with "bzr request 2\n" is version two.
847
        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.
848
        self.assertProtocolTwo(server_protocol)
849
850
    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.
851
        # A request that starts with "bzr request 2\n" is version two.
852
        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.
853
        self.assertProtocolTwo(server_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
854
        
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
855
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
856
class TestSmartTCPServer(tests.TestCase):
857
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
858
    def test_get_error_unexpected(self):
859
        """Error reported by server with no specific representation"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
860
        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
861
        class FlakyTransport(object):
2376.3.3 by Robert Collins
Fix all smart_transport tests.
862
            base = 'a_url'
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
863
            def external_url(self):
864
                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.
865
            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
866
                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.
867
        smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
868
        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
869
        try:
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
870
            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
871
            try:
872
                transport.get('something')
873
            except errors.TransportError, e:
874
                self.assertContainsRe(str(e), 'some random exception')
875
            else:
876
                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.
877
            transport.disconnect()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
878
        finally:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
879
            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
880
881
882
class SmartTCPTests(tests.TestCase):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
883
    """Tests for connection/end to end behaviour using the TCP server.
884
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
885
    All of these tests are run with a server running on another thread serving
886
    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`.
887
888
    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
889
    """
890
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
891
    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`.
892
        """Setup the server.
893
894
        :param readonly: Create a readonly server.
895
        """
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
896
        if not backing_transport:
897
            self.backing_transport = memory.MemoryTransport()
898
        else:
899
            self.backing_transport = backing_transport
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
900
        if readonly:
901
            self.real_backing_transport = self.backing_transport
902
            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.
903
        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
904
        self.server.start_background_thread()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
905
        self.transport = remote.RemoteTCPTransport(self.server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
906
        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
907
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
908
    def tearDownServer(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
909
        if getattr(self, 'transport', None):
910
            self.transport.disconnect()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
911
            del self.transport
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
912
        if getattr(self, 'server', None):
913
            self.server.stop_background_thread()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
914
            del self.server
915
916
917
class TestServerSocketUsage(SmartTCPTests):
918
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.
919
    def test_server_setup_teardown(self):
920
        """It should be safe to teardown the server with no requests."""
921
        self.setUpServer()
922
        server = self.server
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
923
        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.
924
        self.tearDownServer()
925
        self.assertRaises(errors.ConnectionError, transport.has, '.')
926
927
    def test_server_closes_listening_sock_on_shutdown_after_request(self):
2370.4.2 by Robert Collins
Review feedback.
928
        """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
929
        self.setUpServer()
2370.4.2 by Robert Collins
Review feedback.
930
        server = self.server
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
931
        self.transport.has('.')
932
        self.tearDownServer()
933
        # if the listening socket has closed, we should get a BADFD error
934
        # when connecting, rather than a hang.
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
935
        transport = remote.RemoteTCPTransport(server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
936
        self.assertRaises(errors.ConnectionError, transport.has, '.')
937
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
938
939
class WritableEndToEndTests(SmartTCPTests):
940
    """Client to server tests that require a writable transport."""
941
942
    def setUp(self):
943
        super(WritableEndToEndTests, self).setUp()
944
        self.setUpServer()
945
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
946
    def test_start_tcp_server(self):
947
        url = self.server.get_url()
948
        self.assertContainsRe(url, r'^bzr://127\.0\.0\.1:[0-9]{2,}/')
949
950
    def test_smart_transport_has(self):
951
        """Checking for file existence over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
952
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
953
        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
954
        self.assertTrue(self.transport.has("foo"))
955
        self.assertFalse(self.transport.has("non-foo"))
956
957
    def test_smart_transport_get(self):
958
        """Read back a file over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
959
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
960
        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
961
        fp = self.transport.get("foo")
962
        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`.
963
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
964
    def test_get_error_enoent(self):
965
        """Error reported from server getting nonexistent file."""
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
966
        # The path in a raised NoSuchFile exception should be the precise path
967
        # asked for by the client. This gives meaningful and unsurprising errors
968
        # for users.
2402.1.2 by Andrew Bennetts
Deal with review comments.
969
        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
970
        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.
971
            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
972
        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.
973
            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
974
        else:
975
            self.fail("get did not raise expected error")
976
977
    def test_simple_clone_conn(self):
978
        """Test that cloning reuses the same connection."""
979
        # we create a real connection not a loopback one, but it will use the
980
        # 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__.
981
        conn2 = self.transport.clone('.')
2485.8.54 by Vincent Ladeuil
Refactor medium uses by making a distinction betweem shared and real medium.
982
        self.assertIs(self.transport.get_smart_medium(),
983
                      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
984
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)
985
    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
986
        self.assertEquals('/foo/bar',
987
                          self.transport._remote_path('foo/bar'))
988
989
    def test_clone_changes_base(self):
990
        """Cloning transport produces one with a new base location"""
991
        conn2 = self.transport.clone('subdir')
992
        self.assertEquals(self.transport.base + 'subdir/',
993
                          conn2.base)
994
995
    def test_open_dir(self):
996
        """Test changing directory"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
997
        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
998
        transport = self.transport
999
        self.backing_transport.mkdir('toffee')
1000
        self.backing_transport.mkdir('toffee/apple')
1001
        self.assertEquals('/toffee', transport._remote_path('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
1002
        toffee_trans = transport.clone('toffee')
1003
        # Check that each transport has only the contents of its directory
1004
        # directly visible. If state was being held in the wrong object, it's
1005
        # conceivable that cloning a transport would alter the state of the
1006
        # cloned-from transport.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1007
        self.assertTrue(transport.has('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
1008
        self.assertFalse(toffee_trans.has('toffee'))
1009
        self.assertFalse(transport.has('apple'))
1010
        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
1011
1012
    def test_open_bzrdir(self):
1013
        """Open an existing bzrdir over smart transport"""
1014
        transport = self.transport
1015
        t = self.backing_transport
1016
        bzrdir.BzrDirFormat.get_default_format().initialize_on_transport(t)
1017
        result_dir = bzrdir.BzrDir.open_containing_from_transport(transport)
1018
1019
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1020
class ReadOnlyEndToEndTests(SmartTCPTests):
1021
    """Tests from the client to the server using a readonly backing transport."""
1022
1023
    def test_mkdir_error_readonly(self):
1024
        """TransportNotPossible should be preserved from the backing transport."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
1025
        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`.
1026
        self.setUpServer(readonly=True)
1027
        self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
1028
            'foo')
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1029
1030
1031
class TestServerHooks(SmartTCPTests):
1032
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
1033
    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
1034
        """Record a server_started|stopped hook firing."""
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
1035
        self.hook_calls.append((backing_urls, public_url))
1036
1037
    def test_server_started_hook_memory(self):
1038
        """The server_started hook fires when the server is started."""
1039
        self.hook_calls = []
1040
        server.SmartTCPServer.hooks.install_hook('server_started',
1041
            self.capture_server_call)
1042
        self.setUpServer()
1043
        # at this point, the server will be starting a thread up.
1044
        # there is no indicator at the moment, so bodge it by doing a request.
1045
        self.transport.has('.')
1046
        # The default test server uses MemoryTransport and that has no external
1047
        # url:
1048
        self.assertEqual([([self.backing_transport.base], self.transport.base)],
1049
            self.hook_calls)
1050
1051
    def test_server_started_hook_file(self):
1052
        """The server_started hook fires when the server is started."""
1053
        self.hook_calls = []
1054
        server.SmartTCPServer.hooks.install_hook('server_started',
1055
            self.capture_server_call)
1056
        self.setUpServer(backing_transport=get_transport("."))
1057
        # at this point, the server will be starting a thread up.
1058
        # there is no indicator at the moment, so bodge it by doing a request.
1059
        self.transport.has('.')
1060
        # The default test server uses MemoryTransport and that has no external
1061
        # url:
1062
        self.assertEqual([([
1063
            self.backing_transport.base, self.backing_transport.external_url()],
1064
             self.transport.base)],
1065
            self.hook_calls)
1066
1067
    def test_server_stopped_hook_simple_memory(self):
1068
        """The server_stopped hook fires when the server is stopped."""
1069
        self.hook_calls = []
1070
        server.SmartTCPServer.hooks.install_hook('server_stopped',
1071
            self.capture_server_call)
1072
        self.setUpServer()
1073
        result = [([self.backing_transport.base], self.transport.base)]
1074
        # check the stopping message isn't emitted up front.
1075
        self.assertEqual([], self.hook_calls)
1076
        # nor after a single message
1077
        self.transport.has('.')
1078
        self.assertEqual([], self.hook_calls)
1079
        # clean up the server
1080
        self.tearDownServer()
1081
        # now it should have fired.
1082
        self.assertEqual(result, self.hook_calls)
1083
1084
    def test_server_stopped_hook_simple_file(self):
1085
        """The server_stopped hook fires when the server is stopped."""
1086
        self.hook_calls = []
1087
        server.SmartTCPServer.hooks.install_hook('server_stopped',
1088
            self.capture_server_call)
1089
        self.setUpServer(backing_transport=get_transport("."))
1090
        result = [(
1091
            [self.backing_transport.base, self.backing_transport.external_url()]
1092
            , 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.
1093
        # check the stopping message isn't emitted up front.
1094
        self.assertEqual([], self.hook_calls)
1095
        # nor after a single message
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1096
        self.transport.has('.')
1097
        self.assertEqual([], self.hook_calls)
1098
        # clean up the server
1099
        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.
1100
        # now it should have fired.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1101
        self.assertEqual(result, self.hook_calls)
1102
1103
# TODO: test that when the server suffers an exception that it calls the
1104
# server-stopped hook.
1105
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1106
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1107
class SmartServerCommandTests(tests.TestCaseWithTransport):
1108
    """Tests that call directly into the command objects, bypassing the network
1109
    and the request dispatching.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1110
1111
    Note: these tests are rudimentary versions of the command object tests in
1112
    test_remote.py.
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1113
    """
1114
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1115
    def test_hello(self):
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1116
        cmd = 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)
1117
        response = cmd.execute()
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1118
        self.assertEqual(('ok', '2'), response.args)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1119
        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
1120
        
1121
    def test_get_bundle(self):
1122
        from bzrlib.bundle import serializer
1123
        wt = self.make_branch_and_tree('.')
1910.19.13 by Andrew Bennetts
Address various review comments.
1124
        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
1125
        wt.add('hello')
1910.19.13 by Andrew Bennetts
Address various review comments.
1126
        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
1127
        
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1128
        cmd = 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)
1129
        response = cmd.execute('.', rev_id)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1130
        bundle = serializer.read_bundle(StringIO(response.body))
1131
        self.assertEqual((), response.args)
1132
1133
1134
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
1135
    """Test that call directly into the handler logic, bypassing the network."""
1136
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)
1137
    def setUp(self):
1138
        super(SmartServerRequestHandlerTests, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
1139
        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)
1140
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1141
    def build_handler(self, transport):
1142
        """Returns a handler for the commands in protocol version one."""
2018.5.160 by Andrew Bennetts
Merge from bzr.dev.
1143
        return request.SmartServerRequestHandler(transport,
1144
                                                 request.request_handlers)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1145
1146
    def test_construct_request_handler(self):
1147
        """Constructing a request handler should be easy and set defaults."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1148
        handler = request.SmartServerRequestHandler(None, None)
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.
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)
1160
        handler = vfs.HasRequest(None)
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)
1225
        self.assertEqual(('ShortReadvError', 'a-file', '100', '1', '0'),
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)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1316
        self.smart_server_request = request.SmartServerRequestHandler(
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
1317
            None, request.request_handlers)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
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.
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
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)
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1347
                return 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(
1512
            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,
1520
            request.SmartServerResponse(('x',)))
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',
1707
                  request.FailedSmartServerResponse(
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(
1799
            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(
1807
            request.SuccessfulSmartServerResponse(('x',), body_stream=[]))
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,
1815
            request.SmartServerResponse(('x',)))
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(
1823
            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(
1833
            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',
2748.4.6 by Andrew Bennetts
Use chunks for stream errors, rather than the response tuple format.
1994
            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())
2255
        expected_failure = 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):
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2271
        response = request.SuccessfulSmartServerResponse(('foo', 'bar'))
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):
2276
        response = request.SuccessfulSmartServerResponse(
2277
            ('foo', 'bar'), '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']
2283
        response = request.SuccessfulSmartServerResponse(
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,
2292
            request.SuccessfulSmartServerResponse, (), 'body', ['stream'])
2293
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
2294
    def test_is_successful(self):
2295
        """is_successful should return True for SuccessfulSmartServerResponse."""
2296
        response = request.SuccessfulSmartServerResponse(('error',))
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):
2303
        response = request.FailedSmartServerResponse(('foo', 'bar'))
2304
        self.assertEqual(('foo', 'bar'), response.args)
2305
        self.assertEqual(None, response.body)
2306
        response = request.FailedSmartServerResponse(('foo', 'bar'), 'bytes')
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."""
2312
        response = request.FailedSmartServerResponse(('error',))
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.