rama_http_headers/common/
access_control_allow_origin.rs1use rama_http_types::HeaderValue;
2use std::convert::TryFrom;
3
4use super::origin::Origin;
5use crate::Error;
6use crate::util::{IterExt, TryFromValues};
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash)]
37pub struct AccessControlAllowOrigin(OriginOrAny);
38
39impl crate::TypedHeader for AccessControlAllowOrigin {
40 fn name() -> &'static ::rama_http_types::header::HeaderName {
41 &::rama_http_types::header::ACCESS_CONTROL_ALLOW_ORIGIN
42 }
43}
44
45impl crate::HeaderDecode for AccessControlAllowOrigin {
46 fn decode<'i, I>(values: &mut I) -> Result<Self, crate::Error>
47 where
48 I: Iterator<Item = &'i ::rama_http_types::header::HeaderValue>,
49 {
50 crate::util::TryFromValues::try_from_values(values).map(AccessControlAllowOrigin)
51 }
52}
53
54impl crate::HeaderEncode for AccessControlAllowOrigin {
55 fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
56 match &self.0 {
57 OriginOrAny::Origin(origin) => origin.encode(values),
58 OriginOrAny::Any => values.extend(::std::iter::once(HeaderValue::from_static("*"))),
59 }
60 }
61}
62
63#[derive(Clone, Debug, PartialEq, Eq, Hash)]
64enum OriginOrAny {
65 Origin(Origin),
66 Any,
68}
69
70impl AccessControlAllowOrigin {
71 pub const ANY: Self = Self(OriginOrAny::Any);
73 pub const NULL: Self = Self(OriginOrAny::Origin(Origin::NULL));
75
76 pub fn origin(&self) -> Option<&Origin> {
78 match self.0 {
79 OriginOrAny::Origin(ref origin) => Some(origin),
80 OriginOrAny::Any => None,
81 }
82 }
83
84 pub fn try_from_origin_header_value(header_value: &HeaderValue) -> Option<Self> {
85 let origin = Origin::try_from_header_value(header_value)?;
86 Some(Self(OriginOrAny::Origin(origin)))
87 }
88}
89
90impl TryFrom<&str> for AccessControlAllowOrigin {
91 type Error = Error;
92
93 fn try_from(s: &str) -> Result<Self, Error> {
94 let header_value = HeaderValue::from_str(s).map_err(|_e| Error::invalid())?;
95 let origin = OriginOrAny::try_from(&header_value)?;
96 Ok(Self(origin))
97 }
98}
99
100impl TryFrom<&HeaderValue> for OriginOrAny {
101 type Error = Error;
102
103 fn try_from(header_value: &HeaderValue) -> Result<Self, Error> {
104 Origin::try_from_header_value(header_value)
105 .map(OriginOrAny::Origin)
106 .ok_or_else(Error::invalid)
107 }
108}
109
110impl TryFromValues for OriginOrAny {
111 fn try_from_values<'i, I>(values: &mut I) -> Result<Self, Error>
112 where
113 I: Iterator<Item = &'i HeaderValue>,
114 {
115 values
116 .just_one()
117 .and_then(|value| {
118 if value == "*" {
119 return Some(Self::Any);
120 }
121
122 Origin::try_from_header_value(value).map(OriginOrAny::Origin)
123 })
124 .ok_or_else(Error::invalid)
125 }
126}
127
128#[cfg(test)]
129mod tests {
130
131 use super::super::{test_decode, test_encode};
132 use super::*;
133
134 #[test]
135 fn origin() {
136 let s = "http://web-platform.test:8000";
137
138 let allow_origin = test_decode::<AccessControlAllowOrigin>(&[s]).unwrap();
139 {
140 let origin = allow_origin.origin().unwrap();
141 assert_eq!(origin.scheme(), "http");
142 assert_eq!(origin.hostname(), "web-platform.test");
143 assert_eq!(origin.port(), Some(8000));
144 }
145
146 let headers = test_encode(allow_origin);
147 assert_eq!(headers["access-control-allow-origin"], s);
148 }
149
150 #[test]
151 fn try_from_origin() {
152 let s = "http://web-platform.test:8000";
153
154 let allow_origin = AccessControlAllowOrigin::try_from(s).unwrap();
155 {
156 let origin = allow_origin.origin().unwrap();
157 assert_eq!(origin.scheme(), "http");
158 assert_eq!(origin.hostname(), "web-platform.test");
159 assert_eq!(origin.port(), Some(8000));
160 }
161
162 let headers = test_encode(allow_origin);
163 assert_eq!(headers["access-control-allow-origin"], s);
164 }
165
166 #[test]
167 fn any() {
168 let allow_origin = test_decode::<AccessControlAllowOrigin>(&["*"]).unwrap();
169 assert_eq!(allow_origin, AccessControlAllowOrigin::ANY);
170
171 let headers = test_encode(allow_origin);
172 assert_eq!(headers["access-control-allow-origin"], "*");
173 }
174
175 #[test]
176 fn null() {
177 let allow_origin = test_decode::<AccessControlAllowOrigin>(&["null"]).unwrap();
178 assert_eq!(allow_origin, AccessControlAllowOrigin::NULL);
179
180 let headers = test_encode(allow_origin);
181 assert_eq!(headers["access-control-allow-origin"], "null");
182 }
183}