tokio_quiche/
lib.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
27//! Bridging the gap between [quiche] and [tokio].
28//!
29//! tokio-quiche connects [quiche::Connection]s and [quiche::h3::Connection]s to
30//! tokio's event loop. Users have the choice between implementing their own,
31//! custom [`ApplicationOverQuic`] or using the ready-made
32//! [H3Driver](crate::http3::driver::H3Driver) for HTTP/3 clients and servers.
33//!
34//! # Starting an HTTP/3 Server
35//!
36//! A server [`listen`]s on a UDP socket for QUIC connections and spawns a new
37//! tokio task to handle each individual connection.
38//!
39//! ```
40//! use futures::stream::StreamExt;
41//! use tokio_quiche::http3::settings::Http3Settings;
42//! use tokio_quiche::listen;
43//! use tokio_quiche::metrics::DefaultMetrics;
44//! use tokio_quiche::quic::SimpleConnectionIdGenerator;
45//! use tokio_quiche::ConnectionParams;
46//! use tokio_quiche::ServerH3Driver;
47//!
48//! # async fn example() -> tokio_quiche::QuicResult<()> {
49//! let socket = tokio::net::UdpSocket::bind("0.0.0.0:443").await?;
50//! let mut listeners = listen(
51//!     [socket],
52//!     ConnectionParams::default(),
53//!     SimpleConnectionIdGenerator,
54//!     DefaultMetrics,
55//! )?;
56//! let mut accept_stream = &mut listeners[0];
57//!
58//! while let Some(conn) = accept_stream.next().await {
59//!     let (driver, mut controller) =
60//!         ServerH3Driver::new(Http3Settings::default());
61//!     conn?.start(driver);
62//!
63//!     tokio::spawn(async move {
64//!         // `controller` is the handle to our established HTTP/3 connection.
65//!         // For example, inbound requests are available as H3Events via:
66//!         let event = controller.event_receiver_mut().recv().await;
67//!     });
68//! }
69//! # Ok(())
70//! # }
71//! ```
72//!
73//! For client-side use cases, check out our [`connect`](crate::quic::connect)
74//! API.
75//!
76//! # Feature Flags
77//!
78//! tokio-quiche supports a number of feature flags to enable experimental
79//! features, performance enhancements, and additional telemetry. By default, no
80//! feature flags are enabled.
81//!
82//! - `rpk`: Support for raw public keys (RPK) in QUIC handshakes (via
83//!   [boring]).
84//! - `gcongestion`: Replace quiche's original congestion control implementation
85//!   with one adapted from google/quiche.
86//! - `zero-copy`: Use zero-copy sends with quiche (implies `gcongestion`).
87//! - `perf-quic-listener-metrics`: Extra telemetry for QUIC handshake
88//!   durations, including protocol overhead and network delays.
89//! - `tokio-task-metrics`: Scheduling & poll duration histograms for tokio
90//!   tasks.
91//!
92//! Other parts of the crate are enabled by separate build flags instead, to be
93//! controlled by the final binary:
94//!
95//! - `--cfg capture_keylogs`: Optional `SSLKEYLOGFILE` capturing for QUIC
96//!   connections.
97
98pub extern crate quiche;
99
100pub mod buf_factory;
101pub mod http3;
102pub mod metrics;
103pub mod quic;
104mod result;
105pub mod settings;
106pub mod socket;
107
108pub use buffer_pool;
109pub use datagram_socket;
110
111use foundations::telemetry::settings::LogVerbosity;
112use std::io;
113use std::sync::Arc;
114use std::sync::Once;
115use tokio::net::UdpSocket;
116use tokio_stream::wrappers::ReceiverStream;
117
118use crate::metrics::Metrics;
119use crate::socket::QuicListener;
120
121pub use crate::http3::driver::ClientH3Controller;
122pub use crate::http3::driver::ClientH3Driver;
123pub use crate::http3::driver::ServerH3Controller;
124pub use crate::http3::driver::ServerH3Driver;
125pub use crate::http3::ClientH3Connection;
126pub use crate::http3::ServerH3Connection;
127pub use crate::quic::connection::ApplicationOverQuic;
128pub use crate::quic::connection::ConnectionIdGenerator;
129pub use crate::quic::connection::InitialQuicConnection;
130pub use crate::quic::connection::QuicConnection;
131pub use crate::result::BoxError;
132pub use crate::result::QuicResult;
133pub use crate::settings::ConnectionParams;
134
135#[doc(hidden)]
136pub use crate::result::QuicResultExt;
137
138/// A stream of accepted [`InitialQuicConnection`]s from a [`listen`] call.
139///
140/// Errors from processing the client's QUIC initials can also be emitted on
141/// this stream. These do not indicate that the listener itself has failed.
142pub type QuicConnectionStream<M> =
143    ReceiverStream<io::Result<InitialQuicConnection<UdpSocket, M>>>;
144
145/// Starts listening for inbound QUIC connections on the given
146/// [`QuicListener`]s.
147///
148/// Each socket starts a separate tokio task to process and route inbound
149/// packets. This task emits connections on the respective
150/// [`QuicConnectionStream`] after receiving the client's QUIC initial and
151/// (optionally) validating its IP address.
152///
153/// The task shuts down when the returned stream is closed (or dropped) and all
154/// previously-yielded connections are closed.
155pub fn listen_with_capabilities<M>(
156    sockets: impl IntoIterator<Item = QuicListener>, params: ConnectionParams,
157    cid_generator: impl ConnectionIdGenerator<'static> + Clone, metrics: M,
158) -> io::Result<Vec<QuicConnectionStream<M>>>
159where
160    M: Metrics,
161{
162    if params.settings.capture_quiche_logs {
163        capture_quiche_logs();
164    }
165
166    sockets
167        .into_iter()
168        .map(|s| {
169            crate::quic::start_listener(
170                s,
171                &params,
172                cid_generator.clone(),
173                metrics.clone(),
174            )
175        })
176        .collect()
177}
178
179/// Starts listening for inbound QUIC connections on the given `sockets`.
180///
181/// Each socket is converted into a [`QuicListener`] with defaulted socket
182/// parameters. The listeners are then passed to [`listen_with_capabilities`].
183pub fn listen<S, M>(
184    sockets: impl IntoIterator<Item = S>, params: ConnectionParams,
185    cid_generator: impl ConnectionIdGenerator<'static> + Clone, metrics: M,
186) -> io::Result<Vec<QuicConnectionStream<M>>>
187where
188    S: TryInto<QuicListener, Error = io::Error>,
189    M: Metrics,
190{
191    let quic_sockets: Vec<QuicListener> = sockets
192        .into_iter()
193        .map(|s| {
194            #[cfg_attr(not(target_os = "linux"), expect(unused_mut))]
195            let mut socket = s.try_into()?;
196            #[cfg(target_os = "linux")]
197            socket.apply_max_capabilities();
198            Ok(socket)
199        })
200        .collect::<io::Result<_>>()?;
201
202    listen_with_capabilities(quic_sockets, params, cid_generator, metrics)
203}
204
205static GLOBAL_LOGGER_ONCE: Once = Once::new();
206
207/// Forward Quiche logs into the slog::Drain currently used by Foundations
208///
209/// # Warning
210///
211/// This should **only be used for local debugging**. Quiche can potentially
212/// emit lots (and lots, and lots) of logs (the TRACE level emits a log record
213/// on every packet and frame) and you can very easily overwhelm your logging
214/// pipeline.
215///
216/// # Note
217///
218/// Quiche uses the `env_logger` crate, which uses `log` under the hood. `log`
219/// requires that you only set the global logger once. That means that we have
220/// to register the logger at `listen()` time for servers - for clients, we
221/// should register loggers when the `quiche::Connection` is established.
222pub(crate) fn capture_quiche_logs() {
223    GLOBAL_LOGGER_ONCE.call_once(|| {
224        use foundations::telemetry::log as foundations_log;
225        use log::Level as std_level;
226
227        let curr_logger =
228            Arc::clone(&foundations_log::slog_logger()).read().clone();
229        let scope_guard = slog_scope::set_global_logger(curr_logger);
230
231        // Convert slog::Level from Foundations settings to log::Level
232        let normalized_level = match foundations_log::verbosity() {
233            LogVerbosity::Critical | LogVerbosity::Error => std_level::Error,
234            LogVerbosity::Warning => std_level::Warn,
235            LogVerbosity::Info => std_level::Info,
236            LogVerbosity::Debug => std_level::Debug,
237            LogVerbosity::Trace => std_level::Trace,
238        };
239
240        slog_stdlog::init_with_level(normalized_level).unwrap();
241
242        // The slog Drain becomes `slog::Discard` when the scope_guard is dropped,
243        // and you can't set the global logger again because of a mandate
244        // in the `log` crate. We have to manually `forget` the scope
245        // guard so that the logger remains registered for the duration of the
246        // process.
247        std::mem::forget(scope_guard)
248    });
249}