Skip to content

Commit 5017013

Browse files
committed
std.posix: unlock nonblocking connect() use case
Match Windows `WSAEALREADY` with Linux `EALREADY` return gracefully when socket reports it is connected
1 parent 92c6312 commit 5017013

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

lib/std/net/test.zig

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,3 +404,38 @@ test "non-blocking tcp server" {
404404
const msg = buf[0..len];
405405
try testing.expect(mem.eql(u8, msg, "hello from server\n"));
406406
}
407+
408+
test "non-blocking tcp client" {
409+
if (builtin.os.tag == .wasi) return error.SkipZigTest;
410+
411+
if (builtin.os.tag == .windows) {
412+
_ = try std.os.windows.WSAStartup(2, 2);
413+
}
414+
defer {
415+
if (builtin.os.tag == .windows) {
416+
std.os.windows.WSACleanup() catch unreachable;
417+
}
418+
}
419+
420+
// create a blocking server that accepts a single connection then ignores it
421+
const listenAddress = std.net.Address.initIp4(.{ 127, 0, 0, 1 }, 0);
422+
var localhost = try listenAddress.listen(.{});
423+
defer localhost.deinit();
424+
425+
// Address.listen calls getsockname to fill localhost.listen_address with a port
426+
const address = localhost.listen_address;
427+
428+
const tpe: u32 = std.posix.SOCK.STREAM | std.posix.SOCK.NONBLOCK | std.posix.SOCK.CLOEXEC;
429+
const protocol = std.posix.IPPROTO.TCP;
430+
const socket = try std.posix.socket(address.any.family, tpe, protocol);
431+
defer std.posix.close(socket);
432+
433+
// first call returns .WouldBlock like usual
434+
try testing.expectError(error.WouldBlock, std.posix.connect(socket, &address.any, address.getOsSockLen()));
435+
436+
var con = try localhost.accept();
437+
defer con.stream.close();
438+
439+
// another call after accept returns returns gracefully
440+
try std.posix.connect(socket, &address.any, address.getOsSockLen());
441+
}

lib/std/posix.zig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4274,6 +4274,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
42744274
const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(len));
42754275
if (rc == 0) return;
42764276
switch (windows.ws2_32.WSAGetLastError()) {
4277+
.WSAEISCONN => return,
42774278
.WSAEADDRINUSE => return error.AddressInUse,
42784279
.WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
42794280
.WSAECONNREFUSED => return error.ConnectionRefused,
@@ -4284,9 +4285,9 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
42844285
=> return error.NetworkUnreachable,
42854286
.WSAEFAULT => unreachable,
42864287
.WSAEINVAL => unreachable,
4287-
.WSAEISCONN => unreachable,
42884288
.WSAENOTSOCK => unreachable,
42894289
.WSAEWOULDBLOCK => return error.WouldBlock,
4290+
.WSAEALREADY => return error.ConnectionPending,
42904291
.WSAEACCES => unreachable,
42914292
.WSAENOBUFS => return error.SystemResources,
42924293
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
@@ -4298,6 +4299,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
42984299
while (true) {
42994300
switch (errno(system.connect(sock, sock_addr, len))) {
43004301
.SUCCESS => return,
4302+
.ISCONN => return,
43014303
.ACCES => return error.AccessDenied,
43024304
.PERM => return error.PermissionDenied,
43034305
.ADDRINUSE => return error.AddressInUse,
@@ -4310,7 +4312,6 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
43104312
.CONNRESET => return error.ConnectionResetByPeer,
43114313
.FAULT => unreachable, // The socket structure address is outside the user's address space.
43124314
.INTR => continue,
4313-
.ISCONN => unreachable, // The socket is already connected.
43144315
.HOSTUNREACH => return error.NetworkUnreachable,
43154316
.NETUNREACH => return error.NetworkUnreachable,
43164317
.NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.

0 commit comments

Comments
 (0)