Skip to content

Commit 4666f41

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. First, it does not rely on a separate Ruby Thread for monitoring Timeout (which is what the timeout library internally does). Also, it is compatible with Ractors, since it does not rely on Mutexes (which is also what the timeout library does). 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 4666f41

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)