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