Skip to content

Commit

Permalink
Merge branch 'master' into reload-pool
Browse files Browse the repository at this point in the history
  • Loading branch information
tenderlove authored Dec 4, 2024
2 parents df81992 + fad1632 commit 0428107
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 25 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1, jruby-9.2]
os: [ubuntu-20.04, windows-2019]
ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2, jruby-9.2]
os: [ubuntu-20.04, windows-2022]
include:
- { ruby: 3.1, os: ubuntu-20.04, matrix: pipeline }

Expand All @@ -23,13 +23,12 @@ jobs:
CI_MATRIX: ${{ matrix.matrix }}

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
rubygems: 3.3.6
bundler-cache: true

- name: Test things
Expand Down
24 changes: 24 additions & 0 deletions History.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
=== 4.0.5 / 2024-12-04

Bug fixes:

* Allow setting extra_chain_cert=

=== 4.0.4 / 2024-09-09

Bug fixes:

* Allow setting verify_hostname to false

=== 4.0.3 / 2024-09-09

Bug fixes:

* Handle Net::HTTP#verify_hostname was added in Ruby 3.0 or later. #120

=== 4.0.2 / 2023-03-29

Bug fixes:

* Fix compatibility with `connection_pool 2.4+`

=== 4.0.1 / 2021-01-12

Bug fixes:
Expand Down
2 changes: 1 addition & 1 deletion README.rdoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
= net-http-persistent

home :: https://github.com/drbrain/net-http-persistent
rdoc :: https://rdoc.info/gems/net-http-persistent
rdoc :: https://rubydoc.info/gems/net-http-persistent

== DESCRIPTION:

Expand Down
79 changes: 71 additions & 8 deletions lib/net/http/persistent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
# #ca_path :: Directory with certificate-authorities
# #cert_store :: An SSL certificate store
# #ciphers :: List of SSl ciphers allowed
# #extra_chain_cert :: Extra certificates to be added to the certificate chain
# #private_key :: The client's SSL private key
# #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new
# connection
Expand All @@ -73,6 +74,8 @@
# #verify_callback :: For server certificate verification
# #verify_depth :: Depth of certificate verification
# #verify_mode :: How connections should be verified
# #verify_hostname :: Use hostname verification for server certificate
# during the handshake
#
# == Proxies
#
Expand Down Expand Up @@ -179,7 +182,7 @@ class Net::HTTP::Persistent
##
# The version of Net::HTTP::Persistent you are using

VERSION = '4.0.1'
VERSION = '4.0.5'

##
# Error class for errors raised by Net::HTTP::Persistent. Various
Expand Down Expand Up @@ -270,6 +273,11 @@ def self.detect_idle_timeout uri, max = 10

attr_reader :ciphers

##
# Extra certificates to be added to the certificate chain

attr_reader :extra_chain_cert

##
# Sends debug_output to this IO via Net::HTTP#set_debug_output.
#
Expand Down Expand Up @@ -454,6 +462,21 @@ def self.detect_idle_timeout uri, max = 10

attr_reader :verify_mode

##
# HTTPS verify_hostname.
#
# If a client sets this to true and enables SNI with SSLSocket#hostname=,
# the hostname verification on the server certificate is performed
# automatically during the handshake using
# OpenSSL::SSL.verify_certificate_identity().
#
# You can set +verify_hostname+ as true to use hostname verification
# during the handshake.
#
# NOTE: This works with Ruby > 3.0.

attr_reader :verify_hostname

##
# Creates a new Net::HTTP::Persistent.
#
Expand Down Expand Up @@ -513,6 +536,7 @@ def initialize name: nil, proxy: nil, pool_size: DEFAULT_POOL_SIZE
@verify_callback = nil
@verify_depth = nil
@verify_mode = nil
@verify_hostname = nil
@cert_store = nil

@generation = 0 # incremented when proxy URI changes
Expand Down Expand Up @@ -574,6 +598,21 @@ def ciphers= ciphers
reconnect_ssl
end

if Net::HTTP.method_defined?(:extra_chain_cert=)
##
# Extra certificates to be added to the certificate chain.
# It is only supported starting from Net::HTTP version 0.1.1
def extra_chain_cert= extra_chain_cert
@extra_chain_cert = extra_chain_cert

reconnect_ssl
end
else
def extra_chain_cert= _extra_chain_cert
raise "extra_chain_cert= is not supported by this version of Net::HTTP"
end
end

##
# Creates a new connection for +uri+

Expand Down Expand Up @@ -612,13 +651,23 @@ def connection_for uri

return yield connection
rescue Errno::ECONNREFUSED
address = http.proxy_address || http.address
port = http.proxy_port || http.port
if http.proxy?
address = http.proxy_address
port = http.proxy_port
else
address = http.address
port = http.port
end

raise Error, "connection refused: #{address}:#{port}"
rescue Errno::EHOSTDOWN
address = http.proxy_address || http.address
port = http.proxy_port || http.port
if http.proxy?
address = http.proxy_address
port = http.proxy_port
else
address = http.address
port = http.port
end

raise Error, "host down: #{address}:#{port}"
ensure
Expand Down Expand Up @@ -982,8 +1031,10 @@ def ssl connection
connection.min_version = @min_version if @min_version
connection.max_version = @max_version if @max_version

connection.verify_depth = @verify_depth
connection.verify_mode = @verify_mode
connection.verify_depth = @verify_depth
connection.verify_mode = @verify_mode
connection.verify_hostname = @verify_hostname if
@verify_hostname != nil && connection.respond_to?(:verify_hostname=)

if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
Expand Down Expand Up @@ -1025,6 +1076,10 @@ def ssl connection
connection.key = @private_key
end

