Skip to content

Commit 06d982f

Browse files
committed
Remember if TCPSocket impl supports open_timeout
For open_timeout support detection, the previous implementation relied on an ArgumentError being raised and then rescued. In Ruby, rescue is a rather expensive operation and should be avoided when possible. This patch reduces the number of begin-rescues by remembering if the TCPSocket implementation supports open_timeout.
1 parent 09bf573 commit 06d982f

File tree

1 file changed

+25
-10
lines changed

1 file changed

+25
-10
lines changed

lib/net/http.rb

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,7 @@ def initialize(address, port = nil) # :nodoc:
11791179
@debug_output = options[:debug_output]
11801180
@response_body_encoding = options[:response_body_encoding]
11811181
@ignore_eof = options[:ignore_eof]
1182+
@tcpsocket_supports_open_timeout = nil
11821183

11831184
@proxy_from_env = false
11841185
@proxy_uri = nil
@@ -1655,16 +1656,30 @@ def connect
16551656

16561657
debug "opening connection to #{conn_addr}:#{conn_port}..."
16571658
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') || e.message.include?('wrong number of arguments (given 5, expected 2..4)'))
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-
}
1667-
end
1659+
s =
1660+
case @tcpsocket_supports_open_timeout
1661+
when nil, true
1662+
begin
1663+
# Use built-in timeout in TCPSocket.open if available
1664+
sock = TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout)
1665+
@tcpsocket_supports_open_timeout = true
1666+
sock
1667+
rescue ArgumentError => e
1668+
raise if !(e.message.include?('unknown keyword: :open_timeout') || e.message.include?('wrong number of arguments (given 5, expected 2..4)'))
1669+
@tcpsocket_supports_open_timeout = false
1670+
1671+
# Fallback to Timeout.timeout if TCPSocket.open does not support open_timeout
1672+
Timeout.timeout(@open_timeout, Net::OpenTimeout) {
1673+
TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
1674+
}
1675+
end
1676+
when false
1677+
# The current Ruby is known to not support TCPSocket(open_timeout:).
1678+
# Directly fall back to Timeout.timeout to avoid performance penalty incured by rescue.
1679+
Timeout.timeout(@open_timeout, Net::OpenTimeout) {
1680+
TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
1681+
}
1682+
end
16681683
rescue => e
16691684
e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions
16701685
raise e, "Failed to open TCP connection to " +

0 commit comments

Comments
 (0)