srctree

Gregory Mullen parent 51443fc1 e2ba59d1
websocket example that pretends to be two way

examples/websocket.zig added: 60, removed: 7, total 53
@@ -16,12 +16,15 @@ const Root = struct {
}
 
fn socket(frame: *verse.Frame) verse.Router.Error!void {
const ws = frame.acceptWebsocket() catch unreachable;
var ws = frame.acceptWebsocket() catch unreachable;
 
for (0..10) |i| {
var buffer: [0xff]u8 = undefined;
ws.send(print(&buffer, "Iteration {}\n", .{i}) catch unreachable) catch unreachable;
std.time.sleep(1_000_000_000);
var read_buffer: [0x4000]u8 = undefined;
const msg = ws.recieve(&read_buffer) catch unreachable;
std.debug.print("msg: {} -- {s}\n", .{ msg.header, msg.msg });
}
std.debug.print("Socket Example Done\n", .{});
}
@@ -36,6 +39,7 @@ const Root = struct {
\\ body {{ width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }}
\\ </style>
\\ <script>
\\ var socket;
\\ function retry() {{
\\ socket = new WebSocket("ws://localhost:8088/socket");
\\ socket.onmessage = (event) => {{
@@ -43,12 +47,17 @@ const Root = struct {
\\ }};
\\ }}
\\ retry();
\\ function send() {{
\\ socket.send(document.getElementById("text").value);
\\ }}
\\</script>
\\</head>
\\<body>
\\<h1>Title</h1>
\\<p>
\\ {s}
\\ <input type="text" id="text" />
\\ <button onclick="send()"> Send</button>
\\ <button onclick="retry()"> Connect</button>
\\</p>
\\</body>
 
src/websocket.zig added: 60, removed: 7, total 53
@@ -40,14 +40,24 @@ pub fn send(ws: Websocket, msg: []const u8) !void {
};
}
 
pub fn recieve(ws: *Websocket, buffer: []u8) !Message {
var reader = switch (ws.frame.downstream) {
.zwsgi, .http => |stream| stream.reader(),
else => unreachable,
};
var any = reader.any();
return try Message.read(&any, buffer);
}
 
pub const Message = struct {
header: Header,
length: union(enum) {
tiny: void,
tiny: u7,
small: u16,
large: u64,
},
msg: []const u8,
mask: [4]u8 = undefined,
msg: []u8,
 
pub const Header = if (endian == .big)
packed struct(u16) {
@@ -77,16 +87,49 @@ pub const Message = struct {
},
},
.length = switch (msg.len) {
0...125 => .{ .tiny = {} },
0...125 => .{ .tiny = @truncate(msg.len) },
126...0xffff => |len| .{ .small = nativeToBig(u16, @truncate(len)) },
else => |len| .{ .large = nativeToBig(u64, len) },
},
.msg = msg,
.msg = @constCast(msg),
};
 
return message;
}
 
pub fn read(r: *AnyReader, buffer: []u8) !Message {
var m: Message = undefined;
 
if (try r.read(@as(*[2]u8, @ptrCast(&m.header))) != 2) return error.InvalidRead;
if (m.header.final == false) return error.FragmentNotSupported;
if (m.header.extlen == 127) {
m.length = .{ .large = try r.readInt(u64, .big) };
} else if (m.header.extlen == 126) {
m.length = .{ .small = try r.readInt(u16, .big) };
} else {
m.length = .{ .tiny = m.header.extlen };
}
 
const length: usize = switch (m.length) {
inline else => |l| l,
};
 
if (length > buffer.len) return error.NoSpaceLeft;
 
if (m.header.mask) {
_ = try r.read(&m.mask);
}
const size = try r.read(buffer[0..length]);
if (size != length) {
std.debug.print("read error: {} vs {}\n", .{ size, length });
std.debug.print("read error: {} \n", .{m.header});
return error.InvalidRead;
}
m.msg = buffer[0..size];
for (m.msg, 0..) |*msg, i| msg.* ^= m.mask[i % 4];
return m;
}
 
pub fn toVec(m: *const Message) [3]std.posix.iovec_const {
return .{
.{ .base = @ptrCast(&m.header), .len = 2 },
@@ -137,3 +180,4 @@ const Frame = @import("frame.zig");
const Hash = std.crypto.hash.Sha1;
const base64 = std.base64.standard.Encoder;
const nativeToBig = std.mem.nativeToBig;
const AnyReader = std.io.AnyReader;