Skip to main content
Version: 1.0

Runtime Adapters, TLS, HTTP/2, and HTTP/3

Runtime adapters

Trillium is built on futures-lite and is async-runtime-agnostic. To run a server, pick one adapter crate:

  • trillium_smol — built on smol. Lightweight and fast. A good default if you don't have a runtime preference.
  • trillium_tokio — built on tokio. Use this if your application already depends on tokio.
  • trillium_async_std — built on async-std.
  • trillium_aws_lambda — runs on AWS Lambda. TLS and H3 are handled by the load balancer; no TLS configuration is needed.

All adapters expose the same config() builder and run() function, so switching runtimes is a one-line change.

Server configuration

By default, Trillium reads HOST and PORT from the environment. This follows the 12-factor app convention.

On Unix systems:

  • If HOST begins with ., /, or ~, it's treated as a filesystem path and bound as a Unix domain socket.
  • A LISTEN_FD environment variable enables socket activation via catflap or systemfd.

To compile specific host/port values in:

pub fn main() {
trillium_smol::config()
.with_port(1337)
.with_host("127.0.0.1")
.run(|conn: trillium::Conn| async move { conn.ok("hello world") })
}

See trillium_server_common::Config for the full list of configuration options.

TLS

All adapters (except aws_lambda) can be combined with a TLS acceptor.

Rustls

rustdocs

use trillium::Conn;
use trillium_rustls::RustlsAcceptor;


pub fn main() {
env_logger::init();
trillium_smol::config()
.with_acceptor(RustlsAcceptor::from_single_cert(CERT, KEY))
.run(|conn: Conn| async move { conn.ok("ok") });
}

Native TLS

rustdocs

use trillium::Conn;
use trillium_native_tls::NativeTlsAcceptor;

pub fn main() {
env_logger::init();
trillium_smol::config()
.with_acceptor(acceptor)
.run(|conn: Conn| async move { conn.ok("ok") });
}

trillium-native-tls does not currently negotiate ALPN, so HTTP/2 is not available with this adapter. If you need HTTP/2, use trillium-rustls or trillium-openssl.

OpenSSL

rustdocs

Backed by async-openssl and the openssl crate. Use this when you need OpenSSL specifically (e.g. FIPS-validated builds, organization-mandated cryptography library) and still want HTTP/2. Requires a system OpenSSL install at build time, or enable the vendored cargo feature to compile OpenSSL from source.

use trillium::Conn;
use trillium_openssl::OpenSslAcceptor;


pub fn main() {
env_logger::init();
trillium_smol::config()
.with_acceptor(OpenSslAcceptor::from_single_cert(CERT, KEY))
.run(|conn: Conn| async move { conn.ok("ok") });
}

Automatic HTTPS via Let's Encrypt

See trillium-acme for automatic certificate provisioning from ACME providers like Let's Encrypt.

HTTP/2

HTTP/2 multiplexes many requests over a single TCP connection, eliminating the head-of-line blocking and connection-count overhead of HTTP/1.1 pipelining. Trillium speaks HTTP/2 transparently: handlers receive the same Conn regardless of whether the request arrived over h1 or h2.

Over TLS

When trillium-rustls is configured, trillium advertises h2, http/1.1 in ALPN by default. Clients that select h2 during the TLS handshake get HTTP/2; clients that select http/1.1 (or that don't speak ALPN) get HTTP/1.1. No additional configuration is required.

trillium-native-tls doesn't currently expose ALPN, so ALPN-driven h2 negotiation isn't available there. Clients that send the HTTP/2 preface as the first bytes after the TLS handshake still reach h2 via the prior-knowledge path (see below); clients that don't get HTTP/1.1.

To opt out — for example, when fronting a backend that doesn't speak h2 — drop h2 from the advertised list:

use trillium_rustls::RustlsAcceptor;

let acceptor = RustlsAcceptor::from_single_cert_no_h2(CERT, KEY);

Prior knowledge

A client may also reach HTTP/2 by sending the HTTP/2 connection preface as the first 24 bytes — over cleartext TCP (h2c) or after a TLS handshake on a connector that doesn't expose ALPN. Trillium peeks at the leading bytes and dispatches to the h2 driver when it sees the preface; otherwise the connection is handled as HTTP/1.x. There is no separate listener and no configuration switch.

ℹ️ The Upgrade: h2c mechanism (RFC 7540 §3.2, removed in RFC 9113) is not supported. Use TLS+ALPN or prior knowledge.

Tuning

HTTP/2-specific knobs (max concurrent streams, flow-control window sizes, max frame size, HPACK table capacity, extended-CONNECT for WebSockets-over-h2) live on HttpConfig. See the HttpConfig rustdocs for the full list.

HTTP/3 and QUIC

HTTP/3 is the third major revision of the HTTP protocol. Instead of TCP, it's built on QUIC, a UDP-based transport that was designed with multiplexed HTTP in mind. This gives HTTP/3 several advantages over HTTP/1.x:

  • Faster connection setup — QUIC combines the transport and TLS handshakes, reducing round trips from three (TCP + TLS 1.3) to one.
  • No head-of-line blocking — HTTP/3 sends each request on an independent QUIC stream. A stalled or slow response doesn't delay any other response on the same connection.
  • Better performance on lossy networks — particularly relevant for mobile clients where packet loss is common.

Adding HTTP/3 to a server

The trillium-quinn crate adds HTTP/3 support to any Trillium server. It runs a QUIC endpoint alongside the existing TCP listener. Protocol selection happens automatically: browsers use ALPN during the TLS handshake to negotiate HTTP/3, and the server advertises support via Alt-Svc headers so clients know to use QUIC on future connections.

use trillium::Conn;
use trillium_quinn::QuicConfig;
use trillium_rustls::RustlsAcceptor;

fn main() {

trillium_tokio::config()
.with_acceptor(RustlsAcceptor::from_single_cert(cert, key))
.with_quic(QuicConfig::from_single_cert(cert, key))
.run(|conn: Conn| async move { conn.ok("hello!") });
}

As with HTTP/2, the same handler receives Conn objects regardless of whether they arrived over HTTP/1.x, HTTP/2, or HTTP/3. No changes to application logic are required.

ℹ️ TLS is required for HTTP/3. The RustlsAcceptor and QuicConfig can be initialized from the same certificate and key files.

Crypto providers

trillium-quinn uses aws-lc-rs as its crypto backend by default. To use ring instead, enable the ring feature and disable aws-lc-rs.

HTTP/3 client

The trillium-client HTTP client can upgrade to HTTP/3 automatically when a server advertises support via Alt-Svc. See the HTTP Client page for details.

WebTransport

WebTransport is a browser API built on top of HTTP/3 that provides multiplexed streams and datagrams for real-time communication. It requires HTTP/3 to be configured. See WebTransport for details.