From 8cc45aae947d453acca029e13eb64f3f5f0bf942 Mon Sep 17 00:00:00 2001 From: drbrain Date: Mon, 31 Mar 2008 22:40:06 +0000 Subject: Import RubyGems 1.1.0 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15873 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rubygems/remote_fetcher.rb | 147 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 16 deletions(-) (limited to 'lib/rubygems/remote_fetcher.rb') diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index cb22e1f1b1..f49ee2f4a1 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -2,7 +2,6 @@ require 'net/http' require 'uri' require 'rubygems' -require 'rubygems/gem_open_uri' ## # RemoteFetcher handles the details of fetching gems and gem information from @@ -10,6 +9,8 @@ require 'rubygems/gem_open_uri' class Gem::RemoteFetcher + include Gem::UserInteraction + class FetchError < Gem::Exception; end @fetcher = nil @@ -29,6 +30,10 @@ class Gem::RemoteFetcher # HTTP_PROXY_PASS) # * :no_proxy: ignore environment variables and _don't_ use a proxy def initialize(proxy) + Socket.do_not_reverse_lookup = true + + @connections = {} + @requests = Hash.new 0 @proxy_uri = case proxy when :no_proxy then nil @@ -38,6 +43,65 @@ class Gem::RemoteFetcher end end + ## + # Moves the gem +spec+ from +source_uri+ to the cache dir unless it is + # already there. If the source_uri is local the gem cache dir copy is + # always replaced. + def download(spec, source_uri, install_dir = Gem.dir) + gem_file_name = "#{spec.full_name}.gem" + local_gem_path = File.join install_dir, 'cache', gem_file_name + + Gem.ensure_gem_subdirectories install_dir + + source_uri = URI.parse source_uri unless URI::Generic === source_uri + scheme = source_uri.scheme + + # URI.parse gets confused by MS Windows paths with forward slashes. + scheme = nil if scheme =~ /^[a-z]$/i + + case scheme + when 'http' then + unless File.exist? local_gem_path then + begin + say "Downloading gem #{gem_file_name}" if + Gem.configuration.really_verbose + + remote_gem_path = source_uri + "gems/#{gem_file_name}" + + gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path + rescue Gem::RemoteFetcher::FetchError + raise if spec.original_platform == spec.platform + + alternate_name = "#{spec.original_name}.gem" + + say "Failed, downloading gem #{alternate_name}" if + Gem.configuration.really_verbose + + remote_gem_path = source_uri + "gems/#{alternate_name}" + + gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path + end + + File.open local_gem_path, 'wb' do |fp| + fp.write gem + end + end + when nil, 'file' then # TODO test for local overriding cache + begin + FileUtils.cp source_uri.to_s, local_gem_path + rescue Errno::EACCES + local_gem_path = source_uri.to_s + end + + say "Using local gem #{local_gem_path}" if + Gem.configuration.really_verbose + else + raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}" + end + + local_gem_path + end + # Downloads +uri+. def fetch_path(uri) open_uri_or_path(uri) do |input| @@ -47,9 +111,8 @@ class Gem::RemoteFetcher raise FetchError, "timed out fetching #{uri}" rescue IOError, SocketError, SystemCallError => e raise FetchError, "#{e.class}: #{e} reading #{uri}" - rescue OpenURI::HTTPError => e - body = e.io.readlines.join "\n\t" - message = "#{e.class}: #{e} reading #{uri}\n\t#{body}" + rescue => e + message = "#{e.class}: #{e} reading #{uri}" raise FetchError, message end @@ -83,7 +146,8 @@ class Gem::RemoteFetcher end rescue SocketError, SystemCallError, Timeout::Error => e - raise FetchError, "#{e.message} (#{e.class})\n\tgetting size of #{uri}" + raise Gem::RemoteFetcher::FetchError, + "#{e.message} (#{e.class})\n\tgetting size of #{uri}" end private @@ -131,26 +195,77 @@ class Gem::RemoteFetcher # 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, &block) + def open_uri_or_path(uri, depth = 0, &block) if file_uri?(uri) open(get_file_uri_path(uri), &block) else - connection_options = { - "User-Agent" => "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}" - } + uri = URI.parse uri unless URI::Generic === uri + net_http_args = [uri.host, uri.port] + + if @proxy_uri then + net_http_args += [ @proxy_uri.host, + @proxy_uri.port, + @proxy_uri.user, + @proxy_uri.password + ] + end + + connection_id = net_http_args.join ':' + @connections[connection_id] ||= Net::HTTP.new(*net_http_args) + connection = @connections[connection_id] - if @proxy_uri - http_proxy_url = "#{@proxy_uri.scheme}://#{@proxy_uri.host}:#{@proxy_uri.port}" - connection_options[:proxy_http_basic_authentication] = [http_proxy_url, unescape(@proxy_uri.user)||'', unescape(@proxy_uri.password)||''] + if uri.scheme == 'https' && ! connection.started? + http_obj.use_ssl = true + http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE end - uri = URI.parse uri unless URI::Generic === uri + connection.start unless connection.started? + + request = Net::HTTP::Get.new(uri.request_uri) unless uri.nil? || uri.user.nil? || uri.user.empty? then - connection_options[:http_basic_authentication] = - [unescape(uri.user), unescape(uri.password)] + request.basic_auth(uri.user, uri.password) end - open(uri, connection_options, &block) + ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}" + ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}" + ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL + ua << ")" + + request.add_field 'User-Agent', ua + request.add_field 'Connection', 'keep-alive' + request.add_field 'Keep-Alive', '30' + + # HACK work around EOFError bug in Net::HTTP + retried = false + begin + @requests[connection_id] += 1 + response = connection.request(request) + rescue EOFError + requests = @requests[connection_id] + say "connection reset after #{requests} requests, retrying" if + Gem.configuration.really_verbose + + raise Gem::RemoteFetcher::FetchError, 'too many connection resets' if + retried + + @requests[connection_id] = 0 + + connection.finish + connection.start + retried = true + retry + end + + case response + when Net::HTTPOK then + block.call(StringIO.new(response.body)) if block + when Net::HTTPRedirection then + raise Gem::RemoteFetcher::FetchError, "too many redirects" if depth > 10 + open_uri_or_path(response['Location'], depth + 1, &block) + else + raise Gem::RemoteFetcher::FetchError, + "bad response #{response.message} #{response.code}" + end end end -- cgit v1.2.3