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)]
12pub struct Connector<T>(marker::PhantomData<T>);
14
15#[derive(Clone, Debug)]
16pub struct ConnectorService<T> {
18 cfg: Cfg<IoConfig>,
19 shared: SharedCfg,
20 _t: marker::PhantomData<T>,
21}
22
23impl<T> Connector<T> {
24 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 pub fn new() -> Self {
40 ConnectorService::with(SharedCfg::default())
41 }
42
43 #[inline]
44 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 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 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)]
124pub struct Connector2<T>(marker::PhantomData<T>);
126
127#[derive(Clone, Debug)]
128pub struct ConnectorService2<T> {
130 cfg: Cfg<IoConfig>,
131 shared: SharedCfg,
132 _t: marker::PhantomData<T>,
133}
134
135impl<T> Connector2<T> {
136 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 pub fn new() -> Self {
152 ConnectorService2::with(SharedCfg::default())
153 }
154
155 #[inline]
156 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 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 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
232async 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}