123456789_123456789_123456789_123456789_123456789_

Class: Gem::CompactIndexClient::HTTPFetcher

Relationships & Source Files
Inherits: Object
Defined in: lib/rubygems/compact_index_client/http_fetcher.rb

Overview

Fetches compact index files relative to base_uri using Gem::RemoteFetcher's connection infrastructure (proxy, TLS, connection pooling). Implements the fetcher interface expected by ::Gem::CompactIndexClient: #call(path, headers) returning a ::Gem::Net::HTTP response.

Constant Summary

Class Method Summary

Instance Method Summary

Constructor Details

.new(base_uri, remote_fetcher = Gem::RemoteFetcher.fetcher) ⇒ HTTPFetcher

[ GitHub ]

  
# File 'lib/rubygems/compact_index_client/http_fetcher.rb', line 15

def initialize(base_uri, remote_fetcher = Gem::RemoteFetcher.fetcher)
  base_uri = base_uri.to_s
  base_uri += "/" unless base_uri.end_with?("/")
  @base_uri = Gem::URI(base_uri)
  @remote_fetcher = remote_fetcher
end

Instance Method Details

#bad_response(response, uri) (private)

[ GitHub ]

  
# File 'lib/rubygems/compact_index_client/http_fetcher.rb', line 79

def bad_response(response, uri)
  detail = response["X-Error-Message"] || response.message
  Gem::RemoteFetcher::FetchError.new("bad response #{detail} #{response.code}", uri)
end

#call(path, headers = {})

[ GitHub ]

  
# File 'lib/rubygems/compact_index_client/http_fetcher.rb', line 22

def call(path, headers = {})
  fetch(@base_uri + path, headers, REDIRECT_LIMIT)
end

#fetch(uri, headers, redirects_remaining) (private)

[ GitHub ]

  
# File 'lib/rubygems/compact_index_client/http_fetcher.rb', line 28

def fetch(uri, headers, redirects_remaining)
  response = request(uri, headers)

  case response
  when Gem::Net::HTTPNotModified
    response
  when Gem::Net::HTTPSuccess
    # The callers write the body into the cache, so a body-less success
    # such as 204 would truncate the cached file.
    raise bad_response(response, uri) unless response.class.body_permitted?

    response
  when Gem::Net::HTTPMovedPermanently, Gem::Net::HTTPFound, Gem::Net::HTTPSeeOther,
       Gem::Net::HTTPTemporaryRedirect, Gem::Net::HTTPPermanentRedirect
    raise Gem::RemoteFetcher::FetchError.new("too many redirects", uri) if redirects_remaining.zero?

    location = response["Location"]
    raise Gem::RemoteFetcher::FetchError.new("redirecting but no redirect location was given", uri) unless location

    redirect = uri + location
    if https?(uri) && !https?(redirect)
      raise Gem::RemoteFetcher::FetchError.new("redirecting to non-https resource: #{Gem::Uri.redact(redirect)}", uri)
    end
    # An absolute Location on the same host drops the credentials that a
    # relative one would have kept.
    redirect.userinfo = uri.userinfo if redirect.host == uri.host && !redirect.userinfo

    fetch(redirect, headers, redirects_remaining - 1)
  when Gem::Net::HTTPRangeNotSatisfiable
    raise bad_response(response, uri) unless headers.key?("Range")

    # The local cache is longer than the remote file, refetch it whole. A
    # matching ETag would otherwise turn the retry into a 304 and keep the
    # oversized cache.
    fetch(uri, headers.except("Range", "If-None-Match"), redirects_remaining)
  else
    raise bad_response(response, uri)
  end
end

#https?(uri) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubygems/compact_index_client/http_fetcher.rb', line 84

def https?(uri)
  uri.scheme == "https"
end

#request(uri, headers) (private)

The callers fall back to the Marshal index when a fetch fails, and they only recognize a failure that arrives as a FetchError.

[ GitHub ]

  
# File 'lib/rubygems/compact_index_client/http_fetcher.rb', line 70

def request(uri, headers)
  @remote_fetcher.request(uri, Gem::Net::HTTP::Get) do |req|
    headers.each {|name, value| req[name] = value }
  end
rescue Gem::Timeout::Error, IOError, SocketError, SystemCallError,
       *(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e
  raise Gem::RemoteFetcher::FetchError.new("#{e.class}: #{e}", uri)
end