733
733
server._serve_one_request(SampleRequest('x'))
734
734
self.assertTrue(server.finished)
736
def test_socket_stream_incomplete_request(self):
737
"""The medium should still construct the right protocol version even if
738
the initial read only reads part of the request.
740
Specifically, it should correctly read the protocol version line even
741
if the partial read doesn't end in a newline. An older, naive
742
implementation of _get_line in the server used to have a bug in that
745
incomplete_request_bytes = protocol.REQUEST_VERSION_TWO + 'hel'
746
rest_of_request_bytes = 'lo\n'
747
expected_response = (
748
protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n')
749
server_sock, client_sock = self.portable_socket_pair()
750
server = medium.SmartServerSocketStreamMedium(
752
client_sock.sendall(incomplete_request_bytes)
753
server_protocol = server._build_protocol()
754
client_sock.sendall(rest_of_request_bytes)
755
server._serve_one_request(server_protocol)
757
self.assertEqual(expected_response, client_sock.recv(50),
758
"Not a version 2 response to 'hello' request.")
759
self.assertEqual('', client_sock.recv(1))
761
def test_pipe_stream_incomplete_request(self):
762
"""The medium should still construct the right protocol version even if
763
the initial read only reads part of the request.
765
Specifically, it should correctly read the protocol version line even
766
if the partial read doesn't end in a newline. An older, naive
767
implementation of _get_line in the server used to have a bug in that
770
incomplete_request_bytes = protocol.REQUEST_VERSION_TWO + 'hel'
771
rest_of_request_bytes = 'lo\n'
772
expected_response = (
773
protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n')
774
# Make a pair of pipes, to and from the server
775
to_server, to_server_w = os.pipe()
776
from_server_r, from_server = os.pipe()
777
to_server = os.fdopen(to_server, 'r', 0)
778
to_server_w = os.fdopen(to_server_w, 'w', 0)
779
from_server_r = os.fdopen(from_server_r, 'r', 0)
780
from_server = os.fdopen(from_server, 'w', 0)
781
server = medium.SmartServerPipeStreamMedium(
782
to_server, from_server, None)
783
# Like test_socket_stream_incomplete_request, write an incomplete
784
# request (that does not end in '\n') and build a protocol from it.
785
to_server_w.write(incomplete_request_bytes)
786
server_protocol = server._build_protocol()
787
# Send the rest of the request, and finish serving it.
788
to_server_w.write(rest_of_request_bytes)
789
server._serve_one_request(server_protocol)
792
self.assertEqual(expected_response, from_server_r.read(),
793
"Not a version 2 response to 'hello' request.")
794
self.assertEqual('', from_server_r.read(1))
795
from_server_r.close()
736
798
def test_pipe_like_stream_with_two_requests(self):
737
799
# If two requests are read in one go, then two calls to
738
800
# _serve_one_request should still process both of them as if they had
1172
1234
def build_handler(self, transport):
1173
1235
"""Returns a handler for the commands in protocol version one."""
1174
return request.SmartServerRequestHandler(transport,
1175
request.request_handlers)
1236
return _mod_request.SmartServerRequestHandler(
1237
transport, _mod_request.request_handlers, '/')
1177
1239
def test_construct_request_handler(self):
1178
1240
"""Constructing a request handler should be easy and set defaults."""
1179
handler = request.SmartServerRequestHandler(None, None)
1241
handler = _mod_request.SmartServerRequestHandler(None, commands=None,
1242
root_client_path='/')
1180
1243
self.assertFalse(handler.finished_reading)
1182
1245
def test_hello(self):
1253
1316
handler.accept_body('100,1')
1254
1317
handler.end_of_body()
1255
1318
self.assertTrue(handler.finished_reading)
1256
self.assertEqual(('ShortReadvError', 'a-file', '100', '1', '0'),
1319
self.assertEqual(('ShortReadvError', './a-file', '100', '1', '0'),
1257
1320
handler.response.args)
1258
1321
self.assertEqual(None, handler.response.body)
1307
1370
transport._translate_error, ("ReadOnlyError", ))
1310
class InstrumentedServerProtocol(medium.SmartServerStreamMedium):
1311
"""A smart server which is backed by memory and saves its write requests."""
1313
def __init__(self, write_output_list):
1314
medium.SmartServerStreamMedium.__init__(self, memory.MemoryTransport())
1315
self._write_output_list = write_output_list
1318
1373
class TestSmartProtocol(tests.TestCase):
1319
1374
"""Base class for smart protocol tests.
1333
1388
client_protocol_class = None
1334
1389
server_protocol_class = None
1337
super(TestSmartProtocol, self).setUp()
1338
# XXX: self.server_to_client doesn't seem to be used. If so,
1339
# InstrumentedServerProtocol is redundant too.
1340
self.server_to_client = []
1341
self.to_server = StringIO()
1342
self.to_client = StringIO()
1343
self.client_medium = medium.SmartSimplePipesClientMedium(self.to_client,
1345
self.client_protocol = self.client_protocol_class(self.client_medium)
1346
self.smart_server = InstrumentedServerProtocol(self.server_to_client)
1347
self.smart_server_request = request.SmartServerRequestHandler(
1348
None, request.request_handlers)
1391
def make_client_protocol(self):
1392
client_medium = medium.SmartSimplePipesClientMedium(
1393
StringIO(), StringIO())
1394
return self.client_protocol_class(client_medium.get_request())
1396
def make_server_protocol(self):
1397
out_stream = StringIO()
1398
smart_protocol = self.server_protocol_class(None, out_stream.write)
1399
return smart_protocol, out_stream
1350
1401
def assertOffsetSerialisation(self, expected_offsets, expected_serialised,
1361
1412
# XXX: '_deserialise_offsets' should be a method of the
1362
1413
# SmartServerRequestProtocol in future.
1363
readv_cmd = vfs.ReadvRequest(None)
1414
readv_cmd = vfs.ReadvRequest(None, '/')
1364
1415
offsets = readv_cmd._deserialise_offsets(expected_serialised)
1365
1416
self.assertEqual(expected_offsets, offsets)
1366
1417
serialised = client._serialise_offsets(offsets)
1367
1418
self.assertEqual(expected_serialised, serialised)
1369
1420
def build_protocol_waiting_for_body(self):
1370
out_stream = StringIO()
1371
smart_protocol = self.server_protocol_class(None, out_stream.write)
1421
smart_protocol, out_stream = self.make_server_protocol()
1372
1422
smart_protocol.has_dispatched = True
1373
smart_protocol.request = self.smart_server_request
1423
smart_protocol.request = _mod_request.SmartServerRequestHandler(
1424
None, _mod_request.request_handlers, '/')
1374
1425
class FakeCommand(object):
1375
1426
def do_body(cmd, body_bytes):
1376
1427
self.end_received = True
1377
1428
self.assertEqual('abcdefg', body_bytes)
1378
return request.SuccessfulSmartServerResponse(('ok', ))
1429
return _mod_request.SuccessfulSmartServerResponse(('ok', ))
1379
1430
smart_protocol.request._command = FakeCommand()
1380
1431
# Call accept_bytes to make sure that internal state like _body_decoder
1381
1432
# is initialised. This test should probably be given a clearer
1392
1443
# check the encoding of the server for all input_tuples matches
1393
1444
# expected bytes
1394
1445
for input_tuple in input_tuples:
1395
server_output = StringIO()
1396
server_protocol = self.server_protocol_class(
1397
None, server_output.write)
1446
server_protocol, server_output = self.make_server_protocol()
1398
1447
server_protocol._send_response(
1399
1448
_mod_request.SuccessfulSmartServerResponse(input_tuple))
1400
1449
self.assertEqual(expected_bytes, server_output.getvalue())
1421
1469
self.assertContainsRe(test_log, 'SmartProtocolError')
1423
1471
def test_connection_closed_reporting(self):
1426
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1427
request = client_medium.get_request()
1428
smart_protocol = self.client_protocol_class(request)
1472
smart_protocol = self.make_client_protocol()
1429
1473
smart_protocol.call('hello')
1430
ex = self.assertRaises(errors.ConnectionReset,
1474
ex = self.assertRaises(errors.ConnectionReset,
1431
1475
smart_protocol.read_response_tuple)
1432
1476
self.assertEqual("Connection closed: "
1433
1477
"please check connectivity and permissions "
1434
1478
"(and try -Dhpss if further diagnosis is required)", str(ex))
1480
def test_server_offset_serialisation(self):
1481
"""The Smart protocol serialises offsets as a comma and \n string.
1483
We check a number of boundary cases are as expected: empty, one offset,
1484
one with the order of reads not increasing (an out of order read), and
1485
one that should coalesce.
1487
client_protocol = self.make_client_protocol()
1488
self.assertOffsetSerialisation([], '', client_protocol)
1489
self.assertOffsetSerialisation([(1,2)], '1,2', client_protocol)
1490
self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1492
self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1493
'1,2\n3,4\n100,200', client_protocol)
1437
1496
class TestSmartProtocolOne(TestSmartProtocol, CommonSmartProtocolTestMixin):
1438
1497
"""Tests for the smart protocol version one."""
1454
1513
request = client_medium.get_request()
1455
1514
client_protocol = protocol.SmartClientRequestProtocolOne(request)
1457
def test_server_offset_serialisation(self):
1458
"""The Smart protocol serialises offsets as a comma and \n string.
1460
We check a number of boundary cases are as expected: empty, one offset,
1461
one with the order of reads not increasing (an out of order read), and
1462
one that should coalesce.
1464
self.assertOffsetSerialisation([], '', self.client_protocol)
1465
self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1466
self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1467
self.client_protocol)
1468
self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1469
'1,2\n3,4\n100,200', self.client_protocol)
1471
1516
def test_accept_bytes_of_bad_request_to_protocol(self):
1472
1517
out_stream = StringIO()
1473
1518
smart_protocol = protocol.SmartServerRequestProtocolOne(
1603
1648
smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1604
1649
self.assertEqual(expected_bytes, output.getvalue())
1651
def _test_client_read_response_tuple_raises_UnknownSmartMethod(self,
1653
input = StringIO(server_bytes)
1655
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1656
request = client_medium.get_request()
1657
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1658
smart_protocol.call('foo')
1660
errors.UnknownSmartMethod, smart_protocol.read_response_tuple)
1661
# The request has been finished. There is no body to read, and
1662
# attempts to read one will fail.
1664
errors.ReadingCompleted, smart_protocol.read_body_bytes)
1666
def test_client_read_response_tuple_raises_UnknownSmartMethod(self):
1667
"""read_response_tuple raises UnknownSmartMethod if the response says
1668
the server did not recognise the request.
1671
"error\x01Generic bzr smart protocol error: bad request 'foo'\n")
1672
self._test_client_read_response_tuple_raises_UnknownSmartMethod(
1675
def test_client_read_response_tuple_raises_UnknownSmartMethod_0_11(self):
1676
"""read_response_tuple also raises UnknownSmartMethod if the response
1677
from a bzr 0.11 says the server did not recognise the request.
1679
(bzr 0.11 sends a slightly different error message to later versions.)
1682
"error\x01Generic bzr smart protocol error: bad request u'foo'\n")
1683
self._test_client_read_response_tuple_raises_UnknownSmartMethod(
1606
1686
def test_client_read_body_bytes_all(self):
1607
1687
# read_body_bytes should decode the body bytes from the wire into
1678
1758
request = client_medium.get_request()
1679
1759
client_protocol = protocol.SmartClientRequestProtocolTwo(request)
1681
def test_server_offset_serialisation(self):
1682
"""The Smart protocol serialises offsets as a comma and \n string.
1684
We check a number of boundary cases are as expected: empty, one offset,
1685
one with the order of reads not increasing (an out of order read), and
1686
one that should coalesce.
1688
self.assertOffsetSerialisation([], '', self.client_protocol)
1689
self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1690
self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1691
self.client_protocol)
1692
self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1693
'1,2\n3,4\n100,200', self.client_protocol)
1695
1761
def assertBodyStreamSerialisation(self, expected_serialisation,
1697
1763
"""Assert that body_stream is serialised as expected_serialisation."""
1932
1998
smart_protocol.read_response_tuple(False)
1933
1999
self.assertEqual(True, smart_protocol.response_status)
2001
def test_client_read_response_tuple_raises_UnknownSmartMethod(self):
2002
"""read_response_tuple raises UnknownSmartMethod if the response is
2003
says the server did not recognise the request.
2006
protocol.RESPONSE_VERSION_TWO +
2008
"error\x01Generic bzr smart protocol error: bad request 'foo'\n")
2009
input = StringIO(server_bytes)
2011
client_medium = medium.SmartSimplePipesClientMedium(input, output)
2012
request = client_medium.get_request()
2013
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
2014
smart_protocol.call('foo')
2016
errors.UnknownSmartMethod, smart_protocol.read_response_tuple)
2017
self.assertEqual(False, smart_protocol.response_status)
2018
# The request has been finished. There is no body to read, and
2019
# attempts to read one will fail.
2021
errors.ReadingCompleted, smart_protocol.read_body_bytes)
1935
2023
def test_client_read_body_bytes_all(self):
1936
2024
# read_body_bytes should decode the body bytes from the wire into
2299
2387
class TestSuccessfulSmartServerResponse(tests.TestCase):
2301
2389
def test_construct_no_body(self):
2302
response = request.SuccessfulSmartServerResponse(('foo', 'bar'))
2390
response = _mod_request.SuccessfulSmartServerResponse(('foo', 'bar'))
2303
2391
self.assertEqual(('foo', 'bar'), response.args)
2304
2392
self.assertEqual(None, response.body)
2306
2394
def test_construct_with_body(self):
2307
response = request.SuccessfulSmartServerResponse(
2308
('foo', 'bar'), 'bytes')
2395
response = _mod_request.SuccessfulSmartServerResponse(('foo', 'bar'),
2309
2397
self.assertEqual(('foo', 'bar'), response.args)
2310
2398
self.assertEqual('bytes', response.body)
2311
2399
# repr(response) doesn't trigger exceptions.
2322
2410
"""'body' and 'body_stream' are mutually exclusive."""
2323
2411
self.assertRaises(
2324
2412
errors.BzrError,
2325
request.SuccessfulSmartServerResponse, (), 'body', ['stream'])
2413
_mod_request.SuccessfulSmartServerResponse, (), 'body', ['stream'])
2327
2415
def test_is_successful(self):
2328
2416
"""is_successful should return True for SuccessfulSmartServerResponse."""
2329
response = request.SuccessfulSmartServerResponse(('error',))
2417
response = _mod_request.SuccessfulSmartServerResponse(('error',))
2330
2418
self.assertEqual(True, response.is_successful())
2333
2421
class TestFailedSmartServerResponse(tests.TestCase):
2335
2423
def test_construct(self):
2336
response = request.FailedSmartServerResponse(('foo', 'bar'))
2424
response = _mod_request.FailedSmartServerResponse(('foo', 'bar'))
2337
2425
self.assertEqual(('foo', 'bar'), response.args)
2338
2426
self.assertEqual(None, response.body)
2339
response = request.FailedSmartServerResponse(('foo', 'bar'), 'bytes')
2427
response = _mod_request.FailedSmartServerResponse(('foo', 'bar'), 'bytes')
2340
2428
self.assertEqual(('foo', 'bar'), response.args)
2341
2429
self.assertEqual('bytes', response.body)
2342
2430
# repr(response) doesn't trigger exceptions.