quiche_h3/lib.rs
1//! `quiche-h3` — an [`h3::quic`] bridge that runs hyperium [`h3`] over
2//! Cloudflare [`quiche`], driven asynchronously by [`tokio_quiche`].
3//!
4//! See `docs/design/quiche-h3-bridge.md` for the full design.
5//!
6//! The public surface exposes a standalone [`H3QuicheAcceptor`] /
7//! [`H3QuicheConnector`] whose `accept()` / `connect()` yield the crate's
8//! front-end [`Connection<Bytes>`], which implements
9//! [`h3::quic::Connection<Bytes>`]. The `h3_util::H3Acceptor` /
10//! `h3_util::H3Connector` trait conformance lives in h3-util's `quiche_h3`
11//! wrapper (Phase 9): because `h3-util` depends on `quiche-h3`, this crate must
12//! **not** depend on `h3-util` (that would be circular, design §10). The
13//! strongest checks available here are the compile-time assertions below that
14//! [`Connection<Bytes>`] implements [`h3::quic::Connection<Bytes>`] and that
15//! `accept()` / `connect()` return that type.
16//!
17//! [`h3::quic`]: h3::quic
18//! [`quiche`]: tokio_quiche::quiche
19
20// Re-export the transport crates (design §10) so downstreams can build configs
21// and credentials without a separate dependency.
22pub use tokio_quiche;
23pub use tokio_quiche::quiche;
24
25mod buffer;
26mod conn;
27mod connector;
28mod driver;
29mod endpoint;
30mod error;
31mod listener;
32mod stream;
33
34use bytes::Bytes;
35
36/// The crate error type: a boxed, thread-safe error matching h3-util's boxed
37/// `Error` shape (design §8.4). Defined here rather than imported so this crate
38/// carries no `h3-util` dependency (design §10).
39pub type Error = Box<dyn std::error::Error + Send + Sync>;
40
41/// Validate that `path` names an existing, readable **file** (not a directory)
42/// by opening it (design §7 validation, S1). `std::fs::metadata` is
43/// insufficient: it succeeds for directories and does not prove the contents
44/// are readable, deferring the failure to the asynchronous per-connection path.
45pub(crate) fn ensure_readable_file(path: &str, label: &str) -> Result<(), Error> {
46 let meta = std::fs::metadata(path).map_err(|e| -> Error {
47 format!("quiche-h3: {label} {path:?} is not accessible: {e}").into()
48 })?;
49 if !meta.is_file() {
50 return Err(format!("quiche-h3: {label} {path:?} is not a regular file").into());
51 }
52 // Opening proves the contents are actually readable.
53 std::fs::File::open(path).map_err(|e| -> Error {
54 format!("quiche-h3: {label} {path:?} is not readable: {e}").into()
55 })?;
56 Ok(())
57}
58
59/// Reject an empty ALPN list (design §7 validation): an HTTP/3 endpoint with no
60/// application protocol cannot negotiate and must not be exposed as HTTP/3.
61pub(crate) fn ensure_nonempty_alpn(
62 settings: &tokio_quiche::settings::QuicSettings,
63 label: &str,
64) -> Result<(), Error> {
65 if settings.alpn.is_empty() {
66 return Err(format!("quiche-h3: {label} has an empty ALPN list").into());
67 }
68 Ok(())
69}
70
71pub use connector::{H3QuicheClientConfig, H3QuicheConnector};
72pub use endpoint::H3QuicheEndpoint;
73pub use listener::{H3QuicheAcceptor, H3QuicheServerConfig, DEFAULT_MAX_IN_FLIGHT_HANDSHAKES};
74
75// Front-end `h3::quic` surface. These name the connection/stream types produced
76// by `accept()` / `connect()`; the h3-util wrapper (Phase 9) drives `h3` over
77// them.
78pub use stream::{Connection, H3RecvStream, H3SendStream, H3Stream, StreamOpener};
79
80// Compile-time conformance. Without an `h3-util` dependency (which would be
81// circular, design §10) the strongest checks available are that the front-end
82// `Connection<Bytes>` implements `h3::quic::Connection<Bytes>`, that the
83// connector is `Clone + Send + 'static`, and that `accept()` / `connect()`
84// return exactly `Connection<Bytes>`. The h3-util `H3Acceptor`/`H3Connector`
85// conformance is verified in the h3-util `quiche_h3` wrapper (Phase 9).
86const _: fn() = || {
87 fn assert_h3_conn<C: h3::quic::Connection<Bytes>>() {}
88 assert_h3_conn::<Connection<Bytes>>();
89
90 fn assert_clone_send_static<T: Clone + Send + 'static>() {}
91 assert_clone_send_static::<H3QuicheConnector>();
92
93 // The endpoint control surface must be cheaply cloneable and shareable
94 // across tasks (design §5.2).
95 fn assert_clone_send_sync<T: Clone + Send + Sync + 'static>() {}
96 assert_clone_send_sync::<H3QuicheEndpoint>();
97
98 // Pin the accept/connect return types to `Connection<Bytes>` (behind the
99 // outer `Result`/`Option`). Never called; the coercion is the assertion.
100 fn accept_yields_connection(a: &mut H3QuicheAcceptor) {
101 fn is_accept_result(
102 _: impl std::future::Future<Output = Result<Option<Connection<Bytes>>, Error>>,
103 ) {
104 }
105 is_accept_result(a.accept());
106 }
107 fn connect_yields_connection(c: &H3QuicheConnector) {
108 fn is_connect_result(
109 _: impl std::future::Future<Output = Result<Connection<Bytes>, Error>>,
110 ) {
111 }
112 is_connect_result(c.connect());
113 }
114 let _ = accept_yields_connection;
115 let _ = connect_yields_connection;
116};