Class Net::SSH::Connection::Driver
In: lib/net/ssh/connection/driver.rb
Parent: Object

Methods

Included Modules

Constants

Constants

Request = Struct.new( :type, :data, :callback )   A structure for representing global requests, as registered by the global_request method.
DataRequest = Struct.new( :channel, :data, :type )   A structure for representing a data buffer that must be sent across a channel.
MESSAGES = %w( global_request request_success request_failure channel_open channel_open_failure channel_open_confirmation channel_window_adjust channel_data channel_extended_data channel_eof channel_close channel_request channel_success channel_failure ).inject({}) do |map, event| constant = Constants.const_get event.upcase.to_sym  

Public Class methods

Create a new connection driver that communicates over the given transport session. log is the logger instance to write log messages to, buffers is a buffer factory, and channels is a factory that can return new channel instances.

[Source]

    # File lib/net/ssh/connection/driver.rb, line 46
46:         def initialize( session, log, buffers, factories )
47:           @session = session
48:           @log = log
49:           @buffers = buffers
50:           @factories = factories
51: 
52:           @channel_id_mutex = Mutex.new
53:           @next_channel_id = 0
54: 
55:           @channel_map = Hash.new
56:           @request_queue = Array.new
57:           @channel_open_handlers = Hash.new
58: 
59:           @data_requests = Array.new
60:           @data_requests_mutex = Mutex.new
61:         end

Public Instance methods

Add a callback to be invoked when a channel-open request is recieved for a channel of the given type. The handler-id is returned.

[Source]

    # File lib/net/ssh/connection/driver.rb, line 90
90:         def add_channel_open_handler( type, &block )
91:           ( @channel_open_handlers[ type ] ||= Array.new ).push block
92:           @channel_open_handlers.length
93:         end

Return the next available channel id for this connection. This method is thread-safe.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 103
103:         def allocate_channel_id
104:           @channel_id_mutex.synchronize do
105:             @next_channel_id += 1
106:             return @next_channel_id
107:           end
108:         end

Returns an array of active channels.

[Source]

    # File lib/net/ssh/connection/driver.rb, line 84
84:         def channels
85:           @channel_map.values
86:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 331
331:         def do_channel_close( response )
332:           local_id = response.read_long
333:           @log.debug "CHANNEL_CLOSE recieved (#{local_id})" if @log.debug?
334:           @channel_map[ local_id ].close false
335:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 301
301:         def do_channel_data( response )
302:           local_id = response.read_long
303:           data = response.read_string
304: 
305:           if @log.debug?
306:             @log.debug "CHANNEL_DATA recieved (#{local_id}:#{data.inspect})"
307:           end
308: 
309:           @channel_map[ local_id ].do_data data
310:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 325
325:         def do_channel_eof( response )
326:           local_id = response.read_long
327:           @log.debug "CHANNEL_EOF recieved (#{local_id})" if @log.debug?
328:           @channel_map[ local_id ].do_eof
329:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 312
312:         def do_channel_extended_data( response )
313:           local_id = response.read_long
314:           data_type = response.read_long
315:           data = response.read_string
316: 
317:           if @log.debug?
318:             @log.debug "CHANNEL_EXTENDED_DATA recieved " +
319:               "(#{local_id}:#{data_type}:#{data.inspect})"
320:           end
321: 
322:           @channel_map[ local_id ].do_extended_data data_type, data
323:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 356
356:         def do_channel_failure( response )
357:           local_id = response.read_long
358:           @log.debug "CHANNEL_FAILURE recieved (#{local_id})" if @log.debug?
359:           @channel_map[ local_id ].do_failure
360:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 228
228:         def do_channel_open( response )
229:           ch_type = response.read_string
230:           @log.debug "CHANNEL_OPEN recieved (#{ch_type})" if @log.debug?
231:           handled = false
232: 
233:           sender_channel = response.read_long
234:           window_size = response.read_long
235:           packet_size = response.read_long
236: 
237:           channel = @factories[:create].call( ch_type, sender_channel,
238:                         window_size, packet_size )
239: 
240:           ( @channel_open_handlers[ ch_type ] || [] ).each do |handler|
241:             next unless handler
242:             handled = true
243:             handler.call( self, channel, response )
244:           end
245: 
246:           unless handled
247:             raise Net::SSH::Exception,
248:               "cannot handle request to open a channel of type '#{ch_type}'"
249:           end
250: 
251:           @channel_map[channel.local_id] = channel
252: 
253:           writer = @buffers.writer
254:           writer.write_byte CHANNEL_OPEN_CONFIRMATION
255:           writer.write_long channel.remote_id
256:           writer.write_long channel.local_id
257:           writer.write_long 0x7FFFFFFF
258:           writer.write_long 0x7FFFFFFF
259:           @session.send_message writer
260:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 275
275:         def do_channel_open_confirmation( response )
276:           local_id = response.read_long
277:           remote_id = response.read_long
278:           window_size = response.read_long
279:           packet_size = response.read_long
280: 
281:           if @log.debug?
282:             @log.debug "CHANNEL_OPEN_CONFIRMATION recieved (#{local_id})"
283:           end
284: 
285:           channel = @channel_map[ local_id ]
286:           channel.do_confirm_open remote_id, window_size, packet_size
287:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 262
262:         def do_channel_open_failure( response )
263:           local_id = response.read_long
264:           reason_code = response.read_long
265:           reason = response.read_string
266:           language = response.read_string
267: 
268:           @log.debug "CHANNEL_OPEN_FAILURE recieved (#{reason})" if @log.debug?
269: 
270:           channel = @channel_map[ local_id ]
271:           @channel_map.delete local_id
272:           channel.do_confirm_failed reason_code, reason, language
273:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 337
337:         def do_channel_request( response )
338:           local_id = response.read_long
339:           request = response.read_string
340:           want_reply = response.read_bool
341:           request_data = response.remainder_as_buffer
342: 
343:           if @log.debug?
344:             @log.debug "CHANNEL_REQUEST recieved (#{local_id}:#{request})"
345:           end
346: 
347:           @channel_map[ local_id ].do_request request, want_reply, request_data
348:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 350
350:         def do_channel_success( response )
351:           local_id = response.read_long
352:           @log.debug "CHANNEL_SUCCESS recieved (#{local_id})" if @log.debug?
353:           @channel_map[ local_id ].do_success
354:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 289
289:         def do_channel_window_adjust( response )
290:           local_id = response.read_long
291:           bytes_to_add = response.read_long
292: 
293:           if @log.debug?
294:             @log.debug "CHANNEL_WINDOW_ADJUST recieved " +
295:               "(#{local_id}:#{bytes_to_add})"
296:           end
297: 
298:           @channel_map[ local_id ].do_window_adjust( bytes_to_add )
299:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 204
204:         def do_global_request( response )
205:           name = response.read_string
206:           want_reply = response.read_bool
207:           request_data = response.remainder_as_buffer
208: 
209:           @log.debug "GLOBAL_REQUEST received (#{name})" if @log.debug?
210: 
211:           if want_reply
212:             writer = @buffers.writer
213:             writer.write_byte REQUEST_SUCCESS
214:             @session.send_message writer
215:           end
216:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 223
223:         def do_request_failure( response )
224:           @log.debug "REQUEST_FAILURE received" if @log.debug?
225:           process_request response, false
226:         end

