Defer connectionEstablished observer callback and H2 auto-read until - #3461
Open
daschl wants to merge 2 commits into
Open
Defer connectionEstablished observer callback and H2 auto-read until#3461daschl wants to merge 2 commits into
daschl wants to merge 2 commits into
Conversation
daschl
force-pushed
the
ca-accept
branch
4 times, most recently
from
March 24, 2026 14:05
878ffe6 to
8fcc9cd
Compare
daschl
marked this pull request as ready for review
March 24, 2026 14:05
bryce-anderson
left a comment
Contributor
There was a problem hiding this comment.
It feels like the underlying problem is that we setup our pipelines too early. If that's true and how to address it might b a lot more work than we want to tackle.
after connection acceptors complete
Motivation:
Two related issues stem from the same root cause — actions that should
happen after all connection acceptors complete instead happen when the
connection Single resolves in tryCompleteSubscriber/completeSubscriber:
1. connectionEstablished/multiplexedConnectionEstablished observer
callbacks fire before Late/Legacy ConnectionAcceptors evaluate. This
means connection time measurement excludes acceptor time, and the
callback fires even if the acceptor later rejects the connection.
2. HTTP/2 server enables auto-read in AbstractH2ParentConnection
.handlerAdded() before acceptors evaluate. If the client's HTTP/2
preface and request arrive in the same TCP segment, the server
processes the request before the acceptor can reject — meaning
StreamingHttpService.handle() gets invoked for connections that
should have been rejected.
Modifications:
1. Add a deferConnectionEstablished parameter to
DefaultNettyConnection.initChannel() and guard the
connectionEstablished call in completeSubscriber(). Add a public
notifyConnectionEstablished(ConnectionObserver) method for callers
to invoke after acceptors pass.
2. Add a deferAutoRead parameter to AbstractH2ParentConnection. When
true, handlerAdded() no longer enables auto-read. The server passes
true; the client passes false (unchanged behavior).
3. Store the ConnectionObserver on NettyHttpServerConnection and
H2ServerParentConnectionContext so the connectionConsumer (which
runs after all acceptors) can invoke notifyConnectionEstablished()
followed by process(true) for H1 or setAutoRead(true) for H2.
4. Update all four server-side TcpServerBinder.bind() call sites
consistently: NettyHttpServer, H2ServerParentConnectionContext,
DeferredServerChannelBinder, and OptionalSslNegotiator.
5. Add new tests covering observer callback timing and H2 auto-read
deferral across H1, H2 prior-knowledge, and H2 ALPN code paths.
Result:
connectionEstablished/multiplexedConnectionEstablished observer
callbacks now fire after all connection acceptors complete, and only
when the connection is accepted. HTTP/2 auto-read is deferred until
the connectionConsumer runs, preventing requests from reaching the
service handler before acceptors can reject. Client-side behavior is
unchanged.
bryce-anderson
approved these changes
Apr 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
after connection acceptors complete
Motivation
Two related issues stem from the same root cause — actions that should happen after all connection acceptors complete instead happen when the connection Single resolves in
tryCompleteSubscriber/completeSubscriber:connectionEstablished/multiplexedConnectionEstablishedobserver callbacks fire before Late/LegacyConnectionAcceptorsevaluate. This means connection time measurement excludes acceptor time, and the callback fires even if the acceptor later rejects the connection.HTTP/2 server enables auto-read in
AbstractH2ParentConnection#handlerAdded()before acceptors evaluate. If the client's HTTP/2 preface and request arrive in the same TCP segment, the server processes the request before the acceptor can reject — meaningStreamingHttpService.handle()gets invoked for connections that should have been rejected.Modifications
Add a
deferConnectionEstablishedparameter toDefaultNettyConnection.initChannel()and guard theconnectionEstablishedcall incompleteSubscriber(). Add a method for callers to invoke after acceptors pass.Add a
deferAutoReadparameter toAbstractH2ParentConnection. When true,handlerAdded()no longer enables auto-read. The server passes true; the client passes false (unchanged behavior).Add new tests covering observer callback timing and H2 auto-read deferral across H1, H2 prior-knowledge, and H2 ALPN code paths.
Result
connectionEstablished/multiplexedConnectionEstablishedobservercallbacks now fire after all connection acceptors complete, and only
when the connection is accepted. HTTP/2 auto-read is deferred until
the connectionConsumer runs, preventing requests from reaching the
service handler before acceptors can reject. Client-side behavior is
unchanged.