authorgravatar for lukas@lalinsky.comLukas Lalinsky <lukas@lalinsky.com> 2025-12-27 10:06:21+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-29 02:20:37+01:00
loge8a2e6578a3f5e4cd82eb59388e49e152728791e
tree377c0313ad303dd7f6707801bec8934a44cab8cf
parentf2f474fc785a9bc89f16f91067480e8c410dc773

Add std.Io.net.Stream.shutdown


5 files changed, 118 insertions(+), 0 deletions(-)

lib/std/Io.zig+1
......@@ -734,6 +734,7 @@ pub const VTable = struct {
734734 netWrite: *const fn (?*anyopaque, dest: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize,
735735 netWriteFile: *const fn (?*anyopaque, net.Socket.Handle, header: []const u8, *Io.File.Reader, Io.Limit) net.Stream.Writer.WriteFileError!usize,
736736 netClose: *const fn (?*anyopaque, handle: []const net.Socket.Handle) void,
737 netShutdown: *const fn (?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void,
737738 netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface,
738739 netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name,
739740 netLookup: *const fn (?*anyopaque, net.HostName, *Queue(net.HostName.LookupResult), net.HostName.LookupOptions) net.HostName.LookupError!void,
lib/std/Io/Kqueue.zig+13
......@@ -900,6 +900,7 @@ pub fn io(k: *Kqueue) Io {
900900 .netConnectIp = netConnectIp,
901901 .netConnectUnix = netConnectUnix,
902902 .netClose = netClose,
903 .netShutdown = netShutdown,
903904 .netRead = netRead,
904905 .netWrite = netWrite,
905906 .netSend = netSend,
......@@ -1549,12 +1550,22 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8,
15491550 _ = splat;
15501551 @panic("TODO");
15511552}
1553
15521554fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void {
15531555 const k: *Kqueue = @ptrCast(@alignCast(userdata));
15541556 _ = k;
15551557 _ = handle;
15561558 @panic("TODO");
15571559}
1560
1561fn netShutdown(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
1562 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1563 _ = k;
1564 _ = handle;
1565 _ = how;
1566 @panic("TODO");
1567}
1568
15581569fn netInterfaceNameResolve(
15591570 userdata: ?*anyopaque,
15601571 name: *const net.Interface.Name,
......@@ -1564,12 +1575,14 @@ fn netInterfaceNameResolve(
15641575 _ = name;
15651576 @panic("TODO");
15661577}
1578
15671579fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
15681580 const k: *Kqueue = @ptrCast(@alignCast(userdata));
15691581 _ = k;
15701582 _ = interface;
15711583 @panic("TODO");
15721584}
1585
15731586fn netLookup(
15741587 userdata: ?*anyopaque,
15751588 host_name: net.HostName,
lib/std/Io/Threaded.zig+88
......@@ -891,6 +891,10 @@ pub fn io(t: *Threaded) Io {
891891 else => netConnectUnixPosix,
892892 },
893893 .netClose = netClose,
894 .netShutdown = switch (native_os) {
895 .windows => netShutdownWindows,
896 else => netShutdownPosix,
897 },
894898 .netRead = switch (native_os) {
895899 .windows => netReadWindows,
896900 else => netReadPosix,
......@@ -1007,6 +1011,7 @@ pub fn ioBasic(t: *Threaded) Io {
10071011 .netConnectIp = netConnectIpUnavailable,
10081012 .netConnectUnix = netConnectUnixUnavailable,
10091013 .netClose = netCloseUnavailable,
1014 .netShutdown = netShutdownUnavailable,
10101015 .netRead = netReadUnavailable,
10111016 .netWrite = netWriteUnavailable,
10121017 .netWriteFile = netWriteFileUnavailable,
......@@ -10390,6 +10395,89 @@ fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle
1039010395 unreachable; // How you gonna close something that was impossible to open?
1039110396}
1039210397
10398fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
10399 if (!have_networking) return error.NetworkDown;
10400 const t: *Threaded = @ptrCast(@alignCast(userdata));
10401 const current_thread = Thread.getCurrent(t);
10402
10403 const posix_how: i32 = switch (how) {
10404 .recv => posix.SHUT.RD,
10405 .send => posix.SHUT.WR,
10406 .both => posix.SHUT.RDWR,
10407 };
10408
10409 try current_thread.beginSyscall();
10410 while (true) {
10411 switch (posix.errno(posix.system.shutdown(handle, posix_how))) {
10412 .SUCCESS => {
10413 current_thread.endSyscall();
10414 return;
10415 },
10416 .INTR => {
10417 try current_thread.checkCancel();
10418 continue;
10419 },
10420 else => |e| {
10421 current_thread.endSyscall();
10422 switch (e) {
10423 .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err),
10424 .NOTCONN => return error.SocketUnconnected,
10425 .NOBUFS => return error.SystemResources,
10426 else => |err| return posix.unexpectedErrno(err),
10427 }
10428 },
10429 }
10430 }
10431}
10432
10433fn netShutdownWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
10434 if (!have_networking) return error.NetworkDown;
10435 const t: *Threaded = @ptrCast(@alignCast(userdata));
10436 const current_thread = Thread.getCurrent(t);
10437
10438 const wsa_how: i32 = switch (how) {
10439 .recv => ws2_32.SD_RECEIVE,
10440 .send => ws2_32.SD_SEND,
10441 .both => ws2_32.SD_BOTH,
10442 };
10443
10444 try current_thread.beginSyscall();
10445 while (true) {
10446 const rc = ws2_32.shutdown(handle, wsa_how);
10447 if (rc != ws2_32.SOCKET_ERROR) {
10448 current_thread.endSyscall();
10449 return;
10450 }
10451 switch (ws2_32.WSAGetLastError()) {
10452 .EINTR => {
10453 try current_thread.checkCancel();
10454 continue;
10455 },
10456 .NOTINITIALISED => {
10457 try initializeWsa(t);
10458 try current_thread.checkCancel();
10459 continue;
10460 },
10461 else => |e| {
10462 current_thread.endSyscall();
10463 switch (e) {
10464 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
10465 .ECONNABORTED => return error.ConnectionAborted,
10466 .ECONNRESET => return error.ConnectionResetByPeer,
10467 .ENETDOWN => return error.NetworkDown,
10468 .ENOTCONN => return error.SocketUnconnected,
10469 .EINVAL, .ENOTSOCK => |err| return wsaErrorBug(err),
10470 else => |err| return windows.unexpectedWSAError(err),
10471 }
10472 },
10473 }
10474 }
10475}
10476
10477fn netShutdownUnavailable(_: ?*anyopaque, _: net.Socket.Handle, _: net.ShutdownHow) net.ShutdownError!void {
10478 unreachable; // How you gonna shutdown something that was impossible to open?
10479}
10480
1039310481fn netInterfaceNameResolve(
1039410482 userdata: ?*anyopaque,
1039510483 name: *const net.Interface.Name,
lib/std/Io/net.zig+14
......@@ -954,6 +954,16 @@ pub const SendFlags = packed struct(u8) {
954954 _: u3 = 0,
955955};
956956
957pub const ShutdownHow = enum { recv, send, both };
958
959pub const ShutdownError = error{
960 ConnectionAborted,
961 ConnectionResetByPeer,
962 NetworkDown,
963 SocketUnconnected,
964 SystemResources,
965} || Io.UnexpectedError || Io.Cancelable;
966
957967pub const Interface = struct {
958968 /// Value 0 indicates `none`.
959969 index: u32,
......@@ -1191,6 +1201,10 @@ pub const Stream = struct {
11911201 io.vtable.netClose(io.userdata, (&s.socket.handle)[0..1]);
11921202 }
11931203
1204 pub fn shutdown(s: *const Stream, io: Io, how: ShutdownHow) ShutdownError!void {
1205 return io.vtable.netShutdown(io.userdata, s.socket.handle, how);
1206 }
1207
11941208 pub const Reader = struct {
11951209 io: Io,
11961210 interface: Io.Reader,
lib/std/Io/net/test.zig+2
......@@ -346,6 +346,8 @@ test "non-blocking tcp server" {
346346 const len = try socket_file.read(&buf);
347347 const msg = buf[0..len];
348348 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
349
350 try stream.shutdown(io, .both);
349351}
350352
351353test "decompress compressed DNS name" {