1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-24 00:21:08 +01:00

keep-alive support

This commit is contained in:
Nikolay Kim 2017-10-13 16:33:23 -07:00
parent 0447c66de1
commit 3516f02e4f
11 changed files with 264 additions and 88 deletions

View File

@ -14,6 +14,7 @@ Actix http is licensed under the [Apache-2.0 license](http://opensource.org/lice
* HTTP 1.1 and 1.0 support * HTTP 1.1 and 1.0 support
* Streaming and pipelining support * Streaming and pipelining support
* Keep-alive and slow requests support
* [WebSockets support](https://fafhrd91.github.io/actix-http/actix_http/ws/index.html) * [WebSockets support](https://fafhrd91.github.io/actix-http/actix_http/ws/index.html)
* [Configurable request routing](https://fafhrd91.github.io/actix-http/actix_http/struct.RoutingMap.html) * [Configurable request routing](https://fafhrd91.github.io/actix-http/actix_http/struct.RoutingMap.html)
@ -50,7 +51,7 @@ impl Route for MyRoute {
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self> fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self>
{ {
Reply::reply(req, httpcodes::HTTPOk) Reply::reply(httpcodes::HTTPOk)
} }
} }

View File

@ -33,6 +33,7 @@ impl<A> ActorContext<A> for HttpContext<A> where A: Actor<Context=Self> + Route
if self.state == ActorState::Running { if self.state == ActorState::Running {
self.state = ActorState::Stopping; self.state = ActorState::Stopping;
} }
self.write_eof();
} }
/// Terminate actor execution /// Terminate actor execution
@ -151,9 +152,8 @@ impl<A> Stream for HttpContext<A> where A: Actor<Context=Self> + Route
if self.wait.is_some() && self.act.is_some() { if self.wait.is_some() && self.act.is_some() {
if let Some(ref mut act) = self.act { if let Some(ref mut act) = self.act {
if let Some(ref mut fut) = self.wait { if let Some(ref mut fut) = self.wait {
match fut.poll(act, ctx) { if let Ok(Async::NotReady) = fut.poll(act, ctx) {
Ok(Async::NotReady) => return Ok(Async::NotReady), return Ok(Async::NotReady);
_ => (),
} }
} }
} }

View File

@ -11,7 +11,7 @@ use http::{StatusCode, Error as HttpError};
use httpmessage::{Body, HttpResponse}; use httpmessage::{Body, HttpResponse};
/// A set of errors that can occur parsing HTTP streams. /// A set of errors that can occur during parsing HTTP streams.
#[derive(Debug)] #[derive(Debug)]
pub enum ParseError { pub enum ParseError {
/// An invalid `Method`, such as `GE,T`. /// An invalid `Method`, such as `GE,T`.
@ -31,8 +31,6 @@ pub enum ParseError {
/// A timeout occurred waiting for an IO event. /// A timeout occurred waiting for an IO event.
#[allow(dead_code)] #[allow(dead_code)]
Timeout, Timeout,
/// Unexpected EOF during parsing
Eof,
/// An `io::Error` that occurred while trying to read or write to a network stream. /// An `io::Error` that occurred while trying to read or write to a network stream.
Io(IoError), Io(IoError),
/// Parsing a field as string failed /// Parsing a field as string failed
@ -60,7 +58,6 @@ impl StdError for ParseError {
ParseError::Incomplete => "Message is incomplete", ParseError::Incomplete => "Message is incomplete",
ParseError::Timeout => "Timeout", ParseError::Timeout => "Timeout",
ParseError::Uri => "Uri error", ParseError::Uri => "Uri error",
ParseError::Eof => "Unexpected eof during parse",
ParseError::Io(ref e) => e.description(), ParseError::Io(ref e) => e.description(),
ParseError::Utf8(ref e) => e.description(), ParseError::Utf8(ref e) => e.description(),
} }
@ -107,7 +104,7 @@ impl From<httparse::Error> for ParseError {
} }
} }
/// Return BadRequest for ParseError /// Return `BadRequest` for `ParseError`
impl From<ParseError> for HttpResponse { impl From<ParseError> for HttpResponse {
fn from(err: ParseError) -> Self { fn from(err: ParseError) -> Self {
HttpResponse::new(StatusCode::BAD_REQUEST, HttpResponse::new(StatusCode::BAD_REQUEST,
@ -115,8 +112,8 @@ impl From<ParseError> for HttpResponse {
} }
} }
/// Return InternalServerError for HttpError, /// Return `InternalServerError` for `HttpError`,
/// Response generation can return HttpError, so it is internal error /// Response generation can return `HttpError`, so it is internal error
impl From<HttpError> for HttpResponse { impl From<HttpError> for HttpResponse {
fn from(err: HttpError) -> Self { fn from(err: HttpError) -> Self {
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR, HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR,
@ -124,7 +121,7 @@ impl From<HttpError> for HttpResponse {
} }
} }
/// Return BadRequest for cookie::ParseError /// Return `BadRequest` for `cookie::ParseError`
impl From<cookie::ParseError> for HttpResponse { impl From<cookie::ParseError> for HttpResponse {
fn from(err: cookie::ParseError) -> Self { fn from(err: cookie::ParseError) -> Self {
HttpResponse::new(StatusCode::BAD_REQUEST, HttpResponse::new(StatusCode::BAD_REQUEST,
@ -137,20 +134,19 @@ mod tests {
use std::error::Error as StdError; use std::error::Error as StdError;
use std::io; use std::io;
use httparse; use httparse;
use super::Error; use super::ParseError;
use super::Error::*;
#[test] #[test]
fn test_cause() { fn test_cause() {
let orig = io::Error::new(io::ErrorKind::Other, "other"); let orig = io::Error::new(io::ErrorKind::Other, "other");
let desc = orig.description().to_owned(); let desc = orig.description().to_owned();
let e = Io(orig); let e = ParseError::Io(orig);
assert_eq!(e.cause().unwrap().description(), desc); assert_eq!(e.cause().unwrap().description(), desc);
} }
macro_rules! from { macro_rules! from {
($from:expr => $error:pat) => { ($from:expr => $error:pat) => {
match Error::from($from) { match ParseError::from($from) {
e @ $error => { e @ $error => {
assert!(e.description().len() >= 5); assert!(e.description().len() >= 5);
} , } ,
@ -161,7 +157,7 @@ mod tests {
macro_rules! from_and_cause { macro_rules! from_and_cause {
($from:expr => $error:pat) => { ($from:expr => $error:pat) => {
match Error::from($from) { match ParseError::from($from) {
e @ $error => { e @ $error => {
let desc = e.cause().unwrap().description(); let desc = e.cause().unwrap().description();
assert_eq!(desc, $from.description().to_owned()); assert_eq!(desc, $from.description().to_owned());
@ -174,16 +170,15 @@ mod tests {
#[test] #[test]
fn test_from() { fn test_from() {
from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => ParseError::Io(..));
from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..)); from!(httparse::Error::HeaderName => ParseError::Header);
from!(httparse::Error::HeaderName => ParseError::Header);
from!(httparse::Error::HeaderName => Header); from!(httparse::Error::HeaderValue => ParseError::Header);
from!(httparse::Error::HeaderName => Header); from!(httparse::Error::NewLine => ParseError::Header);
from!(httparse::Error::HeaderValue => Header); from!(httparse::Error::Status => ParseError::Status);
from!(httparse::Error::NewLine => Header); from!(httparse::Error::Token => ParseError::Header);
from!(httparse::Error::Status => Status); from!(httparse::Error::TooManyHeaders => ParseError::TooLarge);
from!(httparse::Error::Token => Header); from!(httparse::Error::Version => ParseError::Version);
from!(httparse::Error::TooManyHeaders => TooLarge);
from!(httparse::Error::Version => Version);
} }
} }

View File

@ -82,8 +82,8 @@ impl HttpRequest {
pub fn cookie(&self) -> Result<Option<cookie::Cookie>, cookie::ParseError> { pub fn cookie(&self) -> Result<Option<cookie::Cookie>, cookie::ParseError> {
if let Some(val) = self.headers.get(header::COOKIE) { if let Some(val) = self.headers.get(header::COOKIE) {
let s = str::from_utf8(val.as_bytes()) let s = str::from_utf8(val.as_bytes())
.map_err(|e| cookie::ParseError::from(e))?; .map_err(cookie::ParseError::from)?;
cookie::Cookie::parse(s).map(|c| Some(c)) cookie::Cookie::parse(s).map(Some)
} else { } else {
Ok(None) Ok(None)
} }

View File

@ -43,7 +43,7 @@ pub mod dev;
pub mod httpcodes; pub mod httpcodes;
pub use application::Application; pub use application::Application;
pub use httpmessage::{Body, Builder, HttpRequest, HttpResponse}; pub use httpmessage::{Body, Builder, HttpRequest, HttpResponse};
pub use payload::{Payload, PayloadItem}; pub use payload::{Payload, PayloadItem, PayloadError};
pub use router::RoutingMap; pub use router::RoutingMap;
pub use resource::{Reply, Resource}; pub use resource::{Reply, Resource};
pub use route::{Route, RouteFactory, RouteHandler}; pub use route::{Route, RouteFactory, RouteHandler};

View File

@ -84,6 +84,9 @@ impl Handler<ws::Message> for MyWS {
ws::Message::Ping(msg) => ws::WsWriter::pong(ctx, msg), ws::Message::Ping(msg) => ws::WsWriter::pong(ctx, msg),
ws::Message::Text(text) => ws::WsWriter::text(ctx, text), ws::Message::Text(text) => ws::WsWriter::text(ctx, text),
ws::Message::Binary(bin) => ws::WsWriter::binary(ctx, bin), ws::Message::Binary(bin) => ws::WsWriter::binary(ctx, bin),
ws::Message::Closed | ws::Message::Error => {
ctx.stop();
}
_ => (), _ => (),
} }
Self::empty() Self::empty()

View File

@ -1,15 +1,31 @@
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::From;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::io::Error as IoError;
use bytes::Bytes; use bytes::Bytes;
use futures::{Async, Poll, Stream}; use futures::{Async, Poll, Stream};
use futures::task::{Task, current as current_task}; use futures::task::{Task, current as current_task};
/// Just Bytes object
pub type PayloadItem = Bytes;
const MAX_PAYLOAD_SIZE: usize = 65_536; // max buffer size 64k const MAX_PAYLOAD_SIZE: usize = 65_536; // max buffer size 64k
/// Just Bytes object
pub type PayloadItem = Result<Bytes, PayloadError>;
#[derive(Debug)]
/// A set of error that can occur during payload parsing.
pub enum PayloadError {
/// A payload reached EOF, but is not complete.
Incomplete,
/// Parse error
ParseError(IoError),
}
impl From<IoError> for PayloadError {
fn from(err: IoError) -> PayloadError {
PayloadError::ParseError(err)
}
}
/// Stream of byte chunks /// Stream of byte chunks
/// ///
@ -55,7 +71,7 @@ impl Payload {
} }
/// Put unused data back to payload /// Put unused data back to payload
pub fn unread_data(&mut self, data: PayloadItem) { pub fn unread_data(&mut self, data: Bytes) {
self.inner.borrow_mut().unread_data(data); self.inner.borrow_mut().unread_data(data);
} }
} }
@ -75,6 +91,12 @@ pub(crate) struct PayloadSender {
} }
impl PayloadSender { impl PayloadSender {
pub(crate) fn set_error(&mut self, err: PayloadError) {
if let Some(shared) = self.inner.upgrade() {
shared.borrow_mut().set_error(err)
}
}
pub(crate) fn feed_eof(&mut self) { pub(crate) fn feed_eof(&mut self) {
if let Some(shared) = self.inner.upgrade() { if let Some(shared) = self.inner.upgrade() {
shared.borrow_mut().feed_eof() shared.borrow_mut().feed_eof()
@ -112,6 +134,7 @@ struct Inner {
len: usize, len: usize,
eof: bool, eof: bool,
paused: bool, paused: bool,
err: Option<PayloadError>,
task: Option<Task>, task: Option<Task>,
items: VecDeque<Bytes>, items: VecDeque<Bytes>,
} }
@ -123,6 +146,7 @@ impl Inner {
len: 0, len: 0,
eof: eof, eof: eof,
paused: false, paused: false,
err: None,
task: None, task: None,
items: VecDeque::new(), items: VecDeque::new(),
} }
@ -140,6 +164,13 @@ impl Inner {
self.paused = false; self.paused = false;
} }
fn set_error(&mut self, err: PayloadError) {
self.err = Some(err);
if let Some(task) = self.task.take() {
task.notify()
}
}
fn feed_eof(&mut self) { fn feed_eof(&mut self) {
self.eof = true; self.eof = true;
if let Some(task) = self.task.take() { if let Some(task) = self.task.take() {
@ -163,12 +194,14 @@ impl Inner {
self.len self.len
} }
fn readany(&mut self) -> Async<Option<Bytes>> { fn readany(&mut self) -> Async<Option<PayloadItem>> {
if let Some(data) = self.items.pop_front() { if let Some(data) = self.items.pop_front() {
self.len -= data.len(); self.len -= data.len();
Async::Ready(Some(data)) Async::Ready(Some(Ok(data)))
} else if self.eof { } else if self.eof {
Async::Ready(None) Async::Ready(None)
} else if let Some(err) = self.err.take() {
Async::Ready(Some(Err(err)))
} else { } else {
self.task = Some(current_task()); self.task = Some(current_task());
Async::NotReady Async::NotReady

View File

@ -10,7 +10,7 @@ use tokio_io::AsyncRead;
use error::ParseError; use error::ParseError;
use decode::Decoder; use decode::Decoder;
use httpmessage::HttpRequest; use httpmessage::HttpRequest;
use payload::{Payload, PayloadSender}; use payload::{Payload, PayloadError, PayloadSender};
const MAX_HEADERS: usize = 100; const MAX_HEADERS: usize = 100;
const INIT_BUFFER_SIZE: usize = 8192; const INIT_BUFFER_SIZE: usize = 8192;
@ -21,7 +21,7 @@ struct PayloadInfo {
decoder: Decoder, decoder: Decoder,
} }
pub struct Reader { pub(crate) struct Reader {
read_buf: BytesMut, read_buf: BytesMut,
payload: Option<PayloadInfo>, payload: Option<PayloadInfo>,
} }
@ -32,6 +32,11 @@ enum Decoding {
NotReady, NotReady,
} }
pub(crate) enum ReaderError {
Payload,
Error(ParseError),
}
impl Reader { impl Reader {
pub fn new() -> Reader { pub fn new() -> Reader {
Reader { Reader {
@ -53,7 +58,7 @@ impl Reader {
} }
} }
fn decode(&mut self) -> std::result::Result<Decoding, ParseError> fn decode(&mut self) -> std::result::Result<Decoding, ReaderError>
{ {
if let Some(ref mut payload) = self.payload { if let Some(ref mut payload) = self.payload {
if payload.tx.maybe_paused() { if payload.tx.maybe_paused() {
@ -69,7 +74,10 @@ impl Reader {
return Ok(Decoding::Ready) return Ok(Decoding::Ready)
}, },
Ok(Async::NotReady) => return Ok(Decoding::NotReady), Ok(Async::NotReady) => return Ok(Decoding::NotReady),
Err(_) => return Err(ParseError::Incomplete), Err(err) => {
payload.tx.set_error(err.into());
return Err(ReaderError::Payload)
}
} }
} }
} else { } else {
@ -77,7 +85,7 @@ impl Reader {
} }
} }
pub fn parse<T>(&mut self, io: &mut T) -> Poll<(HttpRequest, Payload), ParseError> pub fn parse<T>(&mut self, io: &mut T) -> Poll<(HttpRequest, Payload), ReaderError>
where T: AsyncRead where T: AsyncRead
{ {
loop { loop {
@ -88,15 +96,32 @@ impl Reader {
break break
}, },
Decoding::NotReady => { Decoding::NotReady => {
if 0 == try_ready!(self.read_from_io(io)) { match self.read_from_io(io) {
return Err(ParseError::Eof) Ok(Async::Ready(0)) => {
if let Some(ref mut payload) = self.payload {
payload.tx.set_error(PayloadError::Incomplete);
}
// http channel should deal with payload errors
return Err(ReaderError::Payload)
}
Ok(Async::Ready(_)) => {
continue
}
Ok(Async::NotReady) => break,
Err(err) => {
if let Some(ref mut payload) = self.payload {
payload.tx.set_error(err.into());
}
// http channel should deal with payload errors
return Err(ReaderError::Payload)
}
} }
} }
} }
} }
loop { loop {
match try!(parse(&mut self.read_buf)) { match try!(parse(&mut self.read_buf).map_err(ReaderError::Error)) {
Some((msg, decoder)) => { Some((msg, decoder)) => {
let payload = if let Some(decoder) = decoder { let payload = if let Some(decoder) = decoder {
let (tx, rx) = Payload::new(false); let (tx, rx) = Payload::new(false);
@ -118,13 +143,23 @@ impl Reader {
match self.read_from_io(io) { match self.read_from_io(io) {
Ok(Async::Ready(0)) => { Ok(Async::Ready(0)) => {
trace!("parse eof"); trace!("parse eof");
return Err(ParseError::Eof); if let Some(ref mut payload) = self.payload {
payload.tx.set_error(PayloadError::Incomplete);
}
// http channel should deal with payload errors
return Err(ReaderError::Payload)
} }
Ok(Async::Ready(_)) => { Ok(Async::Ready(_)) => {
continue continue
} }
Ok(Async::NotReady) => break, Ok(Async::NotReady) => break,
Err(err) => return Err(err.into()), Err(err) => {
if let Some(ref mut payload) = self.payload {
payload.tx.set_error(err.into());
}
// http channel should deal with payload errors
return Err(ReaderError::Payload)
}
} }
} }
} }
@ -139,13 +174,20 @@ impl Reader {
None => { None => {
if self.read_buf.capacity() >= MAX_BUFFER_SIZE { if self.read_buf.capacity() >= MAX_BUFFER_SIZE {
debug!("MAX_BUFFER_SIZE reached, closing"); debug!("MAX_BUFFER_SIZE reached, closing");
return Err(ParseError::TooLarge); return Err(ReaderError::Error(ParseError::TooLarge));
} }
}, },
} }
if 0 == try_ready!(self.read_from_io(io)) { match self.read_from_io(io) {
Ok(Async::Ready(0)) => {
trace!("parse eof"); trace!("parse eof");
return Err(ParseError::Eof); return Err(ReaderError::Error(ParseError::Incomplete));
},
Ok(Async::Ready(_)) => (),
Ok(Async::NotReady) =>
return Ok(Async::NotReady),
Err(err) =>
return Err(ReaderError::Error(err.into()))
} }
} }
} }

View File

@ -1,13 +1,15 @@
use std::{io, net, mem}; use std::{io, net, mem};
use std::rc::Rc; use std::rc::Rc;
use std::time::Duration;
use std::collections::VecDeque; use std::collections::VecDeque;
use actix::dev::*; use actix::dev::*;
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use tokio_core::reactor::Timeout;
use tokio_core::net::{TcpListener, TcpStream}; use tokio_core::net::{TcpListener, TcpStream};
use task::{Task, RequestInfo}; use task::{Task, RequestInfo};
use reader::Reader; use reader::{Reader, ReaderError};
use router::{Router, RoutingMap}; use router::{Router, RoutingMap};
/// An HTTP Server /// An HTTP Server
@ -58,6 +60,8 @@ impl Handler<(TcpStream, net::SocketAddr), io::Error> for HttpServer {
error: false, error: false,
items: VecDeque::new(), items: VecDeque::new(),
inactive: Vec::new(), inactive: Vec::new(),
keepalive: true,
keepalive_timer: None,
}); });
Self::empty() Self::empty()
} }
@ -72,6 +76,9 @@ struct Entry {
finished: bool, finished: bool,
} }
const KEEPALIVE_PERIOD: u64 = 15; // seconds
const MAX_PIPELINED_MESSAGES: usize = 16;
pub struct HttpChannel { pub struct HttpChannel {
router: Rc<Router>, router: Rc<Router>,
#[allow(dead_code)] #[allow(dead_code)]
@ -81,6 +88,14 @@ pub struct HttpChannel {
error: bool, error: bool,
items: VecDeque<Entry>, items: VecDeque<Entry>,
inactive: Vec<Entry>, inactive: Vec<Entry>,
keepalive: bool,
keepalive_timer: Option<Timeout>,
}
impl Drop for HttpChannel {
fn drop(&mut self) {
println!("Drop http channel");
}
} }
impl Actor for HttpChannel { impl Actor for HttpChannel {
@ -92,6 +107,16 @@ impl Future for HttpChannel {
type Error = (); type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// keep-alive timer
if let Some(ref mut timeout) = self.keepalive_timer {
match timeout.poll() {
Ok(Async::Ready(_)) =>
return Ok(Async::Ready(())),
Ok(Async::NotReady) => (),
Err(_) => unreachable!(),
}
}
loop { loop {
// check in-flight messages // check in-flight messages
let mut idx = 0; let mut idx = 0;
@ -109,10 +134,20 @@ impl Future for HttpChannel {
{ {
Ok(Async::Ready(val)) => { Ok(Async::Ready(val)) => {
let mut item = self.items.pop_front().unwrap(); let mut item = self.items.pop_front().unwrap();
// overide keep-alive state
if self.keepalive {
self.keepalive = item.task.keepalive();
}
if !val { if !val {
item.eof = true; item.eof = true;
self.inactive.push(item); self.inactive.push(item);
} }
// no keep-alive
if !self.keepalive && self.items.is_empty() {
return Ok(Async::Ready(()))
}
continue continue
}, },
Ok(Async::NotReady) => (), Ok(Async::NotReady) => (),
@ -134,15 +169,14 @@ impl Future for HttpChannel {
idx += 1; idx += 1;
} }
// check for parse error
if self.items.is_empty() && self.error {
}
// read incoming data // read incoming data
if !self.error { if !self.error && self.items.len() < MAX_PIPELINED_MESSAGES {
match self.reader.parse(&mut self.stream) { match self.reader.parse(&mut self.stream) {
Ok(Async::Ready((req, payload))) => { Ok(Async::Ready((req, payload))) => {
// stop keepalive timer
self.keepalive_timer.take();
// start request processing
let info = RequestInfo::new(&req); let info = RequestInfo::new(&req);
self.items.push_back( self.items.push_back(
Entry {task: self.router.call(req, payload), Entry {task: self.router.call(req, payload),
@ -151,16 +185,51 @@ impl Future for HttpChannel {
error: false, error: false,
finished: false}); finished: false});
} }
Ok(Async::NotReady) => Err(err) => {
return Ok(Async::NotReady), // kill keepalive
Err(err) => return Err(()) self.keepalive = false;
//self.items.push_back( self.keepalive_timer.take();
// Entry {task: Task::reply(err),
// eof: false, // on parse error, stop reading stream but
// error: false, // complete tasks
// finished: false}) self.error = true;
if let ReaderError::Error(err) = err {
self.items.push_back(
Entry {task: Task::reply(err),
req: RequestInfo::for_error(),
eof: false,
error: false,
finished: false});
} }
} }
Ok(Async::NotReady) => {
// start keep-alive timer, this is also slow request timeout
if self.items.is_empty() {
if self.keepalive {
if self.keepalive_timer.is_none() {
trace!("Start keep-alive timer");
let mut timeout = Timeout::new(
Duration::new(KEEPALIVE_PERIOD, 0),
Arbiter::handle()).unwrap();
// register timeout
let _ = timeout.poll();
self.keepalive_timer = Some(timeout);
}
} else {
// keep-alive disable, drop connection
return Ok(Async::Ready(()))
}
}
return Ok(Async::NotReady)
}
}
}
// check for parse error
if self.items.is_empty() && self.error {
return Ok(Async::Ready(()))
}
} }
} }
} }

View File

@ -56,6 +56,12 @@ impl RequestInfo {
keep_alive: req.keep_alive(), keep_alive: req.keep_alive(),
} }
} }
pub fn for_error() -> Self {
RequestInfo {
version: Version::HTTP_11,
keep_alive: false,
}
}
} }
pub struct Task { pub struct Task {
@ -65,7 +71,8 @@ pub struct Task {
stream: Option<Box<FrameStream>>, stream: Option<Box<FrameStream>>,
encoder: Encoder, encoder: Encoder,
buffer: BytesMut, buffer: BytesMut,
upgraded: bool, upgrade: bool,
keepalive: bool,
} }
impl Task { impl Task {
@ -82,7 +89,8 @@ impl Task {
stream: None, stream: None,
encoder: Encoder::length(0), encoder: Encoder::length(0),
buffer: BytesMut::new(), buffer: BytesMut::new(),
upgraded: false, upgrade: false,
keepalive: false,
} }
} }
@ -96,10 +104,15 @@ impl Task {
stream: Some(Box::new(stream)), stream: Some(Box::new(stream)),
encoder: Encoder::length(0), encoder: Encoder::length(0),
buffer: BytesMut::new(), buffer: BytesMut::new(),
upgraded: false, upgrade: false,
keepalive: false,
} }
} }
pub(crate) fn keepalive(&self) -> bool {
self.keepalive && !self.upgrade
}
fn prepare(&mut self, req: &RequestInfo, mut msg: HttpResponse) fn prepare(&mut self, req: &RequestInfo, mut msg: HttpResponse)
{ {
trace!("Prepare message status={:?}", msg.status); trace!("Prepare message status={:?}", msg.status);
@ -107,6 +120,7 @@ impl Task {
let mut extra = 0; let mut extra = 0;
let body = msg.replace_body(Body::Empty); let body = msg.replace_body(Body::Empty);
let version = msg.version().unwrap_or_else(|| req.version); let version = msg.version().unwrap_or_else(|| req.version);
self.keepalive = msg.keep_alive().unwrap_or_else(|| req.keep_alive);
match body { match body {
Body::Empty => { Body::Empty => {
@ -158,7 +172,7 @@ impl Task {
msg.headers.insert(CONNECTION, HeaderValue::from_static("upgrade")); msg.headers.insert(CONNECTION, HeaderValue::from_static("upgrade"));
} }
// keep-alive // keep-alive
else if msg.keep_alive().unwrap_or_else(|| req.keep_alive) { else if self.keepalive {
if version < Version::HTTP_11 { if version < Version::HTTP_11 {
msg.headers.insert(CONNECTION, HeaderValue::from_static("keep-alive")); msg.headers.insert(CONNECTION, HeaderValue::from_static("keep-alive"));
} }
@ -296,8 +310,8 @@ impl Future for Task {
error!("Non expected frame {:?}", frame); error!("Non expected frame {:?}", frame);
return Err(()) return Err(())
} }
self.upgraded = msg.upgrade(); self.upgrade = msg.upgrade();
if self.upgraded || msg.body().has_body() { if self.upgrade || msg.body().has_body() {
self.iostate = TaskIOState::ReadingPayload; self.iostate = TaskIOState::ReadingPayload;
} else { } else {
self.iostate = TaskIOState::Done; self.iostate = TaskIOState::Done;

View File

@ -27,14 +27,14 @@
//! match ws::handshake(&req) { //! match ws::handshake(&req) {
//! Ok(resp) => { //! Ok(resp) => {
//! // Send handshake response to peer //! // Send handshake response to peer
//! ctx.start(req, resp); //! ctx.start(resp);
//! // Map Payload into WsStream //! // Map Payload into WsStream
//! ctx.add_stream(ws::WsStream::new(payload)); //! ctx.add_stream(ws::WsStream::new(payload));
//! // Start ws messages processing //! // Start ws messages processing
//! Reply::stream(WsRoute) //! Reply::stream(WsRoute)
//! }, //! },
//! Err(err) => //! Err(err) =>
//! Reply::reply(req, err) //! Reply::reply(err)
//! } //! }
//! } //! }
//! } //! }
@ -172,11 +172,13 @@ pub fn handshake(req: &HttpRequest) -> Result<HttpResponse, HttpResponse> {
pub struct WsStream { pub struct WsStream {
rx: Payload, rx: Payload,
buf: BytesMut, buf: BytesMut,
closed: bool,
error_sent: bool,
} }
impl WsStream { impl WsStream {
pub fn new(rx: Payload) -> WsStream { pub fn new(rx: Payload) -> WsStream {
WsStream { rx: rx, buf: BytesMut::new() } WsStream { rx: rx, buf: BytesMut::new(), closed: false, error_sent: false }
} }
} }
@ -187,17 +189,22 @@ impl Stream for WsStream {
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let mut done = false; let mut done = false;
if !self.closed {
loop { loop {
match self.rx.readany() { match self.rx.readany() {
Async::Ready(Some(chunk)) => { Async::Ready(Some(Ok(chunk))) => {
self.buf.extend(chunk) self.buf.extend(chunk)
} }
Async::Ready(Some(Err(_))) => {
self.closed = true;
}
Async::Ready(None) => { Async::Ready(None) => {
done = true; done = true;
} }
Async::NotReady => break, Async::NotReady => break,
} }
} }
}
loop { loop {
match wsframe::Frame::parse(&mut self.buf) { match wsframe::Frame::parse(&mut self.buf) {
@ -229,13 +236,25 @@ impl Stream for WsStream {
} }
} }
} }
Ok(None) => if done { Ok(None) => {
if done {
return Ok(Async::Ready(None)) return Ok(Async::Ready(None))
} else if self.closed {
if !self.error_sent {
self.error_sent = true;
return Ok(Async::Ready(Some(Message::Closed)))
} else {
return Ok(Async::Ready(None))
}
} else { } else {
return Ok(Async::NotReady) return Ok(Async::NotReady)
}
}, },
Err(_) => Err(_) => {
return Err(()), self.closed = true;
self.error_sent = true;
return Ok(Async::Ready(Some(Message::Error)));
}
} }
} }
} }