mini-serve 0.13.12

An HTTP server: trie router, middleware, CORS, optional TLS. Built on hyper + tokio.
Documentation
use hyper::body::{Bytes, Incoming};
use hyper::Request;
use http_body_util::{BodyExt, LengthLimitError, Limited};
use serde::de::DeserializeOwned;

use crate::error::ServeError;

/// Default maximum request body size: 2 MiB.
pub const DEFAULT_MAX_BODY_SIZE: usize = 2_097_152;

/// Maximum body size limit for a request.
///
/// Extracted from the request extensions set by the app.
/// Prevents denial-of-service attacks from clients sending arbitrarily large bodies.
#[derive(Clone, Copy, Debug)]
pub struct MaxBodySize(pub usize);

/// Read the whole request body, refusing anything past the configured limit.
///
/// Returns 413 if the body exceeds [`MaxBodySize`], checked from `Content-Length` before
/// a byte is read *and* again while streaming, so a chunked body with no declared length
/// cannot overrun by lying.
///
/// This is where the size limit lives, and it deliberately does not know about JSON. It
/// used to be reachable only through [`json_body`], which meant the crate's body-size
/// guarantee was a side effect of its JSON deserializer — an application reading bodies
/// any other way got no limit at all, and the guarantee would have disappeared entirely
/// had JSON ever become optional.
pub async fn body_bytes(req: Request<Incoming>) -> Result<Bytes, ServeError> {
	let (parts, body) = req.into_parts();
	let max = parts
		.extensions
		.get::<MaxBodySize>()
		.map(|m| m.0)
		.unwrap_or(DEFAULT_MAX_BODY_SIZE);

	if let Some(content_length) = parts.headers.get("content-length") {
		if let Ok(s) = content_length.to_str() {
			if let Ok(len) = s.parse::<usize>() {
				if len > max {
					return Err(ServeError::new(413, "request body too large"));
				}
			}
		}
	}

	let limited = Limited::new(body, max);
	let collected = limited
		.collect()
		.await
		.map_err(|e| {
			if e.downcast_ref::<LengthLimitError>().is_some() {
				ServeError::new(413, "request body too large")
			} else {
				ServeError::new(400, "failed to read request body")
			}
		})?;
	Ok(collected.to_bytes())
}

/// Extract a JSON-deserialized body from the request.
///
/// Returns an HTTP 400 if the body is not valid JSON, or 413 if it exceeds
/// the configured size limit. The limit itself is [`body_bytes`]'s.
pub async fn json_body<T: DeserializeOwned>(req: Request<Incoming>) -> Result<T, ServeError> {
	let bytes = body_bytes(req).await?;
	serde_json::from_slice(&bytes).map_err(|_| ServeError::new(400, "invalid json body"))
}