Zenwave
Zenwave is an ergonomic, full-featured HTTP client framework for Rust. It exposes a modern, middleware-friendly API that works on both native targets (Tokio + Hyper on Linux/Windows, Apple's URLSession on iOS/tvOS/watchOS/macOS) and browser/Cloudflare Workers targets through the Fetch API.
Why Zenwave?
- Ergonomic requests – convenience helpers (
get,post, …) and a fluentRequestBuilder. - Opt-in middleware – add redirect following, cookie storage, OAuth2 refresh, or redirects only when you need it.
- Streaming bodies – handle large uploads/downloads or upgrade to SSE without buffering.
- HTTP caching – drop-in middleware honors
Cache-Control,Expires,ETag, andLast-Modifiedto avoid redundant network hops. - Native timers – enforce per-request deadlines with high-precision timers on every supported
platform via a simple
.timeout(...)helper. - Proxy aware – honor
HTTP(S)_PROXY/NO_PROXYor define custom SOCKS/HTTP proxies when using the Hyper or curl backends. - WebSocket ready – one API that works natively and in WASM.
- Pluggable backends – Hyper on general native targets, URLSession on Apple platforms, libcurl
when you want small binaries, and Fetch on wasm, all behind the same
zenwave::client()interface.
Quick Start
use ;
async
The ResponseExt trait provides the into_string, into_json, into_bytes, and into_sse helpers
you will see throughout the API.
Examples
Run the shipped samples with cargo run --example <name>:
basic_get– one-off GET request parsed into a typed response.custom_client– compose middleware, send JSON, and read a typed response body.websocket_echo– connect to a public echo server using the cross-platform WebSocket client.
Feel free to copy these examples as starting points for your own projects.
Building richer clients
use Duration;
use ;
use ;
async
You can also call .basic_auth or .with(custom_middleware) to plug in your own behavior. Every
request builder supports .header, .bearer_auth, .basic_auth, .json_body, .bytes_body, and
body readers (.json(), .string(), .bytes(), .form(), .sse()).
Timeouts are middleware too. Calling .timeout(Duration::from_secs(2)) wraps the client in a
native-executor-backed timer so every subsequent request automatically fails with a
504 Gateway Timeout when the deadline is exceeded.
Proxy configuration (native Hyper / curl backends)
Zenwave can route requests through HTTP or SOCKS proxies by reading the
standard HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and NO_PROXY variables or
by constructing a matcher manually. Both the Hyper and libcurl-native backends
honor the same configuration:
use ;
Only the Hyper and curl backends currently honor proxies. HTTP CONNECT proxies
(http:// / https://) and SOCKS4/5 proxies (socks4[a], socks5[h]) are supported.
The Apple (apple-backend) and Web (wasm32) backends do not expose proxy
APIs, so helper functions such as client_with_proxy or .proxy(...) are not
compiled when those backends are selected as the default.
Large downloads with resume
Native targets get an ergonomic helper for writing large responses to disk without buffering into
memory. Any request builder can call download_to_path to stream the body into a file. When the
file already exists Zenwave automatically issues a Range request and appends only the missing
bytes, so interrupted transfers can resume without starting from scratch.
use Client;
# async
If you need to opt out of resume logic you can call download_to_path_with and pass
DownloadOptions { resume_existing: false }. Both methods return a DownloadReport so you can log
how much data was appended and what now resides on disk. This helper is currently available on
non-wasm targets where direct filesystem access exists.
Streaming uploads
Use file_body to upload large files directly from disk without buffering, reader_body to wrap
any AsyncRead, or stream_body to hook up custom chunk producers. Each helper integrates with
Tokio so uploads backpressure naturally with the network stack.
# async
HTTP cache middleware
Call .enable_cache() to enable RFC-compliant client-side caching. The middleware caches
successful GET responses when permitted by Cache-Control/Expires, automatically injects
validators for stale entries (If-None-Match, If-Modified-Since), and serves 304 Not Modified
responses straight from memory. Requests with Authorization headers are skipped unless the
response explicitly declares itself public. Because it is implemented as middleware you can keep
it for native builds only or combine it with other layers as needed.
Persistent cookie store
Call .enable_persistent_cookie() to transparently load and save cookies between runs on native
targets. Zenwave automatically picks a cache file under your platform's local data directory using
the name zenwave_cookie_store_<crate_name>.json, so crates only share cookies with themselves by
default. You can also fully control the path via CookieStore::persistent_with_path if you want to
sync cookies across binaries.
OAuth2 client credentials
Use OAuth2ClientCredentials::new(token_url, client_id, client_secret) to automatically obtain and
refresh bearer tokens. The middleware performs the client credentials flow against the configured
token endpoint, caches responses until they near expiration, and injects the Authorization header
for every outgoing request. Call .with_scope("scope1 scope2") or .with_audience("api") if your
provider requires additional parameters.
Web & Cloudflare Workers
Zenwave targets both wasm32 and native platforms. On wasm it relies on web_sys::Request/Fetch,
so it works in browsers and Cloudflare Workers without extra glue code. The API is identical, so
sharing code between targets is straightforward.
Apple platforms
By default Apple targets (iOS, iPadOS, tvOS, watchOS, macOS) also use the Hyper backend. There is an
experimental apple-backend feature that swaps Hyper out for URLSession, which satisfies
watchOS/App Store restrictions but currently auto-follows redirects and auto-manages cookies. The
two middleware tests that asserted “no redirect / no automatic cookies” are skipped whenever the
apple-backend feature is enabled. Until the URLSession backend is stabilized, we recommend keeping
the default Hyper backend on Apple. If you still want to opt in:
= { = "0.1.0", = ["apple-backend"] }
Curl backend
Many Linux distributions (and some embedded platforms) ship a system libcurl. Zenwave can reuse it to avoid bundling Hyper/OpenSSL and shrink your binary. Disable the default features and enable the curl backend:
[]
= { = "0.1.0", = false, = ["curl-backend"] }
You still get the same middleware API; the only difference is which backend transports the bytes.
WebSocket support
The zenwave::websocket module offers a cross-platform WebSocket client that hides the details of
async-tungstenite or web_sys::WebSocket. Connecting to an endpoint looks like:
use ;
async
You can also split a connection to drive sending and receiving from different tasks:
let socket = connect.await?;
let = socket.split;
// `send` serializes to JSON by default; use `send_text` for raw text frames.
sender.send.await?;
if let Some = receiver.recv.await?
Installation
Add Zenwave to your Cargo.toml. The default configuration uses the Hyper backend with rustls TLS:
[]
= { = "0.1.0" }
For browser/Workers builds, no special configuration is needed - Zenwave automatically uses the built-in web backend (Fetch API) on wasm32 targets:
# For wasm32 targets, default features are ignored and the web backend is used automatically
= { = "0.1.0" }
Feature flags
Backend Selection (native platforms only)
On wasm32 targets, the built-in web backend is always used automatically. No backend selection is needed or available.
On native platforms, you can choose from:
hyper-backend(default) – Hyper with async-io/async-net. The recommended choice for most use cases.curl-backend– libcurl-based backend with built-in proxy support. Good for platforms with system libcurl.apple-backend– experimental URLSession backend for Apple platforms (macOS/iOS).
TLS Selection (hyper-backend only)
rustls(default) – pure-Rust TLS implementation. Cross-platform and secure.native-tls– uses the platform's native TLS (OpenSSL on Linux, Secure Transport on macOS, SChannel on Windows).
Only one TLS feature can be enabled at a time. These features only apply to hyper-backend;
other backends have their own TLS implementations.
Other Features
proxy– enables proxy support (automatically enabled withcurl-backend).
Example configurations
# Use curl backend instead of hyper
= { = "0.1.0", = false, = ["curl-backend"] }
# Use hyper with native-tls instead of rustls
= { = "0.1.0", = false, = ["hyper-backend", "native-tls"] }
# Use Apple's native URLSession on macOS/iOS
= { = "0.1.0", = false, = ["apple-backend"] }
License
MIT License