feat(headers): support Opaque origin headers (#1147)

Add support for Opaque origin header, serializing it to `null`.
https://html.spec.whatwg.org/multipage/browsers.html#concept-origin

Closes #1065

BREAKING CHANGE: `Origin.scheme` and `Origin.host` now return `Option`s,
  since the `Origin` could be `null`.
This commit is contained in:
Alexey Zabelin
2017-04-24 13:05:56 -04:00
committed by Sean McArthur
parent 1cd8ea36f3
commit 414859978b

View File

@@ -31,20 +31,39 @@ use header::parsing::from_one_raw_str;
/// ); /// );
/// ``` /// ```
#[derive(Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub struct Origin { pub struct Origin(OriginOrNull);
/// The scheme, such as http or https
scheme: Cow<'static,str>, #[derive(PartialEq, Clone, Debug)]
/// The host, such as Host{hostname: "hyper.rs".to_owned(), port: None} enum OriginOrNull {
host: Host, Origin {
/// The scheme, such as http or https
scheme: Cow<'static,str>,
/// The host, such as Host{hostname: "hyper.rs".to_owned(), port: None}
host: Host,
},
Null,
} }
impl Origin { impl Origin {
/// Creates a new `Origin` header. /// Creates a new `Origin` header.
pub fn new<S: Into<Cow<'static,str>>, H: Into<Cow<'static,str>>>(scheme: S, hostname: H, port: Option<u16>) -> Origin{ pub fn new<S: Into<Cow<'static,str>>, H: Into<Cow<'static,str>>>(scheme: S, hostname: H, port: Option<u16>) -> Origin{
Origin { Origin(OriginOrNull::Origin {
scheme: scheme.into(), scheme: scheme.into(),
host: Host::new(hostname, port), host: Host::new(hostname, port),
})
}
/// Creates a `Null` `Origin` header.
pub fn null() -> Origin {
Origin(OriginOrNull::Null)
}
/// Checks if `Origin` is `Null`.
pub fn is_null(&self) -> bool {
match self {
&Origin(OriginOrNull::Null) => true,
_ => false,
} }
} }
@@ -52,20 +71,26 @@ impl Origin {
/// ``` /// ```
/// use hyper::header::Origin; /// use hyper::header::Origin;
/// let origin = Origin::new("https", "foo.com", Some(443)); /// let origin = Origin::new("https", "foo.com", Some(443));
/// assert_eq!(origin.scheme(), "https"); /// assert_eq!(origin.scheme(), Some("https"));
/// ``` /// ```
pub fn scheme(&self) -> &str { pub fn scheme(&self) -> Option<&str> {
&(self.scheme) match self {
&Origin(OriginOrNull::Origin { ref scheme, .. }) => Some(&scheme),
_ => None,
}
} }
/// The host, such as Host{hostname: "hyper.rs".to_owned(), port: None} /// The host, such as Host{hostname: "hyper.rs".to_owned(), port: None}
/// ``` /// ```
/// use hyper::header::{Origin,Host}; /// use hyper::header::{Origin,Host};
/// let origin = Origin::new("https", "foo.com", Some(443)); /// let origin = Origin::new("https", "foo.com", Some(443));
/// assert_eq!(origin.host(), &Host::new("foo.com", Some(443))); /// assert_eq!(origin.host(), Some(&Host::new("foo.com", Some(443))));
/// ``` /// ```
pub fn host(&self) -> &Host { pub fn host(&self) -> Option<&Host> {
&(self.host) match self {
&Origin(OriginOrNull::Origin { ref host, .. }) => Some(&host),
_ => None,
}
} }
} }
@@ -104,26 +129,24 @@ impl FromStr for Origin {
s => Cow::Owned(s.to_owned()) s => Cow::Owned(s.to_owned())
}; };
Ok(Origin{ Ok(Origin(OriginOrNull::Origin {
scheme: scheme, scheme: scheme,
host: host host: host
}) }))
} }
} }
impl fmt::Display for Origin { impl fmt::Display for Origin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}://{}", self.scheme, self.host) match self {
&Origin(OriginOrNull::Origin { ref scheme, ref host }) => write!(f, "{}://{}", scheme, host),
/// Serialized as "null" per ASCII serialization of an origin
/// https://html.spec.whatwg.org/multipage/browsers.html#ascii-serialisation-of-an-origin
_ => write!(f, "null")
}
} }
} }
impl PartialEq for Origin {
fn eq(&self, other: &Origin) -> bool {
self.scheme == other.scheme && self.host == other.host
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Origin; use super::Origin;
@@ -143,11 +166,11 @@ mod tests {
fn test_origin() { fn test_origin() {
let origin : Origin = Header::parse_header(&vec![b"http://foo.com".to_vec()].into()).unwrap(); let origin : Origin = Header::parse_header(&vec![b"http://foo.com".to_vec()].into()).unwrap();
assert_eq!(&origin, &Origin::new("http", "foo.com", None)); assert_eq!(&origin, &Origin::new("http", "foo.com", None));
assert_borrowed!(origin.scheme); assert_borrowed!(origin.scheme().unwrap().into());
let origin : Origin = Header::parse_header(&vec![b"https://foo.com:443".to_vec()].into()).unwrap(); let origin : Origin = Header::parse_header(&vec![b"https://foo.com:443".to_vec()].into()).unwrap();
assert_eq!(&origin, &Origin::new("https", "foo.com", Some(443))); assert_eq!(&origin, &Origin::new("https", "foo.com", Some(443)));
assert_borrowed!(origin.scheme); assert_borrowed!(origin.scheme().unwrap().into());
} }
} }