featherbit/plugins/util/
provider_error.rs1use std::collections::HashMap;
16
17use bytes::Bytes;
18
19use crate::context::{Context, GatewayError};
20use crate::plugins::PluginExecutionError;
21
22pub fn provider_error(mut ctx: Context, code: &str, message: String) -> PluginExecutionError {
30 ctx.response.status_code = 502;
31 ctx.response.headers.remove("www-authenticate");
32 ctx.response.body = Bytes::from(
33 serde_json::json!({ "error": "provider_error", "message": message }).to_string(),
34 );
35 ctx.response.headers.insert(
36 "content-type".to_string(),
37 vec!["application/json".to_string()],
38 );
39 PluginExecutionError {
40 context: ctx,
41 error: GatewayError {
42 node_id: String::new(),
43 code: code.to_string(),
44 message,
45 metadata: HashMap::new(),
46 },
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use crate::context::{Context, GatewayRequest};
54 use bytes::Bytes;
55 use std::collections::HashMap;
56
57 fn ctx() -> Context {
58 Context::new(GatewayRequest {
59 method: "GET".into(),
60 path: "/".into(),
61 host: "h".into(),
62 scheme: "http".into(),
63 headers: HashMap::new(),
64 query_params: HashMap::new(),
65 body: Bytes::new(),
66 remote_addr: "1.2.3.4:5".into(),
67 protocol: crate::context::Protocol::Http1,
68 })
69 }
70
71 #[test]
72 fn prepares_a_502_provider_error_response() {
73 let err = provider_error(ctx(), "X_PROVIDER_ERROR", "idp unreachable".to_string());
74 testing::assert_provider_error(&err, "X_PROVIDER_ERROR");
75 assert_eq!(err.error.message, "idp unreachable");
76 assert_eq!(err.error.node_id, "");
78 }
79
80 #[test]
81 fn strips_a_challenge_a_caller_may_have_prepared() {
82 let mut c = ctx();
83 c.response.headers.insert(
84 "www-authenticate".to_string(),
85 vec!["Basic realm=\"x\"".to_string()],
86 );
87 let err = provider_error(c, "X", "m".to_string());
88 assert!(!err
89 .context
90 .response
91 .headers
92 .contains_key("www-authenticate"));
93 }
94
95 #[test]
96 fn message_is_json_escaped_not_interpolated() {
97 let err = provider_error(ctx(), "X", r#"said "no" \ bye"#.to_string());
98 let body: serde_json::Value = serde_json::from_slice(&err.context.response.body).unwrap();
99 assert_eq!(body["message"], r#"said "no" \ bye"#);
100 }
101}
102
103#[cfg(test)]
106pub mod testing {
107 use crate::plugins::PluginExecutionError;
108
109 pub fn assert_provider_error(err: &PluginExecutionError, code: &str) {
113 assert_eq!(err.error.code, code);
114 assert_eq!(
115 err.context.response.status_code, 502,
116 "provider failures are 502"
117 );
118 assert!(
119 !err.context
120 .response
121 .headers
122 .contains_key("www-authenticate"),
123 "a provider failure must not challenge the client"
124 );
125 assert_eq!(
126 err.context
127 .response
128 .headers
129 .get("content-type")
130 .map(|v| v[0].as_str()),
131 Some("application/json")
132 );
133 let body: serde_json::Value = serde_json::from_slice(&err.context.response.body)
134 .expect("provider error body is JSON");
135 assert_eq!(body["error"], "provider_error", "{body}");
136 assert_eq!(body["message"], err.error.message, "{body}");
137 }
138}