http_kit/endpoint.rs
1//! HTTP endpoint abstraction for request handling.
2//!
3//! This module provides the core [`Endpoint`] trait and supporting types for building
4//! HTTP request handlers. Endpoints represent the final destination for HTTP requests
5//! and are responsible for generating appropriate responses.
6//!
7//! # Core Concepts
8//!
9//! - **Endpoint**: A trait for types that can handle HTTP requests and produce responses
10//! - **Middleware Integration**: Endpoints can be combined with middleware for cross-cutting concerns
11//! - **Type Erasure**: Support for dynamic dispatch through [`AnyEndpoint`]
12//! - **Composition**: Endpoints can be wrapped and combined in various ways
13//!
14//! # Examples
15//!
16//! ## Basic Endpoint Implementation
17//!
18//! ```rust
19//! use http_kit::{Request, Response, Endpoint, Body};
20//! use core::convert::Infallible;
21//!
22//! struct HelloEndpoint;
23//!
24//! impl Endpoint for HelloEndpoint {
25//! type Error = Infallible;
26//! async fn respond(&mut self, _request: &mut Request) -> Result<Response, Self::Error> {
27//! Ok(Response::new(Body::from_bytes("Hello, World!")))
28//! }
29//! }
30//! ```
31//!
32//! ## Endpoint with Request Processing
33//!
34//! ```rust
35//! use http_kit::{Request, Response, Endpoint, Body, BoxHttpError};
36//!
37//! struct EchoEndpoint;
38//!
39//! impl Endpoint for EchoEndpoint {
40//! type Error = BoxHttpError;
41//! async fn respond(&mut self, request: &mut Request) -> Result<Response,Self::Error> {
42//! let body = std::mem::replace(request.body_mut(), Body::empty());
43//! Ok(Response::new(body))
44//! }
45//! }
46//! ```
47//!
48//! ## Using with Middleware
49//!
50//! ```rust
51//! use http_kit::{Request, Response, Endpoint, Middleware, endpoint::WithMiddleware, Body, BoxHttpError};
52//! use http_kit::middleware::MiddlewareError;
53//!
54//! struct LoggingMiddleware;
55//!
56//! impl Middleware for LoggingMiddleware {
57//! type Error = BoxHttpError;
58//! async fn handle<E: Endpoint>(&mut self, request: &mut Request, mut next: E) -> Result<Response, MiddlewareError<E::Error, Self::Error>> {
59//! println!("Processing request to {}", request.uri());
60//! next.respond(request).await.map_err(MiddlewareError::Endpoint)
61//! }
62//! }
63//!
64//! struct MyEndpoint;
65//! impl Endpoint for MyEndpoint {
66//! type Error = BoxHttpError;
67//! async fn respond(&mut self, _request: &mut Request) -> Result<Response,Self::Error> {
68//! Ok(Response::new(Body::from_bytes("OK")))
69//! }
70//! }
71//!
72//! let endpoint_with_logging = WithMiddleware::new(MyEndpoint, LoggingMiddleware);
73//! ```
74
75use core::{any::type_name, fmt::Debug, future::Future, ops::DerefMut, pin::Pin};
76
77use alloc::boxed::Box;
78
79use crate::{
80 error::BoxHttpError, middleware::MiddlewareError, HttpError, Middleware, Request, Response,
81};
82
83/// A trait for types that can handle HTTP requests and generate responses.
84///
85/// Endpoints represent the final destination in the HTTP request processing pipeline.
86/// They receive a mutable reference to the request (allowing them to consume the body
87/// or modify headers) and return a response or error.
88///
89/// # Implementation Notes
90///
91/// - Endpoints must be `Send` to work in async contexts
92/// - The request parameter is mutable, allowing body consumption and header modification
93/// - Implementations should handle errors gracefully and return appropriate HTTP status codes
94/// - Endpoints can be combined with middleware for additional functionality
95///
96/// # Examples
97///
98/// ## Simple Text Response
99///
100/// ```rust
101/// use http_kit::{Request, Response, Endpoint, Body, BoxHttpError};
102///
103/// struct GreetingEndpoint {
104/// name: String,
105/// }
106///
107/// impl Endpoint for GreetingEndpoint {
108/// type Error = BoxHttpError;
109/// async fn respond(&mut self, _request: &mut Request) -> Result<Response, Self::Error> {
110/// let message = format!("Hello, {}!", self.name);
111/// Ok(Response::new(Body::from_bytes(message)))
112/// }
113/// }
114/// ```
115///
116/// ## JSON API Endpoint
117///
118/// ```rust
119/// # #[cfg(feature = "json")]
120/// # {
121/// use http::StatusCode;
122/// use http_kit::{Request, Response, Result, Endpoint, Body, HttpError, BodyError};
123/// use serde::{Serialize, Deserialize};
124/// use thiserror::Error;
125///
126/// #[derive(Debug, Error)]
127/// enum ApiError {
128/// #[error("json error: {0}")]
129/// Json(#[from] serde_json::Error),
130/// #[error("body error: {0}")]
131/// Body(#[from] BodyError),
132/// }
133///
134/// impl HttpError for ApiError {
135/// fn status(&self) -> StatusCode {
136/// match self {
137/// Self::Json(_) => StatusCode::BAD_REQUEST,
138/// Self::Body(_) => StatusCode::INTERNAL_SERVER_ERROR,
139/// }
140/// }
141/// }
142///
143/// #[derive(Serialize, Deserialize)]
144/// struct User { name: String, age: u32 }
145///
146/// struct UserEndpoint;
147///
148/// impl Endpoint for UserEndpoint {
149/// type Error = ApiError;
150/// async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
151/// match request.method().as_str() {
152/// "GET" => {
153/// let user = User { name: "Alice".into(), age: 30 };
154/// let body = Body::from_json(&user)?;
155/// Ok(Response::new(body))
156/// }
157/// "POST" => {
158/// let user: User = request
159/// .body_mut()
160/// .into_json()
161/// .await?;
162/// // Process user...
163/// let body = Body::from_json(&user)?;
164/// Ok(Response::new(body))
165/// }
166/// _ => Ok(Response::new(Body::from_bytes("Method Not Allowed")))
167/// }
168/// }
169/// }
170/// # }
171/// ```
172pub trait Endpoint: Send {
173 /// The error type returned by this endpoint.
174 type Error: HttpError;
175 /// Processes an HTTP request and generates a response.
176 ///
177 /// This method receives a mutable reference to the request, allowing it to:
178 /// - Consume the request body with `take_body()` or similar methods
179 /// - Read headers, URI, method, and other request metadata
180 /// - Modify request state if needed (though this is less common)
181 ///
182 /// The method should return either a successful `Response` or an `Error`
183 /// with an appropriate HTTP status code.
184 ///
185 /// # Arguments
186 ///
187 /// * `request` - Mutable reference to the HTTP request being processed
188 ///
189 /// # Examples
190 ///
191 /// ```rust
192 /// use http_kit::{Request, Response, Endpoint, Body, BoxHttpError};
193 ///
194 /// struct StatusEndpoint;
195 ///
196 /// impl Endpoint for StatusEndpoint {
197 /// type Error = BoxHttpError;
198 /// async fn respond(&mut self, request: &mut Request) -> Result<Response,Self::Error> {
199 /// let status = format!("Method: {}, URI: {}", request.method(), request.uri());
200 /// Ok(Response::new(Body::from_bytes(status)))
201 /// }
202 /// }
203 /// ```
204 fn respond(
205 &mut self,
206 request: &mut Request,
207 ) -> impl Future<Output = Result<Response, Self::Error>> + Send;
208}
209
210impl<E: Endpoint> Endpoint for &mut E {
211 type Error = E::Error;
212 async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
213 Endpoint::respond(*self, request).await
214 }
215}
216
217impl<E: Endpoint> Endpoint for Box<E> {
218 type Error = E::Error;
219 async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
220 Endpoint::respond(self.deref_mut(), request).await
221 }
222}
223
224/// A wrapper that combines an endpoint with middleware.
225///
226/// `WithMiddleware` allows you to compose an endpoint with middleware to add
227/// cross-cutting concerns like logging, authentication, rate limiting, etc.
228/// The middleware is executed first and can decide whether to call the endpoint
229/// and how to process the response.
230///
231/// # Type Parameters
232///
233/// * `E` - The endpoint type that implements `Endpoint`
234/// * `M` - The middleware type that implements `Middleware`
235#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
236pub struct WithMiddleware<E: Endpoint, M: Middleware> {
237 endpoint: E,
238 middleware: M,
239}
240
241impl<E: Endpoint, M: Middleware> WithMiddleware<E, M> {
242 /// Creates a new endpoint that wraps the given endpoint with middleware.
243 ///
244 /// When the resulting endpoint handles a request, the middleware will be
245 /// executed first. The middleware can then decide whether to call the
246 /// wrapped endpoint and how to process its response.
247 ///
248 /// # Arguments
249 ///
250 /// * `endpoint` - The endpoint to wrap
251 /// * `middleware` - The middleware to apply
252 pub fn new(endpoint: E, middleware: M) -> Self {
253 Self {
254 endpoint,
255 middleware,
256 }
257 }
258}
259
260impl<E: Endpoint, M: Middleware> Endpoint for WithMiddleware<E, M> {
261 type Error = MiddlewareError<E::Error, M::Error>;
262 async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
263 self.middleware.handle(request, &mut self.endpoint).await
264 }
265}
266
267pub(crate) trait EndpointImpl: Send {
268 fn respond_inner<'this, 'req, 'fut>(
269 &'this mut self,
270 request: &'req mut Request,
271 ) -> Pin<Box<dyn 'fut + Send + Future<Output = Result<Response, BoxHttpError>>>>
272 where
273 'this: 'fut,
274 'req: 'fut;
275 fn name(&self) -> &'static str {
276 type_name::<Self>()
277 }
278}
279
280/// Type-erased endpoint that can hold any endpoint implementation behind a trait object.
281///
282/// `AnyEndpoint` provides dynamic dispatch for endpoints, allowing you to store
283/// different endpoint types in the same collection or pass them around without
284/// knowing their concrete types at compile time. This is useful for building
285/// flexible routing systems or plugin architectures.
286///
287/// # Performance Notes
288///
289/// Using `AnyEndpoint` involves dynamic dispatch and heap allocation, which has
290/// a small performance overhead compared to using concrete types directly.
291/// However, this is often negligible in HTTP server contexts.
292///
293/// # Examples
294///
295/// ```rust
296/// use http_kit::{Request, Response, Endpoint, endpoint::AnyEndpoint, Body, BoxHttpError};
297///
298/// struct HelloEndpoint;
299/// impl Endpoint for HelloEndpoint {
300/// type Error = BoxHttpError;
301/// async fn respond(&mut self, _request: &mut Request) -> Result<Response, Self::Error> {
302/// Ok(Response::new(Body::from_bytes("Hello")))
303/// }
304/// }
305///
306/// struct GoodbyeEndpoint;
307/// impl Endpoint for GoodbyeEndpoint {
308/// type Error = BoxHttpError;
309/// async fn respond(&mut self, _request: &mut Request) -> Result<Response,Self::Error> {
310/// Ok(Response::new(Body::from_bytes("Goodbye")))
311/// }
312/// }
313///
314/// // Store different endpoint types in a collection
315/// let endpoints: Vec<AnyEndpoint> = vec![
316/// AnyEndpoint::new(HelloEndpoint),
317/// AnyEndpoint::new(GoodbyeEndpoint),
318/// ];
319/// ```
320pub struct AnyEndpoint(Box<dyn EndpointImpl>);
321
322impl Debug for AnyEndpoint {
323 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
324 f.write_fmt(format_args!("AnyEndpoint[{}]", self.name()))
325 }
326}
327
328impl AnyEndpoint {
329 /// Creates a new type-erased endpoint wrapper around the given endpoint implementation.
330 ///
331 /// This method takes any type that implements `Endpoint` and wraps it in a
332 /// `AnyEndpoint` that can be stored alongside other endpoints of different types.
333 ///
334 /// # Arguments
335 ///
336 /// * `endpoint` - Any endpoint implementation
337 ///
338 /// # Examples
339 ///
340 /// ```rust
341 /// use http_kit::{Request, Response, Endpoint, endpoint::AnyEndpoint, Body, BoxHttpError};
342 ///
343 /// struct MyEndpoint {
344 /// message: String,
345 /// }
346 ///
347 /// impl Endpoint for MyEndpoint {
348 /// type Error = BoxHttpError;
349 /// async fn respond(&mut self, _request: &mut Request) -> Result<Response,Self::Error> {
350 /// Ok(Response::new(Body::from_bytes(self.message.clone())))
351 /// }
352 /// }
353 ///
354 /// let endpoint = MyEndpoint { message: "Hello!".to_string() };
355 /// let any_endpoint = AnyEndpoint::new(endpoint);
356 /// ```
357 pub fn new(endpoint: impl Endpoint + 'static) -> Self {
358 Self(Box::new(endpoint))
359 }
360
361 /// Returns the type name of the underlying endpoint implementation.
362 ///
363 /// This can be useful for debugging, logging, or introspection purposes.
364 ///
365 /// # Examples
366 ///
367 /// ```rust
368 /// use http_kit::{Request, Response, Endpoint, endpoint::AnyEndpoint, Body, BoxHttpError};
369 ///
370 /// struct MyEndpoint;
371 /// impl Endpoint for MyEndpoint {
372 /// type Error = BoxHttpError;
373 /// async fn respond(&mut self, _request: &mut Request) -> Result<Response,BoxHttpError> {
374 /// Ok(Response::new(Body::from_bytes("OK")))
375 /// }
376 /// }
377 ///
378 /// let any_endpoint = AnyEndpoint::new(MyEndpoint);
379 /// println!("Endpoint type: {}", any_endpoint.name());
380 /// ```
381 pub fn name(&self) -> &'static str {
382 self.0.name()
383 }
384}
385
386impl<E: Endpoint> EndpointImpl for E {
387 fn respond_inner<'this, 'req, 'fut>(
388 &'this mut self,
389 request: &'req mut Request,
390 ) -> Pin<Box<dyn 'fut + Send + Future<Output = Result<Response, BoxHttpError>>>>
391 where
392 'this: 'fut,
393 'req: 'fut,
394 {
395 Box::pin(async move {
396 Endpoint::respond(self, request)
397 .await
398 .map_err(|e| Box::new(e) as BoxHttpError)
399 })
400 }
401}
402
403impl Endpoint for AnyEndpoint {
404 type Error = BoxHttpError;
405 /// Processes an HTTP request using the underlying endpoint implementation.
406 async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
407 self.0.respond_inner(request).await
408 }
409}