use std::collections::HashMap; use regex::Regex; /// Match HTTP headers against a set of patterns. /// /// Pattern values can be: /// - Exact string: `"application/json"` /// - Regex (surrounded by /): `"/^text\/.*/"` pub fn headers_match( patterns: &HashMap, headers: &HashMap, ) -> bool { for (key, pattern) in patterns { let key_lower = key.to_lowercase(); // Find the header (case-insensitive) let header_value = headers .iter() .find(|(k, _)| k.to_lowercase() == key_lower) .map(|(_, v)| v.as_str()); let header_value = match header_value { Some(v) => v, None => return false, // Required header not present }; // Check if pattern is a regex (surrounded by /) if pattern.starts_with('/') && pattern.ends_with('/') && pattern.len() > 2 { let regex_str = &pattern[1..pattern.len() - 1]; match Regex::new(regex_str) { Ok(re) => { if !re.is_match(header_value) { return false; } } Err(_) => { // Invalid regex, fall back to exact match if header_value != pattern { return false; } } } } else { // Exact match if header_value != pattern { return false; } } } true } #[cfg(test)] mod tests { use super::*; #[test] fn test_exact_header_match() { let patterns: HashMap = { let mut m = HashMap::new(); m.insert("Content-Type".to_string(), "application/json".to_string()); m }; let headers: HashMap = { let mut m = HashMap::new(); m.insert("content-type".to_string(), "application/json".to_string()); m }; assert!(headers_match(&patterns, &headers)); } #[test] fn test_regex_header_match() { let patterns: HashMap = { let mut m = HashMap::new(); m.insert("Content-Type".to_string(), "/^text\\/.*/".to_string()); m }; let headers: HashMap = { let mut m = HashMap::new(); m.insert("content-type".to_string(), "text/html".to_string()); m }; assert!(headers_match(&patterns, &headers)); } #[test] fn test_missing_header() { let patterns: HashMap = { let mut m = HashMap::new(); m.insert("X-Custom".to_string(), "value".to_string()); m }; let headers: HashMap = HashMap::new(); assert!(!headers_match(&patterns, &headers)); } }