H3 consolidation: merge client and server code - #9
Merged
Merged
Conversation
The QUIC/HTTP3 server and client were two near-identical files: the read path, the write path, flow control, the ngtcp2/nghttp3 callback bridges and the write loop existed twice, and every fix had to be applied twice, in parallel, to both. A QUIC connection is symmetric, and so is an HTTP/3 stream -- what differs between the roles is only which half of the exchange travels in which direction. So both now share one implementation, phrased in terms of the *incoming* and the *outgoing* message: * http3::Http3Stream (anyhttp/http3_stream.hpp, src/http3_stream.cpp) -- one request/response exchange: read path, write path, header parsing, lifecycle, plus the Http3Reader/Http3Writer adapter templates. Role hooks: on_pseudo_header(), on_headers_complete(), on_failed(), submit_response(). * http3::Http3Session (anyhttp/http3_session.hpp, src/http3_session.cpp) -- one QUIC connection: every callback bridge, write_pkt/write_streams, the timers, flow control, setup_http3(), setup_tls(). Role hooks: handle_error(), send_datagrams(), make_stream(), on_http3_ready(), on_new_cid()/on_remove_cid(). * anyhttp/http3_common.hpp -- make_nv(), log_headers(), the ngtcp2 log callback and the shared constants. What is left in server_impl_udp.cpp is what is genuinely server-side: the TLS server context, the UDP demux (many connections over one socket, a CID table, a strand and a dup()ed fd per session) and the closing/draining bookkeeping. In client_impl_udp.cpp: the TLS client context, the connect()ed socket and its receive loop, wait_ready() and async_submit(). 4902 lines became 3900, of which 2250 are now shared. Making the two sides one implementation also made them behave alike: the client now uses the server's ngtcp2_conn_write_aggregate_pkt2() write path (splitting the buffer by gso_size, without UDP_SEGMENT), both roles register nghttp3's acked_stream_data, both send STOP_SENDING when a reader is dropped early, both support read cancellation, and stream failure reporting goes through one Http3Stream::fail(). The one thing that stayed apart is how a body is handed to nghttp3, now spelled out as WriteMode: the server points nghttp3 straight into the caller's buffer and completes the write on acknowledgement, while the client stages through a bounded copy so that cancelling a write leaves the stream intact. Everything around it is shared. Three bugs found on the way, fixed here: * async_get_response() on a stream that had already failed waited forever, because only a handler installed *before* the failure was ever completed. The stream now remembers why it died and answers a late caller right away. * Write completions ran the application from inside a nghttp3 callback, i.e. from inside ngtcp2: a handler destroying the session there wrote a CONNECTION_CLOSE with a fresh timestamp and the enclosing write pass then continued with its own, stale one, tripping ngtcp2's "conn->log.last_ts <= ts" assertion. Completions are posted now, and write_pkt() stops packing when the connection went away underneath it. * Delivering a response can drop the last reference to its stream, so async_get_response() holds one of its own (found by ASAN). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each protocol implementation now lives in files prefixed h1_ (beast), h2_ (nghttp2) and h3_ (ngtcp2/nghttp3), and the protocol libraries no longer leak out of them: <nghttp2/*> is included only by h2_* files, <ngtcp2/*>/<nghttp3/*> only by h3_* ones. Four places needed more than a rename: * formatter.hpp pulled in <nghttp2/nghttp2.h> for the nghttp2_nv formatter, which moves to h2_common.hpp (absorbing nghttp2_common.hpp). * server_impl.cpp / client_impl.cpp instantiated the h1 and h2 session templates themselves. They now go through make_server_session() / make_client_session(), declared in h1_backend.hpp / h2_backend.hpp in terms of asio types only and defined in the backends' own translation units, which are the only ones instantiating those templates. * the server's ALPN callback called nghttp2_select_next_protocol(), replaced by a preference-ordered selection over the ALPN wire format. * Server::Impl held the whole QUIC demux -- UDP socket, receive loop, connection-ID table, an ngtcp2_cid forward declaration. That is now Http3ServerImpl in h3_server.cpp, behind the abstract Http3Server and make_http3_server() in h3_backend.hpp. What is left in Server::Impl is m_http3 plus add_session()/remove_session(), the session registry all three protocols share. Also drops the stray NGHTTP2 link from the server executable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The error_code overloads of shutdown()/set_option() now return void, so drop the std::ignore = in front of them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the hand-written "[{}] " prefix and log_prefix_ argument with the
mlog* macros in the member functions of h3_session.cpp.
The static ngtcp2/nghttp3 callbacks keep the explicit form: mlog* expands to
an unqualified logPrefix(), which has nothing to bind to there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment wording and formatting only, no behaviour change: - h3_session: half-open range as [dest, dest+destlen[ - h3_backend: reflow the Http3Server comment, "de-multiplexing" - common: section separator before the ReadSome aliases - cspell: sveccnt Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
No description provided.