Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion lib/virtual-net/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,37 @@ impl VirtualIoSource for LocalTcpStream {
}

#[cfg(not(target_os = "windows"))]
match libc_poll(stream.as_raw_fd(), libc::POLLOUT | libc::POLLHUP) {
match libc_poll(
stream.as_raw_fd(),
libc::POLLOUT | libc::POLLHUP | libc::POLLERR,
) {
Some(val) if (val & libc::POLLERR) != 0 => {
Comment on lines +721 to +725
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change fixes a subtle readiness/connection-error behavior; please add a regression test to cover it. A stable approach is to create a nonblocking TCP socket to a known-closed localhost port (e.g., bind a TcpListener to 127.0.0.1:0, capture the port, drop the listener, then attempt connect), wrap it as LocalTcpStream, and assert poll_write_ready resolves to Err(NetworkError::ConnectionRefused) (or appropriate mapped error) instead of reporting writable.

Copilot uses AI. Check for mistakes.
// Get the actual socket error using SO_ERROR
let mut error: libc::c_int = 0;
let mut len = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
let result = unsafe {
libc::getsockopt(
stream.as_raw_fd(),
libc::SOL_SOCKET,
libc::SO_ERROR,
&mut error as *mut libc::c_int as *mut libc::c_void,
&mut len,
)
};
if result != 0 {
// getsockopt itself failed
let io_error = std::io::Error::last_os_error();
return Poll::Ready(Err(io_err_into_net_error(io_error)));
}
if error != 0 {
// Socket has a pending error
let io_error = std::io::Error::from_raw_os_error(error);
return Poll::Ready(Err(io_err_into_net_error(io_error)));
}
// POLLERR was set but SO_ERROR is 0 - this shouldn't normally happen,
// but we'll treat it as a generic IO error
return Poll::Ready(Err(NetworkError::IOError));
}
Some(val) if (val & libc::POLLHUP) != 0 => {
return Poll::Ready(Ok(0));
}
Expand Down
Loading