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:
parent
d583ae62b7
commit
0251a9cbc5
3 changed files with 79 additions and 17 deletions
|
@ -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...">
|
||||||
|
|
|
@ -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";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
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
|
// send notification to all others
|
||||||
WebsocketHandler.publish(.{ .channel = ctx.channel, .message = chat_message });
|
WebsocketHandler.publish(
|
||||||
|
.{ .channel = ctx.channel, .message = chat_message },
|
||||||
|
);
|
||||||
std.log.info("{s}", .{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 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue