mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-23 16:21:06 +01:00
propogate app config with http request; add tests for url_for
This commit is contained in:
parent
c0ce7f0bae
commit
54678308d0
@ -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-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-config = { git = "https://github.com/actix/actix-net.git" }
|
||||
actix-router = { path = "../actix-net/router" }
|
||||
|
||||
bytes = "0.4"
|
||||
derive_more = "0.14"
|
||||
|
@ -17,7 +17,7 @@ use v_htmlescape::escape as escape_html_entity;
|
||||
|
||||
use actix_http::error::{Error, ErrorInternalServerError};
|
||||
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::{
|
||||
blocking, FromRequest, HttpRequest, HttpResponse, Responder, ServiceFromRequest,
|
||||
ServiceRequest, ServiceResponse,
|
||||
@ -305,7 +305,7 @@ where
|
||||
P: 'static,
|
||||
C: StaticFileConfig + 'static,
|
||||
{
|
||||
fn register(self, config: &mut AppConfig<P>) {
|
||||
fn register(self, config: &mut ServiceConfig<P>) {
|
||||
if self.default.borrow().is_none() {
|
||||
*self.default.borrow_mut() = Some(config.default_service());
|
||||
}
|
||||
@ -314,11 +314,12 @@ where
|
||||
} else {
|
||||
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 Error = ();
|
||||
type Service = FilesService<P, C>;
|
||||
@ -350,7 +351,8 @@ pub struct FilesService<S, C = DefaultConfig> {
|
||||
_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 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 {
|
||||
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,
|
||||
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);
|
||||
|
||||
impl PathBufWrp {
|
||||
fn get_pathbuf(path: &dev::Path<Url>) -> Result<Self, UriSegmentError> {
|
||||
let path_str = path.path();
|
||||
fn get_pathbuf(path: &str) -> Result<Self, UriSegmentError> {
|
||||
let mut buf = PathBuf::new();
|
||||
for segment in path_str.split('/') {
|
||||
for segment in path.split('/') {
|
||||
if segment == ".." {
|
||||
buf.pop();
|
||||
} else if segment.starts_with('.') {
|
||||
@ -447,13 +449,14 @@ impl<P> FromRequest<P> for PathBufWrp {
|
||||
type Config = ();
|
||||
|
||||
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
|
||||
PathBufWrp::get_pathbuf(req.match_info())
|
||||
PathBufWrp::get_pathbuf(req.match_info().path())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::Add;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
@ -1093,32 +1096,32 @@ mod tests {
|
||||
#[test]
|
||||
fn test_path_buf() {
|
||||
assert_eq!(
|
||||
PathBuf::from_param("/test/.tt"),
|
||||
PathBufWrp::get_pathbuf("/test/.tt").map(|t| t.0),
|
||||
Err(UriSegmentError::BadStart('.'))
|
||||
);
|
||||
assert_eq!(
|
||||
PathBuf::from_param("/test/*tt"),
|
||||
PathBufWrp::get_pathbuf("/test/*tt").map(|t| t.0),
|
||||
Err(UriSegmentError::BadStart('*'))
|
||||
);
|
||||
assert_eq!(
|
||||
PathBuf::from_param("/test/tt:"),
|
||||
PathBufWrp::get_pathbuf("/test/tt:").map(|t| t.0),
|
||||
Err(UriSegmentError::BadEnd(':'))
|
||||
);
|
||||
assert_eq!(
|
||||
PathBuf::from_param("/test/tt<"),
|
||||
PathBufWrp::get_pathbuf("/test/tt<").map(|t| t.0),
|
||||
Err(UriSegmentError::BadEnd('<'))
|
||||
);
|
||||
assert_eq!(
|
||||
PathBuf::from_param("/test/tt>"),
|
||||
PathBufWrp::get_pathbuf("/test/tt>").map(|t| t.0),
|
||||
Err(UriSegmentError::BadEnd('>'))
|
||||
);
|
||||
assert_eq!(
|
||||
PathBuf::from_param("/seg1/seg2/"),
|
||||
Ok(PathBuf::from_iter(vec!["seg1", "seg2"]))
|
||||
PathBufWrp::get_pathbuf("/seg1/seg2/").unwrap().0,
|
||||
PathBuf::from_iter(vec!["seg1", "seg2"])
|
||||
);
|
||||
assert_eq!(
|
||||
PathBuf::from_param("/seg1/../seg2/"),
|
||||
Ok(PathBuf::from_iter(vec!["seg2"]))
|
||||
PathBufWrp::get_pathbuf("/seg1/../seg2/").unwrap().0,
|
||||
PathBuf::from_iter(vec!["seg2"])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -304,8 +304,6 @@ impl<C: StaticFileConfig> Responder for NamedFile<C> {
|
||||
type Future = Result<HttpResponse, Error>;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> Self::Future {
|
||||
println!("RESP: {:?}", req);
|
||||
|
||||
if self.status_code != StatusCode::OK {
|
||||
let mut resp = HttpResponse::build(self.status_code);
|
||||
resp.set(header::ContentType(self.content_type.clone()))
|
||||
|
@ -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
|
||||
S: Service<ServiceRequest<P>, Response = ServiceResponse<B>>,
|
||||
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
type Request = ServiceRequest<P>;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = S::Error;
|
||||
type InitError = ();
|
||||
@ -281,12 +282,13 @@ pub struct CookieSessionMiddleware<S> {
|
||||
inner: Rc<CookieSessionInner>,
|
||||
}
|
||||
|
||||
impl<S, P, B: 'static> Service<ServiceRequest<P>> for CookieSessionMiddleware<S>
|
||||
impl<S, P, B: 'static> Service for CookieSessionMiddleware<S>
|
||||
where
|
||||
S: Service<ServiceRequest<P>, Response = ServiceResponse<B>>,
|
||||
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
type Request = ServiceRequest<P>;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = S::Error;
|
||||
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
|
||||
|
@ -31,7 +31,7 @@ pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
struct #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
|
||||
actix_web::dev::HttpServiceFactory::register(
|
||||
actix_web::Resource::new(#path)
|
||||
@ -68,7 +68,7 @@ pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
struct #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
|
||||
actix_web::dev::HttpServiceFactory::register(
|
||||
actix_web::Resource::new(#path)
|
||||
@ -105,7 +105,7 @@ pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
struct #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
|
||||
actix_web::dev::HttpServiceFactory::register(
|
||||
actix_web::Resource::new(#path)
|
||||
|
61
src/app.rs
61
src/app.rs
@ -3,7 +3,8 @@ use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
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_service::boxed::{self, BoxedNewService};
|
||||
use actix_service::{
|
||||
@ -12,6 +13,7 @@ use actix_service::{
|
||||
use futures::IntoFuture;
|
||||
|
||||
use crate::app_service::{AppChain, AppEntry, AppInit, AppRouting, AppRoutingFactory};
|
||||
use crate::config::{AppConfig, AppConfigInner};
|
||||
use crate::resource::Resource;
|
||||
use crate::route::Route;
|
||||
use crate::service::{
|
||||
@ -29,9 +31,8 @@ where
|
||||
T: NewService<Request = ServiceRequest, Response = ServiceRequest<P>>,
|
||||
{
|
||||
chain: T,
|
||||
extensions: Extensions,
|
||||
state: Vec<Box<StateFactory>>,
|
||||
host: String,
|
||||
config: AppConfigInner,
|
||||
_t: PhantomData<(P,)>,
|
||||
}
|
||||
|
||||
@ -41,9 +42,8 @@ impl App<PayloadStream, AppChain> {
|
||||
pub fn new() -> Self {
|
||||
App {
|
||||
chain: AppChain,
|
||||
extensions: Extensions::new(),
|
||||
state: Vec::new(),
|
||||
host: "localhost:8080".to_string(),
|
||||
config: AppConfigInner::default(),
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -141,8 +141,8 @@ where
|
||||
services: Vec::new(),
|
||||
default: None,
|
||||
factory_ref: fref,
|
||||
extensions: self.extensions,
|
||||
host: self.host,
|
||||
config: self.config,
|
||||
external: Vec::new(),
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -174,8 +174,7 @@ where
|
||||
App {
|
||||
chain,
|
||||
state: self.state,
|
||||
extensions: self.extensions,
|
||||
host: self.host,
|
||||
config: self.config,
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -223,10 +222,10 @@ where
|
||||
default: None,
|
||||
endpoint: AppEntry::new(fref.clone()),
|
||||
factory_ref: fref,
|
||||
extensions: self.extensions,
|
||||
state: self.state,
|
||||
host: self.host,
|
||||
config: self.config,
|
||||
services: vec![Box::new(ServiceFactoryWrapper::new(service))],
|
||||
external: Vec::new(),
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -239,7 +238,7 @@ where
|
||||
///
|
||||
/// By default host name is set to a "localhost" value.
|
||||
pub fn hostname(mut self, val: &str) -> Self {
|
||||
self.host = val.to_owned();
|
||||
self.config.host = val.to_owned();
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -252,9 +251,9 @@ pub struct AppRouter<C, P, B, T> {
|
||||
services: Vec<Box<ServiceFactory<P>>>,
|
||||
default: Option<Rc<HttpNewService<P>>>,
|
||||
factory_ref: Rc<RefCell<Option<AppRoutingFactory<P>>>>,
|
||||
extensions: Extensions,
|
||||
state: Vec<Box<StateFactory>>,
|
||||
host: String,
|
||||
config: AppConfigInner,
|
||||
external: Vec<ResourceDef>,
|
||||
_t: PhantomData<(P, B)>,
|
||||
}
|
||||
|
||||
@ -348,8 +347,8 @@ where
|
||||
services: self.services,
|
||||
default: self.default,
|
||||
factory_ref: self.factory_ref,
|
||||
extensions: self.extensions,
|
||||
host: self.host,
|
||||
config: self.config,
|
||||
external: self.external,
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -382,33 +381,30 @@ where
|
||||
/// and are never considered for matching at request time. Calls to
|
||||
/// `HttpRequest::url_for()` will work as expected.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// # extern crate actix_web;
|
||||
/// use actix_web::{App, HttpRequest, HttpResponse, Result};
|
||||
/// ```rust
|
||||
/// use actix_web::{web, App, HttpRequest, HttpResponse, Result};
|
||||
///
|
||||
/// fn index(req: &HttpRequest) -> Result<HttpResponse> {
|
||||
/// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?;
|
||||
/// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
|
||||
/// fn index(req: HttpRequest) -> Result<HttpResponse> {
|
||||
/// let url = req.url_for("youtube", &["asdlkjqme"])?;
|
||||
/// assert_eq!(url.as_str(), "https://youtube.com/watch/asdlkjqme");
|
||||
/// Ok(HttpResponse::Ok().into())
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new()
|
||||
/// .resource("/index.html", |r| r.get().f(index))
|
||||
/// .external_resource("youtube", "https://youtube.com/watch/{video_id}")
|
||||
/// .finish();
|
||||
/// .service(web::resource("/index.html").route(
|
||||
/// web::get().to(index)))
|
||||
/// .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
|
||||
N: AsRef<str>,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
// self.parts
|
||||
// .as_mut()
|
||||
// .expect("Use after finish")
|
||||
// .router
|
||||
// .register_external(name.as_ref(), ResourceDef::external(url.as_ref()));
|
||||
let mut rdef = ResourceDef::new(url.as_ref());
|
||||
*rdef.name_mut() = name.as_ref().to_string();
|
||||
self.external.push(rdef);
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -435,9 +431,10 @@ where
|
||||
state: self.state,
|
||||
endpoint: self.endpoint,
|
||||
services: RefCell::new(self.services),
|
||||
external: RefCell::new(self.external),
|
||||
default: self.default,
|
||||
factory_ref: self.factory_ref,
|
||||
extensions: Rc::new(RefCell::new(Rc::new(self.extensions))),
|
||||
config: RefCell::new(AppConfig(Rc::new(self.config))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
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_server_config::ServerConfig;
|
||||
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::{Async, Future, Poll};
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::config::{AppConfig, ServiceConfig};
|
||||
use crate::guard::Guard;
|
||||
use crate::rmap::ResourceMap;
|
||||
use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse};
|
||||
@ -36,10 +36,11 @@ where
|
||||
pub(crate) chain: C,
|
||||
pub(crate) endpoint: T,
|
||||
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) default: Option<Rc<HttpNewService<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>
|
||||
@ -64,7 +65,7 @@ where
|
||||
type Service = AndThen<AppInitService<C::Service, P>, T::Service>;
|
||||
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
|
||||
let default = self.default.clone().unwrap_or_else(|| {
|
||||
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(),
|
||||
"localhost:8080".to_owned(),
|
||||
false,
|
||||
default.clone(),
|
||||
);
|
||||
{
|
||||
let mut c = self.config.borrow_mut();
|
||||
let loc_cfg = Rc::get_mut(&mut c.0).unwrap();
|
||||
loc_cfg.secure = cfg.secure();
|
||||
loc_cfg.addr = cfg.local_addr();
|
||||
}
|
||||
|
||||
let mut config =
|
||||
ServiceConfig::new(self.config.borrow().clone(), default.clone());
|
||||
|
||||
// register services
|
||||
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
|
||||
let rmap = Rc::new(rmap);
|
||||
rmap.finish(rmap.clone());
|
||||
@ -111,7 +120,7 @@ where
|
||||
endpoint: None,
|
||||
endpoint_fut: self.endpoint.new_service(&()),
|
||||
state: self.state.iter().map(|s| s.construct()).collect(),
|
||||
extensions: self.extensions.clone(),
|
||||
config: self.config.borrow().clone(),
|
||||
rmap,
|
||||
_t: PhantomData,
|
||||
}
|
||||
@ -129,7 +138,7 @@ where
|
||||
endpoint_fut: T::Future,
|
||||
rmap: Rc<ResourceMap>,
|
||||
state: Vec<Box<StateFactoryResult>>,
|
||||
extensions: Rc<RefCell<Rc<Extensions>>>,
|
||||
config: AppConfig,
|
||||
_t: PhantomData<(P, B)>,
|
||||
}
|
||||
|
||||
@ -152,20 +161,14 @@ where
|
||||
type Error = C::InitError;
|
||||
|
||||
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;
|
||||
while idx < self.state.len() {
|
||||
if let Async::Ready(_) = self.state[idx].poll_result(extensions)? {
|
||||
self.state.remove(idx);
|
||||
} else {
|
||||
idx += 1;
|
||||
}
|
||||
let mut idx = 0;
|
||||
let mut extensions = self.config.0.extensions.borrow_mut();
|
||||
while idx < self.state.len() {
|
||||
if let Async::Ready(_) = self.state[idx].poll_result(&mut extensions)? {
|
||||
self.state.remove(idx);
|
||||
} else {
|
||||
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() {
|
||||
@ -185,7 +188,7 @@ where
|
||||
AppInitService {
|
||||
chain: self.chain.take().unwrap(),
|
||||
rmap: self.rmap.clone(),
|
||||
extensions: self.extensions.borrow().clone(),
|
||||
config: self.config.clone(),
|
||||
}
|
||||
.and_then(self.endpoint.take().unwrap()),
|
||||
))
|
||||
@ -202,7 +205,7 @@ where
|
||||
{
|
||||
chain: C,
|
||||
rmap: Rc<ResourceMap>,
|
||||
extensions: Rc<Extensions>,
|
||||
config: AppConfig,
|
||||
}
|
||||
|
||||
impl<C, P> Service for AppInitService<C, P>
|
||||
@ -223,7 +226,7 @@ where
|
||||
Path::new(Url::new(req.uri().clone())),
|
||||
req,
|
||||
self.rmap.clone(),
|
||||
self.extensions.clone(),
|
||||
self.config.clone(),
|
||||
);
|
||||
self.chain.call(req)
|
||||
}
|
||||
|
106
src/config.rs
106
src/config.rs
@ -1,6 +1,8 @@
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::net::SocketAddr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use actix_http::Extensions;
|
||||
use actix_router::ResourceDef;
|
||||
use actix_service::{boxed, IntoNewService, NewService};
|
||||
|
||||
@ -13,10 +15,8 @@ type HttpNewService<P> =
|
||||
boxed::BoxedNewService<(), ServiceRequest<P>, ServiceResponse, (), ()>;
|
||||
|
||||
/// Application configuration
|
||||
pub struct AppConfig<P> {
|
||||
addr: SocketAddr,
|
||||
secure: bool,
|
||||
host: String,
|
||||
pub struct ServiceConfig<P> {
|
||||
config: AppConfig,
|
||||
root: bool,
|
||||
default: Rc<HttpNewService<P>>,
|
||||
services: Vec<(
|
||||
@ -27,18 +27,11 @@ pub struct AppConfig<P> {
|
||||
)>,
|
||||
}
|
||||
|
||||
impl<P: 'static> AppConfig<P> {
|
||||
impl<P: 'static> ServiceConfig<P> {
|
||||
/// Crate server settings instance
|
||||
pub(crate) fn new(
|
||||
addr: SocketAddr,
|
||||
host: String,
|
||||
secure: bool,
|
||||
default: Rc<HttpNewService<P>>,
|
||||
) -> Self {
|
||||
AppConfig {
|
||||
addr,
|
||||
secure,
|
||||
host,
|
||||
pub(crate) fn new(config: AppConfig, default: Rc<HttpNewService<P>>) -> Self {
|
||||
ServiceConfig {
|
||||
config,
|
||||
default,
|
||||
root: true,
|
||||
services: Vec::new(),
|
||||
@ -62,31 +55,20 @@ impl<P: 'static> AppConfig<P> {
|
||||
}
|
||||
|
||||
pub(crate) fn clone_config(&self) -> Self {
|
||||
AppConfig {
|
||||
addr: self.addr,
|
||||
secure: self.secure,
|
||||
host: self.host.clone(),
|
||||
ServiceConfig {
|
||||
config: self.config.clone(),
|
||||
default: self.default.clone(),
|
||||
services: Vec::new(),
|
||||
root: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this TCP connection
|
||||
pub fn local_addr(&self) -> SocketAddr {
|
||||
self.addr
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// Service configuration
|
||||
pub fn config(&self) -> &AppConfig {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Default resource
|
||||
pub fn default_service(&self) -> Rc<HttpNewService<P>> {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Error and Result module
|
||||
|
||||
pub use actix_http::error::*;
|
||||
use derive_more::{Display, From};
|
||||
use url::ParseError as UrlParseError;
|
||||
|
61
src/info.rs
61
src/info.rs
@ -1,19 +1,14 @@
|
||||
use std::cell::Ref;
|
||||
|
||||
use actix_http::http::header::{self, HeaderName};
|
||||
use actix_http::RequestHead;
|
||||
use crate::dev::{AppConfig, RequestHead};
|
||||
use crate::http::header::{self, HeaderName};
|
||||
|
||||
const X_FORWARDED_FOR: &[u8] = b"x-forwarded-for";
|
||||
const X_FORWARDED_HOST: &[u8] = b"x-forwarded-host";
|
||||
const X_FORWARDED_PROTO: &[u8] = b"x-forwarded-proto";
|
||||
|
||||
pub enum ConnectionInfoError {
|
||||
UnknownHost,
|
||||
UnknownScheme,
|
||||
}
|
||||
|
||||
/// `HttpRequest` connection information
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ConnectionInfo {
|
||||
scheme: String,
|
||||
host: String,
|
||||
@ -23,19 +18,19 @@ pub struct ConnectionInfo {
|
||||
|
||||
impl ConnectionInfo {
|
||||
/// 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>() {
|
||||
req.extensions_mut().insert(ConnectionInfo::new(req));
|
||||
req.extensions_mut().insert(ConnectionInfo::new(req, cfg));
|
||||
}
|
||||
Ref::map(req.extensions(), |e| e.get().unwrap())
|
||||
}
|
||||
|
||||
#[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 scheme = None;
|
||||
let mut remote = None;
|
||||
let mut peer = None;
|
||||
let peer = None;
|
||||
|
||||
// load forwarded header
|
||||
for hdr in req.headers.get_all(header::FORWARDED) {
|
||||
@ -82,7 +77,7 @@ impl ConnectionInfo {
|
||||
}
|
||||
if scheme.is_none() {
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -105,7 +100,7 @@ impl ConnectionInfo {
|
||||
if host.is_none() {
|
||||
host = req.uri.authority_part().map(|a| a.as_str());
|
||||
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());
|
||||
}
|
||||
}
|
||||
if remote.is_none() {
|
||||
// get peeraddr from socketaddr
|
||||
peer = req.peer_addr().map(|addr| format!("{}", addr));
|
||||
}
|
||||
// if remote.is_none() {
|
||||
// get peeraddr from socketaddr
|
||||
// peer = req.peer_addr().map(|addr| format!("{}", addr));
|
||||
// }
|
||||
}
|
||||
|
||||
ConnectionInfo {
|
||||
@ -186,9 +181,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_forwarded() {
|
||||
let req = TestRequest::default().request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let info = req.connection_info();
|
||||
assert_eq!(info.scheme(), "http");
|
||||
assert_eq!(info.host(), "localhost:8080");
|
||||
|
||||
@ -197,44 +191,39 @@ mod tests {
|
||||
header::FORWARDED,
|
||||
"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();
|
||||
info.update(&req);
|
||||
let info = req.connection_info();
|
||||
assert_eq!(info.scheme(), "https");
|
||||
assert_eq!(info.host(), "rust-lang.org");
|
||||
assert_eq!(info.remote(), Some("192.0.2.60"));
|
||||
|
||||
let req = TestRequest::default()
|
||||
.header(header::HOST, "rust-lang.org")
|
||||
.request();
|
||||
.to_http_request();
|
||||
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
let info = req.connection_info();
|
||||
assert_eq!(info.scheme(), "http");
|
||||
assert_eq!(info.host(), "rust-lang.org");
|
||||
assert_eq!(info.remote(), None);
|
||||
|
||||
let req = TestRequest::default()
|
||||
.header(X_FORWARDED_FOR, "192.0.2.60")
|
||||
.request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
.to_http_request();
|
||||
let info = req.connection_info();
|
||||
assert_eq!(info.remote(), Some("192.0.2.60"));
|
||||
|
||||
let req = TestRequest::default()
|
||||
.header(X_FORWARDED_HOST, "192.0.2.60")
|
||||
.request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
.to_http_request();
|
||||
let info = req.connection_info();
|
||||
assert_eq!(info.host(), "192.0.2.60");
|
||||
assert_eq!(info.remote(), None);
|
||||
|
||||
let req = TestRequest::default()
|
||||
.header(X_FORWARDED_PROTO, "https")
|
||||
.request();
|
||||
let mut info = ConnectionInfo::default();
|
||||
info.update(&req);
|
||||
.to_http_request();
|
||||
let info = req.connection_info();
|
||||
assert_eq!(info.scheme(), "https");
|
||||
}
|
||||
}
|
||||
|
12
src/lib.rs
12
src/lib.rs
@ -2,13 +2,13 @@
|
||||
|
||||
mod app;
|
||||
mod app_service;
|
||||
mod extract;
|
||||
mod handler;
|
||||
// mod info;
|
||||
pub mod blocking;
|
||||
mod config;
|
||||
pub mod error;
|
||||
mod extract;
|
||||
pub mod guard;
|
||||
mod handler;
|
||||
mod info;
|
||||
pub mod middleware;
|
||||
mod request;
|
||||
mod resource;
|
||||
@ -54,7 +54,9 @@ pub mod dev {
|
||||
//! ```
|
||||
|
||||
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 actix_http::body::{Body, BodyLength, MessageBody, ResponseBody};
|
||||
@ -62,7 +64,7 @@ pub mod dev {
|
||||
pub use actix_http::{
|
||||
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 {
|
||||
let mut path = path.to_owned();
|
||||
|
156
src/request.rs
156
src/request.rs
@ -7,8 +7,10 @@ use actix_http::http::{HeaderMap, Method, Uri, Version};
|
||||
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
|
||||
use actix_router::{Path, Url};
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::error::UrlGenerationError;
|
||||
use crate::extract::FromRequest;
|
||||
use crate::info::ConnectionInfo;
|
||||
use crate::rmap::ResourceMap;
|
||||
use crate::service::ServiceFromRequest;
|
||||
|
||||
@ -18,7 +20,7 @@ pub struct HttpRequest {
|
||||
pub(crate) head: Message<RequestHead>,
|
||||
pub(crate) path: Path<Url>,
|
||||
rmap: Rc<ResourceMap>,
|
||||
extensions: Rc<Extensions>,
|
||||
config: AppConfig,
|
||||
}
|
||||
|
||||
impl HttpRequest {
|
||||
@ -27,13 +29,13 @@ impl HttpRequest {
|
||||
head: Message<RequestHead>,
|
||||
path: Path<Url>,
|
||||
rmap: Rc<ResourceMap>,
|
||||
extensions: Rc<Extensions>,
|
||||
config: AppConfig,
|
||||
) -> HttpRequest {
|
||||
HttpRequest {
|
||||
head,
|
||||
path,
|
||||
rmap,
|
||||
extensions,
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,17 +94,17 @@ impl HttpRequest {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Application extensions
|
||||
/// App config
|
||||
#[inline]
|
||||
pub fn app_extensions(&self) -> &Extensions {
|
||||
&self.extensions
|
||||
pub fn config(&self) -> &AppConfig {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Generate url for named resource
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate actix_web;
|
||||
/// # use actix_web::{App, HttpRequest, HttpResponse, http};
|
||||
/// # use actix_web::{web, App, HttpRequest, HttpResponse};
|
||||
/// #
|
||||
/// fn index(req: HttpRequest) -> HttpResponse {
|
||||
/// let url = req.url_for("foo", &["1", "2", "3"]); // <- generate url for "foo" resource
|
||||
@ -111,11 +113,10 @@ impl HttpRequest {
|
||||
///
|
||||
/// fn main() {
|
||||
/// let app = App::new()
|
||||
/// .resource("/test/{one}/{two}/{three}", |r| {
|
||||
/// r.name("foo"); // <- set resource name, then it could be used in `url_for`
|
||||
/// r.method(http::Method::GET).f(|_| HttpResponse::Ok());
|
||||
/// })
|
||||
/// .finish();
|
||||
/// .service(web::resource("/test/{one}/{two}/{three}")
|
||||
/// .name("foo") // <- set resource name, then it could be used in `url_for`
|
||||
/// .route(web::get().to(|| HttpResponse::Ok()))
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
pub fn url_for<U, I>(
|
||||
@ -139,11 +140,11 @@ impl HttpRequest {
|
||||
self.url_for(name, &NO_PARAMS)
|
||||
}
|
||||
|
||||
// /// Get *ConnectionInfo* for the correct request.
|
||||
// #[inline]
|
||||
// pub fn connection_info(&self) -> Ref<ConnectionInfo> {
|
||||
// ConnectionInfo::get(&*self)
|
||||
// }
|
||||
/// Get *ConnectionInfo* for the current request.
|
||||
#[inline]
|
||||
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
|
||||
ConnectionInfo::get(&*self, &*self.config())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for HttpRequest {
|
||||
@ -234,3 +235,124 @@ impl fmt::Debug for HttpRequest {
|
||||
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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use actix_service::{
|
||||
use futures::future::{ok, Either, FutureResult};
|
||||
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::guard::Guard;
|
||||
use crate::handler::{AsyncFactory, Factory};
|
||||
@ -41,6 +41,7 @@ type HttpNewService<P> = BoxedNewService<(), ServiceRequest<P>, ServiceResponse,
|
||||
pub struct Resource<P, T = ResourceEndpoint<P>> {
|
||||
endpoint: T,
|
||||
rdef: String,
|
||||
name: Option<String>,
|
||||
routes: Vec<Route<P>>,
|
||||
guards: Vec<Box<Guard>>,
|
||||
default: Rc<RefCell<Option<Rc<HttpNewService<P>>>>>,
|
||||
@ -54,6 +55,7 @@ impl<P> Resource<P> {
|
||||
Resource {
|
||||
routes: Vec::new(),
|
||||
rdef: path.to_string(),
|
||||
name: None,
|
||||
endpoint: ResourceEndpoint::new(fref.clone()),
|
||||
factory_ref: fref,
|
||||
guards: Vec::new(),
|
||||
@ -72,6 +74,14 @@ where
|
||||
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.
|
||||
///
|
||||
/// ```rust
|
||||
@ -240,6 +250,7 @@ where
|
||||
Resource {
|
||||
endpoint,
|
||||
rdef: self.rdef,
|
||||
name: self.name,
|
||||
guards: self.guards,
|
||||
routes: self.routes,
|
||||
default: self.default,
|
||||
@ -277,7 +288,7 @@ where
|
||||
InitError = (),
|
||||
> + 'static,
|
||||
{
|
||||
fn register(mut self, config: &mut AppConfig<P>) {
|
||||
fn register(mut self, config: &mut ServiceConfig<P>) {
|
||||
if self.default.borrow().is_none() {
|
||||
*self.default.borrow_mut() = Some(config.default_service());
|
||||
}
|
||||
@ -286,11 +297,14 @@ where
|
||||
} else {
|
||||
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))
|
||||
} else {
|
||||
ResourceDef::new(&self.rdef)
|
||||
};
|
||||
if let Some(ref name) = self.name {
|
||||
*rdef.name_mut() = name.clone();
|
||||
}
|
||||
config.register_service(rdef, guards, self, None)
|
||||
}
|
||||
}
|
||||
|
15
src/rmap.rs
15
src/rmap.rs
@ -64,14 +64,13 @@ impl ResourceMap {
|
||||
|
||||
if self.patterns_for(name, &mut path, &mut elements)?.is_some() {
|
||||
if path.starts_with('/') {
|
||||
// let conn = req.connection_info();
|
||||
// Ok(Url::parse(&format!(
|
||||
// "{}://{}{}",
|
||||
// conn.scheme(),
|
||||
// conn.host(),
|
||||
// path
|
||||
// ))?)
|
||||
unimplemented!()
|
||||
let conn = req.connection_info();
|
||||
Ok(Url::parse(&format!(
|
||||
"{}://{}{}",
|
||||
conn.scheme(),
|
||||
conn.host(),
|
||||
path
|
||||
))?)
|
||||
} else {
|
||||
Ok(Url::parse(&path)?)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use actix_service::{
|
||||
use futures::future::{ok, Either, Future, FutureResult};
|
||||
use futures::{Async, Poll};
|
||||
|
||||
use crate::dev::{AppConfig, HttpServiceFactory};
|
||||
use crate::dev::{HttpServiceFactory, ServiceConfig};
|
||||
use crate::guard::Guard;
|
||||
use crate::resource::Resource;
|
||||
use crate::rmap::ResourceMap;
|
||||
@ -237,7 +237,7 @@ where
|
||||
InitError = (),
|
||||
> + 'static,
|
||||
{
|
||||
fn register(self, config: &mut AppConfig<P>) {
|
||||
fn register(self, config: &mut ServiceConfig<P>) {
|
||||
// update default resource if needed
|
||||
if self.default.borrow().is_none() {
|
||||
*self.default.borrow_mut() = Some(config.default_service());
|
||||
|
@ -13,16 +13,16 @@ use actix_http::{
|
||||
use actix_router::{Path, Resource, Url};
|
||||
use futures::future::{ok, FutureResult, IntoFuture};
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::config::{AppConfig, ServiceConfig};
|
||||
use crate::request::HttpRequest;
|
||||
use crate::rmap::ResourceMap;
|
||||
|
||||
pub trait HttpServiceFactory<P> {
|
||||
fn register(self, config: &mut AppConfig<P>);
|
||||
fn register(self, config: &mut ServiceConfig<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> {
|
||||
@ -43,7 +43,7 @@ impl<T, P> ServiceFactory<P> for ServiceFactoryWrapper<T, P>
|
||||
where
|
||||
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() {
|
||||
item.register(config)
|
||||
}
|
||||
@ -60,12 +60,12 @@ impl<P> ServiceRequest<P> {
|
||||
path: Path<Url>,
|
||||
request: Request<P>,
|
||||
rmap: Rc<ResourceMap>,
|
||||
extensions: Rc<Extensions>,
|
||||
config: AppConfig,
|
||||
) -> Self {
|
||||
let (head, payload) = request.into_parts();
|
||||
ServiceRequest {
|
||||
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
|
||||
}
|
||||
|
||||
/// Application extensions
|
||||
/// Service configuration
|
||||
#[inline]
|
||||
pub fn app_extensions(&self) -> &Extensions {
|
||||
self.req.app_extensions()
|
||||
pub fn app_config(&self) -> &AppConfig {
|
||||
self.req.config()
|
||||
}
|
||||
|
||||
/// Deconstruct request into parts
|
||||
|
@ -52,7 +52,7 @@ impl<T: 'static, P> FromRequest<P> for State<T> {
|
||||
|
||||
#[inline]
|
||||
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())
|
||||
} else {
|
||||
Err(ErrorInternalServerError(
|
||||
|
34
src/test.rs
34
src/test.rs
@ -5,7 +5,7 @@ use std::rc::Rc;
|
||||
use actix_http::http::header::{Header, HeaderName, IntoHeaderValue};
|
||||
use actix_http::http::{HttpTryFrom, Method, Version};
|
||||
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_rt::Runtime;
|
||||
use actix_server_config::ServerConfig;
|
||||
@ -13,6 +13,7 @@ use actix_service::{IntoNewService, NewService, Service};
|
||||
use bytes::Bytes;
|
||||
use futures::Future;
|
||||
|
||||
use crate::config::{AppConfig, AppConfigInner};
|
||||
use crate::request::HttpRequest;
|
||||
use crate::rmap::ResourceMap;
|
||||
use crate::service::{ServiceFromRequest, ServiceRequest, ServiceResponse};
|
||||
@ -142,16 +143,16 @@ where
|
||||
/// ```
|
||||
pub struct TestRequest {
|
||||
req: HttpTestRequest,
|
||||
extensions: Extensions,
|
||||
rmap: ResourceMap,
|
||||
config: AppConfigInner,
|
||||
}
|
||||
|
||||
impl Default for TestRequest {
|
||||
fn default() -> TestRequest {
|
||||
TestRequest {
|
||||
req: HttpTestRequest::default(),
|
||||
extensions: Extensions::new(),
|
||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
||||
config: AppConfigInner::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,8 +162,8 @@ impl TestRequest {
|
||||
pub fn with_uri(path: &str) -> TestRequest {
|
||||
TestRequest {
|
||||
req: HttpTestRequest::default().uri(path).take(),
|
||||
extensions: Extensions::new(),
|
||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
||||
config: AppConfigInner::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,7 +171,7 @@ impl TestRequest {
|
||||
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest {
|
||||
TestRequest {
|
||||
req: HttpTestRequest::default().set(hdr).take(),
|
||||
extensions: Extensions::new(),
|
||||
config: AppConfigInner::default(),
|
||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
||||
}
|
||||
}
|
||||
@ -183,7 +184,7 @@ impl TestRequest {
|
||||
{
|
||||
TestRequest {
|
||||
req: HttpTestRequest::default().header(key, value).take(),
|
||||
extensions: Extensions::new(),
|
||||
config: AppConfigInner::default(),
|
||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
||||
}
|
||||
}
|
||||
@ -192,7 +193,7 @@ impl TestRequest {
|
||||
pub fn get() -> TestRequest {
|
||||
TestRequest {
|
||||
req: HttpTestRequest::default().method(Method::GET).take(),
|
||||
extensions: Extensions::new(),
|
||||
config: AppConfigInner::default(),
|
||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
||||
}
|
||||
}
|
||||
@ -201,7 +202,7 @@ impl TestRequest {
|
||||
pub fn post() -> TestRequest {
|
||||
TestRequest {
|
||||
req: HttpTestRequest::default().method(Method::POST).take(),
|
||||
extensions: Extensions::new(),
|
||||
config: AppConfigInner::default(),
|
||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
||||
}
|
||||
}
|
||||
@ -247,8 +248,15 @@ impl TestRequest {
|
||||
}
|
||||
|
||||
/// Set request config
|
||||
pub fn config<T: 'static>(mut self, data: T) -> Self {
|
||||
self.extensions.insert(data);
|
||||
pub fn config<T: 'static>(self, data: T) -> Self {
|
||||
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
|
||||
}
|
||||
|
||||
@ -260,7 +268,7 @@ impl TestRequest {
|
||||
Path::new(Url::new(req.uri().clone())),
|
||||
req,
|
||||
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())),
|
||||
req,
|
||||
Rc::new(self.rmap),
|
||||
Rc::new(self.extensions),
|
||||
AppConfig::new(self.config),
|
||||
)
|
||||
.into_request()
|
||||
}
|
||||
@ -290,7 +298,7 @@ impl TestRequest {
|
||||
Path::new(Url::new(req.uri().clone())),
|
||||
req,
|
||||
Rc::new(self.rmap),
|
||||
Rc::new(self.extensions),
|
||||
AppConfig::new(self.config),
|
||||
);
|
||||
ServiceFromRequest::new(req, None)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user