cogo_http/header/common/upgrade.rs
1use std::fmt::{self, Display};
2use std::str::FromStr;
3use unicase::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 /// ```plain
20 /// Upgrade = 1#protocol
21 ///
22 /// protocol = protocol-name ["/" protocol-version]
23 /// protocol-name = token
24 /// protocol-version = token
25 /// ```
26 ///
27 /// # Example values
28 /// * `HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11`
29 ///
30 /// # Examples
31 /// ```
32 /// use cogo_http::header::{Headers, Upgrade, Protocol, ProtocolName};
33 ///
34 /// let mut headers = Headers::new();
35 /// headers.set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]));
36 /// ```
37 /// ```
38 /// use cogo_http::header::{Headers, Upgrade, Protocol, ProtocolName};
39 ///
40 /// let mut headers = Headers::new();
41 /// headers.set(
42 /// Upgrade(vec![
43 /// Protocol::new(ProtocolName::Http, Some("2.0".to_owned())),
44 /// Protocol::new(ProtocolName::Unregistered("SHTTP".to_owned()),
45 /// Some("1.3".to_owned())),
46 /// Protocol::new(ProtocolName::Unregistered("IRC".to_owned()),
47 /// Some("6.9".to_owned())),
48 /// ])
49 /// );
50 /// ```
51 (Upgrade, "Upgrade") => (Protocol)+
52
53 test_upgrade {
54 // Testcase from the RFC
55 test_header!(
56 test1,
57 vec![b"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"],
58 Some(Upgrade(vec![
59 Protocol::new(ProtocolName::Http, Some("2.0".to_owned())),
60 Protocol::new(ProtocolName::Unregistered("SHTTP".to_owned()),
61 Some("1.3".to_owned())),
62 Protocol::new(ProtocolName::Unregistered("IRC".to_owned()), Some("6.9".to_owned())),
63 Protocol::new(ProtocolName::Unregistered("RTA".to_owned()), Some("x11".to_owned())),
64 ])));
65 // Own tests
66 test_header!(
67 test2, vec![b"websocket"],
68 Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
69 #[test]
70 fn test3() {
71 let x: crate::Result<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]);
72 assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
73 }
74 }
75}
76
77/// A protocol name used to identify a spefic protocol. Names are case-sensitive
78/// except for the `WebSocket` value.
79#[derive(Clone, Debug, Eq, PartialEq)]
80pub enum ProtocolName {
81 /// `HTTP` value, Hypertext Transfer Protocol
82 Http,
83 /// `TLS` value, Transport Layer Security [RFC2817](http://tools.ietf.org/html/rfc2817)
84 Tls,
85 /// `WebSocket` value, matched case insensitively,Web Socket Protocol
86 /// [RFC6455](http://tools.ietf.org/html/rfc6455)
87 WebSocket,
88 /// `h2c` value, HTTP/2 over cleartext TCP
89 H2c,
90 /// Any other protocol name not known to hyper
91 Unregistered(String),
92}
93
94impl FromStr for ProtocolName {
95 type Err = ();
96 fn from_str(s: &str) -> Result<ProtocolName, ()> {
97 Ok(match s {
98 "HTTP" => ProtocolName::Http,
99 "TLS" => ProtocolName::Tls,
100 "h2c" => ProtocolName::H2c,
101 _ => {
102 if UniCase(s) == UniCase("websocket") {
103 ProtocolName::WebSocket
104 } else {
105 ProtocolName::Unregistered(s.to_owned())
106 }
107 }
108 })
109 }
110}
111
112impl Display for ProtocolName {
113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 f.write_str(match *self {
115 ProtocolName::Http => "HTTP",
116 ProtocolName::Tls => "TLS",
117 ProtocolName::WebSocket => "websocket",
118 ProtocolName::H2c => "h2c",
119 ProtocolName::Unregistered(ref s) => s,
120 })
121 }
122}
123
124/// Protocols that appear in the `Upgrade` header field
125#[derive(Clone, Debug, Eq, PartialEq)]
126pub struct Protocol {
127 /// The protocol identifier
128 pub name: ProtocolName,
129 /// The optional version of the protocol, often in the format "DIGIT.DIGIT" (e.g.. "1.2")
130 pub version: Option<String>,
131}
132
133impl Protocol {
134 /// Creates a new Protocol with the given name and version
135 pub fn new(name: ProtocolName, version: Option<String>) -> Protocol {
136 Protocol { name: name, version: version }
137 }
138}
139
140impl FromStr for Protocol {
141 type Err =();
142 fn from_str(s: &str) -> Result<Protocol, ()> {
143 let mut parts = s.splitn(2, '/');
144 Ok(Protocol::new(r#try!(parts.next().unwrap().parse()), parts.next().map(|x| x.to_owned())))
145 }
146}
147
148impl Display for Protocol {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 r#try!(fmt::Display::fmt(&self.name, f));
151 if let Some(ref version) = self.version {
152 r#try!(write!(f, "/{}", version));
153 }
154 Ok(())
155 }
156}
157
158bench_header!(bench, Upgrade, { vec![b"HTTP/2.0, RTA/x11, websocket".to_vec()] });