Skip to content

Commit 728eb8f

Browse files
committed
Replace Timeout.timeout with TCPSocket.open(open_timeout:) when available
This patch replaces the implementation of #open_timeout from Timeout.timeout from the builtin timeout in TCPSocket.open, which was introduced in Ruby 3.5 (https://bugs.ruby-lang.org/issues/21347). The builtin timeout in TCPSocket.open is better in several ways than Timeout.timeout. It does not rely on a separate Ruby Thread for monitoring Timeout (which is what the timeout library internally does). Furthermore, it is compatible with Ractors, as opposed to Timeout.timeout (it internally uses Thread::Mutex which can not be used in non-main Ractors). This change allows the following code to work. require 'net/http' Ractor.new { uri = URI('http://example.com/') http = Net::HTTP.new(uri.host, uri.port) http.open_timeout = 1 http.get(uri.path) }.value In Ruby <3.5 environments where `TCPSocket.open` does not have the `open_timeout` option, I have kept the behavior unchanged. net/http will use `Timeout.timeout { TCPSocket.open }`.
1 parent 30e6b5f commit 728eb8f

File tree

1 file changed

+15
-7
lines changed

1 file changed

+15
-7
lines changed

lib/net/http.rb

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,14 +1654,22 @@ def connect
16541654
end
16551655

16561656
debug "opening connection to #{conn_addr}:#{conn_port}..."
1657-
s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
1658-
begin
1659-
TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
1660-
rescue => e
1661-
raise e, "Failed to open TCP connection to " +
1662-
"#{conn_addr}:#{conn_port} (#{e.message})"
1657+
begin
1658+
s = begin
1659+
# Use built-in timeout in TCPSocket.open if available
1660+
TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout)
1661+
rescue ArgumentError => e
1662+
raise if !e.message.include?('unknown keyword: :open_timeout')
1663+
# Fallback to Timeout.timeout if TCPSocket.open does not support open_timeout
1664+
Timeout.timeout(@open_timeout, Net::OpenTimeout) {
1665+
TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
1666+
}
16631667
end
1664-
}
1668+
rescue => e
1669+
e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions
1670+
raise e, "Failed to open TCP connection to " +
1671+
"#{conn_addr}:#{conn_port} (#{e.message})"
1672+
end
16651673
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
16661674
debug "opened"
16671675
if use_ssl?

0 commit comments

Comments
 (0)