|
use std::error::Error; |
|
use std::io::ErrorKind; |
|
|
|
#[tokio::main] |
|
async fn main() -> Result<(), Box<dyn Error>> { |
|
let domain = "httpbin.org"; |
|
let addrs = ["127.0.0.1:9000".parse().unwrap()]; |
|
let client = reqwest::ClientBuilder::new(); |
|
let res = client |
|
.resolve_to_addrs(domain, &addrs) |
|
.build()? |
|
.post("http://wrong.host.badssl.com") |
|
// .post("http://httpbin.org/post") |
|
//.post("http://asdfasdfawefasdfhttpbin.org/post") |
|
.send() |
|
.await |
|
.inspect_err(|err| println!("relevant: {:?}", is_network_error(err))) |
|
.inspect_err(|err| println!("error source: {:?}", err.source().unwrap().source()))?; |
|
|
|
println!("{res:?}"); |
|
|
|
Ok(()) |
|
} |
|
const MAX_ERR_SOURCE_ITERATIONS: usize = 4; |
|
|
|
/// only if there was a network issue should we consider updating the host info |
|
pub(crate) fn is_network_error(err: &reqwest::Error) -> bool { |
|
if err.is_timeout() { |
|
return true; |
|
} |
|
|
|
#[cfg(not(target_arch = "wasm32"))] |
|
if !(err.is_connect() || err.is_request()) { |
|
return false; |
|
} |
|
|
|
// The io::Error source is several layers deep, for clarity this is done as a loop |
|
// * reqwest::Error -> hyper_util::Error |
|
// * hyper_util::Error -> hyper_util::ClientError |
|
// * hyper_util::ClientError -> io::Error |
|
let mut inner = err.source(); |
|
for _ in 0..MAX_ERR_SOURCE_ITERATIONS { |
|
match inner { |
|
None => break, |
|
Some(e) => { |
|
// try downcast to io::Error from <dyn std::error:Error> |
|
if let Some(io_err) = e.downcast_ref::<std::io::Error>() { |
|
match io_err.kind() { |
|
// device not connected to the internet |
|
ErrorKind::NetworkUnreachable | ErrorKind::NetworkDown => return false, |
|
// connection errors can indicate connection interference |
|
ErrorKind::ConnectionReset |
|
| ErrorKind::HostUnreachable |
|
| ErrorKind::ConnectionRefused => return true, |
|
// TLS errors get wrapped in custom io::Errors |
|
ErrorKind::Other | ErrorKind::InvalidData => { |
|
// io::Error get_ref works while source doesn't here -_- |
|
// if you don't like it take it up with the rust devs https://users.rust-lang.org/t/question-about-implementation-of-std-source/121117 |
|
inner = io_err.get_ref().map(|e| e as &dyn std::error::Error); |
|
} |
|
_ => return false, |
|
} |
|
} else if let Some(_tls_err) = e.downcast_ref::<rustls::Error>() { |
|
return true; |
|
} else { |
|
inner = e.source(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
false |
|
} |