Skip to content

Commit 01843f8

Browse files
puhrezseanmonstar
authored andcommitted
feat(headers): add origin header
Add an Origin header so users may properly send CORS requests Closes #651
1 parent 220d09f commit 01843f8

File tree

3 files changed

+145
-32
lines changed

3 files changed

+145
-32
lines changed

src/header/common/host.rs

+32-32
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use header::{Header};
22
use std::fmt;
3+
use std::str::FromStr;
34
use header::parsing::from_one_raw_str;
5+
use url::idna::domain_to_unicode;
46

57
/// The `Host` header.
68
///
@@ -48,38 +50,7 @@ impl Header for Host {
4850
}
4951

5052
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> {
51-
from_one_raw_str(raw).and_then(|mut s: String| {
52-
// FIXME: use rust-url to parse this
53-
// https://github.com/servo/rust-url/issues/42
54-
let idx = {
55-
let slice = &s[..];
56-
if slice.starts_with('[') {
57-
match slice.rfind(']') {
58-
Some(idx) => {
59-
if slice.len() > idx + 2 {
60-
Some(idx + 1)
61-
} else {
62-
None
63-
}
64-
}
65-
None => return Err(::Error::Header) // this is a bad ipv6 address...
66-
}
67-
} else {
68-
slice.rfind(':')
69-
}
70-
};
71-
72-
let port = idx.and_then(|idx| s[idx + 1..].parse().ok());
73-
74-
if let Some(idx) = idx {
75-
s.truncate(idx)
76-
}
77-
78-
Ok(Host {
79-
hostname: s,
80-
port: port
81-
})
82-
})
53+
from_one_raw_str(raw)
8354
}
8455

8556
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -90,6 +61,35 @@ impl Header for Host {
9061
}
9162
}
9263

64+
impl fmt::Display for Host {
65+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66+
self.fmt_header(f)
67+
}
68+
}
69+
70+
impl FromStr for Host {
71+
type Err = ::Error;
72+
73+
fn from_str(s: &str) -> ::Result<Host> {
74+
let (host_port, res) = domain_to_unicode(s);
75+
if res.is_err() {
76+
return Err(::Error::Header)
77+
}
78+
let idx = host_port.rfind(':');
79+
let port = idx.and_then(
80+
|idx| s[idx + 1..].parse().ok()
81+
);
82+
let hostname = match idx {
83+
None => host_port,
84+
Some(idx) => host_port[..idx].to_owned()
85+
};
86+
Ok(Host {
87+
hostname: hostname,
88+
port: port
89+
})
90+
}
91+
}
92+
9393
#[cfg(test)]
9494
mod tests {
9595
use super::Host;

src/header/common/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub use self::if_unmodified_since::IfUnmodifiedSince;
4343
pub use self::if_range::IfRange;
4444
pub use self::last_modified::LastModified;
4545
pub use self::location::Location;
46+
pub use self::origin::Origin;
4647
pub use self::pragma::Pragma;
4748
pub use self::prefer::{Prefer, Preference};
4849
pub use self::preference_applied::PreferenceApplied;
@@ -402,6 +403,7 @@ mod if_range;
402403
mod if_unmodified_since;
403404
mod last_modified;
404405
mod location;
406+
mod origin;
405407
mod pragma;
406408
mod prefer;
407409
mod preference_applied;

src/header/common/origin.rs

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use header::{Header, Host};
2+
use std::fmt;
3+
use std::str::FromStr;
4+
use header::parsing::from_one_raw_str;
5+
6+
/// The `Origin` header.
7+
///
8+
/// 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.
9+
/// This header is often used to inform recipients of the security context of where the request was initiated.
10+
///
11+
///
12+
/// Following the spec, https://fetch.spec.whatwg.org/#origin-header, the value of this header is composed of
13+
/// a String (scheme), header::Host (host/port)
14+
///
15+
/// # Examples
16+
/// ```
17+
/// use hyper::header::{Headers, Origin};
18+
///
19+
/// let mut headers = Headers::new();
20+
/// headers.set(
21+
/// Origin::new("http", "hyper.rs", None)
22+
/// );
23+
/// ```
24+
/// ```
25+
/// use hyper::header::{Headers, Origin};
26+
///
27+
/// let mut headers = Headers::new();
28+
/// headers.set(
29+
/// Origin::new("https", "wikipedia.org", Some(443))
30+
/// );
31+
/// ```
32+
33+
#[derive(Clone, Debug)]
34+
pub struct Origin {
35+
/// The scheme, such as http or https
36+
pub scheme: String,
37+
/// The host, such as Host{hostname: "hyper.rs".to_owned(), port: None}
38+
pub host: Host,
39+
}
40+
41+
impl Origin {
42+
/// Creates a new `Origin` header.
43+
pub fn new<S: Into<String>, H: Into<String>>(scheme: S, hostname: H, port: Option<u16>) -> Origin{
44+
Origin {
45+
scheme: scheme.into(),
46+
host: Host {
47+
hostname: hostname.into(),
48+
port: port
49+
}
50+
}
51+
}
52+
}
53+
54+
impl Header for Origin {
55+
fn header_name() -> &'static str {
56+
static NAME: &'static str = "Origin";
57+
NAME
58+
}
59+
60+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Origin> {
61+
from_one_raw_str(raw)
62+
}
63+
64+
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
65+
write!(f, "{}://{}", self.scheme, self.host)
66+
}
67+
}
68+
69+
impl FromStr for Origin {
70+
type Err = ::Error;
71+
72+
fn from_str(s: &str) -> ::Result<Origin> {
73+
let idx = match s.find("://") {
74+
Some(idx) => idx,
75+
None => return Err(::Error::Header)
76+
};
77+
// idx + 3 because thats how long "://" is
78+
let (scheme, etc) = (&s[..idx], &s[idx + 3..]);
79+
let host = try!(Host::from_str(etc));
80+
81+
82+
Ok(Origin{
83+
scheme: scheme.to_owned(),
84+
host: host
85+
})
86+
}
87+
}
88+
89+
impl PartialEq for Origin {
90+
fn eq(&self, other: &Origin) -> bool {
91+
self.scheme == other.scheme && self.host == other.host
92+
}
93+
}
94+
95+
96+
#[cfg(test)]
97+
mod tests {
98+
use super::Origin;
99+
use header::Header;
100+
101+
#[test]
102+
fn test_origin() {
103+
let origin = Header::parse_header([b"http://foo.com".to_vec()].as_ref());
104+
assert_eq!(origin.ok(), Some(Origin::new("http", "foo.com", None)));
105+
106+
let origin = Header::parse_header([b"https://foo.com:443".to_vec()].as_ref());
107+
assert_eq!(origin.ok(), Some(Origin::new("https", "foo.com", Some(443))));
108+
}
109+
}
110+
111+
bench_header!(bench, Origin, { vec![b"https://foo.com".to_vec()] });

0 commit comments

Comments
 (0)