diff --git a/build.zig.zon b/build.zig.zon index 7e33864..96e3e2b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,7 @@ .{ .name = .websocket, .version = "0.1.10", + .minimum_zig_version = "0.17.0-dev.1464+6aff551f1", .dependencies = .{ .zlib = .{ .url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.2.tar.gz", diff --git a/readme.md b/readme.md index f823edc..0bb5940 100644 --- a/readme.md +++ b/readme.md @@ -2,19 +2,19 @@ A WebSocket server and client for Zig — a fork of [karlseguin/websocket.zig](https://github.com/karlseguin/websocket.zig) by Karl -Seguin, maintained for Zig **0.16**. +Seguin, maintained for Zig **0.17.0-dev.1464**. All credit for this library is Karl's. The original is MIT-licensed and that copyright is retained in [LICENSE](./LICENSE). ## what this fork changes -- Zig **0.16** `std.Io` threaded through the server: `WorkerState.init(allocator, io, config)`. +- Zig **0.17** `std.Io` threaded through the server: `WorkerState.init(allocator, io, config)`. - `Server.runIo` for callers that own a `std.Io` listener and task lifecycle. - plain HTTP fallback dispatch on the same listener used for WebSocket upgrades. - bounded, independently configurable WebSocket-client and ordinary-HTTP drain periods for `runIo` and blocking-mode shutdown. -- a 0.16-compatible test runner. +- a 0.17-compatible test runner. Otherwise it tracks upstream. Full upstream documentation (server + client examples, options, migration links) is preserved below; the additions above are diff --git a/src/client/client.zig b/src/client/client.zig index 735c976..351d362 100644 --- a/src/client/client.zig +++ b/src/client/client.zig @@ -267,7 +267,7 @@ pub const Client = struct { switch (message_type) { .text, .binary => { - switch (comptime @typeInfo(@TypeOf(Handler.serverMessage)).@"fn".params.len) { + switch (comptime @typeInfo(@TypeOf(Handler.serverMessage)).@"fn".param_types.len) { 2 => try handler.serverMessage(message.data), 3 => try handler.serverMessage(message.data, if (message_type == .text) .text else .binary), else => @compileError(@typeName(Handler) ++ ".serverMessage must accept 2 or 3 parameters"), @@ -341,7 +341,7 @@ pub const Client = struct { switch (message_type) { .text, .binary => { - switch (comptime @typeInfo(@TypeOf(Handler.serverMessage)).@"fn".params.len) { + switch (comptime @typeInfo(@TypeOf(Handler.serverMessage)).@"fn".param_types.len) { 2 => try handler.serverMessage(message.data), 3 => try handler.serverMessage(message.data, if (message_type == .text) .text else .binary), else => @compileError(@typeName(Handler) ++ ".serverMessage must accept 2 or 3 parameters"), @@ -543,11 +543,10 @@ pub const Stream = struct { } } } - var bufs = [_][]u8{buf}; - return self.io.vtable.netRead(self.io.userdata, self.stream.socket.handle, &bufs) catch |err| { + return posix.read(self.stream.socket.handle, buf) catch |err| { return switch (err) { error.ConnectionResetByPeer => error.ConnectionResetByPeer, - error.Timeout => error.WouldBlock, + error.WouldBlock => error.WouldBlock, else => error.Unexpected, }; }; diff --git a/src/deflate.zig b/src/deflate.zig index 6d85f0d..7716015 100644 --- a/src/deflate.zig +++ b/src/deflate.zig @@ -1,8 +1,6 @@ const std = @import("std"); -const c = @cImport({ - @cInclude("zlib.h"); -}); +const c = @import("zlib.zig"); const Allocator = std.mem.Allocator; diff --git a/src/server/handshake.zig b/src/server/handshake.zig index 71ae702..03985ac 100644 --- a/src/server/handshake.zig +++ b/src/server/handshake.zig @@ -168,7 +168,7 @@ pub const Handshake = struct { }, .connection => { // Connection: keep-alive, Upgrade — the spec allows multiple tokens - if (std.ascii.indexOfIgnoreCase(value, "upgrade") == null) { + if (std.ascii.findIgnoreCase(value, "upgrade") == null) { // Defer — keep parsing to find Content-Length. if (deferred_fallback_err == null) deferred_fallback_err = error.InvalidConnection; } else { diff --git a/src/server/server.zig b/src/server/server.zig index 8838c7a..3247aef 100644 --- a/src/server/server.zig +++ b/src/server/server.zig @@ -28,7 +28,7 @@ const Address = struct { pub fn initUnix(path: []const u8) !Self { var addr: posix.sockaddr.storage = std.mem.zeroes(posix.sockaddr.storage); const sun = @as(*posix.sockaddr.un, @ptrCast(@alignCast(&addr))); - sun.* = .{ .path = [_]u8{0} ** @typeInfo(@TypeOf(sun.path)).array.len }; + sun.* = .{ .path = @splat(0) }; sun.family = posix.AF.UNIX; if (path.len >= sun.path.len) return error.NameTooLong; @memcpy(sun.path[0..path.len], path); @@ -135,17 +135,17 @@ const Address = struct { } }; -// wraps a socket handle for proto.Reader.fill, using Io vtable +// Wraps a socket handle for proto.Reader.fill. This path uses posix.read +// directly because socket-level SO_RCVTIMEO reports EAGAIN, which std.Io's +// blocking net_read operation intentionally does not expose. const SocketReader = struct { socket: posix.socket_t, - io: Io, pub fn read(self: SocketReader, buf: []u8) !usize { - var bufs = [_][]u8{buf}; - return self.io.vtable.netRead(self.io.userdata, self.socket, &bufs) catch |err| { + return posix.read(self.socket, buf) catch |err| { return switch (err) { error.ConnectionResetByPeer => error.ConnectionResetByPeer, - error.Timeout => error.WouldBlock, + error.WouldBlock => error.WouldBlock, else => error.Unexpected, }; }; @@ -180,10 +180,10 @@ const DEFAULT_MAX_CONN = 16_384; const DEFAULT_BUFFER_SIZE = 2048; const DEFAULT_MAX_MESSAGE_SIZE = 65_536; -const EMPTY_PONG = ([2]u8{ @intFromEnum(OpCode.pong), 0 })[0..]; +const EMPTY_PONG = ([2]u8{ @backingInt(OpCode.pong), 0 })[0..]; // CLOSE, 2 length, code -const CLOSE_NORMAL = ([_]u8{ @intFromEnum(OpCode.close), 2, 3, 232 })[0..]; // code: 1000 -const CLOSE_PROTOCOL_ERROR = ([_]u8{ @intFromEnum(OpCode.close), 2, 3, 234 })[0..]; //code: 1002 +const CLOSE_NORMAL = ([_]u8{ @backingInt(OpCode.close), 2, 3, 232 })[0..]; // code: 1000 +const CLOSE_PROTOCOL_ERROR = ([_]u8{ @backingInt(OpCode.close), 2, 3, 234 })[0..]; //code: 1002 const force_blocking: bool = blk: { const build = @import("build"); @@ -625,12 +625,24 @@ pub fn Blocking(comptime H: type) type { const socket = libc.accept(listener, @ptrCast(&address.any), &address_len); if (socket == -1) { const err = posix.errno(-1); - if (err == .CONNABORTED) { - log.info("received shutdown signal", .{}); - return; + switch (err) { + .INTR, .CONNABORTED => continue, + // stop() shuts down and closes the listening socket. + // Linux can report either EINVAL or EBADF depending on + // which syscall wins the race with accept(). + .INVAL, .BADF, .NOTSOCK => { + log.info("received shutdown signal", .{}); + return; + }, + else => { + log.err("failed to accept socket: {}", .{err}); + // Resource errors can persist. Bound the retry rate + // so a degraded listener cannot become a CPU/log + // amplification loop. + self.conn_manager.io.sleep(.fromMilliseconds(10), .awake) catch {}; + continue; + }, } - log.err("failed to accept socket: {}", .{err}); - continue; } // Set CLOEXEC on accepted socket _ = libc.fcntl(socket, libc.F.SETFD, @as(c_int, libc.FD_CLOEXEC)); @@ -732,8 +744,7 @@ pub fn Blocking(comptime H: type) type { } fn interruptShutdown(_: *Self, hc: *HandlerConn(H)) void { - const io = hc.conn.io; - io.vtable.netShutdown(io.userdata, hc.socket, .both) catch {}; + hc.conn.shutdownSocket(.both); } fn waitForConnections(self: *Self, started_ns: i96, grace: Io.Duration, websocket_only: bool) void { @@ -749,11 +760,10 @@ pub fn Blocking(comptime H: type) type { // called for each hc when shutting down fn shutdownCleanup(_: *Self, hc: *HandlerConn(H)) bool { - const io = hc.conn.io; // Wake both readers and writers without racing their next syscall // against a closed descriptor. The connection task owns the final // close after it observes EOF/error or cancellation. - io.vtable.netShutdown(io.userdata, hc.socket, .both) catch {}; + hc.conn.shutdownSocket(.both); return false; } }; @@ -1822,6 +1832,7 @@ pub const Conn = struct { address: Address, io: Io, lock: Io.Mutex = .init, + transport_lock: Io.Mutex = .init, compression: ?*Conn.Compression = null, accept_compression: bool = true, @@ -1889,8 +1900,8 @@ pub const Conn = struct { /// Wake the server's blocked reader without closing its descriptor out /// from underneath it. The read loop owns final socket cleanup. - pub fn interruptRead(self: *const Conn) void { - self.io.vtable.netShutdown(self.io.userdata, self.stream.socket.handle, .recv) catch {}; + pub fn interruptRead(self: *Conn) void { + self.shutdownSocket(.recv); } const CloseOpts = struct { @@ -1925,7 +1936,7 @@ pub const Conn = struct { } var buf: [4]u8 = undefined; - buf[0] = @intFromEnum(OpCode.close); + buf[0] = @backingInt(OpCode.close); buf[1] = @intCast(reason.len + 2); std.mem.writeInt(u16, buf[2..], opts.code, .big); @@ -2028,11 +2039,23 @@ pub const Conn = struct { } fn closeSocket(self: *Conn) void { + self.transport_lock.lockUncancelable(self.io); + defer self.transport_lock.unlock(self.io); + if (@atomicRmw(bool, &self._closed, .Xchg, true, .monotonic) == false) { self.io.vtable.netClose(self.io.userdata, (&self.stream.socket.handle)[0..1]); } } + fn shutdownSocket(self: *Conn, how: net.ShutdownHow) void { + self.transport_lock.lockUncancelable(self.io); + defer self.transport_lock.unlock(self.io); + + if (!self.isClosed()) { + self.io.vtable.netShutdown(self.io.userdata, self.stream.socket.handle, how) catch {}; + } + } + pub const Writer = struct { conn: *Conn, op_code: OpCode, @@ -2084,7 +2107,7 @@ fn _handleHandshake(comptime H: type, worker: anytype, hc: *HandlerConn(H), ctx: return .{ false, false }; } - const n = (SocketReader{ .socket = hc.socket, .io = hc.conn.io }).read(buf[len..]) catch |err| { + const n = (SocketReader{ .socket = hc.socket }).read(buf[len..]) catch |err| { switch (err) { error.ConnectionResetByPeer => log.debug("({f}) handshake connection closed: {}", .{ conn.address, err }), error.WouldBlock => { @@ -2167,7 +2190,7 @@ fn _handleHandshake(comptime H: type, worker: anytype, hc: *HandlerConn(H), ctx: fn afterInit(comptime H: type, hc: *HandlerConn(H), ctx: anytype) !void { if (comptime std.meta.hasFn(H, "afterInit")) { - const params = @typeInfo(@TypeOf(H.afterInit)).@"fn".params; + const params = @typeInfo(@TypeOf(H.afterInit)).@"fn".param_types; const result = if (params.len == 1) hc.handler.?.afterInit() else hc.handler.?.afterInit(ctx); result catch |err| { log.debug("({f}) " ++ @typeName(H) ++ ".afterInit error: {}", .{ hc.conn.address, err }); @@ -2187,7 +2210,7 @@ fn handleClientData(comptime H: type, hc: *HandlerConn(H), allocator: Allocator, fn _handleClientData(comptime H: type, hc: *HandlerConn(H), allocator: Allocator, fba: ?*FixedBufferAllocator) !bool { var conn = &hc.conn; var reader = &hc.reader.?; - reader.fill(SocketReader{ .socket = conn.stream.socket.handle, .io = conn.io }) catch |err| { + reader.fill(SocketReader{ .socket = conn.stream.socket.handle }) catch |err| { switch (err) { error.Closed, error.ConnectionResetByPeer => log.debug("({f}) connection closed: {}", .{ conn.address, err }), else => log.warn("({f}) error reading from connection: {}", .{ conn.address, err }), @@ -2219,7 +2242,7 @@ fn _handleClientData(comptime H: type, hc: *HandlerConn(H), allocator: Allocator log.debug("({f}) received {s} message", .{ hc.conn.address, @tagName(message_type) }); switch (message_type) { .text, .binary => { - const params = @typeInfo(@TypeOf(H.clientMessage)).@"fn".params; + const params = @typeInfo(@TypeOf(H.clientMessage)).@"fn".param_types; const needs_allocator = comptime needsAllocator(H); var arena: std.heap.ArenaAllocator = undefined; @@ -2326,8 +2349,8 @@ fn _handleClientData(comptime H: type, hc: *HandlerConn(H), allocator: Allocator } fn needsAllocator(comptime H: type) bool { - const params = @typeInfo(@TypeOf(H.clientMessage)).@"fn".params; - return comptime params[1].type == Allocator; + const params = @typeInfo(@TypeOf(H.clientMessage)).@"fn".param_types; + return comptime params[1] == Allocator; } /// Result of parsing a plain HTTP request from a raw buffer. @@ -2582,7 +2605,8 @@ test "Server: parser errors are reported to the handler" { var stream = try testStreamPort(true, 9294); defer stream.close(); - try stream.writeAll(&proto.frame(.text, "x" ** 4097)); + const oversized_payload: [4097]u8 = @splat('x'); + try stream.writeAll(&proto.frame(.text, &oversized_payload)); var attempts: usize = 0; while (observed.load(.acquire) == 0 and attempts < 1000) : (attempts += 1) { @@ -3084,7 +3108,7 @@ const TestStream = struct { } fn read(self: *const TestStream, buf: []u8) !usize { - return (SocketReader{ .socket = self.socket, .io = self.io }).read(buf); + return (SocketReader{ .socket = self.socket }).read(buf); } }; @@ -3193,8 +3217,13 @@ const TestHandler = struct { } }; -const compression_history_a = "alpha-window::the first dictionary must survive a different middle message::0123456789abcdef::" ** 120; -const compression_history_b = "beta-window::this second dictionary must not erase the earlier stream history::fedcba9876543210::" ** 120; +const compression_history_a_chunk = "alpha-window::the first dictionary must survive a different middle message::0123456789abcdef::"; +const compression_history_a_repeated: [120][compression_history_a_chunk.len]u8 = @splat(compression_history_a_chunk.*); +const compression_history_a = std.mem.asBytes(&compression_history_a_repeated); + +const compression_history_b_chunk = "beta-window::this second dictionary must not erase the earlier stream history::fedcba9876543210::"; +const compression_history_b_repeated: [120][compression_history_b_chunk.len]u8 = @splat(compression_history_b_chunk.*); +const compression_history_b = std.mem.asBytes(&compression_history_b_repeated); const compression_test_messages = [_][]const u8{ compression_history_a, compression_history_b, @@ -3287,7 +3316,7 @@ const FanoutShutdownHandler = struct { pub fn serverClose(self: *FanoutShutdownHandler) void { if (self.blocked) { self.context.blocked_close_started.store(true, .release); - const payload = [_]u8{'x'} ** (16 * 1024); + const payload: [16 * 1024]u8 = @splat('x'); while (true) self.conn.writeBin(&payload) catch { self.context.blocked_close_unblocked.store(true, .release); return; @@ -3330,7 +3359,7 @@ const SlowHttpHandler = struct { context.unblocked.store(true, .release); return; }; - const body = [_]u8{'x'} ** (16 * 1024); + const body: [16 * 1024]u8 = @splat('x'); while (true) socketWriteAll(context.io, socket, &body) catch { context.unblocked.store(true, .release); return; diff --git a/src/server/thread_pool.zig b/src/server/thread_pool.zig index 4feb68d..9c6a3b7 100644 --- a/src/server/thread_pool.zig +++ b/src/server/thread_pool.zig @@ -27,8 +27,8 @@ pub fn ThreadPool(comptime F: anytype) type { // So F would be: handle(server: *Server, conn: *Conn, buf: []u8) // and FullArgs would be our 3 args.... const FullArgs = std.meta.ArgsTuple(@TypeOf(F)); - const full_fields = std.meta.fields(FullArgs); - const ARG_COUNT = full_fields.len - 1; + const full_field_types = std.meta.fieldTypes(FullArgs); + const ARG_COUNT = full_field_types.len - 1; // Args will be FullArgs[0..len-1], so in the above example, args would be // (*Server, *Conn) @@ -42,8 +42,8 @@ pub fn ThreadPool(comptime F: anytype) type { // 0.16: @Type was removed, use @Tuple instead var field_types: [ARG_COUNT]type = undefined; - inline for (full_fields[0..ARG_COUNT], 0..) |field, index| { - field_types[index] = field.type; + inline for (full_field_types[0..ARG_COUNT], 0..) |field_type, index| { + field_types[index] = field_type; } const Args = @Tuple(&field_types); diff --git a/src/t.zig b/src/t.zig index 45426f0..83f4d7f 100644 --- a/src/t.zig +++ b/src/t.zig @@ -145,17 +145,15 @@ pub const Writer = struct { } }; -/// reads from a net.Stream via the Io vtable (replaces raw libc recv) +/// Reads directly from a socket, preserving SO_RCVTIMEO as WouldBlock. pub const StreamReader = struct { - io: Io, handle: net.Socket.Handle, pub fn read(self: *StreamReader, buf: []u8) !usize { - var bufs = [_][]u8{buf}; - return self.io.vtable.netRead(self.io.userdata, self.handle, &bufs) catch |err| { + return std.posix.read(self.handle, buf) catch |err| { return switch (err) { error.ConnectionResetByPeer => error.ConnectionResetByPeer, - error.Timeout => error.WouldBlock, + error.WouldBlock => error.WouldBlock, else => error.Unexpected, }; }; @@ -241,10 +239,10 @@ pub const SocketPair = struct { } pub fn serverReader(self: *SocketPair) StreamReader { - return .{ .io = self.io, .handle = self.server.socket.handle }; + return .{ .handle = self.server.socket.handle }; } pub fn clientReader(self: *SocketPair) StreamReader { - return .{ .io = self.io, .handle = self.client.socket.handle }; + return .{ .handle = self.client.socket.handle }; } }; diff --git a/src/websocket.zig b/src/websocket.zig index e2b955f..9a41f82 100644 --- a/src/websocket.zig +++ b/src/websocket.zig @@ -58,8 +58,8 @@ test "frameText" { } { - const msg = "A" ** 130; - const framed = frameText(msg); + const msg: [130]u8 = @splat('A'); + const framed = frameText(&msg); try t.expectEqual(134, framed.len); @@ -87,8 +87,8 @@ test "frameBin" { } { - const msg = "A" ** 130; - const framed = frameBin(msg); + const msg: [130]u8 = @splat('A'); + const framed = frameBin(&msg); try t.expectEqual(134, framed.len); diff --git a/src/zlib.zig b/src/zlib.zig new file mode 100644 index 0000000..3c94262 --- /dev/null +++ b/src/zlib.zig @@ -0,0 +1,100 @@ +//! Minimal bindings for the pinned zlib dependency. +//! +//! Zig 0.17 removed `@cImport`. Keeping the small ABI surface used by +//! `deflate.zig` here avoids running translate-c over all of zlib.h on every +//! clean build. + +pub const uInt = c_uint; +pub const uLong = c_ulong; +pub const Bytef = u8; +pub const voidpf = ?*anyopaque; + +pub const alloc_func = ?*const fn ( + @"opaque": voidpf, + items: uInt, + size: uInt, +) callconv(.c) voidpf; + +pub const free_func = ?*const fn ( + @"opaque": voidpf, + address: voidpf, +) callconv(.c) void; + +const internal_state = opaque {}; + +pub const z_stream = extern struct { + next_in: [*c]Bytef, + avail_in: uInt, + total_in: uLong, + next_out: [*c]Bytef, + avail_out: uInt, + total_out: uLong, + msg: [*c]u8, + state: ?*internal_state, + zalloc: alloc_func, + zfree: free_func, + @"opaque": voidpf, + data_type: c_int, + adler: uLong, + reserved: uLong, +}; + +pub const Z_NO_FLUSH: c_int = 0; +pub const Z_SYNC_FLUSH: c_int = 2; +pub const Z_OK: c_int = 0; +pub const Z_BUF_ERROR: c_int = -5; +pub const Z_DEFAULT_COMPRESSION: c_int = -1; +pub const Z_DEFAULT_STRATEGY: c_int = 0; +pub const Z_DEFLATED: c_int = 8; +pub const MAX_WBITS: c_int = 15; + +const ZLIB_VERSION: [*:0]const u8 = "1.3.2"; + +pub fn deflateInit2( + stream: *z_stream, + level: c_int, + method: c_int, + window_bits: c_int, + mem_level: c_int, + strategy: c_int, +) c_int { + return deflateInit2_( + stream, + level, + method, + window_bits, + mem_level, + strategy, + ZLIB_VERSION, + @sizeOf(z_stream), + ); +} + +pub fn inflateInit2(stream: *z_stream, window_bits: c_int) c_int { + return inflateInit2_(stream, window_bits, ZLIB_VERSION, @sizeOf(z_stream)); +} + +extern fn deflateInit2_( + stream: *z_stream, + level: c_int, + method: c_int, + window_bits: c_int, + mem_level: c_int, + strategy: c_int, + version: [*:0]const u8, + stream_size: c_int, +) c_int; + +extern fn inflateInit2_( + stream: *z_stream, + window_bits: c_int, + version: [*:0]const u8, + stream_size: c_int, +) c_int; + +pub extern fn deflate(stream: *z_stream, flush: c_int) c_int; +pub extern fn deflateEnd(stream: *z_stream) c_int; +pub extern fn deflateReset(stream: *z_stream) c_int; +pub extern fn inflate(stream: *z_stream, flush: c_int) c_int; +pub extern fn inflateEnd(stream: *z_stream) c_int; +pub extern fn inflateReset(stream: *z_stream) c_int; diff --git a/support/autobahn/client/build.zig.zon b/support/autobahn/client/build.zig.zon index dcd40b8..79a7448 100644 --- a/support/autobahn/client/build.zig.zon +++ b/support/autobahn/client/build.zig.zon @@ -2,6 +2,7 @@ .name = .autobahn_test_client, .paths = .{""}, .version = "0.0.0", + .minimum_zig_version = "0.17.0-dev.1464+6aff551f1", .fingerprint = 0xf155d3bf7ec3bfc, .dependencies = .{ .websocket = .{ .path = "../../../" }, diff --git a/support/autobahn/client/main.zig b/support/autobahn/client/main.zig index 409576f..44006c2 100644 --- a/support/autobahn/client/main.zig +++ b/support/autobahn/client/main.zig @@ -4,8 +4,8 @@ const websocket = @import("websocket"); const Allocator = std.mem.Allocator; pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.detectLeaks(); + var gpa: std.heap.DebugAllocator(.{}) = .init; + defer _ = gpa.deinit(); const allocator = gpa.allocator(); @@ -46,7 +46,7 @@ pub fn main() !void { }; // wait 5 seconds for autobanh server to be up - std.Thread.sleep(std.time.ns_per_s * 5); + _ = try std.Options.debug_io.sleep(.fromSeconds(5), .awake); var buffer_provider = try websocket.bufferProvider(allocator, .{ .count = 10, .size = 32768, .max = 20_000_000 }); defer buffer_provider.deinit(); @@ -80,7 +80,7 @@ pub fn main() !void { } fn updateReport(allocator: std.mem.Allocator) !void { - var client = try websocket.Client.init(allocator, .{ + var client = try websocket.Client.init(std.Options.debug_io, allocator, .{ .port = 9001, .host = "localhost", }); @@ -100,7 +100,7 @@ const Handler = struct { const path = try std.fmt.allocPrint(allocator, "/runCase?casetuple={s}&agent=websocket.zig", .{case}); defer allocator.free(path); - var client = try websocket.Client.init(allocator, .{ + var client = try websocket.Client.init(std.Options.debug_io, allocator, .{ .port = 9001, .host = "localhost", .buffer_provider = buffer_provider, diff --git a/support/autobahn/server/build.zig.zon b/support/autobahn/server/build.zig.zon index 9c9c4d7..da82417 100644 --- a/support/autobahn/server/build.zig.zon +++ b/support/autobahn/server/build.zig.zon @@ -2,6 +2,7 @@ .name = .autobahn_test_server, .paths = .{""}, .version = "0.0.0", + .minimum_zig_version = "0.17.0-dev.1464+6aff551f1", .fingerprint = 0x923c8c98ca4d09ab, .dependencies = .{ .websocket = .{ .path = "../../../" }, diff --git a/support/autobahn/server/main.zig b/support/autobahn/server/main.zig index e34d868..4f234a9 100644 --- a/support/autobahn/server/main.zig +++ b/support/autobahn/server/main.zig @@ -14,12 +14,11 @@ var nonblocking_server: websocket.Server(Handler) = undefined; var nonblocking_bp_server: websocket.Server(Handler) = undefined; pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + var gpa: std.heap.DebugAllocator(.{}) = .init; + defer _ = gpa.deinit(); const allocator = gpa.allocator(); if (@import("builtin").os.tag != .windows) { - defer _ = gpa.detectLeaks(); - std.posix.sigaction(std.posix.SIG.TERM, &.{ .handler = .{ .handler = shutdown }, .mask = std.posix.sigemptyset(), @@ -38,7 +37,7 @@ pub fn main() !void { } fn startNonBlocking(allocator: Allocator) !std.Thread { - nonblocking_server = try websocket.Server(Handler).init(allocator, .{ + nonblocking_server = try websocket.Server(Handler).init(allocator, std.Options.debug_io, .{ .port = 9224, .address = "127.0.0.1", .buffers = .{ @@ -62,7 +61,7 @@ fn startNonBlocking(allocator: Allocator) !std.Thread { } fn startNonBlockingBufferPool(allocator: Allocator) !std.Thread { - nonblocking_bp_server = try websocket.Server(Handler).init(allocator, .{ + nonblocking_bp_server = try websocket.Server(Handler).init(allocator, std.Options.debug_io, .{ .port = 9225, .address = "127.0.0.1", .buffers = .{ @@ -109,7 +108,7 @@ const Handler = struct { } }; -fn shutdown(_: c_int) callconv(.c) void { +fn shutdown(_: std.posix.SIG) callconv(.c) void { nonblocking_server.stop(); nonblocking_bp_server.stop(); } diff --git a/test_runner.zig b/test_runner.zig index 54f2ec9..3847484 100644 --- a/test_runner.zig +++ b/test_runner.zig @@ -13,7 +13,7 @@ const builtin = @import("builtin"); const Allocator = std.mem.Allocator; -const BORDER = "=" ** 80; +const BORDER: [80]u8 = @splat('='); // use in custom panic handler var current_test: ?[]const u8 = null; @@ -74,13 +74,13 @@ pub fn main() !void { }; current_test = friendly_name; - std.testing.allocator_instance = .{}; + std.testing.allocator_instance = .init(std.heap.page_allocator, .{}); const result = t.func(); current_test = null; const ns_taken = slowest.endTiming(friendly_name); - if (std.testing.allocator_instance.deinit() == .leak) { + if (std.testing.allocator_instance.deinit() != 0) { leak += 1; Printer.status(.fail, "\n{s}\n\"{s}\" - Memory Leak\n{s}\n", .{ BORDER, friendly_name, BORDER }); } @@ -255,7 +255,7 @@ const Env = struct { fn readEnv(allocator: Allocator, key: []const u8) ?[]const u8 { // 0.16: std.process.getEnvVarOwned removed, use std.c.getenv - const key_z = allocator.dupeZ(u8, key) catch return null; + const key_z = allocator.dupeSentinel(u8, key, 0) catch return null; defer allocator.free(key_z); if (std.c.getenv(key_z)) |ptr| { const value = std.mem.span(ptr);