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