1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2025-01-02 05:18:44 +00:00

propogate app config with http request; add tests for url_for

This commit is contained in:
Nikolay Kim 2019-03-09 14:06:24 -08:00
parent c0ce7f0bae
commit 54678308d0
18 changed files with 397 additions and 215 deletions

View file

@ -70,9 +70,10 @@ actix-service = { git = "https://github.com/actix/actix-net.git" }
actix-utils = { git = "https://github.com/actix/actix-net.git" } actix-utils = { git = "https://github.com/actix/actix-net.git" }
actix-http = { git = "https://github.com/actix/actix-http.git" } actix-http = { git = "https://github.com/actix/actix-http.git" }
actix-router = { git = "https://github.com/actix/actix-net.git" } #actix-router = { git = "https://github.com/actix/actix-net.git" }
actix-server = { git = "https://github.com/actix/actix-net.git" } actix-server = { git = "https://github.com/actix/actix-net.git" }
actix-server-config = { git = "https://github.com/actix/actix-net.git" } actix-server-config = { git = "https://github.com/actix/actix-net.git" }
actix-router = { path = "../actix-net/router" }
bytes = "0.4" bytes = "0.4"
derive_more = "0.14" derive_more = "0.14"

View file

@ -17,7 +17,7 @@ use v_htmlescape::escape as escape_html_entity;
use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::error::{Error, ErrorInternalServerError};
use actix_service::{boxed::BoxedNewService, NewService, Service}; use actix_service::{boxed::BoxedNewService, NewService, Service};
use actix_web::dev::{self, AppConfig, HttpServiceFactory, ResourceDef, Url}; use actix_web::dev::{HttpServiceFactory, ResourceDef, ServiceConfig};
use actix_web::{ use actix_web::{
blocking, FromRequest, HttpRequest, HttpResponse, Responder, ServiceFromRequest, blocking, FromRequest, HttpRequest, HttpResponse, Responder, ServiceFromRequest,
ServiceRequest, ServiceResponse, ServiceRequest, ServiceResponse,
@ -305,7 +305,7 @@ where
P: 'static, P: 'static,
C: StaticFileConfig + 'static, C: StaticFileConfig + 'static,
{ {
fn register(self, config: &mut AppConfig<P>) { fn register(self, config: &mut ServiceConfig<P>) {
if self.default.borrow().is_none() { if self.default.borrow().is_none() {
*self.default.borrow_mut() = Some(config.default_service()); *self.default.borrow_mut() = Some(config.default_service());
} }
@ -314,11 +314,12 @@ where
} else { } else {
ResourceDef::prefix(&self.path) ResourceDef::prefix(&self.path)
}; };
config.register_service(rdef, None, self) config.register_service(rdef, None, self, None)
} }
} }
impl<P, C: StaticFileConfig + 'static> NewService<ServiceRequest<P>> for Files<P, C> { impl<P, C: StaticFileConfig + 'static> NewService for Files<P, C> {
type Request = ServiceRequest<P>;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = (); type Error = ();
type Service = FilesService<P, C>; type Service = FilesService<P, C>;
@ -350,7 +351,8 @@ pub struct FilesService<S, C = DefaultConfig> {
_cd_map: PhantomData<C>, _cd_map: PhantomData<C>,
} }
impl<P, C: StaticFileConfig> Service<ServiceRequest<P>> for FilesService<P, C> { impl<P, C: StaticFileConfig> Service for FilesService<P, C> {
type Request = ServiceRequest<P>;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = (); type Error = ();
type Future = FutureResult<Self::Response, Self::Error>; type Future = FutureResult<Self::Response, Self::Error>;
@ -362,7 +364,7 @@ impl<P, C: StaticFileConfig> Service<ServiceRequest<P>> for FilesService<P, C> {
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future { fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
let real_path = match PathBufWrp::get_pathbuf(req.match_info()) { let real_path = match PathBufWrp::get_pathbuf(req.match_info().path()) {
Ok(item) => item, Ok(item) => item,
Err(e) => return ok(ServiceResponse::from_err(e, req.clone())), Err(e) => return ok(ServiceResponse::from_err(e, req.clone())),
}; };
@ -409,13 +411,13 @@ impl<P, C: StaticFileConfig> Service<ServiceRequest<P>> for FilesService<P, C> {
} }
} }
#[derive(Debug)]
struct PathBufWrp(PathBuf); struct PathBufWrp(PathBuf);
impl PathBufWrp { impl PathBufWrp {
fn get_pathbuf(path: &dev::Path<Url>) -> Result<Self, UriSegmentError> { fn get_pathbuf(path: &str) -> Result<Self, UriSegmentError> {
let path_str = path.path();
let mut buf = PathBuf::new(); let mut buf = PathBuf::new();
for segment in path_str.split('/') { for segment in path.split('/') {
if segment == ".." { if segment == ".." {
buf.pop(); buf.pop();
} else if segment.starts_with('.') { } else if segment.starts_with('.') {
@ -447,13 +449,14 @@ impl<P> FromRequest<P> for PathBufWrp {
type Config = (); type Config = ();
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future { fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
PathBufWrp::get_pathbuf(req.match_info()) PathBufWrp::get_pathbuf(req.match_info().path())
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::fs; use std::fs;
use std::iter::FromIterator;
use std::ops::Add; use std::ops::Add;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
@ -1093,32 +1096,32 @@ mod tests {
#[test] #[test]
fn test_path_buf() { fn test_path_buf() {
assert_eq!( assert_eq!(
PathBuf::from_param("/test/.tt"), PathBufWrp::get_pathbuf("/test/.tt").map(|t| t.0),
Err(UriSegmentError::BadStart('.')) Err(UriSegmentError::BadStart('.'))
); );
assert_eq!( assert_eq!(
PathBuf::from_param("/test/*tt"), PathBufWrp::get_pathbuf("/test/*tt").map(|t| t.0),
Err(UriSegmentError::BadStart('*')) Err(UriSegmentError::BadStart('*'))
); );
assert_eq!( assert_eq!(
PathBuf::from_param("/test/tt:"), PathBufWrp::get_pathbuf("/test/tt:").map(|t| t.0),
Err(UriSegmentError::BadEnd(':')) Err(UriSegmentError::BadEnd(':'))
); );
assert_eq!( assert_eq!(
PathBuf::from_param("/test/tt<"), PathBufWrp::get_pathbuf("/test/tt<").map(|t| t.0),
Err(UriSegmentError::BadEnd('<')) Err(UriSegmentError::BadEnd('<'))
); );
assert_eq!( assert_eq!(
PathBuf::from_param("/test/tt>"), PathBufWrp::get_pathbuf("/test/tt>").map(|t| t.0),
Err(UriSegmentError::BadEnd('>')) Err(UriSegmentError::BadEnd('>'))
); );
assert_eq!( assert_eq!(
PathBuf::from_param("/seg1/seg2/"), PathBufWrp::get_pathbuf("/seg1/seg2/").unwrap().0,
Ok(PathBuf::from_iter(vec!["seg1", "seg2"])) PathBuf::from_iter(vec!["seg1", "seg2"])
); );
assert_eq!( assert_eq!(
PathBuf::from_param("/seg1/../seg2/"), PathBufWrp::get_pathbuf("/seg1/../seg2/").unwrap().0,
Ok(PathBuf::from_iter(vec!["seg2"])) PathBuf::from_iter(vec!["seg2"])
); );
} }
} }

View file

@ -304,8 +304,6 @@ impl<C: StaticFileConfig> Responder for NamedFile<C> {
type Future = Result<HttpResponse, Error>; type Future = Result<HttpResponse, Error>;
fn respond_to(self, req: &HttpRequest) -> Self::Future { fn respond_to(self, req: &HttpRequest) -> Self::Future {
println!("RESP: {:?}", req);
if self.status_code != StatusCode::OK { if self.status_code != StatusCode::OK {
let mut resp = HttpResponse::build(self.status_code); let mut resp = HttpResponse::build(self.status_code);
resp.set(header::ContentType(self.content_type.clone())) resp.set(header::ContentType(self.content_type.clone()))

View file

@ -255,12 +255,13 @@ impl CookieSession {
} }
} }
impl<S, P, B: 'static> Transform<S, ServiceRequest<P>> for CookieSession impl<S, P, B: 'static> Transform<S> for CookieSession
where where
S: Service<ServiceRequest<P>, Response = ServiceResponse<B>>, S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
S::Future: 'static, S::Future: 'static,
S::Error: 'static, S::Error: 'static,
{ {
type Request = ServiceRequest<P>;
type Response = ServiceResponse<B>; type Response = ServiceResponse<B>;
type Error = S::Error; type Error = S::Error;
type InitError = (); type InitError = ();
@ -281,12 +282,13 @@ pub struct CookieSessionMiddleware<S> {
inner: Rc<CookieSessionInner>, inner: Rc<CookieSessionInner>,
} }
impl<S, P, B: 'static> Service<ServiceRequest<P>> for CookieSessionMiddleware<S> impl<S, P, B: 'static> Service for CookieSessionMiddleware<S>
where where
S: Service<ServiceRequest<P>, Response = ServiceResponse<B>>, S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
S::Future: 'static, S::Future: 'static,
S::Error: 'static, S::Error: 'static,
{ {
type Request = ServiceRequest<P>;
type Response = ServiceResponse<B>; type Response = ServiceResponse<B>;
type Error = S::Error; type Error = S::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>; type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

View file

@ -31,7 +31,7 @@ pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
struct #name; struct #name;
impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name { impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name {
fn register(self, config: &mut actix_web::dev::AppConfig<P>) { fn register(self, config: &mut actix_web::dev::ServiceConfig<P>) {
#ast #ast
actix_web::dev::HttpServiceFactory::register( actix_web::dev::HttpServiceFactory::register(
actix_web::Resource::new(#path) actix_web::Resource::new(#path)
@ -68,7 +68,7 @@ pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
struct #name; struct #name;
impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name { impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name {
fn register(self, config: &mut actix_web::dev::AppConfig<P>) { fn register(self, config: &mut actix_web::dev::ServiceConfig<P>) {
#ast #ast
actix_web::dev::HttpServiceFactory::register( actix_web::dev::HttpServiceFactory::register(
actix_web::Resource::new(#path) actix_web::Resource::new(#path)
@ -105,7 +105,7 @@ pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
struct #name; struct #name;
impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name { impl<P: 'static> actix_web::dev::HttpServiceFactory<P> for #name {
fn register(self, config: &mut actix_web::dev::AppConfig<P>) { fn register(self, config: &mut actix_web::dev::ServiceConfig<P>) {
#ast #ast
actix_web::dev::HttpServiceFactory::register( actix_web::dev::HttpServiceFactory::register(
actix_web::Resource::new(#path) actix_web::Resource::new(#path)

View file

@ -3,7 +3,8 @@ use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use actix_http::body::{Body, MessageBody}; use actix_http::body::{Body, MessageBody};
use actix_http::{Extensions, PayloadStream}; use actix_http::PayloadStream;
use actix_router::ResourceDef;
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::boxed::{self, BoxedNewService}; use actix_service::boxed::{self, BoxedNewService};
use actix_service::{ use actix_service::{
@ -12,6 +13,7 @@ use actix_service::{
use futures::IntoFuture; use futures::IntoFuture;
use crate::app_service::{AppChain, AppEntry, AppInit, AppRouting, AppRoutingFactory}; use crate::app_service::{AppChain, AppEntry, AppInit, AppRouting, AppRoutingFactory};
use crate::config::{AppConfig, AppConfigInner};
use crate::resource::Resource; use crate::resource::Resource;
use crate::route::Route; use crate::route::Route;
use crate::service::{ use crate::service::{
@ -29,9 +31,8 @@ where
T: NewService<Request = ServiceRequest, Response = ServiceRequest<P>>, T: NewService<Request = ServiceRequest, Response = ServiceRequest<P>>,
{ {
chain: T, chain: T,
extensions: Extensions,
state: Vec<Box<StateFactory>>, state: Vec<Box<StateFactory>>,
host: String, config: AppConfigInner,
_t: PhantomData<(P,)>, _t: PhantomData<(P,)>,
} }
@ -41,9 +42,8 @@ impl App<PayloadStream, AppChain> {
pub fn new() -> Self { pub fn new() -> Self {
App { App {
chain: AppChain, chain: AppChain,
extensions: Extensions::new(),
state: Vec::new(), state: Vec::new(),
host: "localhost:8080".to_string(), config: AppConfigInner::default(),
_t: PhantomData, _t: PhantomData,
} }
} }
@ -141,8 +141,8 @@ where
services: Vec::new(), services: Vec::new(),
default: None, default: None,
factory_ref: fref, factory_ref: fref,
extensions: self.extensions, config: self.config,
host: self.host, external: Vec::new(),
_t: PhantomData, _t: PhantomData,
} }
} }
@ -174,8 +174,7 @@ where
App { App {
chain, chain,
state: self.state, state: self.state,
extensions: self.extensions, config: self.config,
host: self.host,
_t: PhantomData, _t: PhantomData,
} }
} }
@ -223,10 +222,10 @@ where
default: None, default: None,
endpoint: AppEntry::new(fref.clone()), endpoint: AppEntry::new(fref.clone()),
factory_ref: fref, factory_ref: fref,
extensions: self.extensions,
state: self.state, state: self.state,
host: self.host, config: self.config,
services: vec![Box::new(ServiceFactoryWrapper::new(service))], services: vec![Box::new(ServiceFactoryWrapper::new(service))],
external: Vec::new(),
_t: PhantomData, _t: PhantomData,
} }
} }
@ -239,7 +238,7 @@ where
/// ///
/// By default host name is set to a "localhost" value. /// By default host name is set to a "localhost" value.
pub fn hostname(mut self, val: &str) -> Self { pub fn hostname(mut self, val: &str) -> Self {
self.host = val.to_owned(); self.config.host = val.to_owned();
self self
} }
} }
@ -252,9 +251,9 @@ pub struct AppRouter<C, P, B, T> {
services: Vec<Box<ServiceFactory<P>>>, services: Vec<Box<ServiceFactory<P>>>,
default: Option<Rc<HttpNewService<P>>>, default: Option<Rc<HttpNewService<P>>>,
factory_ref: Rc<RefCell<Option<AppRoutingFactory<P>>>>, factory_ref: Rc<RefCell<Option<AppRoutingFactory<P>>>>,
extensions: Extensions,
state: Vec<Box<StateFactory>>, state: Vec<Box<StateFactory>>,
host: String, config: AppConfigInner,
external: Vec<ResourceDef>,
_t: PhantomData<(P, B)>, _t: PhantomData<(P, B)>,
} }
@ -348,8 +347,8 @@ where
services: self.services, services: self.services,
default: self.default, default: self.default,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
extensions: self.extensions, config: self.config,
host: self.host, external: self.external,
_t: PhantomData, _t: PhantomData,
} }
} }
@ -382,33 +381,30 @@ where
/// and are never considered for matching at request time. Calls to /// and are never considered for matching at request time. Calls to
/// `HttpRequest::url_for()` will work as expected. /// `HttpRequest::url_for()` will work as expected.
/// ///
/// ```rust,ignore /// ```rust
/// # extern crate actix_web; /// use actix_web::{web, App, HttpRequest, HttpResponse, Result};
/// use actix_web::{App, HttpRequest, HttpResponse, Result};
/// ///
/// fn index(req: &HttpRequest) -> Result<HttpResponse> { /// fn index(req: HttpRequest) -> Result<HttpResponse> {
/// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?; /// let url = req.url_for("youtube", &["asdlkjqme"])?;
/// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0"); /// assert_eq!(url.as_str(), "https://youtube.com/watch/asdlkjqme");
/// Ok(HttpResponse::Ok().into()) /// Ok(HttpResponse::Ok().into())
/// } /// }
/// ///
/// fn main() { /// fn main() {
/// let app = App::new() /// let app = App::new()
/// .resource("/index.html", |r| r.get().f(index)) /// .service(web::resource("/index.html").route(
/// .external_resource("youtube", "https://youtube.com/watch/{video_id}") /// web::get().to(index)))
/// .finish(); /// .external_resource("youtube", "https://youtube.com/watch/{video_id}");
/// } /// }
/// ``` /// ```
pub fn external_resource<N, U>(self, _name: N, _url: U) -> Self pub fn external_resource<N, U>(mut self, name: N, url: U) -> Self
where where
N: AsRef<str>, N: AsRef<str>,
U: AsRef<str>, U: AsRef<str>,
{ {
// self.parts let mut rdef = ResourceDef::new(url.as_ref());
// .as_mut() *rdef.name_mut() = name.as_ref().to_string();
// .expect("Use after finish") self.external.push(rdef);
// .router
// .register_external(name.as_ref(), ResourceDef::external(url.as_ref()));
self self
} }
} }
@ -435,9 +431,10 @@ where
state: self.state, state: self.state,
endpoint: self.endpoint, endpoint: self.endpoint,
services: RefCell::new(self.services), services: RefCell::new(self.services),
external: RefCell::new(self.external),
default: self.default, default: self.default,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
extensions: Rc::new(RefCell::new(Rc::new(self.extensions))), config: RefCell::new(AppConfig(Rc::new(self.config))),
} }
} }
} }

View file

@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use actix_http::{Extensions, Request, Response}; use actix_http::{Request, Response};
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::boxed::{self, BoxedNewService, BoxedService};
@ -10,7 +10,7 @@ use actix_service::{fn_service, AndThen, NewService, Service, ServiceExt};
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use crate::config::AppConfig; use crate::config::{AppConfig, ServiceConfig};
use crate::guard::Guard; use crate::guard::Guard;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse}; use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse};
@ -36,10 +36,11 @@ where
pub(crate) chain: C, pub(crate) chain: C,
pub(crate) endpoint: T, pub(crate) endpoint: T,
pub(crate) state: Vec<Box<StateFactory>>, pub(crate) state: Vec<Box<StateFactory>>,
pub(crate) extensions: Rc<RefCell<Rc<Extensions>>>, pub(crate) config: RefCell<AppConfig>,
pub(crate) services: RefCell<Vec<Box<ServiceFactory<P>>>>, pub(crate) services: RefCell<Vec<Box<ServiceFactory<P>>>>,
pub(crate) default: Option<Rc<HttpNewService<P>>>, pub(crate) default: Option<Rc<HttpNewService<P>>>,
pub(crate) factory_ref: Rc<RefCell<Option<AppRoutingFactory<P>>>>, pub(crate) factory_ref: Rc<RefCell<Option<AppRoutingFactory<P>>>>,
pub(crate) external: RefCell<Vec<ResourceDef>>,
} }
impl<C, T, P: 'static, B> NewService<ServerConfig> for AppInit<C, T, P, B> impl<C, T, P: 'static, B> NewService<ServerConfig> for AppInit<C, T, P, B>
@ -64,7 +65,7 @@ where
type Service = AndThen<AppInitService<C::Service, P>, T::Service>; type Service = AndThen<AppInitService<C::Service, P>, T::Service>;
type Future = AppInitResult<C, T, P, B>; type Future = AppInitResult<C, T, P, B>;
fn new_service(&self, _: &ServerConfig) -> Self::Future { fn new_service(&self, cfg: &ServerConfig) -> Self::Future {
// update resource default service // update resource default service
let default = self.default.clone().unwrap_or_else(|| { let default = self.default.clone().unwrap_or_else(|| {
Rc::new(boxed::new_service(fn_service(|req: ServiceRequest<P>| { Rc::new(boxed::new_service(fn_service(|req: ServiceRequest<P>| {
@ -72,12 +73,15 @@ where
}))) })))
}); });
let mut config = AppConfig::new( {
"127.0.0.1:8080".parse().unwrap(), let mut c = self.config.borrow_mut();
"localhost:8080".to_owned(), let loc_cfg = Rc::get_mut(&mut c.0).unwrap();
false, loc_cfg.secure = cfg.secure();
default.clone(), loc_cfg.addr = cfg.local_addr();
); }
let mut config =
ServiceConfig::new(self.config.borrow().clone(), default.clone());
// register services // register services
std::mem::replace(&mut *self.services.borrow_mut(), Vec::new()) std::mem::replace(&mut *self.services.borrow_mut(), Vec::new())
@ -101,6 +105,11 @@ where
), ),
}); });
// external resources
for mut rdef in std::mem::replace(&mut *self.external.borrow_mut(), Vec::new()) {
rmap.add(&mut rdef, None);
}
// complete ResourceMap tree creation // complete ResourceMap tree creation
let rmap = Rc::new(rmap); let rmap = Rc::new(rmap);
rmap.finish(rmap.clone()); rmap.finish(rmap.clone());
@ -111,7 +120,7 @@ where
endpoint: None, endpoint: None,
endpoint_fut: self.endpoint.new_service(&()), endpoint_fut: self.endpoint.new_service(&()),
state: self.state.iter().map(|s| s.construct()).collect(), state: self.state.iter().map(|s| s.construct()).collect(),
extensions: self.extensions.clone(), config: self.config.borrow().clone(),
rmap, rmap,
_t: PhantomData, _t: PhantomData,
} }
@ -129,7 +138,7 @@ where
endpoint_fut: T::Future, endpoint_fut: T::Future,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
state: Vec<Box<StateFactoryResult>>, state: Vec<Box<StateFactoryResult>>,
extensions: Rc<RefCell<Rc<Extensions>>>, config: AppConfig,
_t: PhantomData<(P, B)>, _t: PhantomData<(P, B)>,
} }
@ -152,20 +161,14 @@ where
type Error = C::InitError; type Error = C::InitError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(extensions) = Rc::get_mut(&mut *self.extensions.borrow_mut()) { let mut idx = 0;
let mut idx = 0; let mut extensions = self.config.0.extensions.borrow_mut();
while idx < self.state.len() { while idx < self.state.len() {
if let Async::Ready(_) = self.state[idx].poll_result(extensions)? { if let Async::Ready(_) = self.state[idx].poll_result(&mut extensions)? {
self.state.remove(idx); self.state.remove(idx);
} else { } else {
idx += 1; idx += 1;
}
} }
if !self.state.is_empty() {
return Ok(Async::NotReady);
}
} else {
log::warn!("Multiple copies of app extensions exists");
} }
if self.chain.is_none() { if self.chain.is_none() {
@ -185,7 +188,7 @@ where
AppInitService { AppInitService {
chain: self.chain.take().unwrap(), chain: self.chain.take().unwrap(),
rmap: self.rmap.clone(), rmap: self.rmap.clone(),
extensions: self.extensions.borrow().clone(), config: self.config.clone(),
} }
.and_then(self.endpoint.take().unwrap()), .and_then(self.endpoint.take().unwrap()),
)) ))
@ -202,7 +205,7 @@ where
{ {
chain: C, chain: C,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
extensions: Rc<Extensions>, config: AppConfig,
} }
impl<C, P> Service for AppInitService<C, P> impl<C, P> Service for AppInitService<C, P>
@ -223,7 +226,7 @@ where
Path::new(Url::new(req.uri().clone())), Path::new(Url::new(req.uri().clone())),
req, req,
self.rmap.clone(), self.rmap.clone(),
self.extensions.clone(), self.config.clone(),
); );
self.chain.call(req) self.chain.call(req)
} }

