feat(client): introduce lower-level Connection API

Closes #1449
This commit is contained in:
Sean McArthur
2018-03-07 12:59:55 -08:00
parent 0786ea1f87
commit 1207c2b624
19 changed files with 1814 additions and 792 deletions

View File

@@ -5,8 +5,8 @@ use httparse;
use bytes::{BytesMut, Bytes};
use header::{self, Headers, ContentLength, TransferEncoding};
use proto::{MessageHead, RawStatus, Http1Transaction, ParseResult,
ServerTransaction, ClientTransaction, RequestLine, RequestHead};
use proto::{Decode, MessageHead, RawStatus, Http1Transaction, ParseResult,
RequestLine, RequestHead};
use proto::h1::{Encoder, Decoder, date};
use method::Method;
use status::StatusCode;
@@ -15,7 +15,19 @@ use version::HttpVersion::{Http10, Http11};
const MAX_HEADERS: usize = 100;
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
impl Http1Transaction for ServerTransaction {
// There are 2 main roles, Client and Server.
//
// There is 1 modifier, OnUpgrade, which can wrap Client and Server,
// to signal that HTTP upgrades are not supported.
pub struct Client<T>(T);
pub struct Server<T>(T);
impl<T> Http1Transaction for Server<T>
where
T: OnUpgrade,
{
type Incoming = RequestLine;
type Outgoing = StatusCode;
@@ -72,7 +84,7 @@ impl Http1Transaction for ServerTransaction {
}, len)))
}
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Option<Decoder>> {
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Decode> {
use ::header;
*method = Some(head.subject.0.clone());
@@ -95,37 +107,40 @@ impl Http1Transaction for ServerTransaction {
debug!("HTTP/1.0 has Transfer-Encoding header");
Err(::Error::Header)
} else if encodings.last() == Some(&header::Encoding::Chunked) {
Ok(Some(Decoder::chunked()))
Ok(Decode::Normal(Decoder::chunked()))
} else {
debug!("request with transfer-encoding header, but not chunked, bad request");
Err(::Error::Header)
}
} else if let Some(&header::ContentLength(len)) = head.headers.get() {
Ok(Some(Decoder::length(len)))
Ok(Decode::Normal(Decoder::length(len)))
} else if head.headers.has::<header::ContentLength>() {
debug!("illegal Content-Length: {:?}", head.headers.get_raw("Content-Length"));
Err(::Error::Header)
} else {
Ok(Some(Decoder::length(0)))
Ok(Decode::Normal(Decoder::length(0)))
}
}
fn encode(mut head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> ::Result<Encoder> {
trace!("ServerTransaction::encode has_body={}, method={:?}", has_body, method);
trace!("Server::encode has_body={}, method={:?}", has_body, method);
// hyper currently doesn't support returning 1xx status codes as a Response
// This is because Service only allows returning a single Response, and
// so if you try to reply with a e.g. 100 Continue, you have no way of
// replying with the latter status code response.
let ret = if head.subject.is_informational() {
let ret = if ::StatusCode::SwitchingProtocols == head.subject {
T::on_encode_upgrade(&mut head)
.map(|_| Server::set_length(&mut head, has_body, method.as_ref()))
} else if head.subject.is_informational() {
error!("response with 1xx status code not supported");
head = MessageHead::default();
head.subject = ::StatusCode::InternalServerError;
head.headers.set(ContentLength(0));
Err(::Error::Status)
} else {
Ok(ServerTransaction::set_length(&mut head, has_body, method.as_ref()))
Ok(Server::set_length(&mut head, has_body, method.as_ref()))
};
@@ -179,7 +194,7 @@ impl Http1Transaction for ServerTransaction {
}
}
impl ServerTransaction {
impl Server<()> {
fn set_length(head: &mut MessageHead<StatusCode>, has_body: bool, method: Option<&Method>) -> Encoder {
// these are here thanks to borrowck
// `if method == Some(&Method::Get)` says the RHS doesn't live long enough
@@ -214,7 +229,10 @@ impl ServerTransaction {
}
}
impl Http1Transaction for ClientTransaction {
impl<T> Http1Transaction for Client<T>
where
T: OnUpgrade,
{
type Incoming = RawStatus;
type Outgoing = RequestLine;
@@ -262,7 +280,7 @@ impl Http1Transaction for ClientTransaction {
}, len)))
}
fn decoder(inc: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Option<Decoder>> {
fn decoder(inc: &MessageHead<Self::Incoming>, method: &mut Option<Method>) -> ::Result<Decode> {
// According to https://tools.ietf.org/html/rfc7230#section-3.3.3
// 1. HEAD responses, and Status 1xx, 204, and 304 cannot have a body.
// 2. Status 2xx to a CONNECT cannot have a body.
@@ -274,30 +292,29 @@ impl Http1Transaction for ClientTransaction {
match inc.subject.0 {
101 => {
debug!("received 101 upgrade response, not supported");
return Err(::Error::Upgrade);
return T::on_decode_upgrade().map(Decode::Final);
},
100...199 => {
trace!("ignoring informational response: {}", inc.subject.0);
return Ok(None);
return Ok(Decode::Ignore);
},
204 |
304 => return Ok(Some(Decoder::length(0))),
304 => return Ok(Decode::Normal(Decoder::length(0))),
_ => (),
}
match *method {
Some(Method::Head) => {
return Ok(Some(Decoder::length(0)));
return Ok(Decode::Normal(Decoder::length(0)));
}
Some(Method::Connect) => match inc.subject.0 {
200...299 => {
return Ok(Some(Decoder::length(0)));
return Ok(Decode::Final(Decoder::length(0)));
},
_ => {},
},
Some(_) => {},
None => {
trace!("ClientTransaction::decoder is missing the Method");
trace!("Client::decoder is missing the Method");
}
}
@@ -307,28 +324,28 @@ impl Http1Transaction for ClientTransaction {
debug!("HTTP/1.0 has Transfer-Encoding header");
Err(::Error::Header)
} else if codings.last() == Some(&header::Encoding::Chunked) {
Ok(Some(Decoder::chunked()))
Ok(Decode::Normal(Decoder::chunked()))
} else {
trace!("not chunked. read till eof");
Ok(Some(Decoder::eof()))
Ok(Decode::Normal(Decoder::eof()))
}
} else if let Some(&header::ContentLength(len)) = inc.headers.get() {
Ok(Some(Decoder::length(len)))
Ok(Decode::Normal(Decoder::length(len)))
} else if inc.headers.has::<header::ContentLength>() {
debug!("illegal Content-Length: {:?}", inc.headers.get_raw("Content-Length"));
Err(::Error::Header)
} else {
trace!("neither Transfer-Encoding nor Content-Length");
Ok(Some(Decoder::eof()))
Ok(Decode::Normal(Decoder::eof()))
}
}
fn encode(mut head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> ::Result<Encoder> {
trace!("ClientTransaction::encode has_body={}, method={:?}", has_body, method);
trace!("Client::encode has_body={}, method={:?}", has_body, method);
*method = Some(head.subject.0.clone());
let body = ClientTransaction::set_length(&mut head, has_body);
let body = Client::set_length(&mut head, has_body);
let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE;
dst.reserve(init_cap);
@@ -351,7 +368,7 @@ impl Http1Transaction for ClientTransaction {
}
}
impl ClientTransaction {
impl Client<()> {
fn set_length(head: &mut RequestHead, has_body: bool) -> Encoder {
if has_body {
let can_chunked = head.version == Http11
@@ -393,6 +410,42 @@ fn set_length(headers: &mut Headers, can_chunked: bool) -> Encoder {
}
}
pub trait OnUpgrade {
fn on_encode_upgrade(head: &mut MessageHead<StatusCode>) -> ::Result<()>;
fn on_decode_upgrade() -> ::Result<Decoder>;
}
pub enum YesUpgrades {}
pub enum NoUpgrades {}
impl OnUpgrade for YesUpgrades {
fn on_encode_upgrade(_head: &mut MessageHead<StatusCode>) -> ::Result<()> {
Ok(())
}
fn on_decode_upgrade() -> ::Result<Decoder> {
debug!("101 response received, upgrading");
// 101 upgrades always have no body
Ok(Decoder::length(0))
}
}
impl OnUpgrade for NoUpgrades {
fn on_encode_upgrade(head: &mut MessageHead<StatusCode>) -> ::Result<()> {
error!("response with 101 status code not supported");
*head = MessageHead::default();
head.subject = ::StatusCode::InternalServerError;
head.headers.set(ContentLength(0));
Err(::Error::Status)
}
fn on_decode_upgrade() -> ::Result<Decoder> {
debug!("received 101 upgrade response, not supported");
return Err(::Error::Upgrade);
}
}
#[derive(Clone, Copy)]
struct HeaderIndices {
name: (usize, usize),
@@ -456,16 +509,43 @@ fn extend(dst: &mut Vec<u8>, data: &[u8]) {
mod tests {
use bytes::BytesMut;
use proto::{MessageHead, ServerTransaction, ClientTransaction, Http1Transaction};
use proto::{Decode, MessageHead};
use super::{Decoder, Server as S, Client as C, NoUpgrades, Http1Transaction};
use header::{ContentLength, TransferEncoding};
type Server = S<NoUpgrades>;
type Client = C<NoUpgrades>;
impl Decode {
fn final_(self) -> Decoder {
match self {
Decode::Final(d) => d,
other => panic!("expected Final, found {:?}", other),
}
}
fn normal(self) -> Decoder {
match self {
Decode::Normal(d) => d,
other => panic!("expected Normal, found {:?}", other),
}
}
fn ignore(self) {
match self {
Decode::Ignore => {},
other => panic!("expected Ignore, found {:?}", other),
}
}
}
#[test]
fn test_parse_request() {
extern crate pretty_env_logger;
let _ = pretty_env_logger::try_init();
let mut raw = BytesMut::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec());
let expected_len = raw.len();
let (req, len) = ServerTransaction::parse(&mut raw).unwrap().unwrap();
let (req, len) = Server::parse(&mut raw).unwrap().unwrap();
assert_eq!(len, expected_len);
assert_eq!(req.subject.0, ::Method::Get);
assert_eq!(req.subject.1, "/echo");
@@ -481,7 +561,7 @@ mod tests {
let _ = pretty_env_logger::try_init();
let mut raw = BytesMut::from(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".to_vec());
let expected_len = raw.len();
let (req, len) = ClientTransaction::parse(&mut raw).unwrap().unwrap();
let (req, len) = Client::parse(&mut raw).unwrap().unwrap();
assert_eq!(len, expected_len);
assert_eq!(req.subject.0, 200);
assert_eq!(req.subject.1, "OK");
@@ -493,17 +573,17 @@ mod tests {
#[test]
fn test_parse_request_errors() {
let mut raw = BytesMut::from(b"GET htt:p// HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec());
ServerTransaction::parse(&mut raw).unwrap_err();
Server::parse(&mut raw).unwrap_err();
}
#[test]
fn test_parse_raw_status() {
let mut raw = BytesMut::from(b"HTTP/1.1 200 OK\r\n\r\n".to_vec());
let (res, _) = ClientTransaction::parse(&mut raw).unwrap().unwrap();
let (res, _) = Client::parse(&mut raw).unwrap().unwrap();
assert_eq!(res.subject.1, "OK");
let mut raw = BytesMut::from(b"HTTP/1.1 200 Howdy\r\n\r\n".to_vec());
let (res, _) = ClientTransaction::parse(&mut raw).unwrap().unwrap();
let (res, _) = Client::parse(&mut raw).unwrap().unwrap();
assert_eq!(res.subject.1, "Howdy");
}
@@ -516,32 +596,32 @@ mod tests {
let mut head = MessageHead::<::proto::RequestLine>::default();
head.subject.0 = ::Method::Get;
assert_eq!(Decoder::length(0), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(0), Server::decoder(&head, method).unwrap().normal());
assert_eq!(*method, Some(::Method::Get));
head.subject.0 = ::Method::Post;
assert_eq!(Decoder::length(0), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(0), Server::decoder(&head, method).unwrap().normal());
assert_eq!(*method, Some(::Method::Post));
head.headers.set(TransferEncoding::chunked());
assert_eq!(Decoder::chunked(), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::chunked(), Server::decoder(&head, method).unwrap().normal());
// transfer-encoding and content-length = chunked
head.headers.set(ContentLength(10));
assert_eq!(Decoder::chunked(), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::chunked(), Server::decoder(&head, method).unwrap().normal());
head.headers.remove::<TransferEncoding>();
assert_eq!(Decoder::length(10), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(10), Server::decoder(&head, method).unwrap().normal());
head.headers.set_raw("Content-Length", vec![b"5".to_vec(), b"5".to_vec()]);
assert_eq!(Decoder::length(5), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(5), Server::decoder(&head, method).unwrap().normal());
head.headers.set_raw("Content-Length", vec![b"10".to_vec(), b"11".to_vec()]);
ServerTransaction::decoder(&head, method).unwrap_err();
Server::decoder(&head, method).unwrap_err();
head.headers.remove::<ContentLength>();
head.headers.set_raw("Transfer-Encoding", "gzip");
ServerTransaction::decoder(&head, method).unwrap_err();
Server::decoder(&head, method).unwrap_err();
// http/1.0
@@ -549,14 +629,14 @@ mod tests {
head.headers.clear();
// 1.0 requests can only have bodies if content-length is set
assert_eq!(Decoder::length(0), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(0), Server::decoder(&head, method).unwrap().normal());
head.headers.set(TransferEncoding::chunked());
ServerTransaction::decoder(&head, method).unwrap_err();
Server::decoder(&head, method).unwrap_err();
head.headers.remove::<TransferEncoding>();
head.headers.set(ContentLength(15));
assert_eq!(Decoder::length(15), ServerTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(15), Server::decoder(&head, method).unwrap().normal());
}
#[test]
@@ -567,64 +647,64 @@ mod tests {
let mut head = MessageHead::<::proto::RawStatus>::default();
head.subject.0 = 204;
assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().normal());
head.subject.0 = 304;
assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().normal());
head.subject.0 = 200;
assert_eq!(Decoder::eof(), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::eof(), Client::decoder(&head, method).unwrap().normal());
*method = Some(::Method::Head);
assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().normal());
*method = Some(::Method::Connect);
assert_eq!(Decoder::length(0), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(0), Client::decoder(&head, method).unwrap().final_());
// CONNECT receiving non 200 can have a body
head.subject.0 = 404;
head.headers.set(ContentLength(10));
assert_eq!(Decoder::length(10), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(10), Client::decoder(&head, method).unwrap().normal());
head.headers.remove::<ContentLength>();
*method = Some(::Method::Get);
head.headers.set(TransferEncoding::chunked());
assert_eq!(Decoder::chunked(), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::chunked(), Client::decoder(&head, method).unwrap().normal());
// transfer-encoding and content-length = chunked
head.headers.set(ContentLength(10));
assert_eq!(Decoder::chunked(), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::chunked(), Client::decoder(&head, method).unwrap().normal());
head.headers.remove::<TransferEncoding>();
assert_eq!(Decoder::length(10), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(10), Client::decoder(&head, method).unwrap().normal());
head.headers.set_raw("Content-Length", vec![b"5".to_vec(), b"5".to_vec()]);
assert_eq!(Decoder::length(5), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::length(5), Client::decoder(&head, method).unwrap().normal());
head.headers.set_raw("Content-Length", vec![b"10".to_vec(), b"11".to_vec()]);
ClientTransaction::decoder(&head, method).unwrap_err();
Client::decoder(&head, method).unwrap_err();
head.headers.clear();
// 1xx status codes
head.subject.0 = 100;
assert!(ClientTransaction::decoder(&head, method).unwrap().is_none());
Client::decoder(&head, method).unwrap().ignore();
head.subject.0 = 103;
assert!(ClientTransaction::decoder(&head, method).unwrap().is_none());
Client::decoder(&head, method).unwrap().ignore();
// 101 upgrade not supported yet
head.subject.0 = 101;
ClientTransaction::decoder(&head, method).unwrap_err();
Client::decoder(&head, method).unwrap_err();
head.subject.0 = 200;
// http/1.0
head.version = ::HttpVersion::Http10;
assert_eq!(Decoder::eof(), ClientTransaction::decoder(&head, method).unwrap().unwrap());
assert_eq!(Decoder::eof(), Client::decoder(&head, method).unwrap().normal());
head.headers.set(TransferEncoding::chunked());
ClientTransaction::decoder(&head, method).unwrap_err();
Client::decoder(&head, method).unwrap_err();
}
#[cfg(feature = "nightly")]
@@ -656,7 +736,7 @@ mod tests {
b.bytes = len as u64;
b.iter(|| {
ServerTransaction::parse(&mut raw).unwrap();
Server::parse(&mut raw).unwrap();
restart(&mut raw, len);
});
@@ -688,7 +768,7 @@ mod tests {
b.iter(|| {
let mut vec = Vec::new();
ServerTransaction::encode(head.clone(), true, &mut None, &mut vec).unwrap();
Server::encode(head.clone(), true, &mut None, &mut vec).unwrap();
assert_eq!(vec.len(), len);
::test::black_box(vec);
})