HTTP/3
Serve HTTP/3 over QUIC with quinn — tokio-only, TLS mandatory, configurable congestion control and stream caps.
HTTP/3
HTTP/3 runs over QUIC (UDP) using quinn for transport and the h3 crate for
the protocol. It is gated behind the http3 feature. The same Router you use
for HTTP/1.1 and HTTP/2 drives HTTP/3 unchanged.
HTTP/3 is tokio-only — it is not available on the Compio runtime. TLS is
mandatory for QUIC, so a TlsCert is always required. Enabling http3 also
enables the webtransport feature (see WebTransport).
Quickstart
QUIC binds a UDP socket, not a TcpListener, so the address is passed at spawn
time. tako::serve_h3 boots a single HTTP/3 server until the process exits.
use anyhow::Result;
use tako::Method;
use tako::responder::Responder;
use tako::router::Router;
async fn hello() -> impl Responder {
"Hello, HTTP/3 World!".into_response()
}
#[tokio::main]
async fn main() -> Result<()> {
let mut router = Router::new();
router.route(Method::GET, "/", hello);
tako::serve_h3(router, "[::]:4433", Some("cert.pem"), Some("key.pem")).await;
Ok(())
}The certs and key arguments are Option<&str> paths to PEM files; they
default to cert.pem / key.pem when None. Test with a recent curl:
curl --http3 -k https://localhost:4433/use anyhow::Result;
use tako::Method;
use tako::responder::Responder;
use tako::router::Router;
async fn hello_world() -> impl Responder {
"Hello, HTTP/3 World!".into_response()
}
async fn json_example() -> impl Responder {
r#"{"message": "Hello from HTTP/3!", "protocol": "h3"}"#.into_response()
}
#[tokio::main]
async fn main() -> Result<()> {
// HTTP/3 requires TLS certificates
// You can generate self-signed certificates for testing with:
// openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
let mut router = Router::new();
router.route(Method::GET, "/", hello_world);
router.route(Method::GET, "/json", json_example);
println!("Starting HTTP/3 server on [::]:4433");
println!("Test with: curl --http3 -k https://localhost:4433/");
tako::serve_h3(router, "[::]:4433", Some("cert.pem"), Some("key.pem")).await;
Ok(())
}
The Server builder
For graceful drain and an owned shutdown handle, build a Server, attach a
TlsCert, and call spawn_h3 with the bind address. It returns the same
ServerHandle as every other transport.
use tako::{Server, TlsCert};
use tako::router::Router;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut router = Router::new();
router.route(tako::Method::GET, "/", || async { "Hello over HTTP/3" });
let server = Server::builder()
.tls(TlsCert::pem_paths("cert.pem", "key.pem"))
.build();
let handle = server.spawn_h3("[::]:4433", router);
handle.join().await;
Ok(())
}spawn_h3 panics if the builder was not given a TlsCert — QUIC cannot run
without one. The PEM-paths fast path is used directly; Der, Resolver, and
mTLS variants flow through the shared rustls-config helper.
Tuning QUIC
HTTP/3 knobs live on ServerConfig alongside the HTTP/1 and HTTP/2 settings:
| Field | Purpose |
|---|---|
h3_max_concurrent_bidi_streams | Cap on client-initiated bidirectional streams (default 100). |
h3_max_concurrent_uni_streams | Cap on client-initiated unidirectional streams (default 8). |
h3_max_idle_timeout | Idle timeout with no QUIC packets either direction (default 30 s). |
h3_congestion | Congestion controller: Cubic (default), NewReno, or Bbr. |
h3_enable_datagrams | Enable QUIC datagrams (RFC 9221) on the connection. |
h3_use_retry | Issue a QUIC Retry packet to validate source addresses (anti-amplification). |
h3_goaway_grace | Per-connection grace for in-flight streams after GOAWAY. |
use tako::{Server, ServerConfig, TlsCert};
use tako::router::Router;
use tako_rs_server::H3Congestion;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut router = Router::new();
router.route(tako::Method::GET, "/", || async { "h3 tuned" });
let server = Server::builder()
.tls(TlsCert::pem_paths("cert.pem", "key.pem"))
.config(ServerConfig {
h3_congestion: H3Congestion::Bbr,
h3_use_retry: true,
h3_enable_datagrams: true,
..ServerConfig::default()
})
.build();
let handle = server.spawn_h3("[::]:4433", router);
handle.join().await;
Ok(())
}ServerConfig is re-exported as tako::ServerConfig; the H3Congestion enum
lives in tako-rs-server, so import it from there.
The effective per-connection GOAWAY grace at runtime is
min(h3_goaway_grace, drain_timeout) — a long per-connection grace cannot push
the total shutdown past the global drain budget.
h3_goaway_grace larger than drain_timeout is a no-op beyond the global
ceiling. Raise drain_timeout too if you need a longer overall drain window.
Certificate provider note
HTTP/3 installs the ring rustls CryptoProvider during its QUIC bootstrap. If
your process also serves plain TLS and you want a specific provider, pin it at
startup with rustls::crypto::aws_lc_rs::default_provider().install_default()
before constructing any server — once a provider is installed, Tako uses it
as-is for key loading.
Related
- HTTP — HTTP/1.1 and HTTP/2.
- WebTransport — raw QUIC sessions, enabled
alongside
http3. - Feature flags — the
http3feature and what it pulls in.