headers/common/
referrer_policy.rs

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