use std::cell::RefCell; use std::marker::PhantomData; use std::rc::Rc; use actix_http::body::{Body, MessageBody}; use actix_http::{Extensions, PayloadStream, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::{ AndThenNewService, ApplyNewService, IntoNewService, IntoNewTransform, NewService, NewTransform, Service, }; use futures::future::{ok, Either, FutureResult}; use futures::{Async, Future, IntoFuture, Poll}; use crate::resource::Resource; use crate::service::{ServiceRequest, ServiceResponse}; use crate::state::{State, StateFactory, StateFactoryResult}; type HttpService
= BoxedService = BoxedNewService<(), ServiceRequest , ServiceResponse, (), ()>;
type BoxedResponse = Box
where
T: NewService >,
{
chain: T,
extensions: Extensions,
state: Vec App
where
P: 'static,
T: NewService<
Request = ServiceRequest ,
Error = (),
InitError = (),
>,
{
/// Set application state. Applicatin state could be accessed
/// by using `State ) -> Resource ,
U: NewService<
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
> + 'static,
{
let rdef = ResourceDef::new(path);
let resource = f(Resource::new());
let default = resource.get_default();
let fref = Rc::new(RefCell::new(None));
AppRouter {
chain: self.chain,
services: vec![(rdef, boxed::new_service(resource.into_new_service()))],
default: None,
defaults: vec![default],
endpoint: AppEntry::new(fref.clone()),
factory_ref: fref,
extensions: self.extensions,
state: self.state,
_t: PhantomData,
}
}
/// Register a middleware.
pub fn middleware ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
>
where
M: NewTransform<
AppRouting ,
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
F: IntoNewTransform ,
Response = ServiceRequest )>,
default: Option ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
{
/// Configure resource for a specific path.
///
/// Resources may have variable path segments. For example, a
/// resource with the path `/a/{name}/c` would match all incoming
/// requests with paths such as `/a/b/c`, `/a/1/c`, or `/a/etc/c`.
///
/// A variable segment is specified in the form `{identifier}`,
/// where the identifier can be used later in a request handler to
/// access the matched value for that segment. This is done by
/// looking up the identifier in the `Params` object returned by
/// `HttpRequest.match_info()` method.
///
/// By default, each segment matches the regular expression `[^{}/]+`.
///
/// You can also specify a custom regex in the form `{identifier:regex}`:
///
/// For instance, to route `GET`-requests on any route matching
/// `/users/{userid}/{friend}` and store `userid` and `friend` in
/// the exposed `Params` object:
///
/// ```rust
/// use actix_web::{web, http, App, HttpResponse};
///
/// fn main() {
/// let app = App::new()
/// .resource("/users/{userid}/{friend}", |r| {
/// r.route(web::to(|| HttpResponse::Ok()))
/// })
/// .resource("/index.html", |r| {
/// r.route(web::head().to(|| HttpResponse::MethodNotAllowed()))
/// });
/// }
/// ```
pub fn resource ) -> Resource ,
U: NewService<
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
InitError = (),
> + 'static,
{
let rdef = ResourceDef::new(path);
let resource = f(Resource::new());
self.defaults.push(resource.get_default());
self.services
.push((rdef, boxed::new_service(resource.into_new_service())));
self
}
/// Default resource to be used if no matching route could be found.
///
/// Default resource works with resources only and does not work with
/// custom services.
pub fn default_resource ) -> R,
R: IntoNewService,
U: NewService<
Request = ServiceRequest ,
Response = ServiceResponse,
Error = (),
> + 'static,
{
// create and configure default resource
self.default = Some(Rc::new(boxed::new_service(
f(Resource::new()).into_new_service().map_init_err(|_| ()),
)));
self
}
/// Register resource handler service.
pub fn service ,
Response = ServiceResponse,
Error = (),
> + 'static,
{
self.services.push((
rdef.into(),
boxed::new_service(factory.into_new_service().map_init_err(|_| ())),
));
self
}
/// Register a middleware.
pub fn middleware ,
Response = ServiceResponse ,
Response = ServiceResponse ,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
C: NewService<
Request = ServiceRequest ,
Error = (),
InitError = (),
>,
{
fn into_new_service(self) -> AndThenNewService {
services: Rc {
type Request = ServiceRequest ;
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = AppRouting ;
type Future = AppRoutingFactoryResponse ;
fn new_service(&self, _: &()) -> Self::Future {
AppRoutingFactoryResponse {
fut: self
.services
.iter()
.map(|(path, service)| {
CreateAppRoutingItem::Future(
Some(path.clone()),
service.new_service(&()),
)
})
.collect(),
}
}
}
type HttpServiceFut = Box {
fut: Vec {
Future(Option ),
Service(ResourceDef, HttpService ),
}
impl Future for AppRoutingFactoryResponse {
type Item = AppRouting ;
type Error = ();
fn poll(&mut self) -> Poll {
router: Router , ResourceInfo)>,
}
impl Service for AppRouting {
type Request = ServiceRequest ;
type Response = ServiceResponse;
type Error = ();
type Future = Either ) -> Self::Future {
if let Some((srv, _info)) = self.router.recognize_mut(req.match_info_mut()) {
Either::A(srv.call(req))
} else {
let req = req.into_request();
Either::B(ok(ServiceResponse::new(req, Response::NotFound().finish())))
}
}
}
#[doc(hidden)]
/// Wrapper service for routing
pub struct AppEntry {
factory: Rc AppEntry {
fn new(factory: Rc {
type Request = ServiceRequest ;
type Response = ServiceResponse;
type Error = ();
type InitError = ();
type Service = AppRouting ;
type Future = AppRoutingFactoryResponse ;
fn new_service(&self, _: &()) -> Self::Future {
self.factory.borrow_mut().as_mut().unwrap().new_service(&())
}
}
#[doc(hidden)]
pub struct AppChain;
impl NewService<()> for AppChain {
type Request = ServiceRequest >,
{
chain: C,
state: Vec ,
InitError = (),
>,
{
type Request = Request ;
type Error = C::Error;
type InitError = C::InitError;
type Service = AppInitService ,
InitError = (),
>,
{
chain: C::Future,
state: Vec ,
InitError = (),
>,
{
type Item = AppInitService >,
{
chain: C,
extensions: Rc >,
{
type Request = Request ;
type Error = C::Error;
type Future = C::Future;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.chain.poll_ready()
}
fn call(&mut self, req: Request(mut self, state: F) -> Self
where
F: Fn() -> Out + 'static,
Out: IntoFuture + 'static,
Out::Error: std::fmt::Debug,
{
self.state.push(Box::new(State::new(state)));
self
}
/// Configure resource for a specific path.
///
/// Resources may have variable path segments. For example, a
/// resource with the path `/a/{name}/c` would match all incoming
/// requests with paths such as `/a/b/c`, `/a/1/c`, or `/a/etc/c`.
///
/// A variable segment is specified in the form `{identifier}`,
/// where the identifier can be used later in a request handler to
/// access the matched value for that segment. This is done by
/// looking up the identifier in the `Params` object returned by
/// `HttpRequest.match_info()` method.
///
/// By default, each segment matches the regular expression `[^{}/]+`.
///
/// You can also specify a custom regex in the form `{identifier:regex}`:
///
/// For instance, to route `GET`-requests on any route matching
/// `/users/{userid}/{friend}` and store `userid` and `friend` in
/// the exposed `Params` object:
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{web, http, App, HttpResponse};
///
/// fn main() {
/// let app = App::new().resource("/users/{userid}/{friend}", |r| {
/// r.route(web::get().to(|| HttpResponse::Ok()))
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
/// });
/// }
/// ```
pub fn resource`.
/// It also executes state factories.
pub struct AppInit`
pub struct AppInitService