Skip to content
Browse files

Finish documenting Net::SCP

  • Loading branch information...
1 parent 430bfb5 commit 0cc6674a16fc3b51c74ed626eefa43fc546694e5 @jamis jamis committed Mar 15, 2008
Showing with 68 additions and 2 deletions.
  1. +43 −2 lib/net/scp.rb
  2. +25 −0 lib/net/scp/upload.rb
View
45 lib/net/scp.rb
@@ -104,6 +104,11 @@ module Net
# This is the start state for downloads. It simply sends a 0-byte to the
# server. The next state is Net::SCP::Download#read_directive_state.
#
+ # == Net::SCP::Upload#upload_start_state
+ #
+ # Sets up the initial upload scaffolding and waits for a 0-byte from the
+ # server, and then switches to Net::SCP::Upload#upload_current_state.
+ #
# == Net::SCP::Download#read_directive_state
#
# Reads a directive line from the input. The following directives are
@@ -127,10 +132,14 @@ module Net
# Net::SCP::Download#read_data_state. If an 'E' directive is received, and
# there is no parent directory, we switch over to Net::SCP#finish_state.
#
+ # Regardless of what the next state is, we send a 0-byte to the server
+ # before moving to the next state.
+ #
# == Net::SCP::Download#read_data_state
#
# Bytes are read to satisfy the size of the incoming file. When all pending
- # data has been read, we switch to the Net::SCP::Download#finish_read_state.
+ # data has been read, we wait for the server to send a 0-byte, and then we
+ # switch to the Net::SCP::Download#finish_read_state.
#
# == Net::SCP::Download#finish_read_state
#
@@ -139,6 +148,35 @@ module Net
# file and we switch to Net::SCP#finish_state. Otherwise we jump back to the
# Net::SCP::Download#read_directive state to see what we get to download next.
#
+ # == Net::SCP::Upload#upload_current_state
+ #
+ # If the current item is a file, send a file. Sending a file starts with a
+ # 'T' directive (if :preserve is true), then a wait for the server to respond,
+ # and then a 'C' directive, and then a wait for the server to respond, and
+ # then a jump to Net::SCP::Upload#send_data_state.
+ #
+ # If current item is a directory, send a 'D' directive, and wait for the
+ # server to respond with a 0-byte. Then jump to Net::SCP::Upload#next_item_state.
+ #
+ # == Net::SCP::Upload#send_data_state
+ #
+ # Reads and sends the next chunk of data to the server. The state machine
+ # remains in this state until all data has been sent, at which point we
+ # send a 0-byte to the server, and wait for the server to respond with a
+ # 0-byte of its own. Then we jump back to Net::SCP::Upload#next_item_state.
+ #
+ # == Net::SCP::Upload#next_item_state
+ #
+ # If there is nothing left to upload, and there is no parent directory,
+ # jump to Net::SCP#finish_state.
+ #
+ # If there is nothing left to upload from the current directory, send an
+ # 'E' directive and wait for the server to respond with a 0-byte. Then go
+ # to Net::SCP::Upload#next_item_state.
+ #
+ # Otherwise, set the current upload source and go to
+ # Net::SCP::Upload#upload_current_state.
+ #
# == Net::SCP#finish_state
#
# Tells the server that no more data is forthcoming from this end of the
@@ -218,7 +256,10 @@ def initialize(session)
# * :preserve - the atime and mtime of the file should be preserved.
# * :verbose - the process should result in verbose output on the server
# end (useful for debugging).
- #
+ # # :chunk_size - the size of each "chunk" that should be sent. Defaults
+ # to 2048. Changing this value may improve throughput at the expense
+ # of decreasing interactivity.
+ #
# This method will return immediately, returning the Net::SSH::Connection::Channel
# object that will support the upload. To wait for the upload to finish,
# you can either call the #wait method on the channel, or otherwise run
View
25 lib/net/scp/upload.rb
@@ -2,11 +2,18 @@
module Net; class SCP
+ # This module implements the state machine for uploading information to
+ # a remote server. It exposes no public methods. See Net::SCP#upload for
+ # a discussion of how to use Net::SCP to upload data.
module Upload
private
+ # The default read chunk size, if an explicit chunk-size is not specified
+ # by the client.
DEFAULT_CHUNK_SIZE = 2048
+ # The start state for uploads. Simply sets up the upload scaffolding,
+ # sets the current item to upload, and jumps to #upload_current_state.
def upload_start_state(channel)
if channel[:local].respond_to?(:read)
channel[:options].delete(:recursive)
@@ -18,6 +25,9 @@ def upload_start_state(channel)
await_response(channel, :upload_current)
end
+ # Determines what the next thing to upload is, and branches. If the next
+ # item is a file, goes to #upload_file_state. If it is a directory, goes
+ # to #upload_directory_state.
def upload_current_state(channel)
if channel[:current].respond_to?(:read)
upload_file_state(channel)
@@ -31,6 +41,8 @@ def upload_current_state(channel)
end
end
+ # After transferring attributes (if requested), sends a 'D' directive and
+ # awaites the server's 0-byte response. Then goes to #next_item_state.
def upload_directory_state(channel)
if preserve_attributes_if_requested(channel)
mode = channel[:stat].mode & 07777
@@ -42,6 +54,8 @@ def upload_directory_state(channel)
end
end
+ # After transferring attributes (if requested), sends a 'C' directive and
+ # awaits the server's 0-byte response. Then goes to #send_data_state.
def upload_file_state(channel)
if preserve_attributes_if_requested(channel)
mode = channel[:stat] ? channel[:stat].mode & 07777 : channel[:options][:mode]
@@ -55,6 +69,8 @@ def upload_file_state(channel)
end
end
+ # If any data remains to be transferred from the current file, sends it.
+ # Otherwise, sends a 0-byte and transfers to #next_item_state.
def send_data_state(channel)
data = channel[:io].read(channel[:chunk_size])
if data.nil?
@@ -68,6 +84,11 @@ def send_data_state(channel)
end
end
+ # Checks the work queue to see what needs to be done next. If there is
+ # nothing to do, calls Net::SCP#finish_state. If we're at the end of a
+ # directory, sends an 'E' directive and waits for the server to respond
+ # before moving to #next_item_state. Otherwise, sets the next thing to
+ # upload and moves to #upload_current_state.
def next_item_state(channel)
if channel[:stack].empty?
finish_state(channel)
@@ -85,6 +106,7 @@ def next_item_state(channel)
end
end
+ # Sets the given +path+ as the new current item to upload.
def set_current(channel, path)
path = channel[:cwd] ? File.join(channel[:cwd], path) : path
channel[:current] = path
@@ -98,6 +120,9 @@ def set_current(channel, path)
channel[:size] = channel[:stat] ? channel[:stat].size : channel[:current].size
end
+ # If the :preserve option is set, send a 'T' directive and wait for the
+ # server to respond before proceeding to either #upload_file_state or
+ # #upload_directory_state, depending on what is being uploaded.
def preserve_attributes_if_requested(channel)
if channel[:options][:preserve] && !channel[:preserved]
channel[:preserved] = true

0 comments on commit 0cc6674

Please sign in to comment.
Something went wrong with that request. Please try again.