hyperx/header/common/
origin.rs

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