mirror of
https://github.com/actix/actix-web.git
synced 2024-12-17 21:56:38 +00:00
refactor payload
This commit is contained in:
parent
e398694bdb
commit
e2dc775e21
12 changed files with 257 additions and 153 deletions
|
@ -48,8 +48,7 @@ impl Actor for MyRoute {
|
||||||
impl Route for MyRoute {
|
impl Route for MyRoute {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
fn request(req: HttpRequest, payload: Option<Payload>,
|
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self>
|
||||||
ctx: &mut HttpContext<Self>) -> Reply<Self>
|
|
||||||
{
|
{
|
||||||
Reply::with(req, httpcodes::HTTPOk)
|
Reply::with(req, httpcodes::HTTPOk)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,10 @@ use std::collections::HashMap;
|
||||||
use route_recognizer::Router;
|
use route_recognizer::Router;
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use route::{Payload, RouteHandler};
|
use route::RouteHandler;
|
||||||
use router::Handler;
|
use router::Handler;
|
||||||
use resource::Resource;
|
use resource::Resource;
|
||||||
|
use payload::Payload;
|
||||||
use httpmessage::HttpRequest;
|
use httpmessage::HttpRequest;
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,7 +94,7 @@ struct InnerApplication<S> {
|
||||||
|
|
||||||
impl<S: 'static> Handler for InnerApplication<S> {
|
impl<S: 'static> Handler for InnerApplication<S> {
|
||||||
|
|
||||||
fn handle(&self, req: HttpRequest, payload: Option<Payload>) -> Task {
|
fn handle(&self, req: HttpRequest, payload: Payload) -> Task {
|
||||||
if let Ok(h) = self.router.recognize(req.path()) {
|
if let Ok(h) = self.router.recognize(req.path()) {
|
||||||
h.handler.handle(req.with_params(h.params), payload, Rc::clone(&self.state))
|
h.handler.handle(req.with_params(h.params), payload, Rc::clone(&self.state))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,7 +4,8 @@ use std::rc::Rc;
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use route::{Payload, RouteHandler};
|
use route::RouteHandler;
|
||||||
|
use payload::Payload;
|
||||||
use httpmessage::{Body, HttpRequest, HttpResponse, IntoHttpResponse};
|
use httpmessage::{Body, HttpRequest, HttpResponse, IntoHttpResponse};
|
||||||
|
|
||||||
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
||||||
|
@ -25,8 +26,7 @@ impl StaticResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> RouteHandler<S> for StaticResponse {
|
impl<S> RouteHandler<S> for StaticResponse {
|
||||||
fn handle(&self, req: HttpRequest, _: Option<Payload>, _: Rc<S>) -> Task
|
fn handle(&self, req: HttpRequest, _: Payload, _: Rc<S>) -> Task {
|
||||||
{
|
|
||||||
Task::reply(HttpResponse::new(req, self.0, Body::Empty))
|
Task::reply(HttpResponse::new(req, self.0, Body::Empty))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ mod error;
|
||||||
mod date;
|
mod date;
|
||||||
mod decode;
|
mod decode;
|
||||||
mod httpmessage;
|
mod httpmessage;
|
||||||
|
mod payload;
|
||||||
mod resource;
|
mod resource;
|
||||||
mod route;
|
mod route;
|
||||||
mod router;
|
mod router;
|
||||||
|
@ -40,9 +41,10 @@ mod wsproto;
|
||||||
pub mod httpcodes;
|
pub mod httpcodes;
|
||||||
pub use application::Application;
|
pub use application::Application;
|
||||||
pub use httpmessage::{HttpRequest, HttpResponse, IntoHttpResponse};
|
pub use httpmessage::{HttpRequest, HttpResponse, IntoHttpResponse};
|
||||||
|
pub use payload::{Payload, PayloadItem};
|
||||||
pub use router::RoutingMap;
|
pub use router::RoutingMap;
|
||||||
pub use resource::{Reply, Resource};
|
pub use resource::{Reply, Resource};
|
||||||
pub use route::{Route, RouteFactory, RouteHandler, Payload, PayloadItem};
|
pub use route::{Route, RouteFactory, RouteHandler};
|
||||||
pub use server::HttpServer;
|
pub use server::HttpServer;
|
||||||
pub use context::HttpContext;
|
pub use context::HttpContext;
|
||||||
pub use route_recognizer::Params;
|
pub use route_recognizer::Params;
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -19,12 +19,9 @@ impl Actor for MyRoute {
|
||||||
impl Route for MyRoute {
|
impl Route for MyRoute {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
fn request(req: HttpRequest,
|
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self> {
|
||||||
payload: Option<Payload>,
|
if !payload.eof() {
|
||||||
ctx: &mut HttpContext<Self>) -> Reply<Self>
|
ctx.add_stream(payload);
|
||||||
{
|
|
||||||
if let Some(pl) = payload {
|
|
||||||
ctx.add_stream(pl);
|
|
||||||
Reply::stream(MyRoute{req: Some(req)})
|
Reply::stream(MyRoute{req: Some(req)})
|
||||||
} else {
|
} else {
|
||||||
Reply::with(req, httpcodes::HTTPOk)
|
Reply::with(req, httpcodes::HTTPOk)
|
||||||
|
@ -37,7 +34,7 @@ impl ResponseType<PayloadItem> for MyRoute {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandler<PayloadItem, ()> for MyRoute {}
|
impl StreamHandler<PayloadItem> for MyRoute {}
|
||||||
|
|
||||||
impl Handler<PayloadItem> for MyRoute {
|
impl Handler<PayloadItem> for MyRoute {
|
||||||
fn handle(&mut self, msg: PayloadItem, ctx: &mut HttpContext<Self>)
|
fn handle(&mut self, msg: PayloadItem, ctx: &mut HttpContext<Self>)
|
||||||
|
@ -48,7 +45,6 @@ impl Handler<PayloadItem> for MyRoute {
|
||||||
ctx.start(httpcodes::HTTPOk.response(req));
|
ctx.start(httpcodes::HTTPOk.response(req));
|
||||||
ctx.write_eof();
|
ctx.write_eof();
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::empty()
|
Self::empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,22 +58,15 @@ impl Actor for MyWS {
|
||||||
impl Route for MyWS {
|
impl Route for MyWS {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
fn request(req: HttpRequest,
|
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self> {
|
||||||
payload: Option<Payload>,
|
match ws::handshake(req) {
|
||||||
ctx: &mut HttpContext<Self>) -> Reply<Self>
|
Ok(resp) => {
|
||||||
{
|
ctx.start(resp);
|
||||||
if let Some(payload) = payload {
|
ctx.add_stream(ws::WsStream::new(payload));
|
||||||
match ws::handshake(req) {
|
Reply::stream(MyWS{})
|
||||||
Ok(resp) => {
|
},
|
||||||
ctx.start(resp);
|
Err(err) =>
|
||||||
ctx.add_stream(ws::WsStream::new(payload));
|
Reply::reply(err)
|
||||||
Reply::stream(MyWS{})
|
|
||||||
},
|
|
||||||
Err(err) =>
|
|
||||||
Reply::reply(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Reply::with(req, httpcodes::HTTPBadRequest)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
180
src/payload.rs
Normal file
180
src/payload.rs
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
use std::rc::{Rc, Weak};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use futures::{Async, Poll, Stream};
|
||||||
|
use futures::task::{Task, current as current_task};
|
||||||
|
|
||||||
|
pub type PayloadItem = Bytes;
|
||||||
|
|
||||||
|
const MAX_PAYLOAD_SIZE: usize = 65_536; // max buffer size 64k
|
||||||
|
|
||||||
|
|
||||||
|
/// Stream of byte chunks
|
||||||
|
///
|
||||||
|
/// Payload stores chunks in vector. First chunk can be received with `.readany()` method.
|
||||||
|
pub struct Payload {
|
||||||
|
inner: Rc<RefCell<Inner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Payload {
|
||||||
|
|
||||||
|
pub(crate) fn new(eof: bool) -> (PayloadSender, Payload) {
|
||||||
|
let shared = Rc::new(RefCell::new(Inner::new(eof)));
|
||||||
|
|
||||||
|
(PayloadSender{inner: Rc::downgrade(&shared)},
|
||||||
|
Payload{inner: shared})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates paused state of the payload. If payload data is not consumed
|
||||||
|
/// it get paused. Max size of not consumed data is 64k
|
||||||
|
pub fn paused(&self) -> bool {
|
||||||
|
self.inner.borrow().paused()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates EOF of payload
|
||||||
|
pub fn eof(&self) -> bool {
|
||||||
|
self.inner.borrow().eof()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Length of the data in this payload
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.inner.borrow().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is payload empty
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.inner.borrow().len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get any chunk of data
|
||||||
|
pub fn readany(&mut self) -> Async<Option<PayloadItem>> {
|
||||||
|
self.inner.borrow_mut().readany()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Put unused data back to payload
|
||||||
|
pub fn unread_data(&mut self, data: PayloadItem) {
|
||||||
|
self.inner.borrow_mut().unread_data(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Stream for Payload {
|
||||||
|
type Item = PayloadItem;
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Option<PayloadItem>, ()> {
|
||||||
|
Ok(self.readany())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct PayloadSender {
|
||||||
|
inner: Weak<RefCell<Inner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PayloadSender {
|
||||||
|
pub(crate) fn feed_eof(&mut self) {
|
||||||
|
if let Some(shared) = self.inner.upgrade() {
|
||||||
|
shared.borrow_mut().feed_eof()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn feed_data(&mut self, data: Bytes) {
|
||||||
|
if let Some(shared) = self.inner.upgrade() {
|
||||||
|
shared.borrow_mut().feed_data(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn maybe_paused(&self) -> bool {
|
||||||
|
match self.inner.upgrade() {
|
||||||
|
Some(shared) => {
|
||||||
|
let inner = shared.borrow();
|
||||||
|
if inner.paused() && inner.len() < MAX_PAYLOAD_SIZE {
|
||||||
|
drop(inner);
|
||||||
|
shared.borrow_mut().resume();
|
||||||
|
false
|
||||||
|
} else if !inner.paused() && inner.len() > MAX_PAYLOAD_SIZE {
|
||||||
|
drop(inner);
|
||||||
|
shared.borrow_mut().pause();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
inner.paused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
len: usize,
|
||||||
|
eof: bool,
|
||||||
|
paused: bool,
|
||||||
|
task: Option<Task>,
|
||||||
|
items: VecDeque<Bytes>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
|
||||||
|
fn new(eof: bool) -> Self {
|
||||||
|
Inner {
|
||||||
|
len: 0,
|
||||||
|
eof: eof,
|
||||||
|
paused: false,
|
||||||
|
task: None,
|
||||||
|
items: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paused(&self) -> bool {
|
||||||
|
self.paused
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pause(&mut self) {
|
||||||
|
self.paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume(&mut self) {
|
||||||
|
self.paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn feed_eof(&mut self) {
|
||||||
|
self.eof = true;
|
||||||
|
if let Some(task) = self.task.take() {
|
||||||
|
task.notify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn feed_data(&mut self, data: Bytes) {
|
||||||
|
self.len += data.len();
|
||||||
|
self.items.push_back(data);
|
||||||
|
if let Some(task) = self.task.take() {
|
||||||
|
task.notify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eof(&self) -> bool {
|
||||||
|
self.eof
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readany(&mut self) -> Async<Option<Bytes>> {
|
||||||
|
if let Some(data) = self.items.pop_front() {
|
||||||
|
self.len -= data.len();
|
||||||
|
Async::Ready(Some(data))
|
||||||
|
} else if self.eof {
|
||||||
|
Async::Ready(None)
|
||||||
|
} else {
|
||||||
|
self.task = Some(current_task());
|
||||||
|
Async::NotReady
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unread_data(&mut self, data: Bytes) {
|
||||||
|
self.len += data.len();
|
||||||
|
self.items.push_front(data)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,26 +3,23 @@ use std::{self, fmt, io, ptr};
|
||||||
use httparse;
|
use httparse;
|
||||||
use http::{Method, Version, Uri, HttpTryFrom};
|
use http::{Method, Version, Uri, HttpTryFrom};
|
||||||
use bytes::{Bytes, BytesMut, BufMut};
|
use bytes::{Bytes, BytesMut, BufMut};
|
||||||
use futures::{Async, AsyncSink, Poll, Sink};
|
use futures::{Async, Poll};
|
||||||
use futures::unsync::mpsc::{channel, Sender};
|
|
||||||
use tokio_io::AsyncRead;
|
use tokio_io::AsyncRead;
|
||||||
|
|
||||||
use hyper::header::{Headers, ContentLength};
|
use hyper::header::{Headers, ContentLength};
|
||||||
|
|
||||||
use {Payload, PayloadItem};
|
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
use decode::Decoder;
|
use decode::Decoder;
|
||||||
|
use payload::{Payload, PayloadSender};
|
||||||
use httpmessage::{Message, HttpRequest};
|
use httpmessage::{Message, HttpRequest};
|
||||||
|
|
||||||
|
|
||||||
const MAX_HEADERS: usize = 100;
|
const MAX_HEADERS: usize = 100;
|
||||||
const INIT_BUFFER_SIZE: usize = 8192;
|
const INIT_BUFFER_SIZE: usize = 8192;
|
||||||
pub const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
|
const MAX_BUFFER_SIZE: usize = 131_072;
|
||||||
|
|
||||||
struct PayloadInfo {
|
struct PayloadInfo {
|
||||||
tx: Sender<PayloadItem>,
|
tx: PayloadSender,
|
||||||
decoder: Decoder,
|
decoder: Decoder,
|
||||||
tmp_item: Option<PayloadItem>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Reader {
|
pub struct Reader {
|
||||||
|
@ -61,48 +58,17 @@ impl Reader {
|
||||||
fn decode(&mut self) -> std::result::Result<Decoding, Error>
|
fn decode(&mut self) -> std::result::Result<Decoding, Error>
|
||||||
{
|
{
|
||||||
if let Some(ref mut payload) = self.payload {
|
if let Some(ref mut payload) = self.payload {
|
||||||
|
if payload.tx.maybe_paused() {
|
||||||
|
return Ok(Decoding::Paused)
|
||||||
|
}
|
||||||
loop {
|
loop {
|
||||||
if let Some(item) = payload.tmp_item.take() {
|
|
||||||
let eof = item.is_eof();
|
|
||||||
|
|
||||||
match payload.tx.start_send(item) {
|
|
||||||
Ok(AsyncSink::NotReady(item)) => {
|
|
||||||
payload.tmp_item = Some(item);
|
|
||||||
return Ok(Decoding::Paused)
|
|
||||||
}
|
|
||||||
Ok(AsyncSink::Ready) => {
|
|
||||||
if eof {
|
|
||||||
return Ok(Decoding::Ready)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(_) => return Err(Error::Incomplete),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match payload.decoder.decode(&mut self.read_buf) {
|
match payload.decoder.decode(&mut self.read_buf) {
|
||||||
Ok(Async::Ready(Some(bytes))) => {
|
Ok(Async::Ready(Some(bytes))) => {
|
||||||
match payload.tx.start_send(PayloadItem::Chunk(bytes)) {
|
payload.tx.feed_data(bytes)
|
||||||
Ok(AsyncSink::NotReady(item)) => {
|
|
||||||
payload.tmp_item = Some(item);
|
|
||||||
return Ok(Decoding::Paused)
|
|
||||||
}
|
|
||||||
Ok(AsyncSink::Ready) => {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
Err(_) => return Err(Error::Incomplete),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Ok(Async::Ready(None)) => {
|
Ok(Async::Ready(None)) => {
|
||||||
match payload.tx.start_send(PayloadItem::Eof) {
|
payload.tx.feed_eof();
|
||||||
Ok(AsyncSink::NotReady(item)) => {
|
return Ok(Decoding::Ready)
|
||||||
payload.tmp_item = Some(item);
|
|
||||||
return Ok(Decoding::Paused)
|
|
||||||
}
|
|
||||||
Ok(AsyncSink::Ready) => {
|
|
||||||
return Ok(Decoding::Ready)
|
|
||||||
}
|
|
||||||
Err(_) => return Err(Error::Incomplete),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => return Ok(Decoding::NotReady),
|
Ok(Async::NotReady) => return Ok(Decoding::NotReady),
|
||||||
Err(_) => return Err(Error::Incomplete),
|
Err(_) => return Err(Error::Incomplete),
|
||||||
|
@ -113,9 +79,11 @@ impl Reader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse<T>(&mut self, io: &mut T) -> Poll<(HttpRequest, Option<Payload>), Error>
|
pub fn parse<T>(&mut self, io: &mut T) -> Poll<(HttpRequest, Payload), Error>
|
||||||
where T: AsyncRead
|
where T: AsyncRead
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.decode()? {
|
match self.decode()? {
|
||||||
Decoding::Paused => return Ok(Async::NotReady),
|
Decoding::Paused => return Ok(Async::NotReady),
|
||||||
|
@ -137,11 +105,10 @@ impl Reader {
|
||||||
match try!(parse(&mut self.read_buf)) {
|
match try!(parse(&mut self.read_buf)) {
|
||||||
Some((msg, decoder)) => {
|
Some((msg, decoder)) => {
|
||||||
let payload = if let Some(decoder) = decoder {
|
let payload = if let Some(decoder) = decoder {
|
||||||
let (tx, rx) = channel(32);
|
let (tx, rx) = Payload::new(false);
|
||||||
let payload = PayloadInfo {
|
let payload = PayloadInfo {
|
||||||
tx: tx,
|
tx: tx,
|
||||||
decoder: decoder,
|
decoder: decoder,
|
||||||
tmp_item: None,
|
|
||||||
};
|
};
|
||||||
self.payload = Some(payload);
|
self.payload = Some(payload);
|
||||||
|
|
||||||
|
@ -170,9 +137,10 @@ impl Reader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(rx)
|
rx
|
||||||
} else {
|
} else {
|
||||||
None
|
let (_, rx) = Payload::new(true);
|
||||||
|
rx
|
||||||
};
|
};
|
||||||
return Ok(Async::Ready((msg, payload)));
|
return Ok(Async::Ready((msg, payload)));
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,8 @@ use actix::Actor;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use route::{Route, Payload, RouteHandler};
|
use route::{Route, RouteHandler};
|
||||||
|
use payload::Payload;
|
||||||
use context::HttpContext;
|
use context::HttpContext;
|
||||||
use httpcodes::HTTPMethodNotAllowed;
|
use httpcodes::HTTPMethodNotAllowed;
|
||||||
use httpmessage::{HttpRequest, HttpResponse, IntoHttpResponse};
|
use httpmessage::{HttpRequest, HttpResponse, IntoHttpResponse};
|
||||||
|
@ -92,7 +93,7 @@ impl<S> Resource<S> where S: 'static {
|
||||||
|
|
||||||
impl<S: 'static> RouteHandler<S> for Resource<S> {
|
impl<S: 'static> RouteHandler<S> for Resource<S> {
|
||||||
|
|
||||||
fn handle(&self, req: HttpRequest, payload: Option<Payload>, state: Rc<S>) -> Task {
|
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<S>) -> Task {
|
||||||
if let Some(handler) = self.routes.get(req.method()) {
|
if let Some(handler) = self.routes.get(req.method()) {
|
||||||
handler.handle(req, payload, state)
|
handler.handle(req, payload, state)
|
||||||
} else {
|
} else {
|
||||||
|
|
37
src/route.rs
37
src/route.rs
|
@ -3,40 +3,13 @@ use std::marker::PhantomData;
|
||||||
|
|
||||||
use actix::Actor;
|
use actix::Actor;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::unsync::mpsc::Receiver;
|
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use context::HttpContext;
|
use context::HttpContext;
|
||||||
use resource::Reply;
|
use resource::Reply;
|
||||||
|
use payload::Payload;
|
||||||
use httpmessage::{HttpRequest, HttpResponse};
|
use httpmessage::{HttpRequest, HttpResponse};
|
||||||
|
|
||||||
/// Stream of `PayloadItem`'s
|
|
||||||
pub type Payload = Receiver<PayloadItem>;
|
|
||||||
|
|
||||||
/// `PayloadItem` represents one payload item
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum PayloadItem {
|
|
||||||
/// Indicates end of payload stream
|
|
||||||
Eof,
|
|
||||||
/// Chunk of bytes
|
|
||||||
Chunk(Bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PayloadItem {
|
|
||||||
/// Is item an eof
|
|
||||||
pub fn is_eof(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
PayloadItem::Eof => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Is item a chunk
|
|
||||||
pub fn is_chunk(&self) -> bool {
|
|
||||||
!self.is_eof()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))]
|
#[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))]
|
||||||
|
@ -47,7 +20,7 @@ pub enum Frame {
|
||||||
|
|
||||||
/// Trait defines object that could be regestered as resource route
|
/// Trait defines object that could be regestered as resource route
|
||||||
pub trait RouteHandler<S>: 'static {
|
pub trait RouteHandler<S>: 'static {
|
||||||
fn handle(&self, req: HttpRequest, payload: Option<Payload>, state: Rc<S>) -> Task;
|
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<S>) -> Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actors with ability to handle http requests
|
/// Actors with ability to handle http requests
|
||||||
|
@ -60,9 +33,7 @@ pub trait Route: Actor<Context=HttpContext<Self>> {
|
||||||
/// result immediately with `Reply::reply` or `Reply::with`.
|
/// result immediately with `Reply::reply` or `Reply::with`.
|
||||||
/// Actor itself could be returned for handling streaming request/response.
|
/// Actor itself could be returned for handling streaming request/response.
|
||||||
/// In that case `HttpContext::start` and `HttpContext::write` has to be used.
|
/// In that case `HttpContext::start` and `HttpContext::write` has to be used.
|
||||||
fn request(req: HttpRequest,
|
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self>;
|
||||||
payload: Option<Payload>,
|
|
||||||
ctx: &mut HttpContext<Self>) -> Reply<Self>;
|
|
||||||
|
|
||||||
/// This method creates `RouteFactory` for this actor.
|
/// This method creates `RouteFactory` for this actor.
|
||||||
fn factory() -> RouteFactory<Self, Self::State> {
|
fn factory() -> RouteFactory<Self, Self::State> {
|
||||||
|
@ -77,7 +48,7 @@ impl<A, S> RouteHandler<S> for RouteFactory<A, S>
|
||||||
where A: Route<State=S>,
|
where A: Route<State=S>,
|
||||||
S: 'static
|
S: 'static
|
||||||
{
|
{
|
||||||
fn handle(&self, req: HttpRequest, payload: Option<Payload>, state: Rc<A::State>) -> Task
|
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<A::State>) -> Task
|
||||||
{
|
{
|
||||||
let mut ctx = HttpContext::new(state);
|
let mut ctx = HttpContext::new(state);
|
||||||
A::request(req, payload, &mut ctx).into(ctx)
|
A::request(req, payload, &mut ctx).into(ctx)
|
||||||
|
|
|
@ -4,14 +4,15 @@ use std::collections::HashMap;
|
||||||
use route_recognizer::{Router as Recognizer};
|
use route_recognizer::{Router as Recognizer};
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use route::{Payload, RouteHandler};
|
use payload::Payload;
|
||||||
|
use route::RouteHandler;
|
||||||
use resource::Resource;
|
use resource::Resource;
|
||||||
use application::Application;
|
use application::Application;
|
||||||
use httpcodes::HTTPNotFound;
|
use httpcodes::HTTPNotFound;
|
||||||
use httpmessage::{HttpRequest, IntoHttpResponse};
|
use httpmessage::{HttpRequest, IntoHttpResponse};
|
||||||
|
|
||||||
pub(crate) trait Handler: 'static {
|
pub(crate) trait Handler: 'static {
|
||||||
fn handle(&self, req: HttpRequest, payload: Option<Payload>) -> Task;
|
fn handle(&self, req: HttpRequest, payload: Payload) -> Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request routing map
|
/// Request routing map
|
||||||
|
@ -127,7 +128,7 @@ struct Router {
|
||||||
|
|
||||||
impl Router {
|
impl Router {
|
||||||
|
|
||||||
pub fn call(&self, req: HttpRequest, payload: Option<Payload>) -> Task
|
pub fn call(&self, req: HttpRequest, payload: Payload) -> Task
|
||||||
{
|
{
|
||||||
if let Ok(h) = self.resources.recognize(req.path()) {
|
if let Ok(h) = self.resources.recognize(req.path()) {
|
||||||
h.handler.handle(req.with_params(h.params), payload, Rc::new(()))
|
h.handler.handle(req.with_params(h.params), payload, Rc::new(()))
|
||||||
|
|
|
@ -18,8 +18,7 @@ use httpmessage::{Body, HttpResponse};
|
||||||
|
|
||||||
type FrameStream = Stream<Item=Frame, Error=io::Error>;
|
type FrameStream = Stream<Item=Frame, Error=io::Error>;
|
||||||
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
|
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
|
||||||
const DEFAULT_LIMIT: usize = 65_536; // max buffer size 64k
|
const MAX_WRITE_BUFFER_SIZE: usize = 65_536; // max buffer size 64k
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
enum TaskRunningState {
|
enum TaskRunningState {
|
||||||
|
@ -239,7 +238,7 @@ impl Task {
|
||||||
|
|
||||||
// should pause task
|
// should pause task
|
||||||
if self.state != TaskRunningState::Done {
|
if self.state != TaskRunningState::Done {
|
||||||
if self.buffer.len() > DEFAULT_LIMIT {
|
if self.buffer.len() > MAX_WRITE_BUFFER_SIZE {
|
||||||
self.state = TaskRunningState::Paused;
|
self.state = TaskRunningState::Paused;
|
||||||
} else if self.state == TaskRunningState::Paused {
|
} else if self.state == TaskRunningState::Paused {
|
||||||
self.state = TaskRunningState::Running;
|
self.state = TaskRunningState::Running;
|
||||||
|
|
55
src/ws.rs
55
src/ws.rs
|
@ -21,25 +21,20 @@
|
||||||
//! impl Route for WsRoute {
|
//! impl Route for WsRoute {
|
||||||
//! type State = ();
|
//! type State = ();
|
||||||
//!
|
//!
|
||||||
//! fn request(req: HttpRequest, payload: Option<Payload>,
|
//! fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self>
|
||||||
//! ctx: &mut HttpContext<Self>) -> Reply<Self>
|
|
||||||
//! {
|
//! {
|
||||||
//! if let Some(payload) = payload {
|
//! // WebSocket handshake
|
||||||
//! // WebSocket handshake
|
//! match ws::handshake(req) {
|
||||||
//! match ws::handshake(req) {
|
//! Ok(resp) => {
|
||||||
//! Ok(resp) => {
|
//! // Send handshake response to peer
|
||||||
//! // Send handshake response to peer
|
//! ctx.start(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(err)
|
||||||
//! Reply::reply(err)
|
|
||||||
//! }
|
|
||||||
//! } else {
|
|
||||||
//! Reply::with(req, httpcodes::HTTPBadRequest)
|
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
@ -77,7 +72,8 @@ use hyper::header;
|
||||||
use actix::Actor;
|
use actix::Actor;
|
||||||
|
|
||||||
use context::HttpContext;
|
use context::HttpContext;
|
||||||
use route::{Route, Payload, PayloadItem};
|
use route::Route;
|
||||||
|
use payload::Payload;
|
||||||
use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed};
|
use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed};
|
||||||
use httpmessage::{Body, ConnectionType, HttpRequest, HttpResponse, IntoHttpResponse};
|
use httpmessage::{Body, ConnectionType, HttpRequest, HttpResponse, IntoHttpResponse};
|
||||||
|
|
||||||
|
@ -204,21 +200,18 @@ impl Stream for WsStream {
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.rx.poll() {
|
match self.rx.readany() {
|
||||||
Ok(Async::Ready(Some(item))) => {
|
Async::Ready(Some(chunk)) => {
|
||||||
match item {
|
self.buf.extend(chunk)
|
||||||
PayloadItem::Eof =>
|
|
||||||
return Ok(Async::Ready(None)),
|
|
||||||
PayloadItem::Chunk(chunk) => {
|
|
||||||
self.buf.extend(chunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(Async::Ready(None)) => done = true,
|
Async::Ready(None) => {
|
||||||
Ok(Async::NotReady) => {},
|
done = true;
|
||||||
Err(err) => return Err(err),
|
}
|
||||||
|
Async::NotReady => break,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
match wsframe::Frame::parse(&mut self.buf) {
|
match wsframe::Frame::parse(&mut self.buf) {
|
||||||
Ok(Some(frame)) => {
|
Ok(Some(frame)) => {
|
||||||
trace!("Frame {}", frame);
|
trace!("Frame {}", frame);
|
||||||
|
|
Loading…
Reference in a new issue