Skip to content

Commit fb19f3a

Browse files
authored
feat(client): add HttpConnector::set_local_addresses to set both IPv6 and IPv4 local addrs (#2172)
Currently HttpConnector::set_local_address method accepts a single argument. Server might not support IPv6 or IPv4. Therefore, the only solution at the moment is to manually perform DNS resolution and pick appropriate local address family. This is inefficient, as leads to 2 DNS lookups per request. This commit allows specifying both IPv4 and IPv6, so connector can decide which one to use based on DNS resolution results.
1 parent 02732be commit fb19f3a

File tree

3 files changed

+211
-56
lines changed

3 files changed

+211
-56
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ tokio-util = { version = "0.3", features = ["codec"] }
5555
tower-util = "0.3"
5656
url = "1.0"
5757

58+
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dev-dependencies]
59+
pnet = "0.25.0"
60+
5861
[features]
5962
default = [
6063
"runtime",

src/client/connect/dns.rs

+49-27
Original file line numberDiff line numberDiff line change
@@ -200,27 +200,33 @@ impl IpAddrs {
200200
None
201201
}
202202

203-
pub(super) fn split_by_preference(self, local_addr: Option<IpAddr>) -> (IpAddrs, IpAddrs) {
204-
if let Some(local_addr) = local_addr {
205-
let preferred = self
206-
.iter
207-
.filter(|addr| addr.is_ipv6() == local_addr.is_ipv6())
208-
.collect();
209-
210-
(IpAddrs::new(preferred), IpAddrs::new(vec![]))
211-
} else {
212-
let preferring_v6 = self
213-
.iter
214-
.as_slice()
215-
.first()
216-
.map(SocketAddr::is_ipv6)
217-
.unwrap_or(false);
218-
219-
let (preferred, fallback) = self
220-
.iter
221-
.partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6);
222-
223-
(IpAddrs::new(preferred), IpAddrs::new(fallback))
203+
#[inline]
204+
fn filter(self, predicate: impl FnMut(&SocketAddr) -> bool) -> IpAddrs {
205+
IpAddrs::new(self.iter.filter(predicate).collect())
206+
}
207+
208+
pub(super) fn split_by_preference(
209+
self,
210+
local_addr_ipv4: Option<Ipv4Addr>,
211+
local_addr_ipv6: Option<Ipv6Addr>,
212+
) -> (IpAddrs, IpAddrs) {
213+
match (local_addr_ipv4, local_addr_ipv6) {
214+
(Some(_), None) => (self.filter(SocketAddr::is_ipv4), IpAddrs::new(vec![])),
215+
(None, Some(_)) => (self.filter(SocketAddr::is_ipv6), IpAddrs::new(vec![])),
216+
_ => {
217+
let preferring_v6 = self
218+
.iter
219+
.as_slice()
220+
.first()
221+
.map(SocketAddr::is_ipv6)
222+
.unwrap_or(false);
223+
224+
let (preferred, fallback) = self
225+
.iter
226+
.partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6);
227+
228+
(IpAddrs::new(preferred), IpAddrs::new(fallback))
229+
}
224230
}
225231
}
226232

@@ -355,34 +361,50 @@ mod tests {
355361

356362
#[test]
357363
fn test_ip_addrs_split_by_preference() {
358-
let v4_addr = (Ipv4Addr::new(127, 0, 0, 1), 80).into();
359-
let v6_addr = (Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 80).into();
364+
let ip_v4 = Ipv4Addr::new(127, 0, 0, 1);
365+
let ip_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
366+
let v4_addr = (ip_v4, 80).into();
367+
let v6_addr = (ip_v6, 80).into();
368+
369+
let (mut preferred, mut fallback) = IpAddrs {
370+
iter: vec![v4_addr, v6_addr].into_iter(),
371+
}
372+
.split_by_preference(None, None);
373+
assert!(preferred.next().unwrap().is_ipv4());
374+
assert!(fallback.next().unwrap().is_ipv6());
375+
376+
let (mut preferred, mut fallback) = IpAddrs {
377+
iter: vec![v6_addr, v4_addr].into_iter(),
378+
}
379+
.split_by_preference(None, None);
380+
assert!(preferred.next().unwrap().is_ipv6());
381+
assert!(fallback.next().unwrap().is_ipv4());
360382

361383
let (mut preferred, mut fallback) = IpAddrs {
362384
iter: vec![v4_addr, v6_addr].into_iter(),
363385
}
364-
.split_by_preference(None);
386+
.split_by_preference(Some(ip_v4), Some(ip_v6));
365387
assert!(preferred.next().unwrap().is_ipv4());
366388
assert!(fallback.next().unwrap().is_ipv6());
367389

368390
let (mut preferred, mut fallback) = IpAddrs {
369391
iter: vec![v6_addr, v4_addr].into_iter(),
370392
}
371-
.split_by_preference(None);
393+
.split_by_preference(Some(ip_v4), Some(ip_v6));
372394
assert!(preferred.next().unwrap().is_ipv6());
373395
assert!(fallback.next().unwrap().is_ipv4());
374396

375397
let (mut preferred, fallback) = IpAddrs {
376398
iter: vec![v4_addr, v6_addr].into_iter(),
377399
}
378-
.split_by_preference(Some(v4_addr.ip()));
400+
.split_by_preference(Some(ip_v4), None);
379401
assert!(preferred.next().unwrap().is_ipv4());
380402
assert!(fallback.is_empty());
381403

382404
let (mut preferred, fallback) = IpAddrs {
383405
iter: vec![v4_addr, v6_addr].into_iter(),
384406
}
385-
.split_by_preference(Some(v6_addr.ip()));
407+
.split_by_preference(None, Some(ip_v6));
386408
assert!(preferred.next().unwrap().is_ipv6());
387409
assert!(fallback.is_empty());
388410
}

0 commit comments

Comments
 (0)