198
254
client_medium._accept_bytes('abc')
199
255
self.assertEqual('abc', output.getvalue())
257
def test_simple_pipes__accept_bytes_subprocess_closed(self):
258
# It is unfortunate that we have to use Popen for this. However,
259
# os.pipe() does not behave the same as subprocess.Popen().
260
# On Windows, if you use os.pipe() and close the write side,
261
# read.read() hangs. On Linux, read.read() returns the empty string.
262
p = subprocess.Popen([sys.executable, '-c',
264
'sys.stdout.write(sys.stdin.read(4))\n'
265
'sys.stdout.close()\n'],
266
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
267
client_medium = medium.SmartSimplePipesClientMedium(
268
p.stdout, p.stdin, 'base')
269
client_medium._accept_bytes('abc\n')
270
self.assertEqual('abc', client_medium._read_bytes(3))
272
# While writing to the underlying pipe,
273
# Windows py2.6.6 we get IOError(EINVAL)
274
# Lucid py2.6.5, we get IOError(EPIPE)
275
# In both cases, it should be wrapped to ConnectionReset
276
self.assertRaises(errors.ConnectionReset,
277
client_medium._accept_bytes, 'more')
279
def test_simple_pipes__accept_bytes_pipe_closed(self):
280
child_read, client_write = create_file_pipes()
281
client_medium = medium.SmartSimplePipesClientMedium(
282
None, client_write, 'base')
283
client_medium._accept_bytes('abc\n')
284
self.assertEqual('abc\n', child_read.read(4))
285
# While writing to the underlying pipe,
286
# Windows py2.6.6 we get IOError(EINVAL)
287
# Lucid py2.6.5, we get IOError(EPIPE)
288
# In both cases, it should be wrapped to ConnectionReset
290
self.assertRaises(errors.ConnectionReset,
291
client_medium._accept_bytes, 'more')
293
def test_simple_pipes__flush_pipe_closed(self):
294
child_read, client_write = create_file_pipes()
295
client_medium = medium.SmartSimplePipesClientMedium(
296
None, client_write, 'base')
297
client_medium._accept_bytes('abc\n')
299
# Even though the pipe is closed, flush on the write side seems to be a
300
# no-op, rather than a failure.
301
client_medium._flush()
303
def test_simple_pipes__flush_subprocess_closed(self):
304
p = subprocess.Popen([sys.executable, '-c',
306
'sys.stdout.write(sys.stdin.read(4))\n'
307
'sys.stdout.close()\n'],
308
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
309
client_medium = medium.SmartSimplePipesClientMedium(
310
p.stdout, p.stdin, 'base')
311
client_medium._accept_bytes('abc\n')
313
# Even though the child process is dead, flush seems to be a no-op.
314
client_medium._flush()
316
def test_simple_pipes__read_bytes_pipe_closed(self):
317
child_read, client_write = create_file_pipes()
318
client_medium = medium.SmartSimplePipesClientMedium(
319
child_read, client_write, 'base')
320
client_medium._accept_bytes('abc\n')
322
self.assertEqual('abc\n', client_medium._read_bytes(4))
323
self.assertEqual('', client_medium._read_bytes(4))
325
def test_simple_pipes__read_bytes_subprocess_closed(self):
326
p = subprocess.Popen([sys.executable, '-c',
328
'if sys.platform == "win32":\n'
329
' import msvcrt, os\n'
330
' msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\n'
331
' msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)\n'
332
'sys.stdout.write(sys.stdin.read(4))\n'
333
'sys.stdout.close()\n'],
334
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
335
client_medium = medium.SmartSimplePipesClientMedium(
336
p.stdout, p.stdin, 'base')
337
client_medium._accept_bytes('abc\n')
339
self.assertEqual('abc\n', client_medium._read_bytes(4))
340
self.assertEqual('', client_medium._read_bytes(4))
201
342
def test_simple_pipes_client_disconnect_does_nothing(self):
202
343
# calling disconnect does nothing.
203
344
input = StringIO()
3738
class Test_SmartClientRequest(tests.TestCase):
3740
def make_client_with_failing_medium(self, fail_at_write=True, response=''):
3741
response_io = StringIO(response)
3743
vendor = FirstRejectedStringIOSSHVendor(response_io, output,
3744
fail_at_write=fail_at_write)
3745
ssh_params = medium.SSHParams('a host', 'a port', 'a user', 'a pass')
3746
client_medium = medium.SmartSSHClientMedium('base', ssh_params, vendor)
3747
smart_client = client._SmartClient(client_medium, headers={})
3748
return output, vendor, smart_client
3750
def make_response(self, args, body=None, body_stream=None):
3751
response_io = StringIO()
3752
response = _mod_request.SuccessfulSmartServerResponse(args, body=body,
3753
body_stream=body_stream)
3754
responder = protocol.ProtocolThreeResponder(response_io.write)
3755
responder.send_response(response)
3756
return response_io.getvalue()
3758
def test__call_doesnt_retry_append(self):
3759
response = self.make_response(('appended', '8'))
3760
output, vendor, smart_client = self.make_client_with_failing_medium(
3761
fail_at_write=False, response=response)
3762
smart_request = client._SmartClientRequest(smart_client, 'append',
3763
('foo', ''), body='content\n')
3764
self.assertRaises(errors.ConnectionReset, smart_request._call, 3)
3766
def test__call_retries_get_bytes(self):
3767
response = self.make_response(('ok',), 'content\n')
3768
output, vendor, smart_client = self.make_client_with_failing_medium(
3769
fail_at_write=False, response=response)
3770
smart_request = client._SmartClientRequest(smart_client, 'get',
3772
response, response_handler = smart_request._call(3)
3773
self.assertEqual(('ok',), response)
3774
self.assertEqual('content\n', response_handler.read_body_bytes())
3776
def test__call_noretry_get_bytes(self):
3777
debug.debug_flags.add('noretry')
3778
response = self.make_response(('ok',), 'content\n')
3779
output, vendor, smart_client = self.make_client_with_failing_medium(
3780
fail_at_write=False, response=response)
3781
smart_request = client._SmartClientRequest(smart_client, 'get',
3783
self.assertRaises(errors.ConnectionReset, smart_request._call, 3)
3785
def test__send_no_retry_pipes(self):
3786
client_read, server_write = create_file_pipes()
3787
server_read, client_write = create_file_pipes()
3788
client_medium = medium.SmartSimplePipesClientMedium(client_read,
3789
client_write, base='/')
3790
smart_client = client._SmartClient(client_medium)
3791
smart_request = client._SmartClientRequest(smart_client,
3793
# Close the server side
3795
encoder, response_handler = smart_request._construct_protocol(3)
3796
self.assertRaises(errors.ConnectionReset,
3797
smart_request._send_no_retry, encoder)
3799
def test__send_read_response_sockets(self):
3800
listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3801
listen_sock.bind(('127.0.0.1', 0))
3802
listen_sock.listen(1)
3803
host, port = listen_sock.getsockname()
3804
client_medium = medium.SmartTCPClientMedium(host, port, '/')
3805
client_medium._ensure_connection()
3806
smart_client = client._SmartClient(client_medium)
3807
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3808
# Accept the connection, but don't actually talk to the client.
3809
server_sock, _ = listen_sock.accept()
3811
# Sockets buffer and don't really notice that the server has closed the
3812
# connection until we try to read again.
3813
handler = smart_request._send(3)
3814
self.assertRaises(errors.ConnectionReset,
3815
handler.read_response_tuple, expect_body=False)
3817
def test__send_retries_on_write(self):
3818
output, vendor, smart_client = self.make_client_with_failing_medium()
3819
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3820
handler = smart_request._send(3)
3821
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3822
'\x00\x00\x00\x02de' # empty headers
3823
's\x00\x00\x00\tl5:helloee',
3826
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3827
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3829
('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3830
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3834
def test__send_doesnt_retry_read_failure(self):
3835
output, vendor, smart_client = self.make_client_with_failing_medium(
3836
fail_at_write=False)
3837
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3838
handler = smart_request._send(3)
3839
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3840
'\x00\x00\x00\x02de' # empty headers
3841
's\x00\x00\x00\tl5:helloee',
3844
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3845
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3848
self.assertRaises(errors.ConnectionReset, handler.read_response_tuple)
3850
def test__send_request_retries_body_stream_if_not_started(self):
3851
output, vendor, smart_client = self.make_client_with_failing_medium()
3852
smart_request = client._SmartClientRequest(smart_client, 'hello', (),
3853
body_stream=['a', 'b'])
3854
response_handler = smart_request._send(3)
3855
# We connect, get disconnected, and notice before consuming the stream,
3856
# so we try again one time and succeed.
3858
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3859
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3861
('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3862
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3865
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3866
'\x00\x00\x00\x02de' # empty headers
3867
's\x00\x00\x00\tl5:helloe'
3868
'b\x00\x00\x00\x01a'
3869
'b\x00\x00\x00\x01b'
3873
def test__send_request_stops_if_body_started(self):
3874
# We intentionally use the python StringIO so that we can subclass it.
3875
from StringIO import StringIO
3876
response = StringIO()
3878
class FailAfterFirstWrite(StringIO):
3879
"""Allow one 'write' call to pass, fail the rest"""
3881
StringIO.__init__(self)
3887
return StringIO.write(self, s)
3888
raise IOError(errno.EINVAL, 'invalid file handle')
3889
output = FailAfterFirstWrite()
3891
vendor = FirstRejectedStringIOSSHVendor(response, output,
3892
fail_at_write=False)
3893
ssh_params = medium.SSHParams('a host', 'a port', 'a user', 'a pass')
3894
client_medium = medium.SmartSSHClientMedium('base', ssh_params, vendor)
3895
smart_client = client._SmartClient(client_medium, headers={})
3896
smart_request = client._SmartClientRequest(smart_client, 'hello', (),
3897
body_stream=['a', 'b'])
3898
self.assertRaises(errors.ConnectionReset, smart_request._send, 3)
3899
# We connect, and manage to get to the point that we start consuming
3900
# the body stream. The next write fails, so we just stop.
3902
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3903
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3907
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3908
'\x00\x00\x00\x02de' # empty headers
3909
's\x00\x00\x00\tl5:helloe',
3912
def test__send_disabled_retry(self):
3913
debug.debug_flags.add('noretry')
3914
output, vendor, smart_client = self.make_client_with_failing_medium()
3915
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3916
self.assertRaises(errors.ConnectionReset, smart_request._send, 3)
3918
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3919
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3529
3925
class LengthPrefixedBodyDecoder(tests.TestCase):
3531
3927
# XXX: TODO: make accept_reading_trailer invoke translate_response or