From e25b3b4e4cff127aeec324725ce601404976c27b Mon Sep 17 00:00:00 2001 From: "irvingouj @ Devolutions" <139169536+irvingoujAtDevolution@users.noreply.github.com> Date: Thu, 21 Mar 2024 01:04:46 -0400 Subject: [PATCH] feat: Replace is_connect_failed with is_err In linux, epoll, EPOLLHUP may happen even if no connection call is made. It would confuse callers for what is actually happening. Replaced is_connect_failed, and we detect if connection failed by using the combination of is_err and is_interrupt, please see the example, tcp_client --- examples/tcp_client.rs | 2 +- src/epoll.rs | 7 ++++++- src/iocp/mod.rs | 8 +++++++- src/kqueue.rs | 5 +++++ src/lib.rs | 21 +++++++++++++++++++++ src/poll.rs | 5 +++++ src/port.rs | 7 ++++++- 7 files changed, 51 insertions(+), 4 deletions(-) diff --git a/examples/tcp_client.rs b/examples/tcp_client.rs index 413f6b6..7a8d26d 100644 --- a/examples/tcp_client.rs +++ b/examples/tcp_client.rs @@ -28,7 +28,7 @@ fn main() -> io::Result<()> { }; println!("event: {:?}", event); - if event.is_connect_failed().unwrap_or_default() { + if event.is_err().unwrap_or(false) { println!("connect failed"); } diff --git a/src/epoll.rs b/src/epoll.rs index f6a6159..2f1a661 100644 --- a/src/epoll.rs +++ b/src/epoll.rs @@ -386,9 +386,14 @@ impl EventExtra { pub fn is_connect_failed(&self) -> Option { Some( self.flags.contains(epoll::EventFlags::ERR) - || self.flags.contains(epoll::EventFlags::HUP), + && self.flags.contains(epoll::EventFlags::HUP), ) } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(epoll::EventFlags::ERR)) + } } /// The notifier for Linux. diff --git a/src/iocp/mod.rs b/src/iocp/mod.rs index ce56ac4..36609f4 100644 --- a/src/iocp/mod.rs +++ b/src/iocp/mod.rs @@ -682,11 +682,17 @@ impl EventExtra { self.flags.set(AfdPollMask::RECEIVE_EXPEDITED, active); } - /// Check if TCP connect failed. + /// Check if TCP connect failed. Deprecated. #[inline] pub fn is_connect_failed(&self) -> Option { Some(self.flags.intersects(AfdPollMask::CONNECT_FAIL)) } + + /// Check if TCP connect failed. + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.intersects(AfdPollMask::CONNECT_FAIL)) + } } /// A packet used to wake up the poller with an event. diff --git a/src/kqueue.rs b/src/kqueue.rs index 3e0b044..30bdaf9 100644 --- a/src/kqueue.rs +++ b/src/kqueue.rs @@ -376,6 +376,11 @@ impl EventExtra { pub fn is_connect_failed(&self) -> Option { None } + + #[inline] + pub fn is_err(&self) -> Option { + None + } } pub(crate) fn mode_to_flags(mode: PollMode) -> kqueue::EventFlags { diff --git a/src/lib.rs b/src/lib.rs index 42c1afe..1be752b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,10 +392,31 @@ impl Event { /// Returns `Some(true)` if the connection has failed, `Some(false)` if the connection has not failed, /// or `None` if the platform does not support detecting this condition. #[inline] + #[deprecated( + since = "3.4.0", + note = "use `is_err` in combination of is_hup instead, see documentation for `is_err`" + )] pub fn is_connect_failed(&self) -> Option { self.extra.is_connect_failed() } + /// Tells if this event is the result of a connection failure. + /// + /// This function checks if an error exist,particularlly useful in detecting if TCP connection failed. It corresponds to the `EPOLLERR` event in Linux + /// and `CONNECT_FAILED` event in Windows IOCP. + /// + /// ## Caveats + /// + /// In `epoll`, a TCP connection failure is indicated by `EPOLLERR` + `EPOLLHUP`, though just `EPOLLERR` is enough to indicate a connection failure. + /// EPOLLHUP may happen when we haven't event called `connect` on the socket, but it is still a valid event to check for. + /// + /// Returns `Some(true)` if the connection has failed, `Some(false)` if there is an error, + /// or `None` if the platform does not support detecting this condition. + #[inline] + pub fn is_err(&self) -> Option { + self.extra.is_err() + } + /// Remove any extra information from this event. #[inline] pub fn clear_extra(&mut self) { diff --git a/src/poll.rs b/src/poll.rs index d219538..13c742e 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -435,6 +435,11 @@ impl EventExtra { pub fn is_connect_failed(&self) -> Option { Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP)) } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR)) + } } fn cvt_mode_as_remove(mode: PollMode) -> io::Result { diff --git a/src/port.rs b/src/port.rs index c88b36b..57e0ed2 100644 --- a/src/port.rs +++ b/src/port.rs @@ -253,6 +253,11 @@ impl EventExtra { #[inline] pub fn is_connect_failed(&self) -> Option { - Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP)) + Some(self.flags.contains(PollFlags::ERR) && self.flags.contains(PollFlags::HUP)) + } + + #[inline] + pub fn is_err(&self) -> Option { + Some(self.flags.contains(PollFlags::ERR)) } }