diff options
-rw-r--r-- | CHANGES.txt | 1 | ||||
-rw-r--r-- | lib/net/ssh/proxy/command.rb | 26 | ||||
-rw-r--r-- | test/proxy/test_command.rb | 23 | ||||
-rw-r--r-- | test/start/test_connection.rb | 9 |
4 files changed, 48 insertions, 11 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 5cede5e..ab6a889 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ === 5.0.0.beta1 + * Don't leave proxy command as zombie on timeout [DimitriosLisenko, #560] * Use OpenSSL for aes*-ctr for up to 5x throughput improvement [Miklós Fazekas, Harald Sitter, #570] * Optimize slice! usage in CTR for up to 2x throughput improvement [Harald Sitter, #569] * Replace RbNaCl dependency with ed25519 gem [Tony Arcieri ,#563] diff --git a/lib/net/ssh/proxy/command.rb b/lib/net/ssh/proxy/command.rb index 4c9eb62..6e362bd 100644 --- a/lib/net/ssh/proxy/command.rb +++ b/lib/net/ssh/proxy/command.rb @@ -23,6 +23,9 @@ module Net; module SSH; module Proxy # The command line for the session attr_reader :command_line + # Timeout in seconds in open, defaults to 60 + attr_accessor :timeout + # Create a new socket factory that tunnels via a command executed # with the user's shell, which is composed from the given command # template. In the command template, `%h' will be substituted by @@ -30,6 +33,7 @@ module Net; module SSH; module Proxy def initialize(command_line_template) @command_line_template = command_line_template @command_line = nil + @timeout = 60 end # Return a new socket connected to the given host and port via the @@ -56,13 +60,17 @@ module Net; module SSH; module Proxy } begin io = IO.popen(command_line, "r+") - if result = IO.select([io], nil, [io], 60) - if result.last.any? || io.eof? - io.close - raise "command failed" + begin + if result = IO.select([io], nil, [io], @timeout) + if result.last.any? || io.eof? + raise "command failed" + end + else + raise "command timed out" end - else - raise "command timed out" + rescue + close_on_error(io) + raise end rescue => e raise ConnectError, "#{e}: #{command_line}" @@ -104,6 +112,12 @@ module Net; module SSH; module Proxy end io end + + def close_on_error(io) + Process.kill('TERM', io.pid) + Thread.new { io.close } + end + end end; end; end diff --git a/test/proxy/test_command.rb b/test/proxy/test_command.rb new file mode 100644 index 0000000..22dd17e --- /dev/null +++ b/test/proxy/test_command.rb @@ -0,0 +1,23 @@ +require_relative '../common' +require 'net/ssh' +require 'net/ssh/proxy/command' + +module NetSSH + class TestProxy < NetSSHTest + unless Gem.win_platform? + def test_process_is_stopped_on_timeout + proxy = Net::SSH::Proxy::Command.new('sleep 10') + proxy.timeout = 2 + host = 'foo' + port = 1 + assert_raises Net::SSH::Proxy::ConnectError do + proxy.open(host, port) + end + sleep 0.1 + assert_raises Errno::ECHILD do + Process.waitpid(0, Process::WNOHANG) + end + end + end + end +end diff --git a/test/start/test_connection.rb b/test/start/test_connection.rb index eaf826e..47546ee 100644 --- a/test/start/test_connection.rb +++ b/test/start/test_connection.rb @@ -1,10 +1,10 @@ -require 'common' +require_relative '../common' require 'net/ssh' module NetSSH class TestConnection < NetSSHTest attr_reader :connection_session - + def setup authentication_session = mock('authentication_session') authentication_session.stubs(:authenticate).returns(true) @@ -40,15 +40,14 @@ module NetSSH # We aren't interested in the exception end end - + def test_return_value_is_returned @connection_session.expects(:closed?).returns(false) @connection_session.expects(:close).once - + val = 1 retval = Net::SSH.start('localhost', 'testuser') { val } assert_equal(val, retval) end end end - |