diff options
| author | Daisuke Aritomo <osyoyu@osyoyu.com> | 2025-12-04 21:52:53 +0900 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2025-12-05 12:34:14 +0000 |
| commit | ea415e9636d3be8890d5dc97cf0b67fc95403a46 (patch) | |
| tree | d13970bc1d29d6d49b3e435e32256f5cd1ab3bf2 | |
| parent | f179885d3c454c6a98c23b2a977480657bb0f676 (diff) | |
[ruby/net-http] open: Never call Timeout.timeout in rescue clause
The try-open_timeout-then-fallback-to-timeout introduced in
https://github.com/ruby/net-http/commit/1903cedd8cd0 works well, but when it errors
due to any reason in Rubies which do not support `open_timeout`, it
spits the rescued ArgumentError that is unrelated to user code and not
actionable.
Net::HTTP.start('foo.bar', 80)
/.../net-http-0.8.0/lib/net/http.rb:1691:in 'TCPSocket#initialize': Failed to open TCP connection to foo.bar:80 (getaddrinfo(3): nodename nor servname provided, or not known) (Socket::ResolutionError)
from /.../net-http-0.8.0/lib/net/http.rb:1691:in 'IO.open'
from /.../net-http-0.8.0/lib/net/http.rb:1691:in 'block in Net::HTTP#connect'
from /.../timeout-0.4.4/lib/timeout.rb:188:in 'block in Timeout.timeout'
from /.../timeout-0.4.4/lib/timeout.rb:195:in 'Timeout.timeout'
from /.../net-http-0.8.0/lib/net/http.rb:1690:in 'Net::HTTP#connect'
from /.../net-http-0.8.0/lib/net/http.rb:1655:in 'Net::HTTP#do_start'
from /.../net-http-0.8.0/lib/net/http.rb:1635:in 'Net::HTTP#start'
from /.../net-http-0.8.0/lib/net/http.rb:1064:in 'Net::HTTP.start'
(snip)
/.../net-http-0.8.0/lib/net/http.rb:1682:in 'TCPSocket#initialize': unknown keyword: :open_timeout (ArgumentError)
sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
from /.../net-http-0.8.0/lib/net/http.rb:1682:in 'IO.open'
from /.../net-http-0.8.0/lib/net/http.rb:1682:in 'Net::HTTP#connect'
from /.../net-http-0.8.0/lib/net/http.rb:1655:in 'Net::HTTP#do_start'
from /.../net-http-0.8.0/lib/net/http.rb:1635:in 'Net::HTTP#start'
from /.../net-http-0.8.0/lib/net/http.rb:1064:in 'Net::HTTP.start'
(snip)
... 8 levels...
This patch suppresses the ArgumentError by moving the retry out of the
rescue clause.
https://github.com/ruby/net-http/commit/86232d62f5
| -rw-r--r-- | lib/net/http.rb | 48 |
1 files changed, 24 insertions, 24 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb index 4205d41460..8a4ff48187 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1674,30 +1674,7 @@ module Net #:nodoc: debug "opening connection to #{conn_addr}:#{conn_port}..." begin - s = - case @tcpsocket_supports_open_timeout - when nil, true - begin - # Use built-in timeout in TCPSocket.open if available - sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) - @tcpsocket_supports_open_timeout = true - 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 - - # Fallback to Timeout.timeout if TCPSocket.open does not support open_timeout - Timeout.timeout(@open_timeout, Net::OpenTimeout) { - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port) - } - end - when false - # The current Ruby is known to not 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 + s = timeouted_connect(conn_addr, conn_port) rescue => e e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions raise e, "Failed to open TCP connection to " + @@ -1795,6 +1772,29 @@ module Net #:nodoc: end private :connect + 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 + 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 + def on_connect end private :on_connect |
