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()] });