apimock_server/http_util.rs
1//! HTTP utilities used only by the server crate.
2//!
3//! `normalize_url_path` previously lived alongside these but moved to
4//! `apimock-routing::util::http` in 5.0 — the matcher needs it; these
5//! two helpers are HTTP-layer only.
6
7use hyper::{
8 HeaderMap,
9 header::{CONTENT_TYPE, HeaderValue},
10};
11use tokio::time;
12
13use std::time::Duration;
14
15/// Inspect `Content-Type` to decide whether a request body should be
16/// parsed as JSON.
17///
18/// Returns:
19/// - `Some(true)` — header is present and starts with `application/json`
20/// (supports `application/json; charset=utf-8` and similar).
21/// - `Some(false)` — header is present but is something else.
22/// - `None` — header is absent, so we can't tell.
23///
24/// The three-valued return is deliberate: callers treat "absent" and
25/// "present-but-wrong" differently (the first is a common shortcut, the
26/// second is a likely client bug).
27pub fn content_type_is_application_json(headers: &HeaderMap<HeaderValue>) -> Option<bool> {
28 let content_type = headers.get(CONTENT_TYPE)?;
29
30 let Ok(content_type) = content_type.to_str() else {
31 return Some(false);
32 };
33
34 Some(
35 content_type
36 .trim_start()
37 .to_ascii_lowercase()
38 .starts_with("application/json"),
39 )
40}
41
42/// Sleep `milliseconds` ms on the async runtime.
43///
44/// Used by `respond.delay_response_milliseconds` to simulate slow
45/// backends when a mock needs to exercise client timeout behaviour.
46pub async fn delay_response(milliseconds: u32) {
47 time::sleep(Duration::from_millis(milliseconds.into())).await
48}