use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::Arc; #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] use crate::body::Body; #[cfg(feature = "server")] use crate::body::HttpBody; #[cfg(all(feature = "http2", feature = "server"))] use crate::proto::h2::server::H2Stream; use crate::rt::Executor; #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] use crate::server::server::{new_svc::NewSvcTask, Watcher}; #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] use crate::service::HttpService; #[cfg(feature = "server")] pub trait ConnStreamExec: Clone { fn execute_h2stream(&mut self, fut: H2Stream); } #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] pub trait NewSvcExec, E, W: Watcher>: Clone { fn execute_new_svc(&mut self, fut: NewSvcTask); } pub(crate) type BoxSendFuture = Pin + Send>>; // Either the user provides an executor for background tasks, or we use // `tokio::spawn`. #[derive(Clone)] pub enum Exec { Default, Executor(Arc + Send + Sync>), } // ===== impl Exec ===== impl Exec { pub(crate) fn execute(&self, fut: F) where F: Future + Send + 'static, { match *self { Exec::Default => { #[cfg(feature = "tcp")] { tokio::task::spawn(fut); } #[cfg(not(feature = "tcp"))] { // If no runtime, we need an executor! panic!("executor must be set") } } Exec::Executor(ref e) => { e.execute(Box::pin(fut)); } } } } impl fmt::Debug for Exec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Exec").finish() } } #[cfg(feature = "server")] impl ConnStreamExec for Exec where H2Stream: Future + Send + 'static, B: HttpBody, { fn execute_h2stream(&mut self, fut: H2Stream) { self.execute(fut) } } #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] impl NewSvcExec for Exec where NewSvcTask: Future + Send + 'static, S: HttpService, W: Watcher, { fn execute_new_svc(&mut self, fut: NewSvcTask) { self.execute(fut) } } // ==== impl Executor ===== #[cfg(feature = "server")] impl ConnStreamExec for E where E: Executor> + Clone, H2Stream: Future, B: HttpBody, { fn execute_h2stream(&mut self, fut: H2Stream) { self.execute(fut) } } #[cfg(all(feature = "server", any(feature = "http1", feature = "http2")))] impl NewSvcExec for E where E: Executor> + Clone, NewSvcTask: Future, S: HttpService, W: Watcher, { fn execute_new_svc(&mut self, fut: NewSvcTask) { self.execute(fut) } } // If http2 is not enable, we just have a stub here, so that the trait bounds // that *would* have been needed are still checked. Why? // // Because enabling `http2` shouldn't suddenly add new trait bounds that cause // a compilation error. #[cfg(not(feature = "http2"))] #[allow(missing_debug_implementations)] pub struct H2Stream(std::marker::PhantomData<(F, B)>); #[cfg(not(feature = "http2"))] impl Future for H2Stream where F: Future, E>>, B: crate::body::HttpBody, B::Error: Into>, E: Into>, { type Output = (); fn poll( self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>, ) -> std::task::Poll { unreachable!() } }