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

@@ -1,3 +1,5 @@
use std::fmt;
use bytes::Bytes;
use futures::{Async, AsyncSink, Future, Poll, Sink, StartSend, Stream};
use futures::sync::{mpsc, oneshot};
@@ -13,11 +15,12 @@ pub type BodySender = mpsc::Sender<Result<Chunk, ::Error>>;
/// A `Stream` for `Chunk`s used in requests and responses.
#[must_use = "streams do nothing unless polled"]
#[derive(Debug)]
pub struct Body(Inner);
pub struct Body {
kind: Kind,
}
#[derive(Debug)]
enum Inner {
enum Kind {
#[cfg(feature = "tokio-proto")]
Tokio(TokioBody),
Chan {
@@ -40,7 +43,7 @@ impl Body {
/// Return an empty body stream
#[inline]
pub fn empty() -> Body {
Body(Inner::Empty)
Body::new(Kind::Empty)
}
/// Return a body stream with an associated sender half
@@ -60,11 +63,32 @@ impl Body {
/// there are more chunks immediately.
#[inline]
pub fn is_empty(&self) -> bool {
match self.0 {
Inner::Empty => true,
match self.kind {
Kind::Empty => true,
_ => false,
}
}
fn new(kind: Kind) -> Body {
Body {
kind: kind,
}
}
fn poll_inner(&mut self) -> Poll<Option<Chunk>, ::Error> {
match self.kind {
#[cfg(feature = "tokio-proto")]
Kind::Tokio(ref mut rx) => rx.poll(),
Kind::Chan { ref mut rx, .. } => match rx.poll().expect("mpsc cannot error") {
Async::Ready(Some(Ok(chunk))) => Ok(Async::Ready(Some(chunk))),
Async::Ready(Some(Err(err))) => Err(err),
Async::Ready(None) => Ok(Async::Ready(None)),
Async::NotReady => Ok(Async::NotReady),
},
Kind::Once(ref mut val) => Ok(Async::Ready(val.take())),
Kind::Empty => Ok(Async::Ready(None)),
}
}
}
impl Default for Body {
@@ -80,18 +104,15 @@ impl Stream for Body {
#[inline]
fn poll(&mut self) -> Poll<Option<Chunk>, ::Error> {
match self.0 {
#[cfg(feature = "tokio-proto")]
Inner::Tokio(ref mut rx) => rx.poll(),
Inner::Chan { ref mut rx, .. } => match rx.poll().expect("mpsc cannot error") {
Async::Ready(Some(Ok(chunk))) => Ok(Async::Ready(Some(chunk))),
Async::Ready(Some(Err(err))) => Err(err),
Async::Ready(None) => Ok(Async::Ready(None)),
Async::NotReady => Ok(Async::NotReady),
},
Inner::Once(ref mut val) => Ok(Async::Ready(val.take())),
Inner::Empty => Ok(Async::Ready(None)),
}
self.poll_inner()
}
}
impl fmt::Debug for Body {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Body")
.field(&self.kind)
.finish()
}
}
@@ -105,7 +126,7 @@ pub fn channel() -> (ChunkSender, Body) {
close_rx_check: true,
tx: tx,
};
let rx = Body(Inner::Chan {
let rx = Body::new(Kind::Chan {
close_tx: close_tx,
rx: rx,
});
@@ -142,24 +163,24 @@ impl ChunkSender {
feat_server_proto! {
impl From<Body> for tokio_proto::streaming::Body<Chunk, ::Error> {
fn from(b: Body) -> tokio_proto::streaming::Body<Chunk, ::Error> {
match b.0 {
Inner::Tokio(b) => b,
Inner::Chan { close_tx, rx } => {
match b.kind {
Kind::Tokio(b) => b,
Kind::Chan { close_tx, rx } => {
// disable knowing if the Rx gets dropped, since we cannot
// pass this tx along.
let _ = close_tx.send(false);
rx.into()
},
Inner::Once(Some(chunk)) => TokioBody::from(chunk),
Inner::Once(None) |
Inner::Empty => TokioBody::empty(),
Kind::Once(Some(chunk)) => TokioBody::from(chunk),
Kind::Once(None) |
Kind::Empty => TokioBody::empty(),
}
}
}
impl From<tokio_proto::streaming::Body<Chunk, ::Error>> for Body {
fn from(tokio_body: tokio_proto::streaming::Body<Chunk, ::Error>) -> Body {
Body(Inner::Tokio(tokio_body))
Body::new(Kind::Tokio(tokio_body))
}
}
}
@@ -168,7 +189,7 @@ impl From<mpsc::Receiver<Result<Chunk, ::Error>>> for Body {
#[inline]
fn from(src: mpsc::Receiver<Result<Chunk, ::Error>>) -> Body {
let (tx, _) = oneshot::channel();
Body(Inner::Chan {
Body::new(Kind::Chan {
close_tx: tx,
rx: src,
})
@@ -178,7 +199,7 @@ impl From<mpsc::Receiver<Result<Chunk, ::Error>>> for Body {
impl From<Chunk> for Body {
#[inline]
fn from (chunk: Chunk) -> Body {
Body(Inner::Once(Some(chunk)))
Body::new(Kind::Once(Some(chunk)))
}
}

View File

@@ -2,6 +2,7 @@ use std::fmt;
use std::io::{self};
use std::marker::PhantomData;
use bytes::Bytes;
use futures::{Async, AsyncSink, Poll, StartSend};
#[cfg(feature = "tokio-proto")]
use futures::{Sink, Stream};
@@ -10,7 +11,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
#[cfg(feature = "tokio-proto")]
use tokio_proto::streaming::pipeline::{Frame, Transport};
use proto::{Chunk, Http1Transaction, MessageHead};
use proto::{Chunk, Decode, Http1Transaction, MessageHead};
use super::io::{Cursor, Buffered};
use super::{EncodedBuf, Encoder, Decoder};
use method::Method;
@@ -24,24 +25,34 @@ use version::HttpVersion;
/// The connection will determine when a message begins and ends as well as
/// determine if this connection can be kept alive after the message,
/// or if it is complete.
pub struct Conn<I, B, T, K = KA> {
pub struct Conn<I, B, T> {
io: Buffered<I, EncodedBuf<Cursor<B>>>,
state: State<K>,
state: State,
_marker: PhantomData<T>
}
impl<I, B, T, K> Conn<I, B, T, K>
/*
impl<I, B> Conn<I, B, ClientTransaction>
where I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>,
{
pub fn new_client(io: I) -> Conn<I, B, ClientTransaction> {
Conn::new(io)
}
}
*/
impl<I, B, T> Conn<I, B, T>
where I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>,
T: Http1Transaction,
K: KeepAlive
{
pub fn new(io: I, keep_alive: K) -> Conn<I, B, T, K> {
pub fn new(io: I) -> Conn<I, B, T> {
Conn {
io: Buffered::new(io),
state: State {
error: None,
keep_alive: keep_alive,
keep_alive: KA::Busy,
method: None,
read_task: None,
reading: Reading::Init,
@@ -66,6 +77,10 @@ where I: AsyncRead + AsyncWrite,
self.io.set_write_strategy_flatten();
}
pub fn into_inner(self) -> (I, Bytes) {
self.io.into_inner()
}
#[cfg(feature = "tokio-proto")]
fn poll_incoming(&mut self) -> Poll<Option<Frame<MessageHead<T::Incoming>, Chunk, ::Error>>, io::Error> {
trace!("Conn::poll_incoming()");
@@ -205,8 +220,16 @@ where I: AsyncRead + AsyncWrite,
};
let decoder = match T::decoder(&head, &mut self.state.method) {
Ok(Some(d)) => d,
Ok(None) => {
Ok(Decode::Normal(d)) => {
d
},
Ok(Decode::Final(d)) => {
trace!("final decoder, HTTP ending");
debug_assert!(d.is_eof());
self.state.close_read();
d
},
Ok(Decode::Ignore) => {
// likely a 1xx message that we can ignore
continue;
}
@@ -232,7 +255,11 @@ where I: AsyncRead + AsyncWrite,
} else {
(true, Reading::Body(decoder))
};
self.state.reading = reading;
if let Reading::Closed = self.state.reading {
// actually want an `if not let ...`
} else {
self.state.reading = reading;
}
if !body {
self.try_keep_alive();
}
@@ -434,6 +461,12 @@ where I: AsyncRead + AsyncWrite,
}
pub fn can_write_head(&self) -> bool {
if !T::should_read_first() {
match self.state.reading {
Reading::Closed => return false,
_ => {},
}
}
match self.state.writing {
Writing::Init => true,
_ => false
@@ -456,6 +489,10 @@ where I: AsyncRead + AsyncWrite,
pub fn write_head(&mut self, mut head: MessageHead<T::Outgoing>, body: bool) {
debug_assert!(self.can_write_head());
if !T::should_read_first() {
self.state.busy();
}
self.enforce_version(&mut head);
let buf = self.io.write_buf_mut();
@@ -623,11 +660,10 @@ where I: AsyncRead + AsyncWrite,
// ==== tokio_proto impl ====
#[cfg(feature = "tokio-proto")]
impl<I, B, T, K> Stream for Conn<I, B, T, K>
impl<I, B, T> Stream for Conn<I, B, T>
where I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>,
T: Http1Transaction,
K: KeepAlive,
T::Outgoing: fmt::Debug {
type Item = Frame<MessageHead<T::Incoming>, Chunk, ::Error>;
type Error = io::Error;
@@ -642,11 +678,10 @@ where I: AsyncRead + AsyncWrite,
}
#[cfg(feature = "tokio-proto")]
impl<I, B, T, K> Sink for Conn<I, B, T, K>
impl<I, B, T> Sink for Conn<I, B, T>
where I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>,
T: Http1Transaction,
K: KeepAlive,
T::Outgoing: fmt::Debug {
type SinkItem = Frame<MessageHead<T::Outgoing>, B, ::Error>;
type SinkError = io::Error;
@@ -711,14 +746,13 @@ where I: AsyncRead + AsyncWrite,
}
#[cfg(feature = "tokio-proto")]
impl<I, B, T, K> Transport for Conn<I, B, T, K>
impl<I, B, T> Transport for Conn<I, B, T>
where I: AsyncRead + AsyncWrite + 'static,
B: AsRef<[u8]> + 'static,
T: Http1Transaction + 'static,
K: KeepAlive + 'static,
T::Outgoing: fmt::Debug {}
impl<I, B: AsRef<[u8]>, T, K: KeepAlive> fmt::Debug for Conn<I, B, T, K> {
impl<I, B: AsRef<[u8]>, T> fmt::Debug for Conn<I, B, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Conn")
.field("state", &self.state)
@@ -727,9 +761,9 @@ impl<I, B: AsRef<[u8]>, T, K: KeepAlive> fmt::Debug for Conn<I, B, T, K> {
}
}
struct State<K> {
struct State {
error: Option<::Error>,
keep_alive: K,
keep_alive: KA,
method: Option<Method>,
read_task: Option<Task>,
reading: Reading,
@@ -752,12 +786,12 @@ enum Writing {
Closed,
}
impl<K: KeepAlive> fmt::Debug for State<K> {
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("State")
.field("reading", &self.reading)
.field("writing", &self.writing)
.field("keep_alive", &self.keep_alive.status())
.field("keep_alive", &self.keep_alive)
.field("error", &self.error)
//.field("method", &self.method)
.field("read_task", &self.read_task)
@@ -786,15 +820,8 @@ impl ::std::ops::BitAndAssign<bool> for KA {
}
}
pub trait KeepAlive: fmt::Debug + ::std::ops::BitAndAssign<bool> {
fn busy(&mut self);
fn disable(&mut self);
fn idle(&mut self);
fn status(&self) -> KA;
}
#[derive(Clone, Copy, Debug)]
pub enum KA {
enum KA {
Idle,
Busy,
Disabled,
@@ -806,7 +833,7 @@ impl Default for KA {
}
}
impl KeepAlive for KA {
impl KA {
fn idle(&mut self) {
*self = KA::Idle;
}
@@ -824,7 +851,7 @@ impl KeepAlive for KA {
}
}
impl<K: KeepAlive> State<K> {
impl State {
fn close(&mut self) {
trace!("State::close()");
self.reading = Reading::Closed;
@@ -976,7 +1003,7 @@ mod tests {
let good_message = b"GET / HTTP/1.1\r\n\r\n".to_vec();
let len = good_message.len();
let io = AsyncIo::new_buf(good_message, len);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
match conn.poll().unwrap() {
Async::Ready(Some(Frame::Message { message, body: false })) => {
@@ -994,7 +1021,7 @@ mod tests {
let _: Result<(), ()> = future::lazy(|| {
let good_message = b"GET / HTTP/1.1\r\nHost: foo.bar\r\n\r\n".to_vec();
let io = AsyncIo::new_buf(good_message, 10);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
assert!(conn.poll().unwrap().is_not_ready());
conn.io.io_mut().block_in(50);
let async = conn.poll().unwrap();
@@ -1010,7 +1037,7 @@ mod tests {
#[test]
fn test_conn_init_read_eof_idle() {
let io = AsyncIo::new_buf(vec![], 1);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.idle();
match conn.poll().unwrap() {
@@ -1022,7 +1049,7 @@ mod tests {
#[test]
fn test_conn_init_read_eof_idle_partial_parse() {
let io = AsyncIo::new_buf(b"GET / HTTP/1.1".to_vec(), 100);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.idle();
match conn.poll() {
@@ -1036,7 +1063,7 @@ mod tests {
let _: Result<(), ()> = future::lazy(|| {
// server ignores
let io = AsyncIo::new_eof();
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.busy();
match conn.poll().unwrap() {
@@ -1046,7 +1073,7 @@ mod tests {
// client
let io = AsyncIo::new_eof();
let mut conn = Conn::<_, proto::Chunk, ClientTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ClientTransaction>::new(io);
conn.state.busy();
match conn.poll() {
@@ -1061,7 +1088,7 @@ mod tests {
fn test_conn_body_finish_read_eof() {
let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_eof();
let mut conn = Conn::<_, proto::Chunk, ClientTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ClientTransaction>::new(io);
conn.state.busy();
conn.state.writing = Writing::KeepAlive;
conn.state.reading = Reading::Body(Decoder::length(0));
@@ -1086,7 +1113,7 @@ mod tests {
fn test_conn_message_empty_body_read_eof() {
let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".to_vec(), 1024);
let mut conn = Conn::<_, proto::Chunk, ClientTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ClientTransaction>::new(io);
conn.state.busy();
conn.state.writing = Writing::KeepAlive;
@@ -1110,7 +1137,7 @@ mod tests {
fn test_conn_read_body_end() {
let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(b"POST / HTTP/1.1\r\nContent-Length: 5\r\n\r\n12345".to_vec(), 1024);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.busy();
match conn.poll() {
@@ -1140,7 +1167,7 @@ mod tests {
#[test]
fn test_conn_closed_read() {
let io = AsyncIo::new_buf(vec![], 0);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.close();
match conn.poll().unwrap() {
@@ -1155,7 +1182,7 @@ mod tests {
let _ = pretty_env_logger::try_init();
let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 0);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
let max = super::super::io::DEFAULT_MAX_BUFFER_SIZE + 4096;
conn.state.writing = Writing::Body(Encoder::length((max * 2) as u64));
@@ -1180,7 +1207,7 @@ mod tests {
fn test_conn_body_write_chunked() {
let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.writing = Writing::Body(Encoder::chunked());
assert!(conn.start_send(Frame::Body { chunk: Some("headers".into()) }).unwrap().is_ready());
@@ -1193,7 +1220,7 @@ mod tests {
fn test_conn_body_flush() {
let _: Result<(), ()> = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 1024 * 1024 * 5);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.writing = Writing::Body(Encoder::length(1024 * 1024));
assert!(conn.start_send(Frame::Body { chunk: Some(vec![b'a'; 1024 * 1024].into()) }).unwrap().is_ready());
assert!(!conn.can_buffer_body());
@@ -1230,7 +1257,7 @@ mod tests {
// test that once writing is done, unparks
let f = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.reading = Reading::KeepAlive;
assert!(conn.poll().unwrap().is_not_ready());
@@ -1244,7 +1271,7 @@ mod tests {
// test that flushing when not waiting on read doesn't unpark
let f = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.writing = Writing::KeepAlive;
assert!(conn.poll_complete().unwrap().is_ready());
Ok::<(), ()>(())
@@ -1255,7 +1282,7 @@ mod tests {
// test that flushing and writing isn't done doesn't unpark
let f = future::lazy(|| {
let io = AsyncIo::new_buf(vec![], 4096);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.reading = Reading::KeepAlive;
assert!(conn.poll().unwrap().is_not_ready());
conn.state.writing = Writing::Body(Encoder::length(5_000));
@@ -1268,7 +1295,7 @@ mod tests {
#[test]
fn test_conn_closed_write() {
let io = AsyncIo::new_buf(vec![], 0);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.close();
match conn.start_send(Frame::Body { chunk: Some(b"foobar".to_vec().into()) }) {
@@ -1282,7 +1309,7 @@ mod tests {
#[test]
fn test_conn_write_empty_chunk() {
let io = AsyncIo::new_buf(vec![], 0);
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io, Default::default());
let mut conn = Conn::<_, proto::Chunk, ServerTransaction>::new(io);
conn.state.writing = Writing::KeepAlive;
assert!(conn.start_send(Frame::Body { chunk: None }).unwrap().is_ready());

View File

@@ -1,15 +1,16 @@
use std::io;
use bytes::Bytes;
use futures::{Async, AsyncSink, Future, Poll, Stream};
use futures::sync::oneshot;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_service::Service;
use proto::{Body, Conn, KeepAlive, Http1Transaction, MessageHead, RequestHead, ResponseHead};
use proto::{Body, Conn, Http1Transaction, MessageHead, RequestHead, ResponseHead};
use ::StatusCode;
pub struct Dispatcher<D, Bs, I, B, T, K> {
conn: Conn<I, B, T, K>,
pub struct Dispatcher<D, Bs, I, B, T> {
conn: Conn<I, B, T>,
dispatch: D,
body_tx: Option<::proto::body::ChunkSender>,
body_rx: Option<Bs>,
@@ -40,16 +41,15 @@ pub type ClientMsg<B> = (RequestHead, Option<B>);
type ClientRx<B> = ::client::dispatch::Receiver<ClientMsg<B>, ::Response>;
impl<D, Bs, I, B, T, K> Dispatcher<D, Bs, I, B, T, K>
impl<D, Bs, I, B, T> Dispatcher<D, Bs, I, B, T>
where
D: Dispatch<PollItem=MessageHead<T::Outgoing>, PollBody=Bs, RecvItem=MessageHead<T::Incoming>>,
I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>,
T: Http1Transaction,
K: KeepAlive,
Bs: Stream<Item=B, Error=::Error>,
{
pub fn new(dispatch: D, conn: Conn<I, B, T, K>) -> Self {
pub fn new(dispatch: D, conn: Conn<I, B, T>) -> Self {
Dispatcher {
conn: conn,
dispatch: dispatch,
@@ -63,15 +63,44 @@ where
self.conn.disable_keep_alive()
}
fn poll2(&mut self) -> Poll<(), ::Error> {
pub fn into_inner(self) -> (I, Bytes) {
self.conn.into_inner()
}
/// The "Future" poll function. Runs this dispatcher until the
/// connection is shutdown, or an error occurs.
pub fn poll_until_shutdown(&mut self) -> Poll<(), ::Error> {
self.poll_catch(true)
}
/// Run this dispatcher until HTTP says this connection is done,
/// but don't call `AsyncWrite::shutdown` on the underlying IO.
///
/// This is useful for HTTP upgrades.
pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> {
self.poll_catch(false)
}
fn poll_catch(&mut self, should_shutdown: bool) -> Poll<(), ::Error> {
self.poll_inner(should_shutdown).or_else(|e| {
// An error means we're shutting down either way.
// We just try to give the error to the user,
// and close the connection with an Ok. If we
// cannot give it to the user, then return the Err.
self.dispatch.recv_msg(Err(e)).map(Async::Ready)
})
}
fn poll_inner(&mut self, should_shutdown: bool) -> Poll<(), ::Error> {
self.poll_read()?;
self.poll_write()?;
self.poll_flush()?;
if self.is_done() {
try_ready!(self.conn.shutdown());
if should_shutdown {
try_ready!(self.conn.shutdown());
}
self.conn.take_error()?;
trace!("Dispatch::poll done");
Ok(Async::Ready(()))
} else {
Ok(Async::NotReady)
@@ -183,7 +212,7 @@ where
loop {
if self.is_closing {
return Ok(Async::Ready(()));
} else if self.body_rx.is_none() && self.dispatch.should_poll() {
} else if self.body_rx.is_none() && self.conn.can_write_head() && self.dispatch.should_poll() {
if let Some((head, body)) = try_ready!(self.dispatch.poll_msg()) {
self.conn.write_head(head, body.is_some());
self.body_rx = body;
@@ -257,13 +286,12 @@ where
}
impl<D, Bs, I, B, T, K> Future for Dispatcher<D, Bs, I, B, T, K>
impl<D, Bs, I, B, T> Future for Dispatcher<D, Bs, I, B, T>
where
D: Dispatch<PollItem=MessageHead<T::Outgoing>, PollBody=Bs, RecvItem=MessageHead<T::Incoming>>,
I: AsyncRead + AsyncWrite,
B: AsRef<[u8]>,
T: Http1Transaction,
K: KeepAlive,
Bs: Stream<Item=B, Error=::Error>,
{
type Item = ();
@@ -271,14 +299,7 @@ where
#[inline]
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
trace!("Dispatcher::poll");
self.poll2().or_else(|e| {
// An error means we're shutting down either way.
// We just try to give the error to the user,
// and close the connection with an Ok. If we
// cannot give it to the user, then return the Err.
self.dispatch.recv_msg(Err(e)).map(Async::Ready)
})
self.poll_until_shutdown()
}
}
@@ -445,8 +466,8 @@ mod tests {
let _ = pretty_env_logger::try_init();
::futures::lazy(|| {
let io = AsyncIo::new_buf(b"HTTP/1.1 200 OK\r\n\r\n".to_vec(), 100);
let (tx, rx) = ::client::dispatch::channel();
let conn = Conn::<_, ::Chunk, ClientTransaction>::new(io, Default::default());
let (mut tx, rx) = ::client::dispatch::channel();
let conn = Conn::<_, ::Chunk, ClientTransaction>::new(io);
let mut dispatcher = Dispatcher::new(Client::new(rx), conn);
let req = RequestHead {

View File

@@ -152,6 +152,10 @@ where
})
}
pub fn into_inner(self) -> (T, Bytes) {
(self.io, self.read_buf.freeze())
}
pub fn io_mut(&mut self) -> &mut T {
&mut self.io
}

View File

@@ -1,4 +1,4 @@
pub use self::conn::{Conn, KeepAlive, KA};
pub use self::conn::Conn;
pub use self::decode::Decoder;
pub use self::encode::{EncodedBuf, Encoder};

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

View File

@@ -16,7 +16,7 @@ pub use self::body::Body;
#[cfg(feature = "tokio-proto")]
pub use self::body::TokioBody;
pub use self::chunk::Chunk;
pub use self::h1::{dispatch, Conn, KeepAlive, KA};
pub use self::h1::{dispatch, Conn};
mod body;
mod chunk;
@@ -134,17 +134,16 @@ pub fn expecting_continue(version: HttpVersion, headers: &Headers) -> bool {
ret
}
#[derive(Debug)]
pub enum ServerTransaction {}
pub type ServerTransaction = h1::role::Server<h1::role::NoUpgrades>;
#[derive(Debug)]
pub enum ClientTransaction {}
pub type ClientTransaction = h1::role::Client<h1::role::NoUpgrades>;
pub type ClientUpgradeTransaction = h1::role::Client<h1::role::YesUpgrades>;
pub trait Http1Transaction {
type Incoming;
type Outgoing: Default;
fn parse(bytes: &mut BytesMut) -> ParseResult<Self::Incoming>;
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<::Method>) -> ::Result<Option<h1::Decoder>>;
fn decoder(head: &MessageHead<Self::Incoming>, method: &mut Option<::Method>) -> ::Result<Decode>;
fn encode(head: MessageHead<Self::Outgoing>, has_body: bool, method: &mut Option<Method>, dst: &mut Vec<u8>) -> ::Result<h1::Encoder>;
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>>;
@@ -154,6 +153,16 @@ pub trait Http1Transaction {
pub type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>;
#[derive(Debug)]
pub enum Decode {
/// Decode normally.
Normal(h1::Decoder),
/// After this decoder is done, HTTP is done.
Final(h1::Decoder),
/// A header block that should be ignored, like unknown 1xx responses.
Ignore,
}
#[test]
fn test_should_keep_alive() {
let mut headers = Headers::new();