Skip to content

Commit 33146bd

Browse files
committed
Merge pull request #499 from pyfisch/upgradeprotocol
feat(headers): Parse Upgrade header protocols further
2 parents b916a7b + f47d11b commit 33146bd

File tree

2 files changed

+87
-29
lines changed

2 files changed

+87
-29
lines changed

src/header/common/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub use self::referer::Referer;
4444
pub use self::server::Server;
4545
pub use self::set_cookie::SetCookie;
4646
pub use self::transfer_encoding::TransferEncoding;
47-
pub use self::upgrade::{Upgrade, Protocol};
47+
pub use self::upgrade::{Upgrade, Protocol, ProtocolName};
4848
pub use self::user_agent::UserAgent;
4949
pub use self::vary::Vary;
5050

src/header/common/upgrade.rs

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
use std::fmt;
1+
use std::fmt::{self, Display};
22
use std::str::FromStr;
33
use unicase::UniCase;
44

5-
use self::Protocol::{WebSocket, ProtocolExt};
6-
75
header! {
86
#[doc="`Upgrade` header, defined in [RFC7230](http://tools.ietf.org/html/rfc7230#section-6.7)"]
97
#[doc=""]
@@ -31,47 +29,107 @@ header! {
3129
(Upgrade, "Upgrade") => (Protocol)+
3230

3331
test_upgrade {
32+
// Testcase from the RFC
3433
test_header!(
3534
test1,
3635
vec![b"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"],
37-
Some(HeaderField(vec![
38-
Protocol::ProtocolExt("HTTP/2.0".to_string()),
39-
Protocol::ProtocolExt("SHTTP/1.3".to_string()),
40-
Protocol::ProtocolExt("IRC/6.9".to_string()),
41-
Protocol::ProtocolExt("RTA/x11".to_string()),
36+
Some(Upgrade(vec![
37+
Protocol::new(ProtocolName::Http, Some("2.0".to_string())),
38+
Protocol::new(ProtocolName::Unregistered("SHTTP".to_string()), Some("1.3".to_string())),
39+
Protocol::new(ProtocolName::Unregistered("IRC".to_string()), Some("6.9".to_string())),
40+
Protocol::new(ProtocolName::Unregistered("RTA".to_string()), Some("x11".to_string())),
4241
])));
42+
// Own tests
43+
test_header!(
44+
test2, vec![b"websocket"],
45+
Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
46+
#[test]
47+
fn test3() {
48+
let x: Option<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]);
49+
assert_eq!(x, Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
50+
}
4351
}
4452
}
4553

46-
/// Protocol values that can appear in the Upgrade header.
47-
// TODO: Parse version part seperately
48-
#[derive(Clone, PartialEq, Debug)]
49-
pub enum Protocol {
50-
/// The websocket protocol.
54+
/// A protocol name used to identify a spefic protocol. Names are case-sensitive
55+
/// except for the `WebSocket` value.
56+
#[derive(Clone, Debug, Eq, PartialEq)]
57+
pub enum ProtocolName {
58+
/// `HTTP` value, Hypertext Transfer Protocol
59+
Http,
60+
/// `TLS` value, Transport Layer Security [RFC2817](http://tools.ietf.org/html/rfc2817)
61+
Tls,
62+
/// `WebSocket` value, matched case insensitively,Web Socket Protocol
63+
/// [RFC6455](http://tools.ietf.org/html/rfc6455)
5164
WebSocket,
52-
/// Some other less common protocol.
53-
ProtocolExt(String),
65+
/// `h2c` value, HTTP/2 over cleartext TCP
66+
H2c,
67+
/// Any other protocol name not known to hyper
68+
Unregistered(String),
5469
}
5570

56-
impl FromStr for Protocol {
71+
impl FromStr for ProtocolName {
5772
type Err = ();
58-
fn from_str(s: &str) -> Result<Protocol, ()> {
59-
if UniCase(s) == UniCase("websocket") {
60-
Ok(WebSocket)
61-
}
62-
else {
63-
Ok(ProtocolExt(s.to_string()))
64-
}
73+
fn from_str(s: &str) -> Result<ProtocolName, ()> {
74+
Ok(match s {
75+
"HTTP" => ProtocolName::Http,
76+
"TLS" => ProtocolName::Tls,
77+
"h2c" => ProtocolName::H2c,
78+
_ => {
79+
if UniCase(s) == UniCase("websocket") {
80+
ProtocolName::WebSocket
81+
} else {
82+
ProtocolName::Unregistered(s.to_string())
83+
}
84+
}
85+
})
6586
}
6687
}
6788

68-
impl fmt::Display for Protocol {
69-
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
70-
write!(fmt, "{}", match *self {
71-
WebSocket => "websocket",
72-
ProtocolExt(ref s) => s.as_ref()
89+
impl Display for ProtocolName {
90+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91+
f.write_str(match *self {
92+
ProtocolName::Http => "HTTP",
93+
ProtocolName::Tls => "TLS",
94+
ProtocolName::WebSocket => "websocket",
95+
ProtocolName::H2c => "h2c",
96+
ProtocolName::Unregistered(ref s) => s,
7397
})
7498
}
7599
}
76100

101+
/// Protocols that appear in the `Upgrade` header field
102+
#[derive(Clone, Debug, Eq, PartialEq)]
103+
pub struct Protocol {
104+
/// The protocol identifier
105+
pub name: ProtocolName,
106+
/// The optional version of the protocol, often in the format "DIGIT.DIGIT" (e.g.. "1.2")
107+
pub version: Option<String>,
108+
}
109+
110+
impl Protocol {
111+
/// Creates a new Protocol with the given name and version
112+
pub fn new(name: ProtocolName, version: Option<String>) -> Protocol {
113+
Protocol { name: name, version: version }
114+
}
115+
}
116+
117+
impl FromStr for Protocol {
118+
type Err =();
119+
fn from_str(s: &str) -> Result<Protocol, ()> {
120+
let mut parts = s.splitn(2, '/');
121+
Ok(Protocol::new(try!(parts.next().unwrap().parse()), parts.next().map(|x| x.to_string())))
122+
}
123+
}
124+
125+
impl Display for Protocol {
126+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127+
try!(fmt::Display::fmt(&self.name, f));
128+
if let Some(ref version) = self.version {
129+
try!(write!(f, "/{}", version));
130+
}
131+
Ok(())
132+
}
133+
}
134+
77135
bench_header!(bench, Upgrade, { vec![b"HTTP/2.0, RTA/x11, websocket".to_vec()] });

0 commit comments

Comments
 (0)