Skip to main content

ntex_net/connect/
service.rs

1use std::{collections::VecDeque, future::Future, io, marker, net::SocketAddr};
2
3use ntex_error::Error;
4use ntex_io::{Io, IoConfig, types};
5use ntex_service::cfg::{Cfg, SharedCfg};
6use ntex_service::{Service, ServiceCtx, ServiceFactory};
7use ntex_util::{future::Either, future::Ready, time::timeout_checked};
8
9use super::{Address, Connect, ConnectError, ConnectServiceError, resolve};
10
11#[derive(Copy, Clone, Debug)]
12/// Basic tcp stream connector
13pub struct Connector<T>(marker::PhantomData<T>);
14
15#[derive(Clone, Debug)]
16/// Basic tcp stream connector
17pub struct ConnectorService<T> {
18    cfg: Cfg<IoConfig>,
19    shared: SharedCfg,
20    _t: marker::PhantomData<T>,
21}
22
23impl<T> Connector<T> {
24    /// Construct new connect service with default configuration
25    pub fn new() -> Self {
26        Connector(marker::PhantomData)
27    }
28}
29
30impl<T> Default for Connector<T> {
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36impl<T> ConnectorService<T> {
37    #[inline]
38    /// Construct new connect service with default configuration
39    pub fn new() -> Self {
40        ConnectorService::with(SharedCfg::default())
41    }
42
43    #[inline]
44    /// Construct new connect service with custom configuration
45    pub fn with(cfg: SharedCfg) -> Self {
46        ConnectorService {
47            cfg: cfg.get(),
48            shared: cfg,
49            _t: marker::PhantomData,
50        }
51    }
52}
53
54impl<T> Default for ConnectorService<T> {
55    fn default() -> Self {
56        ConnectorService::new()
57    }
58}
59
60impl<T: Address> ConnectorService<T> {
61    /// Resolve and connect to remote host
62    pub async fn connect<U>(&self, message: U) -> Result<Io, ConnectError>
63    where
64        Connect<T>: From<U>,
65    {
66        timeout_checked(self.cfg.connect_timeout(), async {
67            // resolve first
68            let msg = resolve::lookup(message.into(), self.shared.tag())
69                .await
70                .map_err(Error::into_error)?;
71
72            let port = msg.port();
73            let Connect { req, addr, .. } = msg;
74
75            if let Some(addr) = addr {
76                connect(req, port, addr, self.shared.clone())
77                    .await
78                    .map_err(Error::into_error)
79            } else if let Some(addr) = req.addr() {
80                connect(req, addr.port(), Either::Left(addr), self.shared.clone())
81                    .await
82                    .map_err(Error::into_error)
83            } else {
84                log::error!("{}: TCP connector: got unresolved address", self.cfg.tag());
85                Err(ConnectError::Unresolved)
86            }
87        })
88        .await
89        .map_err(|()| {
90            ConnectError::Io(io::Error::new(io::ErrorKind::TimedOut, "Connect timeout"))
91        })
92        .and_then(|item| item)
93    }
94}
95
96impl<T: Address> ServiceFactory<Connect<T>, SharedCfg> for Connector<T> {
97    type Response = Io;
98    type Error = ConnectError;
99    type Service = ConnectorService<T>;
100    type InitError = ConnectServiceError;
101
102    fn create(
103        &self,
104        cfg: SharedCfg,
105    ) -> impl Future<Output = Result<Self::Service, Self::InitError>> {
106        Ready::Ok(ConnectorService::with(cfg))
107    }
108}
109
110impl<T: Address> Service<Connect<T>> for ConnectorService<T> {
111    type Response = Io;
112    type Error = ConnectError;
113
114    async fn call(
115        &self,
116        req: Connect<T>,
117        _: ServiceCtx<'_, Self>,
118    ) -> Result<Self::Response, Self::Error> {
119        self.connect(req).await
120    }
121}
122
123#[derive(Copy, Clone, Debug)]
124/// Basic tcp stream connector
125pub struct Connector2<T>(marker::PhantomData<T>);
126
127#[derive(Clone, Debug)]
128/// Basic tcp stream connector
129pub struct ConnectorService2<T> {
130    cfg: Cfg<IoConfig>,
131    shared: SharedCfg,
132    _t: marker::PhantomData<T>,
133}
134
135impl<T> Connector2<T> {
136    /// Construct new connect service with default configuration
137    pub fn new() -> Self {
138        Connector2(marker::PhantomData)
139    }
140}
141
142impl<T> Default for Connector2<T> {
143    fn default() -> Self {
144        Self::new()
145    }
146}
147
148impl<T> ConnectorService2<T> {
149    #[inline]
150    /// Construct new connect service with default configuration
151    pub fn new() -> Self {
152        ConnectorService2::with(SharedCfg::default())
153    }
154
155    #[inline]
156    /// Construct new connect service with custom configuration
157    pub fn with(cfg: SharedCfg) -> Self {
158        ConnectorService2 {
159            cfg: cfg.get(),
160            shared: cfg,
161            _t: marker::PhantomData,
162        }
163    }
164}
165
166impl<T> Default for ConnectorService2<T> {
167    fn default() -> Self {
168        ConnectorService2::new()
169    }
170}
171
172impl<T: Address> ConnectorService2<T> {
173    /// Resolve and connect to remote host
174    pub async fn connect<U>(&self, message: U) -> Result<Io, Error<ConnectError>>
175    where
176        Connect<T>: From<U>,
177    {
178        timeout_checked(self.cfg.connect_timeout(), async {
179            // resolve first
180            let msg = resolve::lookup(message.into(), self.shared.tag()).await?;
181
182            let port = msg.port();
183            let Connect { req, addr, .. } = msg;
184
185            if let Some(addr) = addr {
186                connect(req, port, addr, self.shared.clone()).await
187            } else if let Some(addr) = req.addr() {
188                connect(req, addr.port(), Either::Left(addr), self.shared.clone()).await
189            } else {
190                Err(Error::from(ConnectError::Unresolved))
191            }
192        })
193        .await
194        .map_err(|()| {
195            Error::from(ConnectError::Io(io::Error::new(
196                io::ErrorKind::TimedOut,
197                "Connect timeout",
198            )))
199        })
200        .and_then(|item| item)
201        .map_err(|e| e.set_service(self.shared.service()))
202    }
203}
204
205impl<T: Address> ServiceFactory<Connect<T>, SharedCfg> for Connector2<T> {
206    type Response = Io;
207    type Error = Error<ConnectError>;
208    type Service = ConnectorService2<T>;
209    type InitError = ConnectServiceError;
210
211    fn create(
212        &self,
213        cfg: SharedCfg,
214    ) -> impl Future<Output = Result<Self::Service, Self::InitError>> {
215        Ready::Ok(ConnectorService2::with(cfg))
216    }
217}
218
219impl<T: Address> Service<Connect<T>> for ConnectorService2<T> {
220    type Response = Io;
221    type Error = Error<ConnectError>;
222
223    async fn call(
224        &self,
225        req: Connect<T>,
226        _: ServiceCtx<'_, Self>,
227    ) -> Result<Self::Response, Self::Error> {
228        self.connect(req).await
229    }
230}
231
232/// Tcp stream connector
233async fn connect<T: Address>(
234    req: T,
235    port: u16,
236    addr: Either<SocketAddr, VecDeque<SocketAddr>>,
237    cfg: SharedCfg,
238) -> Result<Io, Error<ConnectError>> {
239    log::trace!(
240        "{}: TCP connector - connecting to {:?} addr:{addr:?} port:{port}",
241        cfg.tag(),
242        req.host(),
243    );
244
245    let io = match addr {
246        Either::Left(addr) => crate::tcp_connect(addr, cfg.clone())
247            .await
248            .map_err(ConnectError::from)?,
249        Either::Right(mut addrs) => loop {
250            let addr = addrs.pop_front().unwrap();
251
252            match crate::tcp_connect(addr, cfg.clone()).await {
253                Ok(io) => break io,
254                Err(err) => {
255                    log::trace!(
256                        "{}: TCP connector - failed to connect to {:?} port: {port} err: {err:?}",
257                        cfg.tag(),
258                        req.host(),
259                    );
260                    if addrs.is_empty() {
261                        return Err(ConnectError::from(err).into());
262                    }
263                }
264            }
265        },
266    };
267
268    log::trace!(
269        "{}: TCP connector - successfully connected to {:?} - {:?}",
270        cfg.tag(),
271        req.host(),
272        io.query::<types::PeerAddr>().get()
273    );
274    Ok(io)
275}