Initial downloader implementation

This commit is contained in:
Mitchell Hashimoto 2013-03-17 01:22:08 -07:00
parent 06d055f443
commit 913e0e529d
1 changed files with 102 additions and 0 deletions

View File

@ -0,0 +1,102 @@
require "log4r"
require "vagrant/util/subprocess"
module Vagrant
module Util
# This class downloads files using various protocols by subprocessing
# to cURL. cURL is a much more capable and complete download tool than
# a hand-rolled Ruby library, so we defer to it's expertise.
class Downloader
def initialize(source, destination, options=nil)
@logger = Log4r::Logger.new("vagrant::util::downloader")
@source = source
@destination = destination
# Get the various optional values
@options ||= {}
@ui = @options[:ui]
end
# This executes the actual download, downloading the source file
# to the destination with the given opens used to initialize this
# class.
#
# If this method returns without an exception, the download
# succeeded. An exception will be raised if the download failed.
def download!
# Build the list of parameters to execute with cURL
options = [
"--fail",
"--output", @destination,
@source
]
# This variable can contain the proc that'll be sent to
# the subprocess execute.
data_proc = nil
if @ui
# If we're outputting progress, then setup the subprocess to
# tell us output so we can parse it out.
options << { :notify => :stderr }
# Setup the proc that'll receive the real-time data from
# the downloader.
data_proc = Proc.new do |type, data|
# Type will always be "stderr" because that is the only
# type of data we're subscribed for notifications.
# If the data doesn't start with a \r then it isn't a progress
# notification, so ignore it.
next if data[0] != "\r"
# Ignore the first \r and split by whitespace to grab the columns
columns = data[1..-1].split(/\s+/)
# COLUMN DATA:
#
# 0 - blank
# 1 - % total
# 2 - Total size
# 3 - % received
# 4 - Received size
# 5 - % transferred
# 6 - Transferred size
# 7 - Average download speed
# 8 - Average upload speed
# 9 - Total time
# 10 - Time spent
# 11 - Time left
# 12 - Current speed
output = "Progress: #{columns[1]}% (Rate: #{columns[12]}/s, Estimated time remaining: #{columns[11]}"
@ui.clear_line
@ui.info(output, :new_line => false)
end
end
@logger.info("Downloader starting download: ")
@logger.info(" -- Source: #{@source}")
@logger.info(" -- Destination: #{@destination}")
# Execute!
result = Subprocess.execute("curl", *options, &data_proc)
# If it didn't exit successfully, we need to parse the data and
# show an error message.
if result.exit_code != 0
parts = result.stderr.split(/\ncurl:\s+\(\d+\)\s*/, 2)
# If the length is correct, we properly parsed an error message
if parts.length == 2
# TODO: Raise the error
end
end
# Everything succeeded
true
end
end
end
end