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}