High-performance, async-first HTTP client for Rust. Cross-platform with native backends: NSURLSession on Apple platforms, WinHTTP on Windows, and Reqwest elsewhere.
- Async/await with tokio - Full async support, no blocking APIs
- All HTTP methods - GET, POST, PUT, DELETE, PATCH, HEAD
- WebSocket support - NSURLSessionWebSocketTask/WinHTTP integration
- File operations - Uploads/downloads with progress tracking
- Background sessions - Downloads that survive app suspension (iOS)
- Cookie management - NSHTTPCookieStorage/cookie_store integration
- Proxy configuration - HTTP/HTTPS/SOCKS proxy support
- Authentication - Basic, Bearer, and Custom authentication
- TLS/Certificate handling - Server trust challenge support
- Streaming responses - AsyncRead for memory-efficient large downloads
- Multipart form data - File uploads with form fields
- Zero-overhead - Direct objc2 bindings with minimal abstractions
Add this to your Cargo.toml:
[dependencies]
frakt = "0.1"- macOS 10.15+ (Foundation backend using NSURLSession)
- iOS 13.0+ (Foundation backend using NSURLSession)
- Windows (WinHTTP backend)
- Linux/Unix (Reqwest backend)
- Rust 1.86+
The library automatically selects the best native backend for each platform. You can also manually specify a backend using BackendType if needed.
use frakt::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client
let client = Client::builder()
.user_agent("MyApp/1.0")
.timeout(std::time::Duration::from_secs(30))
.build()?;
// Make a GET request
let response = client
.get("https://httpbin.org/json")
.header("Accept", "application/json")
.send()
.await?;
println!("Status: {}", response.status());
let json: serde_json::Value = response.json().await?;
println!("Response: {}", json);
Ok(())
}// GET request
let response = client.get("https://api.example.com/data").send().await?;
// POST with JSON
let response = client
.post("https://api.example.com/users")
.header("Content-Type", "application/json")
.body(r#"{"name": "John", "email": "[email protected]"}"#)
.send()
.await?;use frakt::Auth;
// Basic authentication
let response = client
.get("https://api.example.com/protected")?
.auth(Auth::basic("user", "pass"))
.send()
.await?;
// Bearer token
let response = client
.get("https://api.example.com/protected")?
.auth(Auth::bearer("your-token"))
.send()
.await?;let response = client
.get("https://example.com/large-file.zip")?
.progress(|downloaded, total| {
if let Some(total) = total {
let percent = (downloaded as f64 / total as f64) * 100.0;
println!("Progress: {:.1}%", percent);
}
})
.send()
.await?;
// Download directly to file
client
.download("https://example.com/file.zip")
.to_file("./downloads/file.zip")
.progress(|downloaded, total| {
println!("Downloaded: {} / {:?} bytes", downloaded, total);
})
.send()
.await?;use frakt::{Message, CloseCode};
let websocket = client
.websocket()
.maximum_message_size(1024 * 1024)
.connect("wss://echo.websocket.org")
.await?;
// Send message
websocket.send(Message::text("Hello, WebSocket!")).await?;
// Receive message
let message = websocket.receive().await?;
match message {
Message::Text(text) => println!("Received: {}", text),
Message::Binary(data) => println!("Received {} bytes", data.len()),
}
// Close connection
websocket.close(CloseCode::Normal, Some("Goodbye"));let client = Client::builder()
.use_cookies(true)
.build()?;
// Cookies are automatically managed
let response = client.get("https://httpbin.org/cookies/set/session/abc123")?.send().await?;
let response = client.get("https://httpbin.org/cookies")?.send().await?; // Cookie sent automaticallylet client = Client::builder()
.http_proxy("proxy.example.com", 8080)
.proxy_auth("username", "password")
.build()?;use tokio::io::AsyncReadExt;
let response = client.get("https://example.com/large-file")?.send().await?;
let mut stream = response.stream();
let mut buffer = [0u8; 8192];
while let bytes_read = stream.read(&mut buffer).await? {
if bytes_read == 0 { break; }
// Process chunk
process_chunk(&buffer[..bytes_read]);
}Run examples with cargo run --example <name>:
auth- Authentication methods (Basic, Bearer, Custom)cookies- Cookie management and automatic handlingdownload- Basic file downloadsfile_download- Direct-to-file downloads with progressfile_upload- File uploads using upload buildermultipart- Multipart form data uploadsprogress- Progress tracking for downloadsproxy- Proxy configuration (HTTP/HTTPS/SOCKS)streaming- Streaming large responses with AsyncReadupload_task- Upload tasks with progress trackingwebsocket- WebSocket client usagebackground_download- Background downloads (iOS)
This library provides a unified HTTP client interface with platform-native backends. On Apple platforms, it uses direct Rust bindings to NSURLSession via objc2. On Windows, it uses WinHTTP APIs. On other platforms, it uses the battle-tested Reqwest library. Key design principles:
- Async-only: Built for tokio, no blocking APIs
- Zero-overhead: Direct native API calls with minimal abstraction
- Memory efficient: Uses platform-native data types where possible
- Type safe: All unsafe native API calls are wrapped in safe APIs
- Rusty: Builder patterns and ergonomic error handling
All errors are mapped to Rust's Result type:
use frakt::Error;
match client.get("https://invalid-url")?.send().await {
Ok(response) => println!("Success: {}", response.status()),
Err(Error::InvalidUrl) => println!("Invalid URL"),
Err(Error::Network(msg)) => println!("Network error: {}", msg),
Err(Error::Timeout) => println!("Request timed out"),
Err(e) => println!("Other error: {}", e),
}frakt leverages platform-native performance optimizations:
- HTTP/2 and HTTP/3 support (when available)
- Connection pooling and keep-alive
- Automatic compression (gzip, deflate, br)
- Native TLS implementation
- Background processing capabilities
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.