diff --git a/examples/websockets/frontend/index.html b/examples/websockets/frontend/index.html index 9a15c86..4b643bb 100644 --- a/examples/websockets/frontend/index.html +++ b/examples/websockets/frontend/index.html @@ -189,6 +189,7 @@ +
Connecting...
diff --git a/examples/websockets/frontend/index.js b/examples/websockets/frontend/index.js index 4f2130b..6b46cab 100644 --- a/examples/websockets/frontend/index.js +++ b/examples/websockets/frontend/index.js @@ -3,6 +3,7 @@ var chatContainer = document.getElementById("chat-container"); var promptInput = document.getElementById("prompt-input"); var sendButton = document.getElementById("send-button"); +var statusBar = document.getElementById("statusBar"); // var ws; @@ -61,8 +62,17 @@ init(); var ws = new WebSocket("ws://localhost:3010/chat"); ws.onmessage = function(e) { addBotMessage(e.data); return false;}; -ws.onclose = function(e) { console.log("closed"); }; -ws.onopen = function(e) { /*e.target.send("Yo!");*/ return false;}; +ws.onclose = function(e) { + statusBar.className = "status-bar"; + statusBar.classList.add("status-busy"); + statusBar.innerHTML = "Disconnected"; +}; + +ws.onopen = function(e) { + statusBar.className = "status-bar"; + statusBar.classList.add("status-thinking"); + statusBar.innerHTML = "Connected"; +}; diff --git a/examples/websockets/websockets.zig b/examples/websockets/websockets.zig index 0e5f66b..09975bb 100644 --- a/examples/websockets/websockets.zig +++ b/examples/websockets/websockets.zig @@ -5,7 +5,8 @@ const WebSockets = zap.WebSockets; const Context = struct { userName: []const u8, channel: []const u8, - // we need to hold on to them and just re-use them for every incoming connection + // we need to hold on to them and just re-use them for every incoming + // connection subscribeArgs: WebsocketHandler.SubscribeArgs, settings: WebsocketHandler.WebSocketSettings, }; @@ -21,7 +22,11 @@ const ContextManager = struct { const Self = @This(); - pub fn init(allocator: std.mem.Allocator, channelName: []const u8, usernamePrefix: []const u8) Self { + pub fn init( + allocator: std.mem.Allocator, + channelName: []const u8, + usernamePrefix: []const u8, + ) Self { return .{ .allocator = allocator, .channel = channelName, @@ -42,7 +47,11 @@ const ContextManager = struct { defer self.lock.unlock(); var ctx = try self.allocator.create(Context); - var userName = try std.fmt.allocPrint(self.allocator, "{s}{d}", .{ self.usernamePrefix, self.contexts.items.len }); + var userName = try std.fmt.allocPrint( + self.allocator, + "{s}{d}", + .{ self.usernamePrefix, self.contexts.items.len }, + ); ctx.* = .{ .userName = userName, .channel = self.channel, @@ -77,7 +86,11 @@ fn on_open_websocket(context: ?*Context, handle: WebSockets.WsHandle) void { // say hello var buf: [128]u8 = undefined; - const message = std.fmt.bufPrint(&buf, "{s} joined the chat.", .{ctx.userName}) catch unreachable; + const message = std.fmt.bufPrint( + &buf, + "{s} joined the chat.", + .{ctx.userName}, + ) catch unreachable; // send notification to all others WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message }); @@ -90,24 +103,59 @@ fn on_close_websocket(context: ?*Context, uuid: isize) void { if (context) |ctx| { // say goodbye var buf: [128]u8 = undefined; - const message = std.fmt.bufPrint(&buf, "{s} left the chat.", .{ctx.userName}) catch unreachable; + const message = std.fmt.bufPrint( + &buf, + "{s} left the chat.", + .{ctx.userName}, + ) catch unreachable; // send notification to all others WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message }); std.log.info("websocket closed: {s}", .{message}); } } -fn handle_websocket_message(context: ?*Context, handle: WebSockets.WsHandle, message: []const u8, is_text: bool) void { +fn handle_websocket_message( + context: ?*Context, + handle: WebSockets.WsHandle, + message: []const u8, + is_text: bool, +) void { _ = is_text; _ = handle; if (context) |ctx| { - // say goodbye - var buf: [128]u8 = undefined; - const chat_message = std.fmt.bufPrint(&buf, "{s}: {s}", .{ ctx.userName, message }) catch unreachable; + // send message + const buflen = 128; // arbitrary len + var buf: [buflen]u8 = undefined; - // send notification to all others - WebsocketHandler.publish(.{ .channel = ctx.channel, .message = chat_message }); - std.log.info("{s}", .{chat_message}); + const format_string = "{s}: {s}"; + const fmt_string_extra_len = 2; // ": " between the two strings + // + const max_msg_len = buflen - ctx.userName.len - fmt_string_extra_len; + if (max_msg_len > 0) { + // there is space for the message, because the user name + format + // string extra do not exceed the buffer now, let's check: do we + // need to trim the message? + var trimmed_message: []const u8 = message; + if (message.len > max_msg_len) { + trimmed_message = message[0..max_msg_len]; + } + const chat_message = std.fmt.bufPrint( + &buf, + format_string, + .{ ctx.userName, trimmed_message }, + ) catch unreachable; + + // send notification to all others + WebsocketHandler.publish( + .{ .channel = ctx.channel, .message = chat_message }, + ); + std.log.info("{s}", .{chat_message}); + } else { + std.log.warn( + "Username is very long, cannot deal with that size: {d}", + .{ctx.userName.len}, + ); + } } } @@ -116,13 +164,17 @@ fn handle_websocket_message(context: ?*Context, handle: WebSockets.WsHandle, mes // fn on_request(r: zap.SimpleRequest) void { r.setHeader("Server", "zap.example") catch unreachable; - r.sendBody("

This is a simple Websocket chatroom example

") catch return; + r.sendBody( + \\ + \\

This is a simple Websocket chatroom example

+ \\ + ) catch return; } fn on_upgrade(r: zap.SimpleRequest, target_protocol: []const u8) void { // make sure we're talking the right protocol if (!std.mem.eql(u8, target_protocol, "websocket")) { - std.log.warn("received illegal target protocol: {s}", .{target_protocol}); + std.log.warn("received illegal protocol: {s}", .{target_protocol}); r.setStatus(.bad_request); r.sendBody("400 - BAD REQUEST") catch unreachable; return; @@ -143,7 +195,6 @@ fn on_upgrade(r: zap.SimpleRequest, target_protocol: []const u8) void { var GlobalContextManager: ContextManager = undefined; const WebsocketHandler = WebSockets.Handler(Context); -var handler_instance: WebsocketHandler = .{}; // here we go pub fn main() !void {