A websocket implementation for zig
0

Configure Feed

Select the types of activity you want to include in your feed.

dedupe frame writing

better 32-bit support

let autobahn test be run on windows

Karl Seguin (Aug 8, 2024, 2:23 PM +0800) 2448c2a8 90c72e51

+68 -91
+10 -30
src/client/client.zig
··· 240 240 } 241 241 242 242 pub fn writeFrame(self: *Client, op_code: proto.OpCode, data: []u8) !void { 243 - const l = data.len; 244 - const mask = self._mask_fn(); 245 - var stream = &self.stream; 246 - 247 243 // maximum possible prefix length. op_code + length_type + 8byte length + 4 byte mask 248 244 var buf: [14]u8 = undefined; 249 - buf[0] = @intFromEnum(op_code); 245 + const header = proto.writeFrameHeader(&buf, op_code, data.len); 250 246 251 - if (l <= 125) { 252 - buf[1] = @as(u8, @intCast(l)) | 128; 253 - @memcpy(buf[2..6], &mask); 254 - try stream.writeAll(buf[0..6]); 255 - } else if (l < 65536) { 256 - buf[1] = 254; // 126 | 128 257 - buf[2] = @intCast((l >> 8) & 0xFF); 258 - buf[3] = @intCast(l & 0xFF); 259 - @memcpy(buf[4..8], &mask); 260 - try stream.writeAll(buf[0..8]); 261 - } else { 262 - buf[1] = 255; // 127 | 128 263 - buf[2] = @intCast((l >> 56) & 0xFF); 264 - buf[3] = @intCast((l >> 48) & 0xFF); 265 - buf[4] = @intCast((l >> 40) & 0xFF); 266 - buf[5] = @intCast((l >> 32) & 0xFF); 267 - buf[6] = @intCast((l >> 24) & 0xFF); 268 - buf[7] = @intCast((l >> 16) & 0xFF); 269 - buf[8] = @intCast((l >> 8) & 0xFF); 270 - buf[9] = @intCast(l & 0xFF); 271 - @memcpy(buf[10..], &mask); 272 - try stream.writeAll(buf[0..]); 273 - } 247 + const header_len = header.len; 248 + const header_end = header.len + 4; // for the mask 274 249 275 - if (l > 0) { 250 + buf[1] |= 128; // indicate that the payload is masked 251 + const mask = self._mask_fn(); 252 + @memcpy(buf[header_len..header_end], &mask); 253 + 254 + try self.stream.writeAll(buf[0..header_end]); 255 + if (data.len > 0) { 276 256 proto.mask(&mask, data); 277 - try stream.writeAll(data); 257 + try self.stream.writeAll(data); 278 258 } 279 259 } 280 260
+33 -21
src/proto.zig
··· 439 439 440 440 pub fn frame(op_code: OpCode, comptime msg: []const u8) [calculateFrameLen(msg)]u8 { 441 441 var framed: [calculateFrameLen(msg)]u8 = undefined; 442 - framed[0] = @intFromEnum(op_code); 442 + const header = writeFrameHeader(&framed, op_code, msg.len); 443 + @memcpy(framed[header.len..], msg); 444 + return framed; 445 + } 443 446 444 - const len = msg.len; 445 - if (len <= 125) { 446 - framed[1] = @intCast(len); 447 - @memcpy(framed[2..], msg); 448 - } else if (len < 65536) { 449 - framed[1] = 126; 450 - framed[2] = @intCast((len >> 8) & 0xFF); 451 - framed[3] = @intCast(len & 0xFF); 452 - @memcpy(framed[4..], msg); 447 + pub fn writeFrameHeader(buf: []u8, op_code: OpCode, l: usize) []u8 { 448 + buf[0] = @intFromEnum(op_code); 449 + 450 + if (l <= 125) { 451 + buf[1] = @intCast(l); 452 + return buf[0..2]; 453 + } 454 + if (l < 65536) { 455 + buf[1] = 126; 456 + buf[2] = @intCast((l >> 8) & 0xFF); 457 + buf[3] = @intCast(l & 0xFF); 458 + return buf[0..4]; 459 + } 460 + 461 + buf[1] = 127; 462 + if (comptime builtin.target.ptrBitWidth() >= 64) { 463 + buf[2] = @intCast((l >> 56) & 0xFF); 464 + buf[3] = @intCast((l >> 48) & 0xFF); 465 + buf[4] = @intCast((l >> 40) & 0xFF); 466 + buf[5] = @intCast((l >> 32) & 0xFF); 453 467 } else { 454 - framed[1] = 127; 455 - framed[2] = @intCast((len >> 56) & 0xFF); 456 - framed[3] = @intCast((len >> 48) & 0xFF); 457 - framed[4] = @intCast((len >> 40) & 0xFF); 458 - framed[5] = @intCast((len >> 32) & 0xFF); 459 - framed[6] = @intCast((len >> 24) & 0xFF); 460 - framed[7] = @intCast((len >> 16) & 0xFF); 461 - framed[8] = @intCast((len >> 8) & 0xFF); 462 - framed[9] = @intCast(len & 0xFF); 463 - @memcpy(framed[10..], msg); 468 + buf[2] = 0; 469 + buf[3] = 0; 470 + buf[4] = 0; 471 + buf[5] = 0; 464 472 } 465 - return framed; 473 + buf[6] = @intCast((l >> 24) & 0xFF); 474 + buf[7] = @intCast((l >> 16) & 0xFF); 475 + buf[8] = @intCast((l >> 8) & 0xFF); 476 + buf[9] = @intCast(l & 0xFF); 477 + return buf[0..10]; 466 478 } 467 479 468 480 pub fn calculateFrameLen(comptime msg: []const u8) usize {
+4 -27
src/server/server.zig
··· 1282 1282 } 1283 1283 1284 1284 pub fn writeFrame(self: *Conn, op_code: OpCode, data: []const u8) !void { 1285 - const l: u64 = data.len; 1286 - const stream = self.stream; 1287 - 1288 1285 // maximum possible prefix length. op_code + length_type + 8byte length 1289 1286 var buf: [10]u8 = undefined; 1290 - var header: []const u8 = undefined; 1291 - buf[0] = @intFromEnum(op_code); 1287 + const header = proto.writeFrameHeader(&buf, op_code, data.len); 1292 1288 1293 - if (l <= 125) { 1294 - buf[1] = @intCast(l); 1295 - header = buf[0..2]; 1296 - } else if (l < 65536) { 1297 - buf[1] = 126; 1298 - buf[2] = @intCast((l >> 8) & 0xFF); 1299 - buf[3] = @intCast(l & 0xFF); 1300 - header = buf[0..4]; 1301 - } else { 1302 - buf[1] = 127; 1303 - buf[2] = @intCast((l >> 56) & 0xFF); 1304 - buf[3] = @intCast((l >> 48) & 0xFF); 1305 - buf[4] = @intCast((l >> 40) & 0xFF); 1306 - buf[5] = @intCast((l >> 32) & 0xFF); 1307 - buf[6] = @intCast((l >> 24) & 0xFF); 1308 - buf[7] = @intCast((l >> 16) & 0xFF); 1309 - buf[8] = @intCast((l >> 8) & 0xFF); 1310 - buf[9] = @intCast(l & 0xFF); 1311 - header = buf[0..]; 1312 - } 1289 + const stream = self.stream; 1313 1290 1314 - if (l == 0) { 1291 + if (data.len == 0) { 1315 1292 // no body, just write the header 1316 1293 self.lock.lock(); 1317 1294 defer self.lock.unlock(); ··· 1674 1651 } 1675 1652 1676 1653 fn timestamp() u32 { 1677 - if (comptime @hasDecl(std.c, "CLOCK") == false) { 1654 + if (comptime @hasDecl(posix, "CLOCK") == false or posix.CLOCK == void) { 1678 1655 return @intCast(std.time.timestamp()); 1679 1656 } 1680 1657 var ts: posix.timespec = undefined;
+11 -6
support/autobahn/client/config.json
··· 1 1 { 2 - "url": "ws://127.0.0.1:9001", 3 - "outdir": "/ab/reports", 4 - "cases": ["*"], 5 - "exclude-cases": [], 6 - "exclude-agent-cases": {} 7 - } 2 + "url": "ws://127.0.0.1:9001", 3 + "outdir": "/ab/reports", 4 + "cases": 5 + [ 6 + "*" 7 + ], 8 + "exclude-cases": 9 + [], 10 + "exclude-agent-cases": 11 + {} 12 + }
+1 -1
support/autobahn/client/main.zig
··· 63 63 defer buffer_provider.deinit(); 64 64 65 65 for (cases, 0..) |case, i| { 66 - // if (!std.mem.eql(u8, case, "6.3.1")) continue; 66 + // if (!std.mem.eql(u8, case, "1.1.1")) continue; 67 67 std.debug.print("running case: {s}\n", .{case}); 68 68 69 69 const path = try std.fmt.allocPrint(allocator, "/runCase?casetuple={s}&agent=websocket.zig", .{case});
+9 -6
support/autobahn/server/main.zig
··· 18 18 pub fn main() !void { 19 19 var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 20 20 const allocator = gpa.allocator(); 21 - defer _ = gpa.detectLeaks(); 21 + 22 + if (@import("builtin").os.tag != .windows) { 23 + defer _ = gpa.detectLeaks(); 22 24 23 - std.posix.sigaction(std.posix.SIG.TERM, &.{ 24 - .handler = .{.handler = shutdown}, 25 - .mask = std.posix.empty_sigset, 26 - .flags = 0, 27 - }, null); 25 + std.posix.sigaction(std.posix.SIG.TERM, &.{ 26 + .handler = .{.handler = shutdown}, 27 + .mask = std.posix.empty_sigset, 28 + .flags = 0, 29 + }, null); 30 + } 28 31 29 32 const t1 = try startNonBlocking(allocator); 30 33 const t2 = try startNonBlockingBufferPool(allocator);