authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-13 08:27:14+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-13 08:27:14+01:00
log2c61deaae26ee492fa5a7b68726801c9414305a5
tree7ead45fae4dd852cc3ad3357156c2daa7dde9f34
parentab1a4b26ec2b90f514059b6e446b83c3881dbe4d
parent314264ee7d76c365a80105e13db40819ecd33329

Merge pull request 'std.Io.net: enable more tests + coverage for canceling accept' (#31468) from net-test-coverage into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31468

2 files changed, 105 insertions(+), 130 deletions(-)

lib/std/Io/Threaded.zig+27-13
...@@ -11846,7 +11846,10 @@ fn netListenUnixWindows(...@@ -11846,7 +11846,10 @@ fn netListenUnixWindows(
11846 var syscall: AlertableSyscall = try .start();11846 var syscall: AlertableSyscall = try .start();
11847 while (true) {11847 while (true) {
11848 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);11848 const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
11849 if (rc != ws2_32.SOCKET_ERROR) break;11849 if (rc != ws2_32.SOCKET_ERROR) {
11850 syscall.finish();
11851 break;
11852 }
11850 switch (ws2_32.WSAGetLastError()) {11853 switch (ws2_32.WSAGetLastError()) {
11851 .NOTINITIALISED => {11854 .NOTINITIALISED => {
11852 syscall.finish();11855 syscall.finish();
...@@ -11869,15 +11872,18 @@ fn netListenUnixWindows(...@@ -11869,15 +11872,18 @@ fn netListenUnixWindows(
11869 }11872 }
11870 }11873 }
1187111874
11875 syscall = try .start();
11872 while (true) {11876 while (true) {
11873 try syscall.checkCancel();
11874 const rc = ws2_32.listen(socket_handle, options.kernel_backlog);11877 const rc = ws2_32.listen(socket_handle, options.kernel_backlog);
11875 if (rc != ws2_32.SOCKET_ERROR) {11878 if (rc != ws2_32.SOCKET_ERROR) {
11876 syscall.finish();11879 syscall.finish();
11877 return socket_handle;11880 return socket_handle;
11878 }11881 }
11879 switch (ws2_32.WSAGetLastError()) {11882 switch (ws2_32.WSAGetLastError()) {
11880 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => continue,11883 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
11884 try syscall.checkCancel();
11885 continue;
11886 },
11881 .NOTINITIALISED => {11887 .NOTINITIALISED => {
11882 syscall.finish();11888 syscall.finish();
11883 try initializeWsa(t);11889 try initializeWsa(t);
...@@ -12160,9 +12166,12 @@ fn setSocketOption(fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void...@@ -12160,9 +12166,12 @@ fn setSocketOption(fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void
12160fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, opt_name: u32, option: u32) !void {12166fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, opt_name: u32, option: u32) !void {
12161 const o: []const u8 = @ptrCast(&option);12167 const o: []const u8 = @ptrCast(&option);
12162 var syscall: AlertableSyscall = try .start();12168 var syscall: AlertableSyscall = try .start();
12163 const rc = ws2_32.setsockopt(socket, level, @bitCast(opt_name), o.ptr, @intCast(o.len));
12164 while (true) {12169 while (true) {
12165 if (rc != ws2_32.SOCKET_ERROR) return syscall.finish();12170 const rc = ws2_32.setsockopt(socket, level, @bitCast(opt_name), o.ptr, @intCast(o.len));
12171 if (rc != ws2_32.SOCKET_ERROR) {
12172 syscall.finish();
12173 return;
12174 }
12166 switch (ws2_32.WSAGetLastError()) {12175 switch (ws2_32.WSAGetLastError()) {
12167 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {12176 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
12168 try syscall.checkCancel();12177 try syscall.checkCancel();
...@@ -12175,7 +12184,9 @@ fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, op...@@ -12175,7 +12184,9 @@ fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, op
12175 continue;12184 continue;
12176 },12185 },
12177 .ENETDOWN => return syscall.fail(error.NetworkDown),12186 .ENETDOWN => return syscall.fail(error.NetworkDown),
12178 .EFAULT, .ENOTSOCK, .EINVAL => |err| return syscall.wsaErrorBug(err),12187 .EFAULT => |err| return syscall.wsaErrorBug(err),
12188 .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
12189 .EINVAL => |err| return syscall.wsaErrorBug(err),
12179 else => |err| return syscall.unexpectedWsaError(err),12190 else => |err| return syscall.unexpectedWsaError(err),
12180 }12191 }
12181 }12192 }
...@@ -12313,7 +12324,10 @@ fn netConnectUnixWindows(...@@ -12313,7 +12324,10 @@ fn netConnectUnixWindows(
12313 var syscall: AlertableSyscall = try .start();12324 var syscall: AlertableSyscall = try .start();
12314 while (true) {12325 while (true) {
12315 const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);12326 const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
12316 if (rc != ws2_32.SOCKET_ERROR) break;12327 if (rc != ws2_32.SOCKET_ERROR) {
12328 syscall.finish();
12329 break;
12330 }
12317 switch (ws2_32.WSAGetLastError()) {12331 switch (ws2_32.WSAGetLastError()) {
12318 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {12332 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
12319 try syscall.checkCancel();12333 try syscall.checkCancel();
...@@ -12326,14 +12340,14 @@ fn netConnectUnixWindows(...@@ -12326,14 +12340,14 @@ fn netConnectUnixWindows(
12326 continue;12340 continue;
12327 },12341 },
12328 .ECONNREFUSED => return syscall.fail(error.FileNotFound),12342 .ECONNREFUSED => return syscall.fail(error.FileNotFound),
12329 .EFAULT => |err| return syscall.wsaErrorBug(err),
12330 .EINVAL => |err| return syscall.wsaErrorBug(err),
12331 .EISCONN => |err| return syscall.wsaErrorBug(err),
12332 .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
12333 .EWOULDBLOCK => return syscall.fail(error.WouldBlock),12343 .EWOULDBLOCK => return syscall.fail(error.WouldBlock),
12334 .EACCES => return syscall.fail(error.AccessDenied),12344 .EACCES => return syscall.fail(error.AccessDenied),
12335 .ENOBUFS => return syscall.fail(error.SystemResources),12345 .ENOBUFS => return syscall.fail(error.SystemResources),
12336 .EAFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),12346 .EAFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
12347 .EFAULT => |err| return syscall.wsaErrorBug(err),
12348 .EINVAL => |err| return syscall.wsaErrorBug(err),
12349 .EISCONN => |err| return syscall.wsaErrorBug(err),
12350 .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
12337 else => |err| return syscall.unexpectedWsaError(err),12351 else => |err| return syscall.unexpectedWsaError(err),
12338 }12352 }
12339 }12353 }
...@@ -13439,7 +13453,6 @@ fn netWriteWindows(...@@ -13439,7 +13453,6 @@ fn netWriteWindows(
13439 return n;13453 return n;
13440 }13454 }
13441 switch (ws2_32.WSAGetLastError()) {13455 switch (ws2_32.WSAGetLastError()) {
13442 .IO_PENDING => unreachable, // not overlapped
13443 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {13456 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
13444 try syscall.checkCancel();13457 try syscall.checkCancel();
13445 continue;13458 continue;
...@@ -13461,6 +13474,7 @@ fn netWriteWindows(...@@ -13461,6 +13474,7 @@ fn netWriteWindows(
13461 .ENOTSOCK => |err| return syscall.wsaErrorBug(err),13474 .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
13462 .EOPNOTSUPP => |err| return syscall.wsaErrorBug(err),13475 .EOPNOTSUPP => |err| return syscall.wsaErrorBug(err),
13463 .ESHUTDOWN => |err| return syscall.wsaErrorBug(err),13476 .ESHUTDOWN => |err| return syscall.wsaErrorBug(err),
13477 .IO_PENDING => |err| return syscall.wsaErrorBug(err),
13464 else => |err| return syscall.unexpectedWsaError(err),13478 else => |err| return syscall.unexpectedWsaError(err),
13465 }13479 }
13466 }13480 }
...@@ -15321,7 +15335,7 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT...@@ -15321,7 +15335,7 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT
15321 const handle = child.id.?;15335 const handle = child.id.?;
15322 _ = windows.ntdll.RtlReportSilentProcessExit(handle, @enumFromInt(exit_code));15336 _ = windows.ntdll.RtlReportSilentProcessExit(handle, @enumFromInt(exit_code));
15323 switch (windows.ntdll.NtTerminateProcess(handle, @enumFromInt(exit_code))) {15337 switch (windows.ntdll.NtTerminateProcess(handle, @enumFromInt(exit_code))) {
15324 .SUCCESS => {15338 .SUCCESS, .PROCESS_IS_TERMINATING => {
15325 const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);15339 const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
15326 _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout);15340 _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout);
15327 childCleanupWindows(child);15341 childCleanupWindows(child);
lib/std/Io/net/test.zig+78-117
...@@ -5,6 +5,7 @@ const Io = std.Io;...@@ -5,6 +5,7 @@ const Io = std.Io;
5const net = std.Io.net;5const net = std.Io.net;
6const mem = std.mem;6const mem = std.mem;
7const testing = std.testing;7const testing = std.testing;
8const Allocator = std.mem.Allocator;
89
9test "parse and render IP addresses at comptime" {10test "parse and render IP addresses at comptime" {
10 comptime {11 comptime {
...@@ -84,28 +85,19 @@ test "IPv6 address parse failures" {...@@ -84,28 +85,19 @@ test "IPv6 address parse failures" {
84}85}
8586
86test "invalid but parseable IPv6 scope ids" {87test "invalid but parseable IPv6 scope ids" {
87 const io = testing.io;88 if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin()) return error.SkipZigTest;
8889
89 if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin()) {90 const io = testing.io;
90 return error.SkipZigTest; // TODO
91 }
9291
93 try testing.expectError(error.InterfaceNotFound, net.IpAddress.resolveIp6(io, "ff01::fb%123s45678901234", 0));92 try testing.expectError(error.InterfaceNotFound, net.IpAddress.resolveIp6(io, "ff01::fb%123s45678901234", 0));
94}93}
9594
96test "parse and render IPv4 addresses" {95test "parse and render IPv4 addresses" {
97 var buffer: [18]u8 = undefined;96 try testIp4ParseAndRender("0.0.0.0");
98 for ([_][]const u8{97 try testIp4ParseAndRender("255.255.255.255");
99 "0.0.0.0",98 try testIp4ParseAndRender("1.2.3.4");
100 "255.255.255.255",99 try testIp4ParseAndRender("123.255.0.91");
101 "1.2.3.4",100 try testIp4ParseAndRender("127.0.0.1");
102 "123.255.0.91",
103 "127.0.0.1",
104 }) |ip| {
105 const addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
106 var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
107 try testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
108 }
109101
110 try testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0));102 try testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0));
111 try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0));103 try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0));
...@@ -115,9 +107,15 @@ test "parse and render IPv4 addresses" {...@@ -115,9 +107,15 @@ test "parse and render IPv4 addresses" {
115 try testing.expectError(error.NonCanonical, net.IpAddress.parseIp4("127.01.0.1", 0));107 try testing.expectError(error.NonCanonical, net.IpAddress.parseIp4("127.01.0.1", 0));
116}108}
117109
118test "resolve DNS" {110fn testIp4ParseAndRender(text: []const u8) !void {
119 if (builtin.os.tag == .wasi) return error.SkipZigTest;111 var buffer: [18]u8 = undefined;
112 const addr = try net.IpAddress.parseIp4(text, 0);
113 const rendered = try std.fmt.bufPrint(&buffer, "{f}", .{addr});
114 const without_port = rendered[0 .. rendered.len - 2];
115 try testing.expectEqualStrings(text, without_port);
116}
120117
118test "resolve DNS" {
121 const io = testing.io;119 const io = testing.io;
122120
123 // Resolve localhost, this should not fail.121 // Resolve localhost, this should not fail.
...@@ -129,10 +127,13 @@ test "resolve DNS" {...@@ -129,10 +127,13 @@ test "resolve DNS" {
129 var results_buffer: [32]net.HostName.LookupResult = undefined;127 var results_buffer: [32]net.HostName.LookupResult = undefined;
130 var results: Io.Queue(net.HostName.LookupResult) = .init(&results_buffer);128 var results: Io.Queue(net.HostName.LookupResult) = .init(&results_buffer);
131129
132 try net.HostName.lookup(try .init("localhost"), io, &results, .{130 net.HostName.lookup(try .init("localhost"), io, &results, .{
133 .port = 80,131 .port = 80,
134 .canonical_name_buffer = &canonical_name_buffer,132 .canonical_name_buffer = &canonical_name_buffer,
135 });133 }) catch |err| switch (err) {
134 error.NetworkDown => return error.SkipZigTest,
135 else => |e| return e,
136 };
136137
137 var addresses_found: usize = 0;138 var addresses_found: usize = 0;
138139
...@@ -177,17 +178,16 @@ test "resolve DNS" {...@@ -177,17 +178,16 @@ test "resolve DNS" {
177}178}
178179
179test "listen on a port, send bytes, receive bytes" {180test "listen on a port, send bytes, receive bytes" {
180 if (true) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/31388
181 if (builtin.single_threaded) return error.SkipZigTest;
182 if (builtin.os.tag == .wasi) return error.SkipZigTest;
183
184 const io = testing.io;181 const io = testing.io;
185182
186 // Try only the IPv4 variant as some CI builders have no IPv6 localhost183 // Try only the IPv4 variant as some CI builders have no IPv6 localhost
187 // configured.184 // configured.
188 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };185 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
189186
190 var server = try localhost.listen(io, .{});187 var server = localhost.listen(io, .{}) catch |err| switch (err) {
188 error.NetworkDown => return error.SkipZigTest,
189 else => |e| return e,
190 };
191 defer server.deinit(io);191 defer server.deinit(io);
192192
193 const S = struct {193 const S = struct {
...@@ -200,89 +200,54 @@ test "listen on a port, send bytes, receive bytes" {...@@ -200,89 +200,54 @@ test "listen on a port, send bytes, receive bytes" {
200 }200 }
201 };201 };
202202
203 const t = try std.Thread.spawn(.{}, S.clientFn, .{server.socket.address});203 var client_task = io.concurrent(S.clientFn, .{server.socket.address}) catch |err| switch (err) {
204 defer t.join();204 error.ConcurrencyUnavailable => return error.SkipZigTest,
205 };
206 defer client_task.cancel(io) catch {};
205207
206 var stream = try server.accept(io);208 var stream = try server.accept(io);
207 defer stream.close(io);209 defer stream.close(io);
210
208 var buf: [16]u8 = undefined;211 var buf: [16]u8 = undefined;
209 var stream_reader = stream.reader(io, &.{});212 var stream_reader = stream.reader(io, &.{});
210 const n = try stream_reader.interface.readSliceShort(&buf);213 const n = try stream_reader.interface.readSliceShort(&buf);
211214
212 try testing.expectEqual(@as(usize, 12), n);215 try testing.expectEqual(@as(usize, 12), n);
213 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);216 try testing.expectEqualStrings("Hello world!", buf[0..n]);
217
218 try client_task.await(io);
214}219}
215220
216test "listen on an in use port" {221test "listen on an in use port" {
217 if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin() and builtin.os.tag != .windows) {
218 // TODO build abstractions for other operating systems
219 return error.SkipZigTest;
220 }
221
222 const io = testing.io;222 const io = testing.io;
223223
224 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };224 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
225225
226 var server1 = try localhost.listen(io, .{ .reuse_address = true });226 var server1 = localhost.listen(io, .{ .reuse_address = true }) catch |err| switch (err) {
227 error.NetworkDown => return error.SkipZigTest,
228 else => |e| return e,
229 };
227 defer server1.deinit(io);230 defer server1.deinit(io);
228231
229 var server2 = try server1.socket.address.listen(io, .{ .reuse_address = true });232 var server2 = try server1.socket.address.listen(io, .{ .reuse_address = true });
230 defer server2.deinit(io);233 defer server2.deinit(io);
231}234}
232235
233fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyerror!void {
234 if (builtin.os.tag == .wasi) return error.SkipZigTest;
235
236 const io = testing.io;
237
238 const connection = try net.tcpConnectToHost(allocator, name, port);
239 defer connection.close(io);
240
241 var buf: [100]u8 = undefined;
242 const len = try connection.read(&buf);
243 const msg = buf[0..len];
244 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
245}
246
247fn testClient(addr: net.IpAddress) anyerror!void {
248 if (builtin.os.tag == .wasi) return error.SkipZigTest;
249
250 const io = testing.io;
251
252 const socket_file = try net.tcpConnectToAddress(addr);
253 defer socket_file.close(io);
254
255 var buf: [100]u8 = undefined;
256 const len = try socket_file.read(&buf);
257 const msg = buf[0..len];
258 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
259}
260
261fn testServer(server: *net.Server) anyerror!void {
262 if (builtin.os.tag == .wasi) return error.SkipZigTest;
263
264 const io = testing.io;
265
266 var stream = try server.accept(io);
267 var writer = stream.writer(io, &.{});
268 try writer.interface.print("hello from server\n", .{});
269}
270
271test "listen on a unix socket, send bytes, receive bytes" {236test "listen on a unix socket, send bytes, receive bytes" {
272 if (builtin.single_threaded) return error.SkipZigTest;
273 if (!net.has_unix_sockets) return error.SkipZigTest;
274 if (builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25983
275 if (builtin.cpu.arch == .mipsel) return error.SkipZigTest; // TODO
276
277 const io = testing.io;237 const io = testing.io;
238 const gpa = testing.allocator;
278239
279 const socket_path = try generateFileName(io, "socket.unix");240 const socket_path = try generateFileName(gpa, io, "socket.unix");
280 defer testing.allocator.free(socket_path);241 defer gpa.free(socket_path);
281242
282 const socket_addr = try net.UnixAddress.init(socket_path);243 const socket_addr = try net.UnixAddress.init(socket_path);
283 defer Io.Dir.cwd().deleteFile(io, socket_path) catch {};244 defer Io.Dir.cwd().deleteFile(io, socket_path) catch {};
284245
285 var server = try socket_addr.listen(io, .{});246 var server = socket_addr.listen(io, .{}) catch |err| switch (err) {
247 error.AddressFamilyUnsupported => return error.SkipZigTest,
248 error.NetworkDown => return error.SkipZigTest,
249 else => |e| return e,
250 };
286 defer server.socket.close(io);251 defer server.socket.close(io);
287252
288 const S = struct {253 const S = struct {
...@@ -296,59 +261,32 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -296,59 +261,32 @@ test "listen on a unix socket, send bytes, receive bytes" {
296 }261 }
297 };262 };
298263
299 const t = try std.Thread.spawn(.{}, S.clientFn, .{socket_path});264 var client_task = io.concurrent(S.clientFn, .{socket_path}) catch |err| switch (err) {
300 defer t.join();265 error.ConcurrencyUnavailable => return error.SkipZigTest,
266 };
267 defer client_task.cancel(io) catch {};
301268
302 var stream = try server.accept(io);269 var stream = try server.accept(io);
303 defer stream.close(io);270 defer stream.close(io);
271
304 var buf: [16]u8 = undefined;272 var buf: [16]u8 = undefined;
305 var stream_reader = stream.reader(io, &.{});273 var stream_reader = stream.reader(io, &.{});
306 const n = try stream_reader.interface.readSliceShort(&buf);274 const n = try stream_reader.interface.readSliceShort(&buf);
307275
308 try testing.expectEqual(@as(usize, 12), n);276 try testing.expectEqual(@as(usize, 12), n);
309 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);277 try testing.expectEqualStrings("Hello world!", buf[0..n]);
278
279 try client_task.await(io);
310}280}
311281
312fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {282fn generateFileName(gpa: Allocator, io: Io, base_name: []const u8) ![]const u8 {
313 const random_bytes_count = 12;283 const random_bytes_count = 12;
314 const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);284 const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);
315 var random_bytes: [12]u8 = undefined;285 var random_bytes: [12]u8 = undefined;
316 io.random(&random_bytes);286 io.random(&random_bytes);
317 var sub_path: [sub_path_len]u8 = undefined;287 var sub_path: [sub_path_len]u8 = undefined;
318 _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);288 _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
319 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });289 return std.fmt.allocPrint(gpa, "{s}-{s}", .{ sub_path[0..], base_name });
320}
321
322test "non-blocking tcp server" {
323 if (builtin.os.tag == .wasi) return error.SkipZigTest;
324 if (true) {
325 // https://github.com/ziglang/zig/issues/18315
326 return error.SkipZigTest;
327 }
328
329 const io = testing.io;
330
331 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
332 var server = localhost.listen(io, .{ .force_nonblocking = true });
333 defer server.deinit(io);
334
335 const accept_err = server.accept(io);
336 try testing.expectError(error.WouldBlock, accept_err);
337
338 const socket_file = try net.tcpConnectToAddress(server.socket.address);
339 defer socket_file.close(io);
340
341 var stream = try server.accept(io);
342 defer stream.close(io);
343 var writer = stream.writer(io, .{});
344 try writer.interface.print("hello from server\n", .{});
345
346 var buf: [100]u8 = undefined;
347 const len = try socket_file.read(&buf);
348 const msg = buf[0..len];
349 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
350
351 try stream.shutdown(io, .both);
352}290}
353291
354test "decompress compressed DNS name" {292test "decompress compressed DNS name" {
...@@ -400,3 +338,26 @@ test "decompress compressed DNS name" {...@@ -400,3 +338,26 @@ test "decompress compressed DNS name" {
400 try testing.expectEqual(packet.len - cname_data_index, n_consumed);338 try testing.expectEqual(packet.len - cname_data_index, n_consumed);
401 try testing.expectEqualStrings("target.ziglang.org", result.bytes);339 try testing.expectEqualStrings("target.ziglang.org", result.bytes);
402}340}
341
342test "cancel accept" {
343 if (builtin.os.tag == .windows) {
344 // https://codeberg.org/ziglang/zig/issues/30865
345 return error.SkipZigTest;
346 }
347
348 const io = testing.io;
349 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
350
351 var server = localhost.listen(io, .{}) catch |err| switch (err) {
352 error.NetworkDown => return error.SkipZigTest,
353 else => |e| return e,
354 };
355 defer server.deinit(io);
356
357 var accept = io.concurrent(std.Io.net.Server.accept, .{ &server, io }) catch |err| switch (err) {
358 error.ConcurrencyUnavailable => return error.SkipZigTest,
359 };
360 defer if (accept.cancel(io)) |stream| stream.close(io) else |_| {};
361
362 try io.sleep(.fromNanoseconds(1), .awake);
363}