Skip to main content

mini_serve/
body.rs

1use hyper::body::{Bytes, Incoming};
2use hyper::Request;
3use http_body_util::{BodyExt, LengthLimitError, Limited};
4use serde::de::DeserializeOwned;
5
6use crate::error::ServeError;
7
8/// Default maximum request body size: 2 MiB.
9pub const DEFAULT_MAX_BODY_SIZE: usize = 2_097_152;
10
11/// Maximum body size limit for a request.
12///
13/// Extracted from the request extensions set by the app.
14/// Prevents denial-of-service attacks from clients sending arbitrarily large bodies.
15#[derive(Clone, Copy, Debug)]
16pub struct MaxBodySize(pub usize);
17
18/// Read the whole request body, refusing anything past the configured limit.
19///
20/// Returns 413 if the body exceeds [`MaxBodySize`], checked from `Content-Length` before
21/// a byte is read *and* again while streaming, so a chunked body with no declared length
22/// cannot overrun by lying.
23///
24/// This is where the size limit lives, and it deliberately does not know about JSON. It
25/// used to be reachable only through [`json_body`], which meant the crate's body-size
26/// guarantee was a side effect of its JSON deserializer — an application reading bodies
27/// any other way got no limit at all, and the guarantee would have disappeared entirely
28/// had JSON ever become optional.
29pub async fn body_bytes(req: Request<Incoming>) -> Result<Bytes, ServeError> {
30	let (parts, body) = req.into_parts();
31	let max = parts
32		.extensions
33		.get::<MaxBodySize>()
34		.map(|m| m.0)
35		.unwrap_or(DEFAULT_MAX_BODY_SIZE);
36
37	if let Some(content_length) = parts.headers.get("content-length") {
38		if let Ok(s) = content_length.to_str() {
39			if let Ok(len) = s.parse::<usize>() {
40				if len > max {
41					return Err(ServeError::new(413, "request body too large"));
42				}
43			}
44		}
45	}
46
47	let limited = Limited::new(body, max);
48	let collected = limited
49		.collect()
50		.await
51		.map_err(|e| {
52			if e.downcast_ref::<LengthLimitError>().is_some() {
53				ServeError::new(413, "request body too large")
54			} else {
55				ServeError::new(400, "failed to read request body")
56			}
57		})?;
58	Ok(collected.to_bytes())
59}
60
61/// Extract a JSON-deserialized body from the request.
62///
63/// Returns an HTTP 400 if the body is not valid JSON, or 413 if it exceeds
64/// the configured size limit. The limit itself is [`body_bytes`]'s.
65pub async fn json_body<T: DeserializeOwned>(req: Request<Incoming>) -> Result<T, ServeError> {
66	let bytes = body_bytes(req).await?;
67	serde_json::from_slice(&bytes).map_err(|_| ServeError::new(400, "invalid json body"))
68}