1 use super::origin::Origin;
2 use util::{IterExt, TryFromValues};
3 use HeaderValue;
4 
5 /// The `Access-Control-Allow-Origin` response header,
6 /// part of [CORS](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header)
7 ///
8 /// The `Access-Control-Allow-Origin` header indicates whether a resource
9 /// can be shared based by returning the value of the Origin request header,
10 /// `*`, or `null` in the response.
11 ///
12 /// ## ABNF
13 ///
14 /// ```text
15 /// Access-Control-Allow-Origin = "Access-Control-Allow-Origin" ":" origin-list-or-null | "*"
16 /// ```
17 ///
18 /// ## Example values
19 /// * `null`
20 /// * `*`
21 /// * `http://google.com/`
22 ///
23 /// # Examples
24 ///
25 /// ```
26 /// # extern crate headers;
27 /// use headers::AccessControlAllowOrigin;
28 ///
29 /// let any_origin = AccessControlAllowOrigin::ANY;
30 /// let null_origin = AccessControlAllowOrigin::NULL;
31 /// ```
32 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
33 pub struct AccessControlAllowOrigin(OriginOrAny);
34 
35 derive_header! {
36     AccessControlAllowOrigin(_),
37     name: ACCESS_CONTROL_ALLOW_ORIGIN
38 }
39 
40 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
41 enum OriginOrAny {
42     Origin(Origin),
43     /// Allow all origins
44     Any,
45 }
46 
47 impl AccessControlAllowOrigin {
48     /// `Access-Control-Allow-Origin: *`
49     pub const ANY: AccessControlAllowOrigin = AccessControlAllowOrigin(OriginOrAny::Any);
50     /// `Access-Control-Allow-Origin: null`
51     pub const NULL: AccessControlAllowOrigin =
52         AccessControlAllowOrigin(OriginOrAny::Origin(Origin::NULL));
53 
54     /// Returns the origin if there's one specified.
origin(&self) -> Option<&Origin>55     pub fn origin(&self) -> Option<&Origin> {
56         match self.0 {
57             OriginOrAny::Origin(ref origin) => Some(origin),
58             _ => None,
59         }
60     }
61 }
62 
63 impl TryFromValues for OriginOrAny {
try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error> where I: Iterator<Item = &'i HeaderValue>,64     fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
65     where
66         I: Iterator<Item = &'i HeaderValue>,
67     {
68         values
69             .just_one()
70             .and_then(|value| {
71                 if value == "*" {
72                     return Some(OriginOrAny::Any);
73                 }
74 
75                 Origin::try_from_value(value).map(OriginOrAny::Origin)
76             })
77             .ok_or_else(::Error::invalid)
78     }
79 }
80 
81 impl<'a> From<&'a OriginOrAny> for HeaderValue {
from(origin: &'a OriginOrAny) -> HeaderValue82     fn from(origin: &'a OriginOrAny) -> HeaderValue {
83         match origin {
84             OriginOrAny::Origin(ref origin) => origin.into_value(),
85             OriginOrAny::Any => HeaderValue::from_static("*"),
86         }
87     }
88 }
89 
90 #[cfg(test)]
91 mod tests {
92     use super::super::{test_decode, test_encode};
93     use super::*;
94 
95     #[test]
origin()96     fn origin() {
97         let s = "http://web-platform.test:8000";
98         let allow_origin = test_decode::<AccessControlAllowOrigin>(&[s]).unwrap();
99         {
100             let origin = allow_origin.origin().unwrap();
101             assert_eq!(origin.scheme(), "http");
102             assert_eq!(origin.hostname(), "web-platform.test");
103             assert_eq!(origin.port(), Some(8000));
104         }
105 
106         let headers = test_encode(allow_origin);
107         assert_eq!(headers["access-control-allow-origin"], s);
108     }
109 
110     #[test]
any()111     fn any() {
112         let allow_origin = test_decode::<AccessControlAllowOrigin>(&["*"]).unwrap();
113         assert_eq!(allow_origin, AccessControlAllowOrigin::ANY);
114 
115         let headers = test_encode(allow_origin);
116         assert_eq!(headers["access-control-allow-origin"], "*");
117     }
118 
119     #[test]
null()120     fn null() {
121         let allow_origin = test_decode::<AccessControlAllowOrigin>(&["null"]).unwrap();
122         assert_eq!(allow_origin, AccessControlAllowOrigin::NULL);
123 
124         let headers = test_encode(allow_origin);
125         assert_eq!(headers["access-control-allow-origin"], "null");
126     }
127 }
128