em-ssh 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/em-ssh.rb CHANGED
@@ -26,6 +26,8 @@ module EventMachine
26
26
  class ConnectionFailed < SshError; end
27
27
  class ConnectionTimeout < ConnectionFailed; end
28
28
  class ConnectionTerminated < SshError; end
29
+ # The server failed to negotiate an ssh protocol version
30
+ class NegotiationTimeout < ConnectionFailed; end
29
31
 
30
32
 
31
33
  class << self
@@ -90,6 +90,7 @@ module EventMachine
90
90
  def cancel
91
91
  raise "#{@obj} does not have any callbacks for #{@event.inspect}" unless @obj.respond_to?(:callbacks) && @obj.callbacks.respond_to?(:[]) && @obj.callbacks[@event].respond_to?(:delete)
92
92
  @obj.callbacks[@event].delete(self)
93
+ @obj = nil
93
94
  self
94
95
  end # cancel
95
96
  end # class::Callback
@@ -100,11 +100,35 @@ module EventMachine
100
100
  @data = @socket.input
101
101
  end # post_init
102
102
 
103
- # @return
104
103
  def unbind
105
104
  debug("#{self} is unbound")
106
105
  fire(:closed)
107
106
  @closed = true
107
+ @socket && @socket.close
108
+ @socket = nil
109
+ @data = nil
110
+ @error_callback = nil
111
+
112
+ failed_timeout = [@contimeout, @negotimeout, @algotimeout].find { |t| t }
113
+ instance_variables.each do |iv|
114
+ if (t = instance_variable_get(iv)) && (t.is_a?(EM::Timer) || t.is_a?(EM::PeriodicTimer))
115
+ t.cancel
116
+ instance_variable_set(iv, nil)
117
+ end
118
+ end
119
+
120
+ if @algorithms
121
+ @algorithms.instance_variable_set(:@session, nil)
122
+ @algorithms = nil
123
+ end
124
+
125
+ callbacks.values.flatten.each(&:cancel)
126
+ callbacks.clear
127
+ instance_variables.select{|iv| (t = instance_variable_get(iv)) && t.is_a?(EM::Ssh::Callbacks::Callback) }.each do |iv|
128
+ instance_variable_set(iv, nil)
129
+ end
130
+
131
+ fail(NegotiationTimeout.new(@host)) if failed_timeout
108
132
  end
109
133
 
110
134
  def receive_data(data)
@@ -116,7 +140,7 @@ module EventMachine
116
140
  def connection_completed
117
141
  @contimeout.cancel
118
142
  @nocon.cancel
119
- end # connection_completed
143
+ end
120
144
 
121
145
  def initialize(options = {})
122
146
  debug("#{self.class}.new(#{options})")
@@ -129,7 +153,7 @@ module EventMachine
129
153
 
130
154
  begin
131
155
  on(:connected) do |session|
132
- @connected = true
156
+ @connected = true
133
157
  succeed(session, @host)
134
158
  end
135
159
  on(:error) do |e|
@@ -141,11 +165,20 @@ module EventMachine
141
165
  fail(ConnectionFailed.new(@host))
142
166
  close_connection
143
167
  end
168
+
144
169
  @contimeout = EM::Timer.new(@timeout) do
145
170
  fail(ConnectionTimeout.new(@host))
146
171
  close_connection
147
172
  end
148
173
 
174
+ @negotimeout = EM::Timer.new(options[:nego_timeout] || @timeout) do
175
+ fail(NegotiationTimeout.new(@host))
176
+ end
177
+
178
+ @algotimeout = EM::Timer.new(options[:nego_timeout] || @timeout) do
179
+ fail(NegotiationTimeout.new(@host))
180
+ end
181
+
149
182
  @nonego = on(:closed) do
150
183
  fail(ConnectionTerminated.new(@host))
151
184
  close_connection
@@ -160,12 +193,16 @@ module EventMachine
160
193
  @host_key_verifier = select_host_key_verifier(options[:paranoid])
161
194
  @server_version = ServerVersion.new(self)
162
195
  on(:version_negotiated) do
196
+ @negotimeout.cancel
197
+ @negotimeout = nil
163
198
  @data.consume!(@server_version.header.length)
164
199
  @algorithms = Net::SSH::Transport::Algorithms.new(self, options)
165
200
 
166
201
  register_data_handler
167
202
 
168
203
  on_next(:algo_init) do
204
+ @algotimeout.cancel
205
+ @algotimeout = nil
169
206
  auth = AuthenticationSession.new(self, options)
170
207
  user = options.fetch(:user, user)
171
208
  Fiber.new do
@@ -34,6 +34,12 @@ module EventMachine
34
34
  @packet = nil
35
35
  end # initialize(content="")
36
36
 
37
+ def close
38
+ # remove reference to the connection to facilitate Garbage Collection
39
+ return super.tap { @connection = nil } if respond_to?(:super)
40
+ @connection = nil
41
+ end
42
+
37
43
  # Consumes n bytes from the buffer, where n is the current position
