reinhardt-http

Crates.ioreinhardt-http
lib.rsreinhardt-http
version0.1.0-alpha.1
created_at2026-01-23 04:34:44.961879+00
updated_at2026-01-23 04:34:44.961879+00
descriptionHTTP primitives, request and response handling for Reinhardt
homepage
repositoryhttps://github.com/kent8192/reinhardt-rs
max_upload_size
id2063428
size247,454
kento (kent8192)

documentation

README

reinhardt-http

HTTP request and response handling for the Reinhardt framework

Overview

Core HTTP abstractions for the Reinhardt framework. Provides comprehensive request and response types, header handling, cookie management, content negotiation, and streaming support with a Django/DRF-inspired API design.

Features

Implemented ✓

Request Type

  • Complete HTTP request representation with all standard components
    • HTTP method, URI, version, headers, body
    • Path parameters (path_params) and query string parsing (query_params)
    • HTTPS detection (is_secure)
    • Remote address tracking (remote_addr)
    • Type-safe extensions system (Extensions)
  • Builder pattern for fluent request construction
    • Request::builder() - Start building
    • .method() - Set HTTP method
    • .uri() - Set URI (with automatic query parsing)
    • .version() - Set HTTP version (defaults to HTTP/1.1)
    • .headers() - Set headers
    • .header() - Set single header
    • .body() - Set request body
    • .secure() - Set HTTPS flag
    • .remote_addr() - Set remote address
    • .build() - Finalize construction
  • Request parsing (with parsers feature)
    • JSON body parsing
    • Form data parsing
    • Multipart form data
    • Lazy parsing (parse on first access)

Response Type

  • Flexible HTTP response creation with status code helpers
    • Response::ok() - 200 OK
    • Response::created() - 201 Created
    • Response::no_content() - 204 No Content
    • Response::bad_request() - 400 Bad Request
    • Response::unauthorized() - 401 Unauthorized
    • Response::forbidden() - 403 Forbidden
    • Response::not_found() - 404 Not Found
    • Response::gone() - 410 Gone
    • Response::internal_server_error() - 500 Internal Server Error
  • Redirect responses
    • Response::permanent_redirect(url) - 301 Moved Permanently
    • Response::temporary_redirect(url) - 302 Found
    • Response::temporary_redirect_preserve_method(url) - 307 Temporary Redirect
  • Builder pattern methods
    • .with_body(data) - Set response body (bytes or string)
    • .with_header(name, value) - Add single header
    • .with_typed_header(header) - Add typed header
    • .with_json(data) - Serialize data to JSON and set Content-Type
    • .with_location(url) - Set Location header (for redirects)
    • .with_stop_chain(bool) - Control middleware chain execution
  • JSON serialization support with automatic Content-Type
  • Middleware chain control via stop_chain flag

StreamingResponse

  • Streaming response support for large data or real-time content
    • Custom media type configuration
    • Header support
    • Stream-based body (any type implementing Stream)

Extensions System

  • Type-safe request extensions for storing arbitrary typed data
    • request.extensions.insert::<T>(value) - Store typed data
    • request.extensions.get::<T>() - Retrieve typed data
    • Thread-safe with Arc<Mutex<TypeMap>>
    • Common use cases: authentication context, request ID, user data

Error Integration

  • Re-exports reinhardt_exception::Error and Result for consistent error handling

Installation

Add reinhardt to your Cargo.toml:

[dependencies]
reinhardt = "0.1.0-alpha.1"

# Or use a preset with parsers support:
# reinhardt = { version = "0.1.0-alpha.1", features = ["standard"] }  # Recommended
# reinhardt = { version = "0.1.0-alpha.1", features = ["full"] }      # All features

Note: HTTP types are available through the main reinhardt crate, which provides a unified interface to all framework components.

Usage Examples

Basic Request Construction

use reinhardt::http::Request;
use hyper::Method;
use bytes::Bytes;

