headers_ext/common/
referrer_policy.rs

1use {HeaderValue};
2
3/// `Referrer-Policy` header, part of
4/// [Referrer Policy](https://www.w3.org/TR/referrer-policy/#referrer-policy-header)
5///
6/// The `Referrer-Policy` HTTP header specifies the referrer
7/// policy that the user agent applies when determining what
8/// referrer information should be included with requests made,
9/// and with browsing contexts created from the context of the
10/// protected resource.
11///
12/// # ABNF
13///
14/// ```text
15/// Referrer-Policy: 1#policy-token
16/// policy-token   = "no-referrer" / "no-referrer-when-downgrade"
17///                  / "same-origin" / "origin"
18///                  / "origin-when-cross-origin" / "unsafe-url"
19/// ```
20///
21/// # Example values
22///
23/// * `no-referrer`
24///
25/// # Example
26///
27/// ```
28/// # extern crate headers_ext as headers;
29/// use headers::ReferrerPolicy;
30///
31/// let rp = ReferrerPolicy::NO_REFERRER;
32/// ```
33#[derive(Clone, Debug, PartialEq, Eq, Hash, Header)]
34pub struct ReferrerPolicy(Policy);
35
36#[derive(Clone, Debug, PartialEq, Eq, Hash)]
37enum Policy {
38    NoReferrer,
39    NoReferrerWhenDowngrade,
40    SameOrigin,
41    Origin,
42    OriginWhenCrossOrigin,
43    UnsafeUrl,
44    StrictOrigin,
45    StrictOriginWhenCrossOrigin,
46}
47
48impl ReferrerPolicy {
49    /// `no-referrer`
50    pub const NO_REFERRER: Self = ReferrerPolicy(Policy::NoReferrer);
51
52    /// `no-referrer-when-downgrade`
53    pub const NO_REFERRER_WHEN_DOWNGRADE: Self = ReferrerPolicy(Policy::NoReferrerWhenDowngrade);
54
55    /// `same-origin`
56    pub const SAME_ORIGIN: Self = ReferrerPolicy(Policy::SameOrigin);
57
58    /// `origin`
59    pub const ORIGIN: Self = ReferrerPolicy(Policy::Origin);
60
61    /// `origin-when-cross-origin`
62    pub const ORIGIN_WHEN_CROSS_ORIGIN: Self = ReferrerPolicy(Policy::OriginWhenCrossOrigin);
63
64    /// `unsafe-url`
65    pub const UNSAFE_URL: Self = ReferrerPolicy(Policy::UnsafeUrl);
66
67    /// `strict-origin`
68    pub const STRICT_ORIGIN: Self = ReferrerPolicy(Policy::StrictOrigin);
69
70    ///`strict-origin-when-cross-origin`
71    pub const STRICT_ORIGIN_WHEN_CROSS_ORIGIN: Self = ReferrerPolicy(Policy::StrictOriginWhenCrossOrigin);
72}
73
74impl ::util::TryFromValues for Policy {
75    fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
76    where
77        I: Iterator<Item = &'i HeaderValue>,
78    {
79        // See https://www.w3.org/TR/referrer-policy/#determine-policy-for-token
80        // tl;dr - Pick *last* known policy in the list
81        let mut known = None;
82        for s in csv(values) {
83            known = Some(match s {
84                "no-referrer" | "never" =>  Policy::NoReferrer,
85                "no-referrer-when-downgrade" | "default" => Policy::NoReferrerWhenDowngrade,
86                "same-origin" => Policy::SameOrigin,
87                "origin" => Policy::Origin,
88                "origin-when-cross-origin" => Policy::OriginWhenCrossOrigin,
89                "strict-origin" => Policy::StrictOrigin,
90                "strict-origin-when-cross-origin" => Policy::StrictOriginWhenCrossOrigin,
91                "unsafe-url" | "always" => Policy::UnsafeUrl,
92                _ => continue,
93            });
94        }
95
96        known
97            .ok_or_else(::Error::invalid)
98    }
99}
100
101impl<'a> From<&'a Policy> for HeaderValue {
102    fn from(policy: &'a Policy) -> HeaderValue {
103        HeaderValue::from_static(match *policy {
104            Policy::NoReferrer => "no-referrer",
105            Policy::NoReferrerWhenDowngrade => "no-referrer-when-downgrade",
106            Policy::SameOrigin => "same-origin",
107            Policy::Origin => "origin",
108            Policy::OriginWhenCrossOrigin => "origin-when-cross-origin",
109            Policy::StrictOrigin => "strict-origin",
110            Policy::StrictOriginWhenCrossOrigin => "strict-origin-when-cross-origin",
111            Policy::UnsafeUrl => "unsafe-url",
112        })
113    }
114}
115
116fn csv<'i, I>(values: I) -> impl Iterator<Item=&'i str>
117where
118    I: Iterator<Item = &'i HeaderValue>,
119{
120    values
121        .flat_map(|value| {
122            value
123                .to_str()
124                .into_iter()
125                .flat_map(|string| {
126                    string
127                        .split(',')
128                        .filter_map(|x| match x.trim() {
129                            "" => None,
130                            y => Some(y),
131                        })
132                })
133        })
134}
135
136#[cfg(test)]
137mod tests {
138    use super::ReferrerPolicy;
139    use super::super::test_decode;
140
141    #[test]
142    fn decode_as_last_policy() {
143        assert_eq!(
144            test_decode::<ReferrerPolicy>(&["same-origin, origin"]),
145            Some(ReferrerPolicy::ORIGIN),
146        );
147
148        assert_eq!(
149            test_decode::<ReferrerPolicy>(&["origin", "same-origin"]),
150            Some(ReferrerPolicy::SAME_ORIGIN),
151        );
152    }
153
154    #[test]
155    fn decode_as_last_known() {
156        assert_eq!(
157            test_decode::<ReferrerPolicy>(&["origin, nope, nope, nope"]),
158            Some(ReferrerPolicy::ORIGIN),
159        );
160
161        assert_eq!(
162            test_decode::<ReferrerPolicy>(&["nope, origin, nope, nope"]),
163            Some(ReferrerPolicy::ORIGIN),
164        );
165
166        assert_eq!(
167            test_decode::<ReferrerPolicy>(&["nope, origin", "nope, nope"]),
168            Some(ReferrerPolicy::ORIGIN),
169        );
170
171        assert_eq!(
172            test_decode::<ReferrerPolicy>(&["nope", "origin", "nope, nope"]),
173            Some(ReferrerPolicy::ORIGIN),
174        );
175    }
176
177    #[test]
178    fn decode_unknown() {
179        assert_eq!(
180            test_decode::<ReferrerPolicy>(&["nope"]),
181            None,
182        );
183    }
184
185    #[test]
186    fn matching() {
187        let rp = ReferrerPolicy::ORIGIN;
188
189        match rp {
190            ReferrerPolicy::ORIGIN => (),
191            _ => panic!("matched wrong"),
192        }
193    }
194}