1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-30 18:34:36 +01:00

simplify middleware api; fix examples

This commit is contained in:
Nikolay Kim 2017-11-26 21:47:33 -08:00
parent 5a3b6638a7
commit fdafb0c848
11 changed files with 74 additions and 64 deletions

View File

@ -15,11 +15,10 @@ impl Actor for MyRoute {
impl Route for MyRoute { impl Route for MyRoute {
type State = (); type State = ();
fn request(req: &mut HttpRequest, payload: Payload, fn request(mut req: HttpRequest, ctx: &mut HttpContext<Self>) -> RouteResult<Self> {
ctx: &mut HttpContext<Self>) -> RouteResult<Self> {
println!("{:?}", req); println!("{:?}", req);
let multipart = req.multipart(payload)?; let multipart = req.multipart()?;
// get Multipart stream // get Multipart stream
WrapStream::<MyRoute>::actstream(multipart) WrapStream::<MyRoute>::actstream(multipart)

View File

@ -9,7 +9,7 @@ use std::io::Read;
use actix_web::*; use actix_web::*;
/// somple handle /// somple handle
fn index(req: &mut HttpRequest, _payload: Payload, state: &()) -> HttpResponse { fn index(req: HttpRequest) -> HttpResponse {
println!("{:?}", req); println!("{:?}", req);
httpcodes::HTTPOk httpcodes::HTTPOk
.builder() .builder()
@ -36,7 +36,7 @@ fn main() {
// register simple handler, handle all methods // register simple handler, handle all methods
.handler("/index.html", index) .handler("/index.html", index)
// with path parameters // with path parameters
.resource("/", |r| r.handler(Method::GET, |req, _, _| { .resource("/", |r| r.handler(Method::GET, |req| {
Ok(httpcodes::HTTPFound Ok(httpcodes::HTTPFound
.builder() .builder()
.header("LOCATION", "/index.html") .header("LOCATION", "/index.html")

View File

@ -48,13 +48,13 @@ impl Actor for WsChatSession {
impl Route for WsChatSession { impl Route for WsChatSession {
type State = WsChatSessionState; type State = WsChatSessionState;
fn request(req: &mut HttpRequest, fn request(mut req: HttpRequest<WsChatSessionState>,
payload: Payload, ctx: &mut HttpContext<Self>) -> RouteResult<Self> ctx: &mut HttpContext<Self>) -> RouteResult<Self>
{ {
// websocket handshakre, it may fail if request is not websocket request // websocket handshakre, it may fail if request is not websocket request
let resp = ws::handshake(&req)?; let resp = ws::handshake(&req)?;
ctx.start(resp); ctx.start(resp);
ctx.add_stream(ws::WsStream::new(payload)); ctx.add_stream(ws::WsStream::new(&mut req));
Reply::async( Reply::async(
WsChatSession { WsChatSession {
id: 0, id: 0,
@ -207,10 +207,10 @@ fn main() {
// Create Http server with websocket support // Create Http server with websocket support
HttpServer::new( HttpServer::new(
Application::builder("/", state) Application::build("/", state)
// redirect to websocket.html // redirect to websocket.html
.resource("/", |r| .resource("/", |r|
r.handler(Method::GET, |req, payload, state| { r.handler(Method::GET, |req| {
Ok(httpcodes::HTTPFound Ok(httpcodes::HTTPFound
.builder() .builder()
.header("LOCATION", "/static/websocket.html") .header("LOCATION", "/static/websocket.html")

View File

@ -29,6 +29,7 @@ pub struct HttpContext<A> where A: Actor<Context=HttpContext<A>> + Route,
address: ActorAddressCell<A>, address: ActorAddressCell<A>,
stream: VecDeque<Frame>, stream: VecDeque<Frame>,
wait: ActorWaitCell<A>, wait: ActorWaitCell<A>,
app_state: Rc<<A as Route>::State>,
disconnected: bool, disconnected: bool,
} }
@ -101,9 +102,10 @@ impl<A> AsyncContextApi<A> for HttpContext<A> where A: Actor<Context=Self> + Rou
} }
} }
impl<A> Default for HttpContext<A> where A: Actor<Context=Self> + Route { impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
fn default() -> HttpContext<A> { pub fn new(state: Rc<<A as Route>::State>) -> HttpContext<A>
{
HttpContext { HttpContext {
act: None, act: None,
state: ActorState::Started, state: ActorState::Started,
@ -112,12 +114,10 @@ impl<A> Default for HttpContext<A> where A: Actor<Context=Self> + Route {
address: ActorAddressCell::default(), address: ActorAddressCell::default(),
wait: ActorWaitCell::default(), wait: ActorWaitCell::default(),
stream: VecDeque::new(), stream: VecDeque::new(),
app_state: state,
disconnected: false, disconnected: false,
} }
} }
}
impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
pub(crate) fn set_actor(&mut self, act: A) { pub(crate) fn set_actor(&mut self, act: A) {
self.act = Some(act) self.act = Some(act)
@ -126,6 +126,11 @@ impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
impl<A> HttpContext<A> where A: Actor<Context=Self> + Route { impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
/// Shared application state
pub fn state(&self) -> &<A as Route>::State {
&self.app_state
}
/// Start response processing /// Start response processing
pub fn start<R: Into<HttpResponse>>(&mut self, response: R) { pub fn start<R: Into<HttpResponse>>(&mut self, response: R) {
self.stream.push_back(Frame::Message(response.into())) self.stream.push_back(Frame::Message(response.into()))

View File

@ -99,6 +99,11 @@ impl<S> HttpRequest<S> {
&self.1 &self.1
} }
/// Clone application state
pub(crate) fn clone_state(&self) -> Rc<S> {
Rc::clone(&self.1)
}
/// Protocol extensions. /// Protocol extensions.
#[inline] #[inline]
pub fn extensions(&mut self) -> &mut Extensions { pub fn extensions(&mut self) -> &mut Extensions {
@ -287,8 +292,9 @@ impl<S> HttpRequest<S> {
/// Return stream to process BODY as multipart. /// Return stream to process BODY as multipart.
/// ///
/// Content-type: multipart/form-data; /// Content-type: multipart/form-data;
pub fn multipart(&self, payload: Payload) -> Result<Multipart, MultipartError> { pub fn multipart(&mut self) -> Result<Multipart, MultipartError> {
Ok(Multipart::new(Multipart::boundary(&self.0.headers)?, payload)) let boundary = Multipart::boundary(&self.0.headers)?;
Ok(Multipart::new(boundary, self.take_payload()))
} }
/// Parse `application/x-www-form-urlencoded` encoded body. /// Parse `application/x-www-form-urlencoded` encoded body.

View File

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

View File

@ -16,12 +16,12 @@ pub enum Started {
/// Moddleware error /// Moddleware error
Err(Error), Err(Error),
/// Execution completed /// Execution completed
Done(HttpRequest), Done,
/// New http response got generated. If middleware generates response /// New http response got generated. If middleware generates response
/// handler execution halts. /// handler execution halts.
Response(HttpRequest, HttpResponse), Response(HttpResponse),
/// Execution completed, runs future to completion. /// Execution completed, runs future to completion.
Future(Box<Future<Item=(HttpRequest, Option<HttpResponse>), Error=Error>>), Future(Box<Future<Item=Option<HttpResponse>, Error=Error>>),
} }
/// Middleware execution result /// Middleware execution result
@ -48,8 +48,8 @@ pub trait Middleware {
/// Method is called when request is ready. It may return /// Method is called when request is ready. It may return
/// future, which should resolve before next middleware get called. /// future, which should resolve before next middleware get called.
fn start(&self, req: HttpRequest) -> Started { fn start(&self, req: &mut HttpRequest) -> Started {
Started::Done(req) Started::Done
} }
/// Method is called when handler returns response, /// Method is called when handler returns response,

View File

@ -90,14 +90,15 @@ impl<T: SessionBackend> SessionStorage<T> {
impl<T: SessionBackend> Middleware for SessionStorage<T> { impl<T: SessionBackend> Middleware for SessionStorage<T> {
fn start(&self, mut req: HttpRequest) -> Started { fn start(&self, req: &mut HttpRequest) -> Started {
let mut req = req.clone();
let fut = self.0.from_request(&mut req) let fut = self.0.from_request(&mut req)
.then(|res| { .then(move |res| {
match res { match res {
Ok(sess) => { Ok(sess) => {
req.extensions().insert(Arc::new(SessionImplBox(Box::new(sess)))); req.extensions().insert(Arc::new(SessionImplBox(Box::new(sess))));
let resp: Option<HttpResponse> = None; FutOk(None)
FutOk((req, resp))
}, },
Err(err) => FutErr(err) Err(err) => FutErr(err)
} }

View File

@ -146,13 +146,14 @@ impl Pipeline {
} }
} }
type Fut = Box<Future<Item=(HttpRequest, Option<HttpResponse>), Error=Error>>; type Fut = Box<Future<Item=Option<HttpResponse>, Error=Error>>;
/// Middlewares start executor /// Middlewares start executor
struct Start { struct Start {
idx: usize, idx: usize,
hnd: *mut Handler, hnd: *mut Handler,
disconnected: bool, disconnected: bool,
req: HttpRequest,
fut: Option<Fut>, fut: Option<Fut>,
middlewares: Rc<Vec<Box<Middleware>>>, middlewares: Rc<Vec<Box<Middleware>>>,
} }
@ -169,10 +170,11 @@ impl Start {
Start { Start {
idx: 0, idx: 0,
fut: None, fut: None,
req: req,
disconnected: false, disconnected: false,
hnd: handler as *const _ as *mut _, hnd: handler as *const _ as *mut _,
middlewares: mw, middlewares: mw,
}.start(req) }.start()
} }
fn disconnected(&mut self) { fn disconnected(&mut self) {
@ -187,43 +189,40 @@ impl Start {
task task
} }
fn start(mut self, mut req: HttpRequest) -> Result<StartResult, Error> { fn start(mut self) -> Result<StartResult, Error> {
let len = self.middlewares.len(); let len = self.middlewares.len();
loop { loop {
if self.idx == len { if self.idx == len {
let task = (unsafe{&*self.hnd})(req.clone()); let task = (unsafe{&*self.hnd})(self.req.clone());
return Ok(StartResult::Ready( return Ok(StartResult::Ready(
Box::new(Handle::new(self.idx-1, req, self.prepare(task), self.middlewares)))) Box::new(Handle::new(self.idx-1, self.req.clone(),
self.prepare(task), self.middlewares))))
} else { } else {
req = match self.middlewares[self.idx].start(req) { match self.middlewares[self.idx].start(&mut self.req) {
Started::Done(req) => { Started::Done =>
self.idx += 1; self.idx += 1,
req Started::Response(resp) =>
}
Started::Response(req, resp) => {
return Ok(StartResult::Ready( return Ok(StartResult::Ready(
Box::new(Handle::new( Box::new(Handle::new(
self.idx, req, self.prepare(Task::reply(resp)), self.middlewares)))) self.idx, self.req.clone(),
}, self.prepare(Task::reply(resp)), self.middlewares)))),
Started::Future(mut fut) => { Started::Future(mut fut) =>
match fut.poll() { match fut.poll() {
Ok(Async::NotReady) => { Ok(Async::NotReady) => {
self.fut = Some(fut); self.fut = Some(fut);
return Ok(StartResult::NotReady(self)) return Ok(StartResult::NotReady(self))
} }
Ok(Async::Ready((req, resp))) => { Ok(Async::Ready(resp)) => {
if let Some(resp) = resp { if let Some(resp) = resp {
return Ok(StartResult::Ready( return Ok(StartResult::Ready(
Box::new(Handle::new( Box::new(Handle::new(
self.idx, req, self.idx, self.req.clone(),
self.prepare(Task::reply(resp)), self.middlewares)))) self.prepare(Task::reply(resp)), self.middlewares))))
} }
self.idx += 1; self.idx += 1;
req
} }
Err(err) => return Err(err) Err(err) => return Err(err)
} },
},
Started::Err(err) => return Err(err), Started::Err(err) => return Err(err),
} }
} }
@ -235,28 +234,28 @@ impl Start {
'outer: loop { 'outer: loop {
match self.fut.as_mut().unwrap().poll() { match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady), Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready((mut req, resp))) => { Ok(Async::Ready(resp)) => {
self.idx += 1; self.idx += 1;
if let Some(resp) = resp { if let Some(resp) = resp {
return Ok(Async::Ready(Box::new(Handle::new( return Ok(Async::Ready(Box::new(Handle::new(
self.idx-1, req, self.idx-1, self.req.clone(),
self.prepare(Task::reply(resp)), Rc::clone(&self.middlewares))))) self.prepare(Task::reply(resp)), Rc::clone(&self.middlewares)))))
} }
if self.idx == len { if self.idx == len {
let task = (unsafe{&*self.hnd})(req.clone()); let task = (unsafe{&*self.hnd})(self.req.clone());
return Ok(Async::Ready(Box::new(Handle::new( return Ok(Async::Ready(Box::new(Handle::new(
self.idx-1, req, self.prepare(task), Rc::clone(&self.middlewares))))) self.idx-1, self.req.clone(),
self.prepare(task), Rc::clone(&self.middlewares)))))
} else { } else {
loop { loop {
req = match self.middlewares[self.idx].start(req) { match self.middlewares[self.idx].start(&mut self.req) {
Started::Done(req) => { Started::Done =>
self.idx += 1; self.idx += 1,
req Started::Response(resp) => {
}
Started::Response(req, resp) => {
self.idx += 1; self.idx += 1;
return Ok(Async::Ready(Box::new(Handle::new( return Ok(Async::Ready(Box::new(Handle::new(
self.idx-1, req, self.prepare(Task::reply(resp)), self.idx-1, self.req.clone(),
self.prepare(Task::reply(resp)),
Rc::clone(&self.middlewares))))) Rc::clone(&self.middlewares)))))
}, },
Started::Future(fut) => { Started::Future(fut) => {

View File

@ -96,7 +96,7 @@ impl<A, S> RouteHandler<S> for RouteFactory<A, S>
S: 'static S: 'static
{ {
fn handle(&self, mut req: HttpRequest<A::State>) -> Task { fn handle(&self, mut req: HttpRequest<A::State>) -> Task {
let mut ctx = HttpContext::default(); let mut ctx = HttpContext::new(req.clone_state());
// handle EXPECT header // handle EXPECT header
if req.headers().contains_key(header::EXPECT) { if req.headers().contains_key(header::EXPECT) {

View File

@ -61,9 +61,9 @@ struct MiddlewareTest {
} }
impl middlewares::Middleware for MiddlewareTest { impl middlewares::Middleware for MiddlewareTest {
fn start(&self, req: HttpRequest) -> middlewares::Started { fn start(&self, _: &mut HttpRequest) -> middlewares::Started {
self.start.store(self.start.load(Ordering::Relaxed) + 1, Ordering::Relaxed); self.start.store(self.start.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
middlewares::Started::Done(req) middlewares::Started::Done
} }
fn response(&self, _: &mut HttpRequest, resp: HttpResponse) -> middlewares::Response { fn response(&self, _: &mut HttpRequest, resp: HttpResponse) -> middlewares::Response {