View file

@ -1,6 +1,8 @@
use std::cell::{Ref, RefCell};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::rc::Rc; use std::rc::Rc;
use actix_http::Extensions;
use actix_router::ResourceDef; use actix_router::ResourceDef;
use actix_service::{boxed, IntoNewService, NewService}; use actix_service::{boxed, IntoNewService, NewService};
@ -13,10 +15,8 @@ type HttpNewService<P> =
boxed::BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>; boxed::BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
/// Application configuration /// Application configuration
pub struct AppConfig<P> { pub struct ServiceConfig<P> {
addr: SocketAddr, config: AppConfig,
secure: bool,
host: String,
root: bool, root: bool,
default: Rc<HttpNewService<P>>, default: Rc<HttpNewService<P>>,
services: Vec<( services: Vec<(
@ -27,18 +27,11 @@ pub struct AppConfig<P> {
)>, )>,
} }
impl<P: 'static> AppConfig<P> { impl<P: 'static> ServiceConfig<P> {
/// Crate server settings instance /// Crate server settings instance
pub(crate) fn new( pub(crate) fn new(config: AppConfig, default: Rc<HttpNewService<P>>) -> Self {
addr: SocketAddr, ServiceConfig {
host: String, config,
secure: bool,
default: Rc<HttpNewService<P>>,
) -> Self {
AppConfig {
addr,
secure,
host,
default, default,
root: true, root: true,
services: Vec::new(), services: Vec::new(),
@ -62,31 +55,20 @@ impl<P: 'static> AppConfig<P> {
} }
pub(crate) fn clone_config(&self) -> Self { pub(crate) fn clone_config(&self) -> Self {
AppConfig { ServiceConfig {
addr: self.addr, config: self.config.clone(),
secure: self.secure,
host: self.host.clone(),
default: self.default.clone(), default: self.default.clone(),
services: Vec::new(), services: Vec::new(),
root: false, root: false,
} }
} }
/// Returns the socket address of the local half of this TCP connection /// Service configuration
pub fn local_addr(&self) -> SocketAddr { pub fn config(&self) -> &AppConfig {
self.addr &self.config
}
/// Returns true if connection is secure(https)
pub fn secure(&self) -> bool {
self.secure
}
/// Returns host header value
pub fn host(&self) -> &str {
&self.host
} }
/// Default resource
pub fn default_service(&self) -> Rc<HttpNewService<P>> { pub fn default_service(&self) -> Rc<HttpNewService<P>> {
self.default.clone() self.default.clone()
} }
@ -114,3 +96,63 @@ impl<P: 'static> AppConfig<P> {
)); ));
} }
} }
#[derive(Clone)]
pub struct AppConfig(pub(crate) Rc<AppConfigInner>);
impl AppConfig {
pub(crate) fn new(inner: AppConfigInner) -> Self {
AppConfig(Rc::new(inner))
}
/// Set server host name.
///
/// Host name is used by application router aa a hostname for url
/// generation. Check [ConnectionInfo](./dev/struct.ConnectionInfo.
/// html#method.host) documentation for more information.
///
/// By default host name is set to a "localhost" value.
pub fn host(&self) -> &str {
&self.0.host
}
/// Returns true if connection is secure(https)
pub fn secure(&self) -> bool {
self.0.secure
}
/// Returns the socket address of the local half of this TCP connection
pub fn local_addr(&self) -> SocketAddr {
self.0.addr
}
/// Resource map
pub fn rmap(&self) -> &ResourceMap {
&self.0.rmap
}
/// Application extensions
pub fn extensions(&self) -> Ref<Extensions> {
self.0.extensions.borrow()
}
}
pub(crate) struct AppConfigInner {
pub(crate) secure: bool,
pub(crate) host: String,
pub(crate) addr: SocketAddr,
pub(crate) rmap: ResourceMap,
pub(crate) extensions: RefCell<Extensions>,
}
impl Default for AppConfigInner {
fn default() -> AppConfigInner {
AppConfigInner {
secure: false,
addr: "127.0.0.1:8080".parse().unwrap(),
host: "localhost:8080".to_owned(),
rmap: ResourceMap::new(ResourceDef::new("")),
extensions: RefCell::new(Extensions::new()),
}
}
}

