~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart_transport.py

Upgraded to the latest bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
30
30
        tests,
31
31
        urlutils,
32
32
        )
 
33
from bzrlib.smart import (
 
34
        client,
 
35
        medium,
 
36
        protocol,
 
37
        request,
 
38
        server,
 
39
        vfs,
 
40
)
33
41
from bzrlib.tests.HTTPTestUtil import (
34
42
        HTTPServerWithSmarts,
35
43
        SmartRequestHandler,
36
44
        )
 
45
from bzrlib.tests.test_smart import TestCaseWithSmartMedium
37
46
from bzrlib.transport import (
38
47
        get_transport,
39
48
        local,
40
49
        memory,
41
 
        smart,
 
50
        remote,
42
51
        )
43
52
from bzrlib.transport.http import SmartClientHTTPMediumRequest
44
53
 
85
94
        sock.bind(('127.0.0.1', 0))
86
95
        sock.listen(1)
87
96
        port = sock.getsockname()[1]
88
 
        medium = smart.SmartTCPClientMedium('127.0.0.1', port)
89
 
        return sock, medium
 
97
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', port)
 
98
        return sock, client_medium
90
99
 
91
100
    def receive_bytes_on_server(self, sock, bytes):
92
101
        """Accept a connection on sock and read 3 bytes.
108
117
        # this just ensures that the constructor stays parameter-free which
109
118
        # is important for reuse : some subclasses will dynamically connect,
110
119
        # others are always on, etc.
111
 
        medium = smart.SmartClientStreamMedium()
 
120
        client_medium = medium.SmartClientStreamMedium()
112
121
 
113
122
    def test_construct_smart_client_medium(self):
114
123
        # the base client medium takes no parameters
115
 
        medium = smart.SmartClientMedium()
 
124
        client_medium = medium.SmartClientMedium()
116
125
    
117
126
    def test_construct_smart_simple_pipes_client_medium(self):
118
127
        # the SimplePipes client medium takes two pipes:
119
128
        # readable pipe, writeable pipe.
120
129
        # Constructing one should just save these and do nothing.
121
130
        # We test this by passing in None.
122
 
        medium = smart.SmartSimplePipesClientMedium(None, None)
 
131
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
123
132
        
124
133
    def test_simple_pipes_client_request_type(self):
125
134
        # SimplePipesClient should use SmartClientStreamMediumRequest's.
126
 
        medium = smart.SmartSimplePipesClientMedium(None, None)
127
 
        request = medium.get_request()
128
 
        self.assertIsInstance(request, smart.SmartClientStreamMediumRequest)
 
135
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
 
136
        request = client_medium.get_request()
 
137
        self.assertIsInstance(request, medium.SmartClientStreamMediumRequest)
129
138
 
130
139
    def test_simple_pipes_client_get_concurrent_requests(self):
131
140
        # the simple_pipes client does not support pipelined requests:
135
144
        # classes - as the sibling classes share this logic, they do not have
136
145
        # explicit tests for this.
137
146
        output = StringIO()
138
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
139
 
        request = medium.get_request()
 
147
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
148
        request = client_medium.get_request()
140
149
        request.finished_writing()
141
150
        request.finished_reading()
142
 
        request2 = medium.get_request()
 
151
        request2 = client_medium.get_request()
143
152
        request2.finished_writing()
144
153
        request2.finished_reading()
145
154
 
146
155
    def test_simple_pipes_client__accept_bytes_writes_to_writable(self):
147
156
        # accept_bytes writes to the writeable pipe.
148
157
        output = StringIO()
149
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
150
 
        medium._accept_bytes('abc')
 
158
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
159
        client_medium._accept_bytes('abc')
151
160
        self.assertEqual('abc', output.getvalue())
152
161
    
153
162
    def test_simple_pipes_client_disconnect_does_nothing(self):
154
163
        # calling disconnect does nothing.
155
164
        input = StringIO()
156
165
        output = StringIO()
157
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
 
166
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
158
167
        # send some bytes to ensure disconnecting after activity still does not
159
168
        # close.
160
 
        medium._accept_bytes('abc')
161
 
        medium.disconnect()
 
169
        client_medium._accept_bytes('abc')
 
170
        client_medium.disconnect()
162
171
        self.assertFalse(input.closed)
163
172
        self.assertFalse(output.closed)
164
173
 
167
176
        # accept_bytes writes to.
168
177
        input = StringIO()
169
178
        output = StringIO()
170
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
171
 
        medium._accept_bytes('abc')
172
 
        medium.disconnect()
173
 
        medium._accept_bytes('abc')
 
179
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
180
        client_medium._accept_bytes('abc')
 
181
        client_medium.disconnect()
 
182
        client_medium._accept_bytes('abc')
174
183
        self.assertFalse(input.closed)
175
184
        self.assertFalse(output.closed)
176
185
        self.assertEqual('abcabc', output.getvalue())
178
187
    def test_simple_pipes_client_ignores_disconnect_when_not_connected(self):
179
188
        # Doing a disconnect on a new (and thus unconnected) SimplePipes medium
180
189
        # does nothing.
181
 
        medium = smart.SmartSimplePipesClientMedium(None, None)
182
 
        medium.disconnect()
 
190
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
 
191
        client_medium.disconnect()
183
192
 
184
193
    def test_simple_pipes_client_can_always_read(self):
185
194
        # SmartSimplePipesClientMedium is never disconnected, so read_bytes
186
195
        # always tries to read from the underlying pipe.
187
196
        input = StringIO('abcdef')
188
 
        medium = smart.SmartSimplePipesClientMedium(input, None)
189
 
        self.assertEqual('abc', medium.read_bytes(3))
190
 
        medium.disconnect()
191
 
        self.assertEqual('def', medium.read_bytes(3))
 
197
        client_medium = medium.SmartSimplePipesClientMedium(input, None)
 
198
        self.assertEqual('abc', client_medium.read_bytes(3))
 
199
        client_medium.disconnect()
 
200
        self.assertEqual('def', client_medium.read_bytes(3))
192
201
        
193
202
    def test_simple_pipes_client_supports__flush(self):
194
203
        # invoking _flush on a SimplePipesClient should flush the output 
200
209
        flush_calls = []
201
210
        def logging_flush(): flush_calls.append('flush')
202
211
        output.flush = logging_flush
203
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
 
212
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
204
213
        # this call is here to ensure we only flush once, not on every
205
214
        # _accept_bytes call.
206
 
        medium._accept_bytes('abc')
207
 
        medium._flush()
208
 
        medium.disconnect()
 
215
        client_medium._accept_bytes('abc')
 
216
        client_medium._flush()
 
217
        client_medium.disconnect()
209
218
        self.assertEqual(['flush'], flush_calls)
210
219
 
211
220
    def test_construct_smart_ssh_client_medium(self):
219
228
        unopened_port = sock.getsockname()[1]
220
229
        # having vendor be invalid means that if it tries to connect via the
221
230
        # vendor it will blow up.
222
 
        medium = smart.SmartSSHClientMedium('127.0.0.1', unopened_port,
 
231
        client_medium = medium.SmartSSHClientMedium('127.0.0.1', unopened_port,
223
232
            username=None, password=None, vendor="not a vendor")
224
233
        sock.close()
225
234
 
228
237
        # it bytes.
229
238
        output = StringIO()
230
239
        vendor = StringIOSSHVendor(StringIO(), output)
231
 
        medium = smart.SmartSSHClientMedium('a hostname', 'a port', 'a username',
232
 
            'a password', vendor)
233
 
        medium._accept_bytes('abc')
 
240
        client_medium = medium.SmartSSHClientMedium(
 
241
            'a hostname', 'a port', 'a username', 'a password', vendor)
 
242
        client_medium._accept_bytes('abc')
234
243
        self.assertEqual('abc', output.getvalue())
235
244
        self.assertEqual([('connect_ssh', 'a username', 'a password',
236
245
            'a hostname', 'a port',
247
256
            osutils.set_or_unset_env('BZR_REMOTE_PATH', orig_bzr_remote_path)
248
257
        self.addCleanup(cleanup_environ)
249
258
        os.environ['BZR_REMOTE_PATH'] = 'fugly'
250
 
        medium = smart.SmartSSHClientMedium('a hostname', 'a port', 'a username',
 
259
        client_medium = medium.SmartSSHClientMedium('a hostname', 'a port', 'a username',
251
260
            'a password', vendor)
252
 
        medium._accept_bytes('abc')
 
261
        client_medium._accept_bytes('abc')
253
262
        self.assertEqual('abc', output.getvalue())
254
263
        self.assertEqual([('connect_ssh', 'a username', 'a password',
255
264
            'a hostname', 'a port',
262
271
        input = StringIO()
263
272
        output = StringIO()
264
273
        vendor = StringIOSSHVendor(input, output)
265
 
        medium = smart.SmartSSHClientMedium('a hostname', vendor=vendor)
266
 
        medium._accept_bytes('abc')
267
 
        medium.disconnect()
 
274
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
 
275
        client_medium._accept_bytes('abc')
 
276
        client_medium.disconnect()
268
277
        self.assertTrue(input.closed)
269
278
        self.assertTrue(output.closed)
270
279
        self.assertEqual([
282
291
        input = StringIO()
283
292
        output = StringIO()
284
293
        vendor = StringIOSSHVendor(input, output)
285
 
        medium = smart.SmartSSHClientMedium('a hostname', vendor=vendor)
286
 
        medium._accept_bytes('abc')
287
 
        medium.disconnect()
 
294
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
 
295
        client_medium._accept_bytes('abc')
 
296
        client_medium.disconnect()
288
297
        # the disconnect has closed output, so we need a new output for the
289
298
        # new connection to write to.
290
299
        input2 = StringIO()
291
300
        output2 = StringIO()
292
301
        vendor.read_from = input2
293
302
        vendor.write_to = output2
294
 
        medium._accept_bytes('abc')
295
 
        medium.disconnect()
 
303
        client_medium._accept_bytes('abc')
 
304
        client_medium.disconnect()
296
305
        self.assertTrue(input.closed)
297
306
        self.assertTrue(output.closed)
298
307
        self.assertTrue(input2.closed)
310
319
    def test_ssh_client_ignores_disconnect_when_not_connected(self):
311
320
        # Doing a disconnect on a new (and thus unconnected) SSH medium
312
321
        # does not fail.  It's ok to disconnect an unconnected medium.
313
 
        medium = smart.SmartSSHClientMedium(None)
314
 
        medium.disconnect()
 
322
        client_medium = medium.SmartSSHClientMedium(None)
 
323
        client_medium.disconnect()
315
324
 
316
325
    def test_ssh_client_raises_on_read_when_not_connected(self):
317
326
        # Doing a read on a new (and thus unconnected) SSH medium raises
318
327
        # MediumNotConnected.
319
 
        medium = smart.SmartSSHClientMedium(None)
320
 
        self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 0)
321
 
        self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 1)
 
328
        client_medium = medium.SmartSSHClientMedium(None)
 
329
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
 
330
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
322
331
 
323
332
    def test_ssh_client_supports__flush(self):
324
333
        # invoking _flush on a SSHClientMedium should flush the output 
331
340
        def logging_flush(): flush_calls.append('flush')
332
341
        output.flush = logging_flush
333
342
        vendor = StringIOSSHVendor(input, output)
334
 
        medium = smart.SmartSSHClientMedium('a hostname', vendor=vendor)
 
343
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
335
344
        # this call is here to ensure we only flush once, not on every
336
345
        # _accept_bytes call.
337
 
        medium._accept_bytes('abc')
338
 
        medium._flush()
339
 
        medium.disconnect()
 
346
        client_medium._accept_bytes('abc')
 
347
        client_medium._flush()
 
348
        client_medium.disconnect()
340
349
        self.assertEqual(['flush'], flush_calls)
341
350
        
342
351
    def test_construct_smart_tcp_client_medium(self):
345
354
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
346
355
        sock.bind(('127.0.0.1', 0))
347
356
        unopened_port = sock.getsockname()[1]
348
 
        medium = smart.SmartTCPClientMedium('127.0.0.1', unopened_port)
 
357
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', unopened_port)
349
358
        sock.close()
350
359
 
351
360
    def test_tcp_client_connects_on_first_use(self):
378
387
    def test_tcp_client_ignores_disconnect_when_not_connected(self):
379
388
        # Doing a disconnect on a new (and thus unconnected) TCP medium
380
389
        # does not fail.  It's ok to disconnect an unconnected medium.
381
 
        medium = smart.SmartTCPClientMedium(None, None)
382
 
        medium.disconnect()
 
390
        client_medium = medium.SmartTCPClientMedium(None, None)
 
391
        client_medium.disconnect()
383
392
 
384
393
    def test_tcp_client_raises_on_read_when_not_connected(self):
385
394
        # Doing a read on a new (and thus unconnected) TCP medium raises
386
395
        # MediumNotConnected.
387
 
        medium = smart.SmartTCPClientMedium(None, None)
388
 
        self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 0)
389
 
        self.assertRaises(errors.MediumNotConnected, medium.read_bytes, 1)
 
396
        client_medium = medium.SmartTCPClientMedium(None, None)
 
397
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
 
398
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
390
399
 
391
400
    def test_tcp_client_supports__flush(self):
392
401
        # invoking _flush on a TCPClientMedium should do something useful.
421
430
        # WritingCompleted to prevent bad assumptions on stream environments
422
431
        # breaking the needs of message-based environments.
423
432
        output = StringIO()
424
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
425
 
        request = smart.SmartClientStreamMediumRequest(medium)
 
433
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
434
        request = medium.SmartClientStreamMediumRequest(client_medium)
426
435
        request.finished_writing()
427
436
        self.assertRaises(errors.WritingCompleted, request.accept_bytes, None)
428
437
 
432
441
        # and checking that the pipes get the data.
433
442
        input = StringIO()
434
443
        output = StringIO()
435
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
436
 
        request = smart.SmartClientStreamMediumRequest(medium)
 
444
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
445
        request = medium.SmartClientStreamMediumRequest(client_medium)
437
446
        request.accept_bytes('123')
438
447
        request.finished_writing()
439
448
        request.finished_reading()
444
453
        # constructing a SmartClientStreamMediumRequest on a StreamMedium sets
445
454
        # the current request to the new SmartClientStreamMediumRequest
446
455
        output = StringIO()
447
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
448
 
        request = smart.SmartClientStreamMediumRequest(medium)
449
 
        self.assertIs(medium._current_request, request)
 
456
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
457
        request = medium.SmartClientStreamMediumRequest(client_medium)
 
458
        self.assertIs(client_medium._current_request, request)
450
459
 
451
460
    def test_construct_while_another_request_active_throws(self):
452
461
        # constructing a SmartClientStreamMediumRequest on a StreamMedium with
453
462
        # a non-None _current_request raises TooManyConcurrentRequests.
454
463
        output = StringIO()
455
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
456
 
        medium._current_request = "a"
 
464
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
465
        client_medium._current_request = "a"
457
466
        self.assertRaises(errors.TooManyConcurrentRequests,
458
 
            smart.SmartClientStreamMediumRequest, medium)
 
467
            medium.SmartClientStreamMediumRequest, client_medium)
459
468
 
460
469
    def test_finished_read_clears_current_request(self):
461
470
        # calling finished_reading clears the current request from the requests
462
471
        # medium
463
472
        output = StringIO()
464
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
465
 
        request = smart.SmartClientStreamMediumRequest(medium)
 
473
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
474
        request = medium.SmartClientStreamMediumRequest(client_medium)
466
475
        request.finished_writing()
467
476
        request.finished_reading()
468
 
        self.assertEqual(None, medium._current_request)
 
477
        self.assertEqual(None, client_medium._current_request)
469
478
 
470
479
    def test_finished_read_before_finished_write_errors(self):
471
480
        # calling finished_reading before calling finished_writing triggers a
472
481
        # WritingNotComplete error.
473
 
        medium = smart.SmartSimplePipesClientMedium(None, None)
474
 
        request = smart.SmartClientStreamMediumRequest(medium)
 
482
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
 
483
        request = medium.SmartClientStreamMediumRequest(client_medium)
475
484
        self.assertRaises(errors.WritingNotComplete, request.finished_reading)
476
485
        
477
486
    def test_read_bytes(self):
483
492
        # smoke tests.
484
493
        input = StringIO('321')
485
494
        output = StringIO()
486
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
487
 
        request = smart.SmartClientStreamMediumRequest(medium)
 
495
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
496
        request = medium.SmartClientStreamMediumRequest(client_medium)
488
497
        request.finished_writing()
489
498
        self.assertEqual('321', request.read_bytes(3))
490
499
        request.finished_reading()
496
505
        # WritingNotComplete error because the Smart protocol is designed to be
497
506
        # compatible with strict message based protocols like HTTP where the
498
507
        # request cannot be submitted until the writing has completed.
499
 
        medium = smart.SmartSimplePipesClientMedium(None, None)
500
 
        request = smart.SmartClientStreamMediumRequest(medium)
 
508
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
 
509
        request = medium.SmartClientStreamMediumRequest(client_medium)
501
510
        self.assertRaises(errors.WritingNotComplete, request.read_bytes, None)
502
511
 
503
512
    def test_read_bytes_after_finished_reading_errors(self):
505
514
        # ReadingCompleted to prevent bad assumptions on stream environments
506
515
        # breaking the needs of message-based environments.
507
516
        output = StringIO()
508
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
509
 
        request = smart.SmartClientStreamMediumRequest(medium)
 
517
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
518
        request = medium.SmartClientStreamMediumRequest(client_medium)
510
519
        request.finished_writing()
511
520
        request.finished_reading()
512
521
        self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
513
522
 
514
523
 
515
 
class RemoteTransportTests(tests.TestCaseWithTransport):
516
 
 
517
 
    def setUp(self):
518
 
        super(RemoteTransportTests, self).setUp()
519
 
        # We're allowed to set  the transport class here, so that we don't use
520
 
        # the default or a parameterized class, but rather use the
521
 
        # TestCaseWithTransport infrastructure to set up a smart server and
522
 
        # transport.
523
 
        self.transport_server = smart.SmartTCPServer_for_testing
 
524
class RemoteTransportTests(TestCaseWithSmartMedium):
524
525
 
525
526
    def test_plausible_url(self):
526
527
        self.assert_(self.get_url().startswith('bzr://'))
527
528
 
528
529
    def test_probe_transport(self):
529
530
        t = self.get_transport()
530
 
        self.assertIsInstance(t, smart.SmartTransport)
 
531
        self.assertIsInstance(t, remote.RemoteTransport)
531
532
 
532
533
    def test_get_medium_from_transport(self):
533
534
        """Remote transport has a medium always, which it can return."""
534
535
        t = self.get_transport()
535
 
        medium = t.get_smart_medium()
536
 
        self.assertIsInstance(medium, smart.SmartClientMedium)
 
536
        client_medium = t.get_smart_medium()
 
537
        self.assertIsInstance(client_medium, medium.SmartClientMedium)
537
538
 
538
539
 
539
540
class ErrorRaisingProtocol(object):
568
569
 
569
570
class TestSmartServerStreamMedium(tests.TestCase):
570
571
 
 
572
    def setUp(self):
 
573
        super(TestSmartServerStreamMedium, self).setUp()
 
574
        self._captureVar('BZR_NO_SMART_VFS', None)
 
575
 
571
576
    def portable_socket_pair(self):
572
577
        """Return a pair of TCP sockets connected to each other.
