summaryrefslogtreecommitdiff
path: root/lib/rubygems/remote_fetcher.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/remote_fetcher.rb')
-rw-r--r--lib/rubygems/remote_fetcher.rb266
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)