View file

@ -1,3 +1,5 @@
//! Error and Result module
pub use actix_http::error::*; pub use actix_http::error::*;
use derive_more::{Display, From}; use derive_more::{Display, From};
use url::ParseError as UrlParseError; use url::ParseError as UrlParseError;

View file

@ -1,19 +1,14 @@
use std::cell::Ref; use std::cell::Ref;
use actix_http::http::header::{self, HeaderName}; use crate::dev::{AppConfig, RequestHead};
use actix_http::RequestHead; use crate::http::header::{self, HeaderName};
const X_FORWARDED_FOR: &[u8] = b"x-forwarded-for"; const X_FORWARDED_FOR: &[u8] = b"x-forwarded-for";
const X_FORWARDED_HOST: &[u8] = b"x-forwarded-host"; const X_FORWARDED_HOST: &[u8] = b"x-forwarded-host";
const X_FORWARDED_PROTO: &[u8] = b"x-forwarded-proto"; const X_FORWARDED_PROTO: &[u8] = b"x-forwarded-proto";
pub enum ConnectionInfoError {
UnknownHost,
UnknownScheme,
}
/// `HttpRequest` connection information /// `HttpRequest` connection information
#[derive(Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ConnectionInfo { pub struct ConnectionInfo {
scheme: String, scheme: String,
host: String, host: String,
@ -23,19 +18,19 @@ pub struct ConnectionInfo {
impl ConnectionInfo { impl ConnectionInfo {
/// Create *ConnectionInfo* instance for a request. /// Create *ConnectionInfo* instance for a request.
pub fn get(req: &RequestHead) -> Ref<Self> { pub fn get<'a>(req: &'a RequestHead, cfg: &AppConfig) -> Ref<'a, Self> {
if !req.extensions().contains::<ConnectionInfo>() { if !req.extensions().contains::<ConnectionInfo>() {
req.extensions_mut().insert(ConnectionInfo::new(req)); req.extensions_mut().insert(ConnectionInfo::new(req, cfg));
} }
Ref::map(req.extensions(), |e| e.get().unwrap()) Ref::map(req.extensions(), |e| e.get().unwrap())
} }
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
fn new(req: &RequestHead) -> ConnectionInfo { fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo {
let mut host = None; let mut host = None;
let mut scheme = None; let mut scheme = None;
let mut remote = None; let mut remote = None;
let mut peer = None; let peer = None;
// load forwarded header // load forwarded header
for hdr in req.headers.get_all(header::FORWARDED) { for hdr in req.headers.get_all(header::FORWARDED) {
@ -82,7 +77,7 @@ impl ConnectionInfo {
} }
if scheme.is_none() { if scheme.is_none() {
scheme = req.uri.scheme_part().map(|a| a.as_str()); scheme = req.uri.scheme_part().map(|a| a.as_str());
if scheme.is_none() && req.server_settings().secure() { if scheme.is_none() && cfg.secure() {
scheme = Some("https") scheme = Some("https")
} }
} }
@ -105,7 +100,7 @@ impl ConnectionInfo {
if host.is_none() { if host.is_none() {
host = req.uri.authority_part().map(|a| a.as_str()); host = req.uri.authority_part().map(|a| a.as_str());
if host.is_none() { if host.is_none() {
host = Some(req.server_settings().host()); host = Some(cfg.host());
} }
} }
} }
@ -121,10 +116,10 @@ impl ConnectionInfo {
remote = h.split(',').next().map(|v| v.trim()); remote = h.split(',').next().map(|v| v.trim());
} }
} }
if remote.is_none() { // if remote.is_none() {
// get peeraddr from socketaddr // get peeraddr from socketaddr
peer = req.peer_addr().map(|addr| format!("{}", addr)); // peer = req.peer_addr().map(|addr| format!("{}", addr));
} // }
} }
ConnectionInfo { ConnectionInfo {
@ -186,9 +181,8 @@ mod tests {
#[test] #[test]
fn test_forwarded() { fn test_forwarded() {
let req = TestRequest::default().request(); let req = TestRequest::default().to_http_request();
let mut info = ConnectionInfo::default(); let info = req.connection_info();
info.update(&req);
assert_eq!(info.scheme(), "http"); assert_eq!(info.scheme(), "http");
assert_eq!(info.host(), "localhost:8080"); assert_eq!(info.host(), "localhost:8080");
@ -197,44 +191,39 @@ mod tests {
header::FORWARDED, header::FORWARDED,
"for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org", "for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org",
) )
.request(); .to_http_request();
let mut info = ConnectionInfo::default(); let info = req.connection_info();
info.update(&req);
assert_eq!(info.scheme(), "https"); assert_eq!(info.scheme(), "https");
assert_eq!(info.host(), "rust-lang.org"); assert_eq!(info.host(), "rust-lang.org");
assert_eq!(info.remote(), Some("192.0.2.60")); assert_eq!(info.remote(), Some("192.0.2.60"));
let req = TestRequest::default() let req = TestRequest::default()
.header(header::HOST, "rust-lang.org") .header(header::HOST, "rust-lang.org")
.request(); .to_http_request();
let mut info = ConnectionInfo::default(); let info = req.connection_info();
info.update(&req);
assert_eq!(info.scheme(), "http"); assert_eq!(info.scheme(), "http");
assert_eq!(info.host(), "rust-lang.org"); assert_eq!(info.host(), "rust-lang.org");
assert_eq!(info.remote(), None); assert_eq!(info.remote(), None);
let req = TestRequest::default() let req = TestRequest::default()
.header(X_FORWARDED_FOR, "192.0.2.60") .header(X_FORWARDED_FOR, "192.0.2.60")
.request(); .to_http_request();
let mut info = ConnectionInfo::default(); let info = req.connection_info();
info.update(&req);
assert_eq!(info.remote(), Some("192.0.2.60")); assert_eq!(info.remote(), Some("192.0.2.60"));
let req = TestRequest::default() let req = TestRequest::default()
.header(X_FORWARDED_HOST, "192.0.2.60") .header(X_FORWARDED_HOST, "192.0.2.60")
.request(); .to_http_request();
let mut info = ConnectionInfo::default(); let info = req.connection_info();
info.update(&req);
assert_eq!(info.host(), "192.0.2.60"); assert_eq!(info.host(), "192.0.2.60");
assert_eq!(info.remote(), None); assert_eq!(info.remote(), None);
let req = TestRequest::default() let req = TestRequest::default()
.header(X_FORWARDED_PROTO, "https") .header(X_FORWARDED_PROTO, "https")
.request(); .to_http_request();
let mut info = ConnectionInfo::default(); let info = req.connection_info();
info.update(&req);
assert_eq!(info.scheme(), "https"); assert_eq!(info.scheme(), "https");
} }
} }

