summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDelano Mandelbaum <delano@solutious.com>2014-12-02 09:31:55 -0800
committerDelano Mandelbaum <delano@solutious.com>2014-12-02 09:31:55 -0800
commit6ce8667acc8ca47a48620aca96dfd25b45a7c3ab (patch)
tree46be27c5587bdd5dd3454b338c11796ef7c76cc8
parentad6a459121314bf2b71a12bf2bb18a1bcf3981d9 (diff)
parente978c1d00184db6f6b5d2e65d00286b34a1cceec (diff)
downloadnet-ssh-6ce8667acc8ca47a48620aca96dfd25b45a7c3ab.tar.gz
Merge pull request #200 from net-ssh/keepalive-maxcount
Implemented keepalive_maxcount
-rw-r--r--lib/net/ssh.rb3
-rw-r--r--lib/net/ssh/connection/keepalive.rb47
-rw-r--r--lib/net/ssh/connection/session.rb29
-rw-r--r--lib/net/ssh/errors.rb4
-rw-r--r--test/connection/test_session.rb21
5 files changed, 76 insertions, 28 deletions
diff --git a/lib/net/ssh.rb b/lib/net/ssh.rb
index d9a1f57..d4eb307 100644
--- a/lib/net/ssh.rb
+++ b/lib/net/ssh.rb
@@ -63,7 +63,7 @@ module Net
VALID_OPTIONS = [
:auth_methods, :bind_address, :compression, :compression_level, :config,
:encryption, :forward_agent, :hmac, :host_key,
- :keepalive, :keepalive_interval, :kex, :keys, :key_data,
+ :keepalive, :keepalive_interval, :keepalive_maxcount, :kex, :keys, :key_data,
:languages, :logger, :paranoid, :password, :port, :proxy,
:rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
:global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
@@ -130,6 +130,7 @@ module Net
# the keepalive_interval seconds. Defaults to +false+.
# :keepalive_interval => the interval seconds for keepalive.
# Defaults to +300+ seconds.
+ # :keepalive_countmax => the maximun number of keepalive packet miss allowed.
# * :kex => the key exchange algorithm (or algorithms) to use
# * :keys => an array of file names of private keys to use for publickey
# and hostbased authentication
diff --git a/lib/net/ssh/connection/keepalive.rb b/lib/net/ssh/connection/keepalive.rb
new file mode 100644
index 0000000..6b0daa8
--- /dev/null
+++ b/lib/net/ssh/connection/keepalive.rb
@@ -0,0 +1,47 @@
+module Net; module SSH; module Connection
+
+module Keepalive
+ # Default IO.select timeout threshold
+ DEFAULT_IO_SELECT_TIMEOUT = 300
+
+ def initialize_keepalive
+ @last_keepalive_sent_at = nil
+ @unresponded_keepalive_count = 0
+ end
+
+ def keepalive_enabled?
+ options[:keepalive]
+ end
+
+ def keepalive_interval
+ options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT
+ end
+
+ def should_send_keepalive?
+ return false unless keepalive_enabled?
+ return true unless @last_keepalive_sent_at
+ Time.now - @last_keepalive_sent_at >= keepalive_interval
+ end
+
+ def keepalive_maxcount
+ (options[:keepalive_maxcount] || 3).to_i
+ end
+
+ def send_keepalive_as_needed(readers, writers)
+ return unless readers.nil? && writers.nil?
+ return unless should_send_keepalive?
+ info { "sending keepalive #{@unresponded_keepalive_count}" }
+
+ @unresponded_keepalive_count += 1
+ send_global_request("keepalive@openssh.com") { |success, response|
+ @unresponded_keepalive_count = 0
+ }
+ if keepalive_maxcount > 0 && @unresponded_keepalive_count > keepalive_maxcount
+ error { "Timeout, server #{host} not responding. Missed #{@unresponded_keepalive_count} timeouts." }
+ raise Net::SSH::Timeout, "Timeout, server #{host} not responding."
+ end
+ @last_keepalive_sent_at = Time.now
+ end
+end
+
+end; end; end \ No newline at end of file
diff --git a/lib/net/ssh/connection/session.rb b/lib/net/ssh/connection/session.rb
index 9be74e8..3137514 100644
--- a/lib/net/ssh/connection/session.rb
+++ b/lib/net/ssh/connection/session.rb
@@ -3,6 +3,7 @@ require 'net/ssh/ruby_compat'
require 'net/ssh/connection/channel'
require 'net/ssh/connection/constants'
require 'net/ssh/service/forward'
+require 'net/ssh/connection/keepalive'
module Net; module SSH; module Connection
@@ -23,10 +24,7 @@ module Net; module SSH; module Connection
# ssh.exec! "/etc/init.d/some_process start"
# end
class Session
- include Constants, Loggable
-
- # Default IO.select timeout threshold
- DEFAULT_IO_SELECT_TIMEOUT = 300
+ include Constants, Loggable, Keepalive
# The underlying transport layer abstraction (see Net::SSH::Transport::Session).
attr_reader :transport
@@ -79,7 +77,7 @@ module Net; module SSH; module Connection
@max_pkt_size = (options.has_key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000)
@max_win_size = (options.has_key?(:max_win_size) ? options[:max_win_size] : 0x20000)
- @last_keepalive_sent_at = nil
+ initialize_keepalive
end
# Retrieves a custom property from this instance. This can be used to
@@ -598,29 +596,10 @@ module Net; module SSH; module Connection
def io_select_wait(wait)
return wait if wait
- return wait unless options[:keepalive]
+ return wait unless keepalive_enabled?
keepalive_interval
end
- def keepalive_interval
- options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT
- end
-
- def should_send_keepalive?
- return false unless options[:keepalive]
- return true unless @last_keepalive_sent_at
- Time.now - @last_keepalive_sent_at >= keepalive_interval
- end
-
- def send_keepalive_as_needed(readers, writers)
- return unless readers.nil? && writers.nil?
- return unless should_send_keepalive?
- info { "sending keepalive" }
- msg = Net::SSH::Buffer.from(:byte, Packet::IGNORE, :string, "keepalive")
- send_message(msg)
- @last_keepalive_sent_at = Time.now
- end
-
MAP = Constants.constants.inject({}) do |memo, name|
value = const_get(name)
next unless Integer === value
diff --git a/lib/net/ssh/errors.rb b/lib/net/ssh/errors.rb
index 8f5a136..910c961 100644
--- a/lib/net/ssh/errors.rb
+++ b/lib/net/ssh/errors.rb
@@ -14,6 +14,10 @@ module Net; module SSH
# unexpectedly.
class Disconnect < Exception; end
+ # This exception is raised when the remote host has disconnected/
+ # timeouted unexpectedly.
+ class Timeout < Disconnect; end
+
# This exception is primarily used internally, but if you have a channel
# request handler (see Net::SSH::Connection::Channel#on_request) that you
# want to fail in such a way that the server knows it failed, you can
diff --git a/test/connection/test_session.rb b/test/connection/test_session.rb
index 8af6cd3..268afb9 100644
--- a/test/connection/test_session.rb
+++ b/test/connection/test_session.rb
@@ -366,12 +366,29 @@ module Connection
def test_process_should_call_enqueue_message_if_io_select_timed_out
timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT
options = { :keepalive => true }
- expected_packet = P(:byte, Net::SSH::Packet::IGNORE, :string, "keepalive")
+ expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true)
IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
- transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
+ transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
session(options).process
end
+ def test_process_should_raise_if_keepalives_not_answered
+ timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT
+ options = { :keepalive => true, :keepalive_interval => 300, :keepalive_maxcount => 3 }
+ expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true)
+ [1,2,3].each do |i|
+ Time.stubs(:now).returns(i*300)
+ IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
+ transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
+ session(options).process
+ end
+
+ Time.stubs(:now).returns(4*300)
+ IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
+ transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
+ assert_raises(Net::SSH::Timeout) { session(options).process }
+ end
+
def test_process_should_not_call_enqueue_message_unless_io_select_timed_out
timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT
options = { :keepalive => true }