if defined?(@extra_chain_cert) and @extra_chain_cert
connection.extra_chain_cert = @extra_chain_cert
end

connection.cert_store = if @cert_store then
@cert_store
else
Expand Down Expand Up @@ -1092,6 +1147,15 @@ def verify_mode= verify_mode
reconnect_ssl
end

##
# Sets the HTTPS verify_hostname.

def verify_hostname= verify_hostname
@verify_hostname = verify_hostname

reconnect_ssl
end

##
# SSL verification callback.

Expand All @@ -1104,4 +1168,3 @@ def verify_callback= callback

require_relative 'persistent/connection'
require_relative 'persistent/pool'

1 change: 1 addition & 0 deletions lib/net/http/persistent/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def finish
ensure
reset
end
alias_method :close, :finish

def reset
@last_use = Net::HTTP::Persistent::EPOCH
Expand Down
30 changes: 21 additions & 9 deletions lib/net/http/persistent/pool.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,32 @@ def initialize(options = {}, &block)
end

def checkin net_http_args
stack = Thread.current[@key][net_http_args] ||= []
if net_http_args.is_a?(Hash) && net_http_args.size == 1 && net_http_args[:force]
# ConnectionPool 2.4+ calls `checkin(force: true)` after fork.
# When this happens, we should remove all connections from Thread.current
if stacks = Thread.current[@key]
stacks.each do |http_args, connections|
connections.each do |conn|
@available.push conn, connection_args: http_args
end
connections.clear
end
end
else
stack = Thread.current[@key][net_http_args] ||= []

raise ConnectionPool::Error, 'no connections are checked out' if
stack.empty?
raise ConnectionPool::Error, 'no connections are checked out' if
stack.empty?

conn = stack.pop
conn = stack.pop

if stack.empty?
@available.push conn, connection_args: net_http_args
if stack.empty?
@available.push conn, connection_args: net_http_args

Thread.current[@key].delete(net_http_args)
Thread.current[@key] = nil if Thread.current[@key].empty?
Thread.current[@key].delete(net_http_args)
Thread.current[@key] = nil if Thread.current[@key].empty?
end
end

nil
end

Expand Down
3 changes: 2 additions & 1 deletion lib/net/http/persistent/timed_stack_multi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def try_create options = {} # :nodoc:
if @created >= @max && @enqueued >= 1
oldest, = @lru.first
@lru.delete oldest
@ques[oldest].pop
connection = @ques[oldest].pop
connection.close if connection.respond_to?(:close)

@created -= 1
end
Expand Down
66 changes: 65 additions & 1 deletion test/test_net_http_persistent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ def proxy_address
end
def proxy_port
end
def proxy?
false
end
end

def basic_connection
Expand Down Expand Up @@ -244,6 +247,14 @@ def test_ciphers_equals
assert_equal 1, @http.ssl_generation
end

def test_extra_chain_cert_equals
skip 'extra_chain_cert is not supported by Net::HTTP' unless Net::HTTP.method_defined?(:extra_chain_cert)
@http.extra_chain_cert = :extra_chain_cert

assert_equal :extra_chain_cert, @http.extra_chain_cert
assert_equal 1, @http.ssl_generation
end

def test_connection_for
@http.open_timeout = 123
@http.read_timeout = 321
Expand Down Expand Up @@ -1267,6 +1278,7 @@ def test_ssl
assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
assert_kind_of OpenSSL::X509::Store, c.cert_store
assert_nil c.verify_callback
assert_nil c.verify_hostname if c.respond_to?(:verify_hostname)
end

def test_ssl_ca_file
Expand Down Expand Up @@ -1350,6 +1362,49 @@ def test_ssl_verify_mode
assert_equal OpenSSL::SSL::VERIFY_NONE, c.verify_mode
end

def test_ssl_enable_verify_hostname
skip 'OpenSSL is missing' unless HAVE_OPENSSL

@http.verify_hostname = true
c = Net::HTTP.new 'localhost', 80

skip 'net/http doesn\'t provide verify_hostname= method' unless
c.respond_to?(:verify_hostname=)

@http.ssl c

assert c.use_ssl?
assert c.verify_hostname
end

def test_ssl_disable_verify_hostname
skip 'OpenSSL is missing' unless HAVE_OPENSSL

@http.verify_hostname = false
c = Net::HTTP.new 'localhost', 80

skip 'net/http doesn\'t provide verify_hostname= method' unless
c.respond_to?(:verify_hostname=)

@http.ssl c

assert c.use_ssl?
assert c.verify_hostname == false
end

def test_ssl_extra_chain_cert
skip 'OpenSSL is missing' unless HAVE_OPENSSL
skip 'extra_chain_cert is not supported by Net::HTTP' unless Net::HTTP.method_defined?(:extra_chain_cert)

@http.extra_chain_cert = :extra_chain_cert
c = Net::HTTP.new 'localhost', 80

@http.ssl c

assert c.use_ssl?
assert_equal :extra_chain_cert, c.extra_chain_cert
end

def test_ssl_warning
skip 'OpenSSL is missing' unless HAVE_OPENSSL

Expand Down Expand Up @@ -1455,5 +1510,14 @@ def test_verify_mode_equals
assert_equal 1, @http.ssl_generation
end

end
def test_connection_pool_after_fork
# ConnectionPool 2.4+ calls `checkin(force: true)` after fork
@http.pool.checkin(force: true)

@http.pool.checkout ['example.com', 80, nil, nil, nil, nil]
@http.pool.checkin(force: true)
@http.pool.reload do |connection|
connection.close
end
end
end
Loading

0 comments on commit 0428107

Please sign in to comment.