diff options
author | Miklos Fazekas <mfazekas@szemafor.com> | 2015-12-02 19:08:15 +0100 |
---|---|---|
committer | Miklos Fazekas <mfazekas@szemafor.com> | 2015-12-02 19:10:52 +0100 |
commit | 80fc15d3e7ed3a44c8b7e0b98f48e04dfa85163c (patch) | |
tree | a2256ee49da20ffc3e32190edf145de743e6ea28 | |
parent | a15a18d45ba901415992f0173b65bcfabf22227c (diff) | |
download | net-ssh-80fc15d3e7ed3a44c8b7e0b98f48e04dfa85163c.tar.gz |
Moved test_forward to intgration tests
-rw-r--r-- | test/README.txt | 22 | ||||
-rw-r--r-- | test/integration/common.rb | 6 | ||||
-rw-r--r-- | test/integration/test_forward.rb | 330 | ||||
-rw-r--r-- | test/integration/test_id_rsa_keys.rb | 6 | ||||
-rw-r--r-- | test/manual/test_forward.rb | 285 |
5 files changed, 336 insertions, 313 deletions
diff --git a/test/README.txt b/test/README.txt index f03f759..c767974 100644 --- a/test/README.txt +++ b/test/README.txt @@ -16,25 +16,3 @@ INTEGRATION TESTS brew install ansible ; ansible-galaxy install rvm_io.rvm1-ruby ; vagrant up ; vagrant ssh cd /net-ssh ; rake integration-test - -PORT FORWARDING TESTS - - ruby -Ilib -Itest -rrubygems test/manual/test_forward.rb - -test_forward.rb must be run separately from the test suite because -it requires authorizing your public SSH keys on you localhost. - -If you already have keys you can do this: - - cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys - -If you don't have keys see: - - http://kimmo.suominen.com/docs/ssh/#ssh-keygen - -You should now be able to login to your localhost with out -bring prompted for a password: - - ssh localhost - --Delano diff --git a/test/integration/common.rb b/test/integration/common.rb index 706e647..42ad37f 100644 --- a/test/integration/common.rb +++ b/test/integration/common.rb @@ -14,6 +14,12 @@ module IntegrationTestHelpers raise "Command: #{command} failed:#{status.exitstatus}" unless res end + def tmpdir(&block) + Dir.mktmpdir do |dir| + yield(dir) + end + end + def set_authorized_key(user,pubkey) authorized_key = "/home/#{user}/.ssh/authorized_keys" sh "sudo cp #{pubkey} #{authorized_key}" diff --git a/test/integration/test_forward.rb b/test/integration/test_forward.rb new file mode 100644 index 0000000..24db2da --- /dev/null +++ b/test/integration/test_forward.rb @@ -0,0 +1,330 @@ +# $ ruby -Ilib -Itest -rrubygems test/manual/test_forward.rb + +# Tests for the following patch: +# +# http://github.com/net-ssh/net-ssh/tree/portfwfix +# +# It fixes 3 issues, regarding closing forwarded ports: +# +# 1.) if client closes a forwarded connection, but the server is reading, net-ssh terminates with IOError socket closed. +# 2.) if client force closes (RST) a forwarded connection, but server is reading, net-ssh terminates with +# 3.) if server closes the sending side, the on_eof is not handled. +# +# More info: +# +# http://net-ssh.lighthouseapp.com/projects/36253/tickets/7 + +require 'common' +require 'net/ssh/buffer' +require 'net/ssh' +require 'timeout' +require 'tempfile' + +class TestForward < Test::Unit::TestCase + include IntegrationTestHelpers + + def localhost + 'localhost' + end + + def user + 'net_ssh_1' + end + + def ssh_start_params + [localhost ,user , {:keys => @key_id_rsa, :verbose => :debug}] + end + + def setup_ssh_env(&block) + tmpdir do |dir| + @key_id_rsa = "#{dir}/id_rsa" + sh "rm -rf #{@key_id_rsa} #{@key_id_rsa}.pub" + sh "ssh-keygen -f #{@key_id_rsa} -t rsa -N ''" + set_authorized_key(user,"#{@key_id_rsa}.pub") + yield + end + end + + def start_server_sending_lot_of_data(exceptions) + server = TCPServer.open(0) + Thread.start do + loop do + Thread.start(server.accept) do |client| + begin + 10000.times do |i| + client.puts "item#{i}" + end + client.close + rescue + exceptions << $! + raise + end + end + end + end + return server + end + + def start_server_closing_soon(exceptions=nil) + server = TCPServer.open(0) + Thread.start do + loop do + Thread.start(server.accept) do |client| + begin + client.recv(1024) + client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii")) + client.close + rescue + exceptions << $! + raise + end + end + end + end + return server + end + + def test_in_file_no_password + setup_ssh_env do + ret = Net::SSH.start(*ssh_start_params) do |ssh| + #ret = Net::SSH.start("localhost", "net_ssh_1", {keys: @key_id_rsa}) do |ssh| + ssh.exec! 'echo "hello from:$USER"' + end + assert_equal "hello from:net_ssh_1\n", ret + end + end + + def test_local_ephemeral_port_should_work_correctly + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + assigned_port = session.forward.local(0, localhost, 22) + assert_not_nil assigned_port + assert_operator assigned_port, :>, 0 + end + end + end + + def test_remote_ephemeral_port_should_work_correctly + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + session.forward.remote(22, localhost, 0, localhost) + session.loop { !(session.forward.active_remotes.length > 0) } + assigned_port = session.forward.active_remotes.first[0] + assert_not_nil assigned_port + assert_operator assigned_port, :>, 0 + end + end + end + + def test_remote_callback_should_fire + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + got_port = nil + session.forward.remote(22, localhost, 0, localhost) do |port| + got_port = port + end + session.loop { !(session.forward.active_remotes.length > 0) } + assert_operator session.forward.active_remote_destinations.length, :==, 1 + assert_operator session.forward.active_remote_destinations.keys.first, :==, [ 22, localhost ] + assert_operator session.forward.active_remote_destinations.values.first, :==, [ got_port, localhost ] + assert_operator session.forward.active_remotes.first, :==, [ got_port, localhost ] + assigned_port = session.forward.active_remotes.first[0] + assert_operator got_port, :==, assigned_port + assert_not_nil assigned_port + assert_operator assigned_port, :>, 0 + end + end + end + + def test_remote_callback_should_fire_on_error_and_still_throw_exception + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + session.forward.remote(22, localhost, 22, localhost) do |port| + assert_operator port, :==, :error + end + end + assert_raises(Net::SSH::Exception) do + session.loop { true } + end + end + end + + def test_remote_callback_should_fire_on_error_but_not_throw_exception_if_asked_not_to + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + + assert_nothing_raised do + got_port = nil + session.forward.remote(22, localhost, 22, localhost) do |port| + assert_operator port, :==, :error + got_port = port + :no_exception + end + session.loop { !got_port } + assert_operator got_port, :==, :error + assert_operator session.forward.active_remotes.length, :==, 0 + end + end + end + + def test_loop_should_not_abort_when_local_side_of_forward_is_closed + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + server_exc = Queue.new + server = start_server_sending_lot_of_data(server_exc) + remote_port = server.addr[1] + local_port = 0 # request ephemeral port + session.forward.local(local_port, localhost, remote_port) + client_done = Queue.new + Thread.start do + begin + client = TCPSocket.new(localhost, local_port) + client.recv(1024) + client.close + sleep(0.2) + ensure + client_done << true + end + end + session.loop(0.1) { client_done.empty? } + assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty? + end + end + + def test_loop_should_not_abort_when_local_side_of_forward_is_reset + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + server_exc = Queue.new + server = start_server_sending_lot_of_data(server_exc) + remote_port = server.addr[1] + local_port = 0 # request ephemeral port + session.forward.local(local_port, localhost, remote_port) + client_done = Queue.new + Thread.start do + begin + client = TCPSocket.new(localhost, local_port) + client.recv(1024) + client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii")) + client.close + sleep(0.1) + ensure + client_done << true + end + end + session.loop(0.1) { client_done.empty? } + assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty? + end + end + + def create_local_socket(&blk) + tempfile = Tempfile.new("net_ssh_forward_test") + path = tempfile.path + tempfile.delete + yield UNIXServer.open(path) + File.delete(path) + end if defined?(UNIXServer) + + def test_forward_local_unix_socket_to_remote_port + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + server_exc = Queue.new + server = start_server_sending_lot_of_data(server_exc) + remote_port = server.addr[1] + client_data = nil + + create_local_socket do |local_socket| + session.forward.local(local_socket, localhost, remote_port) + client_done = Queue.new + + Thread.start do + begin + client = UNIXSocket.new(local_socket.path) + client_data = client.recv(1024) + client.close + sleep(0.2) + ensure + client_done << true + end + end + + session.loop(0.1) { client_done.empty? } + end + + assert_not_nil(client_data, "client should have received data") + assert(client_data.match(/item\d/), 'client should have received the string item') + end + end if defined?(UNIXSocket) + + def test_loop_should_not_abort_when_server_side_of_forward_is_closed + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + server = start_server_closing_soon + remote_port = server.addr[1] + local_port = 0 # request ephemeral port + session.forward.local(local_port, localhost, remote_port) + client_done = Queue.new + Thread.start do + begin + client = TCPSocket.new(localhost, local_port) + 1.times do |i| + client.puts "item#{i}" + end + client.close + sleep(0.1) + ensure + client_done << true + end + end + session.loop(0.1) { client_done.empty? } + end + end + + def start_server + server = TCPServer.open(0) + Thread.start do + loop do + Thread.start(server.accept) do |client| + yield(client) + end + end + end + return server + end + + def test_server_eof_should_be_handled + setup_ssh_env do + session = Net::SSH.start(*ssh_start_params) + server = start_server do |client| + client.write "This is a small message!" + client.close + end + client_done = Queue.new + client_exception = Queue.new + client_data = Queue.new + remote_port = server.addr[1] + local_port = session.forward.local(0, localhost, remote_port) + Thread.start do + begin + client = TCPSocket.new(localhost, local_port) + data = client.read(4096) + client.close + client_done << data + rescue + client_done << $! + end + end + timeout(5) do + session.loop(0.1) { client_done.empty? } + assert_equal "This is a small message!", client_done.pop + end + end + end +end diff --git a/test/integration/test_id_rsa_keys.rb b/test/integration/test_id_rsa_keys.rb index 572cf7c..f43c005 100644 --- a/test/integration/test_id_rsa_keys.rb +++ b/test/integration/test_id_rsa_keys.rb @@ -10,12 +10,6 @@ require 'net/ssh' class TestIDRSAPKeys < Test::Unit::TestCase include IntegrationTestHelpers - def tmpdir(&block) - Dir.mktmpdir do |dir| - yield(dir) - end - end - def test_in_file_no_password tmpdir do |dir| sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub" diff --git a/test/manual/test_forward.rb b/test/manual/test_forward.rb deleted file mode 100644 index 7e864e2..0000000 --- a/test/manual/test_forward.rb +++ /dev/null @@ -1,285 +0,0 @@ -# $ ruby -Ilib -Itest -rrubygems test/manual/test_forward.rb - -# Tests for the following patch: -# -# http://github.com/net-ssh/net-ssh/tree/portfwfix -# -# It fixes 3 issues, regarding closing forwarded ports: -# -# 1.) if client closes a forwarded connection, but the server is reading, net-ssh terminates with IOError socket closed. -# 2.) if client force closes (RST) a forwarded connection, but server is reading, net-ssh terminates with -# 3.) if server closes the sending side, the on_eof is not handled. -# -# More info: -# -# http://net-ssh.lighthouseapp.com/projects/36253/tickets/7 - -require 'common' -require 'net/ssh/buffer' -require 'net/ssh' -require 'timeout' -require 'tempfile' - -class TestForward < Test::Unit::TestCase - - def localhost - 'localhost' - end - - def ssh_start_params - [localhost ,ENV['USER'], {:keys => "~/.ssh/id_rsa", :verbose => :debug}] - end - - def start_server_sending_lot_of_data(exceptions) - server = TCPServer.open(0) - Thread.start do - loop do - Thread.start(server.accept) do |client| - begin - 10000.times do |i| - client.puts "item#{i}" - end - client.close - rescue - exceptions << $! - raise - end - end - end - end - return server - end - - def start_server_closing_soon(exceptions=nil) - server = TCPServer.open(0) - Thread.start do - loop do - Thread.start(server.accept) do |client| - begin - client.recv(1024) - client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii")) - client.close - rescue - exceptions << $! - raise - end - end - end - end - return server - end - - def test_local_ephemeral_port_should_work_correctly - session = Net::SSH.start(*ssh_start_params) - - assert_nothing_raised do - assigned_port = session.forward.local(0, localhost, 22) - assert_not_nil assigned_port - assert_operator assigned_port, :>, 0 - end - end - - def test_remote_ephemeral_port_should_work_correctly - session = Net::SSH.start(*ssh_start_params) - - assert_nothing_raised do - session.forward.remote(22, localhost, 0, localhost) - session.loop { !(session.forward.active_remotes.length > 0) } - assigned_port = session.forward.active_remotes.first[0] - assert_not_nil assigned_port - assert_operator assigned_port, :>, 0 - end - end - - def test_remote_callback_should_fire - session = Net::SSH.start(*ssh_start_params) - - assert_nothing_raised do - got_port = nil - session.forward.remote(22, localhost, 0, localhost) do |port| - got_port = port - end - session.loop { !(session.forward.active_remotes.length > 0) } - assert_operator session.forward.active_remote_destinations.length, :==, 1 - assert_operator session.forward.active_remote_destinations.keys.first, :==, [ 22, localhost ] - assert_operator session.forward.active_remote_destinations.values.first, :==, [ got_port, localhost ] - assert_operator session.forward.active_remotes.first, :==, [ got_port, localhost ] - assigned_port = session.forward.active_remotes.first[0] - assert_operator got_port, :==, assigned_port - assert_not_nil assigned_port - assert_operator assigned_port, :>, 0 - end - end - - def test_remote_callback_should_fire_on_error_and_still_throw_exception - session = Net::SSH.start(*ssh_start_params) - - assert_nothing_raised do - session.forward.remote(22, localhost, 22, localhost) do |port| - assert_operator port, :==, :error - end - end - assert_raises(Net::SSH::Exception) do - session.loop { true } - end - end - - def test_remote_callback_should_fire_on_error_but_not_throw_exception_if_asked_not_to - session = Net::SSH.start(*ssh_start_params) - - assert_nothing_raised do - got_port = nil - session.forward.remote(22, localhost, 22, localhost) do |port| - assert_operator port, :==, :error - got_port = port - :no_exception - end - session.loop { !got_port } - assert_operator port, :==, :error - assert_operator session.forward.active_remotes.length, :==, 0 - end - end - - def test_loop_should_not_abort_when_local_side_of_forward_is_closed - session = Net::SSH.start(*ssh_start_params) - server_exc = Queue.new - server = start_server_sending_lot_of_data(server_exc) - remote_port = server.addr[1] - local_port = 0 # request ephemeral port - session.forward.local(local_port, localhost, remote_port) - client_done = Queue.new - Thread.start do - begin - client = TCPSocket.new(localhost, local_port) - client.recv(1024) - client.close - sleep(0.2) - ensure - client_done << true - end - end - session.loop(0.1) { client_done.empty? } - assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty? - end - - def test_loop_should_not_abort_when_local_side_of_forward_is_reset - session = Net::SSH.start(*ssh_start_params) - server_exc = Queue.new - server = start_server_sending_lot_of_data(server_exc) - remote_port = server.addr[1] - local_port = 0 # request ephemeral port - session.forward.local(local_port, localhost, remote_port) - client_done = Queue.new - Thread.start do - begin - client = TCPSocket.new(localhost, local_port) - client.recv(1024) - client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii")) - client.close - sleep(0.1) - ensure - client_done << true - end - end - session.loop(0.1) { client_done.empty? } - assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty? - end - - def create_local_socket(&blk) - tempfile = Tempfile.new("net_ssh_forward_test") - path = tempfile.path - tempfile.delete - yield UNIXServer.open(path) - File.delete(path) - end if defined?(UNIXServer) - - def test_forward_local_unix_socket_to_remote_port - session = Net::SSH.start(*ssh_start_params) - server_exc = Queue.new - server = start_server_sending_lot_of_data(server_exc) - remote_port = server.addr[1] - client_data = nil - - create_local_socket do |local_socket| - session.forward.local(local_socket, localhost, remote_port) - client_done = Queue.new - - Thread.start do - begin - client = UNIXSocket.new(local_socket.path) - client_data = client.recv(1024) - client.close - sleep(0.2) - ensure - client_done << true - end - end - - session.loop(0.1) { client_done.empty? } - end - - assert_not_nil(client_data, "client should have received data") - assert(client_data.match(/item\d/), 'client should have received the string item') - end if defined?(UNIXSocket) - - def test_loop_should_not_abort_when_server_side_of_forward_is_closed - session = Net::SSH.start(*ssh_start_params) - server = start_server_closing_soon - remote_port = server.addr[1] - local_port = 0 # request ephemeral port - session.forward.local(local_port, localhost, remote_port) - client_done = Queue.new - Thread.start do - begin - client = TCPSocket.new(localhost, local_port) - 1.times do |i| - client.puts "item#{i}" - end - client.close - sleep(0.1) - ensure - client_done << true - end - end - session.loop(0.1) { client_done.empty? } - end - - def start_server - server = TCPServer.open(0) - Thread.start do - loop do - Thread.start(server.accept) do |client| - yield(client) - end - end - end - return server - end - - def test_server_eof_should_be_handled - session = Net::SSH.start(*ssh_start_params) - server = start_server do |client| - client.write "This is a small message!" - client.close - end - client_done = Queue.new - client_exception = Queue.new - client_data = Queue.new - remote_port = server.addr[1] - local_port = session.forward.local(0, localhost, remote_port) - Thread.start do - begin - client = TCPSocket.new(localhost, local_port) - data = client.read(4096) - client.close - client_done << data - rescue - client_done << $! - end - end - timeout(5) do - session.loop(0.1) { client_done.empty? } - assert_equal "This is a small message!", client_done.pop - end - end -end |