Rev 4886: Merge 2.1-client-reconnect-819604 in http://bazaar.launchpad.net/~jameinel/bzr/2.1-all-reconnect-819604
John Arbash Meinel
john at arbash-meinel.com
Wed Sep 12 07:09:01 UTC 2012
At http://bazaar.launchpad.net/~jameinel/bzr/2.1-all-reconnect-819604
------------------------------------------------------------
revno: 4886 [merge]
revision-id: john at arbash-meinel.com-20120912070841-390l0nw5atmd3xlj
parent: john at arbash-meinel.com-20120912070732-8xh7kllfxa6qr3ax
parent: john at arbash-meinel.com-20120911122646-j2x1rxlbgnrkyoox
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.1-all-reconnect-819604
timestamp: Wed 2012-09-12 11:08:41 +0400
message:
Merge 2.1-client-reconnect-819604
modified:
bzrlib/osutils.py osutils.py-20050309040759-eeaff12fbf77ac86
bzrlib/tests/test_osutils.py test_osutils.py-20051201224856-e48ee24c12182989
-------------- next part --------------
=== modified file 'bzrlib/osutils.py'
--- a/bzrlib/osutils.py 2010-05-27 04:00:01 +0000
+++ b/bzrlib/osutils.py 2012-09-11 12:26:46 +0000
@@ -40,6 +40,7 @@
rmtree,
)
import signal
+import socket
import subprocess
import tempfile
from tempfile import (
@@ -1929,6 +1930,20 @@
return socket.gethostname().decode(get_user_encoding())
+# We must not read/write any more than 64k at a time from/to a socket so we
+# don't risk "no buffer space available" errors on some platforms. Windows in
+# particular is likely to throw WSAECONNABORTED or WSAENOBUFS if given too much
+# data at once.
+MAX_SOCKET_CHUNK = 64 * 1024
+
+_end_of_stream_errors = [errno.ECONNRESET, errno.EPIPE, errno.EINVAL]
+for _eno in ['WSAECONNRESET', 'WSAECONNABORTED']:
+ _eno = getattr(errno, _eno, None)
+ if _eno is not None:
+ _end_of_stream_errors.append(_eno)
+del _eno
+
+
def recv_all(socket, bytes):
"""Receive an exact number of bytes.
@@ -1948,21 +1963,37 @@
return b
-def send_all(socket, bytes, report_activity=None):
+def send_all(sock, bytes, report_activity=None):
"""Send all bytes on a socket.
- Regular socket.sendall() can give socket error 10053 on Windows. This
- implementation sends no more than 64k at a time, which avoids this problem.
+ Breaks large blocks in smaller chunks to avoid buffering limitations on
+ some platforms, and catches EINTR which may be thrown if the send is
+ interrupted by a signal.
+
+ This is preferred to socket.sendall(), because it avoids portability bugs
+ and provides activity reporting.
:param report_activity: Call this as bytes are read, see
Transport._report_activity
"""
- chunk_size = 2**16
- for pos in xrange(0, len(bytes), chunk_size):
- block = bytes[pos:pos+chunk_size]
- if report_activity is not None:
- report_activity(len(block), 'write')
- until_no_eintr(socket.sendall, block)
+ sent_total = 0
+ byte_count = len(bytes)
+ while sent_total < byte_count:
+ try:
+ sent = sock.send(buffer(bytes, sent_total, MAX_SOCKET_CHUNK))
+ except (socket.error, IOError), e:
+ if e.args[0] in _end_of_stream_errors:
+ raise errors.ConnectionReset(
+ "Error trying to write to socket", e)
+ if e.args[0] != errno.EINTR:
+ raise
+ else:
+ if sent == 0:
+ raise errors.ConnectionReset('Sending to %s returned 0 bytes'
+ % (sock,))
+ sent_total += sent
+ if report_activity is not None:
+ report_activity(sent, 'write')
def dereference_path(path):
=== modified file 'bzrlib/tests/test_osutils.py'
--- a/bzrlib/tests/test_osutils.py 2010-11-30 20:42:42 +0000
+++ b/bzrlib/tests/test_osutils.py 2012-09-11 12:26:46 +0000
@@ -801,6 +801,45 @@
self.assertEqual(None, osutils.safe_file_id(None))
+class TestSendAll(tests.TestCase):
+
+ def test_send_with_disconnected_socket(self):
+ class DisconnectedSocket(object):
+ def __init__(self, err):
+ self.err = err
+ def send(self, content):
+ raise self.err
+ def close(self):
+ pass
+ # All of these should be treated as ConnectionReset
+ errs = []
+ for err_cls in (IOError, socket.error):
+ for errnum in osutils._end_of_stream_errors:
+ errs.append(err_cls(errnum))
+ for err in errs:
+ sock = DisconnectedSocket(err)
+ self.assertRaises(errors.ConnectionReset,
+ osutils.send_all, sock, 'some more content')
+
+ def test_send_with_no_progress(self):
+ # See https://bugs.launchpad.net/bzr/+bug/1047309
+ # It seems that paramiko can get into a state where it doesn't error,
+ # but it returns 0 bytes sent for requests over and over again.
+ class NoSendingSocket(object):
+ def __init__(self):
+ self.call_count = 0
+ def send(self, bytes):
+ self.call_count += 1
+ if self.call_count > 100:
+ # Prevent the test suite from hanging
+ raise RuntimeError('too many calls')
+ return 0
+ sock = NoSendingSocket()
+ self.assertRaises(errors.ConnectionReset,
+ osutils.send_all, sock, 'content')
+ self.assertEqual(1, sock.call_count)
+
+
class TestWin32Funcs(tests.TestCase):
"""Test that _win32 versions of os utilities return appropriate paths."""
More information about the bazaar-commits
mailing list