diff options
author | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-11-29 06:52:18 +0000 |
---|---|---|
committer | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-11-29 06:52:18 +0000 |
commit | 9694bb8cac12969300692dac5a1cf7aa4e3a46cd (patch) | |
tree | c3cb423d701f7049ba9382de052e2a937cd1302d /lib/rubygems/remote_fetcher.rb | |
parent | 3f606b7063fc7a8b191556365ad343a314719a8d (diff) | |
download | ruby-9694bb8cac12969300692dac5a1cf7aa4e3a46cd.tar.gz |
* lib/rubygems*: Updated to RubyGems 2.0
* test/rubygems*: ditto.
* common.mk (prelude): Updated for RubyGems 2.0 source rearrangement.
* tool/change_maker.rb: Allow invalid UTF-8 characters in source
files.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37976 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rubygems/remote_fetcher.rb')
-rw-r--r-- | lib/rubygems/remote_fetcher.rb | 171 |
1 files changed, 117 insertions, 54 deletions
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 37699d17fc..4bb2e3604f 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -1,6 +1,7 @@ require 'rubygems' require 'rubygems/user_interaction' require 'uri' +require 'resolv' ## # RemoteFetcher handles the details of fetching gems and gem information from @@ -8,8 +9,6 @@ require 'uri' class Gem::RemoteFetcher - BuiltinSSLCerts = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__)) - include Gem::UserInteraction ## @@ -34,6 +33,13 @@ class Gem::RemoteFetcher end + ## + # A FetchError that indicates that the reason for not being + # able to fetch data was that the host could not be contacted + + class UnknownHostError < FetchError + end + @fetcher = nil ## @@ -53,8 +59,11 @@ class Gem::RemoteFetcher # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, # HTTP_PROXY_PASS) # * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy + # + # +dns+: An object to use for DNS resolution of the API endpoint. + # By default, use Resolv::DNS. - def initialize(proxy = nil) + def initialize(proxy=nil, dns=Resolv::DNS.new) require 'net/http' require 'stringio' require 'time' @@ -72,6 +81,27 @@ class Gem::RemoteFetcher else URI.parse(proxy) end @user_agent = user_agent + @env_no_proxy = get_no_proxy_from_env + + @dns = dns + end + + ## + # + # Given a source at +uri+, calculate what hostname to actually + # connect to query the data for it. + + def api_endpoint(uri) + host = uri.host + + begin + res = @dns.getresource "_rubygems._tcp.#{host}", + Resolv::DNS::Resource::IN::SRV + rescue Resolv::ResolvError + uri + else + URI.parse "#{res.target}#{uri.path}" + end end ## @@ -82,14 +112,13 @@ class Gem::RemoteFetcher # larger, more emcompassing effort. -erikh def download_to_cache dependency - found = Gem::SpecFetcher.fetcher.fetch dependency, true, true, - dependency.prerelease? + found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency return if found.empty? - spec, source_uri = found.sort_by { |(s,_)| s.version }.last + spec, source = found.sort_by { |(s,_)| s.version }.last - download spec, source_uri + download spec, source.uri.to_s end ## @@ -100,11 +129,14 @@ class Gem::RemoteFetcher def download(spec, source_uri, install_dir = Gem.dir) Gem.ensure_gem_subdirectories(install_dir) rescue nil - if File.writable?(install_dir) - cache_dir = File.join install_dir, "cache" - else - cache_dir = File.join Gem.user_dir, "cache" - end + cache_dir = + if Dir.pwd == install_dir then # see fetch_command + install_dir + elsif File.writable? install_dir then + File.join install_dir, "cache" + else + File.join Gem.user_dir, "cache" + end gem_file_name = File.basename spec.cache_file local_gem_path = File.join cache_dir, gem_file_name @@ -123,6 +155,8 @@ class Gem::RemoteFetcher # URI.parse gets confused by MS Windows paths with forward slashes. scheme = nil if scheme =~ /^[a-z]$/i + # REFACTOR: split this up and dispatch on scheme (eg download_http) + # REFACTOR: be sure to clean up fake fetcher when you do this... cleaner case scheme when 'http', 'https' then unless File.exist? local_gem_path then @@ -132,7 +166,7 @@ class Gem::RemoteFetcher remote_gem_path = source_uri + "gems/#{gem_file_name}" - gem = self.fetch_path remote_gem_path + self.cache_update_path remote_gem_path, local_gem_path rescue Gem::RemoteFetcher::FetchError raise if spec.original_platform == spec.platform @@ -143,11 +177,7 @@ class Gem::RemoteFetcher remote_gem_path = source_uri + "gems/#{alternate_name}" - gem = self.fetch_path remote_gem_path - end - - File.open local_gem_path, 'wb' do |fp| - fp.write gem + self.cache_update_path remote_gem_path, local_gem_path end end when 'file' then @@ -184,7 +214,7 @@ class Gem::RemoteFetcher say "Using local gem #{local_gem_path}" if Gem.configuration.really_verbose else - raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}" + raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}" end local_gem_path @@ -232,18 +262,54 @@ class Gem::RemoteFetcher uri = URI.parse uri unless URI::Generic === uri raise ArgumentError, "bad uri: #{uri}" unless uri - raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" unless - uri.scheme + + unless uri.scheme + raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" + end data = send "fetch_#{uri.scheme}", uri, mtime, head - data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/ + + if data and !head and uri.to_s =~ /gz$/ + begin + data = Gem.gunzip data + rescue Zlib::GzipFile::Error + raise FetchError.new("server did not return a valid file", uri.to_s) + end + end + data rescue FetchError raise rescue Timeout::Error - raise FetchError.new('timed out', uri.to_s) + raise UnknownHostError.new('timed out', uri.to_s) rescue IOError, SocketError, SystemCallError => e - raise FetchError.new("#{e.class}: #{e}", uri.to_s) + if e.message =~ /getaddrinfo/ + raise UnknownHostError.new('no such name', uri.to_s) + else + raise FetchError.new("#{e.class}: #{e}", uri.to_s) + end + end + + ## + # Downloads +uri+ to +path+ if necessary. If no path is given, it just + # passes the data. + + def cache_update_path(uri, path = nil) + mtime = path && File.stat(path).mtime rescue nil + + if mtime && Net::HTTPNotModified === fetch_path(uri, mtime, true) + Gem.read_binary(path) + else + data = fetch_path(uri) + + if path + open(path, 'wb') do |io| + io.write data + end + end + + data + end end ## @@ -274,6 +340,17 @@ class Gem::RemoteFetcher end ## + # Returns list of no_proxy entries (if any) from the environment + + def get_no_proxy_from_env + env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY'] + + return [] if env_no_proxy.nil? or env_no_proxy.empty? + + env_no_proxy.split(/\s*,\s*/) + end + + ## # Returns an HTTP proxy URI if one is set in the environment variables. def get_proxy_from_env @@ -296,7 +373,7 @@ class Gem::RemoteFetcher # Normalize the URI by adding "http://" if it is missing. def normalize_uri(uri) - (uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}" + (uri =~ /^(https?|ftp|file):/i) ? uri : "http://#{uri}" end ## @@ -306,7 +383,7 @@ class Gem::RemoteFetcher def connection_for(uri) net_http_args = [uri.host, uri.port] - if @proxy_uri then + if @proxy_uri and not no_proxy?(uri.host) then net_http_args += [ @proxy_uri.host, @proxy_uri.port, @@ -319,37 +396,23 @@ class Gem::RemoteFetcher @connections[connection_id] ||= Net::HTTP.new(*net_http_args) connection = @connections[connection_id] - if https?(uri) and !connection.started? then + if https?(uri) and not connection.started? then configure_connection_for_https(connection) - - # Don't refactor this with the else branch. We don't want the - # http-only code path to not depend on anything in OpenSSL. - # - begin - connection.start - rescue OpenSSL::SSL::SSLError, Errno::EHOSTDOWN => e - raise FetchError.new(e.message, uri) - end - else - begin - connection.start unless connection.started? - rescue Errno::EHOSTDOWN => e - raise FetchError.new(e.message, uri) - end end + connection.start unless connection.started? + connection + rescue OpenSSL::SSL::SSLError, Errno::EHOSTDOWN => e + raise FetchError.new(e.message, uri) end def configure_connection_for_https(connection) require 'net/https' - connection.use_ssl = true connection.verify_mode = Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER - store = OpenSSL::X509::Store.new - if Gem.configuration.ssl_ca_cert if File.directory? Gem.configuration.ssl_ca_cert store.add_path Gem.configuration.ssl_ca_cert @@ -360,12 +423,12 @@ class Gem::RemoteFetcher store.set_default_paths add_rubygems_trusted_certs(store) end - connection.cert_store = store end def add_rubygems_trusted_certs(store) - Dir.glob(BuiltinSSLCerts).each do |ssl_cert_file| + pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__)) + Dir.glob(pattern).each do |ssl_cert_file| store.add_file ssl_cert_file end end @@ -378,13 +441,13 @@ class Gem::RemoteFetcher end end - ## - # Read the data from the (source based) URI, but if it is a file:// URI, - # read from the filesystem instead. - - def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0) - raise "NO: Use fetch_path instead" - # TODO: deprecate for fetch_path + def no_proxy? host + host = host.downcase + @env_no_proxy.each do |pattern| + pattern = pattern.downcase + return true if host[-pattern.length, pattern.length ] == pattern + end + return false end ## |