@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user