[rubygems/rubygems] Refactor Gem::RemoteFetcher::FetchError
initializer to build
method
The `initialize` method is already doing a lot and by adding the `Gem::PrintableUri` to redact sensitive information, things are getting complicated and hard to read here. For the start, I have refactored the `initialize` method into a class method called `build`. https://github.com/rubygems/rubygems/commit/4312e8fdf5
This commit is contained in:
parent
19e1d3cdce
commit
3adc141a79
Notes:
git
2021-08-31 19:06:58 +09:00
@ -5,6 +5,7 @@ require_relative 'request/connection_pools'
|
|||||||
require_relative 's3_uri_signer'
|
require_relative 's3_uri_signer'
|
||||||
require_relative 'uri_formatter'
|
require_relative 'uri_formatter'
|
||||||
require_relative 'uri_parser'
|
require_relative 'uri_parser'
|
||||||
|
require_relative 'printable_uri'
|
||||||
require_relative 'user_interaction'
|
require_relative 'user_interaction'
|
||||||
|
|
||||||
##
|
##
|
||||||
@ -21,19 +22,24 @@ class Gem::RemoteFetcher
|
|||||||
class FetchError < Gem::Exception
|
class FetchError < Gem::Exception
|
||||||
##
|
##
|
||||||
# The URI which was being accessed when the exception happened.
|
# The URI which was being accessed when the exception happened.
|
||||||
|
def self.build(message, uri)
|
||||||
|
original_uri = uri.dup
|
||||||
|
uri = Gem::PrintableUri.parse_uri(uri)
|
||||||
|
|
||||||
|
if uri.respond_to?(:original_password) && uri.original_password
|
||||||
|
message = message.sub(uri.original_password, 'REDACTED')
|
||||||
|
end
|
||||||
|
|
||||||
|
new(message, uri.to_s, original_uri)
|
||||||
|
end
|
||||||
|
|
||||||
attr_accessor :uri, :original_uri
|
attr_accessor :uri, :original_uri
|
||||||
|
|
||||||
def initialize(message, uri)
|
def initialize(message, uri, original_uri = nil)
|
||||||
super message
|
super message
|
||||||
|
|
||||||
uri = Gem::UriParser.parse_uri(uri)
|
@uri = uri
|
||||||
|
@original_uri = original_uri ? original_uri : uri
|
||||||
@original_uri = uri.dup
|
|
||||||
|
|
||||||
uri.password = 'REDACTED' if uri.respond_to?(:password) && uri.password
|
|
||||||
|
|
||||||
@uri = uri.to_s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s # :nodoc:
|
def to_s # :nodoc:
|
||||||
@ -219,20 +225,20 @@ class Gem::RemoteFetcher
|
|||||||
head ? response : response.body
|
head ? response : response.body
|
||||||
when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
|
when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
|
||||||
Net::HTTPTemporaryRedirect then
|
Net::HTTPTemporaryRedirect then
|
||||||
raise FetchError.new('too many redirects', uri) if depth > 10
|
raise FetchError.build('too many redirects', uri) if depth > 10
|
||||||
|
|
||||||
unless location = response['Location']
|
unless location = response['Location']
|
||||||
raise FetchError.new("redirecting but no redirect location was given", uri)
|
raise FetchError.build("redirecting but no redirect location was given", uri)
|
||||||
end
|
end
|
||||||
location = Gem::UriParser.parse_uri location
|
location = Gem::UriParser.parse_uri location
|
||||||
|
|
||||||
if https?(uri) && !https?(location)
|
if https?(uri) && !https?(location)
|
||||||
raise FetchError.new("redirecting to non-https resource: #{location}", uri)
|
raise FetchError.build("redirecting to non-https resource: #{location}", uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
fetch_http(location, last_modified, head, depth + 1)
|
fetch_http(location, last_modified, head, depth + 1)
|
||||||
else
|
else
|
||||||
raise FetchError.new("bad response #{response.message} #{response.code}", uri)
|
raise FetchError.build("bad response #{response.message} #{response.code}", uri)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -254,21 +260,21 @@ class Gem::RemoteFetcher
|
|||||||
begin
|
begin
|
||||||
data = Gem::Util.gunzip data
|
data = Gem::Util.gunzip data
|
||||||
rescue Zlib::GzipFile::Error
|
rescue Zlib::GzipFile::Error
|
||||||
raise FetchError.new("server did not return a valid file", uri)
|
raise FetchError.build("server did not return a valid file", uri)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
data
|
data
|
||||||
rescue Timeout::Error, IOError, SocketError, SystemCallError,
|
rescue Timeout::Error, IOError, SocketError, SystemCallError,
|
||||||
*(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e
|
*(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e
|
||||||
raise FetchError.new("#{e.class}: #{e}", uri)
|
raise FetchError.build("#{e.class}: #{e}", uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_s3(uri, mtime = nil, head = false)
|
def fetch_s3(uri, mtime = nil, head = false)
|
||||||
begin
|
begin
|
||||||
public_uri = s3_uri_signer(uri).sign
|
public_uri = s3_uri_signer(uri).sign
|
||||||
rescue Gem::S3URISigner::ConfigurationError, Gem::S3URISigner::InstanceProfileError => e
|
rescue Gem::S3URISigner::ConfigurationError, Gem::S3URISigner::InstanceProfileError => e
|
||||||
raise FetchError.new(e.message, "s3://#{uri.host}")
|
raise FetchError.build(e.message, "s3://#{uri.host}")
|
||||||
end
|
end
|
||||||
fetch_https public_uri, mtime, head
|
fetch_https public_uri, mtime, head
|
||||||
end
|
end
|
||||||
|
@ -127,7 +127,7 @@ class Gem::Request
|
|||||||
@connection_pool.checkout
|
@connection_pool.checkout
|
||||||
rescue Gem::HAVE_OPENSSL ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
|
rescue Gem::HAVE_OPENSSL ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
|
||||||
Errno::EHOSTDOWN => e
|
Errno::EHOSTDOWN => e
|
||||||
raise Gem::RemoteFetcher::FetchError.new(e.message, uri)
|
raise Gem::RemoteFetcher::FetchError.build(e.message, uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch
|
def fetch
|
||||||
@ -228,14 +228,14 @@ class Gem::Request
|
|||||||
|
|
||||||
reset connection
|
reset connection
|
||||||
|
|
||||||
raise Gem::RemoteFetcher::FetchError.new('too many bad responses', @uri) if bad_response
|
raise Gem::RemoteFetcher::FetchError.build('too many bad responses', @uri) if bad_response
|
||||||
|
|
||||||
bad_response = true
|
bad_response = true
|
||||||
retry
|
retry
|
||||||
rescue Net::HTTPFatalError
|
rescue Net::HTTPFatalError
|
||||||
verbose "fatal error"
|
verbose "fatal error"
|
||||||
|
|
||||||
raise Gem::RemoteFetcher::FetchError.new('fatal error', @uri)
|
raise Gem::RemoteFetcher::FetchError.build('fatal error', @uri)
|
||||||
# HACK work around EOFError bug in Net::HTTP
|
# HACK work around EOFError bug in Net::HTTP
|
||||||
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
||||||
# to install gems.
|
# to install gems.
|
||||||
@ -245,7 +245,7 @@ class Gem::Request
|
|||||||
requests = @requests[connection.object_id]
|
requests = @requests[connection.object_id]
|
||||||
verbose "connection reset after #{requests} requests, retrying"
|
verbose "connection reset after #{requests} requests, retrying"
|
||||||
|
|
||||||
raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried
|
raise Gem::RemoteFetcher::FetchError.build('too many connection resets', @uri) if retried
|
||||||
|
|
||||||
reset connection
|
reset connection
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ class TestGemCommandsSourcesCommand < Gem::TestCase
|
|||||||
|
|
||||||
uri = "http://beta-gems.example.com/specs.#{@marshal_version}.gz"
|
uri = "http://beta-gems.example.com/specs.#{@marshal_version}.gz"
|
||||||
@fetcher.data[uri] = proc do
|
@fetcher.data[uri] = proc do
|
||||||
raise Gem::RemoteFetcher::FetchError.new('it died', uri)
|
raise Gem::RemoteFetcher::FetchError.build('it died', uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
@cmd.handle_options %w[--add http://beta-gems.example.com]
|
@cmd.handle_options %w[--add http://beta-gems.example.com]
|
||||||
|
@ -204,7 +204,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||||||
@test_data
|
@test_data
|
||||||
end
|
end
|
||||||
|
|
||||||
raise Gem::RemoteFetcher::FetchError.new("haha!", '')
|
raise Gem::RemoteFetcher::FetchError.build("haha!", '')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -241,6 +241,36 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||||||
assert File.exist?(a1_cache_gem)
|
assert File.exist?(a1_cache_gem)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_download_with_token
|
||||||
|
a1_data = nil
|
||||||
|
File.open @a1_gem, 'rb' do |fp|
|
||||||
|
a1_data = fp.read
|
||||||
|
end
|
||||||
|
|
||||||
|
fetcher = util_fuck_with_fetcher a1_data
|
||||||
|
|
||||||
|
a1_cache_gem = @a1.cache_file
|
||||||
|
assert_equal a1_cache_gem, fetcher.download(@a1, 'http://token@gems.example.com')
|
||||||
|
assert_equal("http://token@gems.example.com/gems/a-1.gem",
|
||||||
|
fetcher.instance_variable_get(:@test_arg).to_s)
|
||||||
|
assert File.exist?(a1_cache_gem)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_download_with_x_oauth_basic
|
||||||
|
a1_data = nil
|
||||||
|
File.open @a1_gem, 'rb' do |fp|
|
||||||
|
a1_data = fp.read
|
||||||
|
end
|
||||||
|
|
||||||
|
fetcher = util_fuck_with_fetcher a1_data
|
||||||
|
|
||||||
|
a1_cache_gem = @a1.cache_file
|
||||||
|
assert_equal a1_cache_gem, fetcher.download(@a1, 'http://token:x-oauth-basic@gems.example.com')
|
||||||
|
assert_equal("http://token:x-oauth-basic@gems.example.com/gems/a-1.gem",
|
||||||
|
fetcher.instance_variable_get(:@test_arg).to_s)
|
||||||
|
assert File.exist?(a1_cache_gem)
|
||||||
|
end
|
||||||
|
|
||||||
def test_download_with_encoded_auth
|
def test_download_with_encoded_auth
|
||||||
a1_data = nil
|
a1_data = nil
|
||||||
File.open @a1_gem, 'rb' do |fp|
|
File.open @a1_gem, 'rb' do |fp|
|
||||||
|
@ -106,7 +106,7 @@ class TestGemResolverBestSet < Gem::TestCase
|
|||||||
|
|
||||||
error_uri = api_uri + 'a'
|
error_uri = api_uri + 'a'
|
||||||
|
|
||||||
error = Gem::RemoteFetcher::FetchError.new 'bogus', error_uri
|
error = Gem::RemoteFetcher::FetchError.build 'bogus', error_uri
|
||||||
|
|
||||||
set.replace_failed_api_set error
|
set.replace_failed_api_set error
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ class TestGemResolverBestSet < Gem::TestCase
|
|||||||
|
|
||||||
set.sets << index_set
|
set.sets << index_set
|
||||||
|
|
||||||
error = Gem::RemoteFetcher::FetchError.new 'bogus', @gem_repo
|
error = Gem::RemoteFetcher::FetchError.build 'bogus', @gem_repo
|
||||||
|
|
||||||
e = assert_raise Gem::RemoteFetcher::FetchError do
|
e = assert_raise Gem::RemoteFetcher::FetchError do
|
||||||
set.replace_failed_api_set error
|
set.replace_failed_api_set error
|
||||||
@ -145,7 +145,7 @@ class TestGemResolverBestSet < Gem::TestCase
|
|||||||
|
|
||||||
error_uri = api_uri + 'a'
|
error_uri = api_uri + 'a'
|
||||||
|
|
||||||
error = Gem::RemoteFetcher::FetchError.new 'bogus', error_uri
|
error = Gem::RemoteFetcher::FetchError.build 'bogus', error_uri
|
||||||
|
|
||||||
set.replace_failed_api_set error
|
set.replace_failed_api_set error
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ class TestGemSpecFetcher < Gem::TestCase
|
|||||||
def test_spec_for_dependency_bad_fetch_spec
|
def test_spec_for_dependency_bad_fetch_spec
|
||||||
src = Gem::Source.new(@gem_repo)
|
src = Gem::Source.new(@gem_repo)
|
||||||
def src.fetch_spec(name)
|
def src.fetch_spec(name)
|
||||||
raise Gem::RemoteFetcher::FetchError.new("bad news from the internet", @uri)
|
raise Gem::RemoteFetcher::FetchError.build("bad news from the internet", @uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
Gem.sources.replace [src]
|
Gem.sources.replace [src]
|
||||||
|
@ -3,17 +3,17 @@ require_relative 'helper'
|
|||||||
|
|
||||||
class TestRemoteFetchError < Gem::TestCase
|
class TestRemoteFetchError < Gem::TestCase
|
||||||
def test_password_redacted
|
def test_password_redacted
|
||||||
error = Gem::RemoteFetcher::FetchError.new('There was an error fetching', 'https://user:secret@gemsource.org')
|
error = Gem::RemoteFetcher::FetchError.build('There was an error fetching', 'https://user:secret@gemsource.org')
|
||||||
refute_match %r{secret}, error.to_s
|
refute_match %r{secret}, error.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_invalid_url
|
def test_invalid_url
|
||||||
error = Gem::RemoteFetcher::FetchError.new('There was an error fetching', 'https://::gemsource.org')
|
error = Gem::RemoteFetcher::FetchError.build('There was an error fetching', 'https://::gemsource.org')
|
||||||
assert_equal error.to_s, 'There was an error fetching (https://::gemsource.org)'
|
assert_equal error.to_s, 'There was an error fetching (https://::gemsource.org)'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_to_s
|
def test_to_s
|
||||||
error = Gem::RemoteFetcher::FetchError.new('There was an error fetching', 'https://gemsource.org')
|
error = Gem::RemoteFetcher::FetchError.build('There was an error fetching', 'https://gemsource.org')
|
||||||
assert_equal error.to_s, 'There was an error fetching (https://gemsource.org)'
|
assert_equal error.to_s, 'There was an error fetching (https://gemsource.org)'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -51,7 +51,7 @@ class Gem::FakeFetcher
|
|||||||
raise ArgumentError, 'need full URI' unless path.start_with?("https://", "http://")
|
raise ArgumentError, 'need full URI' unless path.start_with?("https://", "http://")
|
||||||
|
|
||||||
unless @data.key? path
|
unless @data.key? path
|
||||||
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
|
raise Gem::RemoteFetcher::FetchError.build("no data for #{path}", path)
|
||||||
end
|
end
|
||||||
|
|
||||||
if @data[path].kind_of?(Array) && @data[path].first.kind_of?(Array)
|
if @data[path].kind_of?(Array) && @data[path].first.kind_of?(Array)
|
||||||
@ -124,7 +124,7 @@ class Gem::FakeFetcher
|
|||||||
raise ArgumentError, 'need full URI' unless path =~ %r{^http://}
|
raise ArgumentError, 'need full URI' unless path =~ %r{^http://}
|
||||||
|
|
||||||
unless @data.key? path
|
unless @data.key? path
|
||||||
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
|
raise Gem::RemoteFetcher::FetchError.build("no data for #{path}", path)
|
||||||
end
|
end
|
||||||
|
|
||||||
data = @data[path]
|
data = @data[path]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user