modkit_auth/
http_error.rs1#[must_use]
18pub fn format_http_error(e: &modkit_http::HttpError, prefix: &str) -> String {
19 use modkit_http::HttpError;
20
21 match e {
22 HttpError::HttpStatus { status, .. } => {
23 format!("{prefix} HTTP {status}")
24 }
25 HttpError::Json(err) => format!("{prefix} JSON parse failed: {err}"),
26 HttpError::Timeout(duration) => {
27 format!("{prefix} request timed out after {duration:?}")
28 }
29 HttpError::DeadlineExceeded(duration) => {
30 format!("{prefix} total deadline exceeded after {duration:?}")
31 }
32 HttpError::Transport(err) => format!("{prefix} transport error: {err}"),
33 HttpError::BodyTooLarge { limit, actual } => {
34 format!("{prefix} response too large: limit {limit} bytes, got {actual} bytes")
35 }
36 HttpError::Tls(err) => format!("{prefix} TLS error: {err}"),
37 HttpError::RequestBuild(err) => format!("{prefix} request build failed: {err}"),
38 HttpError::InvalidHeaderName(err) => format!("{prefix} invalid header name: {err}"),
39 HttpError::InvalidHeaderValue(err) => format!("{prefix} invalid header value: {err}"),
40 HttpError::FormEncode(err) => format!("{prefix} form encode error: {err}"),
41 HttpError::Overloaded => format!("{prefix} request rejected: service overloaded"),
42 HttpError::ServiceClosed => format!("{prefix} service unavailable"),
43 HttpError::InvalidUri { url, reason, .. } => {
44 format!("{prefix} invalid URL '{url}': {reason}")
45 }
46 HttpError::InvalidScheme { scheme, reason } => {
47 format!("{prefix} invalid scheme '{scheme}': {reason}")
48 }
49 other => format!("{prefix} request failed: {other}"),
52 }
53}
54
55#[cfg(test)]
56#[cfg_attr(coverage_nightly, coverage(off))]
57mod tests {
58 use super::*;
59 use std::time::Duration;
60
61 #[test]
62 fn http_status_without_body() {
63 let err = modkit_http::HttpError::HttpStatus {
64 status: http::StatusCode::NOT_FOUND,
65 body_preview: String::new(),
66 content_type: None,
67 retry_after: None,
68 };
69 let msg = format_http_error(&err, "TEST");
70 assert_eq!(msg, "TEST HTTP 404 Not Found");
71 }
72
73 #[test]
74 fn http_status_with_body_excludes_body() {
75 let err = modkit_http::HttpError::HttpStatus {
76 status: http::StatusCode::INTERNAL_SERVER_ERROR,
77 body_preview: "something broke".into(),
78 content_type: None,
79 retry_after: None,
80 };
81 let msg = format_http_error(&err, "JWKS");
82 assert_eq!(msg, "JWKS HTTP 500 Internal Server Error");
84 assert!(!msg.contains("something broke"));
85 }
86
87 #[test]
88 fn timeout_error() {
89 let err = modkit_http::HttpError::Timeout(Duration::from_secs(30));
90 let msg = format_http_error(&err, "OAuth2 token");
91 assert_eq!(msg, "OAuth2 token request timed out after 30s");
92 }
93
94 #[test]
95 fn overloaded_error() {
96 let err = modkit_http::HttpError::Overloaded;
97 let msg = format_http_error(&err, "PREFIX");
98 assert_eq!(msg, "PREFIX request rejected: service overloaded");
99 }
100
101 #[test]
102 fn service_closed_error() {
103 let err = modkit_http::HttpError::ServiceClosed;
104 let msg = format_http_error(&err, "PREFIX");
105 assert_eq!(msg, "PREFIX service unavailable");
106 }
107
108 #[test]
109 fn prefix_propagated_to_all_variants() {
110 let cases: Vec<modkit_http::HttpError> = vec![
112 modkit_http::HttpError::Overloaded,
113 modkit_http::HttpError::ServiceClosed,
114 modkit_http::HttpError::Timeout(Duration::from_secs(1)),
115 ];
116 for err in &cases {
117 let msg = format_http_error(err, "CTX");
118 assert!(msg.starts_with("CTX "), "Expected prefix 'CTX' in: {msg}");
119 }
120 }
121}