use std::rc::Rc; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::h1::{Codec, SendResponse}; use actix_http::{Error, Request, Response}; use actix_router::{Path, Router, Url}; use actix_server_config::ServerConfig; use actix_service::{IntoNewService, NewService, Service}; use futures::{Async, Future, Poll}; use crate::helpers::{BoxedHttpNewService, BoxedHttpService, HttpNewService}; use crate::request::FramedRequest; use crate::state::State; type BoxedResponse = Box>; pub trait HttpServiceFactory { type Factory: NewService; fn path(&self) -> &str; fn create(self) -> Self::Factory; } /// Application builder pub struct FramedApp { state: State, services: Vec<(String, BoxedHttpNewService>)>, } impl FramedApp { pub fn new() -> Self { FramedApp { state: State::new(()), services: Vec::new(), } } } impl FramedApp { pub fn with(state: S) -> FramedApp { FramedApp { services: Vec::new(), state: State::new(state), } } pub fn service(mut self, factory: U) -> Self where U: HttpServiceFactory, U::Factory: NewService< Config = (), Request = FramedRequest, Response = (), Error = Error, InitError = (), > + 'static, ::Future: 'static, ::Service: Service< Request = FramedRequest, Response = (), Error = Error, Future = Box>, >, { let path = factory.path().to_string(); self.services .push((path, Box::new(HttpNewService::new(factory.create())))); self } } impl IntoNewService> for FramedApp where T: AsyncRead + AsyncWrite + 'static, S: 'static, { fn into_new_service(self) -> FramedAppFactory { FramedAppFactory { state: self.state, services: Rc::new(self.services), } } } #[derive(Clone)] pub struct FramedAppFactory { state: State, services: Rc>)>>, } impl NewService for FramedAppFactory where T: AsyncRead + AsyncWrite + 'static, S: 'static, { type Config = ServerConfig; type Request = (Request, Framed); type Response = (); type Error = Error; type InitError = (); type Service = FramedAppService; type Future = CreateService; fn new_service(&self, _: &ServerConfig) -> Self::Future { CreateService { fut: self .services .iter() .map(|(path, service)| { CreateServiceItem::Future( Some(path.clone()), service.new_service(&()), ) }) .collect(), state: self.state.clone(), } } } #[doc(hidden)] pub struct CreateService { fut: Vec>, state: State, } enum CreateServiceItem { Future( Option, Box>, Error = ()>>, ), Service(String, BoxedHttpService>), } impl Future for CreateService where T: AsyncRead + AsyncWrite, { type Item = FramedAppService; type Error = (); fn poll(&mut self) -> Poll { let mut done = true; // poll http services for item in &mut self.fut { let res = match item { CreateServiceItem::Future(ref mut path, ref mut fut) => { match fut.poll()? { Async::Ready(service) => Some((path.take().unwrap(), service)), Async::NotReady => { done = false; None } } } CreateServiceItem::Service(_, _) => continue, }; if let Some((path, service)) = res { *item = CreateServiceItem::Service(path, service); } } if done { let router = self .fut .drain(..) .fold(Router::build(), |mut router, item| { match item { CreateServiceItem::Service(path, service) => { router.path(&path, service); } CreateServiceItem::Future(_, _) => unreachable!(), } router }); Ok(Async::Ready(FramedAppService { router: router.finish(), state: self.state.clone(), })) } else { Ok(Async::NotReady) } } } pub struct FramedAppService { state: State, router: Router>>, } impl Service for FramedAppService where T: AsyncRead + AsyncWrite, { type Request = (Request, Framed); type Response = (); type Error = Error; type Future = BoxedResponse; fn poll_ready(&mut self) -> Poll<(), Self::Error> { Ok(Async::Ready(())) } fn call(&mut self, (req, framed): (Request, Framed)) -> Self::Future { let mut path = Path::new(Url::new(req.uri().clone())); if let Some((srv, _info)) = self.router.recognize_mut(&mut path) { return srv.call(FramedRequest::new(req, framed, path, self.state.clone())); } Box::new( SendResponse::new(framed, Response::NotFound().finish()).then(|_| Ok(())), ) } }