Skip to main content

bun_sm/
fetch_headers.rs

1// @trace REQ-ENG-001
2// Fetch headers stub for SpiderMonkey.
3// Provides the Rust-side header representation for HTTP fetch operations.
4// The JS-side Headers object is provided by servo's DOM.
5
6use std::collections::HashMap;
7
8/// Case-insensitive HTTP headers stored as a HashMap.
9#[derive(Debug, Clone, Default)]
10pub struct FetchHeaders {
11    headers: HashMap<String, String>,
12}
13
14impl FetchHeaders {
15    pub fn new() -> Self {
16        Self::default()
17    }
18
19    pub fn from_iter<I: Iterator<Item = (String, String)>>(iter: I) -> Self {
20        let mut headers = HashMap::new();
21        for (key, value) in iter {
22            headers.insert(key.to_ascii_lowercase(), value);
23        }
24        Self { headers }
25    }
26
27    pub fn get(&self, name: &str) -> Option<&str> {
28        self.headers
29            .get(&name.to_ascii_lowercase())
30            .map(|s| s.as_str())
31    }
32
33    pub fn set(&mut self, name: &str, value: String) {
34        self.headers.insert(name.to_ascii_lowercase(), value);
35    }
36
37    pub fn has(&self, name: &str) -> bool {
38        self.headers.contains_key(&name.to_ascii_lowercase())
39    }
40
41    pub fn delete(&mut self, name: &str) -> bool {
42        self.headers.remove(&name.to_ascii_lowercase()).is_some()
43    }
44
45    pub fn len(&self) -> usize {
46        self.headers.len()
47    }
48
49    pub fn is_empty(&self) -> bool {
50        self.headers.is_empty()
51    }
52
53    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
54        self.headers.iter().map(|(k, v)| (k.as_str(), v.as_str()))
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn fetch_headers_case_insensitive() {
64        let mut h = FetchHeaders::new();
65        h.set("Content-Type", "text/html".into());
66        assert_eq!(h.get("content-type"), Some("text/html"));
67        assert_eq!(h.get("CONTENT-TYPE"), Some("text/html"));
68    }
69
70    #[test]
71    fn fetch_headers_delete() {
72        let mut h = FetchHeaders::new();
73        h.set("X-Custom", "value".into());
74        assert!(h.delete("x-custom"));
75        assert!(!h.has("X-Custom"));
76    }
77
78    #[test]
79    fn fetch_headers_from_iter() {
80        let h = FetchHeaders::from_iter(vec![("Name".into(), "val".into())].into_iter());
81        assert_eq!(h.get("name"), Some("val"));
82    }
83}