1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-20 15:14:08 +00:00

improved websocket chat example

This commit is contained in:
Rene Schallner 2023-05-07 16:14:28 +02:00
parent d583ae62b7
commit 0251a9cbc5
3 changed files with 79 additions and 17 deletions

View file

@ -189,6 +189,7 @@
<!-- ... --> <!-- ... -->
</div> </div>
<div class="status-bar .status-offline" id="statusBar">Connecting...</div>
<div class="input-container"> <div class="input-container">
<input id="prompt-input" type="text" placeholder="Type your message..."> <input id="prompt-input" type="text" placeholder="Type your message...">

View file

@ -3,6 +3,7 @@
var chatContainer = document.getElementById("chat-container"); var chatContainer = document.getElementById("chat-container");
var promptInput = document.getElementById("prompt-input"); var promptInput = document.getElementById("prompt-input");
var sendButton = document.getElementById("send-button"); var sendButton = document.getElementById("send-button");
var statusBar = document.getElementById("statusBar");
// var ws; // var ws;
@ -61,8 +62,17 @@ init();
var ws = new WebSocket("ws://localhost:3010/chat"); var ws = new WebSocket("ws://localhost:3010/chat");
ws.onmessage = function(e) { addBotMessage(e.data); return false;}; ws.onmessage = function(e) { addBotMessage(e.data); return false;};
ws.onclose = function(e) { console.log("closed"); }; ws.onclose = function(e) {
ws.onopen = function(e) { /*e.target.send("Yo!");*/ return false;}; 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";
};

View file

@ -5,7 +5,8 @@ const WebSockets = zap.WebSockets;
const Context = struct { const Context = struct {
userName: []const u8, userName: []const u8,
channel: []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, subscribeArgs: WebsocketHandler.SubscribeArgs,
settings: WebsocketHandler.WebSocketSettings, settings: WebsocketHandler.WebSocketSettings,
}; };
@ -21,7 +22,11 @@ const ContextManager = struct {
const Self = @This(); 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 .{ return .{
.allocator = allocator, .allocator = allocator,
.channel = channelName, .channel = channelName,
@ -42,7 +47,11 @@ const ContextManager = struct {
defer self.lock.unlock(); defer self.lock.unlock();
var ctx = try self.allocator.create(Context); 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.* = .{ ctx.* = .{
.userName = userName, .userName = userName,
.channel = self.channel, .channel = self.channel,
@ -77,7 +86,11 @@ fn on_open_websocket(context: ?*Context, handle: WebSockets.WsHandle) void {
// say hello // say hello
var buf: [128]u8 = undefined; 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 // send notification to all others
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message }); WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message });
@ -90,24 +103,59 @@ fn on_close_websocket(context: ?*Context, uuid: isize) void {
if (context) |ctx| { if (context) |ctx| {
// say goodbye // say goodbye
var buf: [128]u8 = undefined; 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 // send notification to all others
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message }); WebsocketHandler.publish(.{ .channel = ctx.channel, .message = message });
std.log.info("websocket closed: {s}", .{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; _ = is_text;
_ = handle; _ = handle;
if (context) |ctx| { if (context) |ctx| {
// say goodbye // send message
var buf: [128]u8 = undefined; const buflen = 128; // arbitrary len
const chat_message = std.fmt.bufPrint(&buf, "{s}: {s}", .{ ctx.userName, message }) catch unreachable; var buf: [buflen]u8 = undefined;
// send notification to all others const format_string = "{s}: {s}";
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = chat_message }); const fmt_string_extra_len = 2; // ": " between the two strings
std.log.info("{s}", .{chat_message}); //
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 { fn on_request(r: zap.SimpleRequest) void {
r.setHeader("Server", "zap.example") catch unreachable; r.setHeader("Server", "zap.example") catch unreachable;
r.sendBody("<html><body><h1>This is a simple Websocket chatroom example</h1></body></html>") catch return; r.sendBody(
\\ <html><body>
\\ <h1>This is a simple Websocket chatroom example</h1>
\\ </body></html>
) catch return;
} }
fn on_upgrade(r: zap.SimpleRequest, target_protocol: []const u8) void { fn on_upgrade(r: zap.SimpleRequest, target_protocol: []const u8) void {
// make sure we're talking the right protocol // make sure we're talking the right protocol
if (!std.mem.eql(u8, target_protocol, "websocket")) { 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.setStatus(.bad_request);
r.sendBody("400 - BAD REQUEST") catch unreachable; r.sendBody("400 - BAD REQUEST") catch unreachable;
return; return;
@ -143,7 +195,6 @@ fn on_upgrade(r: zap.SimpleRequest, target_protocol: []const u8) void {
var GlobalContextManager: ContextManager = undefined; var GlobalContextManager: ContextManager = undefined;
const WebsocketHandler = WebSockets.Handler(Context); const WebsocketHandler = WebSockets.Handler(Context);
var handler_instance: WebsocketHandler = .{};
// here we go // here we go
pub fn main() !void { pub fn main() !void {