diff options
Diffstat (limited to 'lib/rubygems/request')
| -rw-r--r-- | lib/rubygems/request/connection_pools.rb | 96 | ||||
| -rw-r--r-- | lib/rubygems/request/http_pool.rb | 54 | ||||
| -rw-r--r-- | lib/rubygems/request/https_pool.rb | 10 |
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 |
