class Nabit::Downloader

Attributes

ftp_data_chunk_size[RW]
logger[RW]
max_ca_verify_depth[RW]

Public Class Methods

new() click to toggle source
# File lib/nabit/downloader.rb, line 13
def initialize
  @logger = STDOUT.binmode
  @max_ca_verify_depth = 5
  @ftp_data_chunk_size = 32768
end

Public Instance Methods

download_file(url, full_path, count = 3) click to toggle source
# File lib/nabit/downloader.rb, line 19
def download_file(url, full_path, count = 3)
  return if File.exists?(full_path)

  uri = URI.parse(url)
  case uri.scheme.downcase
  when /ftp/
    ftp_download(uri, full_path)
  when /http|https/
    http_download(url, full_path, count)
  else
    raise "\nERROR: Unknown URI scheme '#{uri.scheme}'"
  end
end

Private Instance Methods

ftp_download(parsed_uri, full_path) click to toggle source
# File lib/nabit/downloader.rb, line 130
def ftp_download(parsed_uri, full_path)
  filename = File.basename(parsed_uri.path)

  begin
    temp_file = Tempfile.new("download-#{filename}")
    temp_file.binmode

    size = 0
    progress = 0

    # TODO add user/pw support
    Net::FTP.open(parsed_uri.host) do |ftp|
      ftp.passive = true
      ftp.login
      remote_dir = File.dirname(parsed_uri.path)
      ftp.chdir(remote_dir) unless remote_dir == '.'

      total = ftp.size(filename)

      ftp.getbinaryfile(filename, nil, @ftp_data_chunk_size) do |chunk|
        temp_file << chunk
        size += chunk.size
        new_progress = (size * 100) / total
        unless new_progress == progress
          message "\rDownloading %s (%3d%%) " % [filename, new_progress]
        end
        progress = new_progress
      end
    end

    output

    temp_file.close
    File.unlink full_path if File.exists?(full_path)
    FileUtils.mkdir_p File.dirname(full_path)
    FileUtils.mv temp_file.path, full_path, :force => true

  rescue Exception => e
    File.unlink full_path if File.exists?(full_path)
    output "ERROR: #{e.message}"
    raise "Failed to download file"
  end
end
http_download(url, full_path, count) click to toggle source
# File lib/nabit/downloader.rb, line 45
    def http_download(url, full_path, count)

      begin
        uri = URI.parse(url)
        filename = File.basename(uri.path)

        if ENV['HTTP_PROXY']
          protocol, userinfo, proxy_host, proxy_port  = URI::split(ENV['HTTP_PROXY'])
          proxy_user, proxy_pass = userinfo.split(/:/) if userinfo
          http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port, proxy_user, proxy_pass)
        else
          http = Net::HTTP.new(uri.host, uri.port)
        end

        if uri.scheme.downcase == 'https'
          http.use_ssl = true
          if ENV['CA_CERT_FILE']
            cert_file = ENV['CA_CERT_FILE'].dup
            cert_file.gsub!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
          end
          if cert_file && File.exists?(cert_file)
            http.ca_file = cert_file
            http.verify_mode = OpenSSL::SSL::VERIFY_PEER
            http.verify_depth = @max_ca_verify_depth
          else
            raise <<-EOT
To download using HTTPS you must first set the CA_CERT_FILE
environment variable to the path of a valid CA certificate file.
A file of bundled public CA certs may be downloaded from:

   http://curl.haxx.se/ca/cacert.pem

            EOT
          end
        end

        http.request_get(uri.path) do |response|
          case response
          when Net::HTTPNotFound
            output "404 - Not Found"
            return false

          when Net::HTTPClientError
            output "Error: Client Error: #{response.inspect}"
            return false

          when Net::HTTPRedirection
            raise "Too many redirections for the original URL, halting." if count <= 0
            url = response["location"]
            return http_download(url, full_path, count - 1)

          when Net::HTTPOK
            temp_file = Tempfile.new("download-#{filename}")
            temp_file.binmode

            size = 0
            progress = 0
            total = response.header["Content-Length"].to_i

            response.read_body do |chunk|
              temp_file << chunk
              size += chunk.size
              new_progress = (size * 100) / total
              unless new_progress == progress
                message "\rDownloading %s (%3d%%) " % [filename, new_progress]
              end
              progress = new_progress
            end

            output

            temp_file.close
            File.unlink full_path if File.exists?(full_path)
            FileUtils.mkdir_p File.dirname(full_path)
            FileUtils.mv temp_file.path, full_path, :force => true
          end
        end

      rescue Exception => e
        File.unlink full_path if File.exists?(full_path)
        output "ERROR: #{e.message}"
        raise "Failed to download file"
      end
    end
message(text) click to toggle source
# File lib/nabit/downloader.rb, line 35
def message(text)
  @logger.print text
  @logger.flush
end
output(text = '') click to toggle source
# File lib/nabit/downloader.rb, line 40
def output(text = '')
  @logger.puts text
  @logger.flush
end