BREAKING CHANGE: The `Preference` header had a typo in a variant and it's string representation, change `Preference::HandlingLeniant` to `Preference::HandlingLenient`.
159 lines
5.4 KiB
Rust
159 lines
5.4 KiB
Rust
use std::fmt::{self, Display};
|
|
use std::str::FromStr;
|
|
use unicase::UniCase;
|
|
|
|
header! {
|
|
/// `Upgrade` header, defined in [RFC7230](http://tools.ietf.org/html/rfc7230#section-6.7)
|
|
///
|
|
/// The `Upgrade` header field is intended to provide a simple mechanism
|
|
/// for transitioning from HTTP/1.1 to some other protocol on the same
|
|
/// connection. A client MAY send a list of protocols in the Upgrade
|
|
/// header field of a request to invite the server to switch to one or
|
|
/// more of those protocols, in order of descending preference, before
|
|
/// sending the final response. A server MAY ignore a received Upgrade
|
|
/// header field if it wishes to continue using the current protocol on
|
|
/// that connection. Upgrade cannot be used to insist on a protocol
|
|
/// change.
|
|
///
|
|
/// # ABNF
|
|
/// ```plain
|
|
/// Upgrade = 1#protocol
|
|
///
|
|
/// protocol = protocol-name ["/" protocol-version]
|
|
/// protocol-name = token
|
|
/// protocol-version = token
|
|
/// ```
|
|
///
|
|
/// # Example values
|
|
/// * `HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11`
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use hyper::header::{Headers, Upgrade, Protocol, ProtocolName};
|
|
///
|
|
/// let mut headers = Headers::new();
|
|
/// headers.set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]));
|
|
/// ```
|
|
/// ```
|
|
/// use hyper::header::{Headers, Upgrade, Protocol, ProtocolName};
|
|
///
|
|
/// let mut headers = Headers::new();
|
|
/// headers.set(
|
|
/// Upgrade(vec![
|
|
/// Protocol::new(ProtocolName::Http, Some("2.0".to_owned())),
|
|
/// Protocol::new(ProtocolName::Unregistered("SHTTP".to_owned()),
|
|
/// Some("1.3".to_owned())),
|
|
/// Protocol::new(ProtocolName::Unregistered("IRC".to_owned()),
|
|
/// Some("6.9".to_owned())),
|
|
/// ])
|
|
/// );
|
|
/// ```
|
|
(Upgrade, "Upgrade") => (Protocol)+
|
|
|
|
test_upgrade {
|
|
// Testcase from the RFC
|
|
test_header!(
|
|
test1,
|
|
vec![b"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"],
|
|
Some(Upgrade(vec![
|
|
Protocol::new(ProtocolName::Http, Some("2.0".to_owned())),
|
|
Protocol::new(ProtocolName::Unregistered("SHTTP".to_owned()),
|
|
Some("1.3".to_owned())),
|
|
Protocol::new(ProtocolName::Unregistered("IRC".to_owned()), Some("6.9".to_owned())),
|
|
Protocol::new(ProtocolName::Unregistered("RTA".to_owned()), Some("x11".to_owned())),
|
|
])));
|
|
// Own tests
|
|
test_header!(
|
|
test2, vec![b"websocket"],
|
|
Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
|
#[test]
|
|
fn test3() {
|
|
let x: ::Result<Upgrade> = Header::parse_header(&"WEbSOCKet".into());
|
|
assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A protocol name used to identify a specific protocol. Names are case-sensitive
|
|
/// except for the `WebSocket` value.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub enum ProtocolName {
|
|
/// `HTTP` value, Hypertext Transfer Protocol
|
|
Http,
|
|
/// `TLS` value, Transport Layer Security [RFC2817](http://tools.ietf.org/html/rfc2817)
|
|
Tls,
|
|
/// `WebSocket` value, matched case insensitively,Web Socket Protocol
|
|
/// [RFC6455](http://tools.ietf.org/html/rfc6455)
|
|
WebSocket,
|
|
/// `h2c` value, HTTP/2 over cleartext TCP
|
|
H2c,
|
|
/// Any other protocol name not known to hyper
|
|
Unregistered(String),
|
|
}
|
|
|
|
impl FromStr for ProtocolName {
|
|
type Err = ();
|
|
fn from_str(s: &str) -> Result<ProtocolName, ()> {
|
|
Ok(match s {
|
|
"HTTP" => ProtocolName::Http,
|
|
"TLS" => ProtocolName::Tls,
|
|
"h2c" => ProtocolName::H2c,
|
|
_ => {
|
|
if UniCase(s) == UniCase("websocket") {
|
|
ProtocolName::WebSocket
|
|
} else {
|
|
ProtocolName::Unregistered(s.to_owned())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Display for ProtocolName {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.write_str(match *self {
|
|
ProtocolName::Http => "HTTP",
|
|
ProtocolName::Tls => "TLS",
|
|
ProtocolName::WebSocket => "websocket",
|
|
ProtocolName::H2c => "h2c",
|
|
ProtocolName::Unregistered(ref s) => s,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Protocols that appear in the `Upgrade` header field
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct Protocol {
|
|
/// The protocol identifier
|
|
pub name: ProtocolName,
|
|
/// The optional version of the protocol, often in the format "DIGIT.DIGIT" (e.g.. "1.2")
|
|
pub version: Option<String>,
|
|
}
|
|
|
|
impl Protocol {
|
|
/// Creates a new Protocol with the given name and version
|
|
pub fn new(name: ProtocolName, version: Option<String>) -> Protocol {
|
|
Protocol { name: name, version: version }
|
|
}
|
|
}
|
|
|
|
impl FromStr for Protocol {
|
|
type Err =();
|
|
fn from_str(s: &str) -> Result<Protocol, ()> {
|
|
let mut parts = s.splitn(2, '/');
|
|
Ok(Protocol::new(try!(parts.next().unwrap().parse()), parts.next().map(|x| x.to_owned())))
|
|
}
|
|
}
|
|
|
|
impl Display for Protocol {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
try!(fmt::Display::fmt(&self.name, f));
|
|
if let Some(ref version) = self.version {
|
|
try!(write!(f, "/{}", version));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
bench_header!(bench, Upgrade, { vec![b"HTTP/2.0, RTA/x11, websocket".to_vec()] });
|