Skip to main content

mini_serve/
handler.rs

1use std::fmt;
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::{Arc, Mutex};
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/// The upgraded connection handed to an [`OnUpgrade`] callback.
70///
71/// Wrapped so it implements tokio's `AsyncRead`/`AsyncWrite`, which is what a protocol
72/// crate wants; the raw `hyper::upgrade::Upgraded` is reachable through it if needed.
73pub type UpgradedIo = hyper_util::rt::TokioIo<hyper::upgrade::Upgraded>;
74
75/// Take over a connection once the response has been written.
76///
77/// Attach one to a `101 Switching Protocols` response and the server hands you the raw
78/// stream after the response goes out. Everything past that point speaks whatever protocol
79/// you like — this crate stops interpreting the bytes.
80///
81/// The callback runs **inside the connection's own task**, which is deliberate and is the
82/// reason this type exists rather than callers using `hyper::upgrade::on` directly. That
83/// task holds the connection's semaphore permit and is the one shutdown aborts, so an
84/// upgraded connection still counts against [`RouteBuilder::with_max_connections`] and is
85/// still ended by the shutdown drain. Servicing the stream from a detached `tokio::spawn`
86/// — the usual hyper pattern — escapes both.
87///
88/// Requires [`RouteBuilder::with_upgrades`]; without it the response is sent and the
89/// callback never runs.
90///
91/// ```no_run
92/// # use hyper::{Response, StatusCode};
93/// # use mini_serve::{OnUpgrade, ResponseBody, ServeError};
94/// # fn example() -> Result<Response<ResponseBody>, ServeError> {
95/// let mut response = Response::builder()
96///     .status(StatusCode::SWITCHING_PROTOCOLS)
97///     .body(mini_serve::body(hyper::body::Bytes::new()))
98///     .unwrap();
99/// response.extensions_mut().insert(OnUpgrade::new(|_io| async move {
100///     // speak your protocol here
101/// }));
102/// Ok(response)
103/// # }
104/// ```
105///
106/// The callback is held behind `Arc<Mutex<Option<..>>>` rather than directly, because
107/// `http::Extensions` requires `Clone + Send + Sync` and a `FnOnce` is none of those. The
108/// `Option` is what makes it callable once: the connection takes it, leaving `None`.
109/// The boxed callback inside an [`OnUpgrade`]. Named because the nested type is otherwise
110/// unreadable at every use site.
111type UpgradeCallback =
112	Box<dyn FnOnce(UpgradedIo) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
113
114#[derive(Clone)]
115pub struct OnUpgrade(Arc<Mutex<Option<UpgradeCallback>>>);
116
117impl OnUpgrade {
118	/// Wrap a callback to run once the connection has been upgraded.
119	pub fn new<F, Fut>(f: F) -> Self
120	where
121		F: FnOnce(UpgradedIo) -> Fut + Send + 'static,
122		Fut: Future<Output = ()> + Send + 'static,
123	{
124		OnUpgrade(Arc::new(Mutex::new(Some(Box::new(move |io| {
125			Box::pin(f(io)) as Pin<Box<dyn Future<Output = ()> + Send>>
126		})))))
127	}
128
129	/// Run the callback, if it has not already been taken.
130	///
131	/// A poisoned lock or a second call are both no-ops rather than panics: failing to
132	/// upgrade a connection is not worth taking a server down for.
133	pub(crate) async fn run(self, io: UpgradedIo) {
134		let taken = self.0.lock().ok().and_then(|mut slot| slot.take());
135		if let Some(callback) = taken {
136			callback(io).await;
137		}
138	}
139}
140
141impl fmt::Debug for OnUpgrade {
142	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143		f.write_str("OnUpgrade")
144	}
145}
146
147/// A request handler that processes an HTTP request and returns a response or error.
148///
149/// Handlers receive the full request (method, path, headers, body) and the app state,
150/// and return either a response or a `ServeError` (which is converted to an HTTP error response).
151pub type Handler<S> = Arc<
152	dyn Fn(Request<Incoming>, State<S>)
153			-> Pin<Box<dyn Future<Output = Result<Response<ResponseBody>, ServeError>> + Send>>
154		+ Send
155		+ Sync,
156>;
157
158/// Wrap an async function to create a handler.
159///
160/// # Example
161///
162/// ```ignore
163/// use mini_serve::handler;
164/// use hyper::StatusCode;
165/// use hyper::body::Bytes;
166///
167/// let h = handler(|req, state| async move {
168///     Ok::<_, mini_serve::ServeError>(
169///         mini_serve::json(StatusCode::OK, &serde_json::json!({"status": "ok"}))
170///     )
171/// });
172/// ```
173pub fn handler<S, F, Fut>(f: F) -> Handler<S>
174where
175	S: Send + Sync + 'static,
176	F: Fn(Request<Incoming>, State<S>) -> Fut + Send + Sync + 'static,
177	Fut: Future<Output = Result<Response<ResponseBody>, ServeError>> + Send + 'static,
178{
179	Arc::new(move |req, state| Box::pin(f(req, state)))
180}
181
182/// A middleware transforms a `Handler` into a new `Handler`, typically by
183/// running logic before and/or after calling the inner handler — or by
184/// short-circuiting and never calling it at all (e.g. to block a request).
185///
186/// Registered on a [`crate::RouteBuilder`] via `.wrap()`, and applied to every
187/// route registered after that call.
188pub type Middleware<S> = Arc<dyn Fn(Handler<S>) -> Handler<S> + Send + Sync>;