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