- add_channel_open_handler
- allocate_channel_id
- channel_request
- channels
- do_channel_close
- do_channel_data
- do_channel_eof
- do_channel_extended_data
- do_channel_failure
- do_channel_open
- do_channel_open_confirmation
- do_channel_open_failure
- do_channel_request
- do_channel_success
- do_channel_window_adjust
- do_global_request
- do_request_failure
- do_request_success
- global_request
- loop
- new
- open_channel
- ping!
- process
- reader_ready?
- register_data_request
- remove_channel
- remove_channel_open_handler
- send_message
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 |
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.
[ show 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
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.
[ show 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.
[ show 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
Send a channel request packet to the server.
[ show source ]
# 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
Returns an array of active channels.
[ show source ]
# File lib/net/ssh/connection/driver.rb, line 84 84: def channels 85: @channel_map.values 86: end
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
[ show source ]
# 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
Send a global request packet to the server. This returns immediately. The given block will be invoked when the server responds.
[ show source ]
# 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
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.
[ show source ]
# 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 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.
[ show 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.
[ show source ]
# File lib/net/ssh/connection/driver.rb, line 208 208: def ping! 209: @session.ping! 210: 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.
[ show source ]
# 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
Delegates to the reader_ready method of the transport session.
[ show source ]
# File lib/net/ssh/connection/driver.rb, line 202 202: def reader_ready? 203: @session.reader_ready? 204: 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.
[ show 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.
[ show 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.
[ show 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.
[ show source ]
# File lib/net/ssh/connection/driver.rb, line 196 196: def send_message( msg ) 197: @session.send_message msg 198: self 199: end