diff --git a/src/application.rs b/src/application.rs index 21de726e..925dbb01 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,4 +1,5 @@ use std::rc::Rc; +use std::cell::RefCell; use std::collections::HashMap; use handler::Reply; @@ -6,7 +7,7 @@ use router::{Router, Pattern}; use resource::Resource; use httprequest::HttpRequest; use channel::{HttpHandler, IntoHttpHandler, HttpHandlerTask}; -use pipeline::Pipeline; +use pipeline::{Pipeline, PipelineHandler}; use middleware::Middleware; use server::ServerSettings; @@ -14,19 +15,20 @@ use server::ServerSettings; pub struct HttpApplication { state: Rc, prefix: String, - default: Resource, router: Router, - resources: Vec>, + inner: Rc>>, middlewares: Rc>>>, } -impl HttpApplication { +pub(crate) struct Inner { + default: Resource, + router: Router, + resources: Vec>, +} - pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest { - req.with_state(Rc::clone(&self.state), self.router.clone()) - } +impl PipelineHandler for Inner { - pub(crate) fn run(&mut self, mut req: HttpRequest) -> Reply { + fn handle(&mut self, mut req: HttpRequest) -> Reply { if let Some(idx) = self.router.recognize(&mut req) { self.resources[idx].handle(req.clone(), Some(&mut self.default)) } else { @@ -35,14 +37,25 @@ impl HttpApplication { } } +impl HttpApplication { + #[cfg(test)] + pub(crate) fn run(&mut self, req: HttpRequest) -> Reply { + self.inner.borrow_mut().handle(req) + } + #[cfg(test)] + pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest { + req.with_state(Rc::clone(&self.state), self.router.clone()) + } +} + impl HttpHandler for HttpApplication { fn handle(&mut self, req: HttpRequest) -> Result, HttpRequest> { if req.path().starts_with(&self.prefix) { - let req = self.prepare_request(req); - // TODO: redesign run callback - Ok(Box::new(Pipeline::new(req, Rc::clone(&self.middlewares), - &mut |req: HttpRequest| self.run(req)))) + let inner = Rc::clone(&self.inner); + let req = req.with_state(Rc::clone(&self.state), self.router.clone()); + + Ok(Box::new(Pipeline::new(req, Rc::clone(&self.middlewares), inner))) } else { Err(req) } @@ -267,12 +280,19 @@ impl Application where S: 'static { } let (router, resources) = Router::new(prefix, resources); + + let inner = Rc::new(RefCell::new( + Inner { + default: parts.default, + router: router.clone(), + resources: resources } + )); + HttpApplication { state: Rc::new(parts.state), prefix: prefix.to_owned(), - default: parts.default, - router: router, - resources: resources, + inner: inner, + router: router.clone(), middlewares: Rc::new(parts.middlewares), } } diff --git a/src/lib.rs b/src/lib.rs index 1552787d..ec1aebe4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,15 +48,13 @@ extern crate regex; #[macro_use] extern crate bitflags; #[macro_use] +extern crate failure; +#[macro_use] extern crate futures; extern crate tokio_io; extern crate tokio_core; extern crate mio; extern crate net2; - -extern crate failure; -#[macro_use] extern crate failure_derive; - extern crate cookie; extern crate http; extern crate httparse; diff --git a/src/middleware/session.rs b/src/middleware/session.rs index fbde3125..7f610e2d 100644 --- a/src/middleware/session.rs +++ b/src/middleware/session.rs @@ -276,7 +276,7 @@ impl CookieSessionInner { fn new(key: &[u8]) -> CookieSessionInner { CookieSessionInner { key: Key::from_master(key), - name: "actix_session".to_owned(), + name: "actix-session".to_owned(), path: "/".to_owned(), domain: None, secure: true } diff --git a/src/pipeline.rs b/src/pipeline.rs index d4a8edd8..80c1a19c 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -1,5 +1,6 @@ use std::{io, mem}; use std::rc::Rc; +use std::cell::RefCell; use std::marker::PhantomData; use futures::{Async, Poll, Future, Stream}; @@ -14,21 +15,23 @@ use h1writer::{Writer, WriterState}; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Middleware, Finished, Started, Response}; +use application::Inner; -type Handler = FnMut(HttpRequest) -> Reply; -pub(crate) type PipelineHandler<'a, S> = &'a mut FnMut(HttpRequest) -> Reply; +pub trait PipelineHandler { + fn handle(&mut self, req: HttpRequest) -> Reply; +} -pub struct Pipeline(PipelineInfo, PipelineState); +pub struct Pipeline(PipelineInfo, PipelineState); -enum PipelineState { +enum PipelineState { None, Error, - Starting(StartMiddlewares), - Handler(WaitingResponse), - RunMiddlewares(RunMiddlewares), - Response(ProcessResponse), - Finishing(FinishingMiddlewares), - Completed(Completed), + Starting(StartMiddlewares), + Handler(WaitingResponse), + RunMiddlewares(RunMiddlewares), + Response(ProcessResponse), + Finishing(FinishingMiddlewares), + Completed(Completed), } struct PipelineInfo { @@ -75,11 +78,11 @@ enum PipelineResponse { Response(Box>), } -impl Pipeline { +impl> Pipeline { pub fn new(req: HttpRequest, mws: Rc>>>, - handler: PipelineHandler) -> Pipeline + handler: Rc>) -> Pipeline { let mut info = PipelineInfo { req: req, @@ -94,15 +97,14 @@ impl Pipeline { } } -impl Pipeline<()> { +impl Pipeline<(), Inner<()>> { pub fn error>(err: R) -> Box { - Box::new(Pipeline( - PipelineInfo::new( - HttpRequest::default()), ProcessResponse::init(err.into()))) + Box::new(Pipeline::<(), Inner<()>>( + PipelineInfo::new(HttpRequest::default()), ProcessResponse::init(err.into()))) } } -impl Pipeline { +impl Pipeline { fn is_done(&self) -> bool { match self.1 { @@ -115,7 +117,7 @@ impl Pipeline { } } -impl HttpHandlerTask for Pipeline { +impl> HttpHandlerTask for Pipeline { fn disconnected(&mut self) { if let Some(ref mut context) = self.0.context { @@ -274,20 +276,22 @@ impl HttpHandlerTask for Pipeline { type Fut = Box, Error=Error>>; /// Middlewares start executor -struct StartMiddlewares { - hnd: *mut Handler, +struct StartMiddlewares { + hnd: Rc>, fut: Option, + _s: PhantomData, } -impl StartMiddlewares { +impl> StartMiddlewares { - fn init(info: &mut PipelineInfo, handler: PipelineHandler) -> PipelineState { + fn init(info: &mut PipelineInfo, handler: Rc>) -> PipelineState + { // execute middlewares, we need this stage because middlewares could be non-async // and we can move to next state immidietly let len = info.mws.len(); loop { if info.count == len { - let reply = (&mut *handler)(info.req.clone()); + let reply = handler.borrow_mut().handle(info.req.clone()); return WaitingResponse::init(info, reply) } else { match info.mws[info.count].start(&mut info.req) { @@ -299,8 +303,9 @@ impl StartMiddlewares { match fut.poll() { Ok(Async::NotReady) => return PipelineState::Starting(StartMiddlewares { - hnd: handler as *const _ as *mut _, - fut: Some(fut)}), + hnd: handler, + fut: Some(fut), + _s: PhantomData}), Ok(Async::Ready(resp)) => { if let Some(resp) = resp { return RunMiddlewares::init(info, resp); @@ -317,7 +322,8 @@ impl StartMiddlewares { } } - fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> { + fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> + { let len = info.mws.len(); 'outer: loop { match self.fut.as_mut().unwrap().poll() { @@ -329,7 +335,7 @@ impl StartMiddlewares { return Ok(RunMiddlewares::init(info, resp)); } if info.count == len { - let reply = (unsafe{&mut *self.hnd})(info.req.clone()); + let reply = (*self.hnd.borrow_mut()).handle(info.req.clone()); return Ok(WaitingResponse::init(info, reply)); } else { loop { @@ -357,29 +363,33 @@ impl StartMiddlewares { } // waiting for response -struct WaitingResponse { +struct WaitingResponse { stream: PipelineResponse, _s: PhantomData, + _h: PhantomData, } -impl WaitingResponse { +impl WaitingResponse { #[inline] - fn init(info: &mut PipelineInfo, reply: Reply) -> PipelineState + fn init(info: &mut PipelineInfo, reply: Reply) -> PipelineState { match reply.into() { ReplyItem::Message(resp) => RunMiddlewares::init(info, resp), ReplyItem::Actor(ctx) => PipelineState::Handler( - WaitingResponse { stream: PipelineResponse::Context(ctx), _s: PhantomData }), + WaitingResponse { stream: PipelineResponse::Context(ctx), + _s: PhantomData, _h: PhantomData }), ReplyItem::Future(fut) => PipelineState::Handler( - WaitingResponse { stream: PipelineResponse::Response(fut), _s: PhantomData }), + WaitingResponse { stream: PipelineResponse::Response(fut), + _s: PhantomData, _h: PhantomData }), } } - fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> { + fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> + { let stream = mem::replace(&mut self.stream, PipelineResponse::None); match stream { @@ -430,15 +440,16 @@ impl WaitingResponse { } /// Middlewares response executor -struct RunMiddlewares { +struct RunMiddlewares { curr: usize, fut: Option>>, _s: PhantomData, + _h: PhantomData, } -impl RunMiddlewares { +impl RunMiddlewares { - fn init(info: &mut PipelineInfo, mut resp: HttpResponse) -> PipelineState + fn init(info: &mut PipelineInfo, mut resp: HttpResponse) -> PipelineState { if info.count == 0 { return ProcessResponse::init(resp); @@ -462,20 +473,23 @@ impl RunMiddlewares { }, Response::Future(fut) => { return PipelineState::RunMiddlewares( - RunMiddlewares { curr: curr, fut: Some(fut), _s: PhantomData }) + RunMiddlewares { curr: curr, fut: Some(fut), + _s: PhantomData, _h: PhantomData }) }, }; } } - fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> { + fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> + { let len = info.mws.len(); loop { // poll latest fut let mut resp = match self.fut.as_mut().unwrap().poll() { - Ok(Async::NotReady) => - return Ok(PipelineState::RunMiddlewares(self)), + Ok(Async::NotReady) => { + return Err(PipelineState::RunMiddlewares(self)) + } Ok(Async::Ready(resp)) => { self.curr += 1; resp @@ -506,12 +520,13 @@ impl RunMiddlewares { } } -struct ProcessResponse { +struct ProcessResponse { resp: HttpResponse, iostate: IOState, running: RunningState, drain: Option>, _s: PhantomData, + _h: PhantomData, } #[derive(PartialEq)] @@ -543,21 +558,21 @@ enum IOState { Done, } -impl ProcessResponse { +impl ProcessResponse { #[inline] - fn init(resp: HttpResponse) -> PipelineState + fn init(resp: HttpResponse) -> PipelineState { PipelineState::Response( ProcessResponse{ resp: resp, iostate: IOState::Response, running: RunningState::Running, drain: None, - _s: PhantomData}) + _s: PhantomData, _h: PhantomData}) } fn poll_io(mut self, io: &mut Writer, info: &mut PipelineInfo) - -> Result, PipelineState> + -> Result, PipelineState> { if self.drain.is_none() && self.running != RunningState::Paused { // if task is paused, write buffer is probably full @@ -725,25 +740,28 @@ impl ProcessResponse { } /// Middlewares start executor -struct FinishingMiddlewares { +struct FinishingMiddlewares { resp: HttpResponse, fut: Option>>, _s: PhantomData, + _h: PhantomData, } -impl FinishingMiddlewares { +impl FinishingMiddlewares { - fn init(info: &mut PipelineInfo, resp: HttpResponse) -> PipelineState { + fn init(info: &mut PipelineInfo, resp: HttpResponse) -> PipelineState { if info.count == 0 { Completed::init(info) } else { - match (FinishingMiddlewares{resp: resp, fut: None, _s: PhantomData}).poll(info) { + match (FinishingMiddlewares{resp: resp, fut: None, + _s: PhantomData, _h: PhantomData}).poll(info) { Ok(st) | Err(st) => st, } } } - fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> { + fn poll(mut self, info: &mut PipelineInfo) -> Result, PipelineState> + { loop { // poll latest fut let not_ready = if let Some(ref mut fut) = self.fut { @@ -782,24 +800,26 @@ impl FinishingMiddlewares { } } -struct Completed(PhantomData); +struct Completed(PhantomData, PhantomData); -impl Completed { +impl Completed { #[inline] - fn init(info: &mut PipelineInfo) -> PipelineState { + fn init(info: &mut PipelineInfo) -> PipelineState { if info.context.is_none() { PipelineState::None } else { - PipelineState::Completed(Completed(PhantomData)) + PipelineState::Completed(Completed(PhantomData, PhantomData)) } } #[inline] - fn poll(self, info: &mut PipelineInfo) -> Result, PipelineState> { + fn poll(self, info: &mut PipelineInfo) -> Result, PipelineState> { match info.poll_context() { - Ok(Async::NotReady) => Ok(PipelineState::Completed(Completed(PhantomData))), - Ok(Async::Ready(())) => Ok(PipelineState::None), + Ok(Async::NotReady) => + Ok(PipelineState::Completed(Completed(PhantomData, PhantomData))), + Ok(Async::Ready(())) => + Ok(PipelineState::None), Err(_) => Ok(PipelineState::Error), } } @@ -813,11 +833,11 @@ mod tests { use tokio_core::reactor::Core; use futures::future::{lazy, result}; - impl PipelineState { + impl PipelineState { fn is_none(&self) -> Option { if let PipelineState::None = *self { Some(true) } else { None } } - fn completed(self) -> Option> { + fn completed(self) -> Option> { if let PipelineState::Completed(c) = self { Some(c) } else { None } } } @@ -831,14 +851,14 @@ mod tests { fn test_completed() { Core::new().unwrap().run(lazy(|| { let mut info = PipelineInfo::new(HttpRequest::default()); - Completed::init(&mut info).is_none().unwrap(); + Completed::<(), Inner<()>>::init(&mut info).is_none().unwrap(); let req = HttpRequest::default(); let mut ctx = HttpContext::new(req.clone(), MyActor); let addr: Address<_> = ctx.address(); let mut info = PipelineInfo::new(req); info.context = Some(Box::new(ctx)); - let mut state = Completed::init(&mut info).completed().unwrap(); + let mut state = Completed::<(), Inner<()>>::init(&mut info).completed().unwrap(); let st = state.poll(&mut info).ok().unwrap(); let pp = Pipeline(info, st); diff --git a/src/router.rs b/src/router.rs index bbb70162..e5fc091f 100644 --- a/src/router.rs +++ b/src/router.rs @@ -54,8 +54,11 @@ impl Router { srv: ServerSettings::default() })), resources) } + #[allow(mutable_transmutes)] pub(crate) fn set_server_settings(&mut self, settings: ServerSettings) { - Rc::get_mut(&mut self.0).unwrap().srv = settings; + let inner: &Inner = self.0.as_ref(); + let inner: &mut Inner = unsafe{mem::transmute(inner)}; + inner.srv = settings; } /// Router prefix