Skip to content

Commit 89a08bf

Browse files
committed
Handle remotes closes without eof
Some ssh-server (APC Network Management Card AP9640) close the channel without sending an eof. This is correctly handled by openssh, so NET:SCP should also handle it. Added a test for this case. The changed/added code passes Rubocop with default settings, with the exception of the length of the test name, wich ich intended to be long.
1 parent 8943911 commit 89a08bf

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

lib/net/scp.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,18 @@ def start_command(mode, local, remote, options={}, &callback)
362362
channel[:stack ] = []
363363
channel[:error_string] = ''
364364

365-
channel.on_close { |ch2| send("#{channel[:state]}_state", channel); raise Net::SCP::Error, "SCP did not finish successfully (#{channel[:exit]}): #{channel[:error_string]}" if channel[:exit] != 0 }
365+
channel.on_close do
366+
if channel[:exit].nil? && channel[:state] == :finish
367+
# The remote closed the channel without sending an eof, but
368+
# the transfer was successful, so whe set channel[:exit] to 0
369+
channel[:exit] = 0
370+
end
371+
send("#{channel[:state]}_state", channel)
372+
if channel[:exit] != 0
373+
raise Net::SCP::Error, 'SCP did not finish successfully ' \
374+
"(#{channel[:exit]}): #{channel[:error_string]}"
375+
end
376+
end
366377
channel.on_data { |ch2, data| channel[:buffer].append(data) }
367378
channel.on_extended_data { |ch2, type, data| debug { data.chomp } }
368379
channel.on_request("exit-status") { |ch2, data| channel[:exit] = data.read_long }

test/test_download.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,24 @@ def test_download_directory_should_create_directory_and_files_locally
225225
assert_equal "a" * 1234, file.io.string
226226
end
227227

228+
def test_download_should_work_when_remote_closes_channel_without_eof
229+
file = prepare_file('/path/to/local.txt', 'a' * 1234)
230+
231+
story do |session|
232+
channel = session.opens_channel
233+
channel.sends_exec 'scp -f /path/to/remote.txt'
234+
simple_download(channel)
235+
# Remote closes without sending an eof
236+
channel.gets_close
237+
# We are polite and send an eof & close the channel
238+
channel.sends_eof
239+
channel.sends_close
240+
end
241+
242+
assert_scripted { scp.download!('/path/to/remote.txt', '/path/to/local.txt') }
243+
assert_equal 'a' * 1234, file.io.string
244+
end
245+
228246
private
229247

230248
def simple_download(channel, mode=0666)

0 commit comments

Comments
 (0)