summaryrefslogtreecommitdiff
path: root/lib/rubygems/request
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/request')
-rw-r--r--lib/rubygems/request/connection_pools.rb96
-rw-r--r--lib/rubygems/request/http_pool.rb54
-rw-r--r--lib/rubygems/request/https_pool.rb10
3 files changed, 160 insertions, 0 deletions
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
new file mode 100644
index 0000000000..01e7e0629a
--- /dev/null
+++ b/lib/rubygems/request/connection_pools.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+class Gem::Request::ConnectionPools # :nodoc:
+ @client = Gem::Net::HTTP
+
+ class << self
+ attr_accessor :client
+ end
+
+ def initialize(proxy_uri, cert_files, pool_size = 1)
+ @proxy_uri = proxy_uri
+ @cert_files = cert_files
+ @pools = {}
+ @pool_mutex = Thread::Mutex.new
+ @pool_size = pool_size
+ end
+
+ def pool_for(uri)
+ http_args = net_http_args(uri, @proxy_uri)
+ key = http_args + [https?(uri)]
+ @pool_mutex.synchronize do
+ @pools[key] ||=
+ if https? uri
+ Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri, @pool_size)
+ else
+ Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri, @pool_size)
+ end
+ end
+ end
+
+ def close_all
+ @pools.each_value(&:close_all)
+ end
+
+ private
+
+ ##
+ # 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? || env_no_proxy.empty?
+
+ env_no_proxy.split(/\s*,\s*/)
+ end
+
+ def https?(uri)
+ uri.scheme.casecmp("https").zero?
+ end
+
+ def no_proxy?(host, env_no_proxy)
+ host = host.downcase
+
+ env_no_proxy.any? do |pattern|
+ env_no_proxy_pattern = pattern.downcase.dup
+
+ # Remove dot in front of pattern for wildcard matching
+ env_no_proxy_pattern[0] = "" if env_no_proxy_pattern[0] == "."
+
+ host_tokens = host.split(".")
+ pattern_tokens = env_no_proxy_pattern.split(".")
+
+ intersection = (host_tokens - pattern_tokens) | (pattern_tokens - host_tokens)
+
+ # When we do the split into tokens we miss a dot character, so add it back if we need it
+ missing_dot = intersection.length > 0 ? 1 : 0
+ start = intersection.join(".").size + missing_dot
+
+ no_proxy_host = host[start..-1]
+
+ env_no_proxy_pattern == no_proxy_host
+ end
+ end
+
+ def net_http_args(uri, proxy_uri)
+ hostname = uri.hostname
+ net_http_args = [hostname, uri.port]
+
+ no_proxy = get_no_proxy_from_env
+
+ if proxy_uri && !no_proxy?(hostname, no_proxy)
+ proxy_hostname = proxy_uri.respond_to?(:hostname) ? proxy_uri.hostname : proxy_uri.host
+ net_http_args + [
+ proxy_hostname,
+ proxy_uri.port,
+ Gem::UriFormatter.new(proxy_uri.user).unescape,
+ Gem::UriFormatter.new(proxy_uri.password).unescape,
+ ]
+ elsif no_proxy? hostname, no_proxy
+ net_http_args + [nil, nil]
+ else
+ net_http_args
+ end
+ end
+end
diff --git a/lib/rubygems/request/http_pool.rb b/lib/rubygems/request/http_pool.rb
new file mode 100644
index 0000000000..468502ca6b
--- /dev/null
+++ b/lib/rubygems/request/http_pool.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+##
+# A connection "pool" that only manages one connection for now. Provides
+# thread safe `checkout` and `checkin` methods. The pool consists of one
+# connection that corresponds to `http_args`. This class is private, do not
+# use it.
+
+class Gem::Request::HTTPPool # :nodoc:
+ attr_reader :cert_files, :proxy_uri
+
+ def initialize(http_args, cert_files, proxy_uri, pool_size)
+ @http_args = http_args
+ @cert_files = cert_files
+ @proxy_uri = proxy_uri
+ @pool_size = pool_size
+
+ @queue = Thread::SizedQueue.new @pool_size
+ setup_queue
+ end
+
+ def checkout
+ @queue.pop || make_connection
+ end
+
+ def checkin(connection)
+ @queue.push connection
+ end
+
+ def close_all
+ until @queue.empty?
+ if (connection = @queue.pop(true)) && connection.started?
+ connection.finish
+ end
+ end
+
+ setup_queue
+ end
+
+ private
+
+ def make_connection
+ setup_connection Gem::Request::ConnectionPools.client.new(*@http_args)
+ end
+
+ def setup_connection(connection)
+ connection.start
+ connection
+ end
+
+ def setup_queue
+ @pool_size.times { @queue.push(nil) }
+ end
+end
diff --git a/lib/rubygems/request/https_pool.rb b/lib/rubygems/request/https_pool.rb
new file mode 100644
index 0000000000..cb1d4b59b6
--- /dev/null
+++ b/lib/rubygems/request/https_pool.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc:
+ private
+
+ def setup_connection(connection)
+ Gem::Request.configure_connection_for_https(connection, @cert_files)
+ super
+ end
+end