From 8b424513fd43c398aaeea5dede8bbc5c51602773 Mon Sep 17 00:00:00 2001 From: drbrain Date: Thu, 26 Jun 2008 02:06:00 +0000 Subject: Upgrade to RubyGems 1.2.0 r1824. Incorporates [ruby-core:17353]. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@17576 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rubygems/remote_fetcher.rb | 104 ++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 49 deletions(-) (limited to 'lib/rubygems/remote_fetcher.rb') diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 93252fe83a..3c747f1d65 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -1,5 +1,6 @@ require 'net/http' require 'stringio' +require 'time' require 'uri' require 'rubygems' @@ -74,7 +75,12 @@ class Gem::RemoteFetcher # always replaced. def download(spec, source_uri, install_dir = Gem.dir) - cache_dir = File.join install_dir, 'cache' + if File.writable?(install_dir) + cache_dir = File.join install_dir, 'cache' + else + cache_dir = File.join(ENV['HOME'], '.gem', 'cache') + end + gem_file_name = "#{spec.full_name}.gem" local_gem_path = File.join cache_dir, gem_file_name @@ -132,49 +138,27 @@ class Gem::RemoteFetcher ## # Downloads +uri+ and returns it as a String. - def fetch_path(uri) - open_uri_or_path(uri) do |input| - input.read - end + def fetch_path(uri, mtime = nil, head = false) + data = open_uri_or_path(uri, mtime, head) + data = Gem.gunzip data if uri.to_s =~ /gz$/ and not head + data rescue FetchError raise rescue Timeout::Error raise FetchError.new('timed out', uri) rescue IOError, SocketError, SystemCallError => e raise FetchError.new("#{e.class}: #{e}", uri) - rescue => e - raise FetchError.new("#{e.class}: #{e}", uri) end ## # Returns the size of +uri+ in bytes. - def fetch_size(uri) - return File.size(get_file_uri_path(uri)) if file_uri? uri - - uri = URI.parse uri unless URI::Generic === uri - - raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri - - response = request uri, Net::HTTP::Head + def fetch_size(uri) # TODO: phase this out + response = fetch_path(uri, nil, true) - if response.code !~ /^2/ then - raise FetchError.new("bad response #{response.message} #{response.code}", uri) - end - - if response['content-length'] then - return response['content-length'].to_i - else - response = http.get uri.request_uri - return response.body.size - end - - rescue SocketError, SystemCallError, Timeout::Error => e - raise FetchError.new("#{e.message} (#{e.class})\n\tfetching size", uri) + response['content-length'].to_i end - private - def escape(str) return unless str URI.escape(str) @@ -245,24 +229,26 @@ 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, depth = 0, &block) - if file_uri?(uri) - open(get_file_uri_path(uri), &block) - else - uri = URI.parse uri unless URI::Generic === uri + def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0) + raise "block is dead" if block_given? - response = request uri + return open(get_file_uri_path(uri)) if file_uri? uri - case response - when Net::HTTPOK then - block.call(StringIO.new(response.body)) if block - when Net::HTTPRedirection then - raise FetchError.new('too many redirects', uri) if depth > 10 + uri = URI.parse uri unless URI::Generic === uri + raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri - open_uri_or_path(response['Location'], depth + 1, &block) - else - raise FetchError.new("bad response #{response.message} #{response.code}", uri) - end + fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get + response = request uri, fetch_type, last_modified + + case response + when Net::HTTPOK then + head ? response : response.body + when Net::HTTPRedirection then + raise FetchError.new('too many redirects', uri) if depth > 10 + + open_uri_or_path(response['Location'], last_modified, head, depth + 1) + else + raise FetchError.new("bad response #{response.message} #{response.code}", uri) end end @@ -271,7 +257,7 @@ class Gem::RemoteFetcher # a Net::HTTP response object. request maintains a table of persistent # connections to reduce connect overhead. - def request(uri, request_class = Net::HTTP::Get) + 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 @@ -287,9 +273,14 @@ class Gem::RemoteFetcher request.add_field 'Connection', 'keep-alive' request.add_field 'Keep-Alive', '30' + if last_modified then + request.add_field 'If-Modified-Since', last_modified.rfc2822 + end + connection = connection_for uri retried = false + bad_response = false # HACK work around EOFError bug in Net::HTTP # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible @@ -299,6 +290,13 @@ class Gem::RemoteFetcher response = connection.request request say "#{request.method} #{response.code} #{response.message}: #{uri}" if Gem.configuration.really_verbose + rescue Net::HTTPBadResponse + reset connection + + raise FetchError.new('too many bad responses', uri) if bad_response + + bad_response = true + retry rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET requests = @requests[connection.object_id] say "connection reset after #{requests} requests, retrying" if @@ -306,10 +304,8 @@ class Gem::RemoteFetcher raise FetchError.new('too many connection resets', uri) if retried - @requests.delete connection.object_id + reset connection - connection.finish - connection.start retried = true retry end @@ -317,6 +313,16 @@ class Gem::RemoteFetcher response end + ## + # Resets HTTP connection +connection+. + + def reset(connection) + @requests.delete connection.object_id + + connection.finish + connection.start + end + ## # Checks if the provided string is a file:// URI. -- cgit v1.2.3