38
44
  # unless otherwise specified. This is useful for removing data from the
39
45
  # buffer that has previously been read, when you are expecting more data
@@ -3,7 +3,7 @@ module EventMachine
3
3
  class Session < Net::SSH::Connection::Session
4
4
  include Log
5
5
 
6
- def initialize(transport, options = {})
6
+ def initialize(transport, options={})
7
7
  super(transport, options)
8
8
  register_callbacks
9
9
  end
@@ -28,6 +28,32 @@ module EventMachine
28
28
  transport.send_message(msg)
29
29
  end
30
30
 
31
+ # Override Net::SSH::Connection::Session#close to remove dangling references to
32
+ # EM::Ssh::Connections and EM::Ssh::Sessions. Also directly close local channels
33
+ # if the connection is already closed.
34
+ def close
35
+ if @chan_timer
36
+ @chan_timer.cancel
37
+ remove_instance_variable(:@chan_timer)
38
+ end
39
+
40
+ # Net::SSH::Connection::Session#close doesn't check if the transport is
41
+ # closed. If it is then calling Channel#close will will not close the
42
+ # channel localy and the connection will just spin.
43
+ if transport.closed?
44
+ channels.each do |id, c|
45
+ c.instance_variable_set(:@connection, nil)
46
+ end
47
+ channels.clear
48
+ else
49
+ channels.each { |id, channel| channel.close }
50
+ loop { channels.any? && !transport.closed? }
51
+ end
52
+ # remove the reference to the connection to facilitate Garbage Collection
53
+ transport, @transport = @transport, nil
54
+ @listeners.clear
55
+ transport.close
56
+ end
31
57
 
32
58
  private
33
59
 
@@ -41,12 +67,29 @@ module EventMachine
41
67
  send(MAP[packet.type], packet)
42
68
  end # |packet|
43
69
 
44
- chan_timer = EM.add_periodic_timer(0.01) do
70
+ @chan_timer = EM.add_periodic_timer(0.01) do
45
71
  # we need to check the channel for any data to send and tell it to process any input
46
72
  # at some point we should override Channel#enqueue_pending_output, etc.,.
47
73
  channels.each { |id, channel| channel.process unless channel.closing? }
48
74
  end
49
75
  end # register_callbacks
76
+
77
+ def channel_close(packet)
78
+ channel = channels[packet[:local_id]]
79
+ super(packet).tap do
80
+ # remove the connection reference to facilitate Garbage Collection
81
+ channel.instance_variable_set(:@connection, nil)
82
+ end
83
+ end
84
+
85
+ def channel_open_failure(packet)
86
+ channel = channels[packet[:local_id]]
87
+ super(packet).tap do
88
+ # remove the connection reference to facilitate Garbage Collection
89
+ channel.instance_variable_set(:@connection, nil)
90
+ end
91
+ end
92
+
50
93
  end # class::Session
51
94
  end # class::Ssh
52
95
  end # module::EventMachine
data/lib/em-ssh/shell.rb CHANGED
@@ -93,11 +93,23 @@ module EventMachine
93
93
 
94
94
  # Close the connection to the server and all child shells.
95
95
  # Disconnected shells cannot be split.
96
- def disconnect
96
+ def disconnect(timeout = nil)
97
+ if timeout
98
+ EM::Timer.new(timeout) { disconnect! }
99
+ end
97
100
  close
98
- session && session.close
101
+ @session && @session.close
102
+ disconnect!
99
103
  end
100
104
 
105
+ def disconnect!
106
+ @session = nil
107
+ if @connection
108
+ @connection.close_connection
109
+ @connection = nil
110
+ end
111
+ end # disconnect!
112
+
101
113
  # @return [Boolean] true if the session is still alive
102
114
  def connected?
103
115
  session && !session.closed?
@@ -108,7 +120,7 @@ module EventMachine
108
120
  # Fires :closed event.
109
121
  # @see Callbacks
110
122
  def close
111
- shell.close.tap{ debug("closing") } if shell.active?
123
+ shell.close.tap{ debug("closing") } if shell && shell.active?
112
124
  @closed = true
113
125
  children.each { |c| c.close }
114
126
  fire(:closed)
@@ -305,6 +317,7 @@ module EventMachine
305
317
  # @see #send_and_wait
306
318
  # @param [String] d the data to send encoded as a string
307
319
  def send_data(d, send_newline=true)
320
+ return unless shell
308
321
  if send_newline
309
322
  shell.send_data("#{d}#{line_terminator}")
310
323
  else
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  class Ssh
3
- VERSION='0.4.1'
3
+ VERSION='0.4.2'
4
4
  end # class::Ssh
5
5
  end # module::EventMachine
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-ssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-13 00:00:00.000000000 Z
12
+ date: 2012-09-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine