|
1 |
| -use std::fmt; |
| 1 | +use std::fmt::{self, Display}; |
2 | 2 | use std::str::FromStr;
|
3 | 3 | use unicase::UniCase;
|
4 | 4 |
|
5 |
| -use self::Protocol::{WebSocket, ProtocolExt}; |
6 |
| - |
7 | 5 | header! {
|
8 | 6 | #[doc="`Upgrade` header, defined in [RFC7230](http://tools.ietf.org/html/rfc7230#section-6.7)"]
|
9 | 7 | #[doc=""]
|
@@ -31,47 +29,107 @@ header! {
|
31 | 29 | (Upgrade, "Upgrade") => (Protocol)+
|
32 | 30 |
|
33 | 31 | test_upgrade {
|
| 32 | + // Testcase from the RFC |
34 | 33 | test_header!(
|
35 | 34 | test1,
|
36 | 35 | vec![b"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"],
|
37 |
| - Some(HeaderField(vec![ |
38 |
| - Protocol::ProtocolExt("HTTP/2.0".to_string()), |
39 |
| - Protocol::ProtocolExt("SHTTP/1.3".to_string()), |
40 |
| - Protocol::ProtocolExt("IRC/6.9".to_string()), |
41 |
| - Protocol::ProtocolExt("RTA/x11".to_string()), |
| 36 | + Some(Upgrade(vec![ |
| 37 | + Protocol::new(ProtocolName::Http, Some("2.0".to_string())), |
| 38 | + Protocol::new(ProtocolName::Unregistered("SHTTP".to_string()), Some("1.3".to_string())), |
| 39 | + Protocol::new(ProtocolName::Unregistered("IRC".to_string()), Some("6.9".to_string())), |
| 40 | + Protocol::new(ProtocolName::Unregistered("RTA".to_string()), Some("x11".to_string())), |
42 | 41 | ])));
|
| 42 | + // Own tests |
| 43 | + test_header!( |
| 44 | + test2, vec![b"websocket"], |
| 45 | + Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); |
| 46 | + #[test] |
| 47 | + fn test3() { |
| 48 | + let x: Option<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]); |
| 49 | + assert_eq!(x, Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); |
| 50 | + } |
43 | 51 | }
|
44 | 52 | }
|
45 | 53 |
|
46 |
| -/// Protocol values that can appear in the Upgrade header. |
47 |
| -// TODO: Parse version part seperately |
48 |
| -#[derive(Clone, PartialEq, Debug)] |
49 |
| -pub enum Protocol { |
50 |
| - /// The websocket protocol. |
| 54 | +/// A protocol name used to identify a spefic protocol. Names are case-sensitive |
| 55 | +/// except for the `WebSocket` value. |
| 56 | +#[derive(Clone, Debug, Eq, PartialEq)] |
| 57 | +pub enum ProtocolName { |
| 58 | + /// `HTTP` value, Hypertext Transfer Protocol |
| 59 | + Http, |
| 60 | + /// `TLS` value, Transport Layer Security [RFC2817](http://tools.ietf.org/html/rfc2817) |
| 61 | + Tls, |
| 62 | + /// `WebSocket` value, matched case insensitively,Web Socket Protocol |
| 63 | + /// [RFC6455](http://tools.ietf.org/html/rfc6455) |
51 | 64 | WebSocket,
|
52 |
| - /// Some other less common protocol. |
53 |
| - ProtocolExt(String), |
| 65 | + /// `h2c` value, HTTP/2 over cleartext TCP |
| 66 | + H2c, |
| 67 | + /// Any other protocol name not known to hyper |
| 68 | + Unregistered(String), |
54 | 69 | }
|
55 | 70 |
|
56 |
| -impl FromStr for Protocol { |
| 71 | +impl FromStr for ProtocolName { |
57 | 72 | type Err = ();
|
58 |
| - fn from_str(s: &str) -> Result<Protocol, ()> { |
59 |
| - if UniCase(s) == UniCase("websocket") { |
60 |
| - Ok(WebSocket) |
61 |
| - } |
62 |
| - else { |
63 |
| - Ok(ProtocolExt(s.to_string())) |
64 |
| - } |
| 73 | + fn from_str(s: &str) -> Result<ProtocolName, ()> { |
| 74 | + Ok(match s { |
| 75 | + "HTTP" => ProtocolName::Http, |
| 76 | + "TLS" => ProtocolName::Tls, |
| 77 | + "h2c" => ProtocolName::H2c, |
| 78 | + _ => { |
| 79 | + if UniCase(s) == UniCase("websocket") { |
| 80 | + ProtocolName::WebSocket |
| 81 | + } else { |
| 82 | + ProtocolName::Unregistered(s.to_string()) |
| 83 | + } |
| 84 | + } |
| 85 | + }) |
65 | 86 | }
|
66 | 87 | }
|
67 | 88 |
|
68 |
| -impl fmt::Display for Protocol { |
69 |
| - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
70 |
| - write!(fmt, "{}", match *self { |
71 |
| - WebSocket => "websocket", |
72 |
| - ProtocolExt(ref s) => s.as_ref() |
| 89 | +impl Display for ProtocolName { |
| 90 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 91 | + f.write_str(match *self { |
| 92 | + ProtocolName::Http => "HTTP", |
| 93 | + ProtocolName::Tls => "TLS", |
| 94 | + ProtocolName::WebSocket => "websocket", |
| 95 | + ProtocolName::H2c => "h2c", |
| 96 | + ProtocolName::Unregistered(ref s) => s, |
73 | 97 | })
|
74 | 98 | }
|
75 | 99 | }
|
76 | 100 |
|
| 101 | +/// Protocols that appear in the `Upgrade` header field |
| 102 | +#[derive(Clone, Debug, Eq, PartialEq)] |
| 103 | +pub struct Protocol { |
| 104 | + /// The protocol identifier |
| 105 | + pub name: ProtocolName, |
| 106 | + /// The optional version of the protocol, often in the format "DIGIT.DIGIT" (e.g.. "1.2") |
| 107 | + pub version: Option<String>, |
| 108 | +} |
| 109 | + |
| 110 | +impl Protocol { |
| 111 | + /// Creates a new Protocol with the given name and version |
| 112 | + pub fn new(name: ProtocolName, version: Option<String>) -> Protocol { |
| 113 | + Protocol { name: name, version: version } |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +impl FromStr for Protocol { |
| 118 | + type Err =(); |
| 119 | + fn from_str(s: &str) -> Result<Protocol, ()> { |
| 120 | + let mut parts = s.splitn(2, '/'); |
| 121 | + Ok(Protocol::new(try!(parts.next().unwrap().parse()), parts.next().map(|x| x.to_string()))) |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +impl Display for Protocol { |
| 126 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 127 | + try!(fmt::Display::fmt(&self.name, f)); |
| 128 | + if let Some(ref version) = self.version { |
| 129 | + try!(write!(f, "/{}", version)); |
| 130 | + } |
| 131 | + Ok(()) |
| 132 | + } |
| 133 | +} |
| 134 | + |
77 | 135 | bench_header!(bench, Upgrade, { vec![b"HTTP/2.0, RTA/x11, websocket".to_vec()] });
|
0 commit comments