WebSockets, WebTransport, and JSON
A few client capabilities live behind cargo features, so they only pull in their dependencies when you ask for them.
WebSocket client
With the websockets feature, a built conn can be upgraded to a WebSocket. This works over HTTP/1.1 (RFC 6455) and, when the connection negotiated h2, over HTTP/2 extended CONNECT (RFC 8441) — the same upgrade either way from the caller's side.
let ws_conn = client
.get("wss://example.com/ws")
.await
.unwrap()
.into_websocket()
.await
.unwrap();
The resulting WebSocketConn exposes the same send/receive interface as the server-side WebSocket handler.
WebTransport client
WebTransport is a protocol over HTTP/3 and QUIC offering multiplexed streams and unreliable datagrams. With the webtransport feature, a client built with new_with_quic can open sessions to a WebTransport server.
Client::webtransport(url) builds a conn preconfigured for the extended-CONNECT handshake — method CONNECT, the :protocol pseudo-header set to webtransport, pinned to HTTP/3. Awaiting it with Conn::into_webtransport() completes the upgrade and hands back a WebTransportConnection, the same session type the server handler uses.
use trillium_client::Client;
use trillium_quinn::ClientQuicConfig;
use trillium_rustls::RustlsConfig;
use trillium_tokio::ClientConfig;
let client = Client::new_with_quic(
RustlsConfig::<ClientConfig>::default(),
ClientQuicConfig::with_webpki_roots(),
);
let conn = client.webtransport("https://example.com/wt");
// let session = conn.into_webtransport().await?;
Multiple sessions to the same origin coalesce onto a single underlying QUIC connection, matching how HTTP/3 request multiplexing already works.
JSON bodies
Enabling either the serde_json or sonic-rs feature adds JSON convenience methods backed by that serializer. Conn::response_json::<T>() deserializes a response body, and Conn::with_json_body serializes a request body:
use serde::Deserialize;
use trillium_client::Client;
use trillium_smol::ClientConfig;
#[derive(Deserialize)]
struct Widget {
name: String,
}
let mut conn = client.get("https://api.example.com/widget").await.unwrap();
let widget: Widget = conn.response_json().await.unwrap();
println!("{}", widget.name);
JSON errors surface as ClientSerdeError, which wraps either a transport error or a serializer error. For ad-hoc request bodies without a struct, the crate re-exports a json! macro. The two backends are mutually exclusive — enable one.
Also: gRPC
trillium-grpc builds a spec-conformant gRPC client (and server) on top of trillium-client. You write a .proto, codegen produces a typed <Service>Client wrapping a Client, and each RPC shape — unary, server-streaming, client-streaming, bidirectional — gets a call handle that fits it. See its documentation for the full guide.