webx_host/
problem_response.rs1use std::collections::HashMap;
4use webx_core::http::IHttpContext;
5use webx_core::problem::ProblemDetails;
6
7pub fn problem_status_title(status: u16) -> &'static str {
9 match status {
10 400 => "Bad Request",
11 401 => "Unauthorized",
12 403 => "Forbidden",
13 404 => "Not Found",
14 405 => "Method Not Allowed",
15 409 => "Conflict",
16 413 => "Payload Too Large",
17 415 => "Unsupported Media Type",
18 422 => "Unprocessable Entity",
19 429 => "Too Many Requests",
20 501 => "Not Implemented",
21 500 => "Internal Server Error",
22 502 => "Bad Gateway",
23 503 => "Service Unavailable",
24 504 => "Gateway Timeout",
25 _ => "Error",
26 }
27}
28
29pub fn build_problem(status: u16, detail: impl Into<String>) -> ProblemDetails {
31 ProblemDetails {
32 typ: Some(format!("https://httpstatuses.com/{}", status)),
33 title: problem_status_title(status).into(),
34 status,
35 detail: Some(detail.into()),
36 instance: None,
37 extensions: None,
38 }
39}
40
41pub fn problem_to_bytes(problem: &ProblemDetails) -> Vec<u8> {
43 serde_json::to_vec(problem).unwrap_or_default()
44}
45
46pub async fn write_problem_response(ctx: &mut dyn IHttpContext, problem: ProblemDetails) {
48 let status = problem.status;
49 ctx.response_mut().set_status(status);
50 ctx.response_mut()
51 .set_header("content-type", "application/problem+json");
52 let _ = ctx
53 .response_mut()
54 .write_bytes(problem_to_bytes(&problem))
55 .await;
56}
57
58pub async fn write_problem(ctx: &mut dyn IHttpContext, status: u16, detail: impl Into<String>) {
60 write_problem_response(ctx, build_problem(status, detail)).await;
61}
62
63pub const AUTH_REALM: &str = "rust-webx";
68
69pub async fn write_unauthorized(ctx: &mut dyn IHttpContext, detail: impl Into<String>) {
77 let problem = build_problem(401, detail);
78 let challenge = format!("Bearer realm=\"{AUTH_REALM}\"");
79 ctx.response_mut().set_status(problem.status);
80 ctx.response_mut()
81 .set_header("content-type", "application/problem+json");
82 ctx.response_mut()
83 .set_header("www-authenticate", &challenge);
84 let _ = ctx
85 .response_mut()
86 .write_bytes(problem_to_bytes(&problem))
87 .await;
88}
89
90pub async fn write_forbidden(
92 ctx: &mut dyn IHttpContext,
93 detail: impl Into<String>,
94 extensions: HashMap<String, serde_json::Value>,
95) {
96 let mut problem = build_problem(403, detail);
97 if !extensions.is_empty() {
98 problem.extensions = Some(extensions);
99 }
100 write_problem_response(ctx, problem).await;
101}