Skip to main content

mini_serve/
response.rs

1use hyper::header::HeaderValue;
2use hyper::{Response, StatusCode};
3use http_body_util::combinators::BoxBody;
4use http_body_util::{BodyExt, Full};
5use hyper::body::Bytes;
6use serde::Serialize;
7
8use crate::error::ServeError;
9use crate::handler::ResponseBody;
10
11/// Build a JSON response with the given status code and serializable value.
12///
13/// Sets `Content-Type: application/json` and `Content-Length` headers.
14/// Returns `500 Internal Server Error` if serialization fails.
15pub fn json<T: Serialize>(status: StatusCode, value: &T) -> Result<Response<ResponseBody>, ServeError> {
16	let body = serde_json::to_string(value)
17		.map_err(|_| ServeError::new(500, "failed to serialize response"))?;
18	let len = body.len();
19	let mut resp = Response::new(BoxBody::new(
20		Full::new(Bytes::from(body)).map_err(|never: std::convert::Infallible| match never {}),
21	));
22	*resp.status_mut() = status;
23
24	// Const `HeaderName`s and a static value: a `&str` here would be re-parsed
25	// into a `HeaderName` on every response, which showed up in the profile as
26	// `header::name::parse_hdr`.
27	//
28	// `insert`, not `Response::builder().header(…)`, which routes to `try_append` and
29	// scans the map for an existing entry of that name — 4.58% of this crate's profile
30	// against axum's 1.82%, for a map we just created and know to be empty.
31	//
32	// This is safe here and is *not* a technique to apply elsewhere: `insert` replaces
33	// where `append` adds, which is invisible for `Content-Type` and `Content-Length`
34	// (single-valued by definition) and destructive for headers that may legitimately
35	// repeat — `Set-Cookie`, and above all `Vary`, where dropping a value is a
36	// cache-poisoning vector. `cors.rs` already emits `vary: origin`. Any other call
37	// site needs its own analysis of whether that header can repeat.
38	let headers = resp.headers_mut();
39	headers.insert(hyper::header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
40	headers.insert(hyper::header::CONTENT_LENGTH, len.into());
41	Ok(resp)
42}