summaryrefslogtreecommitdiff
path: root/lib/rubygems
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2021-03-02 20:37:31 +0900
committerNARUSE, Yui <nurse@users.noreply.github.com>2021-03-11 17:24:52 +0900
commitf375bc77d2f347dd2a44705b8abd29398feae427 (patch)
tree0988ab2b519e713ae653cc2e23609b339a9b5979 /lib/rubygems
parent38f8b8d070aaac02f1d048b5d9947b2e58401e2b (diff)
Merge RubyGems-3.2.11 and Bundler-2.2.11
Diffstat (limited to 'lib/rubygems')
-rw-r--r--lib/rubygems/config_file.rb9
-rw-r--r--lib/rubygems/core_ext/tcpsocket_init.rb49
-rw-r--r--lib/rubygems/remote_fetcher.rb1
3 files changed, 59 insertions, 0 deletions
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 854d09ef3d..9dc41a2995 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -45,6 +45,7 @@ class Gem::ConfigFile
DEFAULT_UPDATE_SOURCES = true
DEFAULT_CONCURRENT_DOWNLOADS = 8
DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365
+ DEFAULT_IPV4_FALLBACK_ENABLED = false
##
# For Ruby packagers to set configuration defaults. Set in
@@ -141,6 +142,12 @@ class Gem::ConfigFile
attr_accessor :cert_expiration_length_days
##
+ # == Experimental ==
+ # Fallback to IPv4 when IPv6 is not reachable or slow (default: false)
+
+ attr_accessor :ipv4_fallback_enabled
+
+ ##
# Path name of directory or file of openssl client certificate, used for remote https connection with client authentication
attr_reader :ssl_client_cert
@@ -175,6 +182,7 @@ class Gem::ConfigFile
@update_sources = DEFAULT_UPDATE_SOURCES
@concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
@cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
+ @ipv4_fallback_enabled = ENV['IPV4_FALLBACK_ENABLED'] == 'true' || DEFAULT_IPV4_FALLBACK_ENABLED
operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
@@ -203,6 +211,7 @@ class Gem::ConfigFile
@disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
@sources = @hash[:sources] if @hash.key? :sources
@cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
+ @ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled
@ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
@ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
diff --git a/lib/rubygems/core_ext/tcpsocket_init.rb b/lib/rubygems/core_ext/tcpsocket_init.rb
new file mode 100644
index 0000000000..3275c58112
--- /dev/null
+++ b/lib/rubygems/core_ext/tcpsocket_init.rb
@@ -0,0 +1,49 @@
+require 'socket'
+
+module CoreExtensions
+ module TCPSocketExt
+ def self.prepended(base)
+ base.prepend Initializer
+ end
+
+ module Initializer
+ CONNECTION_TIMEOUT = 5
+ IPV4_DELAY_SECONDS = 0.1
+
+ def initialize(host, serv, *rest)
+ mutex = Mutex.new
+ addrs = []
+ cond_var = ConditionVariable.new
+
+ Addrinfo.foreach(host, serv, nil, :STREAM) do |addr|
+ Thread.report_on_exception = false if defined? Thread.report_on_exception = ()
+
+ Thread.new(addr) do
+ # give head start to ipv6 addresses
+ sleep IPV4_DELAY_SECONDS if addr.ipv4?
+
+ # raises Errno::ECONNREFUSED when ip:port is unreachable
+ Socket.tcp(addr.ip_address, serv, connect_timeout: CONNECTION_TIMEOUT).close
+ mutex.synchronize do
+ addrs << addr.ip_address
+ cond_var.signal
+ end
+ end
+ end
+
+ mutex.synchronize do
+ timeout_time = CONNECTION_TIMEOUT + Time.now.to_f
+ while addrs.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0
+ cond_var.wait(mutex, remaining_time)
+ end
+
+ host = addrs.shift unless addrs.empty?
+ end
+
+ super(host, serv, *rest)
+ end
+ end
+ end
+end
+
+TCPSocket.prepend CoreExtensions::TCPSocketExt
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index 53e840978c..7ac334d30d 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -78,6 +78,7 @@ class Gem::RemoteFetcher
# fetching the gem.
def initialize(proxy=nil, dns=nil, headers={})
+ require 'rubygems/core_ext/tcpsocket_init' if Gem.configuration.ipv4_fallback_enabled
require 'net/http'
require 'stringio'
require 'uri'