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, Result, Endpoint, Body, Error};
36//!
37//! struct EchoEndpoint;
38//!
39//! impl Endpoint for EchoEndpoint {
40//! type Error = Error;
41//! async fn respond(&mut self, request: &mut Request) -> Result<Response> {
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, Result, Endpoint, Middleware, endpoint::WithMiddleware, Body, Error};
52//! use http_kit::middleware::MiddlewareError;
53//!
54//! struct LoggingMiddleware;
55//!
56//! impl Middleware for LoggingMiddleware {
57//! type Error = Error;
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 = Error;
67//! async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
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, Result, Endpoint, Body, Error};
102///
103/// struct GreetingEndpoint {
104/// name: String,
105/// }
106///
107/// impl Endpoint for GreetingEndpoint {
108/// type Error = Error;
109/// async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
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) -> Option<StatusCode> {
136/// match self {
137/// Self::Json(_) => Some(StatusCode::BAD_REQUEST),
138/// Self::Body(_) => Some(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, Result, Endpoint, Body, Error};
193 ///
194 /// struct StatusEndpoint;
195 ///
196 /// impl Endpoint for StatusEndpoint {
197 /// type Error = Error;
198 /// async fn respond(&mut self, request: &mut Request) -> Result<Response> {
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///
236/// # Examples
237///
238/// ```rust
239/// use http_kit::{Request, Response, Result, Endpoint, Middleware, endpoint::WithMiddleware, Body, Error};
240/// use http_kit::middleware::MiddlewareError;
241///
242/// struct TimingMiddleware;
243/// impl Middleware for TimingMiddleware {
244/// type Error = Error;
245/// async fn handle<E: Endpoint>(&mut self, request: &mut Request, mut next: E) -> Result<Response, MiddlewareError<E::Error, Self::Error>> {
246/// let start = std::time::Instant::now();
247/// let response = next.respond(request).await;
248/// let duration = start.elapsed();
249/// println!("Request took {:?}", duration);
250/// response.map_err(MiddlewareError::Endpoint)
251/// }
252/// }
253///
254/// struct HelloEndpoint;
255/// impl Endpoint for HelloEndpoint {
256/// type Error = Error;
257/// async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
258/// Ok(Response::new(Body::from_bytes("Hello")))
259/// }
260/// }
261///
262/// let timed_endpoint = WithMiddleware::new(HelloEndpoint, TimingMiddleware);
263/// ```
264#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
265pub struct WithMiddleware<E: Endpoint, M: Middleware> {
266 endpoint: E,
267 middleware: M,
268}
269
270impl<E: Endpoint, M: Middleware> WithMiddleware<E, M> {
271 /// Creates a new endpoint that wraps the given endpoint with middleware.
272 ///
273 /// When the resulting endpoint handles a request, the middleware will be
274 /// executed first. The middleware can then decide whether to call the
275 /// wrapped endpoint and how to process its response.
276 ///
277 /// # Arguments
278 ///
279 /// * `endpoint` - The endpoint to wrap
280 /// * `middleware` - The middleware to apply
281 ///
282 /// # Examples
283 ///
284 /// ```rust
285 /// use http_kit::{Request, Response, Result, Endpoint, Middleware, endpoint::WithMiddleware, Body, Error};
286 /// use http_kit::middleware::MiddlewareError;
287 ///
288 /// struct AuthMiddleware { token: String }
289 /// impl Middleware for AuthMiddleware {
290 /// type Error = Error;
291 /// async fn handle<E: Endpoint>(&mut self, request: &mut Request, mut next: E) -> Result<Response, MiddlewareError<E::Error, Self::Error>> {
292 /// if let Some(auth) = request.headers().get(http::header::AUTHORIZATION) {
293 /// if auth.as_bytes() == self.token.as_bytes() {
294 /// return next.respond(request).await.map_err(MiddlewareError::Endpoint);
295 /// }
296 /// }
297 /// Ok(Response::new(Body::from_bytes("Unauthorized")))
298 /// }
299 /// }
300 ///
301 /// struct SecretEndpoint;
302 /// impl Endpoint for SecretEndpoint {
303 /// type Error = Error;
304 /// async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
305 /// Ok(Response::new(Body::from_bytes("Secret data")))
306 /// }
307 /// }
308 ///
309 /// let auth_middleware = AuthMiddleware { token: "secret".to_string() };
310 /// let protected_endpoint = WithMiddleware::new(SecretEndpoint, auth_middleware);
311 /// ```
312 pub fn new(endpoint: E, middleware: M) -> Self {
313 Self {
314 endpoint,
315 middleware,
316 }
317 }
318}
319
320impl<E: Endpoint, M: Middleware> Endpoint for WithMiddleware<E, M> {
321 type Error = MiddlewareError<E::Error, M::Error>;
322 async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
323 self.middleware.handle(request, &mut self.endpoint).await
324 }
325}
326
327pub(crate) trait EndpointImpl: Send {
328 fn respond_inner<'this, 'req, 'fut>(
329 &'this mut self,
330 request: &'req mut Request,
331 ) -> Pin<Box<dyn 'fut + Send + Future<Output = Result<Response, BoxHttpError>>>>
332 where
333 'this: 'fut,
334 'req: 'fut;
335 fn name(&self) -> &'static str {
336 type_name::<Self>()
337 }
338}
339
340/// Type-erased endpoint that can hold any endpoint implementation behind a trait object.
341///
342/// `AnyEndpoint` provides dynamic dispatch for endpoints, allowing you to store
343/// different endpoint types in the same collection or pass them around without
344/// knowing their concrete types at compile time. This is useful for building
345/// flexible routing systems or plugin architectures.
346///
347/// # Performance Notes
348///
349/// Using `AnyEndpoint` involves dynamic dispatch and heap allocation, which has
350/// a small performance overhead compared to using concrete types directly.
351/// However, this is often negligible in HTTP server contexts.
352///
353/// # Examples
354///
355/// ```rust
356/// use http_kit::{Request, Response, Result, Endpoint, endpoint::AnyEndpoint, Body, Error};
357///
358/// struct HelloEndpoint;
359/// impl Endpoint for HelloEndpoint {
360/// type Error = Error;
361/// async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
362/// Ok(Response::new(Body::from_bytes("Hello")))
363/// }
364/// }
365///
366/// struct GoodbyeEndpoint;
367/// impl Endpoint for GoodbyeEndpoint {
368/// type Error = Error;
369/// async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
370/// Ok(Response::new(Body::from_bytes("Goodbye")))
371/// }
372/// }
373///
374/// // Store different endpoint types in a collection
375/// let endpoints: Vec<AnyEndpoint> = vec![
376/// AnyEndpoint::new(HelloEndpoint),
377/// AnyEndpoint::new(GoodbyeEndpoint),
378/// ];
379/// ```
380pub struct AnyEndpoint(Box<dyn EndpointImpl>);
381
382impl Debug for AnyEndpoint {
383 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
384 f.write_fmt(format_args!("AnyEndpoint[{}]", self.name()))
385 }
386}
387
388impl AnyEndpoint {
389 /// Creates a new type-erased endpoint wrapper around the given endpoint implementation.
390 ///
391 /// This method takes any type that implements `Endpoint` and wraps it in a
392 /// `AnyEndpoint` that can be stored alongside other endpoints of different types.
393 ///
394 /// # Arguments
395 ///
396 /// * `endpoint` - Any endpoint implementation
397 ///
398 /// # Examples
399 ///
400 /// ```rust
401 /// use http_kit::{Request, Response, Result, Endpoint, endpoint::AnyEndpoint, Body, Error};
402 ///
403 /// struct MyEndpoint {
404 /// message: String,
405 /// }
406 ///
407 /// impl Endpoint for MyEndpoint {
408 /// type Error = Error;
409 /// async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
410 /// Ok(Response::new(Body::from_bytes(self.message.clone())))
411 /// }
412 /// }
413 ///
414 /// let endpoint = MyEndpoint { message: "Hello!".to_string() };
415 /// let any_endpoint = AnyEndpoint::new(endpoint);
416 /// ```
417 pub fn new(endpoint: impl Endpoint + 'static) -> Self {
418 Self(Box::new(endpoint))
419 }
420
421 /// Returns the type name of the underlying endpoint implementation.
422 ///
423 /// This can be useful for debugging, logging, or introspection purposes.
424 ///
425 /// # Examples
426 ///
427 /// ```rust
428 /// use http_kit::{Request, Response, Result, Endpoint, endpoint::AnyEndpoint, Body, Error};
429 ///
430 /// struct MyEndpoint;
431 /// impl Endpoint for MyEndpoint {
432 /// type Error = Error;
433 /// async fn respond(&mut self, _request: &mut Request) -> Result<Response> {
434 /// Ok(Response::new(Body::from_bytes("OK")))
435 /// }
436 /// }
437 ///
438 /// let any_endpoint = AnyEndpoint::new(MyEndpoint);
439 /// println!("Endpoint type: {}", any_endpoint.name());
440 /// ```
441 pub fn name(&self) -> &'static str {
442 self.0.name()
443 }
444}
445
446impl<E: Endpoint> EndpointImpl for E {
447 fn respond_inner<'this, 'req, 'fut>(
448 &'this mut self,
449 request: &'req mut Request,
450 ) -> Pin<Box<dyn 'fut + Send + Future<Output = Result<Response, BoxHttpError>>>>
451 where
452 'this: 'fut,
453 'req: 'fut,
454 {
455 Box::pin(async move {
456 Endpoint::respond(self, request)
457 .await
458 .map_err(|e| Box::new(e) as BoxHttpError)
459 })
460 }
461}
462
463impl Endpoint for AnyEndpoint {
464 type Error = BoxHttpError;
465 /// Processes an HTTP request using the underlying endpoint implementation.
466 async fn respond(&mut self, request: &mut Request) -> Result<Response, Self::Error> {
467 self.0.respond_inner(request).await
468 }
469}