//! UDP error classification shared by Dosh clients, servers, and embedders. pub fn is_transient_udp_error(err: &std::io::Error) -> bool { matches!( err.kind(), std::io::ErrorKind::Interrupted | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WouldBlock ) || err.raw_os_error().is_some_and(is_transient_udp_os_error) } pub fn is_transient_udp_send_error(err: &std::io::Error) -> bool { is_transient_udp_error(err) } #[cfg(unix)] fn is_transient_udp_os_error(code: i32) -> bool { matches!( code, libc::EADDRNOTAVAIL | libc::ECONNREFUSED | libc::ECONNRESET | libc::EHOSTDOWN | libc::EHOSTUNREACH | libc::ENETDOWN | libc::ENETRESET | libc::ENETUNREACH | libc::ETIMEDOUT ) } #[cfg(windows)] fn is_transient_udp_os_error(code: i32) -> bool { matches!( code, 10049 // WSAEADDRNOTAVAIL | 10050 // WSAENETDOWN | 10051 // WSAENETUNREACH | 10052 // WSAENETRESET | 10054 // WSAECONNRESET | 10060 // WSAETIMEDOUT | 10061 // WSAECONNREFUSED | 10064 // WSAEHOSTDOWN | 10065 // WSAEHOSTUNREACH ) } #[cfg(not(any(unix, windows)))] fn is_transient_udp_os_error(_code: i32) -> bool { false } #[cfg(test)] mod tests { use super::{is_transient_udp_error, is_transient_udp_send_error}; #[test] fn classifies_portable_transient_udp_errors() { for kind in [ std::io::ErrorKind::Interrupted, std::io::ErrorKind::TimedOut, std::io::ErrorKind::WouldBlock, ] { let err = std::io::Error::from(kind); assert!(is_transient_udp_error(&err)); assert!(is_transient_udp_send_error(&err)); } let fatal = std::io::Error::from(std::io::ErrorKind::PermissionDenied); assert!(!is_transient_udp_error(&fatal)); assert!(!is_transient_udp_send_error(&fatal)); } #[cfg(unix)] #[test] fn classifies_unix_network_churn_as_transient() { for code in [ libc::EADDRNOTAVAIL, libc::ECONNREFUSED, libc::ECONNRESET, libc::EHOSTDOWN, libc::EHOSTUNREACH, libc::ENETDOWN, libc::ENETRESET, libc::ENETUNREACH, libc::ETIMEDOUT, ] { let err = std::io::Error::from_raw_os_error(code); assert!(is_transient_udp_error(&err)); assert!(is_transient_udp_send_error(&err)); } } #[cfg(windows)] #[test] fn classifies_windows_network_churn_as_transient() { for code in [ 10049, 10050, 10051, 10052, 10054, 10060, 10061, 10064, 10065, ] { let err = std::io::Error::from_raw_os_error(code); assert!(is_transient_udp_error(&err)); assert!(is_transient_udp_send_error(&err)); } } }