hyper_sync/header/common/
origin.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::str::FromStr;
4
5use header::{Header, Raw, Host};
6use header::parsing::from_one_raw_str;
7
8/// The `Origin` header.
9///
10/// The `Origin` header is a version of the `Referer` header that is used for all HTTP fetches and `POST`s whose CORS flag is set.
11/// This header is often used to inform recipients of the security context of where the request was initiated.
12///
13/// Following the spec, [https://fetch.spec.whatwg.org/#origin-header][url], the value of this header is composed of
14/// a String (scheme), header::Host (host/port)
15///
16/// [url]: https://fetch.spec.whatwg.org/#origin-header
17///
18/// # Examples
19///
20/// ```
21/// use hyper_sync::header::{Headers, Origin};
22///
23/// let mut headers = Headers::new();
24/// headers.set(
25///     Origin::new("http", "hyper.rs", None)
26/// );
27/// ```
28///
29/// ```
30/// use hyper_sync::header::{Headers, Origin};
31///
32/// let mut headers = Headers::new();
33/// headers.set(
34///     Origin::new("https", "wikipedia.org", Some(443))
35/// );
36/// ```
37#[derive(PartialEq, Clone, Debug)]
38pub struct Origin(OriginOrNull);
39
40#[derive(PartialEq, Clone, Debug)]
41enum OriginOrNull {
42    Origin {
43        /// The scheme, such as http or https
44        scheme: Cow<'static,str>,
45        /// The host, such as Host{hostname: "hyper.rs".to_owned(), port: None}
46        host: Host,
47    },
48    Null,
49}
50
51impl Origin {
52    /// Creates a new `Origin` header.
53    pub fn new<S: Into<Cow<'static,str>>, H: Into<Cow<'static,str>>>(scheme: S, hostname: H, port: Option<u16>) -> Origin{
54        Origin(OriginOrNull::Origin {
55            scheme: scheme.into(),
56            host: Host::new(hostname, port),
57        })
58    }
59
60    /// Creates a `Null` `Origin` header.
61    pub fn null() -> Origin {
62        Origin(OriginOrNull::Null)
63    }
64
65    /// Checks if `Origin` is `Null`.
66    pub fn is_null(&self) -> bool {
67        match self {
68            &Origin(OriginOrNull::Null) => true,
69            _ => false,
70        }
71    }
72
73    /// The scheme, such as http or https.
74    ///
75    /// ```
76    /// use hyper_sync::header::Origin;
77    /// let origin = Origin::new("https", "foo.com", Some(443));
78    /// assert_eq!(origin.scheme(), Some("https"));
79    /// ```
80    pub fn scheme(&self) -> Option<&str> {
81        match self {
82            &Origin(OriginOrNull::Origin { ref scheme, .. }) => Some(&scheme),
83            _ => None,
84        }
85    }
86
87    /// The host, such as `Host { hostname: "hyper.rs".to_owned(), port: None}`.
88    ///
89    /// ```
90    /// use hyper_sync::header::{Origin,Host};
91    /// let origin = Origin::new("https", "foo.com", Some(443));
92    /// assert_eq!(origin.host(), Some(&Host::new("foo.com", Some(443))));
93    /// ```
94    pub fn host(&self) -> Option<&Host> {
95        match self {
96            &Origin(OriginOrNull::Origin { ref host, .. }) => Some(&host),
97            _ => None,
98        }
99    }
100}
101
102impl Header for Origin {
103    fn header_name() -> &'static str {
104        static NAME: &'static str = "Origin";
105        NAME
106    }
107
108    fn parse_header(raw: &Raw) -> ::Result<Origin> {
109        from_one_raw_str(raw)
110    }
111
112    fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
113        f.fmt_line(self)
114    }
115}
116
117static HTTP : &'static str = "http";
118static HTTPS : &'static str = "https";
119
120impl FromStr for Origin {
121    type Err = ::Error;
122
123    fn from_str(s: &str) -> ::Result<Origin> {
124        let idx = match s.find("://") {
125            Some(idx) => idx,
126            None => return Err(::Error::Header)
127        };
128        // idx + 3 because that's how long "://" is
129        let (scheme, etc) = (&s[..idx], &s[idx + 3..]);
130        let host = try!(Host::from_str(etc));
131        let scheme = match scheme {
132            "http"  => Cow::Borrowed(HTTP),
133            "https" => Cow::Borrowed(HTTPS),
134            s       => Cow::Owned(s.to_owned())
135        };
136
137        Ok(Origin(OriginOrNull::Origin {
138            scheme: scheme,
139            host: host
140        }))
141    }
142}
143
144impl fmt::Display for Origin {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        match self.0 {
147            OriginOrNull::Origin { ref scheme, ref host } => write!(f, "{}://{}", scheme, host),
148            // Serialized as "null" per ASCII serialization of an origin
149            // https://html.spec.whatwg.org/multipage/browsers.html#ascii-serialisation-of-an-origin
150            OriginOrNull::Null => f.write_str("null")
151        }
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::Origin;
158    use header::Header;
159    use std::borrow::Cow;
160
161    macro_rules! assert_borrowed{
162        ($expr : expr) => {
163            match $expr {
164                Cow::Owned(ref v) => panic!("assertion failed: `{}` owns {:?}", stringify!($expr), v),
165                _ => {}
166            }
167        }
168    }
169
170    #[test]
171    fn test_origin() {
172        let origin : Origin = Header::parse_header(&vec![b"http://foo.com".to_vec()].into()).unwrap();
173        assert_eq!(&origin, &Origin::new("http", "foo.com", None));
174        assert_borrowed!(origin.scheme().unwrap().into());
175
176        let origin : Origin = Header::parse_header(&vec![b"https://foo.com:443".to_vec()].into()).unwrap();
177        assert_eq!(&origin, &Origin::new("https", "foo.com", Some(443)));
178        assert_borrowed!(origin.scheme().unwrap().into());
179    }
180}
181
182bench_header!(bench, Origin, { vec![b"https://foo.com".to_vec()] });