-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Closed
differs/Legends
#5Labels
A-clientArea: client.Area: client.C-bugCategory: bug. Something is wrong. This is bad!Category: bug. Something is wrong. This is bad!E-easyEffort: easy. A task that would be a great starting point for a new contributor.Effort: easy. A task that would be a great starting point for a new contributor.
Description
This example uses reqwest
for simplicity:
#[tokio::main]
async fn main() -> () {
let builder = reqwest::ClientBuilder::new();
let builder = builder.local_address(std::net::IpAddr::from([0, 0, 0, 0]));
let client = builder.build().unwrap();
match client.get("http://ipv6.google.com").send().await {
Ok(r) => {
println!("Response: {:?}", r);
}
Err(e) => {
println!("Error: {:?}", e);
}
}
}
Error message:
thread 'main' panicked at 'missing connect error', /home/alexwl/.cargo/registry/src/github.202132.xyz-1ecc6299db9ec823/hyper-0.13.8/src/client/connect/http.rs:544:13
IPv6-only domains (only AAAA
DNS records, not A
) like ipv6.google.com
, are rare, but they do exist in the wild.
The problem is that split_by_preference
removes IPv6 addresses from IpAddrs
when the local_addr
is IPv4:
hyper/src/client/connect/dns.rs
Lines 203 to 225 in 523d66a
pub(super) fn split_by_preference(self, local_addr: Option<IpAddr>) -> (IpAddrs, IpAddrs) { | |
if let Some(local_addr) = local_addr { | |
let preferred = self | |
.iter | |
.filter(|addr| addr.is_ipv6() == local_addr.is_ipv6()) | |
.collect(); | |
(IpAddrs::new(preferred), IpAddrs::new(vec![])) | |
} else { | |
let preferring_v6 = self | |
.iter | |
.as_slice() | |
.first() | |
.map(SocketAddr::is_ipv6) | |
.unwrap_or(false); | |
let (preferred, fallback) = self | |
.iter | |
.partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6); | |
(IpAddrs::new(preferred), IpAddrs::new(fallback)) | |
} | |
} |
connect
panics when the self.addrs
is empty and the err
is None
:
hyper/src/client/connect/http.rs
Lines 524 to 545 in 523d66a
async fn connect( | |
&mut self, | |
local_addr: &Option<IpAddr>, | |
reuse_address: bool, | |
) -> io::Result<TcpStream> { | |
let mut err = None; | |
for addr in &mut self.addrs { | |
debug!("connecting to {}", addr); | |
match connect(&addr, local_addr, reuse_address, self.connect_timeout)?.await { | |
Ok(tcp) => { | |
debug!("connected to {}", addr); | |
return Ok(tcp); | |
} | |
Err(e) => { | |
trace!("connect error for {}: {:?}", addr, e); | |
err = Some(e); | |
} | |
} | |
} | |
Err(err.take().expect("missing connect error")) | |
} |
I think connect
should return an error instead of panicking when the err
is None
.
For example:
match err {
Some(e) => Err(e),
None => Err(std::io::Error::new(
std::io::ErrorKind::NotConnected,
"Network unreachable"
))
}
Metadata
Metadata
Assignees
Labels
A-clientArea: client.Area: client.C-bugCategory: bug. Something is wrong. This is bad!Category: bug. Something is wrong. This is bad!E-easyEffort: easy. A task that would be a great starting point for a new contributor.Effort: easy. A task that would be a great starting point for a new contributor.