// Using builder pattern
let request = Request::builder()
	.method(Method::POST)
	.uri("/api/users?page=1")
	.body(Bytes::from(r#"{"name": "Alice"}"#))
	.build()
	.unwrap();

assert_eq!(request.method, Method::POST);
assert_eq!(request.path(), "/api/users");
assert_eq!(request.query_params.get("page"), Some(&"1".to_string()));

Path and Query Parameters

use reinhardt::http::Request;
use hyper::Method;

let mut request = Request::builder()
	.method(Method::GET)
	.uri("/api/users/123?sort=name&order=asc")
	.build()
	.unwrap();

// Access query parameters
assert_eq!(request.query_params.get("sort"), Some(&"sort".to_string()));
assert_eq!(request.query_params.get("order"), Some(&"asc".to_string()));

// Add path parameters (typically done by router)
request.path_params.insert("id".to_string(), "123".to_string());
assert_eq!(request.path_params.get("id"), Some(&"123".to_string()));

Request Extensions

use reinhardt::http::Request;
use hyper::Method;

#[derive(Clone)]
struct UserId(i64);

let mut request = Request::builder()
	.method(Method::GET)
	.uri("/api/profile")
	.build()
	.unwrap();

// Store typed data in extensions
request.extensions.insert(UserId(42));

// Retrieve typed data
let user_id = request.extensions.get::<UserId>().unwrap();
assert_eq!(user_id.0, 42);

Response Helpers

use reinhardt::http::Response;

// Success responses
let response = Response::ok()
    .with_body("Success");
assert_eq!(response.status, hyper::StatusCode::OK);

let response = Response::created()
    .with_json(&serde_json::json!({
        "id": 123,
        "name": "Alice"
    }))
    .unwrap();
assert_eq!(response.status, hyper::StatusCode::CREATED);
assert_eq!(
    response.headers.get("content-type").unwrap(),
    "application/json"
);

// Error responses
let response = Response::bad_request()
    .with_body("Invalid input");
assert_eq!(response.status, hyper::StatusCode::BAD_REQUEST);

let response = Response::not_found()
    .with_body("Resource not found");
assert_eq!(response.status, hyper::StatusCode::NOT_FOUND);

Redirect Responses

use reinhardt::http::Response;

// Permanent redirect (301)
let response = Response::permanent_redirect("/new-location");
assert_eq!(response.status, hyper::StatusCode::MOVED_PERMANENTLY);
assert_eq!(
	response.headers.get("location").unwrap().to_str().unwrap(),
	"/new-location"
);

// Temporary redirect (302)
let response = Response::temporary_redirect("/login");
assert_eq!(response.status, hyper::StatusCode::FOUND);

// Temporary redirect preserving method (307)
let response = Response::temporary_redirect_preserve_method("/users/123");
assert_eq!(response.status, hyper::StatusCode::TEMPORARY_REDIRECT);

JSON Response

use reinhardt::http::Response;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct User {
    id: i64,
    name: String,
}

let user = User {
    id: 1,
    name: "Alice".to_string(),
};

let response = Response::ok()
    .with_json(&user)
    .unwrap();

// Automatically sets Content-Type: application/json
assert_eq!(
    response.headers.get("content-type").unwrap(),
    "application/json"
);

Middleware Chain Control

use reinhardt::http::Response;

// Stop middleware chain (useful for authentication, rate limiting)
let response = Response::unauthorized()
    .with_body("Authentication required")
    .with_stop_chain(true);

// This response will stop further middleware execution
assert!(response.should_stop_chain());

Streaming Response

use reinhardt::http::StreamingResponse;
use futures::stream::{self, StreamExt};
use bytes::Bytes;
use hyper::StatusCode;

let data = vec![
	Bytes::from("chunk1"),
	Bytes::from("chunk2"),
	Bytes::from("chunk3"),
];

let stream = stream::iter(data.into_iter().map(Ok));

// Create streaming response (default status: 200 OK)
let response = StreamingResponse::new(Box::pin(stream))
	.status(StatusCode::OK)
	.media_type("text/plain");

// Or use with_status for custom status code
let response = StreamingResponse::with_status(
	Box::pin(stream),
	StatusCode::OK,
)
.media_type("text/plain");

// Use for large files, server-sent events, etc.

API Reference

Request

Fields:

  • method: Method - HTTP method (GET, POST, etc.)
  • uri: Uri - Request URI
  • version: Version - HTTP version
  • headers: HeaderMap - HTTP headers
  • path_params: HashMap<String, String> - Path parameters from URL routing
  • query_params: HashMap<String, String> - Query string parameters
  • is_secure: bool - Whether request is over HTTPS
  • remote_addr: Option<SocketAddr> - Client's remote address
  • extensions: Extensions - Type-safe extension storage

Methods:

  • Request::builder() - Create builder
  • .path() - Get URI path without query
  • .body() - Get request body as Option<&Bytes>
  • .json::<T>() - Parse body as JSON (requires parsers feature)
  • .post() - Parse POST data (form/JSON, requires parsers feature)
  • .data() - Get parsed data from body
  • .set_di_context::<T>() - Set DI context for type T
  • .get_di_context::<T>() - Get DI context for type T
  • .decoded_query_params() - Get URL-decoded query parameters
  • .get_accepted_languages() - Parse Accept-Language header
  • .get_preferred_language() - Get user's preferred language
  • .is_secure() - Check if request is over HTTPS
  • .scheme() - Get URI scheme
  • .build_absolute_uri() - Build absolute URI from request

Response

Fields:

  • status: StatusCode - HTTP status code
  • headers: HeaderMap - HTTP headers
  • body: Bytes - Response body

Constructor Methods:

  • Response::new(status) - Create with status code
  • Response::ok() - 200 OK
  • Response::created() - 201 Created
  • Response::no_content() - 204 No Content
  • Response::bad_request() - 400 Bad Request
  • Response::unauthorized() - 401 Unauthorized
  • Response::forbidden() - 403 Forbidden
  • Response::not_found() - 404 Not Found
  • Response::gone() - 410 Gone
  • Response::internal_server_error() - 500 Internal Server Error
  • Response::permanent_redirect(url) - 301 Moved Permanently
  • Response::temporary_redirect(url) - 302 Found
  • Response::temporary_redirect_preserve_method(url) - 307 Temporary Redirect

Builder Methods:

  • .with_body(data) - Set body (bytes or string)
  • .with_header(name, value) - Add header
  • .with_typed_header(header) - Add typed header
  • .with_json(data) - Serialize to JSON
  • .with_location(url) - Set Location header
  • .with_stop_chain(bool) - Control middleware chain
  • .should_stop_chain() - Check if chain should stop

Extensions

Methods:

  • .insert::<T>(value) - Store typed value
  • .get::<T>() - Retrieve typed value (returns Option<T>)
  • .remove::<T>() - Remove typed value

Feature Flags

  • parsers - Enable request body parsing (JSON, form data, multipart)
    • Adds parse_json(), parse_form() methods to Request
    • Requires reinhardt-parsers crate

Dependencies

  • hyper - HTTP types (Method, Uri, StatusCode, HeaderMap, Version)
  • bytes - Efficient byte buffer handling
  • futures - Stream support for streaming responses
  • serde - Serialization support (with serde_json for JSON)
  • reinhardt-exception - Error handling
  • reinhardt-parsers - Request body parsing (optional, with parsers feature)

Testing

The crate includes comprehensive unit tests and doctests covering:

  • Request construction and builder pattern
  • Response helpers and status codes
  • Redirect responses
  • JSON serialization
  • Extensions system
  • Query parameter parsing
  • Middleware chain control

Run tests with:

cargo test
cargo test --features parsers  # With parsers support

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Commit count: 2845

cargo fmt