krabka_raft/connection_limiter/
overrides.rs1use {
14 std::{
15 collections::{BTreeMap, HashMap},
16 net::IpAddr,
17 sync::{Arc, Mutex, MutexGuard, PoisonError},
18 },
19 tokio::net,
20 tracing::warn,
21};
22
23pub type IpOverrides = Arc<HashMap<IpAddr, usize>>;
25
26#[derive(Debug, Default)]
29struct State {
30 applied: BTreeMap<String, usize>,
31 pending: Option<BTreeMap<String, usize>>,
32 addresses: IpOverrides,
33}
34
35#[derive(Debug, Default)]
37pub(super) struct OverrideAddresses {
38 state: Mutex<State>,
39}
40
41fn literals(hosts: &BTreeMap<String, usize>) -> Option<HashMap<IpAddr, usize>> {
43 hosts
44 .iter()
45 .map(|(host, cap)| Some((host.parse::<IpAddr>().ok()?.to_canonical(), *cap)))
46 .collect()
47}
48
49async fn resolve_host(host: &str) -> Option<IpAddr> {
52 if let Ok(address) = host.parse::<IpAddr>() {
53 return Some(address.to_canonical());
54 }
55 net::lookup_host((host, 0))
56 .await
57 .ok()?
58 .next()
59 .map(|address| address.ip().to_canonical())
60}
61
62impl OverrideAddresses {
63 fn state(&self) -> MutexGuard<'_, State> {
64 self.state.lock().unwrap_or_else(PoisonError::into_inner)
65 }
66
67 pub(super) fn addresses(self: &Arc<Self>, hosts: &BTreeMap<String, usize>) -> IpOverrides {
72 let mut state = self.state();
73 if state.applied == *hosts || state.pending.as_ref() == Some(hosts) {
74 return Arc::clone(&state.addresses);
75 }
76 if let Some(addresses) = literals(hosts) {
77 state.applied.clone_from(hosts);
78 state.pending = None;
79 state.addresses = Arc::new(addresses);
80 return Arc::clone(&state.addresses);
81 }
82 state.pending = Some(hosts.clone());
83 let this = Arc::clone(self);
84 let hosts = hosts.clone();
85 tokio::spawn(async move { this.resolve(hosts).await });
86 Arc::clone(&state.addresses)
87 }
88
89 async fn resolve(&self, hosts: BTreeMap<String, usize>) {
92 let mut addresses = HashMap::with_capacity(hosts.len());
93 let mut unresolved = Vec::new();
94 for (host, cap) in &hosts {
95 match resolve_host(host).await {
96 Some(address) => {
97 addresses.insert(address, *cap);
98 }
99 None => unresolved.push(host.clone()),
100 }
101 }
102 let mut state = self.state();
103 if state.pending.as_ref() != Some(&hosts) {
104 return;
105 }
106 state.pending = None;
107 state.applied = hosts;
108 if unresolved.is_empty() {
109 state.addresses = Arc::new(addresses);
110 return;
111 }
112 drop(state);
113 warn!(
114 ?unresolved,
115 "max.connections.per.ip.overrides names hosts that do not resolve; keeping the \
116 previous overrides"
117 );
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use {
124 super::*,
125 assert2::assert,
126 std::time::{Duration, Instant},
127 tokio::time,
128 };
129
130 fn hosts(entries: &[(&str, usize)]) -> BTreeMap<String, usize> {
131 entries
132 .iter()
133 .map(|(host, cap)| ((*host).to_owned(), *cap))
134 .collect()
135 }
136
137 fn addresses(entries: &[(&str, usize)]) -> HashMap<IpAddr, usize> {
138 entries
139 .iter()
140 .map(|(address, cap)| (address.parse().unwrap(), *cap))
141 .collect()
142 }
143
144 async fn applied(cache: &Arc<OverrideAddresses>, hosts: &BTreeMap<String, usize>) {
146 let deadline = Instant::now() + Duration::from_secs(10);
147 while cache.state().applied != *hosts {
148 assert!(Instant::now() < deadline, "the lookup never finished");
149 time::sleep(Duration::from_millis(10)).await;
150 }
151 }
152
153 #[tokio::test]
154 async fn literals_apply_at_once_and_a_v4_mapped_address_canonicalises() {
155 let cache = Arc::new(OverrideAddresses::default());
156 let got = cache.addresses(&hosts(&[
157 ("127.0.0.2", 3),
158 ("::ffff:10.0.0.1", 1),
159 ("::1", 2),
160 ]));
161 assert!(*got == addresses(&[("127.0.0.2", 3), ("10.0.0.1", 1), ("::1", 2)]));
162 }
163
164 #[tokio::test]
165 async fn a_host_name_resolves_in_the_background_and_the_previous_set_holds_meanwhile() {
166 let cache = Arc::new(OverrideAddresses::default());
167 let previous = cache.addresses(&hosts(&[("127.0.0.2", 3)]));
168 let named = hosts(&[("localhost", 4)]);
169 assert!(cache.addresses(&named) == previous);
170 applied(&cache, &named).await;
171 let got = cache.addresses(&named);
172 assert!(got.values().copied().collect::<Vec<_>>() == vec![4]);
173 assert!(got.keys().all(IpAddr::is_loopback));
174 }
175
176 #[tokio::test]
177 async fn an_unresolvable_set_keeps_the_previous_addresses() {
178 let cache = Arc::new(OverrideAddresses::default());
179 let previous = cache.addresses(&hosts(&[("127.0.0.2", 3)]));
180 let unresolvable = hosts(&[("no-such-host.invalid", 1)]);
181 drop(cache.addresses(&unresolvable));
182 applied(&cache, &unresolvable).await;
183 assert!(cache.addresses(&unresolvable) == previous);
184 }
185}