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}