Created
August 6, 2010 17:45
-
-
Save julik/511676 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# We send this as a response object. Rack can use any object that responds to each() as | |
# response body. When each() is called, the object | |
# will activate the FTP connection and couple the FTP receive loop with the HTTP send loop. | |
# This will probably go totally kaput on shitservers like Webrick that still insist on buffering | |
# any output, but someone who uses Webrick can better shoot himself on the spot. Since | |
# we provide range support, we need to pass it downstream to the FTP server and we need to send | |
# exactly the amount of bytes requested to satisfy range. | |
class Fatpete::FTPStream | |
CHUNK_SIZE = 128 * 1024 | |
SLEEP_BETWEEN_CHUNKS = 0.1 | |
def initialize(server, login, password, path, start_at_offset, send_bytes) | |
@server, @login, @password, @path = server, login, password, path | |
@start_at = start_at_offset | |
@send_bytes = send_bytes | |
end | |
def each(&blk) | |
Net::FTP.open(@server) do | conn | | |
conn.resume = true # CRUCIAL! | |
conn.login(@login, @password) | |
conn.chdir(File.dirname(@path)) | |
total_sent = 0 | |
catch(:stop_transfer) do | |
# We use retrbinary since all default get methods in Net::FTP want to also buffer | |
# to a local file. We ain' doin' that. | |
conn.retrbinary("RETR " + File.basename(@path), CHUNK_SIZE, @start_at) do |data| | |
if (total_sent + Rack::Utils.bytesize(data)) > @send_bytes | |
# Chop off the response and close shop | |
blk.call(data[0...(@send_bytes - total_sent)]) | |
throw :stop_transfer | |
else | |
blk.call(data) | |
total_sent += Rack::Utils.bytesize(data) | |
end | |
# Give others some room to breathe and do not peg the processor. | |
# To prevent a delay when starting the transfer send data first, then sleep | |
sleep(SLEEP_BETWEEN_CHUNKS) | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment