From 8c76f9277916f13fa67300182d57f900f1e5bbc5 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 13:11:55 +0100 Subject: [PATCH 1/2] Check whether TCPSocket#initialize supports open_timeout once and without exceptions * See discussion in https://github.com/ruby/net-http/pull/224 * This check is known to work on at least CRuby, TruffleRuby and JRuby. * Exceptions show up with `ruby -d`/`$DEBUG == true` and would show for every Net::HTTP instance. --- lib/net/http.rb | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index f285bb0..b183d19 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1772,26 +1772,24 @@ def connect end private :connect + tcp_socket_parameters = TCPSocket.instance_method(:initialize).parameters + TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT = if tcp_socket_parameters != [[:rest]] + tcp_socket_parameters.include?([:key, :open_timeout]) + else + # Use Socket.tcp to find out since there is no parameters information for TCPSocket#initialize + # See discussion in https://github.com/ruby/net-http/pull/224 + Socket.method(:tcp).parameters.include?([:key, :open_timeout]) + end + private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT + def timeouted_connect(conn_addr, conn_port) - if @tcpsocket_supports_open_timeout == nil || @tcpsocket_supports_open_timeout == true - # Try to use built-in open_timeout in TCPSocket.open if: - # - The current Ruby runtime is known to support it, or - # - It is unknown whether the current Ruby runtime supports it (so we'll try). - begin - sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) - @tcpsocket_supports_open_timeout = true - return sock - rescue ArgumentError => e - raise if !(e.message.include?('unknown keyword: :open_timeout') || e.message.include?('wrong number of arguments (given 5, expected 2..4)')) - @tcpsocket_supports_open_timeout = false - end + if TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) + else + Timeout.timeout(@open_timeout, Net::OpenTimeout) { + TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) + } end - - # This Ruby runtime is known not to support `TCPSocket(open_timeout:)`. - # Directly fall back to Timeout.timeout to avoid performance penalty incured by rescue. - Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) - } end private :timeouted_connect From 7fe7bd4260917426bf3a1ad04f3070bbde3c8ca4 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 8 Dec 2025 13:18:41 +0100 Subject: [PATCH 2/2] Use fail-fast: false in CI --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 845bcca..ecf8152 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,6 +13,7 @@ jobs: needs: ruby-versions name: test (${{ matrix.ruby }} / ${{ matrix.os }}) strategy: + fail-fast: false matrix: ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} os: [ ubuntu-latest, macos-latest, windows-latest ]