diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 88a18094acdb..d92f60c8b21e 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -404,3 +404,38 @@ test "non-blocking tcp server" { const msg = buf[0..len]; try testing.expect(mem.eql(u8, msg, "hello from server\n")); } + +test "non-blocking tcp client" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + if (builtin.os.tag == .windows) { + _ = try std.os.windows.WSAStartup(2, 2); + } + defer { + if (builtin.os.tag == .windows) { + std.os.windows.WSACleanup() catch unreachable; + } + } + + // create a blocking server that accepts a single connection then ignores it + const listenAddress = std.net.Address.initIp4(.{ 127, 0, 0, 1 }, 0); + var localhost = try listenAddress.listen(.{}); + defer localhost.deinit(); + + // Address.listen calls getsockname to fill localhost.listen_address with a port + const address = localhost.listen_address; + + const tpe: u32 = std.posix.SOCK.STREAM | std.posix.SOCK.NONBLOCK | std.posix.SOCK.CLOEXEC; + const protocol = std.posix.IPPROTO.TCP; + const socket = try std.posix.socket(address.any.family, tpe, protocol); + defer std.posix.close(socket); + + // first call returns .WouldBlock like usual + try testing.expectError(error.WouldBlock, std.posix.connect(socket, &address.any, address.getOsSockLen())); + + var con = try localhost.accept(); + defer con.stream.close(); + + // another call after accept returns returns gracefully + try std.posix.connect(socket, &address.any, address.getOsSockLen()); +} diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 936c19591a88..fd0d9d97306d 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -4274,6 +4274,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(len)); if (rc == 0) return; switch (windows.ws2_32.WSAGetLastError()) { + .WSAEISCONN => return, .WSAEADDRINUSE => return error.AddressInUse, .WSAEADDRNOTAVAIL => return error.AddressNotAvailable, .WSAECONNREFUSED => return error.ConnectionRefused, @@ -4284,9 +4285,9 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne => return error.NetworkUnreachable, .WSAEFAULT => unreachable, .WSAEINVAL => unreachable, - .WSAEISCONN => unreachable, .WSAENOTSOCK => unreachable, .WSAEWOULDBLOCK => return error.WouldBlock, + .WSAEALREADY => return error.ConnectionPending, .WSAEACCES => unreachable, .WSAENOBUFS => return error.SystemResources, .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, @@ -4298,6 +4299,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne while (true) { switch (errno(system.connect(sock, sock_addr, len))) { .SUCCESS => return, + .ISCONN => return, .ACCES => return error.AccessDenied, .PERM => return error.PermissionDenied, .ADDRINUSE => return error.AddressInUse, @@ -4310,7 +4312,6 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne .CONNRESET => return error.ConnectionResetByPeer, .FAULT => unreachable, // The socket structure address is outside the user's address space. .INTR => continue, - .ISCONN => unreachable, // The socket is already connected. .HOSTUNREACH => return error.NetworkUnreachable, .NETUNREACH => return error.NetworkUnreachable, .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.