class Object
Public Instance Methods
Returns true if the connection has been closed.
# File lib/net/sftp/session.rb, line 741 def closed? state == :closed end
Attempts to establish an SFTP connection over the SSH session given when this object was instantiated. If the object is already open, this will simply execute the given block (if any), passing the SFTP session itself as argument. If the session is currently being opened, this will add the given block to the list of callbacks, to be executed when the session is fully open.
This method does not block, and will return immediately. If you pass a block to it, that block will be invoked when the connection has been fully established. Thus, you can do something like this:
sftp.connect do puts "open!" end
If you just want to block until the connection is ready, see the connect!
method.
# File lib/net/sftp/session.rb, line 768 def connect(&block) case state when :open block.call(self) if block when :closed @state = :opening @channel = session.open_channel(&method(:when_channel_confirmed)) @packet_length = nil @protocol = nil @on_ready = Array(block) else # opening @on_ready << block if block end self end
Same as the connect
method, but blocks until the SFTP connection has been fully initialized.
# File lib/net/sftp/session.rb, line 787 def connect!(&block) connect(&block) loop { opening? } self end
Parses the packet, finds the associated Request instance, and tells the Request instance to respond to the packet (see Request#respond_to).
# File lib/net/sftp/session.rb, line 947 def dispatch_request(packet) id = packet.read_long request = pending_requests.delete(id) or raise Net::SFTP::Exception, "no such request `#{id}'" request.respond_to(packet) end
Called to handle FXP_VERSION packets. This performs the SFTP protocol version negotiation, instantiating the appropriate Protocol instance and invoking the callback given to connect
, if any.
# File lib/net/sftp/session.rb, line 921 def do_version(packet) debug { "negotiating sftp protocol version, mine is #{HIGHEST_PROTOCOL_VERSION_SUPPORTED}" } server_version = packet.read_long debug { "server reports sftp version #{server_version}" } negotiated_version = [server_version, HIGHEST_PROTOCOL_VERSION_SUPPORTED].min info { "negotiated version is #{negotiated_version}" } extensions = {} until packet.eof? name = packet.read_string data = packet.read_string extensions[name] = data end @protocol = Protocol.load(self, negotiated_version) @pending_requests = {} @state = :open @on_ready.each { |callback| callback.call(self) } @on_ready = nil end
The input buffer used to accumulate packet data
# File lib/net/sftp/session.rb, line 826 def input; @input; end
Runs the SSH event loop while the given block returns true. This lets you set up a state machine and then “fire it off”. If you do not specify a block, the event loop will run for as long as there are any pending SFTP requests. This makes it easy to do thing like this:
sftp.remove("/path/to/file") sftp.loop
# File lib/net/sftp/session.rb, line 802 def loop(&block) block ||= Proc.new { pending_requests.any? } session.loop(&block) end
Returns true if the connection has been initialized.
# File lib/net/sftp/session.rb, line 736 def open? state == :open end
Returns true if the connection is in the process of being initialized (e.g., it is not closed, but is not yet fully open).
# File lib/net/sftp/session.rb, line 747 def opening? !(open? || closed?) end
Create and enqueue a new SFTP request of the given type, with the given arguments. Returns a new Request instance that encapsulates the request.
# File lib/net/sftp/session.rb, line 831 def request(type, *args, &callback) request = Request.new(self, type, protocol.send(type, *args), &callback) info { "sending #{type} packet (#{request.id})" } pending_requests[request.id] = request end
Formats, constructs, and sends an SFTP packet of the given type and with the given data. This does not block, but merely enqueues the packet for sending and returns.
You should probably use the operation methods, rather than building and sending the packet directly. (See open, close, etc.)
# File lib/net/sftp/session.rb, line 813 def send_packet(type, *args) data = Net::SSH::Buffer.from(*args) msg = Net::SSH::Buffer.from(:long, data.length+1, :byte, type, :raw, data) channel.send_data(msg.to_s) end
Waits for the given request to complete. If the response is EOF, nil is returned. If the response was not successful (e.g., !response.ok?), a StatusException will be raised. If property
is given, the corresponding property from the response will be returned; otherwise, the response object itself will be returned.
# File lib/net/sftp/session.rb, line 843 def wait_for(request, property=nil) request.wait if request.response.eof? nil elsif !request.response.ok? raise StatusException.new(request.response) elsif property request.response[property.to_sym] else request.response end end
Called when the SSH server closes the underlying channel.
# File lib/net/sftp/session.rb, line 885 def when_channel_closed(channel) debug { "sftp channel closed" } @channel = nil @state = :closed end
Called when the SSH channel is confirmed as “open” by the server. This is one of the states of the SFTP state machine, and is followed by the when_subsystem_started
state.
# File lib/net/sftp/session.rb, line 859 def when_channel_confirmed(channel) debug { "requesting sftp subsystem" } @state = :subsystem channel.subsystem("sftp", &method(:when_subsystem_started)) end
Called whenever Net::SSH
polls the SFTP channel for pending activity. This basically checks the input buffer to see if enough input has been accumulated to handle. If there has, the packet is parsed and dispatched, according to its type (see do_version
and dispatch_request
).
# File lib/net/sftp/session.rb, line 895 def when_channel_polled(channel) while input.length > 0 if @packet_length.nil? # make sure we've read enough data to tell how long the packet is return unless input.length >= 4 @packet_length = input.read_long end return unless input.length >= @packet_length + 4 packet = Net::SFTP::Packet.new(input.read(@packet_length)) input.consume! @packet_length = nil debug { "received sftp packet #{packet.type} len #{packet.length}" } if packet.type == FXP_VERSION do_version(packet) else dispatch_request(packet) end end end
Called when the SSH server confirms that the SFTP subsystem was successfully started. This sets up the appropriate callbacks on the SSH channel and then starts the SFTP protocol version negotiation process.
# File lib/net/sftp/session.rb, line 869 def when_subsystem_started(channel, success) raise Net::SFTP::Exception, "could not start SFTP subsystem" unless success debug { "sftp subsystem successfully started" } @state = :init channel.on_data { |c,data| input.append(data) } channel.on_extended_data { |c,t,data| debug { data } } channel.on_close(&method(:when_channel_closed)) channel.on_process(&method(:when_channel_polled)) send_packet(FXP_INIT, :long, @version || HIGHEST_PROTOCOL_VERSION_SUPPORTED) end