Skip to content
Open
Show file tree
Hide file tree
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
35 changes: 35 additions & 0 deletions lib/std/net/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
5 changes: 3 additions & 2 deletions lib/std/posix.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason WSAEISCONN/ISCONN shouldn't return an error (something like error.IsConnected)? Is there no use case for distinguishing between success and WSAEISCONN/ISCONN?

Copy link
Author

Choose a reason for hiding this comment

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

I don't know of one, I opted for this because it makes total sense to me. Could opt for an error as well.

Copy link
Member

Choose a reason for hiding this comment

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

Will need to wait for a Zig core team member to weigh in.

.WSAEADDRINUSE => return error.AddressInUse,
.WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
.WSAECONNREFUSED => return error.ConnectionRefused,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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.
Expand Down