From 3d220ef5c8c84c108f80193a60279f2fc0ac175e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 28 Feb 2010 22:13:37 -0800 Subject: [PATCH] HTTP downloader implemented. Boxes can now be added via HTTP! --- lib/vagrant.rb | 2 +- lib/vagrant/actions/box/download.rb | 9 +++-- lib/vagrant/downloaders/http.rb | 40 +++++++++++++++++++++++ test/vagrant/actions/box/download_test.rb | 6 ++++ test/vagrant/downloaders/http_test.rb | 39 ++++++++++++++++++++++ 5 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 lib/vagrant/downloaders/http.rb create mode 100644 test/vagrant/downloaders/http_test.rb diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 8483819e5..1258c0ec8 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -3,7 +3,7 @@ $:.unshift(libdir) PROJECT_ROOT = File.join(libdir, '..') unless defined?(PROJECT_ROOT) # The libs which must be loaded prior to the rest -%w{tempfile open-uri ftools json pathname logger uri virtualbox net/ssh tarruby +%w{tempfile open-uri ftools json pathname logger uri net/http virtualbox net/ssh tarruby net/scp fileutils vagrant/util vagrant/actions/base vagrant/downloaders/base}.each do |f| require f end diff --git a/lib/vagrant/actions/box/download.rb b/lib/vagrant/actions/box/download.rb index e1413a418..5c358ce8e 100644 --- a/lib/vagrant/actions/box/download.rb +++ b/lib/vagrant/actions/box/download.rb @@ -12,10 +12,13 @@ module Vagrant def prepare # Parse the URI given and prepare a downloader uri = URI.parse(@runner.uri) + uri_map = [[URI::HTTP, Downloaders::HTTP], [URI::Generic, Downloaders::File]] - if uri.is_a?(URI::Generic) - logger.info "Generic URI type for box download, assuming file..." - @downloader = Downloaders::File.new + uri_map.find do |uri_type, downloader_klass| + if uri.is_a?(uri_type) + logger.info "#{uri_type} for URI, downloading via #{downloader_klass}..." + @downloader = downloader_klass.new + end end raise ActionException.new("Unknown URI type for box download.") unless @downloader diff --git a/lib/vagrant/downloaders/http.rb b/lib/vagrant/downloaders/http.rb new file mode 100644 index 000000000..ebc0fd030 --- /dev/null +++ b/lib/vagrant/downloaders/http.rb @@ -0,0 +1,40 @@ +module Vagrant + module Downloaders + # Downloads a file from an HTTP URL to a temporary file. This + # downloader reports its progress to stdout while downloading. + class HTTP < Base + # ANSI escape code to clear lines from cursor to end of line + CL_RESET = "\r\e[0K" + + def download!(source_url, destination_file) + Net::HTTP.get_response(URI.parse(source_url)) do |response| + total = response.content_length + progress = 0 + segment_count = 0 + + response.read_body do |segment| + # Report the progress out + progress += segment.length + segment_count += 1 + + # Progress reporting is limited to every 25 segments just so + # we're not constantly updating + if segment_count % 25 == 0 + report_progress(progress, total) + segment_count = 0 + end + + # Store the segment + destination_file.write(segment) + end + end + end + + def report_progress(progress, total) + percent = (progress.to_f / total.to_f) * 100 + print "#{CL_RESET}Download Progress: #{percent.to_i}% (#{progress} / #{total})" + $stdout.flush + end + end + end +end \ No newline at end of file diff --git a/test/vagrant/actions/box/download_test.rb b/test/vagrant/actions/box/download_test.rb index 1e164e7e9..4fb3383d4 100644 --- a/test/vagrant/actions/box/download_test.rb +++ b/test/vagrant/actions/box/download_test.rb @@ -30,6 +30,12 @@ class DownloadBoxActionTest < Test::Unit::TestCase @action.prepare assert @action.downloader.is_a?(Vagrant::Downloaders::File) end + + should "set the downloader to HTTP if URI is HTTP" do + @uri.stubs(:is_a?).with(URI::HTTP).returns(true) + @action.prepare + assert @action.downloader.is_a?(Vagrant::Downloaders::HTTP) + end end context "executing" do diff --git a/test/vagrant/downloaders/http_test.rb b/test/vagrant/downloaders/http_test.rb new file mode 100644 index 000000000..769ce255d --- /dev/null +++ b/test/vagrant/downloaders/http_test.rb @@ -0,0 +1,39 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class HttpDownloaderTest < Test::Unit::TestCase + setup do + @downloader, @tempfile = mock_downloader(Vagrant::Downloaders::HTTP) + @downloader.stubs(:report_progress) + @uri = "foo.box" + end + + context "downloading" do + setup do + @parsed_uri = mock("parsed") + URI.stubs(:parse).with(@uri).returns(@parsed_uri) + end + + should "parse the URI and use that parsed URI for Net::HTTP" do + URI.expects(:parse).with(@uri).returns(@parsed_uri).once + Net::HTTP.expects(:get_response).with(@parsed_uri).once + @downloader.download!(@uri, @tempfile) + end + + should "read the body of the response and place each segment into the file" do + response = mock("response") + response.stubs(:content_length) + segment = mock("segment") + segment.stubs(:length).returns(7) + + Net::HTTP.stubs(:get_response).yields(response) + response.expects(:read_body).once.yields(segment) + @tempfile.expects(:write).with(segment).once + + @downloader.download!(@uri, @tempfile) + end + end + + context "reporting progress" do + # TODO: Testing for this, probably + end +end