headers_ext/common/
access_control_allow_origin.rs

1use ::{HeaderValue};
2use ::util::TryFromValues;
3use super::origin::{Origin};
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_ext as 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, Header)]
33pub struct AccessControlAllowOrigin(OriginOrAny);
34
35#[derive(Clone, Debug, PartialEq, Eq, Hash)]
36enum OriginOrAny {
37    Origin(Origin),
38    /// Allow all origins
39    Any,
40}
41
42impl AccessControlAllowOrigin {
43    /// `Access-Control-Allow-Origin: *`
44    pub const ANY: AccessControlAllowOrigin = AccessControlAllowOrigin(OriginOrAny::Any);
45    /// `Access-Control-Allow-Origin: null`
46    pub const NULL: AccessControlAllowOrigin = AccessControlAllowOrigin(OriginOrAny::Origin(Origin::NULL));
47
48    /// Returns the origin if there's one specified.
49    pub fn origin(&self) -> Option<&Origin> {
50        match self.0 {
51            OriginOrAny::Origin(ref origin) => Some(origin),
52            _ => None
53        }
54    }
55}
56
57impl TryFromValues for OriginOrAny {
58    fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
59    where
60        I: Iterator<Item = &'i HeaderValue>,
61    {
62        values
63            .next()
64            .and_then(|value| {
65                if value == "*" {
66                    return Some(OriginOrAny::Any);
67                }
68
69                Origin::try_from_value(value)
70                    .map(OriginOrAny::Origin)
71            })
72            .ok_or_else(::Error::invalid)
73    }
74}
75
76impl<'a> From<&'a OriginOrAny> for HeaderValue {
77    fn from(origin: &'a OriginOrAny) -> HeaderValue {
78        match origin {
79            OriginOrAny::Origin(ref origin) => origin.into_value(),
80            OriginOrAny::Any => HeaderValue::from_static("*"),
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use super::super::{test_decode, test_encode};
89
90
91    #[test]
92    fn origin() {
93        let s = "http://web-platform.test:8000";
94        let allow_origin = test_decode::<AccessControlAllowOrigin>(&[s]).unwrap();
95        {
96            let origin = allow_origin.origin().unwrap();
97            assert_eq!(origin.scheme(), "http");
98            assert_eq!(origin.hostname(), "web-platform.test");
99            assert_eq!(origin.port(), Some(8000));
100        }
101
102        let headers = test_encode(allow_origin);
103        assert_eq!(headers["access-control-allow-origin"], s);
104    }
105
106    #[test]
107    fn any() {
108        let allow_origin = test_decode::<AccessControlAllowOrigin>(&["*"]).unwrap();
109        assert_eq!(allow_origin, AccessControlAllowOrigin::ANY);
110
111        let headers = test_encode(allow_origin);
112        assert_eq!(headers["access-control-allow-origin"], "*");
113    }
114
115    #[test]
116    fn null() {
117        let allow_origin = test_decode::<AccessControlAllowOrigin>(&["null"]).unwrap();
118        assert_eq!(allow_origin, AccessControlAllowOrigin::NULL);
119
120        let headers = test_encode(allow_origin);
121        assert_eq!(headers["access-control-allow-origin"], "null");
122    }
123}
124