mini-serve 0.7.0

An HTTP server: trie router, middleware, CORS, optional TLS. Built on hyper + tokio.
Documentation
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;

use hyper::body::{Bytes, Incoming};
use hyper::{Request, Response};
use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt, Full};

use crate::error::ServeError;
use crate::state::State;

/// An error that can occur while streaming a response body.
///
/// This error type wraps any error that might occur during the actual transmission
/// of the response body to the client (e.g., I/O errors from a file being streamed).
/// If the body stream yields an error after headers have already been sent to the client,
/// that error will cause the connection to be aborted — it cannot be converted back to
/// an HTTP error response.
#[derive(Debug)]
pub struct BodyError {
	inner: Box<dyn std::error::Error + Send + Sync>,
}

impl BodyError {
	/// Wrap an error in a `BodyError`.
	pub fn new(err: impl std::error::Error + Send + Sync + 'static) -> Self {
		BodyError {
			inner: Box::new(err),
		}
	}
}

impl fmt::Display for BodyError {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		write!(f, "body streaming error: {}", self.inner)
	}
}

impl std::error::Error for BodyError {
	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
		Some(&*self.inner)
	}
}

impl From<Box<dyn std::error::Error + Send + Sync>> for BodyError {
	fn from(err: Box<dyn std::error::Error + Send + Sync>) -> Self {
		BodyError { inner: err }
	}
}

/// The response body type used by all handlers.
///
/// The error type is `BodyError`, which can represent errors that occur during body
/// streaming (e.g., disk I/O failures while reading a large file). These errors will
/// abort the connection rather than being converted to an HTTP error response.
pub type ResponseBody = BoxBody<Bytes, BodyError>;

/// Create a response body from raw bytes.
///
/// Since the bytes come from an infallible source (an in-memory `Full`), the body
/// stream can never actually fail. The error type is converted from `Infallible` to
/// `BodyError` to satisfy the `ResponseBody` type.
pub fn body(bytes: Bytes) -> ResponseBody {
	BoxBody::new(Full::new(bytes).map_err(|never: std::convert::Infallible| match never {}))
}

/// A request handler that processes an HTTP request and returns a response or error.
///
/// Handlers receive the full request (method, path, headers, body) and the app state,
/// and return either a response or a `ServeError` (which is converted to an HTTP error response).
pub type Handler<S> = Arc<
	dyn Fn(Request<Incoming>, State<S>)
			-> Pin<Box<dyn Future<Output = Result<Response<ResponseBody>, ServeError>> + Send>>
		+ Send
		+ Sync,
>;

/// Wrap an async function to create a handler.
///
/// # Example
///
/// ```ignore
/// use mini_serve::handler;
/// use hyper::StatusCode;
/// use hyper::body::Bytes;
///
/// let h = handler(|req, state| async move {
///     Ok::<_, mini_serve::ServeError>(
///         mini_serve::json(StatusCode::OK, &serde_json::json!({"status": "ok"}))
///     )
/// });
/// ```
pub fn handler<S, F, Fut>(f: F) -> Handler<S>
where
	S: Send + Sync + 'static,
	F: Fn(Request<Incoming>, State<S>) -> Fut + Send + Sync + 'static,
	Fut: Future<Output = Result<Response<ResponseBody>, ServeError>> + Send + 'static,
{
	Arc::new(move |req, state| Box::pin(f(req, state)))
}