quic: add Http3Session, so you can explicitly pick the app protocol - #65993
quic: add Http3Session, so you can explicitly pick the app protocol#65993pimterry wants to merge 1 commit into
Conversation
|
Review requested:
|
Previously, ALPN decided automatically which application protocol implementation was used. QuicSession was used everywhere, but its actual behaviour and API changes implicitly based on the wire traffic involved. Now, QuicSession is used for pure QUIC only, and Http3Session is used for HTTP/3 sessions only. To do HTTP/3 on a connection, you enable it explicitly by wrapping a QUIC session in Http3Session. Doing so attaches the internal protocol application handling so everything is HTTP/3 on that session from that point onwards. For now, this only changes the application selection process and the top-level APIs involved, but none of the details. In a future PR, we can introduce Http3Stream and migrate other HTTP/3 specific functionality (e.g. SETTINGS & GOAWAY handling) out of the QUIC API. Signed-off-by: Tim Perry <pimterry@gmail.com>
da14bdc to
77cdc45
Compare
|
Ok, so it's good to get this isolated. Trust me, I'm not trying to be difficult! :-) ... Before going through the code let's see if we can settle on this. And yes! I can be convinced but it might take some doing. Let's take your points:
Useful in our tests does not mean useful in the general API That We Have To Support Indefinitely sense. Also, HTTP/2 is easier to do over raw TCP since it's just framing with no TLS, flow control, retransmission framing, etc. That said, we can do raw QUIC with a HTTP/3 ALPN without this kind of architectural change. It could be as simple as a boolean configuration option that says "use the default application with h3" in which case we simply don't install the Http3Application. Essentially, this point alone does not justify the re-architecture.
This is also something that could just be a config option. "Treat ALPN {FOO} like H3" in which case we install the Http3Application. Or if someone really wanted to implement all the H3 semantics themselves, see point one above... it could be as simple as a boolean configuration option. Still not a strong motivation for the re-architecture.
We can split QuicStream and Http3Stream purely at the JS level without any more C++ side redesign AND make it possible for applications that want to just handle raw QUIC but implement their own HTTP3 semantics to use Http3Stream without going with the dynamic attach path. I'm fine with splitting things at the JS level. You'll not get much argument from me there, but I strongly prefer the existing So my ask would be this: please take a moment to explain why the current architecture needs to be changed to meet the goals. What goals simply cannot be met with the current architecture or why does it make it harder? etc. What I'm not saying is that your suggested approach is wrong, not by any means... I just think there's more than one correct approach to achieve the goal and I haven't yet seen why the current approach isn't workable. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #65993 +/- ##
==========================================
- Coverage 89.98% 89.97% -0.01%
==========================================
Files 784 785 +1
Lines 268410 268751 +341
Branches 51123 51135 +12
==========================================
+ Hits 241520 241815 +295
- Misses 17433 17484 +51
+ Partials 9457 9452 -5
🚀 New features to boost your workflow:
|
This extracts dynamic application attach from #63995. This is still very much under debate, so I'll try to explain thoroughly:
Terminology
Sessionmakes calls into its Application any time it needs anything protocol-specific.Current state
Right now, we have fixed rules that lock which Application is used according to the ALPN negotation on the socket. We use ALPN to make this decision and attach the Application at the earliest possible moment.
For clients that means in the Session constructor (clients currently only support one fixed ALPN) and for servers it means in
OnClientHello, which is the moment we know which ALPN protocol we'll select.Why add dynamic attach
h3-*. It's perfectly valid to do this - if you agree onh3via ALPN you should speak HTTP/3, but the inverse is not required.How this works
This change drops the fixed Application selection & default ALPN configuration, and provides a new
Http3SessionAPI which lets JS control which Application is used directly. You wrap aQuicSessionin anHttp3Sessionand open your streams through the latter if you want to speak HTTP/3.It does this without changing the fundamental steps involved, just by reordering the work within. There's no additional callbacks, and no extra boundary crossing anywhere. Almost everything here is deferring work we're already doing (by moving application selection to the last minute) and offering JS APIs to preconfigure the attach logic.
What happens is:
session.opened).new Http3Session(quicSession)to use HTTP/3. This class:Sessionreads later when it needs an Application:session->EnsureApplication(). If no application has been attached yet, that checksstate.applicationType. If there's a pending HTTP/3 flag, it attaches the HTTP/3 application. If not, it attaches the default raw QUIC application. Attachment itself is the same as before, it just happens later, and checks the state flags instead of ALPN.The actual timing of the calls to
EnsureApplication()are what make this practical: it runs immediately afterMakeCallbackfires thelisten(cb)callback for server sessions, or afterclient.openedresolves. That guarantees a synchronous+microtasks window where the handshake information is available to JS but the application hasn't attached yet. So you can do this:This is possible strictly until the MakeCallback call for this existing event returns - there's no extra tick or delay introduced.
Performance
Benchmarks on my machine show no measurable impact on either the handshake (raw or H3) or H3 request (1RTT or 0RTT) benchmarks. Varying positive/negative on different runs, but always <0.5% and no significant results.
As noted above, this doesn't change when or how we cross between JS & C++. It primarily changes when the Application attach is done, not how. The implementation isn't literally free, but doesn't do anything substantial and I don't see any real-world impact in testing.
Other related changes
node:tls), so HTTP/3 is fully opt-in.Http3Session(this is slightly contrived now, since it could work, but sets a clear model and becomes more useful later).applicationoption for QuicSessions is moved to Http3Session assettings. This exclusively supports HTTP/3 settings, and makes no sense in its current form for anything else (QUIC can't use or validate it by itself even if it wanted to).What this PR does not do
There's many things from #63995 not included here, which I would like to do later, including:
Session. That means the session has to look up and verify the supported features before every HTTP call, store bits of HTTP-only state, and general be a bit inelegant in thinking about both protocols at the same time.connectHttp3orlistenHttp3. We could create API methods like this which preconfigure the ALPN & automatically attach the HTTP/3 session, as a more convenient API sugar. I think the conclusion was we'd rather not and focus on primitives instead, but personally I don't feel strongly either way.