mod tests { use super::*; #[test] fn transformations_preserve_distinct_set_cookie_fields() { let source = r#"{"request":[],"response":[{"op":"remove","name":"server"},{"op":"set","name":"x-test","value":"one"},{"op":"append","name":"x-test","value":"two"},{"op":"append","name":"set-cookie","value":"b=2"}]}"#; let rules: HeaderRules = serde_json::from_str(source).unwrap(); let compiled = rules.compile().unwrap(); let mut headers = HeaderMap::new(); headers.insert("server", "upstream".parse().unwrap()); headers.append("set-cookie", "a=1".parse().unwrap()); apply(&mut headers, &compiled.response); assert!(!headers.contains_key("server")); assert_eq!(headers.get_all("x-test").iter().count(), 2); assert_eq!(headers.get_all("set-cookie").iter().count(), 2); } #[test] fn transport_identity_and_crlf_are_rejected() { for name in [ "Host", "Connection", "Content-Length", "x-forwarded-for", "Upgrade", "X-RustProxy-Backend", "X-Rust-Proxy-Backend", "X-RustProxy-Backend-Time", "X-Rust-Proxy-Backend-Time", ] { let rules = HeaderRules { request: vec![Rule { op: Operation::Set, name: name.into(), value: Some("x".into()), }], response: vec![], }; assert!(rules.compile().is_err(), "{name}"); let response_rules = HeaderRules { request: vec![], response: rules.request }; assert!(response_rules.compile().is_err(), "response {name}"); } let rules = HeaderRules { request: vec![Rule { op: Operation::Set, name: "x-test".into(), value: Some("hello\r\ninjected: yes".into()), }], response: vec![], }; assert!(rules.compile().is_err()); } #[test] fn response_age_is_allowed_but_applied_only_at_wire_stage() { let request_age = HeaderRules { request: vec![Rule { op: Operation::Set, name: "Age".into(), value: Some("7".into()), }], response: vec![], }; let request_compiled = request_age.compile().unwrap(); let mut request_headers = HeaderMap::new(); apply(&mut request_headers, &request_compiled.request); assert_eq!(request_headers["age"], "7"); let response_age = HeaderRules { request: vec![], response: vec![Rule { op: Operation::Remove, name: "Age".into(), value: None, }], }; let compiled = response_age.compile().unwrap(); let mut headers = HeaderMap::new(); headers.insert("age", HeaderValue::from_static("42")); apply(&mut headers, &compiled.response); assert_eq!(headers["age"], "42"); apply_wire_age(&mut headers, &compiled.response); assert!(!headers.contains_key("age")); } #[test] fn response_age_keeps_0266_rule_compatibility() { let rules = HeaderRules { request: vec![], response: vec![ Rule { op: Operation::Set, name: "age".into(), value: Some("custom".into()), }, Rule { op: Operation::Append, name: "age".into(), value: Some("7".into()), }, ], }; let compiled = rules.compile().unwrap(); let mut headers = HeaderMap::new(); headers.insert("age", HeaderValue::from_static("42")); apply(&mut headers, &compiled.response); assert_eq!(headers["age"], "42"); apply_wire_age(&mut headers, &compiled.response); assert_eq!(headers.get_all("age").iter().count(), 2); } }