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