libhttp3 0.1.1

A Rust library for HTTP/3 clients and servers.
Documentation

libhttp3

A Rust library for HTTP/3 clients and servers, built on Quinn (QUIC), h3, and Axum.

Features

  • HTTP/3 Server -- Serve an Axum Router over QUIC/HTTP3 with a single function call
  • HTTP/3 Client -- Full-featured client with GET, POST, PUT, DELETE methods
  • SSE Streaming -- Server-Sent Events over HTTP/3
  • Binary Streaming -- Length-prefixed binary frame protocol over HTTP/3
  • TLS -- Built-in rustls integration with certificate loading

Usage

Add to your Cargo.toml:

[dependencies]
libhttp3 = "0"

Server

Use http3_serve to serve any Axum router over HTTP/3. You need a TLS certificate and key in PEM format.

use axum::{Router, routing::get};
use std::net::SocketAddr;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let app = Router::new()
        .route("/", get(|| async { "Hello over HTTP/3!" }));

    let addr: SocketAddr = "0.0.0.0:4433".parse()?;

    libhttp3::server::http3_serve(
        app,
        addr,
        "certs/server.crt".into(),
        "certs/server.key".into(),
    ).await
}

Client

H3Client connects to an HTTP/3 server using a CA certificate for TLS verification.

use libhttp3::H3Client;
use bytes::Bytes;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut client = H3Client::new(
        "localhost",
        4433,
        "certs/ca.crt".into(),
        None, // optional TLS server name override
    ).await?;

    // GET request
    let resp = client.get("/").await?;
    println!("Status: {}", resp.status);
    println!("Body: {}", String::from_utf8_lossy(&resp.body));

    // POST with JSON body
    let body = Bytes::from(r#"{"key": "value"}"#);
    let resp = client.post("/data", Some(body)).await?;
    println!("POST status: {}", resp.status);

    Ok(())
}

SSE Streaming

let mut stream = client.get_stream("/events").await?;

while let Some(event) = stream.next_event().await? {
    if let Some(ref event_type) = event.event_type {
        println!("Event type: {}", event_type);
    }
    println!("Data: {}", event.data);
}

Binary Streaming

Uses a length-prefixed frame protocol ([4-byte big-endian length][payload], max 16 MiB per frame).

let mut stream = client.get_binary_stream("/binary").await?;

while let Some(frame) = stream.next_frame().await? {
    println!("Received {} bytes", frame.len());
}

Examples

See the examples/ directory for complete, runnable examples:

  • server.rs -- HTTP/3 server with multiple routes
  • client.rs -- HTTP/3 client making requests

Run the server:

cargo run --example server

Then in another terminal:

cargo run --example client

TLS Certificates

Both the server and client require TLS certificates. For local development, generate self-signed certs:

# Generate CA key and certificate
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
  -days 365 -nodes -keyout certs/ca.key -out certs/ca.crt \
  -subj "/CN=localhost CA"

# Generate server key and CSR
openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
  -nodes -keyout certs/server.key -out certs/server.csr \
  -subj "/CN=localhost"

# Sign server cert with CA
openssl x509 -req -in certs/server.csr -CA certs/ca.crt -CAkey certs/ca.key \
  -CAcreateserial -out certs/server.crt -days 365 \
  -extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1")