Skip to content

Commit ff559e5

Browse files
committed
Support of close channel without exit-status
And new test - download interrupted
1 parent 89a08bf commit ff559e5

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

lib/net/scp.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -363,16 +363,19 @@ def start_command(mode, local, remote, options={}, &callback)
363363
channel[:error_string] = ''
364364

365365
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
366+
# If we got an exit-status and it is not 0, something went wrong
367+
if !channel[:exit].nil? && channel[:exit] != 0
373368
raise Net::SCP::Error, 'SCP did not finish successfully ' \
374369
"(#{channel[:exit]}): #{channel[:error_string]}"
375370
end
371+
# We may get no exit-status at all as returning a status is only RECOMENDED
372+
# in RFC4254. But if our state is not :finish, something went wrong
373+
if channel[:exit].nil? && channel[:state] != :finish
374+
raise Net::SCP::Error, 'SCP did not finish successfully ' \
375+
'(channel closed before end of transmission)'
376+
end
377+
# At this point, :state can be :finish or :next_item
378+
send("#{channel[:state]}_state", channel)
376379
end
377380
channel.on_data { |ch2, data| channel[:buffer].append(data) }
378381
channel.on_extended_data { |ch2, type, data| debug { data.chomp } }

test/test_download.rb

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,16 @@ 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
228+
def test_download_should_work_when_remote_closes_channel_without_exit_status
229229
file = prepare_file('/path/to/local.txt', 'a' * 1234)
230230

231231
story do |session|
232232
channel = session.opens_channel
233233
channel.sends_exec 'scp -f /path/to/remote.txt'
234234
simple_download(channel)
235-
# Remote closes without sending an eof
235+
# Remote closes without sending an exit-status
236236
channel.gets_close
237-
# We are polite and send an eof & close the channel
237+
# We just send eof and close the channel
238238
channel.sends_eof
239239
channel.sends_close
240240
end
@@ -243,6 +243,35 @@ def test_download_should_work_when_remote_closes_channel_without_eof
243243
assert_equal 'a' * 1234, file.io.string
244244
end
245245

246+
def test_download_should_raise_error_when_remote_closes_channel_before_end
247+
file = prepare_file('/path/to/local.txt', 'a' * 1234)
248+
249+
story do |session|
250+
channel = session.opens_channel
251+
channel.sends_exec 'scp -f /path/to/remote.txt'
252+
channel.sends_ok
253+
channel.gets_data "C%04o 1234 remote.txt\n" % 0666
254+
channel.sends_ok
255+
channel.gets_data 'a' * 500
256+
# We should have received 1234 bytes and \0 but remote closed before the end
257+
channel.gets_close
258+
# We just send eof and close the channel
259+
channel.sends_eof
260+
channel.sends_close
261+
end
262+
263+
error = nil
264+
begin
265+
assert_scripted { scp.download!('/path/to/remote.txt', '/path/to/local.txt') }
266+
rescue => e
267+
error = e
268+
end
269+
270+
assert_equal Net::SCP::Error, error.class
271+
assert_equal 'SCP did not finish successfully (channel closed before end of transmission)',
272+
error.message
273+
end
274+
246275
private
247276

248277
def simple_download(channel, mode=0666)

0 commit comments

Comments
 (0)