Merge pull request #499 from pyfisch/upgradeprotocol
feat(headers): Parse Upgrade header protocols further
This commit is contained in:
@@ -44,7 +44,7 @@ pub use self::referer::Referer;
|
||||
pub use self::server::Server;
|
||||
pub use self::set_cookie::SetCookie;
|
||||
pub use self::transfer_encoding::TransferEncoding;
|
||||
pub use self::upgrade::{Upgrade, Protocol};
|
||||
pub use self::upgrade::{Upgrade, Protocol, ProtocolName};
|
||||
pub use self::user_agent::UserAgent;
|
||||
pub use self::vary::Vary;
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::fmt::{self, Display};
|
||||
use std::str::FromStr;
|
||||
use unicase::UniCase;
|
||||
|
||||
use self::Protocol::{WebSocket, ProtocolExt};
|
||||
|
||||
header! {
|
||||
#[doc="`Upgrade` header, defined in [RFC7230](http://tools.ietf.org/html/rfc7230#section-6.7)"]
|
||||
#[doc=""]
|
||||
@@ -31,47 +29,107 @@ header! {
|
||||
(Upgrade, "Upgrade") => (Protocol)+
|
||||
|
||||
test_upgrade {
|
||||
// Testcase from the RFC
|
||||
test_header!(
|
||||
test1,
|
||||
vec![b"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"],
|
||||
Some(HeaderField(vec![
|
||||
Protocol::ProtocolExt("HTTP/2.0".to_string()),
|
||||
Protocol::ProtocolExt("SHTTP/1.3".to_string()),
|
||||
Protocol::ProtocolExt("IRC/6.9".to_string()),
|
||||
Protocol::ProtocolExt("RTA/x11".to_string()),
|
||||
Some(Upgrade(vec![
|
||||
Protocol::new(ProtocolName::Http, Some("2.0".to_string())),
|
||||
Protocol::new(ProtocolName::Unregistered("SHTTP".to_string()), Some("1.3".to_string())),
|
||||
Protocol::new(ProtocolName::Unregistered("IRC".to_string()), Some("6.9".to_string())),
|
||||
Protocol::new(ProtocolName::Unregistered("RTA".to_string()), Some("x11".to_string())),
|
||||
])));
|
||||
// Own tests
|
||||
test_header!(
|
||||
test2, vec![b"websocket"],
|
||||
Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
||||
#[test]
|
||||
fn test3() {
|
||||
let x: Option<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]);
|
||||
assert_eq!(x, Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Protocol values that can appear in the Upgrade header.
|
||||
// TODO: Parse version part seperately
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
/// The websocket protocol.
|
||||
/// A protocol name used to identify a spefic protocol. Names are case-sensitive
|
||||
/// except for the `WebSocket` value.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ProtocolName {
|
||||
/// `HTTP` value, Hypertext Transfer Protocol
|
||||
Http,
|
||||
/// `TLS` value, Transport Layer Security [RFC2817](http://tools.ietf.org/html/rfc2817)
|
||||
Tls,
|
||||
/// `WebSocket` value, matched case insensitively,Web Socket Protocol
|
||||
/// [RFC6455](http://tools.ietf.org/html/rfc6455)
|
||||
WebSocket,
|
||||
/// Some other less common protocol.
|
||||
ProtocolExt(String),
|
||||
/// `h2c` value, HTTP/2 over cleartext TCP
|
||||
H2c,
|
||||
/// Any other protocol name not known to hyper
|
||||
Unregistered(String),
|
||||
}
|
||||
|
||||
impl FromStr for Protocol {
|
||||
impl FromStr for ProtocolName {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Protocol, ()> {
|
||||
if UniCase(s) == UniCase("websocket") {
|
||||
Ok(WebSocket)
|
||||
}
|
||||
else {
|
||||
Ok(ProtocolExt(s.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Protocol {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", match *self {
|
||||
WebSocket => "websocket",
|
||||
ProtocolExt(ref s) => s.as_ref()
|
||||
fn from_str(s: &str) -> Result<ProtocolName, ()> {
|
||||
Ok(match s {
|
||||
"HTTP" => ProtocolName::Http,
|
||||
"TLS" => ProtocolName::Tls,
|
||||
"h2c" => ProtocolName::H2c,
|
||||
_ => {
|
||||
if UniCase(s) == UniCase("websocket") {
|
||||
ProtocolName::WebSocket
|
||||
} else {
|
||||
ProtocolName::Unregistered(s.to_string())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ProtocolName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
ProtocolName::Http => "HTTP",
|
||||
ProtocolName::Tls => "TLS",
|
||||
ProtocolName::WebSocket => "websocket",
|
||||
ProtocolName::H2c => "h2c",
|
||||
ProtocolName::Unregistered(ref s) => s,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Protocols that appear in the `Upgrade` header field
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Protocol {
|
||||
/// The protocol identifier
|
||||
pub name: ProtocolName,
|
||||
/// The optional version of the protocol, often in the format "DIGIT.DIGIT" (e.g.. "1.2")
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
/// Creates a new Protocol with the given name and version
|
||||
pub fn new(name: ProtocolName, version: Option<String>) -> Protocol {
|
||||
Protocol { name: name, version: version }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Protocol {
|
||||
type Err =();
|
||||
fn from_str(s: &str) -> Result<Protocol, ()> {
|
||||
let mut parts = s.splitn(2, '/');
|
||||
Ok(Protocol::new(try!(parts.next().unwrap().parse()), parts.next().map(|x| x.to_string())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Protocol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(fmt::Display::fmt(&self.name, f));
|
||||
if let Some(ref version) = self.version {
|
||||
try!(write!(f, "/{}", version));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
bench_header!(bench, Upgrade, { vec![b"HTTP/2.0, RTA/x11, websocket".to_vec()] });
|
||||
|
||||
Reference in New Issue
Block a user