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