Skip to main content

tower_request_guard/
headers.rs

1use http::HeaderMap;
2
3/// Check that all required headers are present.
4/// Returns the first missing header name, or None if all present.
5/// Lookup is case-insensitive (HeaderMap handles this).
6pub fn find_missing_header(headers: &HeaderMap, required: &[String]) -> Option<String> {
7    for name in required {
8        if headers.get(name.as_str()).is_none() {
9            return Some(name.clone());
10        }
11    }
12    None
13}
14
15#[cfg(test)]
16mod tests {
17    use super::*;
18    use http::HeaderMap;
19
20    #[test]
21    fn all_headers_present() {
22        let mut headers = HeaderMap::new();
23        headers.insert("authorization", "Bearer token".parse().unwrap());
24        headers.insert("x-request-id", "abc-123".parse().unwrap());
25        let required = vec!["Authorization".to_string(), "X-Request-Id".to_string()];
26        assert_eq!(find_missing_header(&headers, &required), None);
27    }
28
29    #[test]
30    fn missing_header_detected() {
31        let headers = HeaderMap::new();
32        let required = vec!["Authorization".to_string()];
33        assert_eq!(
34            find_missing_header(&headers, &required),
35            Some("Authorization".to_string())
36        );
37    }
38
39    #[test]
40    fn case_insensitive_lookup() {
41        let mut headers = HeaderMap::new();
42        headers.insert("authorization", "Bearer token".parse().unwrap());
43        let required = vec!["Authorization".to_string()];
44        assert_eq!(find_missing_header(&headers, &required), None);
45    }
46
47    #[test]
48    fn returns_first_missing() {
49        let mut headers = HeaderMap::new();
50        headers.insert("x-request-id", "abc".parse().unwrap());
51        let required = vec![
52            "Authorization".to_string(),
53            "X-Request-Id".to_string(),
54            "X-Tenant-Id".to_string(),
55        ];
56        assert_eq!(
57            find_missing_header(&headers, &required),
58            Some("Authorization".to_string())
59        );
60    }
61
62    #[test]
63    fn empty_required_always_passes() {
64        let headers = HeaderMap::new();
65        let required: Vec<String> = vec![];
66        assert_eq!(find_missing_header(&headers, &required), None);
67    }
68}