Skip to content

Commit 2bd5238

Browse files
committed
Fix TcpStream::connect_timeout on linux
Linux appears to set POLLOUT when a conection's refused, which is pretty weird. Invert the check to look for an error explicitly. Also add an explict test for this case. Closes #45265.
1 parent 02a24db commit 2bd5238

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed

src/libstd/net/tcp.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,20 @@ mod tests {
15791579
"bad error: {} {:?}", e, e.kind());
15801580
}
15811581

1582+
#[test]
1583+
fn connect_timeout_unbound() {
1584+
// bind and drop a socket to track down a "probably unassigned" port
1585+
let socket = TcpListener::bind("127.0.0.1:0").unwrap();
1586+
let addr = socket.local_addr().unwrap();
1587+
drop(socket);
1588+
1589+
let timeout = Duration::from_secs(1);
1590+
let e = TcpStream::connect_timeout(&addr, timeout).unwrap_err();
1591+
assert!(e.kind() == io::ErrorKind::ConnectionRefused ||
1592+
e.kind() == io::ErrorKind::Other,
1593+
"bad error: {} {:?}", e, e.kind());
1594+
}
1595+
15821596
#[test]
15831597
fn connect_timeout_valid() {
15841598
let listener = TcpListener::bind("127.0.0.1:0").unwrap();

src/libstd/sys/unix/net.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,16 @@ impl Socket {
176176
}
177177
0 => {}
178178
_ => {
179-
if pollfd.revents & libc::POLLOUT == 0 {
180-
if let Some(e) = self.take_error()? {
181-
return Err(e);
182-
}
179+
// linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
180+
// for errors rather than read readiness
181+
if pollfd.revents & libc::POLLERR != 0 {
182+
let e = self.take_error()?
183+
.unwrap_or_else(|| {
184+
io::Error::new(io::ErrorKind::Other, "no error set after POLLERR")
185+
});
186+
return Err(e);
183187
}
188+
184189
return Ok(());
185190
}
186191
}

0 commit comments

Comments
 (0)