Skip to content

Commit 157359b

Browse files
committed
add async interface
1 parent 426f2b4 commit 157359b

File tree

4 files changed

+362
-23
lines changed

4 files changed

+362
-23
lines changed

examples/wss/src/main.zig

+4-2
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ pub fn main() !void {
3636
try cli.send(.text, payload, true);
3737

3838
// Show incoming messages
39-
while (cli.nextMessage()) |msg| {
39+
var i: usize = 0;
40+
while (cli.nextMessage()) |msg| : (i += 1) {
4041
defer msg.deinit();
41-
std.debug.print("{s}", .{msg.payload});
42+
//std.debug.print("{s}\n", .{msg.payload});
43+
std.debug.print("{} {}\n", .{ i, msg.payload.len });
4244
}
4345
if (cli.err) |err| return err;
4446
}

src/frame.zig

+142-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const std = @import("std");
22
const mem = std.mem;
3+
const testing = std.testing;
34

45
const assert = std.debug.assert;
56
const Allocator = mem.Allocator;
@@ -57,19 +58,19 @@ pub const Frame = struct {
5758

5859
const default_close_code = 1000;
5960

60-
pub fn closeCode(self: *Self) u16 {
61+
pub fn closeCode(self: Self) u16 {
6162
if (self.opcode != .close) return 0;
6263
if (self.payload.len == 1) return 0; //invalid
6364
if (self.payload.len == 0) return default_close_code;
6465
return mem.readInt(u16, self.payload[0..2], .big);
6566
}
6667

67-
pub fn closePayload(self: *Self) []const u8 {
68+
pub fn closePayload(self: Self) []const u8 {
6869
if (self.payload.len > 2) return self.payload[2..];
6970
return self.payload[0..0];
7071
}
7172

72-
fn assertValidCloseCode(self: *Self) !void {
73+
fn assertValidCloseCode(self: Self) !void {
7374
return switch (self.closeCode()) {
7475
1000...1003 => {},
7576
1007...1011 => {},
@@ -79,34 +80,34 @@ pub const Frame = struct {
7980
};
8081
}
8182

82-
pub fn assertValid(self: *Self) !void {
83+
pub fn assertValid(self: Self) !void {
8384
if (self.isControl()) try self.assertValidControl();
8485
if (self.opcode == .close) try self.assertValidClose();
8586
}
8687

87-
fn assertValidClose(self: *Self) !void {
88+
fn assertValidClose(self: Self) !void {
8889
try assertValidUtf8(self.closePayload());
8990
try self.assertValidCloseCode();
9091
}
9192

92-
fn assertValidControl(self: *Self) !void {
93+
fn assertValidControl(self: Self) !void {
9394
if (self.payload.len > 125) return Error.TooBigPayloadForControlFrame;
9495
if (self.fin == 0) return Error.FragmentedControlFrame;
9596
}
9697

97-
pub fn isFin(self: *Self) bool {
98+
pub fn isFin(self: Self) bool {
9899
return self.fin == 1;
99100
}
100101

101-
pub fn isCompressed(self: *Self) bool {
102+
pub fn isCompressed(self: Self) bool {
102103
return self.rsv1 == 1;
103104
}
104105

105-
pub fn isControl(self: *Self) bool {
106+
pub fn isControl(self: Self) bool {
106107
return self.opcode.isControl();
107108
}
108109

109-
pub fn deinit(self: *Self) void {
110+
pub fn deinit(self: Self) void {
110111
if (self.allocator) |a| a.free(self.payload);
111112
}
112113

@@ -117,15 +118,15 @@ pub const Frame = struct {
117118
end,
118119
};
119120

120-
pub fn fragment(self: *Self) Fragment {
121+
pub fn fragment(self: Self) Fragment {
121122
if (self.fin == 1) {
122123
if (self.opcode == .continuation) return .end else return .unfragmented;
123124
} else {
124125
if (self.opcode == .continuation) return .fragment else return .start;
125126
}
126127
}
127128

128-
fn isValidContinuation(self: *Self, prev: Fragment) bool {
129+
fn isValidContinuation(self: Self, prev: Fragment) bool {
129130
if (self.isControl()) return true;
130131
const curr = self.fragment();
131132
return switch (prev) {
@@ -134,7 +135,7 @@ pub const Frame = struct {
134135
};
135136
}
136137

137-
pub fn assertValidContinuation(self: *Self, prev: Fragment) !void {
138+
pub fn assertValidContinuation(self: Self, prev: Fragment) !void {
138139
if (!self.isValidContinuation(prev)) return Error.InvalidFragmentation;
139140
}
140141

@@ -144,16 +145,14 @@ pub const Frame = struct {
144145
const masked = self.mask == 1;
145146
const is_close = self.opcode == .close;
146147

147-
const required_buf_len: usize = 1 + payload_bytes +
148-
if (masked) 4 else 0 +
149-
if (is_close) 2 else 0 +
148+
const encoded_len = 1 + payload_bytes +
149+
@as(usize, (if (self.mask == 1) 4 else 0)) +
150+
@as(usize, (if (self.opcode == .close) 2 else 0)) +
150151
payload_len;
151-
assert(buf.len >= required_buf_len);
152+
assert(buf.len >= encoded_len);
152153

153154
buf[0] = (@as(u8, @intCast(self.fin)) << 7) +
154155
(@as(u8, @intCast(self.rsv1)) << 6) +
155-
//(@intCast(u8, self.rsv2) << 5) +
156-
//(@intCast(u8, self.rsv3) << 4) +
157156
@intFromEnum(self.opcode);
158157

159158
var offset: usize = 1;
@@ -193,6 +192,15 @@ pub const Frame = struct {
193192
return payload_end;
194193
}
195194

195+
pub fn encodedLen(self: Self) usize {
196+
const payload_len: u64 = if (self.opcode == .close) self.payload.len + 2 else self.payload.len;
197+
const payload_bytes = payloadBytes(payload_len);
198+
return 1 + payload_bytes +
199+
@as(usize, (if (self.mask == 1) 4 else 0)) +
200+
@as(usize, (if (self.opcode == .close) 2 else 0)) +
201+
payload_len;
202+
}
203+
196204
fn maskingKey() [4]u8 {
197205
var masking_key: [4]u8 = undefined;
198206
rnd.random().bytes(&masking_key);
@@ -218,4 +226,119 @@ pub const Frame = struct {
218226
pub fn assertValidUtf8(data: []const u8) !void {
219227
if (!utf8ValidateSlice(data)) return Error.InvalidUtf8Payload;
220228
}
229+
230+
pub fn parse(data: []u8) !struct { Frame, usize } {
231+
if (data.len < 2) return error.SplitBuffer;
232+
233+
const fin: u1 = readBit(data[0], 0b1000_0000);
234+
const rsv1: u1 = readBit(data[0], 0b0100_0000);
235+
const rsv2: u1 = readBit(data[0], 0b0010_0000);
236+
const rsv3: u1 = readBit(data[0], 0b0001_0000);
237+
if (rsv2 == 1 or rsv3 == 1) return Error.ReservedRsv;
238+
239+
const opcode = try Frame.Opcode.decode(@intCast(data[0] & 0b0000_1111));
240+
const mask: u1 = readBit(data[1], 0b1000_0000);
241+
242+
const payload_len, const n = try readPayloadLen(data[1..]);
243+
244+
if (opcode.isControl()) {
245+
if (payload_len > 125) return Error.TooBigPayloadForControlFrame;
246+
if (fin == 0) return Error.FragmentedControlFrame;
247+
}
248+
249+
const mask_start = n + 1;
250+
const mask_end: usize = mask_start + @as(usize, if (mask == 1) 4 else 0);
251+
const payload_start = mask_end;
252+
const payload_end = payload_start + payload_len;
253+
254+
if (data.len < payload_end) return error.SplitBuffer;
255+
256+
const masking_key = data[mask_start..mask_end];
257+
const payload = data[payload_start..payload_end];
258+
if (mask == 1) {
259+
Frame.maskUnmask(masking_key, payload);
260+
}
261+
262+
return .{
263+
.{
264+
.fin = fin,
265+
.rsv1 = rsv1,
266+
//.rsv2 = rsv2,
267+
//.rsv3 = rsv3,
268+
.mask = mask,
269+
.opcode = opcode,
270+
//.masking_key = masking_key,
271+
.payload = payload,
272+
},
273+
payload_end,
274+
};
275+
}
276+
277+
fn readPayloadLen(data: []const u8) !struct { u64, usize } {
278+
if (data.len < 1) return error.SplitBuffer;
279+
const payload_len: u64 = @intCast(data[0] & 0b0111_1111);
280+
switch (payload_len) {
281+
126 => {
282+
if (data.len < 3) return error.SplitBuffer;
283+
return .{ @intCast(std.mem.readInt(u16, data[1..3], .big)), 3 };
284+
},
285+
127 => {
286+
if (data.len < 9) return error.SplitBuffer;
287+
return .{ @intCast(std.mem.readInt(u64, data[1..9], .big)), 9 };
288+
},
289+
else => return .{ payload_len, 1 },
290+
}
291+
}
292+
293+
fn readBit(byte: u8, comptime mask: u8) u1 {
294+
return if (byte & mask == 0) 0 else 1;
295+
}
296+
297+
test readBit {
298+
try testing.expectEqual(0, readBit(0, 0));
299+
try testing.expectEqual(1, readBit(255, 1));
300+
try testing.expectEqual(1, readBit(0b1000_0000, 0b1000_0000));
301+
try testing.expectEqual(0, readBit(0b1000_0000, 0b0100_0000));
302+
}
303+
304+
test readPayloadLen {
305+
{
306+
const len, const n = try readPayloadLen(&.{125});
307+
try testing.expectEqual(125, len);
308+
try testing.expectEqual(1, n);
309+
}
310+
{
311+
const len, const n = try readPayloadLen(&.{ 126, 0xaa, 0xbb });
312+
try testing.expectEqual(0xaabb, len);
313+
try testing.expectEqual(3, n);
314+
}
315+
{
316+
const len, const n = try readPayloadLen(&.{ 127, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11 });
317+
try testing.expectEqual(0xaabbccddeeff0011, len);
318+
try testing.expectEqual(9, n);
319+
}
320+
}
221321
};
322+
323+
test "parse close frame" {
324+
const frame_data = [_]u8{ 0x88, 0x87, 0xa, 0xb, 0xc, 0xd, 0x09, 0xe2, 0x0d, 0x0f, 0x09, 0x0f, 0x09 };
325+
var data = frame_data ++ [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; // suffix which is not parsed
326+
327+
// Parsing partial frame returns SplitBuffer error
328+
for (0..frame_data.len) |i| {
329+
try testing.expectError(error.SplitBuffer, Frame.parse(data[0..i]));
330+
}
331+
const expected_payload = [_]u8{ 0x3, 0xe9, 0x1, 0x2, 0x3, 0x4, 0x5 };
332+
333+
const frm, const n = try Frame.parse(&data);
334+
try testing.expectEqual(frame_data.len, n);
335+
try testing.expectEqual(frm.opcode, .close);
336+
try testing.expectEqual(frm.fin, 1);
337+
try testing.expectEqual(frm.payload.len, 7);
338+
try testing.expectEqualSlices(u8, &expected_payload, frm.payload);
339+
try testing.expectEqualSlices(u8, frm.payload, data[6..][0..7]);
340+
}
341+
342+
test {
343+
_ = Frame;
344+
}

0 commit comments

Comments
 (0)