use std::fmt::{self, Display}; use std::str::FromStr; use unicase::UniCase; header! { #[doc="`Upgrade` header, defined in [RFC7230](http://tools.ietf.org/html/rfc7230#section-6.7)"] #[doc=""] #[doc="The `Upgrade` header field is intended to provide a simple mechanism"] #[doc="for transitioning from HTTP/1.1 to some other protocol on the same"] #[doc="connection. A client MAY send a list of protocols in the Upgrade"] #[doc="header field of a request to invite the server to switch to one or"] #[doc="more of those protocols, in order of descending preference, before"] #[doc="sending the final response. A server MAY ignore a received Upgrade"] #[doc="header field if it wishes to continue using the current protocol on"] #[doc="that connection. Upgrade cannot be used to insist on a protocol"] #[doc="change."] #[doc=""] #[doc="# ABNF"] #[doc="```plain"] #[doc="Upgrade = 1#protocol"] #[doc=""] #[doc="protocol = protocol-name [\"/\" protocol-version]"] #[doc="protocol-name = token"] #[doc="protocol-version = token"] #[doc="```"] #[doc=""] #[doc="# Example values"] #[doc="* `HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11`"] #[doc=""] #[doc="# Examples"] #[doc="```"] #[doc="use hyper::header::{Headers, Upgrade, Protocol, ProtocolName};"] #[doc=""] #[doc="let mut headers = Headers::new();"] #[doc="headers.set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]));"] #[doc="```"] #[doc="```"] #[doc="use hyper::header::{Headers, Upgrade, Protocol, ProtocolName};"] #[doc=""] #[doc="let mut headers = Headers::new();"] #[doc="headers.set("] #[doc=" Upgrade(vec!["] #[doc=" Protocol::new(ProtocolName::Http, Some(\"2.0\".to_owned())),"] #[doc=" Protocol::new(ProtocolName::Unregistered(\"SHTTP\".to_owned()), Some(\"1.3\".to_owned())),"] #[doc=" Protocol::new(ProtocolName::Unregistered(\"IRC\".to_owned()), Some(\"6.9\".to_owned())),"] #[doc=" Protocol::new(ProtocolName::Unregistered(\"RTA\".to_owned()), Some(\"x11\".to_owned())),"] #[doc=" ])"] #[doc=");"] #[doc="```"] (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(Upgrade(vec![ Protocol::new(ProtocolName::Http, Some("2.0".to_owned())), Protocol::new(ProtocolName::Unregistered("SHTTP".to_owned()), Some("1.3".to_owned())), Protocol::new(ProtocolName::Unregistered("IRC".to_owned()), Some("6.9".to_owned())), Protocol::new(ProtocolName::Unregistered("RTA".to_owned()), Some("x11".to_owned())), ]))); // Own tests test_header!( test2, vec![b"websocket"], Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); #[test] fn test3() { let x: ::Result = Header::parse_header(&[b"WEbSOCKet".to_vec()]); assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); } } } /// 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, /// `h2c` value, HTTP/2 over cleartext TCP H2c, /// Any other protocol name not known to hyper Unregistered(String), } impl FromStr for ProtocolName { type Err = (); fn from_str(s: &str) -> Result { Ok(match s { "HTTP" => ProtocolName::Http, "TLS" => ProtocolName::Tls, "h2c" => ProtocolName::H2c, _ => { if UniCase(s) == UniCase("websocket") { ProtocolName::WebSocket } else { ProtocolName::Unregistered(s.to_owned()) } } }) } } 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, } impl Protocol { /// Creates a new Protocol with the given name and version pub fn new(name: ProtocolName, version: Option) -> Protocol { Protocol { name: name, version: version } } } impl FromStr for Protocol { type Err =(); fn from_str(s: &str) -> Result { let mut parts = s.splitn(2, '/'); Ok(Protocol::new(try!(parts.next().unwrap().parse()), parts.next().map(|x| x.to_owned()))) } } 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()] });