573
578
        
588
593
        to_server = StringIO('hello\n')
589
594
        from_server = StringIO()
590
595
        transport = local.LocalTransport(urlutils.local_path_to_url('/'))
591
 
        server = smart.SmartServerPipeStreamMedium(
 
596
        server = medium.SmartServerPipeStreamMedium(
592
597
            to_server, from_server, transport)
593
 
        protocol = smart.SmartServerRequestProtocolOne(transport,
 
598
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
594
599
                from_server.write)
595
 
        server._serve_one_request(protocol)
 
600
        server._serve_one_request(smart_protocol)
596
601
        self.assertEqual('ok\0011\n',
597
602
                         from_server.getvalue())
598
603
 
601
606
        transport.put_bytes('testfile', 'contents\nof\nfile\n')
602
607
        to_server = StringIO('get\001./testfile\n')
603
608
        from_server = StringIO()
604
 
        server = smart.SmartServerPipeStreamMedium(
 
609
        server = medium.SmartServerPipeStreamMedium(
605
610
            to_server, from_server, transport)
606
 
        protocol = smart.SmartServerRequestProtocolOne(transport,
 
611
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
607
612
                from_server.write)
608
 
        server._serve_one_request(protocol)
 
613
        server._serve_one_request(smart_protocol)
609
614
        self.assertEqual('ok\n'
610
615
                         '17\n'
611
616
                         'contents\nof\nfile\n'
619
624
        transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
620
625
        to_server = StringIO('get\001' + utf8_filename + '\n')
621
626
        from_server = StringIO()
622
 
        server = smart.SmartServerPipeStreamMedium(
 
627
        server = medium.SmartServerPipeStreamMedium(
623
628
            to_server, from_server, transport)
624
 
        protocol = smart.SmartServerRequestProtocolOne(transport,
 
629
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
625
630
                from_server.write)
626
 
        server._serve_one_request(protocol)
 
631
        server._serve_one_request(smart_protocol)
627
632
        self.assertEqual('ok\n'
628
633
                         '17\n'
629
634
                         'contents\nof\nfile\n'
634
639
        sample_request_bytes = 'command\n9\nbulk datadone\n'
635
640
        to_server = StringIO(sample_request_bytes)
636
641
        from_server = StringIO()
637
 
        server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
 
642
        server = medium.SmartServerPipeStreamMedium(
 
643
            to_server, from_server, None)
638
644
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
639
645
        server._serve_one_request(sample_protocol)
640
646
        self.assertEqual('', from_server.getvalue())
644
650
    def test_socket_stream_with_bulk_data(self):
645
651
        sample_request_bytes = 'command\n9\nbulk datadone\n'
646
652
        server_sock, client_sock = self.portable_socket_pair()
647
 
        server = smart.SmartServerSocketStreamMedium(
 
653
        server = medium.SmartServerSocketStreamMedium(
648
654
            server_sock, None)
649
655
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
650
656
        client_sock.sendall(sample_request_bytes)
657
663
    def test_pipe_like_stream_shutdown_detection(self):
658
664
        to_server = StringIO('')
659
665
        from_server = StringIO()
660
 
        server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
 
666
        server = medium.SmartServerPipeStreamMedium(to_server, from_server, None)
661
667
        server._serve_one_request(SampleRequest('x'))
662
668
        self.assertTrue(server.finished)
663
669
        
664
670
    def test_socket_stream_shutdown_detection(self):
665
671
        server_sock, client_sock = self.portable_socket_pair()
666
672
        client_sock.close()
667
 
        server = smart.SmartServerSocketStreamMedium(
 
673
        server = medium.SmartServerSocketStreamMedium(
668
674
            server_sock, None)
669
675
        server._serve_one_request(SampleRequest('x'))
670
676
        self.assertTrue(server.finished)
676
682
        sample_request_bytes = 'command\n'
677
683
        to_server = StringIO(sample_request_bytes * 2)
678
684
        from_server = StringIO()
679
 
        server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
 
685
        server = medium.SmartServerPipeStreamMedium(
 
686
            to_server, from_server, None)
680
687
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
681
688
        server._serve_one_request(first_protocol)
682
689
        self.assertEqual(0, first_protocol.next_read_size())
696
703
        # been received seperately.
697
704
        sample_request_bytes = 'command\n'
698
705
        server_sock, client_sock = self.portable_socket_pair()
699
 
        server = smart.SmartServerSocketStreamMedium(
 
706
        server = medium.SmartServerSocketStreamMedium(
700
707
            server_sock, None)
701
708
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
702
709
        # Put two whole requests on the wire.
723
730
        def close():
724
731
            self.closed = True
725
732
        from_server.close = close
726
 
        server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
 
733
        server = medium.SmartServerPipeStreamMedium(
 
734
            to_server, from_server, None)
727
735
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
728
736
        server._serve_one_request(fake_protocol)
729
737
        self.assertEqual('', from_server.getvalue())
731
739
        self.assertTrue(server.finished)
732
740
        
733
741
    def test_socket_stream_error_handling(self):
734
 
        # Use plain python StringIO so we can monkey-patch the close method to
735
 
        # not discard the contents.
736
 
        from StringIO import StringIO
737
742
        server_sock, client_sock = self.portable_socket_pair()
738
 
        server = smart.SmartServerSocketStreamMedium(
 
743
        server = medium.SmartServerSocketStreamMedium(
739
744
            server_sock, None)
740
745
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
741
746
        server._serve_one_request(fake_protocol)
745
750
        self.assertTrue(server.finished)
746
751
        
747
752
    def test_pipe_like_stream_keyboard_interrupt_handling(self):
748
 
        # Use plain python StringIO so we can monkey-patch the close method to
749
 
        # not discard the contents.
750
753
        to_server = StringIO('')
751
754
        from_server = StringIO()
752
 
        server = smart.SmartServerPipeStreamMedium(to_server, from_server, None)
 
755
        server = medium.SmartServerPipeStreamMedium(
 
756
            to_server, from_server, None)
753
757
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
754
758
        self.assertRaises(
755
759
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
757
761
 
758
762
    def test_socket_stream_keyboard_interrupt_handling(self):
759
763
        server_sock, client_sock = self.portable_socket_pair()
760
 
        server = smart.SmartServerSocketStreamMedium(
 
764
        server = medium.SmartServerSocketStreamMedium(
761
765
            server_sock, None)
762
766
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
763
767
        self.assertRaises(
770
774
 
771
775
    def test_get_error_unexpected(self):
772
776
        """Error reported by server with no specific representation"""
 
777
        self._captureVar('BZR_NO_SMART_VFS', None)
773
778
        class FlakyTransport(object):
 
779
            base = 'a_url'
774
780
            def get_bytes(self, path):
775
781
                raise Exception("some random exception from inside server")
776
 
        server = smart.SmartTCPServer(backing_transport=FlakyTransport())
777
 
        server.start_background_thread()
 
782
        smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
 
783
        smart_server.start_background_thread()
778
784
        try:
779
 
            transport = smart.SmartTCPTransport(server.get_url())
 
785
            transport = remote.RemoteTCPTransport(smart_server.get_url())
780
786
            try:
781
787
                transport.get('something')
782
788
            except errors.TransportError, e:
783
789
                self.assertContainsRe(str(e), 'some random exception')
784
790
            else:
785
791
                self.fail("get did not raise expected error")
 
792
            transport.disconnect()
786
793
        finally:
787
 
            server.stop_background_thread()
 
794
            smart_server.stop_background_thread()
788
795
 
789
796
 
790
797
class SmartTCPTests(tests.TestCase):
805
812
        if readonly:
806
813
            self.real_backing_transport = self.backing_transport
807
814
            self.backing_transport = get_transport("readonly+" + self.backing_transport.abspath('.'))
808
 
        self.server = smart.SmartTCPServer(self.backing_transport)
 
815
        self.server = server.SmartTCPServer(self.backing_transport)
809
816
        self.server.start_background_thread()
810
 
        self.transport = smart.SmartTCPTransport(self.server.get_url())
 
817
        self.transport = remote.RemoteTCPTransport(self.server.get_url())
 
818
        self.addCleanup(self.tearDownServer)
811
819
 
812
 
    def tearDown(self):
 
820
    def tearDownServer(self):
813
821
        if getattr(self, 'transport', None):
814
822
            self.transport.disconnect()
 
823
            del self.transport
815
824
        if getattr(self, 'server', None):
816
825
            self.server.stop_background_thread()
817
 
        super(SmartTCPTests, self).tearDown()
818
 
        
 
826
            del self.server
 
827
 
 
828
 
 
829
class TestServerSocketUsage(SmartTCPTests):
 
830
 
 
831
    def test_server_setup_teardown(self):
 
832
        """It should be safe to teardown the server with no requests."""
 
833
        self.setUpServer()
 
834
        server = self.server
 
835
        transport = remote.RemoteTCPTransport(self.server.get_url())
 
836
        self.tearDownServer()
 
837
        self.assertRaises(errors.ConnectionError, transport.has, '.')
 
838
 
 
839
    def test_server_closes_listening_sock_on_shutdown_after_request(self):
 
840
        """The server should close its listening socket when it's stopped."""
 
841
        self.setUpServer()
 
842
        server = self.server
 
843
        self.transport.has('.')
 
844
        self.tearDownServer()
 
845
        # if the listening socket has closed, we should get a BADFD error
 
846
        # when connecting, rather than a hang.
 
847
        transport = remote.RemoteTCPTransport(server.get_url())
 
848
        self.assertRaises(errors.ConnectionError, transport.has, '.')
 
849
 
819
850
 
820
851
class WritableEndToEndTests(SmartTCPTests):
821
852
    """Client to server tests that require a writable transport."""
830
861
 
831
862
    def test_smart_transport_has(self):
832
863
        """Checking for file existence over smart."""
 
864
        self._captureVar('BZR_NO_SMART_VFS', None)
833
865
        self.backing_transport.put_bytes("foo", "contents of foo\n")
834
866
        self.assertTrue(self.transport.has("foo"))
835
867
        self.assertFalse(self.transport.has("non-foo"))
836
868
 
837
869
    def test_smart_transport_get(self):
838
870
        """Read back a file over smart."""
 
871
        self._captureVar('BZR_NO_SMART_VFS', None)
839
872
        self.backing_transport.put_bytes("foo", "contents\nof\nfoo\n")
840
873
        fp = self.transport.get("foo")
841
874
        self.assertEqual('contents\nof\nfoo\n', fp.read())
845
878
        # The path in a raised NoSuchFile exception should be the precise path
846
879
        # asked for by the client. This gives meaningful and unsurprising errors
847
880
        # for users.
 
881
        self._captureVar('BZR_NO_SMART_VFS', None)
848
882
        try:
849
883
            self.transport.get('not%20a%20file')
850
884
        except errors.NoSuchFile, e:
871
905
 
872
906
    def test_open_dir(self):
873
907
        """Test changing directory"""
 
908
        self._captureVar('BZR_NO_SMART_VFS', None)
874
909
        transport = self.transport
875
910
        self.backing_transport.mkdir('toffee')
876
911
        self.backing_transport.mkdir('toffee/apple')
898
933
 
899
934
    def test_mkdir_error_readonly(self):
900
935
        """TransportNotPossible should be preserved from the backing transport."""
 
936
        self._captureVar('BZR_NO_SMART_VFS', None)
901
937
        self.setUpServer(readonly=True)
902
938
        self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
903
939
            'foo')
 
940
 
 
941
 
 
942
class TestServerHooks(SmartTCPTests):
 
943
 
 
944
    def capture_server_call(self, backing_url, public_url):
 
945
        """Record a server_started|stopped hook firing."""
 
946
        self.hook_calls.append((backing_url, public_url))
 
947
 
 
948
    def test_server_started_hook(self):
 
949
        """The server_started hook fires when the server is started."""
 
950
        self.hook_calls = []
 
951
        server.SmartTCPServer.hooks.install_hook('server_started',
 
952
            self.capture_server_call)
 
953
        self.setUpServer()
 
954
        # at this point, the server will be starting a thread up.
 
955
        # there is no indicator at the moment, so bodge it by doing a request.
 
956
        self.transport.has('.')
 
957
        self.assertEqual([(self.backing_transport.base, self.transport.base)],
 
958
            self.hook_calls)
 
959
 
 
960
    def test_server_stopped_hook_simple(self):
 
961
        """The server_stopped hook fires when the server is stopped."""
 
962
        self.hook_calls = []
 
963
        server.SmartTCPServer.hooks.install_hook('server_stopped',
 
964
            self.capture_server_call)
 
965
        self.setUpServer()
 
966
        result = [(self.backing_transport.base, self.transport.base)]
 
967
        # check the stopping message isn't emitted up front.
 
968
        self.assertEqual([], self.hook_calls)
 
969
        # nor after a single message
 
970
        self.transport.has('.')
 
971
        self.assertEqual([], self.hook_calls)
 
972
        # clean up the server
 
973
        self.tearDownServer()
 
974
        # now it should have fired.
 
975
        self.assertEqual(result, self.hook_calls)
 
976
 
 
977
# TODO: test that when the server suffers an exception that it calls the
 
978
# server-stopped hook.
 
979
 
 
980
 
 
981
class SmartServerCommandTests(tests.TestCaseWithTransport):
 
982
    """Tests that call directly into the command objects, bypassing the network
 
983
    and the request dispatching.
 
984
    """
904
985
        
905
 
 
906
 
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
907
 
    """Test that call directly into the handler logic, bypassing the network."""
908
 
 
909
 
    def test_construct_request_handler(self):
910
 
        """Constructing a request handler should be easy and set defaults."""
911
 
        handler = smart.SmartServerRequestHandler(None)
912
 
        self.assertFalse(handler.finished_reading)
913
 
 
914
986
    def test_hello(self):
915
 
        handler = smart.SmartServerRequestHandler(None)
916
 
        handler.dispatch_command('hello', ())
917
 
        self.assertEqual(('ok', '1'), handler.response.args)
918
 
        self.assertEqual(None, handler.response.body)
 
987
        cmd = request.HelloRequest(None)
 
988
        response = cmd.execute()
 
989
        self.assertEqual(('ok', '1'), response.args)
 
990
        self.assertEqual(None, response.body)
919
991
        
920
992
    def test_get_bundle(self):
921
993
        from bzrlib.bundle import serializer
924
996
        wt.add('hello')
925
997
        rev_id = wt.commit('add hello')
926
998
        
927
 
        handler = smart.SmartServerRequestHandler(self.get_transport())
928
 
        handler.dispatch_command('get_bundle', ('.', rev_id))
929
 
        bundle = serializer.read_bundle(StringIO(handler.response.body))
930
 
        self.assertEqual((), handler.response.args)
 
999
        cmd = request.GetBundleRequest(self.get_transport())
 
1000
        response = cmd.execute('.', rev_id)
 
1001
        bundle = serializer.read_bundle(StringIO(response.body))
 
1002
        self.assertEqual((), response.args)
 
1003
 
 
1004
 
 
1005
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
 
1006
    """Test that call directly into the handler logic, bypassing the network."""
 
1007
 
 
1008
    def setUp(self):
 
1009
        super(SmartServerRequestHandlerTests, self).setUp()
 
1010
        self._captureVar('BZR_NO_SMART_VFS', None)
 
1011
 
 
1012
    def build_handler(self, transport):
 
1013
        """Returns a handler for the commands in protocol version one."""
 
1014
        return request.SmartServerRequestHandler(transport,
 
1015
                                                 request.request_handlers)
 
1016
 
 
1017
    def test_construct_request_handler(self):
 
1018
        """Constructing a request handler should be easy and set defaults."""
 
1019
        handler = request.SmartServerRequestHandler(None, None)
 
1020
        self.assertFalse(handler.finished_reading)
 
1021
 
 
1022
    def test_hello(self):
 
1023
        handler = self.build_handler(None)
 
1024
        handler.dispatch_command('hello', ())
 
1025
        self.assertEqual(('ok', '1'), handler.response.args)
 
1026
        self.assertEqual(None, handler.response.body)
 
1027
        
 
1028
    def test_disable_vfs_handler_classes_via_environment(self):
 
1029
        # VFS handler classes will raise an error from "execute" if
 
1030
        # BZR_NO_SMART_VFS is set.
 
1031
        handler = vfs.HasRequest(None)
 
1032
        # set environment variable after construction to make sure it's
 
1033
        # examined.
 
1034
        # Note that we can safely clobber BZR_NO_SMART_VFS here, because setUp
 
1035
        # has called _captureVar, so it will be restored to the right state
 
1036
        # afterwards.
 
1037
        os.environ['BZR_NO_SMART_VFS'] = ''
 
1038
        self.assertRaises(errors.DisabledMethod, handler.execute)
931
1039
 
932
1040
    def test_readonly_exception_becomes_transport_not_possible(self):
933
1041
        """The response for a read-only error is ('ReadOnlyError')."""
934
 
        handler = smart.SmartServerRequestHandler(self.get_readonly_transport())
 
1042
        handler = self.build_handler(self.get_readonly_transport())
935
1043
        # send a mkdir for foo, with no explicit mode - should fail.
936
1044
        handler.dispatch_command('mkdir', ('foo', ''))
937
1045
        # and the failure should be an explicit ReadOnlyError
943
1051
 
944
1052
    def test_hello_has_finished_body_on_dispatch(self):
945
1053
        """The 'hello' command should set finished_reading."""
946
 
        handler = smart.SmartServerRequestHandler(None)
 
1054
        handler = self.build_handler(None)
947
1055
        handler.dispatch_command('hello', ())
948
1056
        self.assertTrue(handler.finished_reading)
949
1057
        self.assertNotEqual(None, handler.response)
950
1058
 
951
1059
    def test_put_bytes_non_atomic(self):
952
1060
        """'put_...' should set finished_reading after reading the bytes."""
953
 
        handler = smart.SmartServerRequestHandler(self.get_transport())
 
1061
        handler = self.build_handler(self.get_transport())
954
1062
        handler.dispatch_command('put_non_atomic', ('a-file', '', 'F', ''))
955
1063
        self.assertFalse(handler.finished_reading)
956
1064
        handler.accept_body('1234')
964
1072
    def test_readv_accept_body(self):
965
1073
        """'readv' should set finished_reading after reading offsets."""
966
1074
        self.build_tree(['a-file'])
967
 
        handler = smart.SmartServerRequestHandler(self.get_readonly_transport())
 
1075
        handler = self.build_handler(self.get_readonly_transport())
968
1076
        handler.dispatch_command('readv', ('a-file', ))
969
1077
        self.assertFalse(handler.finished_reading)
970
1078
        handler.accept_body('2,')
979
1087
    def test_readv_short_read_response_contents(self):
980
1088
        """'readv' when a short read occurs sets the response appropriately."""
981
1089
        self.build_tree(['a-file'])
982
 
        handler = smart.SmartServerRequestHandler(self.get_readonly_transport())
 
1090
        handler = self.build_handler(self.get_readonly_transport())
983
1091
        handler.dispatch_command('readv', ('a-file', ))
984
1092
        # read beyond the end of the file.
985
1093
        handler.accept_body('100,1')
990
1098
        self.assertEqual(None, handler.response.body)
991
1099
 
992
1100
 
993
 
class SmartTransportRegistration(tests.TestCase):
 
1101
class RemoteTransportRegistration(tests.TestCase):
994
1102
 
995
1103
    def test_registration(self):
996
1104
        t = get_transport('bzr+ssh://example.com/path')
997
 
        self.assertIsInstance(t, smart.SmartSSHTransport)
 
1105
        self.assertIsInstance(t, remote.RemoteSSHTransport)
998
1106
        self.assertEqual('example.com', t._host)
999
1107
 
1000
1108
 
1001
 
class TestSmartTransport(tests.TestCase):
 
1109
class TestRemoteTransport(tests.TestCase):
1002
1110
        
1003
1111
    def test_use_connection_factory(self):
1004
 
        # We want to be able to pass a client as a parameter to SmartTransport.
 
1112
        # We want to be able to pass a client as a parameter to RemoteTransport.
1005
1113
        input = StringIO("ok\n3\nbardone\n")
1006
1114
        output = StringIO()
1007
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1008
 
        transport = smart.SmartTransport('bzr://localhost/', medium=medium)
 
1115
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1116
        transport = remote.RemoteTransport(
 
1117
            'bzr://localhost/', medium=client_medium)
1009
1118
 
1010
1119
        # We want to make sure the client is used when the first remote
1011
1120
        # method is called.  No data should have been sent, or read.
1023
1132
 
1024
1133
    def test__translate_error_readonly(self):
1025
1134
        """Sending a ReadOnlyError to _translate_error raises TransportNotPossible."""
1026
 
        medium = smart.SmartClientMedium()
1027
 
        transport = smart.SmartTransport('bzr://localhost/', medium=medium)
 
1135
        client_medium = medium.SmartClientMedium()
 
1136
        transport = remote.RemoteTransport(
 
1137
            'bzr://localhost/', medium=client_medium)
1028
1138
        self.assertRaises(errors.TransportNotPossible,
1029
1139
            transport._translate_error, ("ReadOnlyError", ))
1030
1140
 
1031
1141
 
1032
 
class InstrumentedServerProtocol(smart.SmartServerStreamMedium):
 
1142
class InstrumentedServerProtocol(medium.SmartServerStreamMedium):
1033
1143
    """A smart server which is backed by memory and saves its write requests."""
1034
1144
 
1035
1145
    def __init__(self, write_output_list):
1036
 
        smart.SmartServerStreamMedium.__init__(self, memory.MemoryTransport())
 
1146
        medium.SmartServerStreamMedium.__init__(self, memory.MemoryTransport())
1037
1147
        self._write_output_list = write_output_list
1038
1148
 
1039
1149
 
1052
1162
 
1053
1163
    def setUp(self):
1054
1164
        super(TestSmartProtocol, self).setUp()
 
1165
        # XXX: self.server_to_client doesn't seem to be used.  If so,
 
1166
        # InstrumentedServerProtocol is redundant too.
1055
1167
        self.server_to_client = []
1056
1168
        self.to_server = StringIO()
1057
1169
        self.to_client = StringIO()
1058
 
        self.client_medium = smart.SmartSimplePipesClientMedium(self.to_client,
 
1170
        self.client_medium = medium.SmartSimplePipesClientMedium(self.to_client,
1059
1171
            self.to_server)
1060
 
        self.client_protocol = smart.SmartClientRequestProtocolOne(
 
1172
        self.client_protocol = protocol.SmartClientRequestProtocolOne(
1061
1173
            self.client_medium)
1062
1174
        self.smart_server = InstrumentedServerProtocol(self.server_to_client)
1063
 
        self.smart_server_request = smart.SmartServerRequestHandler(None)
 
1175
        self.smart_server_request = request.SmartServerRequestHandler(
 
1176
            None, request.request_handlers)
1064
1177
 
1065
1178
    def assertOffsetSerialisation(self, expected_offsets, expected_serialised,
1066
 
        client, smart_server_request):
 
1179
        client):
1067
1180
        """Check that smart (de)serialises offsets as expected.
1068
1181
        
1069
1182
        We check both serialisation and deserialisation at the same time
1072
1185
        
1073
1186
        :param expected_offsets: a readv offset list.
1074
1187
        :param expected_seralised: an expected serial form of the offsets.
1075
 
        :param smart_server_request: a SmartServerRequestHandler instance.
1076
1188
        """
1077
 
        # XXX: 'smart_server_request' should be a SmartServerRequestProtocol in
1078
 
        # future.
1079
 
        offsets = smart_server_request._deserialise_offsets(expected_serialised)
 
1189
        # XXX: '_deserialise_offsets' should be a method of the
 
1190
        # SmartServerRequestProtocol in future.
 
1191
        readv_cmd = vfs.ReadvRequest(None)
 
1192
        offsets = readv_cmd._deserialise_offsets(expected_serialised)
1080
1193
        self.assertEqual(expected_offsets, offsets)
1081
1194
        serialised = client._serialise_offsets(offsets)
1082
1195
        self.assertEqual(expected_serialised, serialised)
1083
1196
 
1084
1197
    def build_protocol_waiting_for_body(self):
1085
1198
        out_stream = StringIO()
1086
 
        protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1087
 
        protocol.has_dispatched = True
1088
 
        protocol.request = smart.SmartServerRequestHandler(None)
1089
 
        def handle_end_of_bytes():
1090
 
            self.end_received = True
1091
 
            self.assertEqual('abcdefg', protocol.request._body_bytes)
1092
 
            protocol.request.response = smart.SmartServerResponse(('ok', ))
1093
 
        protocol.request._end_of_body_handler = handle_end_of_bytes
 
1199
        smart_protocol = protocol.SmartServerRequestProtocolOne(None,
 
1200
                out_stream.write)
 
1201
        smart_protocol.has_dispatched = True
 
1202
        smart_protocol.request = self.smart_server_request
 
1203
        class FakeCommand(object):
 
1204
            def do_body(cmd, body_bytes):
 
1205
                self.end_received = True
 
1206
                self.assertEqual('abcdefg', body_bytes)
 
1207
                return request.SmartServerResponse(('ok', ))
 
1208
        smart_protocol.request._command = FakeCommand()
1094
1209
        # Call accept_bytes to make sure that internal state like _body_decoder
1095
1210
        # is initialised.  This test should probably be given a clearer
1096
1211
        # interface to work with that will not cause this inconsistency.
1097
1212
        #   -- Andrew Bennetts, 2006-09-28
1098
 
        protocol.accept_bytes('')
1099
 
        return protocol
 
1213
        smart_protocol.accept_bytes('')
 
1214
        return smart_protocol
1100
1215
 
1101
1216
    def test_construct_version_one_server_protocol(self):
1102
 
        protocol = smart.SmartServerRequestProtocolOne(None, None)
1103
 
        self.assertEqual('', protocol.excess_buffer)
1104
 
        self.assertEqual('', protocol.in_buffer)
1105
 
        self.assertFalse(protocol.has_dispatched)
1106
 
        self.assertEqual(1, protocol.next_read_size())
 
1217
        smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
 
1218
        self.assertEqual('', smart_protocol.excess_buffer)
 
1219
        self.assertEqual('', smart_protocol.in_buffer)
 
1220
        self.assertFalse(smart_protocol.has_dispatched)
 
1221
        self.assertEqual(1, smart_protocol.next_read_size())
1107
1222
 
1108
1223
    def test_construct_version_one_client_protocol(self):
1109
1224
        # we can construct a client protocol from a client medium request
1110
1225
        output = StringIO()
1111
 
        medium = smart.SmartSimplePipesClientMedium(None, output)
1112
 
        request = medium.get_request()
1113
 
        client_protocol = smart.SmartClientRequestProtocolOne(request)
 
1226
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
 
1227
        request = client_medium.get_request()
 
1228
        client_protocol = protocol.SmartClientRequestProtocolOne(request)
1114
1229
 
1115
1230
    def test_server_offset_serialisation(self):
1116
1231
        """The Smart protocol serialises offsets as a comma and \n string.
1119
1234
        one with the order of reads not increasing (an out of order read), and
1120
1235
        one that should coalesce.
1121
1236
        """
1122
 
        self.assertOffsetSerialisation([], '',
1123
 
            self.client_protocol, self.smart_server_request)
1124
 
        self.assertOffsetSerialisation([(1,2)], '1,2',
1125
 
            self.client_protocol, self.smart_server_request)
 
1237
        self.assertOffsetSerialisation([], '', self.client_protocol)
 
1238
        self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1126
1239
        self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1127
 
            self.client_protocol, self.smart_server_request)
 
1240
            self.client_protocol)
1128
1241
        self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1129
 
            '1,2\n3,4\n100,200', self.client_protocol, self.smart_server_request)
 
1242
            '1,2\n3,4\n100,200', self.client_protocol)
1130
1243
 
1131
1244
    def test_accept_bytes_of_bad_request_to_protocol(self):
1132
1245
        out_stream = StringIO()
1133
 
        protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1134
 
        protocol.accept_bytes('abc')
1135
 
        self.assertEqual('abc', protocol.in_buffer)
1136
 
        protocol.accept_bytes('\n')
1137
 
        self.assertEqual("error\x01Generic bzr smart protocol error: bad request"
1138
 
            " 'abc'\n", out_stream.getvalue())
1139
 
        self.assertTrue(protocol.has_dispatched)
1140
 
        self.assertEqual(0, protocol.next_read_size())
 
1246
        smart_protocol = protocol.SmartServerRequestProtocolOne(
 
1247
            None, out_stream.write)
 
1248
        smart_protocol.accept_bytes('abc')
 
1249
        self.assertEqual('abc', smart_protocol.in_buffer)
 
1250
        smart_protocol.accept_bytes('\n')
 
1251
        self.assertEqual(
 
1252
            "error\x01Generic bzr smart protocol error: bad request 'abc'\n",
 
1253
            out_stream.getvalue())
 
1254
        self.assertTrue(smart_protocol.has_dispatched)
 
1255
        self.assertEqual(0, smart_protocol.next_read_size())
1141
1256
 
1142
1257
    def test_accept_body_bytes_to_protocol(self):
1143
1258
        protocol = self.build_protocol_waiting_for_body()
1150
1265
        self.assertTrue(self.end_received)
1151
1266
 
1152
1267
    def test_accept_request_and_body_all_at_once(self):
 
1268
        self._captureVar('BZR_NO_SMART_VFS', None)
1153
1269
        mem_transport = memory.MemoryTransport()
1154
1270
        mem_transport.put_bytes('foo', 'abcdefghij')
1155
1271
        out_stream = StringIO()
1156
 
        protocol = smart.SmartServerRequestProtocolOne(mem_transport,
 
1272
        smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
1157
1273
                out_stream.write)
1158
 
        protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1159
 
        self.assertEqual(0, protocol.next_read_size())
 
1274
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
 
1275
        self.assertEqual(0, smart_protocol.next_read_size())
1160
1276
        self.assertEqual('readv\n3\ndefdone\n', out_stream.getvalue())
1161
 
        self.assertEqual('', protocol.excess_buffer)
1162
 
        self.assertEqual('', protocol.in_buffer)
 
1277
        self.assertEqual('', smart_protocol.excess_buffer)
 
1278
        self.assertEqual('', smart_protocol.in_buffer)
1163
1279
 
1164
1280
    def test_accept_excess_bytes_are_preserved(self):
1165
1281
        out_stream = StringIO()
1166
 
        protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1167
 
        protocol.accept_bytes('hello\nhello\n')
 
1282
        smart_protocol = protocol.SmartServerRequestProtocolOne(
 
1283
            None, out_stream.write)
 
1284
        smart_protocol.accept_bytes('hello\nhello\n')
1168
1285
        self.assertEqual("ok\x011\n", out_stream.getvalue())
1169
 
        self.assertEqual("hello\n", protocol.excess_buffer)
1170
 
        self.assertEqual("", protocol.in_buffer)
 
1286
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
 
1287
        self.assertEqual("", smart_protocol.in_buffer)
1171
1288
 
1172
1289
    def test_accept_excess_bytes_after_body(self):
1173
1290
        protocol = self.build_protocol_waiting_for_body()
1181
1298
 
1182
1299
    def test_accept_excess_bytes_after_dispatch(self):
1183
1300
        out_stream = StringIO()
1184
 
        protocol = smart.SmartServerRequestProtocolOne(None, out_stream.write)
1185
 
        protocol.accept_bytes('hello\n')
 
1301
        smart_protocol = protocol.SmartServerRequestProtocolOne(
 
1302
            None, out_stream.write)
 
1303
        smart_protocol.accept_bytes('hello\n')
1186
1304
        self.assertEqual("ok\x011\n", out_stream.getvalue())
1187
 
        protocol.accept_bytes('hel')
1188
 
        self.assertEqual("hel", protocol.excess_buffer)
1189
 
        protocol.accept_bytes('lo\n')
1190
 
        self.assertEqual("hello\n", protocol.excess_buffer)
1191
 
        self.assertEqual("", protocol.in_buffer)
 
1305
        smart_protocol.accept_bytes('hel')
 
1306
        self.assertEqual("hel", smart_protocol.excess_buffer)
 
1307
        smart_protocol.accept_bytes('lo\n')
 
1308
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
 
1309
        self.assertEqual("", smart_protocol.in_buffer)
1192
1310
 
1193
1311
    def test__send_response_sets_finished_reading(self):
1194
 
        protocol = smart.SmartServerRequestProtocolOne(None, lambda x: None)
1195
 
        self.assertEqual(1, protocol.next_read_size())
1196
 
        protocol._send_response(('x',))
1197
 
        self.assertEqual(0, protocol.next_read_size())
 
1312
        smart_protocol = protocol.SmartServerRequestProtocolOne(
 
1313
            None, lambda x: None)
 
1314
        self.assertEqual(1, smart_protocol.next_read_size())
 
1315
        smart_protocol._send_response(('x',))
 
1316
        self.assertEqual(0, smart_protocol.next_read_size())
1198
1317
 
1199
1318
    def test_query_version(self):
1200
1319
        """query_version on a SmartClientProtocolOne should return a number.
1208
1327
        # the error if the response is a non-understood version.
1209
1328
        input = StringIO('ok\x011\n')
1210
1329
        output = StringIO()
1211
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1212
 
        protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1213
 
        self.assertEqual(1, protocol.query_version())
 
1330
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1331
        request = client_medium.get_request()
 
1332
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
1333
        self.assertEqual(1, smart_protocol.query_version())
1214
1334
 
1215
1335
    def assertServerToClientEncoding(self, expected_bytes, expected_tuple,
1216
1336
            input_tuples):
1221
1341
        # expected bytes
1222
1342
        for input_tuple in input_tuples:
1223
1343
            server_output = StringIO()
1224
 
            server_protocol = smart.SmartServerRequestProtocolOne(
 
1344
            server_protocol = protocol.SmartServerRequestProtocolOne(
1225
1345
                None, server_output.write)
1226
1346
            server_protocol._send_response(input_tuple)
1227
1347
            self.assertEqual(expected_bytes, server_output.getvalue())
1228
 
        # check the decoding of the client protocol from expected_bytes:
 
1348
        # check the decoding of the client smart_protocol from expected_bytes:
1229
1349
        input = StringIO(expected_bytes)
1230
1350
        output = StringIO()
1231
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1232
 
        protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1233
 
        protocol.call('foo')
1234
 
        self.assertEqual(expected_tuple, protocol.read_response_tuple())
 
1351
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1352
        request = client_medium.get_request()
 
1353
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
1354
        smart_protocol.call('foo')
 
1355
        self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
1235
1356
 
1236
1357
    def test_client_call_empty_response(self):
1237
1358
        # protocol.call() can get back an empty tuple as a response. This occurs
1246
1367
            [('a', 'b', '34')])
1247
1368
 
1248
1369
    def test_client_call_with_body_bytes_uploads(self):
1249
 
        # protocol.call_with_upload should length-prefix the bytes onto the 
 
1370
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
1250
1371
        # wire.
1251
1372
        expected_bytes = "foo\n7\nabcdefgdone\n"
1252
1373
        input = StringIO("\n")
1253
1374
        output = StringIO()
1254
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1255
 
        protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1256
 
        protocol.call_with_body_bytes(('foo', ), "abcdefg")
 
1375
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1376
        request = client_medium.get_request()
 
1377
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
1378
        smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
1257
1379
        self.assertEqual(expected_bytes, output.getvalue())
1258
1380
 
1259
1381
    def test_client_call_with_body_readv_array(self):
1262
1384
        expected_bytes = "foo\n7\n1,2\n5,6done\n"
1263
1385
        input = StringIO("\n")
1264
1386
        output = StringIO()
1265
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1266
 
        protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1267
 
        protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
 
1387
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1388
        request = client_medium.get_request()
 
1389
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
1390
        smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1268
1391
        self.assertEqual(expected_bytes, output.getvalue())
1269
1392
 
1270
1393
    def test_client_read_body_bytes_all(self):
1274
1397
        server_bytes = "ok\n7\n1234567done\n"
1275
1398
        input = StringIO(server_bytes)
1276
1399
        output = StringIO()
1277
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1278
 
        protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1279
 
        protocol.call('foo')
1280
 
        protocol.read_response_tuple(True)
1281
 
        self.assertEqual(expected_bytes, protocol.read_body_bytes())
 
1400
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1401
        request = client_medium.get_request()
 
1402
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
1403
        smart_protocol.call('foo')
 
1404
        smart_protocol.read_response_tuple(True)
 
1405
        self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
1282
1406
 
1283
1407
    def test_client_read_body_bytes_incremental(self):
1284
1408
        # test reading a few bytes at a time from the body
1290
1414
        server_bytes = "ok\n7\n1234567done\n"
1291
1415
        input = StringIO(server_bytes)
1292
1416
        output = StringIO()
1293
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1294
 
        protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1295
 
        protocol.call('foo')
1296
 
        protocol.read_response_tuple(True)
1297
 
        self.assertEqual(expected_bytes[0:2], protocol.read_body_bytes(2))
1298
 
        self.assertEqual(expected_bytes[2:4], protocol.read_body_bytes(2))
1299
 
        self.assertEqual(expected_bytes[4:6], protocol.read_body_bytes(2))
1300
 
        self.assertEqual(expected_bytes[6], protocol.read_body_bytes())
 
1417
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1418
        request = client_medium.get_request()
 
1419
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
1420
        smart_protocol.call('foo')
 
1421
        smart_protocol.read_response_tuple(True)
 
1422
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
 
1423
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
 
1424
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
 
1425
        self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
1301
1426
 
1302
1427
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1303
1428
        # cancelling the expected body needs to finish the request, but not
1306
1431
        server_bytes = "ok\n7\n1234567done\n"
1307
1432
        input = StringIO(server_bytes)
1308
1433
        output = StringIO()
1309
 
        medium = smart.SmartSimplePipesClientMedium(input, output)
1310
 
        protocol = smart.SmartClientRequestProtocolOne(medium.get_request())
1311
 
        protocol.call('foo')
1312
 
        protocol.read_response_tuple(True)
1313
 
        protocol.cancel_read_body()
 
1434
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1435
        request = client_medium.get_request()
 
1436
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
1437
        smart_protocol.call('foo')
 
1438
        smart_protocol.read_response_tuple(True)
 
1439
        smart_protocol.cancel_read_body()
1314
1440
        self.assertEqual(3, input.tell())
1315
 
        self.assertRaises(errors.ReadingCompleted, protocol.read_body_bytes)
 
1441
        self.assertRaises(
 
1442
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
 
1443
 
 
1444
 
 
1445
class TestSmartClientUnicode(tests.TestCase):
 
1446
    """_SmartClient tests for unicode arguments.
 
1447
 
 
1448
    Unicode arguments to call_with_body_bytes are not correct (remote method
 
1449
    names, arguments, and bodies must all be expressed as byte strings), but
 
1450
    _SmartClient should gracefully reject them, rather than getting into a
 
1451
    broken state that prevents future correct calls from working.  That is, it
 
1452
    should be possible to issue more requests on the medium afterwards, rather
 
1453
    than allowing one bad call to call_with_body_bytes to cause later calls to
 
1454
    mysteriously fail with TooManyConcurrentRequests.
 
1455
    """
 
1456
 
 
1457
    def assertCallDoesNotBreakMedium(self, method, args, body):
 
1458
        """Call a medium with the given method, args and body, then assert that
 
1459
        the medium is left in a sane state, i.e. is capable of allowing further
 
1460
        requests.
 
1461
        """
 
1462
        input = StringIO("\n")
 
1463
        output = StringIO()
 
1464
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
 
1465
        smart_client = client._SmartClient(client_medium)
 
1466
        self.assertRaises(TypeError,
 
1467
            smart_client.call_with_body_bytes, method, args, body)
 
1468
        self.assertEqual("", output.getvalue())
 
1469
        self.assertEqual(None, client_medium._current_request)
 
1470
 
 
1471
    def test_call_with_body_bytes_unicode_method(self):
 
1472
        self.assertCallDoesNotBreakMedium(u'method', ('args',), 'body')
 
1473
 
 
1474
    def test_call_with_body_bytes_unicode_args(self):
 
1475
        self.assertCallDoesNotBreakMedium('method', (u'args',), 'body')
 
1476
        self.assertCallDoesNotBreakMedium('method', ('arg1', u'arg2'), 'body')
 
1477
 
 
1478
    def test_call_with_body_bytes_unicode_body(self):
 
1479
        self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
1316
1480
 
1317
1481
 
1318
1482
class LengthPrefixedBodyDecoder(tests.TestCase):
1321
1485
    # something similar to the ProtocolBase method.
1322
1486
 
1323
1487
    def test_construct(self):
1324
 
        decoder = smart.LengthPrefixedBodyDecoder()
 
1488
        decoder = protocol.LengthPrefixedBodyDecoder()
1325
1489
        self.assertFalse(decoder.finished_reading)
1326
1490
        self.assertEqual(6, decoder.next_read_size())
1327
1491
        self.assertEqual('', decoder.read_pending_data())
1328
1492
        self.assertEqual('', decoder.unused_data)
1329
1493
 
1330
1494
    def test_accept_bytes(self):
1331
 
        decoder = smart.LengthPrefixedBodyDecoder()
 
1495
        decoder = protocol.LengthPrefixedBodyDecoder()
1332
1496
        decoder.accept_bytes('')
1333
1497
        self.assertFalse(decoder.finished_reading)
1334
1498
        self.assertEqual(6, decoder.next_read_size())
1361
1525
        self.assertEqual('blarg', decoder.unused_data)
1362
1526
        
1363
1527
    def test_accept_bytes_all_at_once_with_excess(self):
1364
 
        decoder = smart.LengthPrefixedBodyDecoder()
 
1528
        decoder = protocol.LengthPrefixedBodyDecoder()
1365
1529
        decoder.accept_bytes('1\nadone\nunused')
1366
1530
        self.assertTrue(decoder.finished_reading)
1367
1531
        self.assertEqual(1, decoder.next_read_size())
1369
1533
        self.assertEqual('unused', decoder.unused_data)
1370
1534
 
1371
1535
    def test_accept_bytes_exact_end_of_body(self):
1372
 
        decoder = smart.LengthPrefixedBodyDecoder()
 
1536
        decoder = protocol.LengthPrefixedBodyDecoder()
1373
1537
        decoder.accept_bytes('1\na')
1374
1538
        self.assertFalse(decoder.finished_reading)
1375
1539
        self.assertEqual(5, decoder.next_read_size())
1393
1557
 
1394
1558
class HTTPTunnellingSmokeTest(tests.TestCaseWithTransport):
1395
1559
    
 
1560
    def setUp(self):
 
1561
        super(HTTPTunnellingSmokeTest, self).setUp()
 
1562
        # We use the VFS layer as part of HTTP tunnelling tests.
 
1563
        self._captureVar('BZR_NO_SMART_VFS', None)
 
1564
 
1396
1565
    def _test_bulk_data(self, url_protocol):
1397
1566
        # We should be able to send and receive bulk data in a single message.
1398
1567
        # The 'readv' command in the smart protocol both sends and receives bulk
1399
1568
        # data, so we use that.
1400
1569
        self.build_tree(['data-file'])
1401
 
        http_server = HTTPServerWithSmarts()
1402
 
        http_server._url_protocol = url_protocol
1403
 
        http_server.setUp()
1404
 
        self.addCleanup(http_server.tearDown)
1405
 
 
1406
 
        http_transport = get_transport(http_server.get_url())
1407
 
 
 
1570
        self.transport_readonly_server = HTTPServerWithSmarts
 
1571
 
 
1572
        http_transport = self.get_readonly_transport()
1408
1573
        medium = http_transport.get_smart_medium()
1409
1574
        #remote_transport = RemoteTransport('fake_url', medium)
1410
 
        remote_transport = smart.SmartTransport('/', medium=medium)
 
1575
        remote_transport = remote.RemoteTransport('/', medium=medium)
1411
1576
        self.assertEqual(
1412
1577
            [(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1413
1578
 
1432
1597
    def _test_http_send_smart_request(self, url_protocol):
1433
1598
        http_server = HTTPServerWithSmarts()
1434
1599
        http_server._url_protocol = url_protocol
1435
 
        http_server.setUp()
 
1600
        http_server.setUp(self.get_vfs_only_server())
1436
1601
        self.addCleanup(http_server.tearDown)
1437
1602
 
1438
1603
        post_body = 'hello\n'
1454
1619
        self._test_http_send_smart_request('http+urllib')
1455
1620
 
1456
1621
    def test_http_server_with_smarts(self):
1457
 
        http_server = HTTPServerWithSmarts()
1458
 
        http_server.setUp()
1459
 
        self.addCleanup(http_server.tearDown)
 
1622
        self.transport_readonly_server = HTTPServerWithSmarts
1460
1623
 
1461
1624
        post_body = 'hello\n'
1462
1625
        expected_reply_body = 'ok\x011\n'
1463
1626
 
1464
 
        smart_server_url = http_server.get_url() + '.bzr/smart'
 
1627
        smart_server_url = self.get_readonly_url('.bzr/smart')
1465
1628
        reply = urllib2.urlopen(smart_server_url, post_body).read()
1466
1629
 
1467
1630
        self.assertEqual(expected_reply_body, reply)
1468
1631
 
1469
1632
    def test_smart_http_server_post_request_handler(self):
1470
 
        http_server = HTTPServerWithSmarts()
1471
 
        http_server.setUp()
1472
 
        self.addCleanup(http_server.tearDown)
1473
 
        httpd = http_server._get_httpd()
 
1633
        self.transport_readonly_server = HTTPServerWithSmarts
 
1634
        httpd = self.get_readonly_server()._get_httpd()
1474
1635
 
1475
1636
        socket = SampleSocket(
1476
1637
            'POST /.bzr/smart HTTP/1.0\r\n'
1478
1639
            'Content-Length: 6\r\n'
1479
1640
            '\r\n'
1480
1641
            'hello\n')
1481
 
        request_handler = SmartRequestHandler(
1482
 
            socket, ('localhost', 80), httpd)
 
1642
        # Beware: the ('localhost', 80) below is the
 
1643
        # client_address parameter, but we don't have one because
 
1644
        # we have defined a socket which is not bound to an
 
1645
        # address. The test framework never uses this client
 
1646
        # address, so far...
 
1647
        request_handler = SmartRequestHandler(socket, ('localhost', 80), httpd)
1483
1648
        response = socket.writefile.getvalue()
1484
1649
        self.assertStartsWith(response, 'HTTP/1.0 200 ')
1485
1650
        # This includes the end of the HTTP headers, and all the body.
1508
1673
        else:
1509
1674
            return self.writefile
1510
1675
 
1511
 
        
 
1676
 
1512
1677
# TODO: Client feature that does get_bundle and then installs that into a
1513
1678
# branch; this can be used in place of the regular pull/fetch operation when
1514
1679
# coming from a smart server.