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}