From 26d1d6e362e0e674d51e43dc3711a525933b7192 Mon Sep 17 00:00:00 2001 From: Moses Narrow <36607567+0pcom@users.noreply.github.com> Date: Tue, 7 Apr 2026 20:11:04 -0500 Subject: [PATCH] Fix server goroutine leak in forwardRequest: add handshake deadline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit forwardRequest opens a stream to the destination and reads the response. If the destination accepts but never responds, readObject blocks forever — observed as 2.4K+ stuck goroutines per server, growing rapidly after restart. The idle timeout fix (PR #372) only covered the bridge phase (CopyReadWriteCloser). This adds HandshakeTimeout to the forwardRequest handshake read, matching the client-side behavior. --- pkg/dmsg/server_session.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/dmsg/server_session.go b/pkg/dmsg/server_session.go index 92e6ba3c..1bec7842 100644 --- a/pkg/dmsg/server_session.go +++ b/pkg/dmsg/server_session.go @@ -366,12 +366,22 @@ func (ss *ServerSession) forwardRequest(req StreamRequest) (mStr io.ReadWriteClo return nil, nil, err } } + // Set a deadline for the forwarding handshake. If the destination visor + // accepts the stream but never responds, readObject blocks forever, + // leaking goroutines. Observed as 2K+ stuck goroutines per DMSG server. + if conn, ok := mStr.(net.Conn); ok { + conn.SetDeadline(time.Now().Add(HandshakeTimeout)) //nolint:errcheck,gosec + } if err = ss.writeObject(mStr, req.raw); err != nil { return nil, nil, err } if respObj, err = ss.readObject(mStr); err != nil { return nil, nil, err } + // Clear deadline before returning the stream for long-lived bridging. + if conn, ok := mStr.(net.Conn); ok { + conn.SetDeadline(time.Time{}) //nolint:errcheck,gosec + } var resp StreamResponse if resp, err = respObj.ObtainStreamResponse(); err != nil { return nil, nil, err