[Source]

     # File lib/net/ssh/connection/driver.rb, line 218
218:         def do_request_success( response )
219:           @log.debug "REQUEST_SUCCESS received" if @log.debug?
220:           process_request response, true
221:         end

Send a global request packet to the server. This returns immediately. The given block will be invoked when the server responds.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 169
169:         def global_request( type, data=nil, &block )
170:           writer = @buffers.writer
171:           writer.write_byte GLOBAL_REQUEST
172:           writer.write_string type.to_s
173:           writer.write_bool true
174:           writer.write data.to_s if data
175:           @session.send_message writer
176: 
177:           @request_queue.push Request.new( type, data, block )
178:           self
179:         end

Repeated call process for as long as the given block returns true. If no block is given, then the loop continues until there are no more open channels on this connection.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 136
136:         def loop( &block )
137:           block ||= proc { not @channel_map.empty? }
138:           process while block.call
139:         end

Open and return a new channel. This returns immediately, before the server confirms that the channel was opened. When the server sends the confirmation, the on_confirm callback will be invoked.

[Source]

    # File lib/net/ssh/connection/driver.rb, line 72
72:         def open_channel( type, extra_data=nil, &on_confirm )
73:           channel = @factories[:open].call( type, extra_data )
74:           channel.on_confirm_open &on_confirm
75:           @channel_map[ channel.local_id ] = channel
76:         end

Sends an innocuous packet to the server to test the connection. Can be used to defeat timeouts on long-running commands.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 194
194:         def ping!
195:           @session.ping!
196:         end

Wait for and dispatch a single event. If nonblock is false (the default) this will block until a message has been received. Otherwise, it will return immediately.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 144
144:         def process( nonblock=false )
145:           process_data_requests
146: 
147:           if !nonblock || reader_ready?
148:             type, response = @session.wait_for_message
149: 
150:             unless ( dispatcher = MESSAGES[ type ] )
151:               raise Net::SSH::Exception,
152:                 "Unexpected response type '#{type}', (#{response.inspect})"
153:             end
154: 
155:             dispatcher[:method].bind( self ).call( response )
156:           end
157: 
158:           self
159:         end

Delegates to the reader_ready method of the transport session.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 188
188:         def reader_ready?
189:           @session.reader_ready?
190:         end

Register a data buffer (of an optional type) to be sent across the given channel at the next available opportunity.

This is used internally by channels to hide the window size and maximum packet size from the client. Clients should not call this method directly.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 116
116:         def register_data_request( channel, data, type=nil )
117:           @data_requests_mutex.synchronize do
118:             @data_requests << DataRequest.new( channel, data, type )
119:           end
120: 
121:           # make sure the new data request has a chance to be sent to the
122:           # server... Otherwise, it cannot be sent until the next time #process
123:           # is invoked, which can be unexpected in synchronous situations.
124:           process_data_requests
125:         end

Remove the given channel from the connection.

[Source]

    # File lib/net/ssh/connection/driver.rb, line 79
79:         def remove_channel( channel )
80:           @channel_map.delete channel.local_id
81:         end

Remove a callback with the given id for channel-open requests of the given type.

[Source]

    # File lib/net/ssh/connection/driver.rb, line 97
97:         def remove_channel_open_handler( type, id )
98:           @channel_open_handlers[ type ][ id-1 ] = nil
99:         end

A convenience method for sending messages.

[Source]

     # File lib/net/ssh/connection/driver.rb, line 182
182:         def send_message( msg )
183:           @session.send_message msg
184:           self
185:         end

[Validate]