Files
hyper/src/http/conn.rs
Joe Wilm 20fac49aa9 fix(http): Prevent busy looping
We encountered some issues where the `Conn::ready()` would busy loop on
reads. Previously, the `ConnInner::can_read_more()` would not consider
whether the previous read got a WouldBlock error, and it didn't consider
whether the transport was blocked. Accounting for this additional state
fixes the busy loop problem.
2016-10-13 14:38:36 -07:00

1145 lines
45 KiB
Rust

use std::borrow::Cow;
use std::fmt;
use std::hash::Hash;
use std::io;
use std::marker::PhantomData;
use std::mem;
use std::time::Duration;
use rotor::{self, EventSet, PollOpt, Scope, Time};
use http::{self, h1, Http1Message, Encoder, Decoder, Next, Next_, Reg, Control};
use http::channel;
use http::internal::WriteBuf;
use http::buffer::Buffer;
use net::{Transport, Blocked};
use version::HttpVersion;
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
/// This handles a connection, which will have been established over a
/// Transport (like a socket), and will likely include multiple
/// `Message`s over HTTP.
///
/// The connection will determine when a message begins and ends, creating
/// a new message `MessageHandler` for each one, as well as determine if this
/// connection can be kept alive after the message, or if it is complete.
pub struct Conn<K: Key, T: Transport, H: MessageHandler<T>>(Box<ConnInner<K, T, H>>);
/// `ConnInner` contains all of a connections state which Conn proxies for in a way
/// that allows Conn to maintain convenient move and self consuming method call
/// semantics but avoiding many costly memcpy calls.
struct ConnInner<K: Key, T: Transport, H: MessageHandler<T>> {
buf: Buffer,
ctrl: (channel::Sender<Next>, channel::Receiver<Next>),
keep_alive_enabled: bool,
key: K,
state: State<H, T>,
transport: T,
/// Records a WouldBlock error when trying to read
///
/// This flag is used to prevent busy looping
read_would_block: bool,
}
impl<K: Key, T: Transport, H: MessageHandler<T>> fmt::Debug for ConnInner<K, T, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Conn")
.field("keep_alive_enabled", &self.keep_alive_enabled)
.field("state", &self.state)
.field("buf", &self.buf)
.finish()
}
}
impl<K: Key, T: Transport, H: MessageHandler<T>> fmt::Debug for Conn<K, T, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> {
/// Desired Register interest based on state of current connection.
///
/// This includes the user interest, such as when they return `Next::read()`.
fn interest(&self) -> Reg {
match self.state {
State::Closed => Reg::Remove,
State::Init { interest, .. } => {
interest.register()
}
State::Http1(Http1 { reading: Reading::Closed, writing: Writing::Closed, .. }) => {
Reg::Remove
}
State::Http1(Http1 { ref reading, ref writing, .. }) => {
let read = match *reading {
Reading::Parse |
Reading::Body(..) => Reg::Read,
Reading::Init |
Reading::Wait(..) |
Reading::KeepAlive |
Reading::Closed => Reg::Wait
};
let write = match *writing {
Writing::Head |
Writing::Chunk(..) |
Writing::Ready(..) => Reg::Write,
Writing::Init |
Writing::Wait(..) |
Writing::KeepAlive => Reg::Wait,
Writing::Closed => Reg::Wait,
};
match (read, write) {
(Reg::Read, Reg::Write) => Reg::ReadWrite,
(Reg::Read, Reg::Wait) => Reg::Read,
(Reg::Wait, Reg::Write) => Reg::Write,
(Reg::Wait, Reg::Wait) => Reg::Wait,
_ => unreachable!("bad read/write reg combo")
}
}
}
}
/// Actual register action.
///
/// Considers the user interest(), but also compares if the underlying
/// transport is blocked(), and adjusts accordingly.
fn register(&self) -> Reg {
let reg = self.interest();
match (reg, self.transport.blocked()) {
(Reg::Remove, _) |
(Reg::Wait, _) |
(_, None) => reg,
(_, Some(Blocked::Read)) => Reg::Read,
(_, Some(Blocked::Write)) => Reg::Write,
}
}
fn parse(&mut self) -> ::Result<http::MessageHead<<<H as MessageHandler<T>>::Message as Http1Message>::Incoming>> {
match self.buf.read_from(&mut self.transport) {
Ok(0) => {
trace!("parse eof");
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "parse eof").into());
}
Ok(_) => {},
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => {},
_ => return Err(e.into())
}
}
match try!(http::parse::<<H as MessageHandler<T>>::Message, _>(self.buf.bytes())) {
Some((head, len)) => {
trace!("parsed {} bytes out of {}", len, self.buf.len());
self.buf.consume(len);
Ok(head)
},
None => {
if self.buf.len() >= MAX_BUFFER_SIZE {
//TODO: Handler.on_too_large_error()
debug!("MAX_BUFFER_SIZE reached, closing");
Err(::Error::TooLarge)
} else {
Err(io::Error::new(io::ErrorKind::WouldBlock, "incomplete parse").into())
}
},
}
}
fn read<F: MessageHandlerFactory<K, T, Output=H>>(&mut self, scope: &mut Scope<F>, state: State<H, T>) -> State<H, T> {
match state {
State::Init { interest: Next_::Read, .. } => {
let head = match self.parse() {
Ok(head) => head,
Err(::Error::Io(e)) => match e.kind() {
io::ErrorKind::WouldBlock |
io::ErrorKind::Interrupted => {
self.read_would_block = true;
return state;
},
_ => {
debug!("io error trying to parse {:?}", e);
return State::Closed;
}
},
Err(e) => {
//TODO: send proper error codes depending on error
trace!("parse eror: {:?}", e);
return State::Closed;
}
};
let mut handler = match scope.create(Seed(&self.key, &self.ctrl.0)) {
Some(handler) => handler,
None => unreachable!()
};
match H::Message::decoder(&head) {
Ok(decoder) => {
trace!("decoder = {:?}", decoder);
let keep_alive = self.keep_alive_enabled && head.should_keep_alive();
let next = handler.on_incoming(head, &self.transport);
trace!("handler.on_incoming() -> {:?}", next);
let now = scope.now();
match next.interest {
Next_::Read => self.read(scope, State::Http1(Http1 {
handler: handler,
reading: Reading::Body(decoder),
writing: Writing::Init,
keep_alive: keep_alive,
timeout: next.timeout,
timeout_start: Some(now),
_marker: PhantomData,
})),
Next_::Write => State::Http1(Http1 {
handler: handler,
reading: if decoder.is_eof() {
if keep_alive {
Reading::KeepAlive
} else {
Reading::Closed
}
} else {
Reading::Wait(decoder)
},
writing: Writing::Head,
keep_alive: keep_alive,
timeout: next.timeout,
timeout_start: Some(now),
_marker: PhantomData,
}),
Next_::ReadWrite => self.read(scope, State::Http1(Http1 {
handler: handler,
reading: Reading::Body(decoder),
writing: Writing::Head,
keep_alive: keep_alive,
timeout: next.timeout,
timeout_start: Some(now),
_marker: PhantomData,
})),
Next_::Wait => State::Http1(Http1 {
handler: handler,
reading: Reading::Wait(decoder),
writing: Writing::Init,
keep_alive: keep_alive,
timeout: next.timeout,
timeout_start: Some(now),
_marker: PhantomData,
}),
Next_::End |
Next_::Remove => State::Closed,
}
},
Err(e) => {
debug!("error creating decoder: {:?}", e);
//TODO: update state from returned Next
//this would allow a Server to respond with a proper
//4xx code
let _ = handler.on_error(e);
State::Closed
}
}
},
State::Init { interest: Next_::Wait, .. } => {
match self.buf.read_from(&mut self.transport) {
Ok(0) => {
// End-of-file; connection was closed by peer
State::Closed
},
Ok(n) => {
// Didn't expect bytes here! Close the connection.
warn!("read {} bytes in State::Init with Wait interest", n);
State::Closed
},
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => {
// This is the expected case reading in this state
self.read_would_block = true;
state
},
_ => {
warn!("socket error reading State::Init with Wait interest: {}", e);
State::Closed
}
}
}
},
State::Init { .. } => {
trace!("on_readable State::{:?}", state);
state
},
State::Http1(mut http1) => {
let next = match http1.reading {
Reading::Init => None,
Reading::Parse => match self.parse() {
Ok(head) => match H::Message::decoder(&head) {
Ok(decoder) => {
trace!("decoder = {:?}", decoder);
// if client request asked for keep alive,
// then it depends entirely on if the server agreed
if http1.keep_alive {
http1.keep_alive = head.should_keep_alive();
}
let next = http1.handler.on_incoming(head, &self.transport);
http1.reading = Reading::Wait(decoder);
trace!("handler.on_incoming() -> {:?}", next);
Some(next)
},
Err(e) => {
debug!("error creating decoder: {:?}", e);
//TODO: respond with 400
return State::Closed;
}
},
Err(::Error::Io(e)) => match e.kind() {
io::ErrorKind::WouldBlock |
io::ErrorKind::Interrupted => {
self.read_would_block = true;
None
},
_ => {
debug!("io error trying to parse {:?}", e);
return State::Closed;
}
},
Err(e) => {
trace!("parse error: {:?}", e);
let _ = http1.handler.on_error(e);
return State::Closed;
}
},
Reading::Body(ref mut decoder) => {
let wrapped = if !self.buf.is_empty() {
super::Trans::Buf(self.buf.wrap(&mut self.transport))
} else {
super::Trans::Port(&mut self.transport)
};
Some(http1.handler.on_decode(&mut Decoder::h1(decoder, wrapped)))
},
_ => {
trace!("Conn.on_readable State::Http1(reading = {:?})", http1.reading);
None
}
};
let mut s = State::Http1(http1);
if let Some(next) = next {
s.update(next, &**scope, Some(scope.now()));
}
trace!("Conn.on_readable State::Http1 completed, new state = State::{:?}", s);
let again = match s {
State::Http1(Http1 { reading: Reading::Body(ref encoder), .. }) => encoder.is_eof(),
_ => false
};
if again {
self.read(scope, s)
} else {
s
}
},
State::Closed => {
trace!("on_readable State::Closed");
State::Closed
}
}
}
fn write<F: MessageHandlerFactory<K, T, Output=H>>(&mut self, scope: &mut Scope<F>, mut state: State<H, T>) -> State<H, T> {
let next = match state {
State::Init { interest: Next_::Write, .. } => {
// this is a Client request, which writes first, so pay
// attention to the version written here, which will adjust
// our internal state to Http1 or Http2
let mut handler = match scope.create(Seed(&self.key, &self.ctrl.0)) {
Some(handler) => handler,
None => {
trace!("could not create handler {:?}", self.key);
return State::Closed;
}
};
let mut head = http::MessageHead::default();
let mut interest = handler.on_outgoing(&mut head);
if head.version == HttpVersion::Http11 {
let mut buf = Vec::new();
let keep_alive = self.keep_alive_enabled && head.should_keep_alive();
let mut encoder = H::Message::encode(head, &mut buf);
let writing = match interest.interest {
// user wants to write some data right away
// try to write the headers and the first chunk
// together, so they are in the same packet
Next_::Write |
Next_::ReadWrite => {
encoder.prefix(WriteBuf {
bytes: buf,
pos: 0
});
interest = handler.on_encode(&mut Encoder::h1(&mut encoder, &mut self.transport));
Writing::Ready(encoder)
},
_ => Writing::Chunk(Chunk {
buf: Cow::Owned(buf),
pos: 0,
next: (encoder, interest.clone())
})
};
state = State::Http1(Http1 {
reading: Reading::Init,
writing: writing,
handler: handler,
keep_alive: keep_alive,
timeout: interest.timeout,
timeout_start: Some(scope.now()),
_marker: PhantomData,
})
}
Some(interest)
}
State::Init { .. } => {
trace!("Conn.on_writable State::{:?}", state);
None
}
State::Http1(Http1 { ref mut handler, ref mut writing, ref mut keep_alive, .. }) => {
match *writing {
Writing::Init => {
trace!("Conn.on_writable Http1::Writing::Init");
None
}
Writing::Head => {
let mut head = http::MessageHead::default();
let mut interest = handler.on_outgoing(&mut head);
// if the request wants to close, server cannot stop it
if *keep_alive {
// if the request wants to stay alive, then it depends
// on the server to agree
*keep_alive = head.should_keep_alive();
}
let mut buf = Vec::new();
let mut encoder = <<H as MessageHandler<T>>::Message as Http1Message>::encode(head, &mut buf);
*writing = match interest.interest {
// user wants to write some data right away
// try to write the headers and the first chunk
// together, so they are in the same packet
Next_::Write |
Next_::ReadWrite => {
encoder.prefix(WriteBuf {
bytes: buf,
pos: 0
});
interest = handler.on_encode(&mut Encoder::h1(&mut encoder, &mut self.transport));
Writing::Ready(encoder)
},
_ => Writing::Chunk(Chunk {
buf: Cow::Owned(buf),
pos: 0,
next: (encoder, interest.clone())
})
};
Some(interest)
},
Writing::Chunk(ref mut chunk) => {
trace!("Http1.Chunk on_writable");
match self.transport.write(&chunk.buf.as_ref()[chunk.pos..]) {
Ok(n) => {
chunk.pos += n;
trace!("Http1.Chunk wrote={}, done={}", n, chunk.is_written());
if chunk.is_written() {
Some(chunk.next.1.clone())
} else {
None
}
},
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock |
io::ErrorKind::Interrupted => None,
_ => {
Some(handler.on_error(e.into()))
}
}
}
},
Writing::Ready(ref mut encoder) => {
trace!("Http1.Ready on_writable");
Some(handler.on_encode(&mut Encoder::h1(encoder, &mut self.transport)))
},
Writing::Wait(..) => {
trace!("Conn.on_writable Http1::Writing::Wait");
None
}
Writing::KeepAlive => {
trace!("Conn.on_writable Http1::Writing::KeepAlive");
None
}
Writing::Closed => {
trace!("on_writable Http1::Writing::Closed");
None
}
}
},
State::Closed => {
trace!("on_writable State::Closed");
None
}
};
if let Some(next) = next {
state.update(next, &**scope, Some(scope.now()));
}
state
}
fn can_read_more(&self, was_init: bool) -> bool {
let transport_blocked = self.transport.blocked().is_some();
let read_would_block = self.read_would_block;
let state_machine_ok = match self.state {
State::Init { .. } => !was_init && !self.buf.is_empty(),
_ => !self.buf.is_empty()
};
!transport_blocked && !read_would_block && state_machine_ok
}
fn on_error<F>(&mut self, err: ::Error, factory: &F) where F: MessageHandlerFactory<K, T> {
debug!("on_error err = {:?}", err);
trace!("on_error state = {:?}", self.state);
let next = match self.state {
State::Init { .. } => Next::remove(),
State::Http1(ref mut http1) => http1.handler.on_error(err),
State::Closed => Next::remove(),
};
self.state.update(next, factory, None);
}
fn on_readable<F>(&mut self, scope: &mut Scope<F>)
where F: MessageHandlerFactory<K, T, Output=H> {
// Clear would_block flag so state is clear going into read
self.read_would_block = false;
trace!("on_readable -> {:?}", self.state);
let state = mem::replace(&mut self.state, State::Closed);
self.state = self.read(scope, state);
trace!("on_readable <- {:?}", self.state);
}
fn on_writable<F>(&mut self, scope: &mut Scope<F>)
where F: MessageHandlerFactory<K, T, Output=H> {
trace!("on_writable -> {:?}", self.state);
let state = mem::replace(&mut self.state, State::Closed);
self.state = self.write(scope, state);
trace!("on_writable <- {:?}", self.state);
}
fn on_remove(self) {
debug!("on_remove");
match self.state {
State::Init { .. } | State::Closed => (),
State::Http1(http1) => http1.handler.on_remove(self.transport),
}
}
}
pub enum ReadyResult<C> {
Continue(C),
Done(Option<(C, Option<Duration>)>)
}
impl<K: Key, T: Transport, H: MessageHandler<T>> Conn<K, T, H> {
pub fn new(
key: K,
transport: T,
next: Next,
notify: rotor::Notifier,
now: Time
) -> Conn<K, T, H> {
Conn(Box::new(ConnInner {
buf: Buffer::new(),
ctrl: channel::new(notify),
keep_alive_enabled: true,
key: key,
state: State::Init {
interest: next.interest,
timeout: next.timeout,
timeout_start: Some(now),
},
transport: transport,
read_would_block: false,
}))
}
pub fn keep_alive(mut self, val: bool) -> Conn<K, T, H> {
self.0.keep_alive_enabled = val;
self
}
pub fn ready<F>(
mut self,
events: EventSet,
scope: &mut Scope<F>
) -> ReadyResult<Self>
where F: MessageHandlerFactory<K, T, Output=H>
{
trace!("Conn::ready events='{:?}', blocked={:?}", events, self.0.transport.blocked());
if events.is_error() {
match self.0.transport.take_socket_error() {
Ok(_) => {
trace!("is_error, but not socket error");
// spurious?
},
Err(e) => self.0.on_error(e.into(), &**scope)
}
}
if events.is_hup() {
trace!("Conn::ready got hangup");
let _ = scope.deregister(&self.0.transport);
self.on_remove();
return ReadyResult::Done(None);
}
// if the user had an io interest, but the transport was blocked differently,
// the event needs to be translated to what the user was actually expecting.
//
// Example:
// - User asks for `Next::write().
// - But transport is in the middle of renegotiating TLS, and is blocked on reading.
// - hyper should not wait on the `write` event, since epoll already
// knows it is writable. We would just loop a whole bunch, and slow down.
// - So instead, hyper waits on the event needed to unblock the transport, `read`.
// - Once epoll detects the transport is readable, it will alert hyper
// with a `readable` event.
// - hyper needs to translate that `readable` event back into a `write`,
// since that is actually what the Handler wants.
let events = if let Some(blocked) = self.0.transport.blocked() {
let interest = self.0.interest();
trace!("translating blocked={:?}, interest={:?}", blocked, interest);
match (blocked, interest) {
(Blocked::Read, Reg::Write) => EventSet::writable(),
(Blocked::Write, Reg::Read) => EventSet::readable(),
// otherwise, the transport was blocked on the same thing the user wanted
_ => events
}
} else {
events
};
let was_init = match self.0.state {
State::Init { .. } => true,
_ => false
};
if events.is_readable() {
self.0.on_readable(scope);
}
if events.is_writable() {
self.0.on_writable(scope);
}
let mut events = match self.0.register() {
Reg::Read => EventSet::readable(),
Reg::Write => EventSet::writable(),
Reg::ReadWrite => EventSet::readable() | EventSet::writable(),
Reg::Wait => EventSet::none(),
Reg::Remove => {
trace!("removing transport");
let _ = scope.deregister(&self.0.transport);
self.on_remove();
return ReadyResult::Done(None);
},
};
if events.is_readable() && self.0.can_read_more(was_init) {
return ReadyResult::Continue(self);
}
events = events | EventSet::hup();
trace!("scope.reregister({:?})", events);
match scope.reregister(&self.0.transport, events, PollOpt::level()) {
Ok(..) => {
let timeout = self.0.state.timeout();
ReadyResult::Done(Some((self, timeout)))
},
Err(e) => {
trace!("error reregistering: {:?}", e);
self.0.on_error(e.into(), &**scope);
ReadyResult::Done(None)
}
}
}
pub fn wakeup<F>(mut self, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)>
where F: MessageHandlerFactory<K, T, Output=H> {
while let Ok(next) = self.0.ctrl.1.try_recv() {
trace!("woke up with {:?}", next);
let timeout_start = self.0.state.timeout_start();
self.0.state.update(next, &**scope, timeout_start);
}
let mut conn = Some(self);
loop {
match conn.take().unwrap().ready(EventSet::readable() | EventSet::writable(), scope) {
ReadyResult::Done(val) => return val,
ReadyResult::Continue(c) => conn = Some(c),
}
}
}
pub fn timeout<F>(mut self, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)>
where F: MessageHandlerFactory<K, T, Output=H> {
// Run error handler if timeout has elapsed
if self.0.state.timeout_elapsed(scope.now()) {
self.0.on_error(::Error::Timeout, &**scope);
}
let mut conn = Some(self);
loop {
match conn.take().unwrap().ready(EventSet::none(), scope) {
ReadyResult::Done(val) => return val,
ReadyResult::Continue(c) => conn = Some(c),
}
}
}
fn on_remove(self) {
self.0.on_remove()
}
pub fn key(&self) -> &K {
&self.0.key
}
pub fn control(&self) -> Control {
Control {
tx: self.0.ctrl.0.clone(),
}
}
pub fn is_idle(&self) -> bool {
if let State::Init { interest: Next_::Wait, .. } = self.0.state {
true
} else {
false
}
}
}
enum State<H: MessageHandler<T>, T: Transport> {
Init {
interest: Next_,
timeout: Option<Duration>,
timeout_start: Option<Time>,
},
/// Http1 will only ever use a connection to send and receive a single
/// message at a time. Once a H1 status has been determined, we will either
/// be reading or writing an H1 message, and optionally multiple if
/// keep-alive is true.
Http1(Http1<H, T>),
/// Http2 allows multiplexing streams over a single connection. So even
/// when we've identified a certain message, we must always parse frame
/// head to determine if the incoming frame is part of a current message,
/// or a new one. This also means we could have multiple messages at once.
//Http2 {},
Closed,
}
/// Given two rotor::Time and a duration, see if the duration has elapsed.
///
/// The rotor::Time type only implements Add<Duration>, doesn't provide an API for comparing
/// itself with other rotor::Time, and it doesn't implement arithmetic operations with itself.
///
/// `Time` is just a newtype around (u64). Since there's no other way to compare them, we'll just
/// use this knowledge to actually do a comparison.
fn timeout_elapsed(timeout: Duration, start: Time, now: Time) -> bool {
// type annotation for sanity
let timeout_at: rotor::Time = start + timeout;
let timeout_at: u64 = unsafe { mem::transmute(timeout_at) };
let now: u64 = unsafe { mem::transmute(now) };
if now >= timeout_at {
true
} else {
false
}
}
impl<H: MessageHandler<T>, T: Transport> State<H, T> {
fn timeout(&self) -> Option<Duration> {
match *self {
State::Init { timeout, .. } => timeout,
State::Http1(ref http1) => http1.timeout,
State::Closed => None,
}
}
fn timeout_start(&self) -> Option<Time> {
match *self {
State::Init { timeout_start, .. } => timeout_start,
State::Http1(ref http1) => http1.timeout_start,
State::Closed => None,
}
}
fn timeout_elapsed(&self, now: Time) -> bool {
match *self {
State::Init { timeout, timeout_start, .. } => {
if let (Some(timeout), Some(start)) = (timeout, timeout_start) {
timeout_elapsed(timeout, start, now)
} else {
false
}
},
State::Http1(ref http1) => http1.timeout_elapsed(now),
State::Closed => false,
}
}
}
impl<H: MessageHandler<T>, T: Transport> fmt::Debug for State<H, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
State::Init { interest, timeout, timeout_start } => f.debug_struct("Init")
.field("interest", &interest)
.field("timeout", &timeout)
.field("timeout_start", &timeout_start)
.finish(),
State::Http1(ref h1) => f.debug_tuple("Http1")
.field(h1)
.finish(),
State::Closed => f.write_str("Closed")
}
}
}
impl<H: MessageHandler<T>, T: Transport> State<H, T> {
fn update<F, K>(&mut self, next: Next, factory: &F, timeout_start: Option<Time>)
where F: MessageHandlerFactory<K, T>,
K: Key
{
let timeout = next.timeout;
let state = mem::replace(self, State::Closed);
trace!("State::update state={:?}, interest={:?}", state, next.interest);
match (state, next.interest) {
(_, Next_::Remove) |
(State::Closed, _) => return, // Keep State::Closed.
(State::Init { .. }, e) => {
mem::replace(self,
State::Init {
interest: e,
timeout: timeout,
timeout_start: timeout_start,
});
}
(State::Http1(mut http1), next_) => {
match next_ {
Next_::Remove => unreachable!(), // Covered in (_, Next_::Remove) case above.
Next_::End => {
let reading = match http1.reading {
Reading::Body(ref decoder) |
Reading::Wait(ref decoder) if decoder.is_eof() => {
if http1.keep_alive {
Reading::KeepAlive
} else {
Reading::Closed
}
}
Reading::KeepAlive => http1.reading,
_ => Reading::Closed,
};
let mut writing = Writing::Closed;
let encoder = match http1.writing {
Writing::Wait(enc) |
Writing::Ready(enc) => Some(enc),
Writing::Chunk(mut chunk) => {
if chunk.is_written() {
Some(chunk.next.0)
} else {
chunk.next.1 = next;
writing = Writing::Chunk(chunk);
None
}
}
Writing::KeepAlive => {
writing = Writing::KeepAlive;
None
}
_ => return, // Keep State::Closed.
};
if let Some(encoder) = encoder {
if encoder.is_eof() {
if http1.keep_alive {
writing = Writing::KeepAlive
}
} else if let Some(buf) = encoder.finish() {
writing = Writing::Chunk(Chunk {
buf: buf.bytes,
pos: buf.pos,
next: (h1::Encoder::length(0), Next::end()),
})
}
};
trace!("(reading, writing) -> {:?}", (&reading, &writing));
match (reading, writing) {
(Reading::KeepAlive, Writing::KeepAlive) => {
let next = factory.keep_alive_interest();
mem::replace(self,
State::Init {
interest: next.interest,
timeout: next.timeout,
timeout_start: timeout_start,
});
return;
}
(reading, Writing::Chunk(chunk)) => {
http1.reading = reading;
http1.writing = Writing::Chunk(chunk);
}
_ => return, // Keep State::Closed.
}
}
Next_::Read => {
http1.reading = match http1.reading {
Reading::Init => Reading::Parse,
Reading::Wait(decoder) => Reading::Body(decoder),
same => same,
};
http1.writing = match http1.writing {
Writing::Ready(encoder) => {
if encoder.is_eof() {
if http1.keep_alive {
Writing::KeepAlive
} else {
Writing::Closed
}
} else if encoder.is_closed() {
if let Some(buf) = encoder.finish() {
Writing::Chunk(Chunk {
buf: buf.bytes,
pos: buf.pos,
next: (h1::Encoder::length(0), Next::wait()),
})
} else {
Writing::Closed
}
} else {
Writing::Wait(encoder)
}
}
Writing::Chunk(chunk) => {
if chunk.is_written() {
Writing::Wait(chunk.next.0)
} else {
Writing::Chunk(chunk)
}
}
same => same,
};
}
Next_::Write => {
http1.writing = match http1.writing {
Writing::Wait(encoder) => Writing::Ready(encoder),
Writing::Init => Writing::Head,
Writing::Chunk(chunk) => {
if chunk.is_written() {
Writing::Ready(chunk.next.0)
} else {
Writing::Chunk(chunk)
}
}
same => same,
};
http1.reading = match http1.reading {
Reading::Body(decoder) => {
if decoder.is_eof() {
if http1.keep_alive {
Reading::KeepAlive
} else {
Reading::Closed
}
} else {
Reading::Wait(decoder)
}
}
same => same,
};
}
Next_::ReadWrite => {
http1.reading = match http1.reading {
Reading::Init => Reading::Parse,
Reading::Wait(decoder) => Reading::Body(decoder),
same => same,
};
http1.writing = match http1.writing {
Writing::Wait(encoder) => Writing::Ready(encoder),
Writing::Init => Writing::Head,
Writing::Chunk(chunk) => {
if chunk.is_written() {
Writing::Ready(chunk.next.0)
} else {
Writing::Chunk(chunk)
}
}
same => same,
};
}
Next_::Wait => {
http1.reading = match http1.reading {
Reading::Body(decoder) => Reading::Wait(decoder),
same => same,
};
http1.writing = match http1.writing {
Writing::Ready(encoder) => Writing::Wait(encoder),
Writing::Chunk(chunk) => {
if chunk.is_written() {
Writing::Wait(chunk.next.0)
} else {
Writing::Chunk(chunk)
}
}
same => same,
};
}
}
http1.timeout = timeout;
mem::replace(self, State::Http1(http1));
}
};
}
}
// These Reading and Writing stuff should probably get moved into h1/message.rs
struct Http1<H, T> {
handler: H,
reading: Reading,
writing: Writing,
keep_alive: bool,
timeout: Option<Duration>,
timeout_start: Option<Time>,
_marker: PhantomData<T>,
}
impl<H, T> Http1<H, T> {
fn timeout_elapsed(&self, now: Time) -> bool {
if let (Some(timeout), Some(start)) = (self.timeout, self.timeout_start) {
timeout_elapsed(timeout, start, now)
} else {
false
}
}
}
impl<H, T> fmt::Debug for Http1<H, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Http1")
.field("reading", &self.reading)
.field("writing", &self.writing)
.field("keep_alive", &self.keep_alive)
.field("timeout", &self.timeout)
.finish()
}
}
#[derive(Debug)]
enum Reading {
Init,
Parse,
Body(h1::Decoder),
Wait(h1::Decoder),
KeepAlive,
Closed
}
#[derive(Debug)]
enum Writing {
Init,
Head,
Chunk(Chunk) ,
Ready(h1::Encoder),
Wait(h1::Encoder),
KeepAlive,
Closed
}
#[derive(Debug)]
struct Chunk {
buf: Cow<'static, [u8]>,
pos: usize,
next: (h1::Encoder, Next),
}
impl Chunk {
fn is_written(&self) -> bool {
self.pos >= self.buf.len()
}
}
pub trait MessageHandler<T: Transport> {
type Message: Http1Message;
fn on_incoming(&mut self, head: http::MessageHead<<Self::Message as Http1Message>::Incoming>, transport: &T) -> Next;
fn on_outgoing(&mut self, head: &mut http::MessageHead<<Self::Message as Http1Message>::Outgoing>) -> Next;
fn on_decode(&mut self, &mut http::Decoder<T>) -> Next;
fn on_encode(&mut self, &mut http::Encoder<T>) -> Next;
fn on_error(&mut self, err: ::Error) -> Next;
fn on_remove(self, T) where Self: Sized;
}
pub struct Seed<'a, K: Key + 'a>(&'a K, &'a channel::Sender<Next>);
impl<'a, K: Key + 'a> Seed<'a, K> {
pub fn control(&self) -> Control {
Control {
tx: self.1.clone(),
}
}
pub fn key(&self) -> &K {
self.0
}
}
pub trait MessageHandlerFactory<K: Key, T: Transport> {
type Output: MessageHandler<T>;
fn create(&mut self, seed: Seed<K>) -> Option<Self::Output>;
fn keep_alive_interest(&self) -> Next;
}
pub trait Key: Eq + Hash + Clone + fmt::Debug {}
impl<T: Eq + Hash + Clone + fmt::Debug> Key for T {}
#[cfg(test)]
mod tests {
/* TODO:
test when the underlying Transport of a Conn is blocked on an action that
differs from the desired interest().
Ex:
transport.blocked() == Some(Blocked::Read)
self.interest() == Reg::Write
Should call `scope.register(EventSet::read())`, not with write
#[test]
fn test_conn_register_when_transport_blocked() {
}
*/
}