http_types/security/
timing_allow_origin.rs1use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, TIMING_ALLOW_ORIGIN};
30use crate::{Status, Url};
31
32use std::fmt::Write;
33use std::fmt::{self, Debug};
34use std::iter::Iterator;
35use std::option;
36use std::slice;
37
38#[derive(Clone, Eq, PartialEq)]
61pub struct TimingAllowOrigin {
62 origins: Vec<Url>,
63 wildcard: bool,
64}
65
66impl TimingAllowOrigin {
67 pub fn new() -> Self {
69 Self {
70 origins: vec![],
71 wildcard: false,
72 }
73 }
74
75 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
81 let headers = match headers.as_ref().get(TIMING_ALLOW_ORIGIN) {
82 Some(headers) => headers,
83 None => return Ok(None),
84 };
85
86 let mut wildcard = false;
87 let mut origins = vec![];
88 for header in headers {
89 for origin in header.as_str().split(',') {
90 match origin.trim_start() {
91 "*" => wildcard = true,
92 r#""null""# => continue,
93 origin => {
94 let url = Url::parse(origin).status(400)?;
95 origins.push(url);
96 }
97 }
98 }
99 }
100
101 Ok(Some(Self { origins, wildcard }))
102 }
103
104 pub fn push(&mut self, origin: impl Into<Url>) {
106 self.origins.push(origin.into());
107 }
108
109 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
111 headers.as_mut().insert(TIMING_ALLOW_ORIGIN, self.value());
112 }
113
114 pub fn name(&self) -> HeaderName {
116 TIMING_ALLOW_ORIGIN
117 }
118
119 pub fn value(&self) -> HeaderValue {
121 let mut output = String::new();
122 for (n, origin) in self.origins.iter().enumerate() {
123 match n {
124 0 => write!(output, "{}", origin).unwrap(),
125 _ => write!(output, ", {}", origin).unwrap(),
126 };
127 }
128
129 if self.wildcard {
130 match output.len() {
131 0 => write!(output, "*").unwrap(),
132 _ => write!(output, ", *").unwrap(),
133 };
134 }
135
136 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
138 }
139
140 pub fn wildcard(&self) -> bool {
142 self.wildcard
143 }
144
145 pub fn set_wildcard(&mut self, wildcard: bool) {
147 self.wildcard = wildcard
148 }
149
150 pub fn iter(&self) -> Iter<'_> {
152 Iter {
153 inner: self.origins.iter(),
154 }
155 }
156
157 pub fn iter_mut(&mut self) -> IterMut<'_> {
159 IterMut {
160 inner: self.origins.iter_mut(),
161 }
162 }
163}
164
165impl IntoIterator for TimingAllowOrigin {
166 type Item = Url;
167 type IntoIter = IntoIter;
168
169 #[inline]
170 fn into_iter(self) -> Self::IntoIter {
171 IntoIter {
172 inner: self.origins.into_iter(),
173 }
174 }
175}
176
177impl<'a> IntoIterator for &'a TimingAllowOrigin {
178 type Item = &'a Url;
179 type IntoIter = Iter<'a>;
180
181 fn into_iter(self) -> Self::IntoIter {
183 self.iter()
184 }
185}
186
187impl<'a> IntoIterator for &'a mut TimingAllowOrigin {
188 type Item = &'a mut Url;
189 type IntoIter = IterMut<'a>;
190
191 #[inline]
192 fn into_iter(self) -> Self::IntoIter {
193 self.iter_mut()
194 }
195}
196
197#[derive(Debug)]
199pub struct IntoIter {
200 inner: std::vec::IntoIter<Url>,
201}
202
203impl Iterator for IntoIter {
204 type Item = Url;
205
206 fn next(&mut self) -> Option<Self::Item> {
207 self.inner.next()
208 }
209
210 #[inline]
211 fn size_hint(&self) -> (usize, Option<usize>) {
212 self.inner.size_hint()
213 }
214}
215
216#[derive(Debug)]
218pub struct Iter<'a> {
219 inner: slice::Iter<'a, Url>,
220}
221
222impl<'a> Iterator for Iter<'a> {
223 type Item = &'a Url;
224
225 fn next(&mut self) -> Option<Self::Item> {
226 self.inner.next()
227 }
228
229 #[inline]
230 fn size_hint(&self) -> (usize, Option<usize>) {
231 self.inner.size_hint()
232 }
233}
234
235#[derive(Debug)]
237pub struct IterMut<'a> {
238 inner: slice::IterMut<'a, Url>,
239}
240
241impl<'a> Iterator for IterMut<'a> {
242 type Item = &'a mut Url;
243
244 fn next(&mut self) -> Option<Self::Item> {
245 self.inner.next()
246 }
247
248 #[inline]
249 fn size_hint(&self) -> (usize, Option<usize>) {
250 self.inner.size_hint()
251 }
252}
253
254impl ToHeaderValues for TimingAllowOrigin {
256 type Iter = option::IntoIter<HeaderValue>;
257 fn to_header_values(&self) -> crate::Result<Self::Iter> {
258 Ok(self.value().to_header_values().unwrap())
259 }
260}
261
262impl Debug for TimingAllowOrigin {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 let mut list = f.debug_list();
265 for origin in &self.origins {
266 list.entry(origin);
267 }
268 list.finish()
269 }
270}
271
272#[cfg(test)]
273mod test {
274 use super::*;
275 use crate::headers::Headers;
276
277 #[test]
278 fn smoke() -> crate::Result<()> {
279 let mut origins = TimingAllowOrigin::new();
280 origins.push(Url::parse("https://example.com")?);
281
282 let mut headers = Headers::new();
283 origins.apply(&mut headers);
284
285 let origins = TimingAllowOrigin::from_headers(headers)?.unwrap();
286 let origin = origins.iter().next().unwrap();
287 assert_eq!(origin, &Url::parse("https://example.com")?);
288 Ok(())
289 }
290
291 #[test]
292 fn multi() -> crate::Result<()> {
293 let mut origins = TimingAllowOrigin::new();
294 origins.push(Url::parse("https://example.com")?);
295 origins.push(Url::parse("https://mozilla.org/")?);
296
297 let mut headers = Headers::new();
298 origins.apply(&mut headers);
299
300 let origins = TimingAllowOrigin::from_headers(headers)?.unwrap();
301 let mut origins = origins.iter();
302 let origin = origins.next().unwrap();
303 assert_eq!(origin, &Url::parse("https://example.com")?);
304
305 let origin = origins.next().unwrap();
306 assert_eq!(origin, &Url::parse("https://mozilla.org/")?);
307 Ok(())
308 }
309
310 #[test]
311 fn bad_request_on_parse_error() {
312 let mut headers = Headers::new();
313 headers.insert(TIMING_ALLOW_ORIGIN, "server; <nori ate your param omnom>");
314 let err = TimingAllowOrigin::from_headers(headers).unwrap_err();
315 assert_eq!(err.status(), 400);
316 }
317
318 #[test]
319 fn wildcard() -> crate::Result<()> {
320 let mut origins = TimingAllowOrigin::new();
321 origins.push(Url::parse("https://example.com")?);
322 origins.set_wildcard(true);
323
324 let mut headers = Headers::new();
325 origins.apply(&mut headers);
326
327 let origins = TimingAllowOrigin::from_headers(headers)?.unwrap();
328 assert!(origins.wildcard());
329 let origin = origins.iter().next().unwrap();
330 assert_eq!(origin, &Url::parse("https://example.com")?);
331 Ok(())
332 }
333}