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": "john@example.com"}"#) .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.