aioduct

The HTTP client that runs everywhere — even right here.

Async-native Rust HTTP client built on hyper 1.x. Multi-runtime, HTTP/3, WASM, bandwidth limiting, connection coalescing, and more.

Live WASM Demo Loading WASM…
let client = WasmClient::new();
let resp = client.get("https://httpbin.org/get")?
    .send().await?;
let data: serde_json::Value = resp.json()?;
Click "Run" to fire a real HTTP request from WASM

Feature-Flag Configurator

Pick the features you need. We'll generate your Cargo.toml with exactly the right dependencies.

Runtime

TLS & Transport

DNS

Compression

Extras

Cargo.toml
[dependencies]
aioduct = { version = "0.1.7", features = ["tokio", "rustls", "json"] }
5
Async Runtimes
113
KB WASM (gzipped)
0
Unsafe Code
20+
Feature Flags

aioduct vs reqwest

Same tasks, side by side. See what aioduct gives you out of the box.

aioduct

let client = Client::<TokioRuntime>::builder()
    .retry(RetryConfig::default().max_retries(3))
    .build();

let resp = client.get("https://httpbin.org/get")?
    .send().await?;
let data: serde_json::Value = resp.json().await?;

reqwest

// reqwest has no built-in retry.
// You need reqwest-middleware + reqwest-retry crates.
let client = reqwest::Client::new();

let resp = client.get("https://httpbin.org/get")
    .send().await?;
let data: serde_json::Value = resp.json().await?;
No built-in retry — requires extra crates

aioduct

let client = Client::<TokioRuntime>::builder()
    .bandwidth_limit(1_048_576) // 1 MB/s
    .build();

let resp = client
    .get("https://example.com/large-file")?
    .send().await?;
let bytes = resp.bytes().await?;
Built-in bandwidth limiting

reqwest

let client = reqwest::Client::new();

let resp = client
    .get("https://example.com/large-file")
    .send().await?;

// No bandwidth limiting available.
// Must manually throttle the byte stream.
let mut stream = resp.bytes_stream();
while let Some(chunk) = stream.next().await {
    // manual throttle logic here...
}
No bandwidth limiting — manual stream throttle needed

aioduct

let client = Client::<TokioRuntime>::builder()
    .tls(RustlsConnector::with_webpki_roots())
    .http3(true)
    .build();

// Automatically upgrades to HTTP/3 via Alt-Svc
let resp = client.get("https://cloudflare.com/")?
    .send().await?;
println!("{:?}", resp.version()); // HTTP/3
Native HTTP/3 with automatic Alt-Svc upgrade

reqwest

// HTTP/3 is not available in reqwest.
//
// You'd need to use h3 + quinn directly,
// which requires hundreds of lines of manual
// connection management, TLS config, and
// stream handling.

let client = reqwest::Client::new();
// Always HTTP/1.1 or HTTP/2 — never HTTP/3
HTTP/3 not supported

aioduct

let resp = client.get(url)?.send().await?;
let timings = resp.timings();

println!("DNS:   {:?}", timings.dns());
println!("TCP:   {:?}", timings.tcp());
println!("TLS:   {:?}", timings.tls());
println!("TTFB:  {:?}", timings.ttfb());
println!("Total: {:?}", timings.total());
Built-in per-request timing breakdown (DNS/TCP/TLS/TTFB)

reqwest

let start = Instant::now();
let resp = client.get(url).send().await?;
let elapsed = start.elapsed();

// Only total time available
// No DNS/TCP/TLS/TTFB breakdown
println!("Total: {elapsed:?}");
No timing breakdown — total elapsed only

aioduct

let cache = HttpCache::new(
    InMemoryCacheStore::new(),
    CacheConfig::default(),
);
let client = Client::<TokioRuntime>::builder()
    .cache(cache)
    .build();

// Second request served from cache
let r1 = client.get(url)?.send().await?;
let r2 = client.get(url)?.send().await?;
Built-in HTTP cache with pluggable stores

reqwest

// reqwest has no built-in HTTP caching.
// Options:
// 1. http-cache-reqwest (third-party)
// 2. Manual Cache-Control/ETag handling
// 3. External caching proxy

let client = reqwest::Client::new();
// Every request hits the network
let r1 = client.get(url).send().await?;
let r2 = client.get(url).send().await?;
No built-in cache — third-party or manual

aioduct

// Same API, any runtime — just change the type parameter
use aioduct::runtime::{TokioRuntime, SmolRuntime};

// With tokio:
let client = Client::<TokioRuntime>::builder().build();

// With smol (zero code changes):
let client = Client::<SmolRuntime>::builder().build();

// Also: CompioRuntime, WasmClient, WasiP2Client
tokio / smol / compio / WASM / WASI-P2

reqwest

// reqwest is hard-coded to tokio.
// If your project uses smol or async-std,
// you must also pull in tokio as a dependency.

let client = reqwest::Client::new();
// No way to use smol, compio, or any other runtime
Locked to tokio — no other runtime support

aioduct

let client = Client::<TokioRuntime>::builder()
    .middleware(TracingMiddleware)
    .middleware(OtelMiddleware::default())
    .build();

// Every request automatically emits:
// - tracing spans (method, url, status, duration)
// - OpenTelemetry spans (Jaeger/Zipkin/OTLP)
let resp = client.get(url)?.send().await?;
Built-in tracing + OpenTelemetry middleware

reqwest

// reqwest has no built-in observability.
//
// For tracing: use reqwest-tracing (third-party)
// For OpenTelemetry: no official integration
// Manual span creation around each request:

let span = tracing::info_span!("http", ...);
let resp = client.get(url).send()
    .instrument(span).await?;
Manual instrumentation or third-party crates

aioduct

let client = Client::<TokioRuntime>::builder()
    .tls(RustlsConnector::with_webpki_roots())
    .dns_over_https(
        "1.1.1.1".parse().unwrap(),
        "cloudflare-dns.com",
    )
    .build();

// All DNS queries encrypted via HTTPS
let resp = client.get(url)?.send().await?;
Built-in DNS-over-HTTPS and DNS-over-TLS

reqwest

// reqwest does not support DoH/DoT.
// You'd need to:
// 1. Add hickory-resolver as a dependency
// 2. Configure it for HTTPS transport
// 3. Implement reqwest's Resolve trait
// 4. Wire the resolver into the client builder
//
// ~50 lines of glue code for something aioduct
// does with a single builder method.
Not available — manual hickory-resolver integration needed

Feature Comparison

Feature aioduct reqwest ureq hyper
HTTP/1.1 Yes Yes Yes Yes
HTTP/2 Yes Yes No Yes
HTTP/3 (QUIC) Yes No No No
Multi-runtime tokio / smol / compio tokio only blocking only tokio only
WASM (browser) Yes Limited No No
WASI Preview 2 Yes No No No
Connection coalescing Yes No No No
Built-in retry Yes No (plugin) No No
Bandwidth limiting Yes No No No
DoH / DoT Yes No No No
HTTP cache Yes No No No
0-RTT reconnection Yes No No No
Tower middleware Yes Via plugin No Yes
OpenTelemetry Built-in No No No