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.
let client = WasmClient::new();
let resp = client.get("https://httpbin.org/get")?
.send().await?;
let data: serde_json::Value = resp.json()?;
Pick the features you need. We'll generate your Cargo.toml with exactly the right dependencies.
[dependencies]
aioduct = { version = "0.1.7", features = ["tokio", "rustls", "json"] }
Same tasks, side by side. See what aioduct gives you out of the box.
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 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
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
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
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
// 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
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)
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
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 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
// 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 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
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 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
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 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 | 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 |