When _recv_file is called and underlying paramiko channel is closed (for example because of ssh server crash), the transfer hangs forever because paramiko's chan.recv function returns empty string when the channel is closed (this behaviour is also documented in paramiko Channel.recv function docs:
def recv(self, nbytes):
"""
Receive data from the channel. The return value is a string
representing the data received. The maximum amount of data to be
received at once is specified by ``nbytes``. If a string of
length zero is returned, the channel stream has closed.
:param int nbytes: maximum number of bytes to read.
:return: received data, as a ``str``/``bytes``.
:raises socket.timeout:
if no data is ready before the timeout set by `settimeout`.
"""
try:
out = self.in_buffer.read(nbytes, self.timeout)
except PipeTimeout:
raise socket.timeout()
Note: if a string of
length zero is returned, the channel stream has closed
scp._recv_file has the following while loop:
while pos < size:
# we have to make sure we don't read the final byte
if size - pos <= buff_size:
buff_size = size - pos
file_hdl.write(chan.recv(buff_size))
pos = file_hdl.tell()
if self._progress:
self._progress(path, size, pos, self.peername)
Which gets an empty string from chan.recv(buff_size) call, writes nothing to file_hdl and therefore pos is never updated.
When _recv_file is called and underlying paramiko channel is closed (for example because of ssh server crash), the transfer hangs forever because paramiko's chan.recv function returns empty string when the channel is closed (this behaviour is also documented in paramiko Channel.recv function docs:
Note: if a string of length zero is returned, the channel stream has closed
scp._recv_file has the following while loop:
Which gets an empty string from chan.recv(buff_size) call, writes nothing to file_hdl and therefore pos is never updated.