Methods
Included Modules
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
new( session, log, buffers, factories )

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.

    # 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_channel_open_handler( type, &block )

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.

    # 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
allocate_channel_id()

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

     # 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
channel_request( type )

Send a channel request packet to the server.

     # File lib/net/ssh/connection/driver.rb, line 185
185:         def channel_request( type )
186:           writer = @buffers.writer
187:           writer.write_byte CHANNEL_REQUEST
188:           writer.write_long 0 # channel id
189:           writer.write_string type
190:           writer.write_byte 0 # want_confirm
191:           
192:           @session.send_message writer
193:         end
channels()

Returns an array of active channels.

    # File lib/net/ssh/connection/driver.rb, line 84
84:         def channels
85:           @channel_map.values
86:         end
do_channel_close( response )
     # File lib/net/ssh/connection/driver.rb, line 345
345:         def do_channel_close( response )
346:           local_id = response.read_long
347:           @log.debug "CHANNEL_CLOSE recieved (#{local_id})" if @log.debug?
348:           @channel_map[ local_id ].close false
349:         end
do_channel_data( response )
     # File lib/net/ssh/connection/driver.rb, line 315
315:         def do_channel_data( response )
316:           local_id = response.read_long
317:           data = response.read_string
318: 
319:           if @log.debug?
320:             @log.debug "CHANNEL_DATA recieved (#{local_id}:#{data.inspect})"
321:           end
322: 
323:           @channel_map[ local_id ].do_data data
324:         end
do_channel_eof( response )
     # File lib/net/ssh/connection/driver.rb, line 339
339:         def do_channel_eof( response )
340:           local_id = response.read_long
341:           @log.debug "CHANNEL_EOF recieved (#{local_id})" if @log.debug?
342:           @channel_map[ local_id ].do_eof
343:         end
do_channel_extended_data( response )
     # File lib/net/ssh/connection/driver.rb, line 326
326:         def do_channel_extended_data( response )
327:           local_id = response.read_long
328:           data_type = response.read_long
329:           data = response.read_string
330: 
331:           if @log.debug?
332:             @log.debug "CHANNEL_EXTENDED_DATA recieved " +
333:               "(#{local_id}:#{data_type}:#{data.inspect})"
334:           end
335: 
336:           @channel_map[ local_id ].do_extended_data data_type, data
337:         end
do_channel_failure( response )
     # File lib/net/ssh/connection/driver.rb, line 370
370:         def do_channel_failure( response )
371:           local_id = response.read_long
372:           @log.debug "CHANNEL_FAILURE recieved (#{local_id})" if @log.debug?
373:           @channel_map[ local_id ].do_failure
374:         end
do_channel_open( response )
     # File lib/net/ssh/connection/driver.rb, line 242
242:         def do_channel_open( response )
243:           ch_type = response.read_string
244:           @log.debug "CHANNEL_OPEN recieved (#{ch_type})" if @log.debug?
245:           handled = false
246: 
247:           sender_channel = response.read_long
248:           window_size = response.read_long
249:           packet_size = response.read_long
250: 
251:           channel = @factories[:create].call( ch_type, sender_channel,
252:                         window_size, packet_size )
253: 
254:           ( @channel_open_handlers[ ch_type ] || [] ).each do |handler|
255:             next unless handler
256:             handled = true
257:             handler.call( self, channel, response )
258:           end
259: 
260:           unless handled
261:             raise Net::SSH::Exception,
262:               "cannot handle request to open a channel of type '#{ch_type}'"
263:           end
264: 
265:           @channel_map[channel.local_id] = channel
266: 
267:           writer = @buffers.writer
268:           writer.write_byte CHANNEL_OPEN_CONFIRMATION
269:           writer.write_long channel.remote_id
270:           writer.write_long channel.local_id
271:           writer.write_long 0x7FFFFFFF
272:           writer.write_long 0x7FFFFFFF
273:           @session.send_message writer
274:         end
do_channel_open_confirmation( response )
     # File lib/net/ssh/connection/driver.rb, line 289
