14 releases
Uses new Rust 2024
| new 0.1.14 | Feb 12, 2026 |
|---|---|
| 0.1.13 | Feb 11, 2026 |
#435 in Network programming
Used in s3
455KB
12K
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. - Fine-tune with
AdvancedConfigonly when required. - 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.
Install
cargo add reqx
Use native-tls:
cargo add reqx --no-default-features -F async-native-tls
Use rustls (alias to async-tls-rustls-ring):
cargo add reqx --no-default-features -F async-rustls
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-rustls
Use blocking client with ureq + native-tls:
cargo add reqx --no-default-features -F blocking-native-tls
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
- ergonomic aliases:
async-rustls->async-tls-rustls-ringasync-native-tls->async-tls-nativeblocking-rustls->blocking-tls-rustls-ringblocking-native-tls->blocking-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) BackendDefaultfollows each backend's default trust roots; setSystemexplicitly for enterprise/private PKI environments- custom root CA:
tls_root_store(TlsRootStore::Specific)+tls_root_ca_pem(...)/tls_root_ca_der(...) - 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) + explicit overrides (AdvancedConfig) - 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
- separate connect timeout (
connect_timeout(...)) - streaming upload and streaming response path
- 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 - 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 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
Release Checklist
just cijust feature-matrixjust feature-contractjust docsrs-checkjust release-check
Error Model
Common Error variants:
Transport { kind, .. }Timeout { phase, .. }DeadlineExceeded { .. }HttpStatus { status, body, .. }DecodeContentEncoding { .. }DeserializeJson { .. }
Use error.code() for stable machine-readable classification.
Dependencies
~19–50MB
~1M SLoC