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 {...@@ -734,6 +734,7 @@ pub const VTable = struct {
734 netWrite: *const fn (?*anyopaque, dest: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize,734 netWrite: *const fn (?*anyopaque, dest: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize,
735 netWriteFile: *const fn (?*anyopaque, net.Socket.Handle, header: []const u8, *Io.File.Reader, Io.Limit) net.Stream.Writer.WriteFileError!usize,735 netWriteFile: *const fn (?*anyopaque, net.Socket.Handle, header: []const u8, *Io.File.Reader, Io.Limit) net.Stream.Writer.WriteFileError!usize,
736 netClose: *const fn (?*anyopaque, handle: []const net.Socket.Handle) void,736 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,
737 netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface,738 netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface,
738 netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name,739 netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name,
739 netLookup: *const fn (?*anyopaque, net.HostName, *Queue(net.HostName.LookupResult), net.HostName.LookupOptions) net.HostName.LookupError!void,740 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 {...@@ -900,6 +900,7 @@ pub fn io(k: *Kqueue) Io {
900 .netConnectIp = netConnectIp,900 .netConnectIp = netConnectIp,
901 .netConnectUnix = netConnectUnix,901 .netConnectUnix = netConnectUnix,
902 .netClose = netClose,902 .netClose = netClose,
903 .netShutdown = netShutdown,
903 .netRead = netRead,904 .netRead = netRead,
904 .netWrite = netWrite,905 .netWrite = netWrite,
905 .netSend = netSend,906 .netSend = netSend,
...@@ -1549,12 +1550,22 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8,...@@ -1549,12 +1550,22 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8,
1549 _ = splat;1550 _ = splat;
1550 @panic("TODO");1551 @panic("TODO");
1551}1552}
1553
1552fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void {1554fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void {
1553 const k: *Kqueue = @ptrCast(@alignCast(userdata));1555 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1554 _ = k;1556 _ = k;
1555 _ = handle;1557 _ = handle;
1556 @panic("TODO");1558 @panic("TODO");
1557}1559}
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
1558fn netInterfaceNameResolve(1569fn netInterfaceNameResolve(
1559 userdata: ?*anyopaque,1570 userdata: ?*anyopaque,
1560 name: *const net.Interface.Name,1571 name: *const net.Interface.Name,
...@@ -1564,12 +1575,14 @@ fn netInterfaceNameResolve(...@@ -1564,12 +1575,14 @@ fn netInterfaceNameResolve(
1564 _ = name;1575 _ = name;
1565 @panic("TODO");1576 @panic("TODO");
1566}1577}
1578
1567fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {1579fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
1568 const k: *Kqueue = @ptrCast(@alignCast(userdata));1580 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1569 _ = k;1581 _ = k;
1570 _ = interface;1582 _ = interface;
1571 @panic("TODO");1583 @panic("TODO");
1572}1584}
1585
1573fn netLookup(1586fn netLookup(
1574 userdata: ?*anyopaque,1587 userdata: ?*anyopaque,
1575 host_name: net.HostName,1588 host_name: net.HostName,
lib/std/Io/Threaded.zig+88
...@@ -891,6 +891,10 @@ pub fn io(t: *Threaded) Io {...@@ -891,6 +891,10 @@ pub fn io(t: *Threaded) Io {
891 else => netConnectUnixPosix,891 else => netConnectUnixPosix,
892 },892 },
893 .netClose = netClose,893 .netClose = netClose,
894 .netShutdown = switch (native_os) {
895 .windows => netShutdownWindows,
896 else => netShutdownPosix,
897 },
894 .netRead = switch (native_os) {898 .netRead = switch (native_os) {
895 .windows => netReadWindows,899 .windows => netReadWindows,
896 else => netReadPosix,900 else => netReadPosix,
...@@ -1007,6 +1011,7 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -1007,6 +1011,7 @@ pub fn ioBasic(t: *Threaded) Io {
1007 .netConnectIp = netConnectIpUnavailable,1011 .netConnectIp = netConnectIpUnavailable,
1008 .netConnectUnix = netConnectUnixUnavailable,1012 .netConnectUnix = netConnectUnixUnavailable,
1009 .netClose = netCloseUnavailable,1013 .netClose = netCloseUnavailable,
1014 .netShutdown = netShutdownUnavailable,
1010 .netRead = netReadUnavailable,1015 .netRead = netReadUnavailable,
1011 .netWrite = netWriteUnavailable,1016 .netWrite = netWriteUnavailable,
1012 .netWriteFile = netWriteFileUnavailable,1017 .netWriteFile = netWriteFileUnavailable,
...@@ -10390,6 +10395,89 @@ fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle...@@ -10390,6 +10395,89 @@ fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle
10390 unreachable; // How you gonna close something that was impossible to open?10395 unreachable; // How you gonna close something that was impossible to open?
10391}10396}
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
10393fn netInterfaceNameResolve(10481fn netInterfaceNameResolve(
10394 userdata: ?*anyopaque,10482 userdata: ?*anyopaque,
10395 name: *const net.Interface.Name,10483 name: *const net.Interface.Name,
lib/std/Io/net.zig+14
...@@ -954,6 +954,16 @@ pub const SendFlags = packed struct(u8) {...@@ -954,6 +954,16 @@ pub const SendFlags = packed struct(u8) {
954 _: u3 = 0,954 _: u3 = 0,
955};955};
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
957pub const Interface = struct {967pub const Interface = struct {
958 /// Value 0 indicates `none`.968 /// Value 0 indicates `none`.
959 index: u32,969 index: u32,
...@@ -1191,6 +1201,10 @@ pub const Stream = struct {...@@ -1191,6 +1201,10 @@ pub const Stream = struct {
1191 io.vtable.netClose(io.userdata, (&s.socket.handle)[0..1]);1201 io.vtable.netClose(io.userdata, (&s.socket.handle)[0..1]);
1192 }1202 }
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
1194 pub const Reader = struct {1208 pub const Reader = struct {
1195 io: Io,1209 io: Io,
1196 interface: Io.Reader,1210 interface: Io.Reader,
lib/std/Io/net/test.zig+2
...@@ -346,6 +346,8 @@ test "non-blocking tcp server" {...@@ -346,6 +346,8 @@ test "non-blocking tcp server" {
346 const len = try socket_file.read(&buf);346 const len = try socket_file.read(&buf);
347 const msg = buf[0..len];347 const msg = buf[0..len];
348 try testing.expect(mem.eql(u8, msg, "hello from server\n"));348 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
349
350 try stream.shutdown(io, .both);
349}351}
350352
351test "decompress compressed DNS name" {353test "decompress compressed DNS name" {