aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bundler/fetcher/downloader.rb
blob: 453e4645eb591b6e3d2993ae3f7d5c8fb31a228d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# frozen_string_literal: true
module Bundler
  class Fetcher
    class Downloader
      attr_reader :connection
      attr_reader :redirect_limit

      def initialize(connection, redirect_limit)
        @connection = connection
        @redirect_limit = redirect_limit
      end

      def fetch(uri, options = {}, counter = 0)
        raise HTTPError, "Too many redirects" if counter >= redirect_limit

        response = request(uri, options)
        Bundler.ui.debug("HTTP #{response.code} #{response.message} #{uri}")

        case response
        when Net::HTTPSuccess, Net::HTTPNotModified
          response
        when Net::HTTPRedirection
          new_uri = URI.parse(response["location"])
          if new_uri.host == uri.host
            new_uri.user = uri.user
            new_uri.password = uri.password
          end
          fetch(new_uri, options, counter + 1)
        when Net::HTTPRequestEntityTooLarge
          raise FallbackError, response.body
        when Net::HTTPUnauthorized
          raise AuthenticationRequiredError, uri.host
        when Net::HTTPNotFound
          raise FallbackError, "Net::HTTPNotFound"
        else
          raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}"
        end
      end

      def request(uri, options)
        validate_uri_scheme!(uri)

        Bundler.ui.debug "HTTP GET #{uri}"
        req = Net::HTTP::Get.new uri.request_uri, options
        if uri.user
          user = CGI.unescape(uri.user)
          password = uri.password ? CGI.unescape(uri.password) : nil
          req.basic_auth(user, password)
        end
        connection.request(uri, req)
      rescue NoMethodError => e
        raise unless ["undefined method", "use_ssl="].all? {|snippet| e.message.include? snippet }
        raise LoadError.new("cannot load such file -- openssl")
      rescue OpenSSL::SSL::SSLError
        raise CertificateFailureError.new(uri)
      rescue *HTTP_ERRORS => e
        Bundler.ui.trace e
        case e.message
        when /host down:/, /getaddrinfo: nodename nor servname provided/
          raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
            "connection and try again."
        else
          raise HTTPError, "Network error while fetching #{URICredentialsFilter.credential_filtered_uri(uri)}" \
            " (#{e})"
        end
      end

    private

      def validate_uri_scheme!(uri)
        return if uri.scheme =~ /\Ahttps?\z/
        raise InvalidOption,
          "The request uri `#{uri}` has an invalid scheme (`#{uri.scheme}`). " \
          "Did you mean `http` or `https`?"
      end
    end
  end
end