em-ssh 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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