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);