Skip to content

Commit

Permalink
Finish documenting Net::SCP
Browse files Browse the repository at this point in the history
  • Loading branch information
jamis committed Mar 16, 2008
1 parent 430bfb5 commit 0cc6674
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
45 changes: 43 additions & 2 deletions lib/net/scp.rb
Expand Up @@ -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
Expand All @@ -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
#
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions lib/net/scp/upload.rb
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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]
Expand All @@ -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?
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 0cc6674

Please sign in to comment.