Skip to main content

moq_native/
lib.rs

1//! Helper library for native MoQ applications.
2//!
3//! Establishes MoQ connections over:
4//! - WebTransport (HTTP/3)
5//! - Raw QUIC (with ALPN negotiation)
6//! - WebSocket (fallback via [web-transport-ws](https://crates.io/crates/web-transport-ws))
7//! - Plain TCP via the `tcp://` scheme (qmux, no TLS; requires `tcp` feature)
8//! - Unix domain socket via the `unix://` scheme (qmux, peer-credential aware; requires `uds` feature, unix-only)
9//! - Iroh P2P (requires `iroh` feature)
10//!
11//! See [`Client`] for connecting to relays and [`Server`] for accepting connections.
12
13#![warn(missing_docs)]
14
15pub mod accept;
16pub mod bind;
17mod client;
18mod connect;
19mod crypto;
20mod error;
21#[cfg(any(feature = "quinn", feature = "noq", feature = "quiche", feature = "tcp"))]
22pub mod failover;
23#[cfg(feature = "jemalloc")]
24pub mod jemalloc;
25mod log;
26#[cfg(feature = "noq")]
27pub mod noq;
28pub mod quic;
29#[cfg(feature = "quinn")]
30pub mod quinn;
31mod reconnect;
32mod server;
33#[cfg(feature = "tcp")]
34pub mod tcp;
35pub mod tls;
36#[cfg(all(feature = "uds", unix))]
37pub mod unix;
38mod util;
39#[cfg(feature = "watch")]
40pub mod watch;
41#[cfg(feature = "websocket")]
42pub mod websocket;
43
44// Enumerated rather than globbed, so the root surface is a deliberate list and a
45// new `pub` item in these modules doesn't silently join it.
46pub use client::{Client, ClientConfig};
47pub use connect::ConnectError;
48pub use error::{Error, Result};
49pub use log::Log;
50pub use reconnect::{Backoff, ConnectionStatsReader, Reconnect, Status};
51pub use server::{Request, Server, ServerConfig, Transport};
52
53/// Spawn the session's protocol driver on the current tokio runtime, handing back
54/// the session it drives.
55///
56/// The driver holds no session clone, so the session still closes when the caller
57/// drops their last [`moq_net::Session`] handle, which in turn lets the driver
58/// task finish.
59pub(crate) fn spawn_session((session, driver): (moq_net::Session, moq_net::Driver)) -> moq_net::Session {
60	tokio::spawn(driver);
61	session
62}
63
64// Re-export these crates.
65pub use moq_net;
66pub use rustls;
67
68/// Re-exported because [`watch::FileWatcher`] surfaces `notify::Result`/`notify::Error`
69/// in its API; a major `notify` bump is therefore a breaking change for this crate.
70#[cfg(feature = "watch")]
71pub use notify;
72
73/// Re-exported because [`tls::init_android`] takes a `jni::Env` handle; a major
74/// `jni` bump is therefore a breaking change for this crate.
75#[cfg(target_os = "android")]
76pub use jni;
77
78#[cfg(feature = "quiche")]
79pub mod quiche;
80
81#[cfg(feature = "iroh")]
82pub mod iroh;
83
84/// The QUIC backend to use for connections.
85#[derive(Clone, Debug, clap::ValueEnum, serde::Serialize, serde::Deserialize)]
86#[serde(rename_all = "lowercase")]
87#[non_exhaustive]
88pub enum QuicBackend {
89	/// [web-transport-quinn](https://crates.io/crates/web-transport-quinn)
90	#[cfg(feature = "quinn")]
91	Quinn,
92
93	/// [web-transport-quiche](https://crates.io/crates/web-transport-quiche)
94	#[cfg(feature = "quiche")]
95	Quiche,
96
97	/// [web-transport-noq](https://crates.io/crates/web-transport-noq)
98	#[cfg(feature = "noq")]
99	Noq,
100}
101
102/// Parses the same spellings the CLI and TOML accept (`quinn`, `quiche`, `noq`),
103/// case-insensitively. A backend this build was compiled without is an error, since
104/// its variant doesn't exist.
105impl std::str::FromStr for QuicBackend {
106	type Err = String;
107
108	fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
109		<Self as clap::ValueEnum>::from_str(s, true)
110	}
111}
112
113impl QuicBackend {
114	/// Every backend this build was compiled with, spelled the way [`FromStr`] accepts.
115	///
116	/// The variants are feature-gated, so this is the only honest answer to "what can I
117	/// pass here". A caller building a menu should read it rather than listing the three
118	/// names, which would offer options that cannot parse.
119	///
120	/// [`FromStr`]: std::str::FromStr
121	pub fn compiled() -> &'static [Self] {
122		&[
123			#[cfg(feature = "quinn")]
124			Self::Quinn,
125			#[cfg(feature = "quiche")]
126			Self::Quiche,
127			#[cfg(feature = "noq")]
128			Self::Noq,
129		]
130	}
131
132	/// The name [`FromStr`] accepts for this backend.
133	///
134	/// [`FromStr`]: std::str::FromStr
135	pub fn as_str(&self) -> &'static str {
136		match *self {
137			#[cfg(feature = "quinn")]
138			Self::Quinn => "quinn",
139			#[cfg(feature = "quiche")]
140			Self::Quiche => "quiche",
141			#[cfg(feature = "noq")]
142			Self::Noq => "noq",
143		}
144	}
145}
146
147/// Whether this build can capture qlog traces, which the `qlog` feature gates.
148///
149/// Setting a qlog directory without it is an error at dial time, so a caller offering
150/// the knob should check here rather than surfacing an option that cannot work.
151pub fn qlog_supported() -> bool {
152	cfg!(feature = "qlog")
153}
154
155fn default_quic_backend() -> QuicBackend {
156	#[cfg(feature = "quinn")]
157	{
158		QuicBackend::Quinn
159	}
160	#[cfg(all(feature = "noq", not(feature = "quinn")))]
161	{
162		QuicBackend::Noq
163	}
164	#[cfg(all(feature = "quiche", not(feature = "quinn"), not(feature = "noq")))]
165	{
166		QuicBackend::Quiche
167	}
168	#[cfg(all(not(feature = "quiche"), not(feature = "quinn"), not(feature = "noq")))]
169	panic!("no QUIC backend compiled; enable noq, quinn, or quiche feature");
170}
171
172#[cfg(test)]
173mod tests {
174	#[cfg(feature = "quinn")]
175	#[test]
176	fn quinn_is_the_default_backend() {
177		assert!(matches!(super::default_quic_backend(), super::QuicBackend::Quinn));
178	}
179}