Loading page…
Rust walkthroughs
Loading page…
The http crate provides core HTTP types that form the foundation for many Rust web libraries like hyper, reqwest, axum, and others. It defines strongly-typed representations of HTTP requests, responses, headers, method, status codes, and URIs. The crate is designed to be a shared foundation that different HTTP libraries can build upon, ensuring interoperability. Understanding http is essential for working with web services in Rust at a lower level or building your own HTTP abstractions.
Key concepts:
# Cargo.toml
[dependencies]
http = "1.0"use http::{Request, Response, Method, StatusCode};
fn main() {
let request = Request::builder()
.method(Method::GET)
.uri("https://example.com/api/users")
.header("Content-Type", "application/json")
.body(()).unwrap();
println!("Request: {} {}", request.method(), request.uri());
}use http::{Request, Method};
fn main() {
// Simple request with builder
let request = Request::builder()
.method(Method::GET)
.uri("/users/123")
.body(())
.unwrap();
println!("Method: {}", request.method());
println!("URI: {}", request.uri());
// POST request with body
let post_request = Request::builder()
.method(Method::POST)
.uri("/api/users")
.header("Content-Type", "application/json")
.body(r#"{"name":"Alice"}"#.to_string())
.unwrap();
println!("Body: {}", post_request.body());
}use http::Request;
fn main() {
let request = Request::builder()
.method("GET")
.uri("https://example.com/search?q=rust")
.header("Accept", "application/json")
.header("User-Agent", "MyApp/1.0")
.body("request body".to_string())
.unwrap();
// Decompose into parts and body
let (parts, body) = request.into_parts();
println!("Method: {:?}", parts.method);
println!("URI: {:?}", parts.uri);
println!("Version: {:?}", parts.version);
println!("Headers: {:?}", parts.headers);
println!("Body: {}", body);
}use http::{Response, StatusCode};
fn main() {
// Simple response
let response = Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "text/plain")
.body("Hello, World!")
.unwrap();
println!("Status: {}", response.status());
println!("Body: {:?}", response.body());
// Response with different status
let not_found = Response::builder()
.status(StatusCode::NOT_FOUND)
.header("Content-Type", "application/json")
.body(r#"{"error":"Not found"}"#)
.unwrap();
println!("Status: {}", not_found.status());
}use http::Method;
fn main() {
// Common methods
let get = Method::GET;
let post = Method::POST;
let put = Method::PUT;
let delete = Method::DELETE;
let patch = Method::PATCH;
let head = Method::HEAD;
let options = Method::OPTIONS;
let connect = Method::CONNECT;
let trace = Method::TRACE;
println!("GET: {}", get);
println!("POST: {}", post);
// Custom method
let custom = Method::from_bytes(b"CUSTOM").unwrap();
println!("Custom: {}", custom);
// Compare methods
if get == Method::GET {
println!("This is a GET request");
}
// Method properties
println!("GET is safe: {}", get.is_safe());
println!("POST is safe: {}", post.is_safe());
println!("GET is idempotent: {}", get.is_idempotent());
}use http::StatusCode;
fn main() {
// Common status codes
let ok = StatusCode::OK;
let created = StatusCode::CREATED;
let bad_request = StatusCode::BAD_REQUEST;
let not_found = StatusCode::NOT_FOUND;
let server_error = StatusCode::INTERNAL_SERVER_ERROR;
println!("OK: {} - {}", ok.as_u16(), ok.canonical_reason().unwrap());
// Status code categories
for code in [200, 201, 301, 400, 404, 500] {
let status = StatusCode::from_u16(code).unwrap();
let category = if status.is_informational() {
"Informational"
} else if status.is_success() {
"Success"
} else if status.is_redirection() {
"Redirection"
} else if status.is_client_error() {
"Client Error"
} else if status.is_server_error() {
"Server Error"
} else {
"Unknown"
};
println!("{}: {} - {:?}", code, category, status.canonical_reason());
}
}use http::{HeaderMap, HeaderName, HeaderValue};
fn main() {
let mut headers = HeaderMap::new();
// Insert headers
headers.insert("content-type", "application/json".parse().unwrap());
headers.insert("authorization", "Bearer token123".parse().unwrap());
// Append header (allows multiple values)
headers.append("set-cookie", "session=abc".parse().unwrap());
headers.append("set-cookie", "theme=dark".parse().unwrap());
// Get single header
if let Some(content_type) = headers.get("content-type") {
println!("Content-Type: {:?}", content_type);
}
// Get all values for a header
for cookie in headers.get_all("set-cookie").iter() {
println!("Cookie: {:?}", cookie);
}
// Check existence
println!("Has authorization: {}", headers.contains_key("authorization"));
// Remove header
headers.remove("authorization");
// Iterate all headers
for (name, value) in &headers {
println!("{}: {:?}", name, value);
}
}use http::{header, HeaderMap};
fn main() {
let mut headers = HeaderMap::new();
// Use standard header names
headers.insert(header::CONTENT_TYPE, "text/html".parse().unwrap());
headers.insert(header::ACCEPT, "application/json".parse().unwrap());
headers.insert(header::USER_AGENT, "MyApp/1.0".parse().unwrap());
headers.insert(header::HOST, "example.com".parse().unwrap());
headers.insert(header::AUTHORIZATION, "Bearer token".parse().unwrap());
// Access using typed names
if let Some(ct) = headers.get(header::CONTENT_TYPE) {
println!("Content-Type: {:?}", ct);
}
// Common headers available in header module
let _headers: &[&str] = &[
"accept",
"accept-encoding",
"accept-language",
"cache-control",
"content-length",
"content-type",
"cookie",
"date",
"etag",
"host",
"if-modified-since",
"location",
"server",
"set-cookie",
"user-agent",
];
}use http::Uri;
fn main() {
let uri: Uri = "https://example.com:8080/api/users?page=1&limit=10#section".parse().unwrap();
// URI components
println!("Scheme: {:?}", uri.scheme());
println!("Authority: {:?}", uri.authority());
println!("Path: {:?}", uri.path());
println!("Query: {:?}", uri.query());
println!("Fragment: {:?}", uri.fragment());
// Build URI from parts
let uri = Uri::builder()
.scheme("https")
.authority("api.example.com")
.path_and_query("/v1/users?active=true")
.build()
.unwrap();
println!("Built URI: {}", uri);
}use http::uri::{Authority, PathAndQuery, Uri};
fn main() {
let uri: Uri = "https://user:pass@example.com:443/api/v1/users?limit=5".parse().unwrap();
if let Some(auth) = uri.authority() {
println!("Host: {:?}", auth.host());
println!("Port: {:?}", auth.port());
println!("Full authority: {}", auth);
}
if let Some(path) = uri.path_and_query() {
println!("Path: {:?}", path.path());
println!("Query: {:?}", path.query());
}
// Build authority
let authority: Authority = "api.example.com:8080".parse().unwrap();
println!("Authority: {}", authority);
// Build path and query
let pq: PathAndQuery = "/search?q=rust&sort=recent".parse().unwrap();
println!("Path+Query: {}", pq);
}use http::{Request, Method, HeaderMap};
fn main() {
// Method 1: Builder pattern
let request1 = Request::builder()
.method(Method::POST)
.uri("/api/users")
.header("Content-Type", "application/json")
.body(r#"{"name":"Alice"}"
#.to_string())
.unwrap();
// Method 2: Create from parts
let mut builder = Request::builder()
.method(Method::GET)
.uri("/api/users");
builder = builder.header("Accept", "application/json");
builder = builder.header("User-Agent", "MyApp");
let request2 = builder.body(()).unwrap();
// Method 3: Modify existing request
let mut request = Request::new("body");
*request.method_mut() = Method::PUT;
*request.uri_mut() = "/update".parse().unwrap();
request.headers_mut().insert("X-Custom", "value".parse().unwrap());
println!("Request: {} {}", request.method(), request.uri());
}use http::{Response, StatusCode, header};
fn main() {
// JSON response
let json_response = Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "application/json")
.body(r#"{"status":"ok"}"
#)
.unwrap();
// Error response
let error_response = Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(header::CONTENT_TYPE, "application/json")
.body(r#"{"error":"Invalid input"}"
#)
.unwrap();
// Redirect response
let redirect = Response::builder()
.status(StatusCode::FOUND)
.header(header::LOCATION, "/new-location")
.body(())
.unwrap();
// Create response and modify
let mut response = Response::new("body");
*response.status_mut() = StatusCode::CREATED;
response.headers_mut().insert(
header::CONTENT_TYPE,
"text/plain".parse().unwrap(),
);
}use http::{Request, Response, Extensions};
use std::any::Any;
#[derive(Debug, Clone)]
struct RequestContext {
request_id: String,
user_id: Option<String>,
}
fn main() {
let mut request = Request::new(());
// Add typed extension
request.extensions_mut().insert(RequestContext {
request_id: "req-123".to_string(),
user_id: Some("user-456".to_string()),
});
// Retrieve extension
if let Some(ctx) = request.extensions().get::<RequestContext>() {
println!("Request ID: {}", ctx.request_id);
}
// Extensions are type-indexed
request.extensions_mut().insert(42i32);
request.extensions_mut().insert("hello".to_string());
println!("Int: {:?}", request.extensions().get::<i32>());
println!("String: {:?}", request.extensions().get::<String>());
}use http::{Version, Request};
fn main() {
let mut request = Request::new(());
// Set HTTP version
*request.version_mut() = Version::HTTP_11;
println!("Version: {:?}", request.version());
// Available versions
println!("HTTP/0.9: {:?}", Version::HTTP_09);
println!("HTTP/1.0: {:?}", Version::HTTP_10);
println!("HTTP/1.1: {:?}", Version::HTTP_11);
println!("HTTP/2.0: {:?}", Version::HTTP_2);
println!("HTTP/3.0: {:?}", Version::HTTP_3);
}use http::{Request, Response, StatusCode};
fn handle_request(req: Request<String>) -> Response<String> {
println!("Handling {} {}", req.method(), req.uri());
Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "application/json")
.body(format!("{{\"path\": \"{}\"}}", req.uri().path()))
.unwrap()
}
fn main() {
let request = Request::builder()
.method("GET")
.uri("/api/users")
.body(String::new())
.unwrap();
let response = handle_request(request);
println!("Response: {} - {}", response.status(), response.body());
}use http::{Request, Method, StatusCode};
fn main() {
// Invalid method
let result = "INVALID METHOD".parse::<Method>();
match result {
Ok(method) => println!("Method: {}", method),
Err(e) => println!("Invalid method: {}", e),
}
// Invalid status code
let result = StatusCode::from_u16(999);
match result {
Ok(status) => println!("Status: {}", status),
Err(e) => println!("Invalid status: {}", e),
}
// Invalid URI
let result = "not a valid uri".parse::<http::Uri>();
match result {
Ok(uri) => println!("URI: {}", uri),
Err(e) => println!("Invalid URI: {}", e),
}
}use http::{Request, Response};
// Different body types
fn process_requests() {
// Empty body
let empty: Request<()> = Request::builder()
.uri("/health")
.body(())
.unwrap();
// String body
let string: Request<String> = Request::builder()
.uri("/api")
.body("text body".to_string())
.unwrap();
// Bytes body
let bytes: Request<Vec<u8>> = Request::builder()
.uri("/upload")
.body(vec![0, 1, 2, 3, 4])
.unwrap();
// Custom body type
#[derive(Debug)]
struct JsonBody(serde_json::Value);
let json = Request::builder()
.uri("/api/json")
.body(JsonBody(serde_json::json!({"key": "value"})))
.unwrap();
}
fn main() {
process_requests();
}use http::{Request, Response, header};
fn logging_middleware<T>(request: &Request<T>) {
println!("[{}] {}", request.method(), request.uri());
for (name, value) in request.headers() {
println!(" {}: {:?}", name, value);
}
}
fn auth_middleware<T>(request: &Request<T>) -> Result<(), String> {
match request.headers().get(header::AUTHORIZATION) {
Some(auth) => {
println!("Auth header present: {:?}", auth);
Ok(())
}
None => Err("Missing authorization header".to_string()),
}
}
fn add_response_headers<B>(response: &mut Response<B>) {
response.headers_mut().insert(
"X-Request-Id",
"req-123".parse().unwrap(),
);
response.headers_mut().insert(
"X-Response-Time",
"42ms".parse().unwrap(),
);
}
fn main() {
let request = Request::builder()
.method("GET")
.uri("/api/users")
.header("Authorization", "Bearer token")
.body(())
.unwrap();
logging_middleware(&request);
if auth_middleware(&request).is_ok() {
let mut response = Response::new("OK");
add_response_headers(&mut response);
println!("Response headers: {:?}", response.headers());
}
}use http::{Request, Response, Method, StatusCode, header};
use std::collections::HashMap;
struct ApiClient {
base_url: String,
default_headers: HashMap<String, String>,
}
impl ApiClient {
fn new(base_url: &str) -> Self {
Self {
base_url: base_url.to_string(),
default_headers: HashMap::new(),
}
}
fn with_header(mut self, key: &str, value: &str) -> Self {
self.default_headers.insert(key.to_string(), value.to_string());
self
}
fn build_request(&self, method: Method, path: &str, body: Option<&str>) -> Request<String> {
let uri = format!("{}{}", self.base_url, path);
let mut builder = Request::builder()
.method(method)
.uri(&uri);
// Add default headers
for (key, value) in &self.default_headers {
builder = builder.header(key, value);
}
builder.body(body.unwrap_or("").to_string()).unwrap()
}
fn get(&self, path: &str) -> Request<String> {
self.build_request(Method::GET, path, None)
}
fn post(&self, path: &str, body: &str) -> Request<String> {
self.build_request(Method::POST, path, Some(body))
}
}
fn main() {
let client = ApiClient::new("https://api.example.com")
.with_header("Accept", "application/json")
.with_header("User-Agent", "MyApp/1.0");
let get_req = client.get("/users");
println!("GET {}", get_req.uri());
let post_req = client.post("/users", r#"{"name":"Alice"}"
#);
println!("POST {} with body: {}", post_req.uri(), post_req.body());
}use http::{Request, Response, Method, StatusCode};
type Handler = fn(&Request<String>) -> Response<String>;
struct Router {
routes: Vec<(Method, String, Handler)>,
}
impl Router {
fn new() -> Self {
Self { routes: Vec::new() }
}
fn route(mut self, method: Method, path: &str, handler: Handler) -> Self {
self.routes.push((method, path.to_string(), handler));
self
}
fn handle(&self, request: &Request<String>) -> Response<String> {
for (method, path, handler) in &self.routes {
if request.method() == method && request.uri().path() == path {
return handler(request);
}
}
Response::builder()
.status(StatusCode::NOT_FOUND)
.body("Not Found".to_string())
.unwrap()
}
}
fn get_users(_req: &Request<String>) -> Response<String> {
Response::builder()
.status(StatusCode::OK)
.body(r#"[{"id":1,"name":"Alice"}]"#.to_string())
.unwrap()
}
fn create_user(req: &Request<String>) -> Response<String> {
println!("Creating user with: {}", req.body());
Response::builder()
.status(StatusCode::CREATED)
.body(r#"{"id":2,"name":"Bob"}"
#.to_string())
.unwrap()
}
fn main() {
let router = Router::new()
.route(Method::GET, "/users", get_users)
.route(Method::POST, "/users", create_user);
let request = Request::builder()
.method(Method::GET)
.uri("/users")
.body(String::new())
.unwrap();
let response = router.handle(&request);
println!("Response: {} - {}", response.status(), response.body());
}Request<T> and Response<T> are generic over body type TRequest::builder() and Response::builder() for fluent constructionMethod provides type-safe HTTP methods with GET, POST, etc.StatusCode offers all HTTP status codes with semantic methodsHeaderMap stores headers with efficient lookup and multiple valuesheader module for standard header names like CONTENT_TYPEUri parses URIs into components: scheme, authority, path, queryExtensions allows attaching typed data to requests/responsesinto_parts() and from_parts() decompose and reconstruct messages