From 126ee01d0df22b43d47464b49e1d3ce1596b0ebd Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 23 Jun 2026 11:40:34 -0700 Subject: [PATCH] feat(moq-native): default QUIC backend to noq instead of quinn Switch the default-compiled and default-selected QUIC backend from quinn to noq across moq-native and all binaries. quinn stays available as an opt-in `--features quinn` escape hatch. - moq-native: `default` feature -> noq; auto-detect priority now prefers noq over quinn when multiple backends are compiled. - Binaries (relay, cli, boy, bench, hls, rtc, srt) default to noq, each gaining a `noq` feature where it was missing. moq-ffi/libmoq/moq-gst inherit the new default via `default-features = true`. - Fix crypto-provider feature wiring: quinn routed its provider through `quinn?/rustls-aws-lc-rs` / `quinn?/rustls-ring`, but noq had no equivalent. web-transport-noq forced aws-lc-rs via its own defaults, so a `--features noq,ring` build silently pulled aws-lc-rs anyway. Set web-transport-noq `default-features = false` and route the provider through moq-native's aws-lc-rs/ring features, mirroring quinn. - Add a `noq=info` tracing directive alongside `quinn=info`; update docs. noq uses BBR3 congestion control by default (quinn ran default CUBIC with a TODO to validate BBR), so the effective default congestion controller changes to BBR3. Co-Authored-By: Claude Opus 4.8 --- Cargo.lock | 1 + Cargo.toml | 4 +++- doc/lib/rs/crate/moq-native.md | 2 +- doc/lib/rs/env/native.md | 2 +- rs/moq-bench/Cargo.toml | 3 ++- rs/moq-boy/Cargo.toml | 3 ++- rs/moq-cli/Cargo.toml | 3 ++- rs/moq-gst/src/lib.rs | 1 + rs/moq-hls/Cargo.toml | 3 ++- rs/moq-native/Cargo.toml | 8 +++++--- rs/moq-native/src/client.rs | 12 ++++++------ rs/moq-native/src/log.rs | 1 + rs/moq-native/src/server.rs | 12 ++++++------ rs/moq-relay/Cargo.toml | 2 +- rs/moq-rtc/Cargo.toml | 3 ++- rs/moq-rtmp/Cargo.toml | 3 ++- rs/moq-srt/Cargo.toml | 3 ++- 17 files changed, 40 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 244bc594d..015fcd77e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4794,6 +4794,7 @@ dependencies = [ "lru-slab", "rand 0.10.1", "rand_pcg", + "ring", "rustc-hash 2.1.2", "rustls", "rustls-pki-types", diff --git a/Cargo.toml b/Cargo.toml index ad0b4da94..86431a59e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,9 @@ serde = { version = "1", features = ["derive"] } tokio = "1.48" web-async = { version = "0.1.4", features = ["tracing"] } web-transport-iroh = "0.6" -web-transport-noq = "0.2.0" +# default-features off so the QUIC crypto provider is chosen by moq-native's +# aws-lc-rs / ring features (mirroring how the quinn dependency is wired). +web-transport-noq = { version = "0.2.0", default-features = false } web-transport-proto = "0.6" web-transport-quiche = "0.4" web-transport-quinn = "0.11" diff --git a/doc/lib/rs/crate/moq-native.md b/doc/lib/rs/crate/moq-native.md index 3a3dc2b77..61a1ca3dc 100644 --- a/doc/lib/rs/crate/moq-native.md +++ b/doc/lib/rs/crate/moq-native.md @@ -15,7 +15,7 @@ QUIC and WebTransport connection helpers for native Rust applications. Provides `moq-native` bridges the gap between the transport-agnostic `moq-net` crate and actual QUIC/WebTransport networking. It handles: - TLS certificate loading and configuration -- QUIC connection setup via [quinn](https://crates.io/crates/quinn) +- QUIC connection setup via a pluggable backend, defaulting to [noq](https://crates.io/crates/noq) (enable the `quinn` feature for [quinn](https://crates.io/crates/quinn) instead) - WebTransport session management - Development certificate generation for local testing diff --git a/doc/lib/rs/env/native.md b/doc/lib/rs/env/native.md index 3b43c8730..b2f9b6f78 100644 --- a/doc/lib/rs/env/native.md +++ b/doc/lib/rs/env/native.md @@ -12,7 +12,7 @@ This guide covers connecting to a relay, discovering broadcasts, subscribing to The key crates: -- [moq-native](https://crates.io/crates/moq-native) — Configures QUIC (via [quinn](https://crates.io/crates/quinn)) and TLS (via [rustls](https://crates.io/crates/rustls)) for you. +- [moq-native](https://crates.io/crates/moq-native) — Configures QUIC (via [noq](https://crates.io/crates/noq) by default, or [quinn](https://crates.io/crates/quinn) with the `quinn` feature) and TLS (via [rustls](https://crates.io/crates/rustls)) for you. - [moq-net](https://crates.io/crates/moq-net) — The core networking layer. Can be used directly with any `web_transport_trait::Session` implementation if you need full control over the QUIC endpoint. - [hang](https://crates.io/crates/hang) — Media-specific catalog and container format on top of `moq-net`. diff --git a/rs/moq-bench/Cargo.toml b/rs/moq-bench/Cargo.toml index c8968c9f3..f8fa67616 100644 --- a/rs/moq-bench/Cargo.toml +++ b/rs/moq-bench/Cargo.toml @@ -20,8 +20,9 @@ path = "src/main.rs" doc = false [features] -default = ["quinn", "websocket"] +default = ["noq", "websocket"] iroh = ["moq-native/iroh"] +noq = ["moq-native/noq"] quinn = ["moq-native/quinn"] quiche = ["moq-native/quiche"] websocket = ["moq-native/websocket"] diff --git a/rs/moq-boy/Cargo.toml b/rs/moq-boy/Cargo.toml index 8d657cb6b..5bedfc8ef 100644 --- a/rs/moq-boy/Cargo.toml +++ b/rs/moq-boy/Cargo.toml @@ -12,8 +12,9 @@ edition = "2024" rust-version.workspace = true [features] -default = ["quinn", "websocket"] +default = ["noq", "websocket"] jemalloc = ["moq-native/jemalloc"] +noq = ["moq-native/noq"] quinn = ["moq-native/quinn"] websocket = ["moq-native/websocket"] diff --git a/rs/moq-cli/Cargo.toml b/rs/moq-cli/Cargo.toml index eb1be4d48..016a80718 100644 --- a/rs/moq-cli/Cargo.toml +++ b/rs/moq-cli/Cargo.toml @@ -13,8 +13,9 @@ keywords = ["quic", "http3", "webtransport", "media", "live"] categories = ["multimedia", "network-programming", "web-programming"] [features] -default = ["iroh", "quinn", "websocket", "nvenc", "vaapi"] +default = ["iroh", "noq", "websocket", "nvenc", "vaapi"] iroh = ["moq-native/iroh"] +noq = ["moq-native/noq"] quinn = ["moq-native/quinn"] quiche = ["moq-native/quiche"] websocket = ["moq-native/websocket"] diff --git a/rs/moq-gst/src/lib.rs b/rs/moq-gst/src/lib.rs index 1b779d7ac..9846e91c0 100644 --- a/rs/moq-gst/src/lib.rs +++ b/rs/moq-gst/src/lib.rs @@ -15,6 +15,7 @@ pub fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { .from_env_lossy() // Allow overriding with RUST_LOG .add_directive("h2=warn".parse().unwrap()) .add_directive("quinn=info".parse().unwrap()) + .add_directive("noq=info".parse().unwrap()) .add_directive("tracing::span=off".parse().unwrap()) .add_directive("tracing::span::active=off".parse().unwrap()); diff --git a/rs/moq-hls/Cargo.toml b/rs/moq-hls/Cargo.toml index 9e38a5e9a..ceb464835 100644 --- a/rs/moq-hls/Cargo.toml +++ b/rs/moq-hls/Cargo.toml @@ -24,10 +24,11 @@ doc = false required-features = ["server"] [features] -default = ["server", "iroh", "quinn", "websocket"] +default = ["server", "iroh", "noq", "websocket"] # HTTP export server + the moq-hls binary. Pulls in axum / moq-native / clap. server = ["dep:axum", "dep:axum-server", "dep:clap", "dep:humantime", "dep:moq-native", "dep:rustls", "dep:sd-notify", "dep:tower-http"] iroh = ["server", "moq-native/iroh"] +noq = ["server", "moq-native/noq"] quinn = ["server", "moq-native/quinn"] quiche = ["server", "moq-native/quiche"] websocket = ["server", "moq-native/websocket"] diff --git a/rs/moq-native/Cargo.toml b/rs/moq-native/Cargo.toml index ab023487d..050025715 100644 --- a/rs/moq-native/Cargo.toml +++ b/rs/moq-native/Cargo.toml @@ -16,13 +16,15 @@ categories = ["multimedia", "network-programming", "web-programming"] doctest = false [features] -default = ["quinn", "aws-lc-rs", "websocket", "tcp", "uds"] +default = ["noq", "aws-lc-rs", "websocket", "tcp", "uds"] quinn = ["dep:quinn", "dep:web-transport-quinn", "dep:rcgen", "dep:reqwest", "dep:rustls-webpki", "watch"] noq = ["dep:web-transport-noq", "dep:rcgen", "dep:reqwest", "dep:rustls-webpki", "watch"] quiche = ["dep:web-transport-quiche", "dep:rcgen"] # Filesystem watcher for hot-reloading on-disk TLS certs/keys; the QUIC backends imply it. watch = ["dep:notify"] -aws-lc-rs = ["rustls/aws-lc-rs", "rcgen?/aws_lc_rs", "quinn?/rustls-aws-lc-rs"] +# The QUIC backends pull in their crypto provider from these features (default-features +# are off on each backend dep), so a backend without aws-lc-rs/ring has no initial cipher. +aws-lc-rs = ["rustls/aws-lc-rs", "rcgen?/aws_lc_rs", "quinn?/rustls-aws-lc-rs", "web-transport-noq?/aws-lc-rs"] iroh = ["dep:web-transport-iroh", "dep:web-transport-proto"] jemalloc = ["dep:tikv-jemallocator", "dep:tikv-jemalloc-ctl"] websocket = ["dep:qmux"] @@ -31,7 +33,7 @@ tcp = ["dep:qmux"] # Unix-domain-socket qmux transport (`unix://`), unix-only. Adds peer-credential # inspection on accept. Pulls in `tcp` (shares qmux's stream transport). uds = ["tcp", "qmux/uds"] -ring = ["rustls/ring", "rcgen?/ring", "quinn?/rustls-ring"] +ring = ["rustls/ring", "rcgen?/ring", "quinn?/rustls-ring", "web-transport-noq?/ring"] android-logcat = ["dep:tracing-android"] [dependencies] diff --git a/rs/moq-native/src/client.rs b/rs/moq-native/src/client.rs index 52d4f2e9b..913b0dff3 100644 --- a/rs/moq-native/src/client.rs +++ b/rs/moq-native/src/client.rs @@ -137,19 +137,19 @@ impl Client { pub fn new(config: ClientConfig) -> crate::Result { #[cfg(any(feature = "noq", feature = "quinn", feature = "quiche"))] let backend = config.backend.clone().unwrap_or({ - #[cfg(feature = "quinn")] + #[cfg(feature = "noq")] { - QuicBackend::Quinn + QuicBackend::Noq } - #[cfg(all(feature = "noq", not(feature = "quinn")))] + #[cfg(all(feature = "quinn", not(feature = "noq")))] { - QuicBackend::Noq + QuicBackend::Quinn } - #[cfg(all(feature = "quiche", not(feature = "quinn"), not(feature = "noq")))] + #[cfg(all(feature = "quiche", not(feature = "noq"), not(feature = "quinn")))] { QuicBackend::Quiche } - #[cfg(all(not(feature = "quiche"), not(feature = "quinn"), not(feature = "noq")))] + #[cfg(all(not(feature = "quiche"), not(feature = "noq"), not(feature = "quinn")))] panic!("no QUIC backend compiled; enable noq, quinn, or quiche feature"); }); diff --git a/rs/moq-native/src/log.rs b/rs/moq-native/src/log.rs index d0e5bfc9c..93319bcea 100644 --- a/rs/moq-native/src/log.rs +++ b/rs/moq-native/src/log.rs @@ -40,6 +40,7 @@ impl Log { .from_env_lossy() // Allow overriding with RUST_LOG .add_directive("h2=warn".parse()?) .add_directive("quinn=info".parse()?) + .add_directive("noq=info".parse()?) .add_directive("tungstenite=info".parse()?) .add_directive("rustls=info".parse()?) .add_directive("tracing::span=off".parse()?) diff --git a/rs/moq-native/src/server.rs b/rs/moq-native/src/server.rs index c75afa7d3..45ea1271b 100644 --- a/rs/moq-native/src/server.rs +++ b/rs/moq-native/src/server.rs @@ -143,19 +143,19 @@ pub struct Server { impl Server { pub fn new(config: ServerConfig) -> crate::Result { let backend = config.backend.clone().unwrap_or({ - #[cfg(feature = "quinn")] + #[cfg(feature = "noq")] { - QuicBackend::Quinn + QuicBackend::Noq } - #[cfg(all(feature = "noq", not(feature = "quinn")))] + #[cfg(all(feature = "quinn", not(feature = "noq")))] { - QuicBackend::Noq + QuicBackend::Quinn } - #[cfg(all(feature = "quiche", not(feature = "quinn"), not(feature = "noq")))] + #[cfg(all(feature = "quiche", not(feature = "noq"), not(feature = "quinn")))] { QuicBackend::Quiche } - #[cfg(all(not(feature = "quiche"), not(feature = "quinn"), not(feature = "noq")))] + #[cfg(all(not(feature = "quiche"), not(feature = "noq"), not(feature = "quinn")))] panic!("no QUIC backend compiled; enable noq, quinn, or quiche feature"); }); diff --git a/rs/moq-relay/Cargo.toml b/rs/moq-relay/Cargo.toml index f1d98e7a4..bdf14d677 100644 --- a/rs/moq-relay/Cargo.toml +++ b/rs/moq-relay/Cargo.toml @@ -21,7 +21,7 @@ path = "src/main.rs" doc = false [features] -default = ["iroh", "quinn", "websocket", "uds"] +default = ["iroh", "noq", "websocket", "uds"] iroh = ["moq-native/iroh"] jemalloc = ["moq-native/jemalloc"] noq = ["moq-native/noq"] diff --git a/rs/moq-rtc/Cargo.toml b/rs/moq-rtc/Cargo.toml index 0c9ba808c..d35fb619c 100644 --- a/rs/moq-rtc/Cargo.toml +++ b/rs/moq-rtc/Cargo.toml @@ -25,12 +25,13 @@ doc = false required-features = ["server"] [features] -default = ["server", "iroh", "quinn", "websocket"] +default = ["server", "iroh", "noq", "websocket"] # Standalone binary: TLS-terminating listener, CORS, CLI parsing, and the # moq-native relay client. Pulls in axum-server / clap / moq-native / rustls / # sd-notify / tower-http. server = ["dep:axum-server", "dep:clap", "dep:moq-native", "dep:rustls", "dep:sd-notify", "dep:tower-http"] iroh = ["server", "moq-native/iroh"] +noq = ["server", "moq-native/noq"] quinn = ["server", "moq-native/quinn"] quiche = ["server", "moq-native/quiche"] websocket = ["server", "moq-native/websocket"] diff --git a/rs/moq-rtmp/Cargo.toml b/rs/moq-rtmp/Cargo.toml index d4a136061..108f8e1b4 100644 --- a/rs/moq-rtmp/Cargo.toml +++ b/rs/moq-rtmp/Cargo.toml @@ -25,10 +25,11 @@ doc = false required-features = ["server"] [features] -default = ["server", "quinn", "websocket"] +default = ["server", "noq", "websocket"] # Relay client/server transports + the moq-rtmp binary. Pulls in moq-native / # axum / clap / rustls. server = ["dep:axum", "dep:axum-server", "dep:clap", "dep:moq-native", "dep:rustls", "dep:sd-notify", "dep:tokio-rustls", "dep:tower-http", "dep:url"] +noq = ["server", "moq-native/noq"] quinn = ["server", "moq-native/quinn"] quiche = ["server", "moq-native/quiche"] websocket = ["server", "moq-native/websocket"] diff --git a/rs/moq-srt/Cargo.toml b/rs/moq-srt/Cargo.toml index cd28364ba..4aa43c93e 100644 --- a/rs/moq-srt/Cargo.toml +++ b/rs/moq-srt/Cargo.toml @@ -25,10 +25,11 @@ doc = false required-features = ["server"] [features] -default = ["server", "quinn", "websocket"] +default = ["server", "noq", "websocket"] # Relay client/server transports + the moq-srt binary. Pulls in moq-native / # axum / clap / rustls. server = ["dep:axum", "dep:axum-server", "dep:clap", "dep:humantime", "dep:moq-native", "dep:rustls", "dep:sd-notify", "dep:tower-http", "dep:url"] +noq = ["server", "moq-native/noq"] quinn = ["server", "moq-native/quinn"] quiche = ["server", "moq-native/quiche"] websocket = ["server", "moq-native/websocket"]