Skip to content

drpcserver: bound the ServeOne TLS handshake with an optional timeout#83

Open
rafiss wants to merge 1 commit into
mainfrom
tls-handshake-timeout
Open

drpcserver: bound the ServeOne TLS handshake with an optional timeout#83
rafiss wants to merge 1 commit into
mainfrom
tls-handshake-timeout

Conversation

@rafiss

@rafiss rafiss commented Jul 22, 2026

Copy link
Copy Markdown

ServeOne performs the TLS handshake explicitly (so it can populate peer connection info before serving requests), but that handshake had no deadline. A peer that completes the TCP connection and then stalls mid-handshake — never sending its ClientHello flight — would pin the serving goroutine and its file descriptor indefinitely.

This adds an Options.TLSHandshakeTimeout field. When positive, ServeOne sets a deadline on the connection for the duration of the handshake and clears it once the handshake returns, so it does not affect subsequent reads and writes on the established connection. A zero value preserves the existing behavior (no deadline), keeping the change backward compatible.

Tested with a stalled net.Pipe peer (handshake times out promptly) and a zero-timeout case (no deadline is set).

Informs: cockroachdb/cockroach#144754

Comment thread drpcserver/server.go Outdated
Comment on lines +163 to +170
handshakeTimeout := s.opts.TLSHandshakeTimeout
if handshakeTimeout > 0 {
_ = tlsConn.SetDeadline(time.Now().Add(handshakeTimeout))
}
err := tlsConn.HandshakeContext(ctx)
if handshakeTimeout > 0 {
_ = tlsConn.SetDeadline(time.Time{})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rafiss Could we use context.WithTimeout and pass the child context to HandshakeContext instead of changing the connection deadline? This preserves any existing connection deadline and respects an earlier parent context deadline. Go's TLS implementation closes the underlying connection when the handshake context is canceled, so stalled I/O is still interrupted without resetting connection state afterward.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I switched to context.WithTimeout(ctx, timeout), instead of setting/clearing the connection deadline. I had used the deadline approach since that was what was required in the first call-site where this got fixed (https://github.com/cockroachlabs/cockroach/pull/2482), since there was no context available there.

Claude agreed on all three points: since ServeOne already passes a cancelable context to HandshakeContext, the interrupt-on-cancel machinery is already in play, so this adds no extra complexity; it respects an earlier parent deadline/cancellation (e.g. server drain); and it touches no connection-deadline state, so the reset-after-handshake dance goes away entirely. Updated the test to cover both the timeout firing and an unset timeout still being interrupted by parent-context cancellation.

ServeOne performs the TLS handshake explicitly so it can populate the
peer connection info before serving requests. That handshake was bounded
only by the context passed to ServeOne, which typically carries no
deadline, so a peer that completes the TCP connection but then stalls
mid-handshake (never sending its ClientHello flight) would pin the
serving goroutine and its file descriptor indefinitely.

Add an Options.TLSHandshakeTimeout field. When positive, ServeOne derives
a child context with that timeout from the context it was given and passes
it to HandshakeContext; Go closes the underlying connection when the
handshake context is done, so a stalled handshake is interrupted without
touching the connection's own deadlines, and serving continues to use the
original context. Deriving the timeout from the parent context also means
an earlier parent deadline or cancellation still applies. A zero value
preserves the existing behavior (bounded only by the parent context),
keeping the change backward compatible.

Informs: cockroachdb/cockroach#144754

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
@rafiss
rafiss force-pushed the tls-handshake-timeout branch from 202df95 to 59cc501 Compare July 24, 2026 14:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants