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::other(e)))
87 }
88 }
89 }
90 }
91}
92
93impl<T> Default for Resolver<T> {
94 fn default() -> Resolver<T> {
95 Resolver::new()
96 }
97}
98
99impl<T> Clone for Resolver<T> {
100 fn clone(&self) -> Self {
101 *self
102 }
103}
104
105impl<T> fmt::Debug for Resolver<T> {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 f.debug_struct("Resolver").finish()
108 }
109}
110
111impl<T: Address, C> ServiceFactory<Connect<T>, C> for Resolver<T> {
112 type Response = Connect<T>;
113 type Error = ConnectError;
114 type Service = Resolver<T>;
115 type InitError = ();
116
117 async fn create(&self, _: C) -> Result<Self::Service, Self::InitError> {
118 Ok(*self)
119 }
120}
121
122impl<T: Address> Service<Connect<T>> for Resolver<T> {
123 type Response = Connect<T>;
124 type Error = ConnectError;
125
126 async fn call(
127 &self,
128 req: Connect<T>,
129 _: ServiceCtx<'_, Self>,
130 ) -> Result<Connect<T>, Self::Error> {
131 self.lookup(req).await
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use ntex_util::future::lazy;
139
140 #[allow(clippy::clone_on_copy)]
141 #[ntex::test]
142 async fn resolver() {
143 let resolver = Resolver::default().clone();
144 assert!(format!("{resolver:?}").contains("Resolver"));
145 let srv = resolver.pipeline(()).await.unwrap().bind();
146 assert!(lazy(|cx| srv.poll_ready(cx)).await.is_ready());
147
148 let res = srv.call(Connect::new("www.rust-lang.org")).await;
149 assert!(res.is_ok());
150
151 let res = srv.call(Connect::new("---11213")).await;
152 assert!(res.is_err());
153
154 let addr: net::SocketAddr = "127.0.0.1:8080".parse().unwrap();
155 let res = srv
156 .call(Connect::new("www.rust-lang.org").set_addrs(vec![addr]))
157 .await
158 .unwrap();
159 let addrs: Vec<_> = res.addrs().collect();
160 assert_eq!(addrs.len(), 1);
161 assert!(addrs.contains(&addr));
162 }
163}