Skip to main content

tokio_quiche/http3/driver/
mod.rs

1// Copyright (C) 2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27mod client;
28/// Wrapper for running HTTP/3 connections.
29pub mod connection;
30mod datagram;
31// `DriverHooks` must stay private to prevent users from creating their own
32// H3Drivers.
33mod hooks;
34mod server;
35mod streams;
36#[cfg(test)]
37pub mod test_utils;
38#[cfg(test)]
39mod tests;
40
41use std::collections::BTreeMap;
42use std::error::Error;
43use std::fmt;
44use std::marker::PhantomData;
45use std::sync::Arc;
46use std::time::Instant;
47
48use bytes::BufMut as _;
49use bytes::Bytes;
50use bytes::BytesMut;
51use datagram_socket::DgramBuffer;
52use datagram_socket::StreamClosureKind;
53use foundations::telemetry::log;
54use futures::FutureExt;
55use futures_util::stream::FuturesUnordered;
56use quiche::h3;
57use quiche::h3::WireErrorCode;
58use tokio::select;
59use tokio::sync::mpsc;
60use tokio::sync::mpsc::error::TryRecvError;
61use tokio::sync::mpsc::error::TrySendError;
62use tokio::sync::mpsc::UnboundedReceiver;
63use tokio::sync::mpsc::UnboundedSender;
64use tokio_stream::StreamExt;
65use tokio_util::sync::PollSender;
66
67use self::hooks::DriverHooks;
68use self::hooks::InboundHeaders;
69use self::streams::FlowCtx;
70use self::streams::HaveUpstreamCapacity;
71use self::streams::ReceivedDownstreamData;
72use self::streams::StreamCtx;
73use self::streams::StreamReady;
74use self::streams::WaitForDownstreamData;
75use self::streams::WaitForStream;
76use self::streams::WaitForUpstreamCapacity;
77use crate::buf_factory::BufFactory;
78use crate::http3::settings::Http3Settings;
79use crate::http3::H3AuditStats;
80use crate::metrics::Metrics;
81use crate::quic::HandshakeInfo;
82use crate::quic::QuicCommand;
83use crate::quic::QuicheConnection;
84use crate::ApplicationOverQuic;
85use crate::QuicResult;
86
87pub use self::client::ClientEventStream;
88pub use self::client::ClientH3Command;
89pub use self::client::ClientH3Controller;
90pub use self::client::ClientH3Driver;
91pub use self::client::ClientH3Event;
92pub use self::client::ClientRequestSender;
93pub use self::client::NewClientRequest;
94pub use self::server::IsInEarlyData;
95pub use self::server::RawPriorityValue;
96pub use self::server::ServerEventStream;
97pub use self::server::ServerH3Command;
98pub use self::server::ServerH3Controller;
99pub use self::server::ServerH3Driver;
100pub use self::server::ServerH3Event;
101
102// The default priority for HTTP/3 responses if the application didn't provide
103// one.
104const DEFAULT_PRIO: h3::Priority = h3::Priority::new(3, true);
105
106// For a stream use a channel with 16 entries, which works out to 16 * 64KB =
107// 1MB of max buffered data.
108#[cfg(not(any(test, debug_assertions)))]
109const STREAM_CAPACITY: usize = 16;
110#[cfg(any(test, debug_assertions))]
111const STREAM_CAPACITY: usize = 1; // Set to 1 to stress write_pending under test conditions
112
113// For *all* flows use a shared channel with 2048 entries, which works out
114// to 3MB of max buffered data at 1500 bytes per datagram.
115const FLOW_CAPACITY: usize = 2048;
116
117// Floor for the lazily-allocated body receive buffer. The buffer is sized to
118// the amount currently readable on the stream (see [`process_h3_data`]), but we
119// never allocate below this floor, for two reasons:
120//
121// - A `Limit<BytesMut>` with a zero limit reports no remaining capacity and
122//   would make `recv_body_buf` a no-op.
123// - Sizing strictly to the readable length defeats allocation amortization: a
124//   body that trickles in a few bytes at a time (e.g. one byte per read) would
125//   reallocate the buffer on every read. Allocating at least this many bytes
126//   lets a single allocation absorb many small reads (each `split()` off)
127//   before it is exhausted and reallocated.
128const MIN_BODY_RECV_BUF_SIZE: usize = 1024;
129
130/// Computes the capacity to use for the body receive buffer given the number of
131/// bytes currently readable on the stream.
132///
133/// The result is clamped to `[MIN_BODY_RECV_BUF_SIZE, MAX_BUF_SIZE]`: reads
134/// below the floor still allocate the floor (so a trickle of tiny reads reuses
135/// one allocation instead of reallocating each time), while a single
136/// (potentially adversarial) read never allocates more than `MAX_BUF_SIZE`.
137fn body_recv_buf_size(readable: usize) -> usize {
138    readable.clamp(MIN_BODY_RECV_BUF_SIZE, BufFactory::MAX_BUF_SIZE)
139}
140
141/// Used by a local task to send [`OutboundFrame`]s to a peer on the
142/// stream or flow associated with this channel.
143pub type OutboundFrameSender = PollSender<OutboundFrame>;
144
145/// Used internally to receive [`OutboundFrame`]s which should be sent to a peer
146/// on the stream or flow associated with this channel.
147type OutboundFrameStream = mpsc::Receiver<OutboundFrame>;
148
149/// Used internally to send [`InboundFrame`]s (data) from the peer to a local
150/// task on the stream or flow associated with this channel.
151type InboundFrameSender = PollSender<InboundFrame>;
152
153/// Used by a local task to receive [`InboundFrame`]s (data) on the stream or
154/// flow associated with this channel.
155pub type InboundFrameStream = mpsc::Receiver<InboundFrame>;
156
157/// The error type used internally in [H3Driver].
158///
159/// Note that [`ApplicationOverQuic`] errors are not exposed to users at this
160/// time. The type is public to document the failure modes in [H3Driver].
161#[derive(Debug, PartialEq, Eq)]
162#[non_exhaustive]
163pub enum H3ConnectionError {
164    /// The controller task was shut down and is no longer listening.
165    ControllerWentAway,
166    /// Other error at the connection, but not stream level.
167    H3(h3::Error),
168    /// Received data for a stream that was closed or never opened.
169    NonexistentStream,
170    /// The server's post-accept timeout was hit.
171    /// The timeout can be configured in [`Http3Settings`].
172    PostAcceptTimeout,
173}
174
175impl From<h3::Error> for H3ConnectionError {
176    fn from(err: h3::Error) -> Self {
177        H3ConnectionError::H3(err)
178    }
179}
180
181impl From<quiche::Error> for H3ConnectionError {
182    fn from(err: quiche::Error) -> Self {
183        H3ConnectionError::H3(h3::Error::TransportError(err))
184    }
185}
186
187impl Error for H3ConnectionError {}
188
189impl fmt::Display for H3ConnectionError {
190    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191        let s: &dyn fmt::Display = match self {
192            Self::ControllerWentAway => &"controller went away",
193            Self::H3(e) => e,
194            Self::NonexistentStream => &"nonexistent stream",
195            Self::PostAcceptTimeout => &"post accept timeout hit",
196        };
197
198        write!(f, "H3ConnectionError: {s}")
199    }
200}
201
202type H3ConnectionResult<T> = Result<T, H3ConnectionError>;
203
204/// HTTP/3 headers that were received on a stream.
205///
206/// `recv` is used to read the message body, while `send` is used to transmit
207/// data back to the peer.
208pub struct IncomingH3Headers {
209    /// Stream ID of the frame.
210    pub stream_id: u64,
211    /// The actual [`h3::Header`]s which were received.
212    pub headers: Vec<h3::Header>,
213    /// An [`OutboundFrameSender`] for streaming body data to the peer. For
214    /// [ClientH3Driver], note that the request body can also be passed a
215    /// cloned sender via [`NewClientRequest`].
216    pub send: OutboundFrameSender,
217    /// An [`InboundFrameStream`] of body data received from the peer.
218    pub recv: InboundFrameStream,
219    /// Whether there is a body associated with the incoming headers.
220    pub read_fin: bool,
221    /// Handle to the [`H3AuditStats`] for the message's stream.
222    pub h3_audit_stats: Arc<H3AuditStats>,
223}
224
225impl fmt::Debug for IncomingH3Headers {
226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227        f.debug_struct("IncomingH3Headers")
228            .field("stream_id", &self.stream_id)
229            .field("headers", &self.headers)
230            .field("read_fin", &self.read_fin)
231            .field("h3_audit_stats", &self.h3_audit_stats)
232            .finish()
233    }
234}
235
236/// [`H3Event`]s are produced by an [H3Driver] to describe HTTP/3 state updates.
237///
238/// Both [ServerH3Driver] and [ClientH3Driver] may extend this enum with
239/// endpoint-specific variants. The events must be consumed by users of the
240/// drivers, like a higher-level `Server` or `Client` controller.
241#[derive(Debug)]
242pub enum H3Event {
243    /// A SETTINGS frame was received.
244    IncomingSettings {
245        /// Raw HTTP/3 setting pairs, in the order received from the peer.
246        settings: Vec<(u64, u64)>,
247    },
248
249    /// A HEADERS frame was received on the given stream. This is either a
250    /// request or a response depending on the perspective of the [`H3Event`]
251    /// receiver.
252    IncomingHeaders(IncomingH3Headers),
253
254    /// A DATAGRAM flow was created and associated with the given `flow_id`.
255    /// This event is fired before a HEADERS event for CONNECT[-UDP] requests.
256    NewFlow {
257        /// Flow ID of the new flow.
258        flow_id: u64,
259        /// An [`OutboundFrameSender`] for transmitting datagrams to the peer.
260        send: OutboundFrameSender,
261        /// An [`InboundFrameStream`] for receiving datagrams from the peer.
262        recv: InboundFrameStream,
263    },
264    /// A RST_STREAM frame was seen on the given `stream_id`. The user of the
265    /// driver should clean up any state allocated for this stream.
266    ResetStream { stream_id: u64 },
267    /// The connection has irrecoverably errored and is shutting down.
268    ConnectionError(h3::Error),
269    /// The connection has been shutdown, optionally due to an
270    /// [`H3ConnectionError`].
271    ConnectionShutdown(Option<H3ConnectionError>),
272    /// Body data has been received over a stream.
273    BodyBytesReceived {
274        /// Stream ID of the body data.
275        stream_id: u64,
276        /// Number of bytes received.
277        num_bytes: u64,
278        /// Whether the stream is finished and won't yield any more data.
279        fin: bool,
280    },
281    /// The stream has been closed. This is used to signal stream closures that
282    /// don't result from RST_STREAM frames, unlike the
283    /// [`H3Event::ResetStream`] variant.
284    StreamClosed { stream_id: u64 },
285    /// A GOAWAY frame was received from the peer containing `id`,
286    /// as described in https://datatracker.ietf.org/doc/html/rfc9114#section-5.2.
287    GoAway { id: u64 },
288}
289
290impl H3Event {
291    /// Generates an event from an applicable [`H3ConnectionError`].
292    fn from_error(err: &H3ConnectionError) -> Option<Self> {
293        Some(match err {
294            H3ConnectionError::H3(e) => Self::ConnectionError(*e),
295            H3ConnectionError::PostAcceptTimeout => Self::ConnectionShutdown(
296                Some(H3ConnectionError::PostAcceptTimeout),
297            ),
298            _ => return None,
299        })
300    }
301}
302
303/// An [`OutboundFrame`] is a data frame that should be sent from a local task
304/// to a peer over a [`quiche::h3::Connection`].
305///
306/// This is used, for example, to send response body data to a peer, or proxied
307/// UDP datagrams.
308#[derive(Debug)]
309pub enum OutboundFrame {
310    /// Response headers to be sent to the peer, with optional priority.
311    Headers(Vec<h3::Header>, Option<quiche::h3::Priority>),
312    /// Response body/CONNECT downstream data plus FIN flag.
313    Body(Bytes, bool),
314    /// CONNECT-UDP (DATAGRAM) downstream data plus flow ID.
315    Datagram(DgramBuffer, u64),
316    /// Close the stream with a trailers, with optional priority.
317    Trailers(Vec<h3::Header>, Option<quiche::h3::Priority>),
318    /// An error encountered when serving the request. Stream should be closed.
319    PeerStreamError,
320    /// DATAGRAM flow explicitly closed.
321    FlowShutdown { flow_id: u64, stream_id: u64 },
322}
323
324/// An [`InboundFrame`] is a data frame that was received from the peer over a
325/// [`quiche::h3::Connection`]. This is used by peers to send body or datagrams
326/// to the local task.
327#[derive(Debug)]
328pub enum InboundFrame {
329    /// Request body/CONNECT upstream data plus FIN flag.
330    Body(BytesMut, bool),
331    /// CONNECT-UDP (DATAGRAM) upstream data.
332    Datagram(DgramBuffer),
333}
334
335/// A ready-made [`ApplicationOverQuic`] which can handle HTTP/3 and MASQUE.
336/// Depending on the `DriverHooks` in use, it powers either a client or a
337/// server.
338///
339/// Use the [ClientH3Driver] and [ServerH3Driver] aliases to access the
340/// respective driver types. The driver is passed into an I/O loop and
341/// communicates with the driver's user (e.g., an HTTP client or a server) via
342/// its associated [H3Controller]. The controller allows the application to both
343/// listen for [`H3Event`]s of note and send [`H3Command`]s into the I/O loop.
344pub struct H3Driver<H: DriverHooks> {
345    /// Configuration used to initialize `conn`. Created from [`Http3Settings`]
346    /// in the constructor.
347    h3_config: h3::Config,
348    /// The underlying HTTP/3 connection. Initialized in
349    /// `ApplicationOverQuic::on_conn_established`.
350    conn: Option<h3::Connection>,
351    /// State required by the client/server hooks.
352    hooks: H,
353    /// Sends [`H3Event`]s to the [H3Controller] paired with this driver.
354    h3_event_sender: mpsc::UnboundedSender<H::Event>,
355    /// Receives [`H3Command`]s from the [H3Controller] paired with this driver.
356    cmd_recv: mpsc::UnboundedReceiver<H::Command>,
357    /// A sender that feeds back into `cmd_recv`. Used by hooks that need to
358    /// re-queue commands (e.g. retrying blocked requests) without access to
359    /// the [H3Controller]'s copy of the sender.
360    cmd_sender: mpsc::UnboundedSender<H::Command>,
361
362    /// A map of stream IDs to their [StreamCtx]. This is mainly used to
363    /// retrieve the internal Tokio channels associated with the stream.
364    stream_map: BTreeMap<u64, StreamCtx>,
365    /// A map of flow IDs to their [FlowCtx]. This is mainly used to retrieve
366    /// the internal Tokio channels associated with the flow.
367    flow_map: BTreeMap<u64, FlowCtx>,
368    /// Set of [`WaitForStream`] futures. A stream is added to this set if
369    /// we need to send to it and its channel is at capacity, or if we need
370    /// data from its channel and the channel is empty.
371    waiting_streams: FuturesUnordered<WaitForStream>,
372
373    /// Receives [`OutboundFrame`]s from all datagram flows on the connection.
374    dgram_recv: OutboundFrameStream,
375    /// Keeps the datagram channel open such that datagram flows can be created.
376    dgram_send: OutboundFrameSender,
377    /// A buffer to receive H3 body data from quiche. Lazily allocated on the
378    /// first body read and released once no streams remain, so idle
379    /// connections hold no receive buffer. We `split()` off filled parts until
380    /// we need to reallocate.
381    body_recv_buf: Option<bytes::buf::Limit<BytesMut>>,
382
383    /// The maximum HTTP/3 stream ID seen on this connection.
384    max_stream_seen: u64,
385
386    /// Tracks whether we have forwarded the HTTP/3 SETTINGS frame
387    /// to the [H3Controller] once.
388    settings_received_and_forwarded: bool,
389    /// Tracks whether the H3 event receiver has been dropped.
390    /// Used to avoid busy-looping on `h3_event_sender.closed()`.
391    h3_event_receiver_dropped: bool,
392}
393
394impl<H: DriverHooks> H3Driver<H> {
395    /// Builds a new [H3Driver] and an associated [H3Controller].
396    ///
397    /// The driver should then be passed to
398    /// [`InitialQuicConnection`](crate::InitialQuicConnection)'s `start`
399    /// method.
400    pub fn new(http3_settings: Http3Settings) -> (Self, H3Controller<H>) {
401        let (dgram_send, dgram_recv) = mpsc::channel(FLOW_CAPACITY);
402        let (cmd_sender, cmd_recv) = mpsc::unbounded_channel();
403        let (h3_event_sender, h3_event_recv) = mpsc::unbounded_channel();
404
405        (
406            H3Driver {
407                h3_config: (&http3_settings).into(),
408                conn: None,
409                hooks: H::new(&http3_settings),
410                h3_event_sender,
411                cmd_recv,
412                cmd_sender: cmd_sender.clone(),
413
414                stream_map: BTreeMap::new(),
415                flow_map: BTreeMap::new(),
416
417                dgram_recv,
418                dgram_send: PollSender::new(dgram_send),
419                max_stream_seen: 0,
420                body_recv_buf: None,
421
422                waiting_streams: FuturesUnordered::new(),
423
424                settings_received_and_forwarded: false,
425                h3_event_receiver_dropped: false,
426            },
427            H3Controller {
428                cmd_sender,
429                h3_event_recv: Some(h3_event_recv),
430            },
431        )
432    }
433
434    /// Returns a sender that feeds back into this driver's own `cmd_recv`.
435    ///
436    /// Hooks that need to re-queue commands (e.g. retrying a request that
437    /// was temporarily blocked) can use this sender without needing access
438    /// to the paired [H3Controller].
439    pub(crate) fn self_cmd_sender(&self) -> &mpsc::UnboundedSender<H::Command> {
440        &self.cmd_sender
441    }
442
443    /// Retrieve the [FlowCtx] associated with the given `flow_id`. If no
444    /// context is found, a new one will be created.
445    fn get_or_insert_flow(
446        &mut self, flow_id: u64,
447    ) -> H3ConnectionResult<&mut FlowCtx> {
448        use std::collections::btree_map::Entry;
449        Ok(match self.flow_map.entry(flow_id) {
450            Entry::Vacant(e) => {
451                // This is a datagram for a new flow we haven't seen before
452                let (flow, recv) = FlowCtx::new(FLOW_CAPACITY);
453                let flow_req = H3Event::NewFlow {
454                    flow_id,
455                    recv,
456                    send: self.dgram_send.clone(),
457                };
458                self.h3_event_sender
459                    .send(flow_req.into())
460                    .map_err(|_| H3ConnectionError::ControllerWentAway)?;
461                e.insert(flow)
462            },
463            Entry::Occupied(e) => e.into_mut(),
464        })
465    }
466
467    /// Adds a [StreamCtx] to the stream map with the given `stream_id`.
468    fn insert_stream(&mut self, stream_id: u64, ctx: StreamCtx) {
469        self.stream_map.insert(stream_id, ctx);
470        self.max_stream_seen = self.max_stream_seen.max(stream_id);
471    }
472
473    /// Fetches body chunks from the [`quiche::h3::Connection`] and forwards
474    /// them to the stream's associated [`InboundFrameStream`].
475    fn process_h3_data(
476        &mut self, qconn: &mut QuicheConnection, stream_id: u64,
477    ) -> H3ConnectionResult<()> {
478        // Split self borrow between conn and stream_map
479        let conn = self.conn.as_mut().ok_or(Self::connection_not_present())?;
480        let ctx = self
481            .stream_map
482            .get_mut(&stream_id)
483            .ok_or(H3ConnectionError::NonexistentStream)?;
484
485        enum StreamStatus {
486            Done { close: bool },
487            Reset { wire_err_code: u64 },
488            Blocked,
489        }
490
491        let status = loop {
492            let Some(sender) = ctx.send.as_ref().and_then(PollSender::get_ref)
493            else {
494                // already waiting for capacity
495                break StreamStatus::Done { close: false };
496            };
497
498            let try_reserve_result = sender.try_reserve();
499            let permit = match try_reserve_result {
500                Ok(permit) => permit,
501                Err(TrySendError::Closed(())) => {
502                    // The channel has closed before we delivered a fin or reset
503                    // to the application.
504                    if !ctx.fin_or_reset_recv &&
505                        ctx.associated_dgram_flow_id.is_none()
506                    // The channel might be closed if the stream was used to
507                    // initiate a datagram exchange.
508                    // TODO: ideally, the application would still shut down the
509                    // stream properly. Once applications code
510                    // is fixed, we can remove this check.
511                    {
512                        let err = h3::WireErrorCode::RequestCancelled as u64;
513                        let _ = qconn.stream_shutdown(
514                            stream_id,
515                            quiche::Shutdown::Read,
516                            err,
517                        );
518                        drop(try_reserve_result); // needed to drop the borrow on ctx.
519                        ctx.handle_sent_stop_sending(err);
520                        // TODO: should we send an H3Event event to
521                        // h3_event_sender? We can only get here if the app
522                        // actively closed or dropped
523                        // the channel so any event we send would be more for
524                        // logging or auditing
525                    }
526                    break StreamStatus::Done {
527                        close: ctx.both_directions_done(),
528                    };
529                },
530                Err(TrySendError::Full(())) => {
531                    if ctx.fin_or_reset_recv || qconn.stream_readable(stream_id) {
532                        break StreamStatus::Blocked;
533                    }
534                    break StreamStatus::Done { close: false };
535                },
536            };
537
538            if ctx.fin_or_reset_recv {
539                // Signal end-of-body to upstream
540                permit.send(InboundFrame::Body(Default::default(), true));
541                break StreamStatus::Done {
542                    close: ctx.fin_or_reset_sent,
543                };
544            }
545
546            // Size the receive buffer to the amount of data currently readable
547            // on the stream, capped at `MAX_BUF_SIZE`, so small or idle bodies
548            // don't pay for a full 64 KiB allocation. `stream_readable_len`
549            // returns the contiguous, in-order bytes available to read now (it
550            // includes H3 framing overhead but never counts data behind a gap),
551            // so the buffer is sized to what a single drain can actually read.
552            // The floor (`MIN_BODY_RECV_BUF_SIZE`) keeps the allocation non-zero
553            // and large enough that a trickle of tiny reads reuses a single
554            // allocation instead of reallocating each time.
555            let want = body_recv_buf_size(qconn.stream_readable_len(stream_id));
556            // Lazily allocate the receive buffer on first use; idle
557            // connections never receive body bytes and never allocate it.
558            let body_recv_buf = self
559                .body_recv_buf
560                .get_or_insert_with(|| BytesMut::with_capacity(want).limit(want));
561            // NOTE: `body_recv_buf` is `Limit<BytesMut>` so `remaining_mut()`
562            // reports the space left until the *limit* is reached. (A plain
563            // `BytesMut` can reallocate and would always report space available.)
564            //
565            // Reallocate whenever the room left is smaller than what we want for
566            // this read. This covers an exhausted buffer, but also grows a
567            // buffer that was previously sized to a smaller readable length so a
568            // later, larger read is not throttled by leftover capacity. Capacity
569            // is kept equal to the limit so the `split()` invariant asserted
570            // below (spare capacity == remaining_mut) continues to hold.
571            if body_recv_buf.remaining_mut() < want {
572                *body_recv_buf = BytesMut::with_capacity(want).limit(want);
573            }
574            match conn.recv_body_buf(qconn, stream_id, &mut *body_recv_buf) {
575                Ok(n) => {
576                    ctx.audit_stats.add_downstream_bytes_recvd(n as u64);
577                    let event = H3Event::BodyBytesReceived {
578                        stream_id,
579                        num_bytes: n as u64,
580                        fin: false,
581                    };
582                    let _ = self.h3_event_sender.send(event.into());
583                    // Take the filled part, leave the remaining capacity
584                    let filled_body = body_recv_buf.get_mut().split();
585                    // Sanity check: the remaining spare capacity should equal
586                    // the limit.
587                    debug_assert_eq!(
588                        body_recv_buf.get_mut().spare_capacity_mut().len(),
589                        body_recv_buf.remaining_mut()
590                    );
591                    // A full split leaves only an empty shared handle, so let
592                    // the forwarded frame own the allocation.
593                    if !body_recv_buf.has_remaining_mut() {
594                        self.body_recv_buf = None;
595                    }
596                    permit.send(InboundFrame::Body(filled_body, false));
597                },
598                Err(h3::Error::Done) =>
599                    break StreamStatus::Done { close: false },
600                Err(h3::Error::TransportError(quiche::Error::StreamReset(
601                    code,
602                ))) => {
603                    break StreamStatus::Reset {
604                        wire_err_code: code,
605                    };
606                },
607                Err(_) => break StreamStatus::Done { close: true },
608            }
609        };
610
611        match status {
612            StreamStatus::Done { close } => {
613                if close {
614                    return self.cleanup_stream(qconn, stream_id);
615                }
616
617                // The QUIC stream is finished, manually invoke `process_h3_fin`
618                // in case `h3::poll()` is never called again.
619                //
620                // Note that this case will not conflict with StreamStatus::Done
621                // being returned due to the body channel being
622                // blocked. qconn.stream_finished() will guarantee
623                // that we've fully parsed the body as it only returns true
624                // if we've seen a Fin for the read half of the stream.
625                if !ctx.fin_or_reset_recv && qconn.stream_finished(stream_id) {
626                    return self.process_h3_fin(qconn, stream_id);
627                }
628            },
629            StreamStatus::Reset { wire_err_code } => {
630                debug_assert!(ctx.send.is_some());
631                ctx.handle_recvd_reset(wire_err_code);
632                self.h3_event_sender
633                    .send(H3Event::ResetStream { stream_id }.into())
634                    .map_err(|_| H3ConnectionError::ControllerWentAway)?;
635                if ctx.both_directions_done() {
636                    return self.cleanup_stream(qconn, stream_id);
637                }
638            },
639            StreamStatus::Blocked => {
640                self.waiting_streams.push(ctx.wait_for_send(stream_id));
641            },
642        }
643
644        Ok(())
645    }
646
647    /// Processes an end-of-stream event from the [`quiche::h3::Connection`].
648    fn process_h3_fin(
649        &mut self, qconn: &mut QuicheConnection, stream_id: u64,
650    ) -> H3ConnectionResult<()> {
651        let ctx = self
652            .stream_map
653            .get_mut(&stream_id)
654            .filter(|c| !c.fin_or_reset_recv);
655        let Some(ctx) = ctx else {
656            // Stream is already finished, nothing to do
657            return Ok(());
658        };
659
660        ctx.fin_or_reset_recv = true;
661        ctx.audit_stats
662            .set_recvd_stream_fin(StreamClosureKind::Explicit);
663
664        // It's important to send this H3Event before process_h3_data so that
665        // a server can (potentially) generate the control response before the
666        // corresponding receiver drops.
667        let event = H3Event::BodyBytesReceived {
668            stream_id,
669            num_bytes: 0,
670            fin: true,
671        };
672        let _ = self.h3_event_sender.send(event.into());
673
674        // Communicate fin to upstream. Since `ctx.fin_recv` is true now,
675        // there can't be a recursive loop.
676        self.process_h3_data(qconn, stream_id)
677    }
678
679    /// Processes a single [`quiche::h3::Event`] received from the underlying
680    /// [`quiche::h3::Connection`]. Some events are dispatched to helper
681    /// methods.
682    fn process_read_event(
683        &mut self, qconn: &mut QuicheConnection, stream_id: u64, event: h3::Event,
684    ) -> H3ConnectionResult<()> {
685        self.forward_settings()?;
686
687        match event {
688            // Requests/responses are exclusively handled by hooks.
689            h3::Event::Headers { list, more_frames } =>
690                H::headers_received(self, qconn, InboundHeaders {
691                    stream_id,
692                    headers: list,
693                    has_body: more_frames,
694                }),
695
696            h3::Event::Data => self.process_h3_data(qconn, stream_id),
697            h3::Event::Finished => self.process_h3_fin(qconn, stream_id),
698
699            h3::Event::Reset(code) => {
700                if let Some(ctx) = self.stream_map.get_mut(&stream_id) {
701                    ctx.handle_recvd_reset(code);
702                    // See if we are waiting on this stream and close the channel
703                    // if we are. If we are not waiting, `handle_recvd_reset()`
704                    // will have taken care of closing.
705                    for pending in self.waiting_streams.iter_mut() {
706                        match pending {
707                            WaitForStream::Upstream(
708                                WaitForUpstreamCapacity {
709                                    stream_id: id,
710                                    chan: Some(chan),
711                                },
712                            ) if stream_id == *id => {
713                                chan.close();
714                            },
715                            _ => {},
716                        }
717                    }
718
719                    self.h3_event_sender
720                        .send(H3Event::ResetStream { stream_id }.into())
721                        .map_err(|_| H3ConnectionError::ControllerWentAway)?;
722                    if ctx.both_directions_done() {
723                        return self.cleanup_stream(qconn, stream_id);
724                    }
725                }
726
727                // TODO: if we don't have the stream in our map: should we
728                // send the H3Event::ResetStream?
729                Ok(())
730            },
731
732            h3::Event::PriorityUpdate => Ok(()),
733            h3::Event::GoAway => {
734                self.h3_event_sender
735                    .send(H3Event::GoAway { id: stream_id }.into())
736                    .map_err(|_| H3ConnectionError::ControllerWentAway)?;
737                Ok(())
738            },
739        }
740    }
741
742    /// The SETTINGS frame can be received at any point, so we
743    /// need to check `peer_settings_raw` to decide if we've received it.
744    ///
745    /// Settings should only be sent once, so we generate a single event
746    /// when `peer_settings_raw` transitions from None to Some.
747    fn forward_settings(&mut self) -> H3ConnectionResult<()> {
748        if self.settings_received_and_forwarded {
749            return Ok(());
750        }
751
752        // capture the peer settings and forward it
753        if let Some(settings) = self.conn_mut()?.peer_settings_raw() {
754            let incoming_settings = H3Event::IncomingSettings {
755                settings: settings.to_vec(),
756            };
757
758            self.h3_event_sender
759                .send(incoming_settings.into())
760                .map_err(|_| H3ConnectionError::ControllerWentAway)?;
761
762            self.settings_received_and_forwarded = true;
763        }
764        Ok(())
765    }
766
767    /// Send an individual frame to the underlying [`quiche::h3::Connection`] to
768    /// be flushed at a later time.
769    ///
770    /// `Self::process_writes` will iterate over all writable streams and call
771    /// this method in a loop for each stream to send all writable packets.
772    fn process_write_frame(
773        conn: &mut h3::Connection, qconn: &mut QuicheConnection,
774        ctx: &mut StreamCtx,
775    ) -> h3::Result<()> {
776        let Some(frame) = &mut ctx.queued_frame else {
777            return Ok(());
778        };
779
780        let audit_stats = &ctx.audit_stats;
781        let stream_id = audit_stats.stream_id();
782
783        match frame {
784            OutboundFrame::Headers(headers, priority) => {
785                let prio = priority.as_ref().unwrap_or(&DEFAULT_PRIO);
786
787                let res = if ctx.initial_headers_sent {
788                    // Initial headers were already sent, send additional
789                    // headers now.
790                    conn.send_additional_headers_with_priority(
791                        qconn, stream_id, headers, prio, false, false,
792                    )
793                } else {
794                    // Send initial headers.
795                    conn.send_response_with_priority(
796                        qconn, stream_id, headers, prio, false,
797                    )
798                    .inspect(|_| ctx.initial_headers_sent = true)
799                };
800
801                if let Err(h3::Error::StreamBlocked) = res {
802                    ctx.first_full_headers_flush_fail_time
803                        .get_or_insert(Instant::now());
804                }
805
806                if res.is_ok() {
807                    if let Some(first) =
808                        ctx.first_full_headers_flush_fail_time.take()
809                    {
810                        ctx.audit_stats.add_header_flush_duration(
811                            Instant::now().duration_since(first),
812                        );
813                    }
814                }
815
816                res
817            },
818
819            OutboundFrame::Body(body, fin) => {
820                let len = body.len();
821                if len == 0 && !*fin {
822                    // quiche doesn't allow sending an empty body when the fin
823                    // flag is not set
824                    return Ok(());
825                }
826                if *fin {
827                    // If this is the last body frame, drop the receiver in the
828                    // stream map to signal that we shouldn't receive any more
829                    // frames. NOTE: we can't use `mpsc::Receiver::close()`
830                    // due to an inconsistency in how tokio handles reading
831                    // from a closed mpsc channel https://github.com/tokio-rs/tokio/issues/7631
832                    ctx.recv = None;
833                }
834                let n = conn.send_body_zc(qconn, stream_id, body, *fin)?;
835
836                audit_stats.add_downstream_bytes_sent(n as _);
837                if n != len {
838                    // Couldn't write the entire body, `send_body_zc` will
839                    // have trimmed `body` accordingly. The driver keeps
840                    // the remainder of the body to send in the future.
841                    debug_assert_eq!(
842                        n + body.len(),
843                        len,
844                        "send_body_zc() should have trimmed body but did not"
845                    );
846                    Err(h3::Error::StreamBlocked)
847                } else {
848                    if *fin {
849                        Self::on_fin_sent(ctx)?;
850                    }
851                    Ok(())
852                }
853            },
854
855            OutboundFrame::Trailers(headers, priority) => {
856                let prio = priority.as_ref().unwrap_or(&DEFAULT_PRIO);
857
858                // trailers always set fin=true
859                let res = conn.send_additional_headers_with_priority(
860                    qconn, stream_id, headers, prio, true, true,
861                );
862
863                if res.is_ok() {
864                    Self::on_fin_sent(ctx)?;
865                }
866                res
867            },
868
869            OutboundFrame::PeerStreamError => Err(h3::Error::MessageError),
870
871            OutboundFrame::FlowShutdown { .. } => {
872                unreachable!("Only flows send shutdowns")
873            },
874
875            OutboundFrame::Datagram(..) => {
876                unreachable!("Only flows send datagrams")
877            },
878        }
879    }
880
881    fn on_fin_sent(ctx: &mut StreamCtx) -> h3::Result<()> {
882        ctx.recv = None;
883        ctx.fin_or_reset_sent = true;
884        ctx.audit_stats
885            .set_sent_stream_fin(StreamClosureKind::Explicit);
886        if ctx.fin_or_reset_recv {
887            // Return a TransportError to trigger stream cleanup
888            // instead of h3::Error::Done
889            Err(h3::Error::TransportError(quiche::Error::Done))
890        } else {
891            Ok(())
892        }
893    }
894
895    /// Resumes reads or writes to the connection when a stream channel becomes
896    /// unblocked.
897    ///
898    /// If we were waiting for more data from a channel, we resume writing to
899    /// the connection. Otherwise, we were blocked on channel capacity and
900    /// continue reading from the connection. `Upstream` in this context is
901    /// the consumer of the stream.
902    fn upstream_ready(
903        &mut self, qconn: &mut QuicheConnection, ready: StreamReady,
904    ) -> H3ConnectionResult<()> {
905        match ready {
906            StreamReady::Downstream(r) => self.upstream_read_ready(qconn, r),
907            StreamReady::Upstream(r) => self.upstream_write_ready(qconn, r),
908        }
909    }
910
911    fn upstream_read_ready(
912        &mut self, qconn: &mut QuicheConnection,
913        read_ready: ReceivedDownstreamData,
914    ) -> H3ConnectionResult<()> {
915        let ReceivedDownstreamData {
916            stream_id,
917            chan,
918            data,
919        } = read_ready;
920
921        match self.stream_map.get_mut(&stream_id) {
922            None => Ok(()),
923            Some(stream) => {
924                stream.recv = Some(chan);
925                stream.queued_frame = data;
926                self.process_writable_stream(qconn, stream_id)
927            },
928        }
929    }
930
931    fn upstream_write_ready(
932        &mut self, qconn: &mut QuicheConnection,
933        write_ready: HaveUpstreamCapacity,
934    ) -> H3ConnectionResult<()> {
935        let HaveUpstreamCapacity {
936            stream_id,
937            mut chan,
938        } = write_ready;
939
940        match self.stream_map.get_mut(&stream_id) {
941            None => Ok(()),
942            Some(stream) => {
943                chan.abort_send(); // Have to do it to release the associated permit
944                stream.send = Some(chan);
945                self.process_h3_data(qconn, stream_id)
946            },
947        }
948    }
949
950    /// Processes all queued outbound datagrams from the `dgram_recv` channel.
951    fn dgram_ready(
952        &mut self, qconn: &mut QuicheConnection, frame: OutboundFrame,
953    ) -> H3ConnectionResult<()> {
954        let mut frame = Ok(frame);
955
956        loop {
957            match frame {
958                Ok(OutboundFrame::Datagram(dgram, flow_id)) => {
959                    // Drop datagrams if there is no capacity
960                    let _ = datagram::send_h3_dgram(qconn, flow_id, dgram);
961                },
962                Ok(OutboundFrame::FlowShutdown { flow_id, stream_id }) => {
963                    self.shutdown_stream(
964                        qconn,
965                        stream_id,
966                        StreamShutdown::Both {
967                            read_error_code: WireErrorCode::NoError as u64,
968                            write_error_code: WireErrorCode::NoError as u64,
969                        },
970                    )?;
971                    self.flow_map.remove(&flow_id);
972                    self.close_if_idle(qconn);
973                    break;
974                },
975                Ok(_) => unreachable!("Flows can't send frame of other types"),
976                Err(TryRecvError::Empty) => break,
977                Err(TryRecvError::Disconnected) =>
978                    return Err(H3ConnectionError::ControllerWentAway),
979            }
980
981            frame = self.dgram_recv.try_recv();
982        }
983
984        Ok(())
985    }
986
987    /// Return a mutable reference to the driver's HTTP/3 connection.
988    ///
989    /// If the connection doesn't exist yet, this function returns
990    /// a `Self::connection_not_present()` error.
991    fn conn_mut(&mut self) -> H3ConnectionResult<&mut h3::Connection> {
992        self.conn.as_mut().ok_or(Self::connection_not_present())
993    }
994
995    /// Alias for [`quiche::Error::TlsFail`], which is used in the case where
996    /// this driver doesn't have an established HTTP/3 connection attached
997    /// to it yet.
998    const fn connection_not_present() -> H3ConnectionError {
999        H3ConnectionError::H3(h3::Error::TransportError(quiche::Error::TlsFail))
1000    }
1001
1002    /// Cleans up internal state for the indicated HTTP/3 stream.
1003    ///
1004    /// This function removes the stream from the stream map, closes any pending
1005    /// futures, removes associated DATAGRAM flows, and sends a
1006    /// [`H3Event::StreamClosed`] event (for servers).
1007    fn cleanup_stream(
1008        &mut self, qconn: &mut QuicheConnection, stream_id: u64,
1009    ) -> H3ConnectionResult<()> {
1010        let Some(stream_ctx) = self.stream_map.remove(&stream_id) else {
1011            return Ok(());
1012        };
1013
1014        // Find if the stream also has any pending futures associated with it
1015        for pending in self.waiting_streams.iter_mut() {
1016            match pending {
1017                WaitForStream::Downstream(WaitForDownstreamData {
1018                    stream_id: id,
1019                    chan: Some(chan),
1020                }) if stream_id == *id => {
1021                    chan.close();
1022                },
1023                WaitForStream::Upstream(WaitForUpstreamCapacity {
1024                    stream_id: id,
1025                    chan: Some(chan),
1026                }) if stream_id == *id => {
1027                    chan.close();
1028                },
1029                _ => {},
1030            }
1031        }
1032
1033        // Close any DATAGRAM-proxying channels when we close the stream, if they
1034        // exist
1035        if let Some(mapped_flow_id) = stream_ctx.associated_dgram_flow_id {
1036            self.flow_map.remove(&mapped_flow_id);
1037        }
1038
1039        if qconn.is_server() {
1040            // Signal the server to remove the stream from its map
1041            let _ = self
1042                .h3_event_sender
1043                .send(H3Event::StreamClosed { stream_id }.into());
1044        }
1045
1046        self.close_if_idle(qconn);
1047
1048        Ok(())
1049    }
1050
1051    /// Handles connection cleanup once no streams or flows remain.
1052    ///
1053    /// Releases the body receive buffer (it is reallocated lazily on the next
1054    /// body read; body bytes only flow on active streams, so an empty stream
1055    /// map means it is unused) and closes the connection with `NoError` if the
1056    /// H3 event receiver has been dropped.
1057    fn close_if_idle(&mut self, qconn: &mut QuicheConnection) {
1058        if self.stream_map.is_empty() && self.flow_map.is_empty() {
1059            self.body_recv_buf = None;
1060
1061            if self.h3_event_receiver_dropped {
1062                let _ = qconn.close(
1063                    true,
1064                    quiche::h3::WireErrorCode::NoError as u64,
1065                    &[],
1066                );
1067            }
1068        }
1069    }
1070
1071    /// Shuts down the indicated HTTP/3 stream by sending frames and cleaning
1072    /// up then cleans up internal state by calling
1073    /// [`Self::cleanup_stream`].
1074    fn shutdown_stream(
1075        &mut self, qconn: &mut QuicheConnection, stream_id: u64,
1076        shutdown: StreamShutdown,
1077    ) -> H3ConnectionResult<()> {
1078        let Some(stream_ctx) = self.stream_map.get(&stream_id) else {
1079            return Ok(());
1080        };
1081
1082        let audit_stats = &stream_ctx.audit_stats;
1083
1084        match shutdown {
1085            StreamShutdown::Read { error_code } => {
1086                audit_stats.set_sent_stop_sending_error_code(error_code as _);
1087                let _ = qconn.stream_shutdown(
1088                    stream_id,
1089                    quiche::Shutdown::Read,
1090                    error_code,
1091                );
1092            },
1093            StreamShutdown::Write { error_code } => {
1094                audit_stats.set_sent_reset_stream_error_code(error_code as _);
1095                let _ = qconn.stream_shutdown(
1096                    stream_id,
1097                    quiche::Shutdown::Write,
1098                    error_code,
1099                );
1100            },
1101            StreamShutdown::Both {
1102                read_error_code,
1103                write_error_code,
1104            } => {
1105                audit_stats
1106                    .set_sent_stop_sending_error_code(read_error_code as _);
1107                let _ = qconn.stream_shutdown(
1108                    stream_id,
1109                    quiche::Shutdown::Read,
1110                    read_error_code,
1111                );
1112                audit_stats
1113                    .set_sent_reset_stream_error_code(write_error_code as _);
1114                let _ = qconn.stream_shutdown(
1115                    stream_id,
1116                    quiche::Shutdown::Write,
1117                    write_error_code,
1118                );
1119            },
1120        }
1121
1122        self.cleanup_stream(qconn, stream_id)
1123    }
1124
1125    /// Handles a regular [`H3Command`]. May be called internally by
1126    /// [DriverHooks] for non-endpoint-specific [`H3Command`]s.
1127    fn handle_core_command(
1128        &mut self, qconn: &mut QuicheConnection, cmd: H3Command,
1129    ) -> H3ConnectionResult<()> {
1130        match cmd {
1131            H3Command::QuicCmd(cmd) => cmd.execute(qconn),
1132            H3Command::GoAway => {
1133                let max_id = self.max_stream_seen;
1134                self.conn_mut()
1135                    .expect("connection should be established")
1136                    .send_goaway(qconn, max_id)?;
1137            },
1138            H3Command::ShutdownStream {
1139                stream_id,
1140                shutdown,
1141            } => {
1142                self.shutdown_stream(qconn, stream_id, shutdown)?;
1143            },
1144        }
1145        Ok(())
1146    }
1147}
1148
1149impl<H: DriverHooks> H3Driver<H> {
1150    /// Reads all buffered datagrams out of `qconn` and distributes them to
1151    /// their flow channels.
1152    fn process_available_dgrams(
1153        &mut self, qconn: &mut QuicheConnection,
1154    ) -> H3ConnectionResult<()> {
1155        loop {
1156            match datagram::receive_h3_dgram(qconn) {
1157                Ok((flow_id, dgram))
1158                    if !qconn.is_server() ||
1159                        self.hooks.extended_connect_enabled() =>
1160                {
1161                    self.get_or_insert_flow(flow_id)?.send_best_effort(dgram);
1162                },
1163                Ok(_) => {},
1164                Err(quiche::Error::Done) => return Ok(()),
1165                Err(err) => return Err(H3ConnectionError::from(err)),
1166            }
1167        }
1168    }
1169
1170    /// Flushes any queued-up frames for `stream_id` into `qconn` until either
1171    /// there is no more capacity in `qconn` or no more frames to send.
1172    fn process_writable_stream(
1173        &mut self, qconn: &mut QuicheConnection, stream_id: u64,
1174    ) -> H3ConnectionResult<()> {
1175        // Split self borrow between conn and stream_map
1176        let conn = self.conn.as_mut().ok_or(Self::connection_not_present())?;
1177        let Some(ctx) = self.stream_map.get_mut(&stream_id) else {
1178            return Ok(()); // Unknown stream_id
1179        };
1180
1181        loop {
1182            // Process each writable frame, queue the next frame for processing
1183            // and shut down any errored streams.
1184            match Self::process_write_frame(conn, qconn, ctx) {
1185                Ok(()) => ctx.queued_frame = None,
1186                Err(h3::Error::StreamBlocked | h3::Error::Done) => break,
1187                Err(h3::Error::MessageError) => {
1188                    return self.shutdown_stream(
1189                        qconn,
1190                        stream_id,
1191                        StreamShutdown::Both {
1192                            read_error_code: WireErrorCode::MessageError as u64,
1193                            write_error_code: WireErrorCode::MessageError as u64,
1194                        },
1195                    );
1196                },
1197                Err(h3::Error::TransportError(quiche::Error::StreamStopped(
1198                    e,
1199                ))) => {
1200                    ctx.handle_recvd_stop_sending(e);
1201                    if ctx.both_directions_done() {
1202                        return self.cleanup_stream(qconn, stream_id);
1203                    } else {
1204                        return Ok(());
1205                    }
1206                },
1207                Err(h3::Error::TransportError(
1208                    quiche::Error::InvalidStreamState(stream),
1209                )) => {
1210                    return self.cleanup_stream(qconn, stream);
1211                },
1212                Err(_) => {
1213                    return self.cleanup_stream(qconn, stream_id);
1214                },
1215            }
1216
1217            let Some(recv) = ctx.recv.as_mut() else {
1218                // This stream is already waiting for data or we wrote a fin and
1219                // closed the channel.
1220                debug_assert!(
1221                    ctx.queued_frame.is_none(),
1222                    "We MUST NOT have a queued frame if we are already waiting on 
1223                    more data from the channel"
1224                );
1225                return Ok(());
1226            };
1227
1228            // Attempt to queue the next frame for processing. The corresponding
1229            // sender is created at the same time as the `StreamCtx`
1230            // and ultimately ends up in an `H3Body`. The body then
1231            // determines which frames to send to the peer via
1232            // this processing loop.
1233            match recv.try_recv() {
1234                Ok(frame) => ctx.queued_frame = Some(frame),
1235                Err(TryRecvError::Disconnected) => {
1236                    if !ctx.fin_or_reset_sent &&
1237                        ctx.associated_dgram_flow_id.is_none()
1238                    // The channel might be closed if the stream was used to
1239                    // initiate a datagram exchange.
1240                    // TODO: ideally, the application would still shut down the
1241                    // stream properly. Once applications code
1242                    // is fixed, we can remove this check.
1243                    {
1244                        // The channel closed without having written a fin. Send a
1245                        // RESET_STREAM to indicate we won't be writing anything
1246                        // else
1247                        let err = h3::WireErrorCode::RequestCancelled as u64;
1248                        let _ = qconn.stream_shutdown(
1249                            stream_id,
1250                            quiche::Shutdown::Write,
1251                            err,
1252                        );
1253                        ctx.handle_sent_reset(err);
1254                        if ctx.both_directions_done() {
1255                            return self.cleanup_stream(qconn, stream_id);
1256                        }
1257                    }
1258                    break;
1259                },
1260                Err(TryRecvError::Empty) => {
1261                    self.waiting_streams.push(ctx.wait_for_recv(stream_id));
1262                    break;
1263                },
1264            }
1265        }
1266
1267        Ok(())
1268    }
1269
1270    /// Tests `qconn` for either a local or peer error and increments
1271    /// the associated HTTP/3 or QUIC error counter.
1272    fn record_quiche_error(qconn: &mut QuicheConnection, metrics: &impl Metrics) {
1273        // split metrics between local/peer and QUIC/HTTP/3 level errors
1274        if let Some(err) = qconn.local_error() {
1275            if err.is_app {
1276                metrics.local_h3_conn_close_error_count(err.error_code.into())
1277            } else {
1278                metrics.local_quic_conn_close_error_count(err.error_code.into())
1279            }
1280            .inc();
1281        } else if let Some(err) = qconn.peer_error() {
1282            if err.is_app {
1283                metrics.peer_h3_conn_close_error_count(err.error_code.into())
1284            } else {
1285                metrics.peer_quic_conn_close_error_count(err.error_code.into())
1286            }
1287            .inc();
1288        }
1289    }
1290}
1291
1292impl<H: DriverHooks> ApplicationOverQuic for H3Driver<H> {
1293    fn on_conn_established(
1294        &mut self, quiche_conn: &mut QuicheConnection,
1295        handshake_info: &HandshakeInfo,
1296    ) -> QuicResult<()> {
1297        let conn = h3::Connection::with_transport(quiche_conn, &self.h3_config)?;
1298        self.conn = Some(conn);
1299
1300        H::conn_established(self, quiche_conn, handshake_info)?;
1301        Ok(())
1302    }
1303
1304    #[inline]
1305    fn should_act(&self) -> bool {
1306        self.conn.is_some()
1307    }
1308
1309    /// Poll the underlying [`quiche::h3::Connection`] for
1310    /// [`quiche::h3::Event`]s and DATAGRAMs, delegating processing to
1311    /// `Self::process_read_event`.
1312    ///
1313    /// If a DATAGRAM is found, it is sent to the receiver on its channel.
1314    fn process_reads(&mut self, qconn: &mut QuicheConnection) -> QuicResult<()> {
1315        loop {
1316            match self.conn_mut()?.poll(qconn) {
1317                Ok((stream_id, event)) =>
1318                    self.process_read_event(qconn, stream_id, event)?,
1319                Err(h3::Error::Done) => break,
1320                Err(err) => {
1321                    // Don't bubble error up, instead keep the worker loop going
1322                    // until quiche reports the connection is
1323                    // closed.
1324                    log::debug!("connection closed due to h3 protocol error"; "error"=>?err);
1325                    return Ok(());
1326                },
1327            };
1328        }
1329
1330        self.process_available_dgrams(qconn)?;
1331        Ok(())
1332    }
1333
1334    /// Write as much data as possible into the [`quiche::h3::Connection`] from
1335    /// all sources. This will attempt to write any queued frames into their
1336    /// respective streams, if writable.
1337    fn process_writes(&mut self, qconn: &mut QuicheConnection) -> QuicResult<()> {
1338        while let Some(stream_id) = qconn.stream_writable_next() {
1339            self.process_writable_stream(qconn, stream_id)?;
1340        }
1341
1342        // Also optimistically check for any ready streams
1343        while let Some(Some(ready)) = self.waiting_streams.next().now_or_never() {
1344            self.upstream_ready(qconn, ready)?;
1345        }
1346
1347        Ok(())
1348    }
1349
1350    /// Reports connection-level error metrics and forwards
1351    /// IOWorker errors to the associated [H3Controller].
1352    fn on_conn_close<M: Metrics>(
1353        &mut self, quiche_conn: &mut QuicheConnection, metrics: &M,
1354        work_loop_result: &QuicResult<()>,
1355    ) {
1356        let max_stream_seen = self.max_stream_seen;
1357        metrics
1358            .maximum_writable_streams()
1359            .observe(max_stream_seen as f64);
1360
1361        Self::record_quiche_error(quiche_conn, metrics);
1362
1363        let Err(work_loop_error) = work_loop_result else {
1364            return;
1365        };
1366
1367        let Some(h3_err) = work_loop_error.downcast_ref::<H3ConnectionError>()
1368        else {
1369            log::error!("Found non-H3ConnectionError"; "error" => %work_loop_error);
1370            return;
1371        };
1372
1373        if matches!(h3_err, H3ConnectionError::ControllerWentAway) {
1374            // Inform client that we won't (can't) respond anymore
1375            let _ = quiche_conn.close(true, WireErrorCode::NoError as u64, &[]);
1376            return;
1377        }
1378
1379        if let Some(ev) = H3Event::from_error(h3_err) {
1380            let _ = self.h3_event_sender.send(ev.into());
1381            #[expect(clippy::needless_return)]
1382            return; // avoid accidental fallthrough in the future
1383        }
1384    }
1385
1386    /// Wait for incoming data from the [H3Controller]. The next iteration of
1387    /// the I/O loop commences when one of the `select!`ed futures triggers.
1388    #[inline]
1389    async fn wait_for_data(
1390        &mut self, qconn: &mut QuicheConnection,
1391    ) -> QuicResult<()> {
1392        select! {
1393            biased;
1394            Some(ready) = self.waiting_streams.next() => self.upstream_ready(qconn, ready),
1395            Some(dgram) = self.dgram_recv.recv() => self.dgram_ready(qconn, dgram),
1396            Some(cmd) = self.cmd_recv.recv() => H::conn_command(self, qconn, cmd),
1397            r = self.hooks.wait_for_action(qconn), if H::has_wait_action(self) => r,
1398            _ = self.h3_event_sender.closed(), if !self.h3_event_receiver_dropped => {
1399                self.h3_event_receiver_dropped = true;
1400                self.close_if_idle(qconn);
1401                Ok(())
1402            }
1403        }?;
1404
1405        // Make sure controller is not starved, but also not prioritized in the
1406        // biased select. So poll it last, however also perform a try_recv
1407        // each iteration.
1408        if let Ok(cmd) = self.cmd_recv.try_recv() {
1409            H::conn_command(self, qconn, cmd)?;
1410        }
1411
1412        Ok(())
1413    }
1414}
1415
1416impl<H: DriverHooks> Drop for H3Driver<H> {
1417    fn drop(&mut self) {
1418        for stream in self.stream_map.values() {
1419            stream
1420                .audit_stats
1421                .set_recvd_stream_fin(StreamClosureKind::Implicit);
1422        }
1423    }
1424}
1425
1426/// [`H3Command`]s are sent by the [H3Controller] to alter the [H3Driver]'s
1427/// state.
1428///
1429/// Both [ServerH3Driver] and [ClientH3Driver] may extend this enum with
1430/// endpoint-specific variants.
1431#[derive(Debug)]
1432pub enum H3Command {
1433    /// A connection-level command that executes directly on the
1434    /// [`quiche::Connection`].
1435    QuicCmd(QuicCommand),
1436    /// Send a GOAWAY frame to the peer to initiate a graceful connection
1437    /// shutdown.
1438    GoAway,
1439    /// Shuts down a stream in the specified direction(s) and removes it from
1440    /// local state.
1441    ///
1442    /// This removes the stream from local state and sends a `RESET_STREAM`
1443    /// frame (for write direction) and/or a `STOP_SENDING` frame (for read
1444    /// direction) to the peer. See [`quiche::Connection::stream_shutdown`]
1445    /// for details.
1446    ShutdownStream {
1447        stream_id: u64,
1448        shutdown: StreamShutdown,
1449    },
1450}
1451
1452/// Specifies which direction(s) of a stream to shut down.
1453///
1454/// Used with [`H3Controller::shutdown_stream`] and the internal
1455/// `shutdown_stream` function to control whether to send a `STOP_SENDING` frame
1456/// (read direction), and/or a `RESET_STREAM` frame (write direction)
1457///
1458/// Note: Despite its name, "shutdown" here refers to signaling the peer about
1459/// stream termination, not sending a FIN flag. `STOP_SENDING` asks the peer to
1460/// stop sending data, while `RESET_STREAM` abruptly terminates the write side.
1461#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1462pub enum StreamShutdown {
1463    /// Shut down only the read direction (sends `STOP_SENDING` frame with the
1464    /// given error code).
1465    Read { error_code: u64 },
1466    /// Shut down only the write direction (sends `RESET_STREAM` frame with the
1467    /// given error code).
1468    Write { error_code: u64 },
1469    /// Shut down both directions (sends both `STOP_SENDING` and `RESET_STREAM`
1470    /// frames).
1471    Both {
1472        read_error_code: u64,
1473        write_error_code: u64,
1474    },
1475}
1476
1477/// Sends [`H3Command`]s to an [H3Driver]. The sender is typed and internally
1478/// wraps instances of `T` in the appropriate `H3Command` variant.
1479pub struct RequestSender<C, T> {
1480    sender: UnboundedSender<C>,
1481    // Required to work around dangling type parameter
1482    _r: PhantomData<fn() -> T>,
1483}
1484
1485impl<C, T: Into<C>> RequestSender<C, T> {
1486    /// Send a request to the [H3Driver]. This can only fail if the driver is
1487    /// gone.
1488    #[inline(always)]
1489    pub fn send(&self, v: T) -> Result<(), mpsc::error::SendError<C>> {
1490        self.sender.send(v.into())
1491    }
1492}
1493
1494impl<C, T> Clone for RequestSender<C, T> {
1495    fn clone(&self) -> Self {
1496        Self {
1497            sender: self.sender.clone(),
1498            _r: Default::default(),
1499        }
1500    }
1501}
1502
1503/// Interface to communicate with a paired [H3Driver].
1504///
1505/// An [H3Controller] receives [`H3Event`]s from its driver, which must be
1506/// consumed by the application built on top of the driver to react to incoming
1507/// events. The controller also allows the application to send ad-hoc
1508/// [`H3Command`]s to the driver, which will be processed when the driver waits
1509/// for incoming data.
1510pub struct H3Controller<H: DriverHooks> {
1511    /// Sends [`H3Command`]s to the [H3Driver], like [`QuicCommand`]s or
1512    /// outbound HTTP requests.
1513    cmd_sender: UnboundedSender<H::Command>,
1514    /// Receives [`H3Event`]s from the [H3Driver]. Can be extracted and
1515    /// used independently of the [H3Controller].
1516    h3_event_recv: Option<UnboundedReceiver<H::Event>>,
1517}
1518
1519impl<H: DriverHooks> H3Controller<H> {
1520    /// Gets a mut reference to the [`H3Event`] receiver for the paired
1521    /// [H3Driver].
1522    pub fn event_receiver_mut(&mut self) -> &mut UnboundedReceiver<H::Event> {
1523        self.h3_event_recv
1524            .as_mut()
1525            .expect("No event receiver on H3Controller")
1526    }
1527
1528    /// Takes the [`H3Event`] receiver for the paired [H3Driver].
1529    pub fn take_event_receiver(&mut self) -> UnboundedReceiver<H::Event> {
1530        self.h3_event_recv
1531            .take()
1532            .expect("No event receiver on H3Controller")
1533    }
1534
1535    /// Creates a [`QuicCommand`] sender for the paired [H3Driver].
1536    pub fn cmd_sender(&self) -> RequestSender<H::Command, QuicCommand> {
1537        RequestSender {
1538            sender: self.cmd_sender.clone(),
1539            _r: Default::default(),
1540        }
1541    }
1542
1543    /// Sends a GOAWAY frame to initiate a graceful connection shutdown.
1544    pub fn send_goaway(&self) {
1545        let _ = self.cmd_sender.send(H3Command::GoAway.into());
1546    }
1547
1548    /// Creates an [`H3Command`] sender for the paired [H3Driver].
1549    pub fn h3_cmd_sender(&self) -> RequestSender<H::Command, H3Command> {
1550        RequestSender {
1551            sender: self.cmd_sender.clone(),
1552            _r: Default::default(),
1553        }
1554    }
1555
1556    /// Shuts down a stream in the specified direction(s) and removes it from
1557    /// local state.
1558    ///
1559    /// This removes the stream from local state and sends a `RESET_STREAM`
1560    /// frame (for write direction) and/or a `STOP_SENDING` frame (for read
1561    /// direction) to the peer, depending on the [`StreamShutdown`] variant.
1562    pub fn shutdown_stream(&self, stream_id: u64, shutdown: StreamShutdown) {
1563        let _ = self.cmd_sender.send(
1564            H3Command::ShutdownStream {
1565                stream_id,
1566                shutdown,
1567            }
1568            .into(),
1569        );
1570    }
1571}