summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Forcier <jeff@bitprophet.org>2014-02-12 17:16:34 -0800
committerJeff Forcier <jeff@bitprophet.org>2014-02-12 17:16:34 -0800
commit58489c893e3f62947ee8235c2a07fc5465949aeb (patch)
tree3819f0d3c97d4e6d66c9e370f6245c152058313b
parent5b6059c4bd1b75121023b50003f5191345ef789c (diff)
downloadparamiko-58489c893e3f62947ee8235c2a07fc5465949aeb.tar.gz
Potentially horrible attempt at manual subprocess-read timeouts
-rw-r--r--paramiko/proxy.py28
1 files changed, 24 insertions, 4 deletions
diff --git a/paramiko/proxy.py b/paramiko/proxy.py
index 218b76e2..abdd157c 100644
--- a/paramiko/proxy.py
+++ b/paramiko/proxy.py
@@ -20,10 +20,13 @@
L{ProxyCommand}.
"""
+from datetime import datetime
import os
from shlex import split as shlsplit
import signal
from subprocess import Popen, PIPE
+from select import select
+import socket
from paramiko.ssh_exception import ProxyCommandFailure
@@ -48,6 +51,7 @@ class ProxyCommand(object):
"""
self.cmd = shlsplit(command_line)
self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ self.timeout = None
def send(self, content):
"""
@@ -78,7 +82,25 @@ class ProxyCommand(object):
@rtype: int
"""
try:
- return os.read(self.process.stdout.fileno(), size)
+ start = datetime.now()
+ read = []
+ while len(read) < size:
+ if self.timeout is not None:
+ elapsed = (datetime.now() - start).microseconds
+ timeout = self.timeout * 1000 * 1000 # to microseconds
+ # Unsure why the 'default' timeout is too short here -
+ # causes us to raise before e.g. the SSH banner is read,
+ # probably generally awful for slow connections.
+ # Try inflating it some.
+ timeout = timeout * 2
+ if elapsed >= timeout:
+ raise socket.timeout()
+ r, w, x = select([self.process.stdout], [], [], 0.0)
+ if r and r[0] == self.process.stdout:
+ b = os.read(self.process.stdout.fileno(), 1)
+ read.append(b)
+ result = ''.join(read)
+ return result
except IOError, e:
raise BadProxyCommand(' '.join(self.cmd), e.strerror)
@@ -86,6 +108,4 @@ class ProxyCommand(object):
os.kill(self.process.pid, signal.SIGTERM)
def settimeout(self, timeout):
- # Timeouts are meaningless for this implementation, but are part of the
- # spec, so must be present.
- pass
+ self.timeout = timeout