headers_ext/common/
origin.rs1use std::fmt;
2
3use bytes::Bytes;
4use http::uri::{self, Authority, Scheme, Uri};
5
6use util::{IterExt, TryFromValues};
7use ::{HeaderValue};
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash, Header)]
28pub struct Origin(OriginOrNull);
29
30#[derive(Clone, Debug, PartialEq, Eq, Hash)]
31enum OriginOrNull {
32 Origin(Scheme, Authority),
33 Null,
34}
35
36impl Origin {
37 pub const NULL: Origin = Origin(OriginOrNull::Null);
39
40 #[inline]
42 pub fn is_null(&self) -> bool {
43 match self.0 {
44 OriginOrNull::Null => true,
45 _ => false,
46 }
47 }
48
49 #[inline]
51 pub fn scheme(&self) -> &str {
52 match self.0 {
53 OriginOrNull::Origin(ref scheme, _) => scheme.as_str(),
54 OriginOrNull::Null => "",
55 }
56 }
57
58 #[inline]
60 pub fn hostname(&self) -> &str {
61 match self.0 {
62 OriginOrNull::Origin(_, ref auth) => auth.host(),
63 OriginOrNull::Null => "",
64 }
65 }
66
67 #[inline]
69 pub fn port(&self) -> Option<u16> {
70 match self.0 {
71 OriginOrNull::Origin(_, ref auth) => auth.port_part().map(|p| p.as_u16()),
72 OriginOrNull::Null => None,
73 }
74 }
75
76 pub fn try_from_parts(scheme: &str, host: &str, port: impl Into<Option<u16>>) -> Result<Self, InvalidOrigin> {
78
79 struct MaybePort(Option<u16>);
80
81 impl fmt::Display for MaybePort {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 if let Some(port) = self.0 {
84 write!(f, ":{}", port)
85 } else {
86 Ok(())
87 }
88 }
89 }
90
91 let bytes = Bytes::from(format!("{}://{}{}", scheme, host, MaybePort(port.into())));
92 HeaderValue::from_shared(bytes)
93 .ok()
94 .and_then(|val| Self::try_from_value(&val))
95 .ok_or_else(|| InvalidOrigin { _inner: () })
96 }
97
98 pub(super) fn try_from_value(value: &HeaderValue) -> Option<Self> {
100 OriginOrNull::try_from_value(value)
101 .map(Origin)
102 }
103
104 pub(super) fn into_value(&self) -> HeaderValue {
105 (&self.0).into()
106 }
107}
108
109impl fmt::Display for Origin {
110 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 match self.0 {
112 OriginOrNull::Origin(ref scheme, ref auth) => {
113 write!(f, "{}://{}", scheme, auth)
114 },
115 OriginOrNull::Null => f.write_str("null"),
116 }
117 }
118}
119
120error_type!(InvalidOrigin);
121
122impl OriginOrNull {
123 fn try_from_value(value: &HeaderValue) -> Option<Self> {
124 if value == "null" {
125 return Some(OriginOrNull::Null);
126 }
127
128 let bytes = Bytes::from(value.clone());
129
130 let uri = Uri::from_shared(bytes).ok()?;
131
132 let (scheme, auth) = match uri.into_parts() {
133 uri::Parts {
134 scheme: Some(scheme),
135 authority: Some(auth),
136 path_and_query: None,
137 ..
138 } => (scheme, auth),
139 uri::Parts {
140 scheme: Some(ref scheme),
141 authority: Some(ref auth),
142 path_and_query: Some(ref p),
143 ..
144 } if p == "/" => (scheme.clone(), auth.clone()),
145 _ => {
146 return None;
147 }
148 };
149
150 Some(OriginOrNull::Origin(scheme, auth))
151 }
152}
153
154impl TryFromValues for OriginOrNull {
155 fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
156 where
157 I: Iterator<Item = &'i HeaderValue>,
158 {
159 values
160 .just_one()
161 .and_then(OriginOrNull::try_from_value)
162 .ok_or_else(::Error::invalid)
163 }
164}
165
166impl<'a> From<&'a OriginOrNull> for HeaderValue {
167 fn from(origin: &'a OriginOrNull) -> HeaderValue {
168 match origin {
169 OriginOrNull::Origin(ref scheme, ref auth) => {
170 let s = format!("{}://{}", scheme, auth);
171 let bytes = Bytes::from(s);
172 HeaderValue::from_shared(bytes)
173 .expect("Scheme and Authority are valid header values")
174 },
175 OriginOrNull::Null => HeaderValue::from_static("null"),
178 }
179 }
180}
181
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use super::super::{test_decode, test_encode};
187
188
189 #[test]
190 fn origin() {
191 let s = "http://web-platform.test:8000";
192 let origin = test_decode::<Origin>(&[s]).unwrap();
193 assert_eq!(origin.scheme(), "http");
194 assert_eq!(origin.hostname(), "web-platform.test");
195 assert_eq!(origin.port(), Some(8000));
196
197 let headers = test_encode(origin);
198 assert_eq!(headers["origin"], s);
199 }
200
201 #[test]
202 fn null() {
203 assert_eq!(
204 test_decode::<Origin>(&["null"]),
205 Some(Origin::NULL),
206 );
207
208 let headers = test_encode(Origin::NULL);
209 assert_eq!(headers["origin"], "null");
210 }
211}
212