View file

@ -2,13 +2,13 @@
mod app; mod app;
mod app_service; mod app_service;
mod extract;
mod handler;
// mod info;
pub mod blocking; pub mod blocking;
mod config; mod config;
pub mod error; pub mod error;
mod extract;
pub mod guard; pub mod guard;
mod handler;
mod info;
pub mod middleware; pub mod middleware;
mod request; mod request;
mod resource; mod resource;
@ -54,7 +54,9 @@ pub mod dev {
//! ``` //! ```
pub use crate::app::AppRouter; pub use crate::app::AppRouter;
pub use crate::config::AppConfig; pub use crate::config::{AppConfig, ServiceConfig};
pub use crate::info::ConnectionInfo;
pub use crate::rmap::ResourceMap;
pub use crate::service::HttpServiceFactory; pub use crate::service::HttpServiceFactory;
pub use actix_http::body::{Body, BodyLength, MessageBody, ResponseBody}; pub use actix_http::body::{Body, BodyLength, MessageBody, ResponseBody};
@ -62,7 +64,7 @@ pub mod dev {
pub use actix_http::{ pub use actix_http::{
Extensions, Payload, PayloadStream, RequestHead, ResponseHead, Extensions, Payload, PayloadStream, RequestHead, ResponseHead,
}; };
pub use actix_router::{Path, ResourceDef, Url}; pub use actix_router::{Path, ResourceDef, ResourcePath, Url};
pub(crate) fn insert_slash(path: &str) -> String { pub(crate) fn insert_slash(path: &str) -> String {
let mut path = path.to_owned(); let mut path = path.to_owned();

View file

@ -7,8 +7,10 @@ use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
use actix_router::{Path, Url}; use actix_router::{Path, Url};
use crate::config::AppConfig;
use crate::error::UrlGenerationError; use crate::error::UrlGenerationError;
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::info::ConnectionInfo;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::ServiceFromRequest; use crate::service::ServiceFromRequest;
@ -18,7 +20,7 @@ pub struct HttpRequest {
pub(crate) head: Message<RequestHead>, pub(crate) head: Message<RequestHead>,
pub(crate) path: Path<Url>, pub(crate) path: Path<Url>,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
extensions: Rc<Extensions>, config: AppConfig,
} }
impl HttpRequest { impl HttpRequest {
@ -27,13 +29,13 @@ impl HttpRequest {
head: Message<RequestHead>, head: Message<RequestHead>,
path: Path<Url>, path: Path<Url>,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
extensions: Rc<Extensions>, config: AppConfig,
) -> HttpRequest { ) -> HttpRequest {
HttpRequest { HttpRequest {
head, head,
path, path,
rmap, rmap,
extensions, config,
} }
} }
} }
@ -92,17 +94,17 @@ impl HttpRequest {
&self.path &self.path
} }
/// Application extensions /// App config
#[inline] #[inline]
pub fn app_extensions(&self) -> &Extensions { pub fn config(&self) -> &AppConfig {
&self.extensions &self.config
} }
/// Generate url for named resource /// Generate url for named resource
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// # use actix_web::{App, HttpRequest, HttpResponse, http}; /// # use actix_web::{web, App, HttpRequest, HttpResponse};
/// # /// #
/// fn index(req: HttpRequest) -> HttpResponse { /// fn index(req: HttpRequest) -> HttpResponse {
/// let url = req.url_for("foo", &["1", "2", "3"]); // <- generate url for "foo" resource /// let url = req.url_for("foo", &["1", "2", "3"]); // <- generate url for "foo" resource
@ -111,11 +113,10 @@ impl HttpRequest {
/// ///
/// fn main() { /// fn main() {
/// let app = App::new() /// let app = App::new()
/// .resource("/test/{one}/{two}/{three}", |r| { /// .service(web::resource("/test/{one}/{two}/{three}")
/// r.name("foo"); // <- set resource name, then it could be used in `url_for` /// .name("foo") // <- set resource name, then it could be used in `url_for`
/// r.method(http::Method::GET).f(|_| HttpResponse::Ok()); /// .route(web::get().to(|| HttpResponse::Ok()))
/// }) /// );
/// .finish();
/// } /// }
/// ``` /// ```
pub fn url_for<U, I>( pub fn url_for<U, I>(
@ -139,11 +140,11 @@ impl HttpRequest {
self.url_for(name, &NO_PARAMS) self.url_for(name, &NO_PARAMS)
} }
// /// Get *ConnectionInfo* for the correct request. /// Get *ConnectionInfo* for the current request.
// #[inline] #[inline]
// pub fn connection_info(&self) -> Ref<ConnectionInfo> { pub fn connection_info(&self) -> Ref<ConnectionInfo> {
// ConnectionInfo::get(&*self) ConnectionInfo::get(&*self, &*self.config())
// } }
} }
impl Deref for HttpRequest { impl Deref for HttpRequest {
@ -234,3 +235,124 @@ impl fmt::Debug for HttpRequest {
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::dev::{ResourceDef, ResourceMap};
use crate::http::header;
use crate::test::TestRequest;
#[test]
fn test_debug() {
let req =
TestRequest::with_header("content-type", "text/plain").to_http_request();
let dbg = format!("{:?}", req);
assert!(dbg.contains("HttpRequest"));
}
#[test]
fn test_no_request_cookies() {
let req = TestRequest::default().to_http_request();
assert!(req.cookies().unwrap().is_empty());
}
#[test]
fn test_request_cookies() {
let req = TestRequest::default()
.header(header::COOKIE, "cookie1=value1")
.header(header::COOKIE, "cookie2=value2")
.to_http_request();
{
let cookies = req.cookies().unwrap();
assert_eq!(cookies.len(), 2);
assert_eq!(cookies[0].name(), "cookie1");
assert_eq!(cookies[0].value(), "value1");
assert_eq!(cookies[1].name(), "cookie2");
assert_eq!(cookies[1].value(), "value2");
}
let cookie = req.cookie("cookie1");
assert!(cookie.is_some());
let cookie = cookie.unwrap();
assert_eq!(cookie.name(), "cookie1");
assert_eq!(cookie.value(), "value1");
let cookie = req.cookie("cookie-unknown");
assert!(cookie.is_none());
}
#[test]
fn test_request_query() {
let req = TestRequest::with_uri("/?id=test").to_http_request();
assert_eq!(req.query_string(), "id=test");
}
#[test]
fn test_url_for() {
let mut res = ResourceDef::new("/user/{name}.{ext}");
*res.name_mut() = "index".to_string();
let mut rmap = ResourceMap::new(ResourceDef::new(""));
rmap.add(&mut res, None);
assert!(rmap.has_resource("/user/test.html"));
assert!(!rmap.has_resource("/test/unknown"));
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
.rmap(rmap)
.to_http_request();
assert_eq!(
req.url_for("unknown", &["test"]),
Err(UrlGenerationError::ResourceNotFound)
);
assert_eq!(
req.url_for("index", &["test"]),
Err(UrlGenerationError::NotEnoughElements)
);
let url = req.url_for("index", &["test", "html"]);
assert_eq!(
url.ok().unwrap().as_str(),
"http://www.rust-lang.org/user/test.html"
);
}
#[test]
fn test_url_for_static() {
let mut rdef = ResourceDef::new("/index.html");
*rdef.name_mut() = "index".to_string();
let mut rmap = ResourceMap::new(ResourceDef::new(""));
rmap.add(&mut rdef, None);
assert!(rmap.has_resource("/index.html"));
let req = TestRequest::with_uri("/test")
.header(header::HOST, "www.rust-lang.org")
.rmap(rmap)
.to_http_request();
let url = req.url_for_static("index");
assert_eq!(
url.ok().unwrap().as_str(),
"http://www.rust-lang.org/index.html"
);
}
#[test]
fn test_url_for_external() {
let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");
*rdef.name_mut() = "youtube".to_string();
let mut rmap = ResourceMap::new(ResourceDef::new(""));
rmap.add(&mut rdef, None);
assert!(rmap.has_resource("https://youtube.com/watch/unknown"));
let req = TestRequest::default().rmap(rmap).to_http_request();
let url = req.url_for("youtube", &["oHg5SJYRHA0"]);
assert_eq!(
url.ok().unwrap().as_str(),
"https://youtube.com/watch/oHg5SJYRHA0"
);
}
}

View file

@ -9,7 +9,7 @@ use actix_service::{
use futures::future::{ok, Either, FutureResult}; use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, IntoFuture, Poll}; use futures::{Async, Future, IntoFuture, Poll};
use crate::dev::{insert_slash, AppConfig, HttpServiceFactory, ResourceDef}; use crate::dev::{insert_slash, HttpServiceFactory, ResourceDef, ServiceConfig};
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::guard::Guard; use crate::guard::Guard;
use crate::handler::{AsyncFactory, Factory}; use crate::handler::{AsyncFactory, Factory};
@ -41,6 +41,7 @@ type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse,
pub struct Resource<P, T = ResourceEndpoint<P>> { pub struct Resource<P, T = ResourceEndpoint<P>> {
endpoint: T, endpoint: T,
rdef: String, rdef: String,
name: Option<String>,
routes: Vec<Route<P>>, routes: Vec<Route<P>>,
guards: Vec<Box<Guard>>, guards: Vec<Box<Guard>>,
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>, default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
@ -54,6 +55,7 @@ impl<P> Resource<P> {
Resource { Resource {
routes: Vec::new(), routes: Vec::new(),
rdef: path.to_string(), rdef: path.to_string(),
name: None,
endpoint: ResourceEndpoint::new(fref.clone()), endpoint: ResourceEndpoint::new(fref.clone()),
factory_ref: fref, factory_ref: fref,
guards: Vec::new(), guards: Vec::new(),
@ -72,6 +74,14 @@ where
InitError = (), InitError = (),
>, >,
{ {
/// Set resource name.
///
/// Name is used for url generation.
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
/// Add match guard to a resource. /// Add match guard to a resource.
/// ///
/// ```rust /// ```rust
@ -240,6 +250,7 @@ where
Resource { Resource {
endpoint, endpoint,
rdef: self.rdef, rdef: self.rdef,
name: self.name,
guards: self.guards, guards: self.guards,
routes: self.routes, routes: self.routes,
default: self.default, default: self.default,
@ -277,7 +288,7 @@ where
InitError = (), InitError = (),
> + 'static, > + 'static,
{ {
fn register(mut self, config: &mut AppConfig<P>) { fn register(mut self, config: &mut ServiceConfig<P>) {
if self.default.borrow().is_none() { if self.default.borrow().is_none() {
*self.default.borrow_mut() = Some(config.default_service()); *self.default.borrow_mut() = Some(config.default_service());
} }
@ -286,11 +297,14 @@ where
} else { } else {
Some(std::mem::replace(&mut self.guards, Vec::new())) Some(std::mem::replace(&mut self.guards, Vec::new()))
}; };
let rdef = if config.is_root() || !self.rdef.is_empty() { let mut rdef = if config.is_root() || !self.rdef.is_empty() {
ResourceDef::new(&insert_slash(&self.rdef)) ResourceDef::new(&insert_slash(&self.rdef))
} else { } else {
ResourceDef::new(&self.rdef) ResourceDef::new(&self.rdef)
}; };
if let Some(ref name) = self.name {
*rdef.name_mut() = name.clone();
}
config.register_service(rdef, guards, self, None) config.register_service(rdef, guards, self, None)
} }
} }

View file

@ -64,14 +64,13 @@ impl ResourceMap {
if self.patterns_for(name, &mut path, &mut elements)?.is_some() { if self.patterns_for(name, &mut path, &mut elements)?.is_some() {
if path.starts_with('/') { if path.starts_with('/') {
// let conn = req.connection_info(); let conn = req.connection_info();
// Ok(Url::parse(&format!( Ok(Url::parse(&format!(
// "{}://{}{}", "{}://{}{}",
// conn.scheme(), conn.scheme(),
// conn.host(), conn.host(),
// path path
// ))?) ))?)
unimplemented!()
} else { } else {
Ok(Url::parse(&path)?) Ok(Url::parse(&path)?)
} }

View file

@ -10,7 +10,7 @@ use actix_service::{
use futures::future::{ok, Either, Future, FutureResult}; use futures::future::{ok, Either, Future, FutureResult};
use futures::{Async, Poll}; use futures::{Async, Poll};
use crate::dev::{AppConfig, HttpServiceFactory}; use crate::dev::{HttpServiceFactory, ServiceConfig};
use crate::guard::Guard; use crate::guard::Guard;
use crate::resource::Resource; use crate::resource::Resource;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
@ -237,7 +237,7 @@ where
InitError = (), InitError = (),
> + 'static, > + 'static,
{ {
fn register(self, config: &mut AppConfig<P>) { fn register(self, config: &mut ServiceConfig<P>) {
// update default resource if needed // update default resource if needed
if self.default.borrow().is_none() { if self.default.borrow().is_none() {
*self.default.borrow_mut() = Some(config.default_service()); *self.default.borrow_mut() = Some(config.default_service());

View file

@ -13,16 +13,16 @@ use actix_http::{
use actix_router::{Path, Resource, Url}; use actix_router::{Path, Resource, Url};
use futures::future::{ok, FutureResult, IntoFuture}; use futures::future::{ok, FutureResult, IntoFuture};
use crate::config::AppConfig; use crate::config::{AppConfig, ServiceConfig};
use crate::request::HttpRequest; use crate::request::HttpRequest;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
pub trait HttpServiceFactory<P> { pub trait HttpServiceFactory<P> {
fn register(self, config: &mut AppConfig<P>); fn register(self, config: &mut ServiceConfig<P>);
} }
pub(crate) trait ServiceFactory<P> { pub(crate) trait ServiceFactory<P> {
fn register(&mut self, config: &mut AppConfig<P>); fn register(&mut self, config: &mut ServiceConfig<P>);
} }
pub(crate) struct ServiceFactoryWrapper<T, P> { pub(crate) struct ServiceFactoryWrapper<T, P> {
@ -43,7 +43,7 @@ impl<T, P> ServiceFactory<P> for ServiceFactoryWrapper<T, P>
where where
T: HttpServiceFactory<P>, T: HttpServiceFactory<P>,
{ {
fn register(&mut self, config: &mut AppConfig<P>) { fn register(&mut self, config: &mut ServiceConfig<P>) {
if let Some(item) = self.factory.take() { if let Some(item) = self.factory.take() {
item.register(config) item.register(config)
} }
@ -60,12 +60,12 @@ impl<P> ServiceRequest<P> {
path: Path<Url>, path: Path<Url>,
request: Request<P>, request: Request<P>,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
extensions: Rc<Extensions>, config: AppConfig,
) -> Self { ) -> Self {
let (head, payload) = request.into_parts(); let (head, payload) = request.into_parts();
ServiceRequest { ServiceRequest {
payload, payload,
req: HttpRequest::new(head, path, rmap, extensions), req: HttpRequest::new(head, path, rmap, config),
} }
} }
@ -156,10 +156,10 @@ impl<P> ServiceRequest<P> {
&mut self.req.path &mut self.req.path
} }
/// Application extensions /// Service configuration
#[inline] #[inline]
pub fn app_extensions(&self) -> &Extensions { pub fn app_config(&self) -> &AppConfig {
self.req.app_extensions() self.req.config()
} }
/// Deconstruct request into parts /// Deconstruct request into parts

View file

@ -52,7 +52,7 @@ impl<T: 'static, P> FromRequest<P> for State<T> {
#[inline] #[inline]
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future { fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
if let Some(st) = req.app_extensions().get::<State<T>>() { if let Some(st) = req.config().extensions().get::<State<T>>() {
Ok(st.clone()) Ok(st.clone())
} else { } else {
Err(ErrorInternalServerError( Err(ErrorInternalServerError(

View file

@ -5,7 +5,7 @@ use std::rc::Rc;
use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; use actix_http::http::header::{Header, HeaderName, IntoHeaderValue};
use actix_http::http::{HttpTryFrom, Method, Version}; use actix_http::http::{HttpTryFrom, Method, Version};
use actix_http::test::TestRequest as HttpTestRequest; use actix_http::test::TestRequest as HttpTestRequest;
use actix_http::{Extensions, PayloadStream, Request}; use actix_http::{PayloadStream, Request};
use actix_router::{Path, ResourceDef, Url}; use actix_router::{Path, ResourceDef, Url};
use actix_rt::Runtime; use actix_rt::Runtime;
use actix_server_config::ServerConfig; use actix_server_config::ServerConfig;
@ -13,6 +13,7 @@ use actix_service::{IntoNewService, NewService, Service};
use bytes::Bytes; use bytes::Bytes;
use futures::Future; use futures::Future;
use crate::config::{AppConfig, AppConfigInner};
use crate::request::HttpRequest; use crate::request::HttpRequest;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::{ServiceFromRequest, ServiceRequest, ServiceResponse}; use crate::service::{ServiceFromRequest, ServiceRequest, ServiceResponse};
@ -142,16 +143,16 @@ where
/// ``` /// ```
pub struct TestRequest { pub struct TestRequest {
req: HttpTestRequest, req: HttpTestRequest,
extensions: Extensions,
rmap: ResourceMap, rmap: ResourceMap,
config: AppConfigInner,
} }
impl Default for TestRequest { impl Default for TestRequest {
fn default() -> TestRequest { fn default() -> TestRequest {
TestRequest { TestRequest {
req: HttpTestRequest::default(), req: HttpTestRequest::default(),
extensions: Extensions::new(),
rmap: ResourceMap::new(ResourceDef::new("")), rmap: ResourceMap::new(ResourceDef::new("")),
config: AppConfigInner::default(),
} }
} }
} }
@ -161,8 +162,8 @@ impl TestRequest {
pub fn with_uri(path: &str) -> TestRequest { pub fn with_uri(path: &str) -> TestRequest {
TestRequest { TestRequest {
req: HttpTestRequest::default().uri(path).take(), req: HttpTestRequest::default().uri(path).take(),
extensions: Extensions::new(),
rmap: ResourceMap::new(ResourceDef::new("")), rmap: ResourceMap::new(ResourceDef::new("")),
config: AppConfigInner::default(),
} }
} }
@ -170,7 +171,7 @@ impl TestRequest {
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest { pub fn with_hdr<H: Header>(hdr: H) -> TestRequest {
TestRequest { TestRequest {
req: HttpTestRequest::default().set(hdr).take(), req: HttpTestRequest::default().set(hdr).take(),
extensions: Extensions::new(), config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")), rmap: ResourceMap::new(ResourceDef::new("")),
} }
} }
@ -183,7 +184,7 @@ impl TestRequest {
{ {
TestRequest { TestRequest {
req: HttpTestRequest::default().header(key, value).take(), req: HttpTestRequest::default().header(key, value).take(),
extensions: Extensions::new(), config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")), rmap: ResourceMap::new(ResourceDef::new("")),
} }
} }
@ -192,7 +193,7 @@ impl TestRequest {
pub fn get() -> TestRequest { pub fn get() -> TestRequest {
TestRequest { TestRequest {
req: HttpTestRequest::default().method(Method::GET).take(), req: HttpTestRequest::default().method(Method::GET).take(),
extensions: Extensions::new(), config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")), rmap: ResourceMap::new(ResourceDef::new("")),
} }
} }
@ -201,7 +202,7 @@ impl TestRequest {
pub fn post() -> TestRequest { pub fn post() -> TestRequest {
TestRequest { TestRequest {
req: HttpTestRequest::default().method(Method::POST).take(), req: HttpTestRequest::default().method(Method::POST).take(),
extensions: Extensions::new(), config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")), rmap: ResourceMap::new(ResourceDef::new("")),
} }
} }
@ -247,8 +248,15 @@ impl TestRequest {
} }
/// Set request config /// Set request config
pub fn config<T: 'static>(mut self, data: T) -> Self { pub fn config<T: 'static>(self, data: T) -> Self {
self.extensions.insert(data); self.config.extensions.borrow_mut().insert(data);
self
}
#[cfg(test)]
/// Set request config
pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {
self.rmap = rmap;
self self
} }
@ -260,7 +268,7 @@ impl TestRequest {
Path::new(Url::new(req.uri().clone())), Path::new(Url::new(req.uri().clone())),
req, req,
Rc::new(self.rmap), Rc::new(self.rmap),
Rc::new(self.extensions), AppConfig::new(self.config),
) )
} }
@ -277,7 +285,7 @@ impl TestRequest {
Path::new(Url::new(req.uri().clone())), Path::new(Url::new(req.uri().clone())),
req, req,
Rc::new(self.rmap), Rc::new(self.rmap),
Rc::new(self.extensions), AppConfig::new(self.config),
) )
.into_request() .into_request()
} }
@ -290,7 +298,7 @@ impl TestRequest {
Path::new(Url::new(req.uri().clone())), Path::new(Url::new(req.uri().clone())),
req, req,
Rc::new(self.rmap), Rc::new(self.rmap),
Rc::new(self.extensions), AppConfig::new(self.config),
); );
ServiceFromRequest::new(req, None) ServiceFromRequest::new(req, None)
} }