use std::marker::PhantomData; use std::net; use actix_net::either::Either; use actix_net::server::{Server, ServiceFactory}; use actix_net::service::{NewService, NewServiceExt}; use super::acceptor::{AcceptorServiceFactory, AcceptorTimeout, TcpAcceptor}; use super::handler::{HttpHandler, IntoHttpHandler}; use super::service::HttpService; use super::settings::{ServerSettings, WorkerSettings}; use super::{IoStream, KeepAlive}; pub(crate) trait ServiceProvider { fn register( &self, server: Server, lst: net::TcpListener, host: Option, addr: net::SocketAddr, keep_alive: KeepAlive, client_timeout: usize, ) -> Server; } /// Utility type that builds complete http pipeline pub struct HttpServiceBuilder where F: Fn() -> H + Send + Clone, { factory: F, acceptor: A, pipeline: P, no_client_timer: bool, } impl HttpServiceBuilder where F: Fn() -> H + Send + Clone + 'static, H: IntoHttpHandler, A: AcceptorServiceFactory, P: HttpPipelineFactory, { /// Create http service builder pub fn new(factory: F, acceptor: A, pipeline: P) -> Self { Self { factory, pipeline, acceptor, no_client_timer: false, } } pub(crate) fn no_client_timer(mut self) -> Self { self.no_client_timer = true; self } /// Use different acceptor factory pub fn acceptor(self, acceptor: A1) -> HttpServiceBuilder where A1: AcceptorServiceFactory, { HttpServiceBuilder { acceptor, pipeline: self.pipeline, factory: self.factory.clone(), no_client_timer: self.no_client_timer, } } /// Use different pipeline factory pub fn pipeline(self, pipeline: P1) -> HttpServiceBuilder where P1: HttpPipelineFactory, { HttpServiceBuilder { pipeline, acceptor: self.acceptor, factory: self.factory.clone(), no_client_timer: self.no_client_timer, } } fn finish( &self, host: Option, addr: net::SocketAddr, keep_alive: KeepAlive, client_timeout: usize, ) -> impl ServiceFactory { let timeout = if self.no_client_timer { 0 } else { client_timeout }; let factory = self.factory.clone(); let pipeline = self.pipeline.clone(); let acceptor = self.acceptor.clone(); move || { let app = (factory)().into_handler(); let settings = WorkerSettings::new( app, keep_alive, timeout as u64, ServerSettings::new(Some(addr), &host, false), ); if timeout == 0 { Either::A(TcpAcceptor::new( settings.clone(), acceptor.create().and_then(pipeline.create(settings)), )) } else { Either::B(TcpAcceptor::new( settings.clone(), AcceptorTimeout::new(timeout, acceptor.create()) .map_err(|_| ()) .and_then(pipeline.create(settings)), )) } } } } impl Clone for HttpServiceBuilder where F: Fn() -> H + Send + Clone, H: IntoHttpHandler, A: AcceptorServiceFactory, P: HttpPipelineFactory, { fn clone(&self) -> Self { HttpServiceBuilder { factory: self.factory.clone(), acceptor: self.acceptor.clone(), pipeline: self.pipeline.clone(), no_client_timer: self.no_client_timer, } } } impl ServiceProvider for HttpServiceBuilder where F: Fn() -> H + Send + Clone + 'static, A: AcceptorServiceFactory, P: HttpPipelineFactory, H: IntoHttpHandler, { fn register( &self, server: Server, lst: net::TcpListener, host: Option, addr: net::SocketAddr, keep_alive: KeepAlive, client_timeout: usize, ) -> Server { server.listen2( "actix-web", lst, self.finish(host, addr, keep_alive, client_timeout), ) } } pub trait HttpPipelineFactory: Send + Clone + 'static { type Io: IoStream; type NewService: NewService< Request = Self::Io, Response = (), Error = (), InitError = (), >; fn create(&self, settings: WorkerSettings) -> Self::NewService; } impl HttpPipelineFactory for F where F: Fn(WorkerSettings) -> T + Send + Clone + 'static, T: NewService, T::Request: IoStream, H: HttpHandler, { type Io = T::Request; type NewService = T; fn create(&self, settings: WorkerSettings) -> T { (self)(settings) } } pub(crate) struct DefaultPipelineFactory { _t: PhantomData<(H, Io)>, } unsafe impl Send for DefaultPipelineFactory {} impl DefaultPipelineFactory where Io: IoStream + Send, H: HttpHandler + 'static, { pub fn new() -> Self { Self { _t: PhantomData } } } impl Clone for DefaultPipelineFactory where Io: IoStream, H: HttpHandler, { fn clone(&self) -> Self { Self { _t: PhantomData } } } impl HttpPipelineFactory for DefaultPipelineFactory where Io: IoStream, H: HttpHandler + 'static, { type Io = Io; type NewService = HttpService; fn create(&self, settings: WorkerSettings) -> Self::NewService { HttpService::new(settings) } }