diff --git a/src/application.rs b/src/application.rs index cf58cc971..38886efc5 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,6 +1,6 @@ use std::mem; use std::rc::Rc; -use std::cell::RefCell; +use std::cell::UnsafeCell; use std::collections::HashMap; use handler::Reply; @@ -9,7 +9,7 @@ use resource::{ResourceHandler}; use header::ContentEncoding; use handler::{Handler, RouteHandler, WrapHandler}; use httprequest::HttpRequest; -use pipeline::{Pipeline, PipelineHandler}; +use pipeline::{Pipeline, PipelineHandler, HandlerType}; use middleware::Middleware; use server::{HttpHandler, IntoHttpHandler, HttpHandlerTask, ServerSettings}; @@ -21,7 +21,7 @@ pub struct HttpApplication { state: Rc, prefix: String, router: Router, - inner: Rc>>, + inner: Rc>>, middlewares: Rc>>>, } @@ -29,7 +29,6 @@ pub(crate) struct Inner { prefix: usize, default: ResourceHandler, encoding: ContentEncoding, - router: Router, resources: Vec>, handlers: Vec<(String, Box>)>, } @@ -40,39 +39,60 @@ impl PipelineHandler for Inner { self.encoding } - 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)) + fn handle(&mut self, req: HttpRequest, htype: HandlerType) -> Reply { + match htype { + HandlerType::Normal(idx) => + self.resources[idx].handle(req, Some(&mut self.default)), + HandlerType::Handler(idx) => + self.handlers[idx].1.handle(req), + HandlerType::Default => + self.default.handle(req, None) + } + } +} + +impl HttpApplication { + + #[inline] + fn as_ref(&self) -> &Inner { + unsafe{&*self.inner.get()} + } + + #[inline] + fn get_handler(&self, req: &mut HttpRequest) -> HandlerType { + if let Some(idx) = self.router.recognize(req) { + HandlerType::Normal(idx) } else { - for &mut (ref prefix, ref mut handler) in &mut self.handlers { + let inner = self.as_ref(); + for idx in 0..inner.handlers.len() { + let &(ref prefix, _) = &inner.handlers[idx]; let m = { - let path = &req.path()[self.prefix..]; + let path = &req.path()[inner.prefix..]; path.starts_with(prefix) && ( path.len() == prefix.len() || path.split_at(prefix.len()).1.starts_with('/')) }; if m { let path: &'static str = unsafe { - mem::transmute(&req.path()[self.prefix+prefix.len()..]) }; + mem::transmute(&req.path()[inner.prefix+prefix.len()..]) }; if path.is_empty() { req.match_info_mut().add("tail", ""); } else { req.match_info_mut().add("tail", path.split_at(1).1); } - return handler.handle(req) + return HandlerType::Handler(idx) } } - self.default.handle(req, None) + HandlerType::Default } } -} -#[cfg(test)] -impl HttpApplication { #[cfg(test)] - pub(crate) fn run(&mut self, req: HttpRequest) -> Reply { - self.inner.borrow_mut().handle(req) + pub(crate) fn run(&mut self, mut req: HttpRequest) -> Reply { + let tp = self.get_handler(&mut req); + unsafe{&mut *self.inner.get()}.handle(req, tp) } + #[cfg(test)] pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest { req.with_state(Rc::clone(&self.state), self.router.clone()) @@ -89,10 +109,10 @@ impl HttpHandler for HttpApplication { path.split_at(self.prefix.len()).1.starts_with('/')) }; if m { + let mut req = req.with_state(Rc::clone(&self.state), self.router.clone()); + let tp = self.get_handler(&mut 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))) + Ok(Box::new(Pipeline::new(req, Rc::clone(&self.middlewares), inner, tp))) } else { Err(req) } @@ -392,12 +412,11 @@ impl App where S: 'static { let (router, resources) = Router::new(prefix, parts.settings, resources); - let inner = Rc::new(RefCell::new( + let inner = Rc::new(UnsafeCell::new( Inner { prefix: prefix.len(), default: parts.default, encoding: parts.encoding, - router: router.clone(), handlers: parts.handlers, resources, } diff --git a/src/httprequest.rs b/src/httprequest.rs index 16dea0c8b..00aacb810 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -39,7 +39,13 @@ pub struct HttpInnerMessage { pub addr: Option, pub payload: Option, pub info: Option>, - pub resource: i16, + resource: RouterResource, +} + +#[derive(Debug, Copy, Clone,PartialEq)] +enum RouterResource { + Notset, + Normal(u16), } impl Default for HttpInnerMessage { @@ -58,7 +64,7 @@ impl Default for HttpInnerMessage { payload: None, extensions: Extensions::new(), info: None, - resource: -1, + resource: RouterResource::Notset, } } } @@ -95,12 +101,12 @@ impl HttpInnerMessage { self.addr = None; self.info = None; self.payload = None; - self.resource = -1; + self.resource = RouterResource::Notset; } } lazy_static!{ - static ref RESOURCE: Resource = Resource::default(); + static ref RESOURCE: Resource = Resource::unset(); } @@ -128,7 +134,7 @@ impl HttpRequest<()> { addr: None, extensions: Extensions::new(), info: None, - resource: -1, + resource: RouterResource::Notset, }), None, None, @@ -330,17 +336,16 @@ impl HttpRequest { /// This method returns reference to matched `Resource` object. #[inline] pub fn resource(&self) -> &Resource { - let idx = self.as_ref().resource; - if idx >= 0 { - if let Some(ref router) = self.2 { + if let Some(ref router) = self.2 { + if let RouterResource::Normal(idx) = self.as_ref().resource { return router.get_resource(idx as usize) } } &*RESOURCE } - pub(crate) fn set_resource(&mut self, idx: usize) { - self.as_mut().resource = idx as i16; + pub(crate) fn set_resource(&mut self, res: usize) { + self.as_mut().resource = RouterResource::Normal(res as u16); } /// Peer socket address diff --git a/src/pipeline.rs b/src/pipeline.rs index d8a5dcfb2..842d519ab 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -1,6 +1,6 @@ use std::{io, mem}; use std::rc::Rc; -use std::cell::RefCell; +use std::cell::UnsafeCell; use std::marker::PhantomData; use log::Level::Debug; @@ -18,11 +18,18 @@ use middleware::{Middleware, Finished, Started, Response}; use application::Inner; use server::{Writer, WriterState, HttpHandlerTask}; +#[derive(Debug, Clone, Copy)] +pub(crate) enum HandlerType { + Normal(usize), + Handler(usize), + Default, +} + pub(crate) trait PipelineHandler { fn encoding(&self) -> ContentEncoding; - fn handle(&mut self, req: HttpRequest) -> Reply; + fn handle(&mut self, req: HttpRequest, htype: HandlerType) -> Reply; } pub(crate) struct Pipeline(PipelineInfo, PipelineState); @@ -105,7 +112,7 @@ impl> Pipeline { pub fn new(req: HttpRequest, mws: Rc>>>, - handler: Rc>) -> Pipeline + handler: Rc>, htype: HandlerType) -> Pipeline { let mut info = PipelineInfo { req, mws, @@ -113,9 +120,9 @@ impl> Pipeline { error: None, context: None, disconnected: None, - encoding: handler.borrow().encoding(), + encoding: unsafe{&*handler.get()}.encoding(), }; - let state = StartMiddlewares::init(&mut info, handler); + let state = StartMiddlewares::init(&mut info, handler, htype); Pipeline(info, state) } @@ -209,20 +216,23 @@ type Fut = Box, Error=Error>>; /// Middlewares start executor struct StartMiddlewares { - hnd: Rc>, + hnd: Rc>, + htype: HandlerType, fut: Option, _s: PhantomData, } impl> StartMiddlewares { - fn init(info: &mut PipelineInfo, handler: Rc>) -> PipelineState { + fn init(info: &mut PipelineInfo, hnd: Rc>, htype: HandlerType) + -> PipelineState + { // execute middlewares, we need this stage because middlewares could be non-async // and we can move to next state immediately let len = info.mws.len() as u16; loop { if info.count == len { - let reply = handler.borrow_mut().handle(info.req.clone()); + let reply = unsafe{&mut *hnd.get()}.handle(info.req.clone(), htype); return WaitingResponse::init(info, reply) } else { match info.mws[info.count as usize].start(&mut info.req) { @@ -234,7 +244,7 @@ impl> StartMiddlewares { match fut.poll() { Ok(Async::NotReady) => return PipelineState::Starting(StartMiddlewares { - hnd: handler, + hnd, htype, fut: Some(fut), _s: PhantomData}), Ok(Async::Ready(resp)) => { @@ -264,7 +274,8 @@ impl> StartMiddlewares { return Some(RunMiddlewares::init(info, resp)); } if info.count == len { - let reply = (*self.hnd.borrow_mut()).handle(info.req.clone()); + let reply = unsafe{ + &mut *self.hnd.get()}.handle(info.req.clone(), self.htype); return Some(WaitingResponse::init(info, reply)); } else { loop { diff --git a/src/router.rs b/src/router.rs index 050520b20..b8e6baf00 100644 --- a/src/router.rs +++ b/src/router.rs @@ -12,7 +12,6 @@ use resource::ResourceHandler; use httprequest::HttpRequest; use server::ServerSettings; - /// Interface for application router. pub struct Router(Rc); @@ -68,7 +67,7 @@ impl Router { pub(crate) fn get_resource(&self, idx: usize) -> &Resource { &self.0.patterns[idx] } - + /// Query for matched resource pub fn recognize(&self, req: &mut HttpRequest) -> Option { if self.0.prefix_len > req.path().len() { @@ -127,7 +126,6 @@ impl Clone for Router { } } - #[derive(Debug, Clone, PartialEq)] enum PatternElement { Str(String), @@ -140,26 +138,27 @@ enum PatternType { Dynamic(Regex, Vec), } +#[derive(Debug, Copy, Clone, PartialEq)] +/// Resource type +pub enum ResourceType { + /// Normal resource + Normal, + /// Resource for applicaiton default handler + Default, + /// External resource + External, + /// Unknown resource type + Unset, +} + /// Reslource type describes an entry in resources table #[derive(Clone)] pub struct Resource { tp: PatternType, + rtp: ResourceType, name: String, pattern: String, elements: Vec, - external: bool, -} - -impl Default for Resource { - fn default() -> Resource { - Resource { - tp: PatternType::Static("".to_owned()), - name: "".to_owned(), - pattern: "".to_owned(), - elements: Vec::new(), - external: false, - } - } } impl Resource { @@ -175,10 +174,21 @@ impl Resource { /// Panics if path pattern is wrong. pub fn external(name: &str, path: &str) -> Self { let mut resource = Resource::with_prefix(name, path, "/"); - resource.external = true; + resource.rtp = ResourceType::External; resource } + /// Unset resource type + pub(crate) fn unset() -> Resource { + Resource { + tp: PatternType::Static("".to_owned()), + rtp: ResourceType::Unset, + name: "".to_owned(), + pattern: "".to_owned(), + elements: Vec::new(), + } + } + /// Parse path pattern and create new `Resource` instance with custom prefix pub fn with_prefix(name: &str, path: &str, prefix: &str) -> Self { let (pattern, elements, is_dynamic) = Resource::parse(path, prefix); @@ -200,8 +210,8 @@ impl Resource { tp, elements, name: name.into(), + rtp: ResourceType::Normal, pattern: path.to_owned(), - external: false, } } @@ -210,6 +220,11 @@ impl Resource { &self.name } + /// Resource type + pub fn rtype(&self) -> ResourceType { + self.rtp + } + /// Path pattern of the resource pub fn pattern(&self) -> &str { &self.pattern @@ -253,7 +268,7 @@ impl Resource { I: AsRef, { let mut iter = elements.into_iter(); - let mut path = if !self.external { + let mut path = if self.rtp != ResourceType::External { format!("{}/", router.prefix()) } else { String::new()