Skip to main content

rama_http_headers/common/
cross_origin_resource_policy.rs

1use rama_http_types::{HeaderName, HeaderValue};
2use rama_utils::macros::enums::enum_builder;
3
4use crate::util::{self, IterExt};
5use crate::{Error, HeaderDecode, HeaderEncode, TypedHeader};
6
7enum_builder! {
8    /// `Cross-Origin-Resource-Policy` (CORP) header, defined by
9    /// [Fetch § cross-origin-resource-policy-header](https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header).
10    ///
11    /// Lets a server opt resources out of being embedded by cross-
12    /// origin / cross-site documents. Single token, no parameters, no
13    /// report-only variant.
14    ///
15    /// # Default semantics
16    ///
17    /// When the header is absent the user agent applies its default
18    /// embedding policy (effectively `cross-origin` for legacy
19    /// backwards compatibility). The typed value here represents the
20    /// header being *present*. The auto-generated
21    /// [`Unknown`](Self::Unknown) variant is reachable only if a
22    /// caller constructs it directly — the [`HeaderDecode`] impl uses
23    /// strict parsing and rejects any unknown token.
24    ///
25    /// # Example values
26    ///
27    /// * `same-origin`
28    /// * `same-site`
29    /// * `cross-origin`
30    ///
31    /// # Example
32    ///
33    /// ```
34    /// use rama_http_headers::CrossOriginResourcePolicy;
35    ///
36    /// let corp = CrossOriginResourcePolicy::SameOrigin;
37    /// assert_eq!(corp.to_string(), "same-origin");
38    /// ```
39    @String
40    pub enum CrossOriginResourcePolicy {
41        /// `same-site` — only documents from the same registrable
42        /// site may embed the resource.
43        SameSite => "same-site",
44        /// `same-origin` — only same-origin documents may embed.
45        SameOrigin => "same-origin",
46        /// `cross-origin` — any document may embed. Matches the
47        /// legacy default but makes the intent explicit on the wire.
48        CrossOrigin => "cross-origin",
49    }
50}
51
52impl TypedHeader for CrossOriginResourcePolicy {
53    fn name() -> &'static HeaderName {
54        &::rama_http_types::header::CROSS_ORIGIN_RESOURCE_POLICY
55    }
56}
57
58impl HeaderDecode for CrossOriginResourcePolicy {
59    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, Error> {
60        // Strict parse: unknown tokens fail rather than landing in
61        // `Unknown(...)`. CORP is a closed set per Fetch — anything
62        // else on the wire is malformed.
63        values
64            .just_one()
65            .and_then(|v| v.to_str().ok())
66            .and_then(|s| Self::strict_parse(s.trim()))
67            .ok_or_else(Error::invalid)
68    }
69}
70
71impl HeaderEncode for CrossOriginResourcePolicy {
72    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
73        values.extend(::std::iter::once(util::fmt(self)));
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::super::{test_decode, test_encode};
80    use super::*;
81
82    #[test]
83    fn round_trip_same_site() {
84        let map = test_encode(CrossOriginResourcePolicy::SameSite);
85        let raw = map
86            .get(CrossOriginResourcePolicy::name())
87            .expect("set")
88            .to_str()
89            .unwrap()
90            .to_owned();
91        assert_eq!(raw, "same-site");
92        assert_eq!(
93            test_decode::<CrossOriginResourcePolicy>(&[raw.as_str()]),
94            Some(CrossOriginResourcePolicy::SameSite),
95        );
96    }
97
98    #[test]
99    fn round_trip_same_origin() {
100        assert_eq!(
101            test_decode::<CrossOriginResourcePolicy>(&["same-origin"]),
102            Some(CrossOriginResourcePolicy::SameOrigin),
103        );
104    }
105
106    #[test]
107    fn round_trip_cross_origin() {
108        assert_eq!(
109            test_decode::<CrossOriginResourcePolicy>(&["cross-origin"]),
110            Some(CrossOriginResourcePolicy::CrossOrigin),
111        );
112    }
113
114    #[test]
115    fn parser_case_insensitive() {
116        assert_eq!(
117            test_decode::<CrossOriginResourcePolicy>(&["Same-Origin"]),
118            Some(CrossOriginResourcePolicy::SameOrigin),
119        );
120        assert_eq!(
121            test_decode::<CrossOriginResourcePolicy>(&["CROSS-ORIGIN"]),
122            Some(CrossOriginResourcePolicy::CrossOrigin),
123        );
124    }
125
126    #[test]
127    fn parser_tolerates_surrounding_whitespace() {
128        assert_eq!(
129            test_decode::<CrossOriginResourcePolicy>(&["  same-origin  "]),
130            Some(CrossOriginResourcePolicy::SameOrigin),
131        );
132    }
133
134    #[test]
135    fn parser_rejects_unknown_token() {
136        // The auto-generated `Unknown(String)` variant exists, but the
137        // decoder uses strict parsing — unknown tokens must fail.
138        assert_eq!(test_decode::<CrossOriginResourcePolicy>(&["nope"]), None);
139    }
140
141    #[test]
142    fn parser_rejects_empty_value() {
143        assert_eq!(test_decode::<CrossOriginResourcePolicy>(&[""]), None);
144    }
145}