use std::{ future::Future, pin::Pin, task::{Context, Poll}, }; use actix_rt::net::TcpStream; use actix_service::{Service, ServiceFactory}; use actix_utils::future::{ok, Ready}; use futures_core::ready; use super::{ error::ConnectError, resolver::{Resolver, ResolverService}, tcp::{TcpConnector, TcpConnectorService}, ConnectInfo, Connection, Host, }; /// Combined resolver and TCP connector service factory. /// /// Used to create [`ConnectorService`]s which receive connection information, resolve DNS if /// required, and return a TCP stream. #[derive(Clone, Default)] pub struct Connector { resolver: Resolver, } impl Connector { /// Constructs new connector factory with the given resolver. pub fn new(resolver: Resolver) -> Self { Connector { resolver } } /// Build connector service. pub fn service(&self) -> ConnectorService { ConnectorService { tcp: TcpConnector::default().service(), resolver: self.resolver.service(), } } } impl ServiceFactory> for Connector { type Response = Connection; type Error = ConnectError; type Config = (); type Service = ConnectorService; type InitError = (); type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { ok(self.service()) } } /// Combined resolver and TCP connector service. /// /// Service implementation receives connection information, resolves DNS if required, and returns /// a TCP stream. #[derive(Clone, Default)] pub struct ConnectorService { tcp: TcpConnectorService, resolver: ResolverService, } impl Service> for ConnectorService { type Response = Connection; type Error = ConnectError; type Future = ConnectServiceResponse; actix_service::always_ready!(); fn call(&self, req: ConnectInfo) -> Self::Future { ConnectServiceResponse { fut: ConnectFut::Resolve(self.resolver.call(req)), tcp: self.tcp, } } } /// Chains futures of resolve and connect steps. pub(crate) enum ConnectFut { Resolve(>>::Future), Connect(>>::Future), } /// Container for the intermediate states of [`ConnectFut`]. pub(crate) enum ConnectFutState { Resolved(ConnectInfo), Connected(Connection), } impl ConnectFut { fn poll_connect( &mut self, cx: &mut Context<'_>, ) -> Poll, ConnectError>> { match self { ConnectFut::Resolve(ref mut fut) => { Pin::new(fut).poll(cx).map_ok(ConnectFutState::Resolved) } ConnectFut::Connect(ref mut fut) => { Pin::new(fut).poll(cx).map_ok(ConnectFutState::Connected) } } } } pub struct ConnectServiceResponse { fut: ConnectFut, tcp: TcpConnectorService, } impl Future for ConnectServiceResponse { type Output = Result, ConnectError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { match ready!(self.fut.poll_connect(cx))? { ConnectFutState::Resolved(res) => { self.fut = ConnectFut::Connect(self.tcp.call(res)); } ConnectFutState::Connected(res) => return Poll::Ready(Ok(res)), } } } }