Skip to main content

worker/
headers.rs

1use crate::{error::Error, Result};
2
3use std::{
4    iter::{FromIterator, Map},
5    result::Result as StdResult,
6    str::FromStr,
7};
8
9use http::{header::HeaderName, HeaderMap, HeaderValue};
10use js_sys::Array;
11use wasm_bindgen::JsValue;
12use worker_sys::ext::HeadersExt;
13
14/// A [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) representation used in
15/// Request and Response objects.
16pub struct Headers(pub web_sys::Headers);
17
18impl std::fmt::Debug for Headers {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        f.write_str("Headers {\n")?;
21        for (k, v) in self.entries() {
22            f.write_str(&format!("{k} = {v}\n"))?;
23        }
24        f.write_str("}\n")
25    }
26}
27
28impl Headers {
29    /// Construct a new `Headers` struct.
30    pub fn new() -> Self {
31        Default::default()
32    }
33
34    /// Returns all the values of a header within a `Headers` object with a given name.
35    /// Returns an error if the name is invalid (e.g. contains spaces)
36    pub fn get(&self, name: &str) -> Result<Option<String>> {
37        self.0.get(name).map_err(Error::from)
38    }
39
40    /// Returns true if the headers object has no entries.
41    pub fn is_empty(&self) -> bool {
42        self.keys().next().is_none()
43    }
44
45    /// Returns a boolean stating whether a `Headers` object contains a certain header.
46    /// Returns an error if the name is invalid (e.g. contains spaces)
47    pub fn has(&self, name: &str) -> Result<bool> {
48        self.0.has(name).map_err(Error::from)
49    }
50
51    /// Returns an error if the name is invalid (e.g. contains spaces)
52    pub fn append(&self, name: &str, value: &str) -> Result<()> {
53        self.0.append(name, value).map_err(Error::from)
54    }
55
56    /// Sets a new value for an existing header inside a `Headers` object, or adds the header if it does not already exist.
57    /// Returns an error if the name is invalid (e.g. contains spaces)
58    pub fn set(&self, name: &str, value: &str) -> Result<()> {
59        self.0.set(name, value).map_err(Error::from)
60    }
61
62    /// Deletes a header from a `Headers` object.
63    /// Returns an error if the name is invalid (e.g. contains spaces)
64    /// or if the JS Headers object's guard is immutable (e.g. for an incoming request)
65    pub fn delete(&self, name: &str) -> Result<()> {
66        self.0.delete(name).map_err(Error::from)
67    }
68
69    /// Returns an iterator allowing to go through all key/value pairs contained in this object.
70    pub fn entries(&self) -> HeaderIterator {
71        self.0
72            .entries()
73            .into_iter()
74            // The entries iterator.next() will always return a proper value: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
75            .map((|a| a.unwrap().into()) as F1)
76            // The entries iterator always returns an array[2] of strings
77            .map(|a: Array| (a.get(0).as_string().unwrap(), a.get(1).as_string().unwrap()))
78    }
79
80    /// Returns an iterator allowing you to go through all keys of the key/value pairs contained in
81    /// this object.
82    pub fn keys(&self) -> impl Iterator<Item = String> {
83        self.0
84            .keys()
85            .into_iter()
86            // The keys iterator.next() will always return a proper value containing a string
87            .map(|a| a.unwrap().as_string().unwrap())
88    }
89
90    /// Returns an iterator allowing you to go through all values of the key/value pairs contained
91    /// in this object.
92    pub fn values(&self) -> impl Iterator<Item = String> {
93        self.0
94            .values()
95            .into_iter()
96            // The values iterator.next() will always return a proper value containing a string
97            .map(|a| a.unwrap().as_string().unwrap())
98    }
99
100    /// Returns all the values of a header within a `Headers` object with a given name.
101    pub fn get_all(&self, name: &str) -> Result<Vec<String>> {
102        let array = self.0.get_all(name);
103        array
104            .iter()
105            .map(|v| {
106                v.as_string()
107                    .ok_or_else(|| Error::JsError("Invalid header value".into()))
108            })
109            .collect()
110    }
111}
112
113impl Default for Headers {
114    fn default() -> Self {
115        // This cannot throw an error: https://developer.mozilla.org/en-US/docs/Web/API/Headers/Headers
116        Headers(web_sys::Headers::new().unwrap())
117    }
118}
119
120type F1 = fn(StdResult<JsValue, JsValue>) -> Array;
121type HeaderIterator = Map<Map<js_sys::IntoIter, F1>, fn(Array) -> (String, String)>;
122
123impl IntoIterator for &Headers {
124    type Item = (String, String);
125
126    type IntoIter = HeaderIterator;
127
128    fn into_iter(self) -> Self::IntoIter {
129        self.entries()
130    }
131}
132
133impl<T: AsRef<str>> FromIterator<(T, T)> for Headers {
134    fn from_iter<U: IntoIterator<Item = (T, T)>>(iter: U) -> Self {
135        let headers = Headers::new();
136        iter.into_iter().for_each(|(name, value)| {
137            headers.append(name.as_ref(), value.as_ref()).ok();
138        });
139        headers
140    }
141}
142
143impl<'a, T: AsRef<str>> FromIterator<&'a (T, T)> for Headers {
144    fn from_iter<U: IntoIterator<Item = &'a (T, T)>>(iter: U) -> Self {
145        let headers = Headers::new();
146        iter.into_iter().for_each(|(name, value)| {
147            headers.append(name.as_ref(), value.as_ref()).ok();
148        });
149        headers
150    }
151}
152
153impl AsRef<JsValue> for Headers {
154    fn as_ref(&self) -> &JsValue {
155        &self.0
156    }
157}
158
159impl From<&HeaderMap> for Headers {
160    fn from(map: &HeaderMap) -> Self {
161        map.keys()
162            .flat_map(|name| {
163                map.get_all(name)
164                    .into_iter()
165                    .map(move |value| (name.to_string(), value.to_str().unwrap().to_owned()))
166            })
167            .collect()
168    }
169}
170
171impl From<HeaderMap> for Headers {
172    fn from(map: HeaderMap) -> Self {
173        (&map).into()
174    }
175}
176
177impl From<&Headers> for HeaderMap {
178    fn from(headers: &Headers) -> Self {
179        headers
180            .into_iter()
181            .map(|(name, value)| {
182                (
183                    HeaderName::from_str(&name).unwrap(),
184                    HeaderValue::from_str(&value).unwrap(),
185                )
186            })
187            .collect()
188    }
189}
190
191impl From<Headers> for HeaderMap {
192    fn from(headers: Headers) -> Self {
193        (&headers).into()
194    }
195}
196
197impl Clone for Headers {
198    fn clone(&self) -> Self {
199        // Headers constructor doesn't throw an error
200        Headers(web_sys::Headers::new_with_headers(&self.0).unwrap())
201    }
202}