(copy/pasted responses from claude)
I found a strong lead. Let me dig further — the Ruby net-http issue references an earlier patch that was rejected, and there's likely a similar story in net-http and possibly net-ssh.Getting closer. There's a key ticket — Ruby Feature #14430 — about replacing Timeout.timeout in net/http with Socket.tcp. Let me find the actual PR that landed and was reverted.Now let me look for the actual merge and revert pattern — particularly whether something was merged in net-http and then rolled back.Let me look for the actual net-http PR that landed and was reverted — there's a clear pattern of attempts here.Let me check ruby/net-http directly for a merged-then-reverted change.Let me check directly in the ruby/net-http repository for any merged-then-reverted activity around this.Found it. Mohamed Hafez submitted three PRs in April 2021 across net-http, net-smtp, and net-ftp. Let me confirm these were reverted.Let me directly check the net-http issue and PR #224 that tracked this to see if it was rolled back.Let me do one more targeted search to verify the revert.I found them. Your memory was close — these were on net-http/net-smtp/net-ftp rather than net-ssh, but the pattern matches exactly: timing window (April 2021, so ~5 years ago), three PRs from the same author, replacing Ruby's Timeout.timeout with the kernel's nonblocking connect via Socket.tcp's connect_timeout, and they ended up reverted.
The three PRs, all by Mohamed Hafez (mohamedhafez), April 2021:
- [ruby/net-ftp] Replace Timeout.timeout with socket timeout — commit a86c6cb3 (April 27, 2021)
- [ruby/net-smtp] Replace Timeout.timeout with socket timeout — commit ff931d03 / 6ae4a59f05 (April 28, 2021)
- [ruby/net-http] Replace Timeout.timeout in Net::HTTP#connect — commit 8a2b7b79 / 753cae3bbc (April 28, 2021)
The tracking issue is ruby/net-http#6, and the net-http PR is #224. Each used Socket.tcp(host, port, connect_timeout: ...) instead of wrapping TCPSocket.open in Timeout.timeout, which avoids spinning up a thread per call and is, as Hafez put it, "inefficient because it spins up a new thread for each invocation, and can be unsafe" — referencing Mike Perham's "Timeout: Ruby's Most Dangerous API" post and Headius's 2008 piece on Thread#raise.
Backstory — why this had been blocked for years before that:
- 2014: pranas opened ruby/ruby#597 / Bug #9744 trying the same thing. Rejected.
- 2016: xiewenwei opened ruby/ruby#1480 / Feature #12928. Stalled.
- 2018: Carl Hörberg opened ruby/ruby#1806 / Feature #14430. Eric Wong replied: "Unfortunately, we can't do this, yet. The Addrinfo calls use getaddrinfo(3) which doesn't support timeout natively."
The blocker was always the same: Timeout.timeout can't actually interrupt getaddrinfo(3) either (it's a blocking C call), but at least it could time out the whole operation. Switching to Socket.tcp meant DNS lookup couldn't be bounded on systems without getaddrinfo_a. Hafez referenced this directly: "Years ago, I submitted a patch to MRI ruby to replace the timeout in Net::HTTP#connect with nonblocking io, however it was shot down because there was no good way to time out the DNS lookup due to limitations in the C libraries available."
Why the 2021 PRs went in, then came back out:
By 2021, Socket.tcp had gained a :resolv_timeout option (Feature #15553, merged Ruby 2.7 — uses getaddrinfo_a where available), so Hafez believed the DNS problem was solved enough to retry. The changes did land, but the current state of ruby/net-smtp/lib/net/smtp.rb shows it's back to s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do tcp_socket(@address, @port) end — so the SMTP one was reverted outright. For net-http, the current code in ruby/net-http master does if TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT then TCPSocket.open(...open_timeout: @open_timeout) else Timeout.timeout(@open_timeout, Net::OpenTimeout) { TCPSocket.open(...) } end — so it's Timeout.timeout again unless you're on a very recent Ruby.
The "for some reason" you remember: the combined-timeout semantics. net stdlib uses a combined open_timeout covering DNS resolving + TCP connecting, while Socket.tcp has two separate timeouts — preserving the combined behaviour would mean using the open timeout for DNS resolution then using the remaining time for the connect timeout, and there's no clean way to express that without recreating much of what Timeout.timeout was doing. The proper fix didn't land until Feature #21347, which added open_timeout directly to Socket.tcp and TCPSocket.new as an overall timeout option — only available in Ruby 3.5+, which is why net-http carries the feature-detection fallback.
So: not net-ssh, but otherwise your memory was solid — three sibling PRs across the net-* family, kernel-native timeout via Socket.tcp(connect_timeout:), all from one author within 24 hours, reverted because the combined open_timeout semantics didn't survive the swap.
You're right, I only linked one. Let me dig up the actual PR numbers for net-ftp and net-smtp.I found the net-smtp revert (PR #51). Let me track down each repo's specific PR numbers.Fair point — I named the commits without giving you the PR URLs. Here's what I can pin down with direct links, and where I had to leave gaps:
net-http
- Tracking issue: ruby/net-http#6 (opened by mohamedhafez, Jan 2021)
- PR: ruby/net-http#224 — referenced today in the master source as "See discussion in ruby/net-http#224"
- Resulting commit: https://github.com/ruby/net-http/commit/753cae3bbc
net-smtp
- Resulting commit: https://github.com/ruby/net-smtp/commit/6ae4a59f05
- Revert PR: ruby/net-smtp#51 — "Revert 'Replace Timeout.timeout with socket timeout'" by @hsbt
net-ftp
- Resulting commit: https://github.com/ruby/net-ftp/commit/a86c6cb3 (April 27, 2021)
For the net-ftp original PR, net-ftp revert PR, net-smtp original PR, and net-http revert PR, I didn't find their numbers in my searches. The commits and the redmine bot mirror are confirmed, but the GitHub API is blocked from this environment, so I couldn't enumerate closed PRs against the three repos directly to map commit → PR. If you visit any of the three commit URLs above, GitHub will show the originating PR at the top — that'll fill in the gaps faster than more searching from my end.