diff options
Diffstat (limited to 'lib/rubygems/remote_fetcher.rb')
-rw-r--r-- | lib/rubygems/remote_fetcher.rb | 266 |
1 files changed, 7 insertions, 259 deletions
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 86bad9de41..6abd6bd9db 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -1,7 +1,7 @@ require 'rubygems' +require 'rubygems/request' +require 'rubygems/uri_formatter' require 'rubygems/user_interaction' -require 'thread' -require 'uri' require 'resolv' ## @@ -72,18 +72,7 @@ class Gem::RemoteFetcher Socket.do_not_reverse_lookup = true - @connections = {} - @connections_mutex = Mutex.new - @requests = Hash.new 0 - @proxy_uri = - case proxy - when :no_proxy then nil - when nil then get_proxy_from_env - when URI::HTTP then proxy - else URI.parse(proxy) - end - @user_agent = user_agent - @env_no_proxy = get_no_proxy_from_env + @proxy = proxy @dns = dns end @@ -202,7 +191,7 @@ class Gem::RemoteFetcher source_uri.path end - source_path = unescape source_path + source_path = Gem::UriFormatter.new(source_path).unescape begin FileUtils.cp source_path, local_gem_path unless @@ -321,128 +310,6 @@ class Gem::RemoteFetcher response['content-length'].to_i end - def escape(str) - return unless str - @uri_parser ||= uri_escaper - @uri_parser.escape str - end - - def unescape(str) - return unless str - @uri_parser ||= uri_escaper - @uri_parser.unescape str - end - - def uri_escaper - URI::Parser.new - rescue NameError - URI - 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 - env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] - - return nil if env_proxy.nil? or env_proxy.empty? - - uri = URI.parse(normalize_uri(env_proxy)) - - if uri and uri.user.nil? and uri.password.nil? then - # Probably we have http_proxy_* variables? - uri.user = escape(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']) - uri.password = escape(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']) - end - - uri - end - - ## - # Normalize the URI by adding "http://" if it is missing. - - def normalize_uri(uri) - (uri =~ /^(https?|ftp|file):/i) ? uri : "http://#{uri}" - end - - ## - # Creates or an HTTP connection based on +uri+, or retrieves an existing - # connection, using a proxy if needed. - - def connection_for(uri) - net_http_args = [uri.host, uri.port] - - if @proxy_uri and not no_proxy?(uri.host) then - net_http_args += [ - @proxy_uri.host, - @proxy_uri.port, - @proxy_uri.user, - @proxy_uri.password - ] - end - - connection_id = [Thread.current.object_id, *net_http_args].join ':' - - connection = @connections_mutex.synchronize do - @connections[connection_id] ||= Net::HTTP.new(*net_http_args) - @connections[connection_id] - end - - if https?(uri) and not connection.started? then - configure_connection_for_https(connection) - end - - connection.start unless connection.started? - - connection - rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN, - 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 - else - store.add_file Gem.configuration.ssl_ca_cert - end - else - store.set_default_paths - add_rubygems_trusted_certs(store) - end - connection.cert_store = store - rescue LoadError => e - raise unless (e.respond_to?(:path) && e.path == 'openssl') || - e.message =~ / -- openssl$/ - - raise Gem::Exception.new( - 'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources') - end - - def add_rubygems_trusted_certs(store) - 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 - def correct_for_windows_path(path) if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':' path = path[1..-1] @@ -451,136 +318,17 @@ class Gem::RemoteFetcher end end - 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 - ## # Performs a Net::HTTP request of type +request_class+ on +uri+ returning # a Net::HTTP response object. request maintains a table of persistent # connections to reduce connect overhead. def request(uri, request_class, last_modified = nil) - request = request_class.new uri.request_uri - - unless uri.nil? || uri.user.nil? || uri.user.empty? then - request.basic_auth uri.user, uri.password - end - - request.add_field 'User-Agent', @user_agent - request.add_field 'Connection', 'keep-alive' - request.add_field 'Keep-Alive', '30' - - if last_modified then - last_modified = last_modified.utc - request.add_field 'If-Modified-Since', last_modified.rfc2822 - end - - yield request if block_given? - - connection = connection_for uri - - retried = false - bad_response = false - - begin - @requests[connection.object_id] += 1 - - say "#{request.method} #{uri}" if - Gem.configuration.really_verbose - - file_name = File.basename(uri.path) - # perform download progress reporter only for gems - if request.response_body_permitted? && file_name =~ /\.gem$/ - reporter = ui.download_reporter - response = connection.request(request) do |incomplete_response| - if Net::HTTPOK === incomplete_response - reporter.fetch(file_name, incomplete_response.content_length) - downloaded = 0 - data = '' - - incomplete_response.read_body do |segment| - data << segment - downloaded += segment.length - reporter.update(downloaded) - end - reporter.done - if incomplete_response.respond_to? :body= - incomplete_response.body = data - else - incomplete_response.instance_variable_set(:@body, data) - end - end - end - else - response = connection.request request - end - - say "#{response.code} #{response.message}" if - Gem.configuration.really_verbose - - rescue Net::HTTPBadResponse - say "bad response" if Gem.configuration.really_verbose - - reset connection - - raise FetchError.new('too many bad responses', uri) if bad_response - - bad_response = true - retry - # HACK work around EOFError bug in Net::HTTP - # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible - # to install gems. - rescue EOFError, Timeout::Error, - Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE - - requests = @requests[connection.object_id] - say "connection reset after #{requests} requests, retrying" if - Gem.configuration.really_verbose - - raise FetchError.new('too many connection resets', uri) if retried - - reset connection - - retried = true - retry - end + request = Gem::Request.new uri, request_class, last_modified, @proxy - response - end - - ## - # Resets HTTP connection +connection+. - - def reset(connection) - @requests.delete connection.object_id - - connection.finish - connection.start - end - - def user_agent - ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}" - - ruby_version = RUBY_VERSION - ruby_version += 'dev' if RUBY_PATCHLEVEL == -1 - - ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}" - if RUBY_PATCHLEVEL >= 0 then - ua << " patchlevel #{RUBY_PATCHLEVEL}" - elsif defined?(RUBY_REVISION) then - ua << " revision #{RUBY_REVISION}" + request.fetch do |req| + yield req if block_given? end - ua << ")" - - ua << " #{RUBY_ENGINE}" if defined?(RUBY_ENGINE) and RUBY_ENGINE != 'ruby' - - ua end def https?(uri) |