summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJeff Forcier <jeff@bitprophet.org>2016-07-25 21:03:49 -0700
committerJeff Forcier <jeff@bitprophet.org>2016-07-25 21:03:49 -0700
commit60878f34c11002bdedb2b5e96ef5836425e8adc3 (patch)
tree44ccdfd47078155f6c7c808f9641bf6ee374a424
parent714595eee85d3db602c2003598eb05d14c56641f (diff)
parente0d6687c9305a8a2cd95093dec638f6fb93a25cb (diff)
Merge branch '1.16' into 1.17
-rw-r--r--paramiko/proxy.py26
-rw-r--r--sites/www/changelog.rst4
2 files changed, 16 insertions, 14 deletions
diff --git a/paramiko/proxy.py b/paramiko/proxy.py
index ca602c4c..d3ae436f 100644
--- a/paramiko/proxy.py
+++ b/paramiko/proxy.py
@@ -38,7 +38,7 @@ class ProxyCommand(ClosingContextManager):
`.Transport` and `.Packetizer` classes. Using this class instead of a
regular socket makes it possible to talk with a Popen'd command that will
proxy traffic between the client and a server hosted in another machine.
-
+
Instances of this class may be used as context managers.
"""
def __init__(self, command_line):
@@ -50,9 +50,9 @@ class ProxyCommand(ClosingContextManager):
the command that should be executed and used as the proxy.
"""
self.cmd = shlsplit(command_line)
- self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE,
+ bufsize=0)
self.timeout = None
- self.buffer = []
def send(self, content):
"""
@@ -77,11 +77,12 @@ class ProxyCommand(ClosingContextManager):
:param int size: how many chars should be read
- :return: the length of the read content, as an `int`
+ :return: the string of bytes read, which may be shorter than requested
"""
try:
+ buffer = b''
start = time.time()
- while len(self.buffer) < size:
+ while len(buffer) < size:
select_timeout = None
if self.timeout is not None:
elapsed = (time.time() - start)
@@ -92,16 +93,13 @@ class ProxyCommand(ClosingContextManager):
r, w, x = select(
[self.process.stdout], [], [], select_timeout)
if r and r[0] == self.process.stdout:
- b = os.read(
- self.process.stdout.fileno(), size - len(self.buffer))
- # Store in class-level buffer for persistence across
- # timeouts; this makes us act more like a real socket
- # (where timeouts don't actually drop data.)
- self.buffer.extend(b)
- result = ''.join(self.buffer)
- self.buffer = []
- return result
+ buffer += os.read(
+ self.process.stdout.fileno(), size - len(buffer))
+ return buffer
except socket.timeout:
+ if buffer:
+ # Don't raise socket.timeout, return partial result instead
+ return buffer
raise # socket.timeout is a subclass of IOError
except IOError as e:
raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst
index ee179526..a31cbd54 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -2,6 +2,10 @@
Changelog
=========
+* :bug:`673` (via :issue:`681`) Fix protocol banner read errors
+ (``SSHException``) which would occasionally pop up when using
+ ``ProxyCommand`` gatewaying. Thanks to ``@Depado`` for the initial report and
+ Paul Kapp for the fix.
* :bug:`774 (1.16+)` Add a ``_closed`` private attribute to
`~paramiko.channel.Channel` objects so that they continue functioning when
used as proxy sockets under Python 3 (e.g. as ``direct-tcpip`` gateways for