35 releases
Uses new Rust 2024
| 0.1.41 | Jun 10, 2026 |
|---|---|
| 0.1.40 | Jun 3, 2026 |
| 0.1.39 | May 26, 2026 |
| 0.1.35 | Apr 23, 2026 |
| 0.1.22 | Feb 25, 2026 |
#258 in Network programming
13,876 downloads per month
Used in 7 crates
(6 directly)
1MB
23K
SLoC
reqx
reqx is an HTTP transport client for Rust API SDK libraries.
It focuses on SDK transport concerns: retries, timeout phases, idempotency, proxy routing, structured errors, and metrics.
For SDK Authors
- Start with a profile:
ClientProfile::StandardSdk,ClientProfile::LowLatency, orClientProfile::HighThroughput. - Override specific builder knobs only when required after
.profile(...). - Keep strict behavior with
StatusPolicy::Error(default), or opt into response-first mode withStatusPolicy::Response. - For multi-endpoint SDKs, plug in an
EndpointSelector(for exampleRoundRobinEndpointSelector). - Hook transport events through
Observerfor retries and server-throttle telemetry. - Use
control_clock(...)only when you need deterministic Retry-After or control-loop behavior in tests. - See
advanced_time_controlsfor the minimal expert-only setup ofcontrol_clock(...)andstream_deadline_slack(...). - Reach for
reqx::advanced::{...}when you need non-default transport controls.
Install
cargo add reqx
Use native-tls:
cargo add reqx --no-default-features -F async-tls-native
Use rustls (ring):
cargo add reqx --no-default-features -F async-tls-rustls-ring
Use rustls + aws-lc-rs:
cargo add reqx --no-default-features -F async-tls-rustls-aws-lc-rs
Use blocking client with ureq + rustls(ring):
cargo add reqx --no-default-features -F blocking-tls-rustls-ring
Use blocking client with ureq + native-tls:
cargo add reqx --no-default-features -F blocking-tls-native
TLS Backends
- feature contract:
- enable at least one transport mode
- for each enabled mode (
async/blocking), enable exactly one TLS backend - enabling both
asyncandblockingis supported
- async backends (default mode):
async-tls-rustls-ring(default)async-tls-rustls-aws-lc-rsasync-tls-native
- blocking backends (
ureq):blocking-tls-rustls-ringblocking-tls-rustls-aws-lc-rsblocking-tls-native
- runtime selection via
tls_backend(TlsBackend::...) - build-time mismatch returns structured error from
build() - trust store selection via
tls_root_store(TlsRootStore::BackendDefault | WebPki | System | Specific) - async TLS version controls via
tls_version(...),tls_min_version(...), andtls_max_version(...) - use
tls_max_version(TlsVersion::V1_2)for version-intolerant endpoints that reject TLS 1.3 - async
native-tlsforwards TLS version bounds to the platform TLS stack - blocking
ureqtransports do not expose TLS version selection BackendDefaultfollows each backend's default trust roots- custom root CA with public roots:
tls_root_store(TlsRootStore::WebPki)+tls_root_ca_pem(...)/tls_root_ca_der(...) - custom root CA only:
tls_root_store(TlsRootStore::Specific)+tls_root_ca_pem(...)/tls_root_ca_der(...) - PEM root CA and certificate-chain inputs must contain only
CERTIFICATEblocks; pass private keys through the dedicated identity key parameter. - mTLS identity:
- PEM chain + key:
tls_client_identity_pem(...)(async + sync) - PKCS#12:
tls_client_identity_pkcs12(...)(asyncasync-tls-native)
- PEM chain + key:
Quick Start
use std::time::Duration;
use reqx::prelude::{Client, RetryPolicy};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct CreateItemResponse {
id: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::builder("https://api.example.com")
.client_name("example-sdk")
.request_timeout(Duration::from_secs(3))
.total_timeout(Duration::from_secs(8))
.retry_policy(
RetryPolicy::standard()
.max_attempts(3)
.base_backoff(Duration::from_millis(100))
.max_backoff(Duration::from_millis(800)),
)
.build()?;
let created: CreateItemResponse = client
.post("/v1/items")
.idempotency_key("create-item-001")?
.json(&serde_json::json!({ "name": "demo" }))?
.send_json()
.await?;
println!("created id={}", created.id);
Ok(())
}
Blocking Quick Start
use std::time::Duration;
use reqx::blocking::Client;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::builder("https://api.example.com")
.request_timeout(Duration::from_secs(3))
.total_timeout(Duration::from_secs(8))
.build()?;
let response = client.get("/v1/items").send()?;
println!("status={}", response.status());
Ok(())
}
Core Capabilities
- global defaults + per-request overrides
- profile presets (
ClientProfile) + direct builder overrides - idempotency-aware retries
- retry budget + circuit breaker + adaptive concurrency controls
- global/per-host rate limiting with
429 Retry-Afterbackpressure - request-level and client-level status handling (
StatusPolicy) - bounded redirect following (
RedirectPolicy) - transport timeout + response-body timeout + total deadline
stream_deadline_slack(...)tunes only near-deadline streaming error classification when total deadline is already tighter than the body-read timeout; it does not shorten actual I/O waits- separate connect timeout (
connect_timeout(...)) - streaming upload plus
ResponseStream: AsyncRead/blocking::ResponseStream: Read - stream
copy_to_writer*/into_bytes_limitedkeep raw bytes (wire semantics) - explicit buffered conversion (
send(),into_response_limited,into_json_limited) decodesgzip,br,deflate,zstdfor both async and blocking - proxy support with auth and
no_proxy - blocking mode proxy auth contract: for
blocking-*features, configure proxy credentials inhttp_proxyURI (http://user:pass@proxy:port); directproxy_authorization(...)forwarding is intentionally rejected byureqtransport - interceptor hooks for SDK concerns (
Interceptor) - response body size limit
- structured error variants + machine error codes
- stable status-error metadata helpers:
status_code(),response_headers(),retry_after(),request_id() - metrics snapshot for retries, latency, status and error buckets
- observer hooks (
Observer) for request-start, retry scheduling, and server throttling
Examples
- Full index:
examples/README.md cargo run --example basic_jsoncargo run --example advanced_time_controlscargo run --example request_helperscargo run --example request_overridescargo run --example profile_and_observercargo run --example error_handlingcargo run --example metrics_snapshotcargo run --example streamingcargo run --example concurrency_limitscargo run --example resilience_controlscargo run --example rate_limit_429cargo run --example retry_classifiercargo run --example proxy_and_no_proxycargo run --example tls_backendscargo run --example custom_ca_mtlscargo run --example interceptor_redirectcargo run --example blocking_basic --no-default-features -F blocking-tls-rustls-ring
Dependencies
~19–47MB
~1M SLoC