[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-60]

limit read/write requests to 32KB, advertise 32KB max packet size
one of the unit tests was failing because the openssh sftp server was dropping
the connection without any error.  turns out they have a maximum allowed write
size (possibly around 64KB).  the sftp rfcs have a small hint that some servers
may drop read/write requests of greater than 32KB.

so, all reads are limited to 32KB, and all writes > 32KB are now chopped up
and sent in 32KB chunks.  this seems to keep openssh happy.

also, we now advertise 32KB max packet size instead of 8KB (the speed
improves a lot), and log when we read/write a packet.  and sftp files are
flushed on seek.
This commit is contained in:
Robey Pointer 2004-06-10 18:08:50 +00:00
parent f0ba3c482e
commit 146417c56c
2 changed files with 25 additions and 9 deletions

View File

@ -15,7 +15,7 @@
# details. # details.
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Foobar; if not, write to the Free Software Foundation, Inc., # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import struct, socket import struct, socket
@ -121,6 +121,11 @@ class SFTPError (Exception):
class SFTPFile (BufferedFile): class SFTPFile (BufferedFile):
# some sftp servers will choke if you send read/write requests larger than
# this size.
MAX_REQUEST_SIZE = 32768
def __init__(self, sftp, handle, mode='r', bufsize=-1): def __init__(self, sftp, handle, mode='r', bufsize=-1):
BufferedFile.__init__(self) BufferedFile.__init__(self)
self.sftp = sftp self.sftp = sftp
@ -143,16 +148,23 @@ class SFTPFile (BufferedFile):
self.sftp._request(CMD_CLOSE, self.handle) self.sftp._request(CMD_CLOSE, self.handle)
def _read(self, size): def _read(self, size):
size = min(size, self.MAX_REQUEST_SIZE)
t, msg = self.sftp._request(CMD_READ, self.handle, long(self._realpos), int(size)) t, msg = self.sftp._request(CMD_READ, self.handle, long(self._realpos), int(size))
if t != CMD_DATA: if t != CMD_DATA:
raise SFTPError('Expected data') raise SFTPError('Expected data')
return msg.get_string() return msg.get_string()
def _write(self, data): def _write(self, data):
t, msg = self.sftp._request(CMD_WRITE, self.handle, long(self._realpos), str(data)) offset = 0
while offset < len(data):
chunk = min(len(data) - offset, self.MAX_REQUEST_SIZE)
t, msg = self.sftp._request(CMD_WRITE, self.handle, long(self._realpos + offset),
str(data[offset : offset + chunk]))
offset += chunk
return len(data) return len(data)
def seek(self, offset, whence=0): def seek(self, offset, whence=0):
self.flush()
if whence == self.SEEK_SET: if whence == self.SEEK_SET:
self._realpos = self._pos = offset self._realpos = self._pos = offset
elif whence == self.SEEK_CUR: elif whence == self.SEEK_CUR:
@ -160,7 +172,7 @@ class SFTPFile (BufferedFile):
self._pos += offset self._pos += offset
else: else:
self._realpos = self._pos = self._get_size() + offset self._realpos = self._pos = self._get_size() + offset
self._rbuffer = self._wbuffer = '' self._rbuffer = ''
def stat(self): def stat(self):
""" """

View File

@ -15,7 +15,7 @@
# details. # details.
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Foobar; if not, write to the Free Software Foundation, Inc., # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
""" """
@ -162,8 +162,8 @@ class BaseTransport (threading.Thread):
self.channel_counter = 1 self.channel_counter = 1
self.logger = logging.getLogger('paramiko.transport') self.logger = logging.getLogger('paramiko.transport')
self.window_size = 65536 self.window_size = 65536
self.max_packet_size = 8192 self.max_packet_size = 32768
self.ultra_debug = 0 self.ultra_debug = False
self.saved_exception = None self.saved_exception = None
# used for noticing when to re-key: # used for noticing when to re-key:
self.received_bytes = 0 self.received_bytes = 0
@ -792,7 +792,9 @@ class BaseTransport (threading.Thread):
def _send_message(self, data): def _send_message(self, data):
# FIXME: should we check for rekeying here too? # FIXME: should we check for rekeying here too?
# encrypt this sucka # encrypt this sucka
packet = self._build_packet(str(data)) data = str(data)
self._log(DEBUG, 'Write packet $%x, length %d' % (ord(data[0]), len(data)))
packet = self._build_packet(data)
if self.ultra_debug: if self.ultra_debug:
self._log(DEBUG, util.format_binary(packet, 'OUT: ')) self._log(DEBUG, util.format_binary(packet, 'OUT: '))
if self.engine_out != None: if self.engine_out != None:
@ -861,8 +863,10 @@ class BaseTransport (threading.Thread):
self.received_packets_overflow += 1 self.received_packets_overflow += 1
if self.received_packets_overflow >= 20: if self.received_packets_overflow >= 20:
raise SSHException('Remote transport is ignoring rekey requests') raise SSHException('Remote transport is ignoring rekey requests')
return ord(payload[0]), msg cmd = ord(payload[0])
self._log(DEBUG, 'Read packet $%x, length %d' % (cmd, len(payload)))
return cmd, msg
def _set_K_H(self, k, h): def _set_K_H(self, k, h):
"used by a kex object to set the K (root key) and H (exchange hash)" "used by a kex object to set the K (root key) and H (exchange hash)"