From 56538ebd9117402d2c8e69a67a08b4ea8b5e660f Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Mon, 25 Nov 2019 21:41:54 +0100 Subject: [PATCH] Improve verbose errors for socket addresses Moves the point of adding error context to the net::addr module so that we have access to the raw address input and can include it in the error message. --- src/net/addr.rs | 32 +++++++++++++++++++++++++++----- src/net/tcp/listener.rs | 4 +--- src/net/tcp/stream.rs | 3 +-- src/net/udp/mod.rs | 3 +-- tests/verbose_errors.rs | 17 ++++++++++++++++- 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/net/addr.rs b/src/net/addr.rs index 2769dd5e..ea839500 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -1,11 +1,12 @@ +use std::future::Future; use std::mem; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; -use std::future::Future; use crate::io; use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; +use crate::utils::Context as ErrorContext; cfg_not_docs! { macro_rules! ret { @@ -67,6 +68,18 @@ pub enum ToSocketAddrsFuture { Done, } +/// Wrap `std::io::Error` with additional message +/// +/// Keeps the original error kind and stores the original I/O error as `source`. +impl ErrorContext for ToSocketAddrsFuture { + fn context(self, message: impl Fn() -> String) -> Self { + match self { + ToSocketAddrsFuture::Ready(res) => ToSocketAddrsFuture::Ready(res.context(message)), + x => x, + } + } +} + impl> Future for ToSocketAddrsFuture { type Output = io::Result; @@ -110,7 +123,9 @@ impl ToSocketAddrs for SocketAddrV4 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V4(*self).to_socket_addrs() + SocketAddr::V4(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -123,7 +138,9 @@ impl ToSocketAddrs for SocketAddrV6 { impl Future, ToSocketAddrsFuture ) { - SocketAddr::V6(*self).to_socket_addrs() + SocketAddr::V6(*self) + .to_socket_addrs() + .context(|| format!("could not resolve address `{}`", self)) } } @@ -195,7 +212,9 @@ impl ToSocketAddrs for (&str, u16) { let host = host.to_string(); let task = spawn_blocking(move || { - std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) + let addr = (host.as_str(), port); + std::net::ToSocketAddrs::to_socket_addrs(&addr) + .context(|| format!("could not resolve address `{:?}`", addr)) }); ToSocketAddrsFuture::Resolving(task) } @@ -215,7 +234,10 @@ impl ToSocketAddrs for str { } let addr = self.to_string(); - let task = spawn_blocking(move || std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())); + let task = spawn_blocking(move || { + std::net::ToSocketAddrs::to_socket_addrs(addr.as_str()) + .context(|| format!("could not resolve address `{:?}`", addr)) + }); ToSocketAddrsFuture::Resolving(task) } } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 9d944f4b..fe06a96d 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -8,7 +8,6 @@ use crate::net::driver::Watcher; use crate::net::{TcpStream, ToSocketAddrs}; use crate::stream::Stream; use crate::task::{Context, Poll}; -use crate::utils::Context as _; /// A TCP socket server, listening for connections. /// @@ -78,8 +77,7 @@ impl TcpListener { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { match mio::net::TcpListener::bind(&addr) { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 9d3c2ecd..41317833 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -74,8 +74,7 @@ impl TcpStream { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { let res = spawn_blocking(move || { diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index a1ca03f3..7fef1ed5 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -71,8 +71,7 @@ impl UdpSocket { let mut last_err = None; let addrs = addrs .to_socket_addrs() - .await - .context(|| String::from("could not resolve addresses"))?; + .await?; for addr in addrs { match mio::net::UdpSocket::bind(&addr) { diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs index 207d0b27..15630928 100644 --- a/tests/verbose_errors.rs +++ b/tests/verbose_errors.rs @@ -1,4 +1,4 @@ -use async_std::{fs, task}; +use async_std::{fs, io, net::ToSocketAddrs, task}; #[test] fn open_file() { @@ -14,3 +14,18 @@ fn open_file() { } }) } + +#[test] +fn resolve_address() { + task::block_on(async { + let non_existing_addr = "ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80"; + let res: Result<_, io::Error> = non_existing_addr.to_socket_addrs().await; + match res { + Ok(_) => panic!("Found address with random name: We live in a simulation"), + Err(e) => assert_eq!( + "could not resolve address `\"ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80\"`", + &format!("{}", e) + ), + } + }) +}