289:         def do_channel_open_confirmation( response )
290:           local_id = response.read_long
291:           remote_id = response.read_long
292:           window_size = response.read_long
293:           packet_size = response.read_long
294: 
295:           if @log.debug?
296:             @log.debug "CHANNEL_OPEN_CONFIRMATION recieved (#{local_id})"
297:           end
298: 
299:           channel = @channel_map[ local_id ]
300:           channel.do_confirm_open remote_id, window_size, packet_size
301:         end
do_channel_open_failure( response )
     # File lib/net/ssh/connection/driver.rb, line 276
276:         def do_channel_open_failure( response )
277:           local_id = response.read_long
278:           reason_code = response.read_long
279:           reason = response.read_string
280:           language = response.read_string
281: 
282:           @log.debug "CHANNEL_OPEN_FAILURE recieved (#{reason})" if @log.debug?
283: 
284:           channel = @channel_map[ local_id ]
285:           @channel_map.delete local_id
286:           channel.do_confirm_failed reason_code, reason, language
287:         end
do_channel_request( response )
     # File lib/net/ssh/connection/driver.rb, line 351
351:         def do_channel_request( response )
352:           local_id = response.read_long
353:           request = response.read_string
354:           want_reply = response.read_bool
355:           request_data = response.remainder_as_buffer
356: 
357:           if @log.debug?
358:             @log.debug "CHANNEL_REQUEST recieved (#{local_id}:#{request})"
359:           end
360: 
361:           @channel_map[ local_id ].do_request request, want_reply, request_data
362:         end
do_channel_success( response )
     # File lib/net/ssh/connection/driver.rb, line 364
364:         def do_channel_success( response )
365:           local_id = response.read_long
366:           @log.debug "CHANNEL_SUCCESS recieved (#{local_id})" if @log.debug?
367:           @channel_map[ local_id ].do_success
368:         end
do_channel_window_adjust( response )
     # File lib/net/ssh/connection/driver.rb, line 303
303:         def do_channel_window_adjust( response )
304:           local_id = response.read_long
305:           bytes_to_add = response.read_long
306: 
307:           if @log.debug?
308:             @log.debug "CHANNEL_WINDOW_ADJUST recieved " +
309:               "(#{local_id}:#{bytes_to_add})"
310:           end
311: 
312:           @channel_map[ local_id ].do_window_adjust( bytes_to_add )
313:         end
do_global_request( response )
     # File lib/net/ssh/connection/driver.rb, line 218
218:         def do_global_request( response )
219:           name = response.read_string
220:           want_reply = response.read_bool
221:           request_data = response.remainder_as_buffer
222: 
223:           @log.debug "GLOBAL_REQUEST received (#{name})" if @log.debug?
224: 
225:           if want_reply
226:             writer = @buffers.writer
227:             writer.write_byte REQUEST_SUCCESS
228:             @session.send_message writer
229:           end
230:         end
do_request_failure( response )
     # File lib/net/ssh/connection/driver.rb, line 237
237:         def do_request_failure( response )
238:           @log.debug "REQUEST_FAILURE received" if @log.debug?
239:           process_request response, false
240:         end
do_request_success( response )
     # File lib/net/ssh/connection/driver.rb, line 232
232:         def do_request_success( response )
233:           @log.debug "REQUEST_SUCCESS received" if @log.debug?
234:           process_request response, true
235:         end
global_request( type, data=nil, &block )

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

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

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.

     # File lib/net/ssh/connection/driver.rb, line 136
136:         def loop( &block )
137:           block ||= proc do 
138:             channels = @channel_map.reject {|k,v| v.type == 'auth-agent@openssh.com' }
139:             not channels.empty?
140:           end
141:           process while block.call
142:         end
open_channel( type, extra_data=nil, &on_confirm )

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.

    # 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
ping!()

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

     # File lib/net/ssh/connection/driver.rb, line 208
208:         def ping!
209:           @session.ping!
210:         end
process( nonblock=false )

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.

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

Delegates to the reader_ready method of the transport session.

     # File lib/net/ssh/connection/driver.rb, line 202
202:         def reader_ready?
203:           @session.reader_ready?
204:         end
register_data_request( channel, data, type=nil )

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.

     # 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_channel( channel )

Remove the given channel from the connection.

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

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

    # 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
send_message( msg )

A convenience method for sending messages.

     # File lib/net/ssh/connection/driver.rb, line 196
196:         def send_message( msg )
197:           @session.send_message msg
198:           self
199:         end