http_extensions/lib.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/http_extensions/logo.png")]
7#![doc(
8 html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/http_extensions/favicon.ico"
9)]
10
11//! Shared HTTP types and extension traits for clients and servers.
12//!
13//! This crate provides common HTTP functionality built on the popular [`http`] crate,
14//! including flexible body handling, unified error types, and ergonomic extension traits
15//! for working with HTTP requests and responses.
16//!
17//! # Core Types
18//!
19//! - [`HttpRequest`] and [`HttpResponse`] - Type aliases for requests and responses with [`HttpBody`]
20//! - [`HttpRequestBuilder`] - Builder for constructing HTTP requests with a fluent API
21//! - [`HttpResponseBuilder`] - Builder for constructing HTTP responses with a fluent API
22//! - [`HttpBody`] - Flexible body type supporting text, binary, JSON, and streaming content
23//! - [`HttpBodyBuilder`] - Builder for creating HTTP bodies with memory pool optimization
24//! - [`HttpError`] - Unified error type with automatic backtraces and recovery classification
25//! - [`RequestHandler`] - Trait for HTTP middleware and request processing pipelines
26//!
27//! # Extension Traits
28//!
29//! The crate provides extension traits that add convenience methods to standard HTTP types:
30//!
31//! - [`StatusExt`] - Status code validation and recovery classification
32//! - [`RequestExt`] - Extensions for HTTP requests
33//! - [`ResponseExt`] - Response recovery classification with `Retry-After` support
34//! - [`HttpRequestExt`] - Request cloning with body support
35//! - [`HeaderMapExt`] - Header value extraction and parsing
36//! - [`HeaderValueExt`] - Construction of [`HeaderValue`][http::HeaderValue] from [`Bytes`][bytes::Bytes]
37//! - [`ExtensionsExt`] - Extensions for [`Extensions`][http::Extensions] to extract URI template labels
38//!
39//! # Quick Start
40//!
41//! Here's a complete example showing how to create an HTTP client, build a request,
42//! and validate the response:
43//!
44//! ```rust
45//! # #[cfg(not(feature = "test-util"))] fn main() {}
46//! # #[cfg(feature = "test-util")]
47//! # use http_extensions::{
48//! # HttpRequestBuilder, HttpResponseBuilder, HttpBodyBuilder, HttpRequestBuilderExt,
49//! # FakeHandler, StatusExt, Result,
50//! # };
51//! # #[cfg(feature = "test-util")]
52//! # #[tokio::main]
53//! # async fn main() -> Result<()> {
54//! // Create a body builder for constructing request/response bodies
55//! let body_builder = HttpBodyBuilder::new_fake();
56//!
57//! // Create a fake handler that returns a successful response
58//! // (This uses the `test-util` feature for testing; similar workflow applies to real clients)
59//! let handler = FakeHandler::from(
60//! HttpResponseBuilder::new(&body_builder)
61//! .status(200)
62//! .header("Content-Type", "application/json")
63//! .text(r#"{"message": "Success"}"#)
64//! .build()?,
65//! );
66//!
67//! // Build and send an HTTP request using the handler
68//! let response = handler
69//! .request_builder()
70//! .get("https://api.example.com/data")
71//! .header("Authorization", "Bearer token")
72//! .fetch()
73//! .await?;
74//!
75//! // Validate that the response succeeded (returns error for `4xx/5xx` status codes)
76//! let validated_response = response.ensure_success()?;
77//!
78//! println!("response status: {}", validated_response.status());
79//! # Ok(())
80//! # }
81//! ```
82//!
83//! **Note**: This example uses the `test-util` feature to create a `FakeHandler` for testing.
84//! In production code, you would use a real HTTP client that implements the
85//! [`RequestHandler`] trait, but the workflow remains the same: build requests with
86//! [`HttpRequestBuilder`], send them through a handler, and validate responses with
87//! [`StatusExt::ensure_success`].
88//!
89//! # Integration with the HTTP Ecosystem
90//!
91//! This crate builds on the popular [`http`] crate rather than inventing new types:
92//!
93//! - Uses [`http::Request`] and [`http::Response`] as base types
94//! - Reuses [`http::Method`], [`http::StatusCode`], and [`http::HeaderMap`]
95//! - Implements standard traits like [`http_body::Body`] for ecosystem compatibility
96//! - Works seamlessly with other Rust HTTP libraries
97//!
98//! # Examples
99//!
100//! ## Validating Response Status
101//! ```rust
102//! # fn main() {
103//! # #[cfg(feature = "test-util")] {
104//! # (|| {
105//! # use http_extensions::{StatusExt, HttpResponse, HttpResponseBuilder, HttpError};
106//! # let response: HttpResponse = HttpResponseBuilder::new_fake().status(200).build().unwrap();
107//! // Check if the response succeeded and return an error if not
108//! let validated_response = response.ensure_success()?;
109//! # Ok::<(), HttpError>(())
110//! # })().unwrap();
111//! # }
112//! # }
113//! ```
114//!
115//! ## Creating Request Bodies
116//! ```rust
117//! # fn main() {
118//! # #[cfg(feature = "test-util")] {
119//! # use http_extensions::HttpBodyBuilder;
120//! # let builder = HttpBodyBuilder::new_fake();
121//! // Create different body types
122//! let text_body = builder.text("Hello, world!");
123//! let binary_body = builder.slice(&[1, 2, 3, 4]);
124//! let empty_body = builder.empty();
125//! # }
126//! # }
127//! ```
128//!
129//! ## Building HTTP Requests
130//! ```rust
131//! # fn main() {
132//! # #[cfg(feature = "test-util")] {
133//! # use http_extensions::{HttpRequestBuilder, HttpBodyBuilder};
134//! # let body_builder = HttpBodyBuilder::new_fake();
135//! let request = HttpRequestBuilder::new(&body_builder)
136//! .get("https://api.example.com/data")
137//! .text("Hello World")
138//! .build()
139//! .unwrap();
140//! # }
141//! # }
142//! ```
143//!
144//! ## Building HTTP Responses
145//! ```rust
146//! # fn main() {
147//! # #[cfg(feature = "test-util")] {
148//! # use http_extensions::{HttpResponseBuilder, HttpBodyBuilder};
149//! # let body_builder = HttpBodyBuilder::new_fake();
150//! let response = HttpResponseBuilder::new(&body_builder)
151//! .status(200)
152//! .header("Content-Type", "text/plain")
153//! .body(body_builder.text("Success"))
154//! .build()
155//! .unwrap();
156//! # }
157//! # }
158//! ```
159//!
160//! ## Building Middleware with `RequestHandler`
161//! ```rust
162//! # use http_extensions::{HttpRequest, HttpResponse, RequestHandler, Result};
163//! # use layered::Service;
164//! struct LoggingMiddleware<S> {
165//! inner: S,
166//! }
167//!
168//! impl<S: RequestHandler> Service<HttpRequest> for LoggingMiddleware<S> {
169//! type Out = Result<HttpResponse>;
170//!
171//! async fn execute(&self, request: HttpRequest) -> Self::Out {
172//! println!("Processing request to: {}", request.uri());
173//! let response = self.inner.execute(request).await?;
174//! println!("Response status: {}", response.status());
175//! Ok(response)
176//! }
177//! }
178//! ```
179//!
180//! ## Testing with `FakeHandler`
181//!
182//! The `FakeHandler` type (available with the `test-util` feature) lets you mock HTTP responses
183//! for testing without making actual network requests. This is useful for unit testing code
184//! that depends on HTTP clients.
185//!
186//! # Features
187//!
188//! - `json` - Enables JSON serialization/deserialization support via `Json` type
189//! - `test-util` - Enables fake implementations for testing
190//!
191//! # Memory Management
192//!
193//! Bodies created through [`HttpBodyBuilder`] use memory pools from [`bytesbuf`] to
194//! reduce allocation overhead. When body data is consumed, memory is automatically recycled
195//! for future requests. This makes the crate particularly efficient for high-throughput scenarios.
196
197use http::{Request, Response};
198
199/// Specialized HTTP request that uses the [`HttpBody`] type for the body.
200///
201/// This is a type alias for [`Request<HttpBody>`].
202pub type HttpRequest = Request<HttpBody>;
203
204/// Specialized HTTP response that uses the [`HttpBody`] type for the body.
205///
206/// This is a type alias for [`Response<HttpBody>`].
207pub type HttpResponse = Response<HttpBody>;
208
209mod error;
210pub use error::{HttpError, Result};
211
212mod error_labels;
213
214mod body;
215pub use body::{HttpBody, HttpBodyBuilder, HttpBodyOptions};
216
217#[cfg(any(feature = "json", test))]
218mod json;
219#[doc(inline)]
220#[cfg(any(feature = "json", test))]
221pub use json::{Json, JsonError};
222
223mod constants;
224
225mod request_handler;
226pub use request_handler::RequestHandler;
227
228mod http_request_builder_ext;
229pub use http_request_builder_ext::HttpRequestBuilderExt;
230
231mod extensions;
232pub use extensions::{ExtensionsExt, HeaderMapExt, HeaderValueExt, HttpRequestExt, RequestExt, ResponseExt, StatusExt};
233
234mod uri_template_label;
235pub use uri_template_label::UriTemplateLabel;
236
237pub mod timeout;
238
239pub mod routing;
240
241mod http_response_builder;
242pub use http_response_builder::HttpResponseBuilder;
243
244mod http_request_builder;
245pub(crate) mod http_utils;
246
247pub use http_request_builder::HttpRequestBuilder;
248
249#[cfg(any(feature = "test-util", test))]
250mod fake_handler;
251#[cfg(any(feature = "test-util", test))]
252pub use fake_handler::FakeHandler;
253
254#[cfg(test)]
255#[cfg_attr(coverage_nightly, coverage(off))]
256pub(crate) mod testing;
257
258pub mod _documentation;