Skip to main content

actix_tls/connect/
info.rs

1//! Connection info struct.
2
3use std::{
4    collections::VecDeque,
5    fmt,
6    iter::{self, FromIterator as _},
7    mem,
8    net::{IpAddr, SocketAddr},
9};
10
11use super::{
12    connect_addrs::{ConnectAddrs, ConnectAddrsIter},
13    Host,
14};
15
16/// Connection request information.
17///
18/// May contain known/pre-resolved socket address(es) or a host that needs resolving with DNS.
19#[derive(Debug, PartialEq, Eq, Hash)]
20pub struct ConnectInfo<R> {
21    pub(crate) request: R,
22    pub(crate) port: u16,
23    pub(crate) addr: ConnectAddrs,
24    pub(crate) local_addr: Option<IpAddr>,
25}
26
27impl<R: Host> ConnectInfo<R> {
28    /// Constructs new connection info using a request.
29    pub fn new(request: R) -> ConnectInfo<R> {
30        let port = request.port();
31
32        ConnectInfo {
33            request,
34            port: port.unwrap_or(0),
35            addr: ConnectAddrs::None,
36            local_addr: None,
37        }
38    }
39
40    /// Constructs new connection info from request and known socket address.
41    ///
42    /// Since socket address is known, [`Connector`](super::Connector) will skip the DNS
43    /// resolution step.
44    pub fn with_addr(request: R, addr: SocketAddr) -> ConnectInfo<R> {
45        ConnectInfo {
46            request,
47            port: 0,
48            addr: ConnectAddrs::One(addr),
49            local_addr: None,
50        }
51    }
52
53    /// Set connection port.
54    ///
55    /// If request provided a port, this will override it.
56    pub fn set_port(mut self, port: u16) -> Self {
57        self.port = port;
58        self
59    }
60
61    /// Set connection socket address.
62    pub fn set_addr(mut self, addr: impl Into<Option<SocketAddr>>) -> Self {
63        self.addr = ConnectAddrs::from(addr.into());
64        self
65    }
66
67    /// Set list of addresses.
68    pub fn set_addrs<I>(mut self, addrs: I) -> Self
69    where
70        I: IntoIterator<Item = SocketAddr>,
71    {
72        let mut addrs = VecDeque::from_iter(addrs);
73        self.addr = if addrs.len() < 2 {
74            ConnectAddrs::from(addrs.pop_front())
75        } else {
76            ConnectAddrs::Multi(addrs)
77        };
78        self
79    }
80
81    /// Set local address to connection with.
82    ///
83    /// Useful in situations where the IP address bound to a particular network interface is known.
84    /// This would make sure the socket is opened through that interface.
85    pub fn set_local_addr(mut self, addr: impl Into<IpAddr>) -> Self {
86        self.local_addr = Some(addr.into());
87        self
88    }
89
90    /// Returns a reference to the connection request.
91    pub fn request(&self) -> &R {
92        &self.request
93    }
94
95    /// Returns request hostname.
96    pub fn hostname(&self) -> &str {
97        self.request.hostname()
98    }
99
100    /// Returns request port.
101    pub fn port(&self) -> u16 {
102        self.request.port().unwrap_or(self.port)
103    }
104
105    /// Get borrowed iterator of resolved request addresses.
106    ///
107    /// # Examples
108    /// ```
109    /// # use std::net::SocketAddr;
110    /// # use actix_tls::connect::ConnectInfo;
111    /// let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
112    ///
113    /// let conn = ConnectInfo::new("localhost");
114    /// let mut addrs = conn.addrs();
115    /// assert!(addrs.next().is_none());
116    ///
117    /// let conn = ConnectInfo::with_addr("localhost", addr);
118    /// let mut addrs = conn.addrs();
119    /// assert_eq!(addrs.next().unwrap(), addr);
120    /// ```
121    #[allow(clippy::implied_bounds_in_impls)]
122    pub fn addrs(
123        &self,
124    ) -> impl Iterator<Item = SocketAddr>
125           + ExactSizeIterator
126           + iter::FusedIterator
127           + Clone
128           + fmt::Debug
129           + '_ {
130        match self.addr {
131            ConnectAddrs::None => ConnectAddrsIter::None,
132            ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr),
133            ConnectAddrs::Multi(ref addrs) => ConnectAddrsIter::Multi(addrs.iter()),
134        }
135    }
136
137    /// Take owned iterator resolved request addresses.
138    ///
139    /// # Examples
140    /// ```
141    /// # use std::net::SocketAddr;
142    /// # use actix_tls::connect::ConnectInfo;
143    /// let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
144    ///
145    /// let mut conn = ConnectInfo::new("localhost");
146    /// let mut addrs = conn.take_addrs();
147    /// assert!(addrs.next().is_none());
148    ///
149    /// let mut conn = ConnectInfo::with_addr("localhost", addr);
150    /// let mut addrs = conn.take_addrs();
151    /// assert_eq!(addrs.next().unwrap(), addr);
152    /// ```
153    #[allow(clippy::implied_bounds_in_impls)]
154    pub fn take_addrs(
155        &mut self,
156    ) -> impl Iterator<Item = SocketAddr>
157           + ExactSizeIterator
158           + iter::FusedIterator
159           + Clone
160           + fmt::Debug
161           + 'static {
162        match mem::take(&mut self.addr) {
163            ConnectAddrs::None => ConnectAddrsIter::None,
164            ConnectAddrs::One(addr) => ConnectAddrsIter::One(addr),
165            ConnectAddrs::Multi(addrs) => ConnectAddrsIter::MultiOwned(addrs.into_iter()),
166        }
167    }
168}
169
170impl<R: Host> From<R> for ConnectInfo<R> {
171    fn from(addr: R) -> Self {
172        ConnectInfo::new(addr)
173    }
174}
175
176impl<R: Host> fmt::Display for ConnectInfo<R> {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        write!(f, "{}:{}", self.hostname(), self.port())
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use std::net::Ipv4Addr;
185
186    use super::*;
187
188    #[test]
189    fn test_addr_iter_multi() {
190        let localhost = SocketAddr::from((IpAddr::from(Ipv4Addr::LOCALHOST), 8080));
191        let unspecified = SocketAddr::from((IpAddr::from(Ipv4Addr::UNSPECIFIED), 8080));
192
193        let mut addrs = VecDeque::new();
194        addrs.push_back(localhost);
195        addrs.push_back(unspecified);
196
197        let mut iter = ConnectAddrsIter::Multi(addrs.iter());
198        assert_eq!(iter.next(), Some(localhost));
199        assert_eq!(iter.next(), Some(unspecified));
200        assert_eq!(iter.next(), None);
201
202        let mut iter = ConnectAddrsIter::MultiOwned(addrs.into_iter());
203        assert_eq!(iter.next(), Some(localhost));
204        assert_eq!(iter.next(), Some(unspecified));
205        assert_eq!(iter.next(), None);
206    }
207
208    #[test]
209    fn test_addr_iter_single() {
210        let localhost = SocketAddr::from((IpAddr::from(Ipv4Addr::LOCALHOST), 8080));
211
212        let mut iter = ConnectAddrsIter::One(localhost);
213        assert_eq!(iter.next(), Some(localhost));
214        assert_eq!(iter.next(), None);
215
216        let mut iter = ConnectAddrsIter::None;
217        assert_eq!(iter.next(), None);
218    }
219
220    #[test]
221    fn test_local_addr() {
222        let conn = ConnectInfo::new("hello").set_local_addr([127, 0, 0, 1]);
223        assert_eq!(
224            conn.local_addr.unwrap(),
225            IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
226        )
227    }
228
229    #[test]
230    fn request_ref() {
231        let conn = ConnectInfo::new("hello");
232        assert_eq!(conn.request(), &"hello")
233    }
234
235    #[test]
236    fn set_connect_addr_into_option() {
237        let addr = SocketAddr::from(([127, 0, 0, 1], 4242));
238
239        let conn = ConnectInfo::new("hello").set_addr(None);
240        let mut addrs = conn.addrs();
241        assert!(addrs.next().is_none());
242
243        let conn = ConnectInfo::new("hello").set_addr(addr);
244        let mut addrs = conn.addrs();
245        assert_eq!(addrs.next().unwrap(), addr);
246
247        let conn = ConnectInfo::new("hello").set_addr(Some(addr));
248        let mut addrs = conn.addrs();
249        assert_eq!(addrs.next().unwrap(), addr);
250    }
251}