1
0
mirror of https://github.com/actix/actix-extras.git synced 2025-06-25 18:09:22 +02:00

refactor request pipeline

This commit is contained in:
Nikolay Kim
2017-11-24 22:15:52 -08:00
parent 59b8214685
commit 7569036dd4
12 changed files with 548 additions and 290 deletions

View File

@ -101,9 +101,9 @@ impl Logger {
impl Middleware for Logger {
fn start(&self, req: &mut HttpRequest) -> Started {
fn start(&self, mut req: HttpRequest) -> Started {
req.extensions().insert(StartTime(time::now()));
Started::Done
Started::Done(req)
}
fn finish(&self, req: &mut HttpRequest, resp: &HttpResponse) -> Finished {
@ -298,16 +298,16 @@ mod tests {
let mut headers = HeaderMap::new();
headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
let mut req = HttpRequest::new(
let req = HttpRequest::new(
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
let resp = HttpResponse::builder(StatusCode::OK)
.header("X-Test", "ttt")
.force_close().body(Body::Empty).unwrap();
match logger.start(&mut req) {
Started::Done => (),
let mut req = match logger.start(req) {
Started::Done(req) => req,
_ => panic!(),
}
};
match logger.finish(&mut req, &resp) {
Finished::Done => (),
_ => panic!(),

View File

@ -1,7 +1,10 @@
//! Middlewares
#![allow(unused_imports, dead_code)]
use std::rc::Rc;
use std::error::Error;
use futures::{Async, Future, Poll};
use error::Error;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
@ -11,12 +14,12 @@ pub use self::logger::Logger;
/// Middleware start result
pub enum Started {
/// Execution completed
Done,
Done(HttpRequest),
/// New http response got generated. If middleware generates response
/// handler execution halts.
Response(HttpResponse),
/// Execution completed, but run future to completion.
Future(Box<Future<Item=(), Error=HttpResponse>>),
Response(HttpRequest, HttpResponse),
/// Execution completed, runs future to completion.
Future(Box<Future<Item=(HttpRequest, Option<HttpResponse>), Error=(HttpRequest, HttpResponse)>>),
}
/// Middleware execution result
@ -32,7 +35,7 @@ pub enum Finished {
/// Execution completed
Done,
/// Execution completed, but run future to completion
Future(Box<Future<Item=(), Error=Box<Error>>>),
Future(Box<Future<Item=(), Error=Error>>),
}
/// Middleware definition
@ -41,8 +44,8 @@ pub trait Middleware {
/// Method is called when request is ready. It may return
/// future, which should resolve before next middleware get called.
fn start(&self, req: &mut HttpRequest) -> Started {
Started::Done
fn start(&self, req: HttpRequest) -> Started {
Started::Done(req)
}
/// Method is called when handler returns response,
@ -56,192 +59,3 @@ pub trait Middleware {
Finished::Done
}
}
/// Middlewares executor
pub(crate) struct MiddlewaresExecutor {
state: ExecutorState,
fut: Option<Box<Future<Item=HttpResponse, Error=HttpResponse>>>,
started: Option<Box<Future<Item=(), Error=HttpResponse>>>,
finished: Option<Box<Future<Item=(), Error=Box<Error>>>>,
middlewares: Option<Rc<Vec<Box<Middleware>>>>,
}
enum ExecutorState {
None,
Starting(usize),
Started(usize),
Processing(usize, usize),
Finishing(usize),
}
impl Default for MiddlewaresExecutor {
fn default() -> MiddlewaresExecutor {
MiddlewaresExecutor {
fut: None,
started: None,
finished: None,
state: ExecutorState::None,
middlewares: None,
}
}
}
impl MiddlewaresExecutor {
pub fn start(&mut self, mw: Rc<Vec<Box<Middleware>>>) {
self.state = ExecutorState::Starting(0);
self.middlewares = Some(mw);
}
pub fn starting(&mut self, req: &mut HttpRequest) -> Poll<Option<HttpResponse>, ()> {
if let Some(ref middlewares) = self.middlewares {
let state = &mut self.state;
if let ExecutorState::Starting(mut idx) = *state {
loop {
// poll latest fut
if let Some(ref mut fut) = self.started {
match fut.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(())) => idx += 1,
Err(response) => {
*state = ExecutorState::Started(idx);
return Ok(Async::Ready(Some(response)))
}
}
}
self.started = None;
if idx >= middlewares.len() {
*state = ExecutorState::Started(idx-1);
return Ok(Async::Ready(None))
} else {
match middlewares[idx].start(req) {
Started::Done => idx += 1,
Started::Response(resp) => {
*state = ExecutorState::Started(idx);
return Ok(Async::Ready(Some(resp)))
},
Started::Future(fut) => {
self.started = Some(fut);
},
}
}
}
}
}
Ok(Async::Ready(None))
}
pub fn processing(&mut self, req: &mut HttpRequest) -> Poll<Option<HttpResponse>, ()> {
if let Some(ref middlewares) = self.middlewares {
let state = &mut self.state;
match *state {
ExecutorState::Processing(mut idx, total) => {
loop {
// poll latest fut
let mut resp = match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(response)) | Err(response) => {
idx += 1;
response
}
};
self.fut = None;
loop {
if idx == 0 {
*state = ExecutorState::Finishing(total);
return Ok(Async::Ready(Some(resp)))
} else {
match middlewares[idx].response(req, resp) {
Response::Response(r) => {
idx -= 1;
resp = r
},
Response::Future(fut) => {
self.fut = Some(fut);
break
},
}
}
}
}
}
_ => Ok(Async::Ready(None))
}
} else {
Ok(Async::Ready(None))
}
}
pub fn finishing(&mut self, req: &mut HttpRequest, resp: &HttpResponse) -> Poll<(), ()> {
if let Some(ref middlewares) = self.middlewares {
let state = &mut self.state;
if let ExecutorState::Finishing(mut idx) = *state {
loop {
// poll latest fut
if let Some(ref mut fut) = self.finished {
match fut.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(())) => idx -= 1,
Err(err) => {
error!("Middleware finish error: {}", err);
}
}
}
self.finished = None;
match middlewares[idx].finish(req, resp) {
Finished::Done => {
if idx == 0 {
return Ok(Async::Ready(()))
} else {
idx -= 1
}
}
Finished::Future(fut) => {
self.finished = Some(fut);
},
}
}
}
}
Ok(Async::Ready(()))
}
pub fn response(&mut self, req: &mut HttpRequest, resp: HttpResponse)
-> Option<HttpResponse>
{
if let Some(ref middlewares) = self.middlewares {
let mut resp = resp;
let state = &mut self.state;
match *state {
ExecutorState::Started(mut idx) => {
let total = idx;
loop {
resp = match middlewares[idx].response(req, resp) {
Response::Response(r) => {
if idx == 0 {
*state = ExecutorState::Finishing(total);
return Some(r)
} else {
idx -= 1;
r
}
},
Response::Future(fut) => {
*state = ExecutorState::Processing(idx, total);
self.fut = Some(fut);
return None
},
};
}
}
_ => Some(resp)
}
} else {
Some(resp)
}
}
}