MoosicBox HTTP
A generic HTTP client abstraction library providing unified interfaces for HTTP operations with pluggable backend implementations.
Features
- Generic Client Interface: Unified traits for HTTP clients, request builders, and responses
- Multiple Backend Support: Abstraction over different HTTP implementations (reqwest, simulator)
- Request Building: Fluent API for building HTTP requests with headers, query parameters, and body
- Response Handling: Unified response interface for status, headers, text, bytes, and streaming
- JSON Support: Built-in JSON serialization/deserialization support
- Streaming Support: Byte stream responses for large data handling
- Method Support: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS methods
- Error Handling: Unified error types across different backends
Installation
Add this to your Cargo.toml:
[dependencies]
moosicbox_http = "0.1.1"
moosicbox_http = { version = "0.1.1", features = ["reqwest"] }
moosicbox_http = { version = "0.1.1", features = ["simulator"] }
Usage
Basic HTTP Requests
use moosicbox_http::{Client, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Client::new();
let mut response = client
.get("https://api.example.com/users")
.header("Authorization", "Bearer token123")
.query_param("page", "1")
.query_param("limit", "10")
.send()
.await?;
println!("Status: {:?}", response.status());
let text = response.text().await?;
println!("Response: {}", text);
Ok(())
}
POST with JSON Body
use moosicbox_http::Client;
use serde_json::json;
async fn create_user() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let user_data = json!({
"name": "John Doe",
"email": "john@example.com"
});
let response = client
.post("https://api.example.com/users")
.header("Content-Type", "application/json")
.json(&user_data)
.send()
.await?;
if response.status().is_success() {
println!("User created successfully");
}
Ok(())
}
Handling Different Response Types
use moosicbox_http::Client;
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
async fn fetch_user_data() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let user: User = client
.get("https://api.example.com/users/1")
.send()
.await?
.json()
.await?;
println!("User: {} ({})", user.name, user.email);
let image_data = client
.get("https://api.example.com/users/1/avatar")
.send()
.await?
.bytes()
.await?;
println!("Downloaded {} bytes", image_data.len());
let readme = client
.get("https://raw.githubusercontent.com/example/repo/README.md")
.send()
.await?
.text()
.await?;
println!("README content: {}", readme);
Ok(())
}
Streaming Large Responses
use moosicbox_http::Client;
use futures::StreamExt;
async fn download_large_file() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let response = client
.get("https://example.com/large-file.zip")
.send()
.await?;
let mut stream = response.bytes_stream();
let mut total_bytes = 0;
while let Some(chunk) = stream.next().await {
let chunk = chunk?;
total_bytes += chunk.len();
println!("Downloaded {} bytes so far", total_bytes);
}
println!("Download complete: {} total bytes", total_bytes);
Ok(())
}
Custom Headers and Query Parameters
use moosicbox_http::{Client, Header};
async fn api_request_with_auth() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let response = client
.get("https://api.example.com/protected")
.header(Header::Authorization.as_ref(), "Bearer secret-token")
.header(Header::UserAgent.as_ref(), "MoosicBox/1.0")
.query_param("format", "json")
.query_param_opt("filter", Some("active"))
.query_params(&[("sort", "name"), ("order", "asc")])
.send()
.await?;
println!("Response status: {:?}", response.status());
Ok(())
}
Error Handling
use moosicbox_http::{Client, Error};
async fn handle_errors() {
let client = Client::new();
match client.get("https://invalid-url").send().await {
Ok(response) => {
println!("Request succeeded: {:?}", response.status());
}
Err(Error::Reqwest(e)) => {
println!("Network error: {}", e);
}
Err(Error::Deserialize(e)) => {
println!("JSON parsing error: {}", e);
}
Err(Error::Decode) => {
println!("Response decoding error");
}
}
}
Architecture
Core Traits
GenericClient<RB>: Main HTTP client interface with method shortcuts
GenericRequestBuilder<R>: Request builder interface for headers, params, and body
GenericResponse: Response interface for status, headers, and body access
GenericClientBuilder<RB, C>: Client builder interface
Wrapper Types
ClientWrapper: Wraps backend-specific clients with unified interface
RequestBuilderWrapper: Wraps backend-specific request builders
ResponseWrapper: Wraps backend-specific responses
Header Enumeration
Common HTTP headers are available as enum variants:
Authorization
UserAgent
Range
ContentLength
Backend Features
reqwest Feature
Enables integration with the popular reqwest HTTP client library.
simulator Feature
Enables a simulated HTTP backend for testing and development.
json Feature
Adds JSON serialization/deserialization support using serde_json.
stream Feature
Enables streaming response support for handling large responses.
Error Types
Error::Decode - Response decoding failures
Error::Deserialize - JSON deserialization errors (with json feature)
Error::Reqwest - Reqwest-specific errors (with reqwest feature)
Thread Safety
All client types implement Send + Sync for safe usage across async tasks and threads.