hyper_sync/header/common/upgrade.rs
1use std::fmt::{self, Display};
2use std::str::FromStr;
3use unicase;
4
5header! {
6 /// `Upgrade` header, defined in [RFC7230](http://tools.ietf.org/html/rfc7230#section-6.7)
7 ///
8 /// The `Upgrade` header field is intended to provide a simple mechanism
9 /// for transitioning from HTTP/1.1 to some other protocol on the same
10 /// connection. A client MAY send a list of protocols in the Upgrade
11 /// header field of a request to invite the server to switch to one or
12 /// more of those protocols, in order of descending preference, before
13 /// sending the final response. A server MAY ignore a received Upgrade
14 /// header field if it wishes to continue using the current protocol on
15 /// that connection. Upgrade cannot be used to insist on a protocol
16 /// change.
17 ///
18 /// # ABNF
19 ///
20 /// ```text
21 /// Upgrade = 1#protocol
22 ///
23 /// protocol = protocol-name ["/" protocol-version]
24 /// protocol-name = token
25 /// protocol-version = token
26 /// ```
27 ///
28 /// # Example values
29 ///
30 /// * `HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11`
31 ///
32 /// # Examples
33 ///
34 /// ```
35 /// use hyper_sync::header::{Headers, Upgrade, Protocol, ProtocolName};
36 ///
37 /// let mut headers = Headers::new();
38 /// headers.set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]));
39 /// ```
40 ///
41 /// ```
42 /// use hyper_sync::header::{Headers, Upgrade, Protocol, ProtocolName};
43 ///
44 /// let mut headers = Headers::new();
45 /// headers.set(
46 /// Upgrade(vec![
47 /// Protocol::new(ProtocolName::Http, Some("2.0".to_owned())),
48 /// Protocol::new(ProtocolName::Unregistered("SHTTP".to_owned()),
49 /// Some("1.3".to_owned())),
50 /// Protocol::new(ProtocolName::Unregistered("IRC".to_owned()),
51 /// Some("6.9".to_owned())),
52 /// ])
53 /// );
54 /// ```
55 (Upgrade, "Upgrade") => (Protocol)+
56
57 test_upgrade {
58 // Testcase from the RFC
59 test_header!(
60 test1,
61 vec![b"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"],
62 Some(Upgrade(vec![
63 Protocol::new(ProtocolName::Http, Some("2.0".to_owned())),
64 Protocol::new(ProtocolName::Unregistered("SHTTP".to_owned()),
65 Some("1.3".to_owned())),
66 Protocol::new(ProtocolName::Unregistered("IRC".to_owned()), Some("6.9".to_owned())),
67 Protocol::new(ProtocolName::Unregistered("RTA".to_owned()), Some("x11".to_owned())),
68 ])));
69 // Own tests
70 test_header!(
71 test2, vec![b"websocket"],
72 Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
73 #[test]
74 fn test3() {
75 let x: ::Result<Upgrade> = Header::parse_header(&"WEbSOCKet".into());
76 assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
77 }
78 }
79}
80
81/// A protocol name used to identify a specific protocol. Names are case-sensitive
82/// except for the `WebSocket` value.
83#[derive(Clone, Debug, Eq, PartialEq)]
84pub enum ProtocolName {
85 /// `HTTP` value, Hypertext Transfer Protocol
86 Http,
87 /// `TLS` value, Transport Layer Security [RFC2817](http://tools.ietf.org/html/rfc2817)
88 Tls,
89 /// `WebSocket` value, matched case insensitively,Web Socket Protocol
90 /// [RFC6455](http://tools.ietf.org/html/rfc6455)
91 WebSocket,
92 /// `h2c` value, HTTP/2 over cleartext TCP
93 H2c,
94 /// Any other protocol name not known to hyper_sync
95 Unregistered(String),
96}
97
98impl FromStr for ProtocolName {
99 type Err = ();
100 fn from_str(s: &str) -> Result<ProtocolName, ()> {
101 Ok(match s {
102 "HTTP" => ProtocolName::Http,
103 "TLS" => ProtocolName::Tls,
104 "h2c" => ProtocolName::H2c,
105 _ => {
106 if unicase::eq_ascii(s, "websocket") {
107 ProtocolName::WebSocket
108 } else {
109 ProtocolName::Unregistered(s.to_owned())
110 }
111 }
112 })
113 }
114}
115
116impl Display for ProtocolName {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 f.write_str(match *self {
119 ProtocolName::Http => "HTTP",
120 ProtocolName::Tls => "TLS",
121 ProtocolName::WebSocket => "websocket",
122 ProtocolName::H2c => "h2c",
123 ProtocolName::Unregistered(ref s) => s,
124 })
125 }
126}
127
128/// Protocols that appear in the `Upgrade` header field
129#[derive(Clone, Debug, Eq, PartialEq)]
130pub struct Protocol {
131 /// The protocol identifier
132 pub name: ProtocolName,
133 /// The optional version of the protocol, often in the format "DIGIT.DIGIT" (e.g.. "1.2")
134 pub version: Option<String>,
135}
136
137impl Protocol {
138 /// Creates a new Protocol with the given name and version
139 pub fn new(name: ProtocolName, version: Option<String>) -> Protocol {
140 Protocol { name: name, version: version }
141 }
142}
143
144impl FromStr for Protocol {
145 type Err =();
146 fn from_str(s: &str) -> Result<Protocol, ()> {
147 let mut parts = s.splitn(2, '/');
148 Ok(Protocol::new(try!(parts.next().unwrap().parse()), parts.next().map(|x| x.to_owned())))
149 }
150}
151
152impl Display for Protocol {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 try!(fmt::Display::fmt(&self.name, f));
155 if let Some(ref version) = self.version {
156 try!(write!(f, "/{}", version));
157 }
158 Ok(())
159 }
160}
161
162bench_header!(bench, Upgrade, { vec![b"HTTP/2.0, RTA/x11, websocket".to_vec()] });