-
-
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.zerozr99.workers.dev-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.