[go: up one dir, main page]

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