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