Skip to content

Commit

Permalink
Handle remotes closes without eof
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
robertcheramy committed Feb 26, 2024
1 parent 8943911 commit 89a08bf
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 1 deletion.
13 changes: 12 additions & 1 deletion lib/net/scp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,18 @@ def start_command(mode, local, remote, options={}, &callback)
channel[:stack ] = []
channel[:error_string] = ''

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 }
channel.on_close do
if channel[:exit].nil? && channel[:state] == :finish
# The remote closed the channel without sending an eof, but
# the transfer was successful, so whe set channel[:exit] to 0
channel[:exit] = 0
end
send("#{channel[:state]}_state", channel)
if channel[:exit] != 0
raise Net::SCP::Error, 'SCP did not finish successfully ' \
"(#{channel[:exit]}): #{channel[:error_string]}"
end
end
channel.on_data { |ch2, data| channel[:buffer].append(data) }
channel.on_extended_data { |ch2, type, data| debug { data.chomp } }
channel.on_request("exit-status") { |ch2, data| channel[:exit] = data.read_long }
Expand Down
18 changes: 18 additions & 0 deletions test/test_download.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,24 @@ def test_download_directory_should_create_directory_and_files_locally
assert_equal "a" * 1234, file.io.string
end

def test_download_should_work_when_remote_closes_channel_without_eof
file = prepare_file('/path/to/local.txt', 'a' * 1234)

story do |session|
channel = session.opens_channel
channel.sends_exec 'scp -f /path/to/remote.txt'
simple_download(channel)
# Remote closes without sending an eof
channel.gets_close
# We are polite and send an eof & close the channel
channel.sends_eof
channel.sends_close
end

assert_scripted { scp.download!('/path/to/remote.txt', '/path/to/local.txt') }
assert_equal 'a' * 1234, file.io.string
end

private

def simple_download(channel, mode=0666)
Expand Down

0 comments on commit 89a08bf

Please sign in to comment.