mini_serve/handler.rs
1use std::fmt;
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use hyper::body::{Bytes, Incoming};
7use hyper::{Request, Response};
8use http_body_util::combinators::BoxBody;
9use http_body_util::{BodyExt, Full};
10
11use crate::error::ServeError;
12use crate::state::State;
13
14/// An error that can occur while streaming a response body.
15///
16/// This error type wraps any error that might occur during the actual transmission
17/// of the response body to the client (e.g., I/O errors from a file being streamed).
18/// If the body stream yields an error after headers have already been sent to the client,
19/// that error will cause the connection to be aborted — it cannot be converted back to
20/// an HTTP error response.
21#[derive(Debug)]
22pub struct BodyError {
23 inner: Box<dyn std::error::Error + Send + Sync>,
24}
25
26impl BodyError {
27 /// Wrap an error in a `BodyError`.
28 pub fn new(err: impl std::error::Error + Send + Sync + 'static) -> Self {
29 BodyError {
30 inner: Box::new(err),
31 }
32 }
33}
34
35impl fmt::Display for BodyError {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 write!(f, "body streaming error: {}", self.inner)
38 }
39}
40
41impl std::error::Error for BodyError {
42 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
43 Some(&*self.inner)
44 }
45}
46
47impl From<Box<dyn std::error::Error + Send + Sync>> for BodyError {
48 fn from(err: Box<dyn std::error::Error + Send + Sync>) -> Self {
49 BodyError { inner: err }
50 }
51}
52
53/// The response body type used by all handlers.
54///
55/// The error type is `BodyError`, which can represent errors that occur during body
56/// streaming (e.g., disk I/O failures while reading a large file). These errors will
57/// abort the connection rather than being converted to an HTTP error response.
58pub type ResponseBody = BoxBody<Bytes, BodyError>;
59
60/// Create a response body from raw bytes.
61///
62/// Since the bytes come from an infallible source (an in-memory `Full`), the body
63/// stream can never actually fail. The error type is converted from `Infallible` to
64/// `BodyError` to satisfy the `ResponseBody` type.
65pub fn body(bytes: Bytes) -> ResponseBody {
66 BoxBody::new(Full::new(bytes).map_err(|never: std::convert::Infallible| match never {}))
67}
68
69/// A request handler that processes an HTTP request and returns a response or error.
70///
71/// Handlers receive the full request (method, path, headers, body) and the app state,
72/// and return either a response or a `ServeError` (which is converted to an HTTP error response).
73pub type Handler<S> = Arc<
74 dyn Fn(Request<Incoming>, State<S>)
75 -> Pin<Box<dyn Future<Output = Result<Response<ResponseBody>, ServeError>> + Send>>
76 + Send
77 + Sync,
78>;
79
80/// Wrap an async function to create a handler.
81///
82/// # Example
83///
84/// ```ignore
85/// use mini_serve::handler;
86/// use hyper::StatusCode;
87/// use hyper::body::Bytes;
88///
89/// let h = handler(|req, state| async move {
90/// Ok::<_, mini_serve::ServeError>(
91/// mini_serve::json(StatusCode::OK, &serde_json::json!({"status": "ok"}))
92/// )
93/// });
94/// ```
95pub fn handler<S, F, Fut>(f: F) -> Handler<S>
96where
97 S: Send + Sync + 'static,
98 F: Fn(Request<Incoming>, State<S>) -> Fut + Send + Sync + 'static,
99 Fut: Future<Output = Result<Response<ResponseBody>, ServeError>> + Send + 'static,
100{
101 Arc::new(move |req, state| Box::pin(f(req, state)))
102}
103
104/// A middleware transforms a `Handler` into a new `Handler`, typically by
105/// running logic before and/or after calling the inner handler — or by
106/// short-circuiting and never calling it at all (e.g. to block a request).
107///
108/// Registered on a [`crate::RouteBuilder`] via `.wrap()`, and applied to every
109/// route registered after that call.
110pub type Middleware<S> = Arc<dyn Fn(Handler<S>) -> Handler<S> + Send + Sync>;