1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-12-22 16:16:40 +00:00

Merge branch 'master' into add-header-for-juniper-example

This commit is contained in:
Nikolay Kim 2018-04-02 10:51:35 -07:00 committed by GitHub
commit 03d851680b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 62 deletions

View file

@ -1,4 +1,4 @@
# Actix web [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![Build status](https://ci.appveyor.com/api/projects/status/kkdb4yce7qhm5w85/branch/master?svg=true)](https://ci.appveyor.com/project/fafhrd91/actix-web-hdy9d/branch/master) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](http://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Actix web [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![Build status](https://ci.appveyor.com/api/projects/status/kkdb4yce7qhm5w85/branch/master?svg=true)](https://ci.appveyor.com/project/fafhrd91/actix-web-hdy9d/branch/master) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Actix web is a simple, pragmatic, extremely fast, web framework for Rust. Actix web is a simple, pragmatic, extremely fast, web framework for Rust.

View file

@ -1,6 +1,6 @@
use std::mem; use std::mem;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::UnsafeCell;
use std::collections::HashMap; use std::collections::HashMap;
use handler::Reply; use handler::Reply;
@ -9,7 +9,7 @@ use resource::{ResourceHandler};
use header::ContentEncoding; use header::ContentEncoding;
use handler::{Handler, RouteHandler, WrapHandler}; use handler::{Handler, RouteHandler, WrapHandler};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use pipeline::{Pipeline, PipelineHandler}; use pipeline::{Pipeline, PipelineHandler, HandlerType};
use middleware::Middleware; use middleware::Middleware;
use server::{HttpHandler, IntoHttpHandler, HttpHandlerTask, ServerSettings}; use server::{HttpHandler, IntoHttpHandler, HttpHandlerTask, ServerSettings};
@ -21,7 +21,7 @@ pub struct HttpApplication<S=()> {
state: Rc<S>, state: Rc<S>,
prefix: String, prefix: String,
router: Router, router: Router,
inner: Rc<RefCell<Inner<S>>>, inner: Rc<UnsafeCell<Inner<S>>>,
middlewares: Rc<Vec<Box<Middleware<S>>>>, middlewares: Rc<Vec<Box<Middleware<S>>>>,
} }
@ -29,7 +29,6 @@ pub(crate) struct Inner<S> {
prefix: usize, prefix: usize,
default: ResourceHandler<S>, default: ResourceHandler<S>,
encoding: ContentEncoding, encoding: ContentEncoding,
router: Router,
resources: Vec<ResourceHandler<S>>, resources: Vec<ResourceHandler<S>>,
handlers: Vec<(String, Box<RouteHandler<S>>)>, handlers: Vec<(String, Box<RouteHandler<S>>)>,
} }
@ -40,39 +39,60 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
self.encoding self.encoding
} }
fn handle(&mut self, mut req: HttpRequest<S>) -> Reply { fn handle(&mut self, req: HttpRequest<S>, htype: HandlerType) -> Reply {
if let Some(idx) = self.router.recognize(&mut req) { match htype {
self.resources[idx].handle(req.clone(), Some(&mut self.default)) 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<S: 'static> HttpApplication<S> {
#[inline]
fn as_ref(&self) -> &Inner<S> {
unsafe{&*self.inner.get()}
}
#[inline]
fn get_handler(&self, req: &mut HttpRequest<S>) -> HandlerType {
if let Some(idx) = self.router.recognize(req) {
HandlerType::Normal(idx)
} else { } 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 m = {
let path = &req.path()[self.prefix..]; let path = &req.path()[inner.prefix..];
path.starts_with(prefix) && ( path.starts_with(prefix) && (
path.len() == prefix.len() || path.len() == prefix.len() ||
path.split_at(prefix.len()).1.starts_with('/')) path.split_at(prefix.len()).1.starts_with('/'))
}; };
if m { if m {
let path: &'static str = unsafe { 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() { if path.is_empty() {
req.match_info_mut().add("tail", ""); req.match_info_mut().add("tail", "");
} else { } else {
req.match_info_mut().add("tail", path.split_at(1).1); 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<S: 'static> HttpApplication<S> {
#[cfg(test)] #[cfg(test)]
pub(crate) fn run(&mut self, req: HttpRequest<S>) -> Reply { pub(crate) fn run(&mut self, mut req: HttpRequest<S>) -> Reply {
self.inner.borrow_mut().handle(req) let tp = self.get_handler(&mut req);
unsafe{&mut *self.inner.get()}.handle(req, tp)
} }
#[cfg(test)] #[cfg(test)]
pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest<S> { pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest<S> {
req.with_state(Rc::clone(&self.state), self.router.clone()) req.with_state(Rc::clone(&self.state), self.router.clone())
@ -89,10 +109,10 @@ impl<S: 'static> HttpHandler for HttpApplication<S> {
path.split_at(self.prefix.len()).1.starts_with('/')) path.split_at(self.prefix.len()).1.starts_with('/'))
}; };
if m { 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 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, tp)))
Ok(Box::new(Pipeline::new(req, Rc::clone(&self.middlewares), inner)))
} else { } else {
Err(req) Err(req)
} }
@ -392,12 +412,11 @@ impl<S> App<S> where S: 'static {
let (router, resources) = Router::new(prefix, parts.settings, resources); let (router, resources) = Router::new(prefix, parts.settings, resources);
let inner = Rc::new(RefCell::new( let inner = Rc::new(UnsafeCell::new(
Inner { Inner {
prefix: prefix.len(), prefix: prefix.len(),
default: parts.default, default: parts.default,
encoding: parts.encoding, encoding: parts.encoding,
router: router.clone(),
handlers: parts.handlers, handlers: parts.handlers,
resources, resources,
} }

View file

@ -39,7 +39,13 @@ pub struct HttpInnerMessage {
pub addr: Option<SocketAddr>, pub addr: Option<SocketAddr>,
pub payload: Option<Payload>, pub payload: Option<Payload>,
pub info: Option<ConnectionInfo<'static>>, pub info: Option<ConnectionInfo<'static>>,
pub resource: i16, resource: RouterResource,
}
#[derive(Debug, Copy, Clone,PartialEq)]
enum RouterResource {
Notset,
Normal(u16),
} }
impl Default for HttpInnerMessage { impl Default for HttpInnerMessage {
@ -58,7 +64,7 @@ impl Default for HttpInnerMessage {
payload: None, payload: None,
extensions: Extensions::new(), extensions: Extensions::new(),
info: None, info: None,
resource: -1, resource: RouterResource::Notset,
} }
} }
} }
@ -95,12 +101,12 @@ impl HttpInnerMessage {
self.addr = None; self.addr = None;
self.info = None; self.info = None;
self.payload = None; self.payload = None;
self.resource = -1; self.resource = RouterResource::Notset;
} }
} }
lazy_static!{ lazy_static!{
static ref RESOURCE: Resource = Resource::default(); static ref RESOURCE: Resource = Resource::unset();
} }
@ -128,7 +134,7 @@ impl HttpRequest<()> {
addr: None, addr: None,
extensions: Extensions::new(), extensions: Extensions::new(),
info: None, info: None,
resource: -1, resource: RouterResource::Notset,
}), }),
None, None,
None, None,
@ -330,17 +336,16 @@ impl<S> HttpRequest<S> {
/// This method returns reference to matched `Resource` object. /// This method returns reference to matched `Resource` object.
#[inline] #[inline]
pub fn resource(&self) -> &Resource { pub fn resource(&self) -> &Resource {
let idx = self.as_ref().resource; if let Some(ref router) = self.2 {
if idx >= 0 { if let RouterResource::Normal(idx) = self.as_ref().resource {
if let Some(ref router) = self.2 {
return router.get_resource(idx as usize) return router.get_resource(idx as usize)
} }
} }
&*RESOURCE &*RESOURCE
} }
pub(crate) fn set_resource(&mut self, idx: usize) { pub(crate) fn set_resource(&mut self, res: usize) {
self.as_mut().resource = idx as i16; self.as_mut().resource = RouterResource::Normal(res as u16);
} }
/// Peer socket address /// Peer socket address

View file

@ -1,6 +1,6 @@
use std::{io, mem}; use std::{io, mem};
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::UnsafeCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use log::Level::Debug; use log::Level::Debug;
@ -18,11 +18,18 @@ use middleware::{Middleware, Finished, Started, Response};
use application::Inner; use application::Inner;
use server::{Writer, WriterState, HttpHandlerTask}; use server::{Writer, WriterState, HttpHandlerTask};
#[derive(Debug, Clone, Copy)]
pub(crate) enum HandlerType {
Normal(usize),
Handler(usize),
Default,
}
pub(crate) trait PipelineHandler<S> { pub(crate) trait PipelineHandler<S> {
fn encoding(&self) -> ContentEncoding; fn encoding(&self) -> ContentEncoding;
fn handle(&mut self, req: HttpRequest<S>) -> Reply; fn handle(&mut self, req: HttpRequest<S>, htype: HandlerType) -> Reply;
} }
pub(crate) struct Pipeline<S, H>(PipelineInfo<S>, PipelineState<S, H>); pub(crate) struct Pipeline<S, H>(PipelineInfo<S>, PipelineState<S, H>);
@ -105,7 +112,7 @@ impl<S: 'static, H: PipelineHandler<S>> Pipeline<S, H> {
pub fn new(req: HttpRequest<S>, pub fn new(req: HttpRequest<S>,
mws: Rc<Vec<Box<Middleware<S>>>>, mws: Rc<Vec<Box<Middleware<S>>>>,
handler: Rc<RefCell<H>>) -> Pipeline<S, H> handler: Rc<UnsafeCell<H>>, htype: HandlerType) -> Pipeline<S, H>
{ {
let mut info = PipelineInfo { let mut info = PipelineInfo {
req, mws, req, mws,
@ -113,9 +120,9 @@ impl<S: 'static, H: PipelineHandler<S>> Pipeline<S, H> {
error: None, error: None,
context: None, context: None,
disconnected: 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) Pipeline(info, state)
} }
@ -209,20 +216,23 @@ type Fut = Box<Future<Item=Option<HttpResponse>, Error=Error>>;
/// Middlewares start executor /// Middlewares start executor
struct StartMiddlewares<S, H> { struct StartMiddlewares<S, H> {
hnd: Rc<RefCell<H>>, hnd: Rc<UnsafeCell<H>>,
htype: HandlerType,
fut: Option<Fut>, fut: Option<Fut>,
_s: PhantomData<S>, _s: PhantomData<S>,
} }
impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> { impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
fn init(info: &mut PipelineInfo<S>, handler: Rc<RefCell<H>>) -> PipelineState<S, H> { fn init(info: &mut PipelineInfo<S>, hnd: Rc<UnsafeCell<H>>, htype: HandlerType)
-> PipelineState<S, H>
{
// execute middlewares, we need this stage because middlewares could be non-async // execute middlewares, we need this stage because middlewares could be non-async
// and we can move to next state immediately // and we can move to next state immediately
let len = info.mws.len() as u16; let len = info.mws.len() as u16;
loop { loop {
if info.count == len { 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) return WaitingResponse::init(info, reply)
} else { } else {
match info.mws[info.count as usize].start(&mut info.req) { match info.mws[info.count as usize].start(&mut info.req) {
@ -234,7 +244,7 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
match fut.poll() { match fut.poll() {
Ok(Async::NotReady) => Ok(Async::NotReady) =>
return PipelineState::Starting(StartMiddlewares { return PipelineState::Starting(StartMiddlewares {
hnd: handler, hnd, htype,
fut: Some(fut), fut: Some(fut),
_s: PhantomData}), _s: PhantomData}),
Ok(Async::Ready(resp)) => { Ok(Async::Ready(resp)) => {
@ -264,7 +274,8 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
return Some(RunMiddlewares::init(info, resp)); return Some(RunMiddlewares::init(info, resp));
} }
if info.count == len { 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)); return Some(WaitingResponse::init(info, reply));
} else { } else {
loop { loop {

View file

@ -12,7 +12,6 @@ use resource::ResourceHandler;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use server::ServerSettings; use server::ServerSettings;
/// Interface for application router. /// Interface for application router.
pub struct Router(Rc<Inner>); pub struct Router(Rc<Inner>);
@ -127,7 +126,6 @@ impl Clone for Router {
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
enum PatternElement { enum PatternElement {
Str(String), Str(String),
@ -140,26 +138,27 @@ enum PatternType {
Dynamic(Regex, Vec<String>), Dynamic(Regex, Vec<String>),
} }
#[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 /// Reslource type describes an entry in resources table
#[derive(Clone)] #[derive(Clone)]
pub struct Resource { pub struct Resource {
tp: PatternType, tp: PatternType,
rtp: ResourceType,
name: String, name: String,
pattern: String, pattern: String,
elements: Vec<PatternElement>, elements: Vec<PatternElement>,
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 { impl Resource {
@ -175,10 +174,21 @@ impl Resource {
/// Panics if path pattern is wrong. /// Panics if path pattern is wrong.
pub fn external(name: &str, path: &str) -> Self { pub fn external(name: &str, path: &str) -> Self {
let mut resource = Resource::with_prefix(name, path, "/"); let mut resource = Resource::with_prefix(name, path, "/");
resource.external = true; resource.rtp = ResourceType::External;
resource 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 /// Parse path pattern and create new `Resource` instance with custom prefix
pub fn with_prefix(name: &str, path: &str, prefix: &str) -> Self { pub fn with_prefix(name: &str, path: &str, prefix: &str) -> Self {
let (pattern, elements, is_dynamic) = Resource::parse(path, prefix); let (pattern, elements, is_dynamic) = Resource::parse(path, prefix);
@ -200,8 +210,8 @@ impl Resource {
tp, tp,
elements, elements,
name: name.into(), name: name.into(),
rtp: ResourceType::Normal,
pattern: path.to_owned(), pattern: path.to_owned(),
external: false,
} }
} }
@ -210,6 +220,11 @@ impl Resource {
&self.name &self.name
} }
/// Resource type
pub fn rtype(&self) -> ResourceType {
self.rtp
}
/// Path pattern of the resource /// Path pattern of the resource
pub fn pattern(&self) -> &str { pub fn pattern(&self) -> &str {
&self.pattern &self.pattern
@ -253,7 +268,7 @@ impl Resource {
I: AsRef<str>, I: AsRef<str>,
{ {
let mut iter = elements.into_iter(); let mut iter = elements.into_iter();
let mut path = if !self.external { let mut path = if self.rtp != ResourceType::External {
format!("{}/", router.prefix()) format!("{}/", router.prefix())
} else { } else {
String::new() String::new()