ntex_net/connect/
resolve.rs1use std::{fmt, io, marker, net};
2
3use ntex_rt::spawn_blocking;
4use ntex_service::{Service, ServiceCtx, ServiceFactory};
5use ntex_util::future::Either;
6
7use super::{Address, Connect, ConnectError};
8
9pub struct Resolver<T>(marker::PhantomData<T>);
11
12impl<T> Resolver<T> {
13 pub fn new() -> Self {
15 Resolver(marker::PhantomData)
16 }
17}
18
19impl<T> Copy for Resolver<T> {}
20
21impl<T: Address> Resolver<T> {
22 pub async fn lookup(&self, req: Connect<T>) -> Result<Connect<T>, ConnectError> {
24 self.lookup_with_tag(req, "TCP-CLIENT").await
25 }
26
27 #[doc(hidden)]
28 pub async fn lookup_with_tag(
30 &self,
31 mut req: Connect<T>,
32 tag: &'static str,
33 ) -> Result<Connect<T>, ConnectError> {
34 if req.addr.is_some() || req.req.addr().is_some() {
35 Ok(req)
36 } else if let Ok(ip) = req.host().parse() {
37 req.addr = Some(Either::Left(net::SocketAddr::new(ip, req.port())));
38 Ok(req)
39 } else {
40 log::trace!("{}: DNS Resolver - resolving host {:?}", tag, req.host());
41
42 let host = if req.host().contains(':') {
43 req.host().to_string()
44 } else {
45 format!("{}:{}", req.host(), req.port())
46 };
47
48 let fut = spawn_blocking(move || net::ToSocketAddrs::to_socket_addrs(&host));
49 match fut.await {
50 Ok(Ok(ips)) => {
51 let port = req.port();
52 let req = req.set_addrs(ips.map(|mut ip| {
53 ip.set_port(port);
54 ip
55 }));
56
57 log::trace!(
58 "{}: DNS Resolver - host {:?} resolved to {:?}",
59 tag,
60 req.host(),
61 req.addrs()
62 );
63
64 if req.addr.is_none() {
65 Err(ConnectError::NoRecords)
66 } else {
67 Ok(req)
68 }
69 }
70 Ok(Err(e)) => {
71 log::trace!(
72 "{}: DNS Resolver - failed to resolve host {:?} err: {}",
73 tag,
74 req.host(),
75 e
76 );
77 Err(ConnectError::Resolver(e))
78 }
79 Err(e) => {
80 log::trace!(
81 "{}: DNS Resolver - failed to resolve host {:?} err: {}",
82 tag,
83 req.host(),
84 e
85 );
86 Err(ConnectError::Resolver(io::Error::new(
87 io::ErrorKind::Other,
88 e,
89 )))
90 }
91 }
92 }
93 }
94}
95
96impl<T> Default for Resolver<T> {
97 fn default() -> Resolver<T> {
98 Resolver::new()
99 }
100}
101
102impl<T> Clone for Resolver<T> {
103 fn clone(&self) -> Self {
104 *self
105 }
106}
107
108impl<T> fmt::Debug for Resolver<T> {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 f.debug_struct("Resolver").finish()
111 }
112}
113
114impl<T: Address, C> ServiceFactory<Connect<T>, C> for Resolver<T> {
115 type Response = Connect<T>;
116 type Error = ConnectError;
117 type Service = Resolver<T>;
118 type InitError = ();
119
120 async fn create(&self, _: C) -> Result<Self::Service, Self::InitError> {
121 Ok(*self)
122 }
123}
124
125impl<T: Address> Service<Connect<T>> for Resolver<T> {
126 type Response = Connect<T>;
127 type Error = ConnectError;
128
129 async fn call(
130 &self,
131 req: Connect<T>,
132 _: ServiceCtx<'_, Self>,
133 ) -> Result<Connect<T>, Self::Error> {
134 self.lookup(req).await
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use ntex_util::future::lazy;
142
143 #[allow(clippy::clone_on_copy)]
144 #[ntex::test]
145 async fn resolver() {
146 let resolver = Resolver::default().clone();
147 assert!(format!("{:?}", resolver).contains("Resolver"));
148 let srv = resolver.pipeline(()).await.unwrap().bind();
149 assert!(lazy(|cx| srv.poll_ready(cx)).await.is_ready());
150
151 let res = srv.call(Connect::new("www.rust-lang.org")).await;
152 assert!(res.is_ok());
153
154 let res = srv.call(Connect::new("---11213")).await;
155 assert!(res.is_err());
156
157 let addr: net::SocketAddr = "127.0.0.1:8080".parse().unwrap();
158 let res = srv
159 .call(Connect::new("www.rust-lang.org").set_addrs(vec![addr]))
160 .await
161 .unwrap();
162 let addrs: Vec<_> = res.addrs().collect();
163 assert_eq!(addrs.len(), 1);
164 assert!(addrs.contains(&addr));
165 }
166}