use std::cell::RefCell; use std::rc::Rc; use actix_http::{Error, Response}; use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::{ ApplyTransform, IntoNewService, IntoTransform, NewService, Service, Transform, }; use futures::future::{ok, Either, FutureResult}; use futures::{Async, Future, IntoFuture, Poll}; use crate::extract::FromRequest; use crate::handler::{AsyncFactory, Factory}; use crate::responder::Responder; use crate::route::{CreateRouteService, Route, RouteService}; use crate::service::{ServiceRequest, ServiceResponse}; type HttpService
= BoxedService = BoxedNewService<(), ServiceRequest , ServiceResponse, (), ()>;
/// *Resource* is an entry in route table which corresponds to requested URL.
///
/// Resource in turn has at least one route.
/// Route consists of an handlers objects and list of guards
/// (objects that implement `Guard` trait).
/// Resources and rouets uses builder-like pattern for configuration.
/// During request handling, resource object iterate through all routes
/// and check guards for specific route, if request matches all
/// guards, route considered matched and route handler get called.
///
/// ```rust
/// use actix_web::{web, App, HttpResponse};
///
/// fn main() {
/// let app = App::new()
/// .resource(
/// "/", |r| r.route(web::get().to(|| HttpResponse::Ok())));
/// }
pub struct Resource > {
routes: Vec Resource {
pub fn new() -> Resource {
let fref = Rc::new(RefCell::new(None));
Resource {
routes: Vec::new(),
endpoint: ResourceEndpoint::new(fref.clone()),
factory_ref: fref,
default: Rc::new(RefCell::new(None)),
}
}
}
impl Default for Resource {
fn default() -> Self {
Self::new()
}
}
impl
where
T: NewService<
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
{
/// Register a new route.
///
/// ```rust
/// use actix_web::{web, guard, App, HttpResponse};
///
/// fn main() {
/// let app = App::new()
/// .resource("/", |r| {
/// r.route(web::route()
/// .guard(guard::Any(guard::Get()).or(guard::Put()))
/// .guard(guard::Header("Content-Type", "text/plain"))
/// .to(|| HttpResponse::Ok()))
/// });
/// }
/// ```
///
/// Multiple routes could be added to a resource.
///
/// ```rust
/// use actix_web::{web, guard, App, HttpResponse};
///
/// fn main() {
/// let app = App::new()
/// .resource("/container/", |r| {
/// r.route(web::get().to(get_handler))
/// .route(web::post().to(post_handler))
/// .route(web::delete().to(delete_handler))
/// });
/// }
/// # fn get_handler() {}
/// # fn post_handler() {}
/// # fn delete_handler() {}
/// ```
pub fn route(mut self, route: Route ) -> Self {
self.routes.push(route.finish());
self
}
/// Register a new route and add handler. This route get called for all
/// requests.
///
/// ```rust
/// use actix_web::*;
///
/// fn index(req: HttpRequest) -> HttpResponse {
/// unimplemented!()
/// }
///
/// App::new().resource("/", |r| r.to(index));
/// ```
///
/// This is shortcut for:
///
/// ```rust
/// # extern crate actix_web;
/// # use actix_web::*;
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() }
/// App::new().resource("/", |r| r.route(web::route().to(index)));
/// ```
pub fn to + 'static,
R: Responder + 'static,
{
self.routes.push(Route::new().to(handler));
self
}
/// Register a new route and add async handler.
///
/// ```rust
/// use actix_web::*;
/// use futures::future::{ok, Future};
///
/// fn index(req: HttpRequest) -> impl Future + 'static,
R: IntoFuture + 'static,
R::Item: Into ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
>
where
M: Transform<
T::Service,
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
F: IntoTransform ) -> R,
R: IntoNewService,
U: NewService<
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
> + 'static,
{
// create and configure default resource
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service(
f(Resource::new()).into_new_service().map_init_err(|_| ()),
)))));
self
}
pub(crate) fn get_default(&self) -> Rc IntoNewService
where
T: NewService<
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
{
fn into_new_service(self) -> T {
*self.factory_ref.borrow_mut() = Some(ResourceFactory {
routes: self.routes,
default: self.default,
});
self.endpoint
}
}
pub struct ResourceFactory {
routes: Vec {
type Request = ServiceRequest ;
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = ResourceService ;
type Future = CreateResourceService ;
fn new_service(&self, _: &()) -> Self::Future {
let default_fut = if let Some(ref default) = *self.default.borrow() {
Some(default.new_service(&()))
} else {
None
};
CreateResourceService {
fut: self
.routes
.iter()
.map(|route| CreateRouteServiceItem::Future(route.new_service(&())))
.collect(),
default: None,
default_fut,
}
}
}
enum CreateRouteServiceItem {
Future(CreateRouteService ),
Service(RouteService ),
}
pub struct CreateResourceService {
fut: Vec Future for CreateResourceService {
type Item = ResourceService ;
type Error = ();
fn poll(&mut self) -> Poll {
routes: Vec Service for ResourceService {
type Request = ServiceRequest ;
type Response = ServiceResponse;
type Error = ();
type Future = Either<
Box ) -> Self::Future {
for route in self.routes.iter_mut() {
if route.check(&mut req) {
return Either::A(route.call(req));
}
}
if let Some(ref mut default) = self.default {
Either::B(Either::A(default.call(req)))
} else {
let req = req.into_request();
Either::B(Either::B(ok(ServiceResponse::new(
req,
Response::NotFound().finish(),
))))
}
}
}
#[doc(hidden)]
pub struct ResourceEndpoint {
factory: Rc ResourceEndpoint {
fn new(factory: Rc {
type Request = ServiceRequest ;
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = ResourceService ;
type Future = CreateResourceService ;
fn new_service(&self, _: &()) -> Self::Future {
self.factory.borrow_mut().as_mut().unwrap().new_service(&())
}
}