class Object

Public Instance Methods

closed?() click to toggle source

Returns true if the connection has been closed.

# File lib/net/sftp/session.rb, line 741
def closed?
  state == :closed
end
connect(&block) click to toggle source

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
connect!(&block) click to toggle source

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
dispatch_request(packet) click to toggle source

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
do_version(packet) click to toggle source

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
input() click to toggle source

The input buffer used to accumulate packet data

# File lib/net/sftp/session.rb, line 826
def input; @input; end
loop(&block) click to toggle source

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
Also aliased as: loop_forever
loop_forever(&block)
Alias for: loop
open?() click to toggle source

Returns true if the connection has been initialized.

# File lib/net/sftp/session.rb, line 736
def open?
  state == :open
end
opening?() click to toggle source

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
request(type, *args, &callback) click to toggle source

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
send_packet(type, *args) click to toggle source

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
wait_for(request, property=nil) click to toggle source

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
when_channel_closed(channel) click to toggle source

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
when_channel_confirmed(channel) click to toggle source

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
when_channel_polled(channel) click to toggle source

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
when_subsystem_started(channel, success) click to toggle source

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