Skip to content

Commit 3059071

Browse files
committed
Replace Timeout.timeout with Socket.tcp(..., open_timeout:)
This patch replaces the implementation of #open_timeout from Timeout.timeout from the builtin timeout in Socket.tcp, which was introduced in Ruby 3.5 (https://bugs.ruby-lang.org/issues/21347). The builtin timeout in Socket.tcp 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
1 parent 30e6b5f commit 3059071

File tree

2 files changed

+15
-8
lines changed

2 files changed

+15
-8
lines changed

lib/net/http.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,14 +1654,21 @@ 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+
# Socket.tcp(..., open_timeout:) is a Ruby 3.5+ feature
1659+
s = if Socket.method(:tcp).parameters.any? {|param| param[0] == :key && param[1] == :open_timeout }
1660+
Socket.tcp(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout)
1661+
else
1662+
Timeout.timeout(@open_timeout, Net::OpenTimeout) {
1663+
TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
1664+
}
16631665
end
1664-
}
1666+
rescue Errno::ETIMEDOUT # We cannot change the exception raised from Socket.tcp
1667+
raise Net::OpenTimeout
1668+
rescue => e
1669+
raise e, "Failed to open TCP connection to " +
1670+
"#{conn_addr}:#{conn_port} (#{e.message})"
1671+
end
16651672
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
16661673
debug "opened"
16671674
if use_ssl?

test/net/http/test_http.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def test_failure_message_includes_failed_domain_and_port
246246
# hostname to be included in the error message
247247
host = Struct.new(:to_s).new("<example>")
248248
port = 2119
249-
# hack to let TCPSocket.open fail
249+
# hack to let Socket.tcp fail
250250
def host.to_str; raise SocketError, "open failure"; end
251251
uri = Struct.new(:scheme, :hostname, :port).new("http", host, port)
252252
assert_raise_with_message(SocketError, /#{host}:#{port}/) do

0 commit comments

Comments
 (0)