tcp_connect/
lib.rs

1//! TCP connect is a ~ drop in replacment of the `std::net::TcpStream::connect` with for DNS caching.
2//!
3//! [TCPConnect] allows you to cache the DNS resolution result for a configured TTL, once cached
4//! subsequent calls will pick a random IP adddress from the original list of IP addresses
5//! returned, with the goal of distributing connections among all IP addresses.
6//!
7//! During cache miss stampede events [TCPConnect] will make sure that only one concurrent DNS
8//! resolution is allowed per host, queing other calls for the same host. This would reduce
9//! drastically the number of DNS resolutions.
10//!
11//! In the example below, a [`TCPConnect`] is [built][TCPConnect::builder] and used to
12//! further connect to any host using a DNS TTL of 60 seconds and a maximum stale time of 1 second.
13//! ```
14//!use std::time::Duration;
15//!use tcp_connect::*;
16//!use std::io::Write;
17//!
18//!fn main() {
19//!   let tcp_connect = TCPConnect::builder()
20//!       .dns_ttl(Duration::from_secs(60))
21//!       .dns_max_stale_time(Duration::from_secs(1))
22//!       .build();
23//!
24//!   tcp_connect.connect("localhost:80");
25//!}
26//! ```
27mod dns_cache;
28mod inner;
29mod resolver;
30mod wait_queue;
31use crate::dns_cache::InMemoryDNSCache;
32use crate::inner::TCPConnectShared;
33use crate::resolver::ToSocketAddrDNSResolver;
34use std::io;
35use std::net::TcpStream;
36use std::sync::Arc;
37use std::time::Duration;
38
39#[cfg(test)]
40mod test_utils;
41
42/// TCP stream connector with DNS caching.
43///
44/// This type is internally reference-counted and can be freely cloned.
45pub struct TCPConnect {
46    inner: Arc<TCPConnectShared<InMemoryDNSCache, ToSocketAddrDNSResolver>>,
47}
48
49impl TCPConnect {
50    /// Create a new builder to configure the [`TCPConnect`] instance.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use std::time::Duration;
56    /// use tcp_connect::TCPConnect;
57    ///
58    /// let comet = TCPConnect::builder()
59    ///     .dns_ttl(Duration::from_secs(60))
60    ///     .dns_max_stale_time(Duration::from_secs(1))
61    ///     .build();
62    /// ```
63    pub fn builder() -> TCPConnectBuilder {
64        TCPConnectBuilder::default()
65    }
66
67    // Connect to speciric address.
68    //
69    // Behind the scenes will make usage of the cached DNS resolution result.
70    pub fn connect(&self, addr: &str) -> io::Result<TcpStream> {
71        self.inner.connect(addr)
72    }
73}
74
75impl Clone for TCPConnect {
76    fn clone(&self) -> Self {
77        TCPConnect {
78            inner: self.inner.clone(),
79        }
80    }
81}
82
83/// This builder allows you to configure the [`TCPConnect`] instance.
84#[derive(Debug, Clone, Copy)]
85pub struct TCPConnectBuilder {
86    dns_ttl: Duration,
87    dns_max_stale_time: Duration,
88}
89
90impl TCPConnectBuilder {
91    /// Set the DNS TTL.
92    ///
93    /// By default, this is set to 60 seconds.
94    pub fn dns_ttl(mut self, dns_ttl: Duration) -> TCPConnectBuilder {
95        self.dns_ttl = dns_ttl;
96        self
97    }
98
99    /// Set the maximum time for returning a stale DNS resolution result.
100    ///
101    /// After the DNS TTL (Time To Live) expires, new requests can be served
102    /// stale data while a new resolution is performed in the background.
103    ///
104    /// By default, this is set to 1 second.
105    pub fn dns_max_stale_time(mut self, dns_max_stale_time: Duration) -> TCPConnectBuilder {
106        self.dns_max_stale_time = dns_max_stale_time;
107        self
108    }
109
110    /// Build the [`TCPConnect`] instance.
111    pub fn build(self) -> TCPConnect {
112        let inner = Arc::new(TCPConnectShared::new(
113            Arc::new(InMemoryDNSCache::new(self.dns_ttl, self.dns_max_stale_time)),
114            Arc::new(ToSocketAddrDNSResolver::new()),
115        ));
116        TCPConnect { inner }
117    }
118}
119
120impl Default for TCPConnectBuilder {
121    fn default() -> Self {
122        TCPConnectBuilder {
123            dns_ttl: Duration::from_secs(60),
124            dns_max_stale_time: Duration::from_secs(1),
125        }
126    }
127}