From 57fd8adec9904976fe9b1bebd77b2ab196c1318f Mon Sep 17 00:00:00 2001 From: dotconfig404 Date: Tue, 11 Nov 2025 09:32:01 +0100 Subject: [PATCH] fix: preventing hang when TCPSocket readable, but no app-data available --- lib/net/http.rb | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 7fd4c3e..9eefb9a 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -2495,10 +2495,17 @@ def begin_transport(req) debug 'Conn close because of keep_alive_timeout' @socket.close connect - elsif @socket.io.to_io.wait_readable(0) && @socket.eof? - debug "Conn close because of EOF" - @socket.close - connect + elsif @socket.io.to_io.wait_readable(0) + # Check for EOF without blocking. + # With TLS 1.3, servers may send NewSessionTicket after responses, + # making the socket appear readable when only handshake data is + # pending. Using eof? here would block waiting for app data. + # See: https://bugs.ruby-lang.org/issues/19017 + if eof_without_blocking? + debug "Conn close because of EOF" + @socket.close + connect + end end end @@ -2527,6 +2534,21 @@ def end_transport(req, res) end end + def eof_without_blocking? + result = @socket.io.read_nonblock(1, exception: false) + case result + when nil + true + when :wait_readable, :wait_writable + false + when String + @socket.io.ungetc(result) + false + end + rescue EOFError + true + end + def keep_alive?(req, res) return false if req.connection_close? if @curr_http_version <= '1.0'