mini-serve 0.13.12

An HTTP server: trie router, middleware, CORS, optional TLS. Built on hyper + tokio.
Documentation
//! `json_body`, which is `body_bytes` plus a deserialize step.
//!
//! Split out of `body_limits.rs` when the size ceiling moved to `body_bytes`: the limit
//! is a property of reading bytes and belongs with the bytes, while "this is not JSON" is
//! a property of the parser and belongs here.

use hyper::StatusCode;
use mini_serve::{handler, json, json_body, RouteBuilder};
use serde::Deserialize;

const LIMIT: usize = 256;

#[derive(Deserialize)]
struct Payload {
	text: String,
}

async fn json_app() -> u16 {
	let app = RouteBuilder::stateless()
		.with_max_body_size(LIMIT)
		.post(
			"/ingest",
			handler(|req, _state| async move {
				let payload: Payload = json_body(req).await?;
				json(
					StatusCode::OK,
					&serde_json::json!({"len": payload.text.len()}),
				)
			}),
		)
		.seal();
	app.bind_ephemeral().await.unwrap()
}

/// A body small enough but not JSON is a client error, not a server one — and the
/// distinction is worth pinning, since both paths return through `ServeError`.
#[tokio::test]
async fn a_malformed_body_within_the_limit_is_a_400_not_a_413() {
	let port = json_app().await;

	let response = reqwest::Client::new()
		.post(format!("http://127.0.0.1:{port}/ingest"))
		.header("content-type", "application/json")
		.body("{not json")
		.send()
		.await
		.unwrap();

	assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}

/// And a well-formed one still deserializes, or the test above would pass against a
/// `json_body` that rejected everything.
#[tokio::test]
async fn a_well_formed_body_deserializes() {
	let port = json_app().await;

	let response = reqwest::Client::new()
		.post(format!("http://127.0.0.1:{port}/ingest"))
		.json(&serde_json::json!({ "text": "abcd" }))
		.send()
		.await
		.unwrap();

	assert_eq!(response.status(), StatusCode::OK);
	let body: serde_json::Value = response.json().await.unwrap();
	assert_eq!(body["len"], 4);
}