From ea410c8ced6acb8961e09bf91217b8eb31f60fd7 Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Thu, 20 Jun 2024 00:32:06 +0200 Subject: [PATCH] Fix changes in Zig 0.12 std lib Signed-off-by: Francis Bouvier --- src/main.zig | 103 ++++++++++++++++++++++++++++++++++++++++++++++++- src/server.zig | 10 ++--- 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/src/main.zig b/src/main.zig index b9604eb6..c6363b32 100644 --- a/src/main.zig +++ b/src/main.zig @@ -17,6 +17,7 @@ // along with this program. If not, see . const std = @import("std"); +const posix = std.posix; const jsruntime = @import("jsruntime"); @@ -31,6 +32,102 @@ pub const UserContext = apiweb.UserContext; const socket_path = "/tmp/browsercore-server.sock"; +// Inspired by std.net.StreamServer in Zig < 0.12 +pub const StreamServer = struct { + /// Copied from `Options` on `init`. + kernel_backlog: u31, + reuse_address: bool, + reuse_port: bool, + nonblocking: bool, + + /// `undefined` until `listen` returns successfully. + listen_address: std.net.Address, + + sockfd: ?posix.socket_t, + + pub const Options = struct { + /// How many connections the kernel will accept on the application's behalf. + /// If more than this many connections pool in the kernel, clients will start + /// seeing "Connection refused". + kernel_backlog: u31 = 128, + + /// Enable SO.REUSEADDR on the socket. + reuse_address: bool = false, + + /// Enable SO.REUSEPORT on the socket. + reuse_port: bool = false, + + /// Non-blocking mode. + nonblocking: bool = false, + }; + + /// After this call succeeds, resources have been acquired and must + /// be released with `deinit`. + pub fn init(options: Options) StreamServer { + return StreamServer{ + .sockfd = null, + .kernel_backlog = options.kernel_backlog, + .reuse_address = options.reuse_address, + .reuse_port = options.reuse_port, + .nonblocking = options.nonblocking, + .listen_address = undefined, + }; + } + + /// Release all resources. The `StreamServer` memory becomes `undefined`. + pub fn deinit(self: *StreamServer) void { + self.close(); + self.* = undefined; + } + + pub fn listen(self: *StreamServer, address: std.net.Address) !void { + const sock_flags = posix.SOCK.STREAM | posix.SOCK.CLOEXEC; + var use_sock_flags: u32 = sock_flags; + if (self.nonblocking) use_sock_flags |= posix.SOCK.NONBLOCK; + const proto = if (address.any.family == posix.AF.UNIX) @as(u32, 0) else posix.IPPROTO.TCP; + + const sockfd = try posix.socket(address.any.family, use_sock_flags, proto); + self.sockfd = sockfd; + errdefer { + posix.close(sockfd); + self.sockfd = null; + } + + if (self.reuse_address) { + try posix.setsockopt( + sockfd, + posix.SOL.SOCKET, + posix.SO.REUSEADDR, + &std.mem.toBytes(@as(c_int, 1)), + ); + } + if (@hasDecl(posix.SO, "REUSEPORT") and self.reuse_port) { + try posix.setsockopt( + sockfd, + posix.SOL.SOCKET, + posix.SO.REUSEPORT, + &std.mem.toBytes(@as(c_int, 1)), + ); + } + + var socklen = address.getOsSockLen(); + try posix.bind(sockfd, &address.any, socklen); + try posix.listen(sockfd, self.kernel_backlog); + try posix.getsockname(sockfd, &self.listen_address.any, &socklen); + } + + /// Stop listening. It is still necessary to call `deinit` after stopping listening. + /// Calling `deinit` will automatically call `close`. It is safe to call `close` when + /// not listening. + pub fn close(self: *StreamServer) void { + if (self.sockfd) |fd| { + posix.close(fd); + self.sockfd = null; + self.listen_address = undefined; + } + } +}; + pub fn main() !void { // create v8 vm @@ -53,13 +150,15 @@ pub fn main() !void { // server const addr = try std.net.Address.initUnix(socket_path); - var srv = std.net.StreamServer.init(.{ + var srv = StreamServer.init(.{ .reuse_address = true, .reuse_port = true, - .force_nonblocking = true, + .nonblocking = true, }); defer srv.deinit(); + try srv.listen(addr); + defer srv.close(); std.debug.print("Listening on: {s}...\n", .{socket_path}); var browser = try Browser.init(arena.allocator(), vm); diff --git a/src/server.zig b/src/server.zig index 8597912d..c53f21e3 100644 --- a/src/server.zig +++ b/src/server.zig @@ -24,7 +24,7 @@ const MaxStdOutSize = 512; // ensure debug msg are not too long pub const Cmd = struct { // internal fields - socket: std.os.socket_t, + socket: std.posix.socket_t, buf: []u8, // only for read operations err: ?Error = null, @@ -154,7 +154,7 @@ pub fn sendAsync(ctx: *Cmd, msg: []const u8) !void { pub fn sendSync(ctx: *Cmd, msg: []const u8) !void { defer ctx.alloc().free(msg); - const s = try std.os.write(ctx.socket, msg); + const s = try std.posix.write(ctx.socket, msg); std.log.debug("send sync {d} bytes", .{s}); } @@ -163,9 +163,9 @@ pub fn sendSync(ctx: *Cmd, msg: []const u8) !void { const Accept = struct { cmd: *Cmd, - socket: std.os.socket_t, + socket: std.posix.socket_t, - fn cbk(self: *Accept, completion: *Completion, result: AcceptError!std.os.socket_t) void { + fn cbk(self: *Accept, completion: *Completion, result: AcceptError!std.posix.socket_t) void { self.cmd.socket = result catch |err| { self.cmd.err = err; return; @@ -179,7 +179,7 @@ const Accept = struct { // Listen // ------ -pub fn listen(browser: *Browser, socket: std.os.socket_t) anyerror!void { +pub fn listen(browser: *Browser, socket: std.posix.socket_t) anyerror!void { const loop = browser.currentSession().loop; // MsgBuffer