mirror of
https://github.com/fafhrd91/actix-web
synced 2025-01-18 05:41:50 +01:00
add HttpContext::drain()
This commit is contained in:
parent
5cd25cc8b1
commit
af1e0bac08
@ -38,7 +38,6 @@ mime = "0.3"
|
|||||||
mime_guess = "1.8"
|
mime_guess = "1.8"
|
||||||
cookie = { version="0.10", features=["percent-encode"] }
|
cookie = { version="0.10", features=["percent-encode"] }
|
||||||
regex = "0.2"
|
regex = "0.2"
|
||||||
slab = "0.4"
|
|
||||||
sha1 = "0.2"
|
sha1 = "0.2"
|
||||||
url = "1.5"
|
url = "1.5"
|
||||||
percent-encoding = "1.0"
|
percent-encoding = "1.0"
|
||||||
@ -46,9 +45,8 @@ percent-encoding = "1.0"
|
|||||||
# tokio
|
# tokio
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
tokio-core = "0.1"
|
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
tokio-proto = "0.1"
|
tokio-core = "0.1"
|
||||||
# h2 = { git = 'https://github.com/carllerche/h2', optional = true }
|
# h2 = { git = 'https://github.com/carllerche/h2', optional = true }
|
||||||
|
|
||||||
[dependencies.actix]
|
[dependencies.actix]
|
||||||
|
@ -91,7 +91,7 @@ impl Application<()> {
|
|||||||
parts: Some(ApplicationBuilderParts {
|
parts: Some(ApplicationBuilderParts {
|
||||||
state: (),
|
state: (),
|
||||||
prefix: prefix.to_string(),
|
prefix: prefix.to_string(),
|
||||||
default: Resource::default(),
|
default: Resource::default_not_found(),
|
||||||
handlers: HashMap::new(),
|
handlers: HashMap::new(),
|
||||||
resources: HashMap::new(),
|
resources: HashMap::new(),
|
||||||
middlewares: Vec::new(),
|
middlewares: Vec::new(),
|
||||||
@ -110,7 +110,7 @@ impl<S> Application<S> where S: 'static {
|
|||||||
parts: Some(ApplicationBuilderParts {
|
parts: Some(ApplicationBuilderParts {
|
||||||
state: state,
|
state: state,
|
||||||
prefix: prefix.to_string(),
|
prefix: prefix.to_string(),
|
||||||
default: Resource::default(),
|
default: Resource::default_not_found(),
|
||||||
handlers: HashMap::new(),
|
handlers: HashMap::new(),
|
||||||
resources: HashMap::new(),
|
resources: HashMap::new(),
|
||||||
middlewares: Vec::new(),
|
middlewares: Vec::new(),
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use std;
|
use std;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use futures::{Async, Stream, Poll};
|
use std::marker::PhantomData;
|
||||||
|
use futures::{Async, Future, Stream, Poll};
|
||||||
use futures::sync::oneshot::Sender;
|
use futures::sync::oneshot::Sender;
|
||||||
|
|
||||||
use actix::{Actor, ActorState, ActorContext, AsyncContext,
|
use actix::{Actor, ActorState, ActorContext, AsyncContext,
|
||||||
@ -10,7 +12,7 @@ use actix::fut::ActorFuture;
|
|||||||
use actix::dev::{AsyncContextApi, ActorAddressCell, ActorItemsCell, ActorWaitCell, SpawnHandle,
|
use actix::dev::{AsyncContextApi, ActorAddressCell, ActorItemsCell, ActorWaitCell, SpawnHandle,
|
||||||
Envelope, ToEnvelope, RemoteEnvelope};
|
Envelope, ToEnvelope, RemoteEnvelope};
|
||||||
|
|
||||||
use task::IoContext;
|
use task::{IoContext, DrainFut};
|
||||||
use body::BinaryBody;
|
use body::BinaryBody;
|
||||||
use route::{Route, Frame};
|
use route::{Route, Frame};
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
@ -137,6 +139,14 @@ impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
|
|||||||
self.stream.push_back(Frame::Payload(None))
|
self.stream.push_back(Frame::Payload(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns drain future
|
||||||
|
pub fn drain(&mut self) -> Drain<A> {
|
||||||
|
let fut = Rc::new(RefCell::new(DrainFut::new()));
|
||||||
|
self.stream.push_back(Frame::Drain(fut.clone()));
|
||||||
|
self.modified = true;
|
||||||
|
Drain{ a: PhantomData, inner: fut }
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if connection still open
|
/// Check if connection still open
|
||||||
pub fn connected(&self) -> bool {
|
pub fn connected(&self) -> bool {
|
||||||
!self.disconnected
|
!self.disconnected
|
||||||
@ -199,6 +209,10 @@ impl<A> Stream for HttpContext<A> where A: Actor<Context=Self> + Route
|
|||||||
|
|
||||||
// check wait futures
|
// check wait futures
|
||||||
if self.wait.poll(act, ctx) {
|
if self.wait.poll(act, ctx) {
|
||||||
|
// get frame
|
||||||
|
if let Some(frame) = self.stream.pop_front() {
|
||||||
|
return Ok(Async::Ready(Some(frame)))
|
||||||
|
}
|
||||||
return Ok(Async::NotReady)
|
return Ok(Async::NotReady)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,3 +283,21 @@ impl<A> ToEnvelope<A> for HttpContext<A>
|
|||||||
RemoteEnvelope::new(msg, tx).into()
|
RemoteEnvelope::new(msg, tx).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Drain<A> {
|
||||||
|
a: PhantomData<A>,
|
||||||
|
inner: Rc<RefCell<DrainFut>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A> ActorFuture for Drain<A>
|
||||||
|
where A: Actor
|
||||||
|
{
|
||||||
|
type Item = ();
|
||||||
|
type Error = ();
|
||||||
|
type Actor = A;
|
||||||
|
|
||||||
|
fn poll(&mut self, _: &mut A, _: &mut <Self::Actor as Actor>::Context) -> Poll<(), ()> {
|
||||||
|
self.inner.borrow_mut().poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,9 +8,8 @@ extern crate sha1;
|
|||||||
extern crate regex;
|
extern crate regex;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate tokio_core;
|
|
||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate tokio_proto;
|
extern crate tokio_core;
|
||||||
|
|
||||||
extern crate cookie;
|
extern crate cookie;
|
||||||
extern crate http;
|
extern crate http;
|
||||||
|
@ -13,7 +13,7 @@ use payload::Payload;
|
|||||||
use context::HttpContext;
|
use context::HttpContext;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
use httpcodes::HTTPMethodNotAllowed;
|
use httpcodes::{HTTPNotFound, HTTPMethodNotAllowed};
|
||||||
|
|
||||||
/// Http resource
|
/// Http resource
|
||||||
///
|
///
|
||||||
@ -51,6 +51,14 @@ impl<S> Default for Resource<S> {
|
|||||||
|
|
||||||
impl<S> Resource<S> where S: 'static {
|
impl<S> Resource<S> where S: 'static {
|
||||||
|
|
||||||
|
pub(crate) fn default_not_found() -> Self {
|
||||||
|
Resource {
|
||||||
|
name: String::new(),
|
||||||
|
state: PhantomData,
|
||||||
|
routes: HashMap::new(),
|
||||||
|
default: Box::new(HTTPNotFound)}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set resource name
|
/// Set resource name
|
||||||
pub fn set_name<T: ToString>(&mut self, name: T) {
|
pub fn set_name<T: ToString>(&mut self, name: T) {
|
||||||
self.name = name.to_string();
|
self.name = name.to_string();
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use actix::Actor;
|
use actix::Actor;
|
||||||
use http::{header, Version};
|
use http::{header, Version};
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
|
|
||||||
use task::Task;
|
use task::{Task, DrainFut};
|
||||||
use body::BinaryBody;
|
use body::BinaryBody;
|
||||||
use context::HttpContext;
|
use context::HttpContext;
|
||||||
use resource::Reply;
|
use resource::Reply;
|
||||||
@ -21,9 +22,9 @@ use httpcodes::HTTPExpectationFailed;
|
|||||||
pub enum Frame {
|
pub enum Frame {
|
||||||
Message(HttpResponse),
|
Message(HttpResponse),
|
||||||
Payload(Option<BinaryBody>),
|
Payload(Option<BinaryBody>),
|
||||||
|
Drain(Rc<RefCell<DrainFut>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Trait defines object that could be regestered as resource route
|
/// Trait defines object that could be regestered as resource route
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub trait RouteHandler<S>: 'static {
|
pub trait RouteHandler<S>: 'static {
|
||||||
|
144
src/task.rs
144
src/task.rs
@ -1,6 +1,7 @@
|
|||||||
use std::{mem, cmp, io};
|
use std::{mem, cmp, io};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use http::{StatusCode, Version};
|
use http::{StatusCode, Version};
|
||||||
@ -8,6 +9,7 @@ use http::header::{HeaderValue,
|
|||||||
CONNECTION, CONTENT_TYPE, CONTENT_LENGTH, TRANSFER_ENCODING, DATE};
|
CONNECTION, CONTENT_TYPE, CONTENT_LENGTH, TRANSFER_ENCODING, DATE};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use futures::{Async, Future, Poll, Stream};
|
use futures::{Async, Future, Poll, Stream};
|
||||||
|
use futures::task::{Task as FutureTask, current as current_task};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use date;
|
use date;
|
||||||
@ -57,6 +59,45 @@ pub(crate) trait IoContext: Stream<Item=Frame, Error=io::Error> + 'static {
|
|||||||
fn disconnected(&mut self);
|
fn disconnected(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DrainFut {
|
||||||
|
drained: bool,
|
||||||
|
task: Option<FutureTask>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrainFut {
|
||||||
|
|
||||||
|
pub fn new() -> DrainFut {
|
||||||
|
DrainFut {
|
||||||
|
drained: false,
|
||||||
|
task: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self) {
|
||||||
|
self.drained = true;
|
||||||
|
if let Some(task) = self.task.take() {
|
||||||
|
task.notify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for DrainFut {
|
||||||
|
type Item = ();
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<(), ()> {
|
||||||
|
if self.drained {
|
||||||
|
Ok(Async::Ready(()))
|
||||||
|
} else {
|
||||||
|
self.task = Some(current_task());
|
||||||
|
Ok(Async::NotReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
state: TaskRunningState,
|
state: TaskRunningState,
|
||||||
iostate: TaskIOState,
|
iostate: TaskIOState,
|
||||||
@ -64,6 +105,7 @@ pub struct Task {
|
|||||||
stream: TaskStream,
|
stream: TaskStream,
|
||||||
encoder: Encoder,
|
encoder: Encoder,
|
||||||
buffer: BytesMut,
|
buffer: BytesMut,
|
||||||
|
drain: Vec<Rc<RefCell<DrainFut>>>,
|
||||||
upgrade: bool,
|
upgrade: bool,
|
||||||
keepalive: bool,
|
keepalive: bool,
|
||||||
prepared: Option<HttpResponse>,
|
prepared: Option<HttpResponse>,
|
||||||
@ -82,6 +124,7 @@ impl Task {
|
|||||||
state: TaskRunningState::Running,
|
state: TaskRunningState::Running,
|
||||||
iostate: TaskIOState::Done,
|
iostate: TaskIOState::Done,
|
||||||
frames: frames,
|
frames: frames,
|
||||||
|
drain: Vec::new(),
|
||||||
stream: TaskStream::None,
|
stream: TaskStream::None,
|
||||||
encoder: Encoder::length(0),
|
encoder: Encoder::length(0),
|
||||||
buffer: BytesMut::new(),
|
buffer: BytesMut::new(),
|
||||||
@ -103,6 +146,7 @@ impl Task {
|
|||||||
stream: TaskStream::Stream(Box::new(stream)),
|
stream: TaskStream::Stream(Box::new(stream)),
|
||||||
encoder: Encoder::length(0),
|
encoder: Encoder::length(0),
|
||||||
buffer: BytesMut::new(),
|
buffer: BytesMut::new(),
|
||||||
|
drain: Vec::new(),
|
||||||
upgrade: false,
|
upgrade: false,
|
||||||
keepalive: false,
|
keepalive: false,
|
||||||
prepared: None,
|
prepared: None,
|
||||||
@ -120,6 +164,7 @@ impl Task {
|
|||||||
stream: TaskStream::Context(Box::new(ctx)),
|
stream: TaskStream::Context(Box::new(ctx)),
|
||||||
encoder: Encoder::length(0),
|
encoder: Encoder::length(0),
|
||||||
buffer: BytesMut::new(),
|
buffer: BytesMut::new(),
|
||||||
|
drain: Vec::new(),
|
||||||
upgrade: false,
|
upgrade: false,
|
||||||
keepalive: false,
|
keepalive: false,
|
||||||
prepared: None,
|
prepared: None,
|
||||||
@ -275,47 +320,53 @@ impl Task {
|
|||||||
if self.frames.is_empty() && self.iostate.is_done() {
|
if self.frames.is_empty() && self.iostate.is_done() {
|
||||||
return Ok(Async::Ready(self.state.is_done()));
|
return Ok(Async::Ready(self.state.is_done()));
|
||||||
} else {
|
} else {
|
||||||
// poll stream
|
if self.drain.is_empty() {
|
||||||
if self.state == TaskRunningState::Running {
|
// poll stream
|
||||||
match self.poll() {
|
if self.state == TaskRunningState::Running {
|
||||||
Ok(Async::Ready(_)) => {
|
match self.poll() {
|
||||||
self.state = TaskRunningState::Done;
|
Ok(Async::Ready(_)) => {
|
||||||
}
|
self.state = TaskRunningState::Done;
|
||||||
Ok(Async::NotReady) => (),
|
|
||||||
Err(_) => return Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use exiting frames
|
|
||||||
while let Some(frame) = self.frames.pop_front() {
|
|
||||||
trace!("IO Frame: {:?}", frame);
|
|
||||||
match frame {
|
|
||||||
Frame::Message(response) => {
|
|
||||||
if !self.disconnected {
|
|
||||||
self.prepare(req, response);
|
|
||||||
}
|
}
|
||||||
|
Ok(Async::NotReady) => (),
|
||||||
|
Err(_) => return Err(())
|
||||||
}
|
}
|
||||||
Frame::Payload(Some(chunk)) => {
|
}
|
||||||
if !self.disconnected {
|
|
||||||
if self.prepared.is_some() {
|
// use exiting frames
|
||||||
// TODO: add warning, write after EOF
|
while let Some(frame) = self.frames.pop_front() {
|
||||||
self.encoder.encode(&mut self.buffer, chunk.as_ref());
|
trace!("IO Frame: {:?}", frame);
|
||||||
} else {
|
match frame {
|
||||||
// might be response for EXCEPT
|
Frame::Message(response) => {
|
||||||
self.buffer.extend_from_slice(chunk.as_ref())
|
if !self.disconnected {
|
||||||
|
self.prepare(req, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
Frame::Payload(Some(chunk)) => {
|
||||||
Frame::Payload(None) => {
|
if !self.disconnected {
|
||||||
if !self.disconnected &&
|
if self.prepared.is_some() {
|
||||||
!self.encoder.encode(&mut self.buffer, [].as_ref())
|
// TODO: add warning, write after EOF
|
||||||
{
|
self.encoder.encode(&mut self.buffer, chunk.as_ref());
|
||||||
// TODO: add error "not eof""
|
} else {
|
||||||
debug!("last payload item, but it is not EOF ");
|
// might be response for EXCEPT
|
||||||
return Err(())
|
self.buffer.extend_from_slice(chunk.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Frame::Payload(None) => {
|
||||||
|
if !self.disconnected &&
|
||||||
|
!self.encoder.encode(&mut self.buffer, [].as_ref())
|
||||||
|
{
|
||||||
|
// TODO: add error "not eof""
|
||||||
|
debug!("last payload item, but it is not EOF ");
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
break
|
||||||
|
},
|
||||||
|
Frame::Drain(fut) => {
|
||||||
|
self.drain.push(fut);
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,6 +398,23 @@ impl Task {
|
|||||||
self.iostate = TaskIOState::Done;
|
self.iostate = TaskIOState::Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// drain
|
||||||
|
if self.buffer.is_empty() && !self.drain.is_empty() {
|
||||||
|
match io.flush() {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
|
return Ok(Async::NotReady)
|
||||||
|
}
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
}
|
||||||
|
|
||||||
|
for fut in &mut self.drain {
|
||||||
|
fut.borrow_mut().set()
|
||||||
|
}
|
||||||
|
self.drain.clear();
|
||||||
|
// return self.poll_io(io, req);
|
||||||
|
}
|
||||||
|
|
||||||
// response is completed
|
// response is completed
|
||||||
if (self.buffer.is_empty() || self.disconnected) && self.iostate.is_done() {
|
if (self.buffer.is_empty() || self.disconnected) && self.iostate.is_done() {
|
||||||
// run middlewares
|
// run middlewares
|
||||||
@ -357,7 +425,6 @@ impl Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Async::Ready(self.state.is_done()))
|
Ok(Async::Ready(self.state.is_done()))
|
||||||
} else {
|
} else {
|
||||||
Ok(Async::NotReady)
|
Ok(Async::NotReady)
|
||||||
@ -391,6 +458,7 @@ impl Task {
|
|||||||
return Err(())
|
return Err(())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
self.frames.push_back(frame)
|
self.frames.push_back(frame)
|
||||||
},
|
},
|
||||||
@ -399,7 +467,7 @@ impl Task {
|
|||||||
Ok(Async::NotReady) =>
|
Ok(Async::NotReady) =>
|
||||||
return Ok(Async::NotReady),
|
return Ok(Async::NotReady),
|
||||||
Err(_) =>
|
Err(_) =>
|
||||||
return Err(())
|
return Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user