Server handles join packets nicely.
This commit is contained in:
@@ -29,13 +29,13 @@ import { renderChat } from "./render.js";
|
|||||||
* @returns {{ socket: WebSocket, sendMessage: (content: string) => void }}
|
* @returns {{ socket: WebSocket, sendMessage: (content: string) => void }}
|
||||||
*/
|
*/
|
||||||
export function initWebSocket(username, chatEl, inputContainer, usersListEl) {
|
export function initWebSocket(username, chatEl, inputContainer, usersListEl) {
|
||||||
const socket = new WebSocket(
|
const socket = new WebSocket("ws://localhost:8080");
|
||||||
(location.protocol === "https:" ? "wss" : "ws") +
|
/*(location.protocol === "https:" ? "wss" : "ws") +
|
||||||
"://" +
|
"://" +
|
||||||
location.host +
|
location.host +
|
||||||
"/ws",
|
"/ws",
|
||||||
"coms",
|
"coms",
|
||||||
);
|
);*/
|
||||||
|
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
// Announce join.
|
// Announce join.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ Packet* packet_init(PacketType type, void* data) {
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet* packet_init_safe(char* type, void* data) {
|
Packet* packet_init_safe(const char* type, const void* data) {
|
||||||
PacketType t = packet_type_parse(type);
|
PacketType t = packet_type_parse(type);
|
||||||
if (t == PACKET_TYPE_BAD) return NULL;
|
if (t == PACKET_TYPE_BAD) return NULL;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "include/chat.h"
|
#include "include/chat.h"
|
||||||
|
#include "include/api.h"
|
||||||
#include "include/data.h"
|
#include "include/data.h"
|
||||||
#include "include/session.h"
|
#include "include/session.h"
|
||||||
|
|
||||||
@@ -41,16 +42,40 @@ MsgData** chat_history_nice(void) {
|
|||||||
return msgs;
|
return msgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t next_msg_id = 1;
|
// Parse a raw packet.
|
||||||
|
Packet* packet_parse(const char* in, size_t len) {
|
||||||
|
yyjson_doc* doc = yyjson_read((const char*)in, len, 0);
|
||||||
|
if (!doc) return NULL;
|
||||||
|
|
||||||
// History buffer for last 100 messages.
|
yyjson_val* root = yyjson_doc_get_root(doc);
|
||||||
#define HISTORY_SIZE 100
|
yyjson_val* jtype = yyjson_obj_get(root, "type");
|
||||||
static char* history[HISTORY_SIZE];
|
const char* type =
|
||||||
static size_t history_len[HISTORY_SIZE];
|
(jtype && yyjson_is_str(jtype)) ? yyjson_get_str(jtype) : NULL;
|
||||||
static size_t history_pos = 0;
|
|
||||||
static size_t history_count = 0;
|
|
||||||
|
|
||||||
#define CHAT_BUF_SIZE SESSION_CHAT_BUF_SIZE
|
yyjson_val* rdata = yyjson_obj_get((yyjson_val*)root, "data");
|
||||||
|
const yyjson_val* data = ((rdata && yyjson_is_obj(rdata)) ? rdata : NULL);
|
||||||
|
|
||||||
|
return packet_init_safe(type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a join packet.
|
||||||
|
void do_join(Session* sess, Packet* packet) {
|
||||||
|
yyjson_val* data = packet->data;
|
||||||
|
yyjson_val* jname = data ? yyjson_obj_get(data, "name") : NULL;
|
||||||
|
const char* rname =
|
||||||
|
(jname && yyjson_is_str(jname)) ? yyjson_get_str(jname) : "Anon";
|
||||||
|
|
||||||
|
Name name = {'\0'};
|
||||||
|
for (size_t i = 0; rname[i] && i < NAME_MAX_LENGTH - 1; i++)
|
||||||
|
name[i] = rname[i];
|
||||||
|
|
||||||
|
session_set_name(sess, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a msg packet.
|
||||||
|
void do_msg(Session* sess, Packet* packet) {}
|
||||||
|
|
||||||
|
#define CHAT_PACKET_SIZE SESSION_CHAT_BUF_SIZE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cb_chat - libwebsockets protocol callback.
|
* cb_chat - libwebsockets protocol callback.
|
||||||
@@ -66,142 +91,117 @@ int cb_chat(
|
|||||||
Session* head = session_get_head();
|
Session* head = session_get_head();
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
|
|
||||||
case LWS_CALLBACK_ESTABLISHED:
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
// New connection, create session.
|
// New connection, create session.
|
||||||
if (ps_p) { *ps_p = session_create(wsi); }
|
if (ps_p) { *ps_p = session_create(wsi); }
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LWS_CALLBACK_RECEIVE: {
|
case LWS_CALLBACK_RECEIVE: {
|
||||||
// Parse inc JSON packet.
|
// Parse inc JSON packet.
|
||||||
if (len > CHAT_BUF_SIZE) {
|
if (len > CHAT_PACKET_SIZE) {
|
||||||
lwsl_warn(
|
lwsl_warn(
|
||||||
"Received JSON payload exceeds limit: %zu bytes\n", len
|
"Received JSON payload exceeds limit: %zu bytes\n", len
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
yyjson_doc* doc = yyjson_read((const char*)in, len, 0);
|
|
||||||
if (!doc) break;
|
|
||||||
const yyjson_val* root = yyjson_doc_get_root(doc);
|
|
||||||
const yyjson_val* tval = yyjson_obj_get((yyjson_val*)root, "type");
|
|
||||||
const char* type = (tval && yyjson_is_str((yyjson_val*)tval))
|
|
||||||
? yyjson_get_str((yyjson_val*)tval)
|
|
||||||
: NULL;
|
|
||||||
|
|
||||||
// JOIN.
|
Packet* packet = packet_parse(in, len);
|
||||||
if (type && strcmp(type, "join") == 0 && sess) {
|
|
||||||
const yyjson_val* data =
|
|
||||||
yyjson_obj_get((yyjson_val*)root, "data");
|
|
||||||
const yyjson_val* uval =
|
|
||||||
data ? yyjson_obj_get((yyjson_val*)data, "username") : NULL;
|
|
||||||
// Enforce max username length.
|
|
||||||
const char* raw_name =
|
|
||||||
(uval && yyjson_is_str((yyjson_val*)uval))
|
|
||||||
? yyjson_get_str((yyjson_val*)uval)
|
|
||||||
: "Anonymous";
|
|
||||||
if (raw_name[0] == '\0') { raw_name = "Anonymous"; }
|
|
||||||
char name_buf[SESSION_USERNAME_MAX_LEN];
|
|
||||||
size_t _i;
|
|
||||||
for (_i = 0; raw_name[_i] && _i < SESSION_USERNAME_MAX_LEN - 1;
|
|
||||||
++_i) {
|
|
||||||
unsigned char c = (unsigned char)raw_name[_i];
|
|
||||||
name_buf[_i] = isprint(c) ? c : '?';
|
|
||||||
}
|
|
||||||
name_buf[_i] = '\0';
|
|
||||||
session_set_username(sess, name_buf);
|
|
||||||
|
|
||||||
// #1: Welcome our new client.
|
if (!packet) {
|
||||||
{
|
lwsl_warn("Received bad packet: \"%s\".\n", (char*)in);
|
||||||
yyjson_mut_doc* wdoc = yyjson_mut_doc_new(NULL);
|
break;
|
||||||
yyjson_mut_val* wroot = yyjson_mut_obj(wdoc);
|
|
||||||
yyjson_mut_doc_set_root(wdoc, wroot);
|
|
||||||
yyjson_mut_obj_add_str(wdoc, wroot, "type", "welcome");
|
|
||||||
|
|
||||||
yyjson_mut_val* wdata = yyjson_mut_obj(wdoc);
|
|
||||||
yyjson_mut_obj_add_val(wdoc, wroot, "data", wdata);
|
|
||||||
yyjson_mut_obj_add_uint(
|
|
||||||
wdoc, wdata, "you", session_get_id(sess)
|
|
||||||
);
|
|
||||||
|
|
||||||
yyjson_mut_val* wusers = yyjson_mut_arr(wdoc);
|
|
||||||
yyjson_mut_obj_add_val(wdoc, wdata, "users", wusers);
|
|
||||||
|
|
||||||
for (Session* s = head; s; s = s->next) {
|
|
||||||
if (session_has_username(s)) {
|
|
||||||
yyjson_mut_val* uobj = yyjson_mut_obj(wdoc);
|
|
||||||
/* add each user once */
|
|
||||||
yyjson_mut_arr_add_val(wusers, uobj);
|
|
||||||
yyjson_mut_obj_add_uint(
|
|
||||||
wdoc, uobj, "id", session_get_id(s)
|
|
||||||
);
|
|
||||||
yyjson_mut_obj_add_str(
|
|
||||||
wdoc, uobj, "username", session_get_username(s)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// History will be streamed individually after welcome.
|
|
||||||
|
|
||||||
size_t out_len;
|
|
||||||
char* out = yyjson_mut_write(wdoc, 0, &out_len);
|
|
||||||
size_t copy_len = out_len < SESSION_CHAT_BUF_SIZE
|
|
||||||
? out_len
|
|
||||||
: SESSION_CHAT_BUF_SIZE;
|
|
||||||
sess->buf_len = copy_len;
|
|
||||||
memcpy(&sess->buf[LWS_PRE], out, copy_len);
|
|
||||||
// Schedule the welcome frame for writable callback.
|
|
||||||
lws_callback_on_writable(sess->wsi);
|
|
||||||
|
|
||||||
free(out);
|
|
||||||
yyjson_mut_doc_free(wdoc);
|
|
||||||
|
|
||||||
// #1a: Stream history messages to new client.
|
|
||||||
for (size_t i = 0; i < history_count; ++i) {
|
|
||||||
size_t idx =
|
|
||||||
(history_pos + HISTORY_SIZE - history_count + i) %
|
|
||||||
HISTORY_SIZE;
|
|
||||||
size_t hist_len = history_len[idx];
|
|
||||||
size_t send_len = hist_len < SESSION_CHAT_BUF_SIZE
|
|
||||||
? hist_len
|
|
||||||
: SESSION_CHAT_BUF_SIZE;
|
|
||||||
memcpy(&sess->buf[LWS_PRE], history[idx], send_len);
|
|
||||||
lws_write(
|
|
||||||
sess->wsi, &sess->buf[LWS_PRE], send_len,
|
|
||||||
LWS_WRITE_TEXT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// #2: Introduce our new client to everybody else.
|
|
||||||
{
|
|
||||||
yyjson_mut_doc* jdoc = yyjson_mut_doc_new(NULL);
|
|
||||||
yyjson_mut_val* jroot = yyjson_mut_obj(jdoc);
|
|
||||||
yyjson_mut_doc_set_root(jdoc, jroot);
|
|
||||||
yyjson_mut_obj_add_str(jdoc, jroot, "type", "join-event");
|
|
||||||
|
|
||||||
yyjson_mut_val* jdata = yyjson_mut_obj(jdoc);
|
|
||||||
yyjson_mut_obj_add_val(jdoc, jroot, "data", jdata);
|
|
||||||
yyjson_mut_obj_add_uint(
|
|
||||||
jdoc, jdata, "id", session_get_id(sess)
|
|
||||||
);
|
|
||||||
yyjson_mut_obj_add_str(
|
|
||||||
jdoc, jdata, "username", session_get_username(sess)
|
|
||||||
);
|
|
||||||
|
|
||||||
size_t out_len;
|
|
||||||
char* out = yyjson_mut_write(jdoc, 0, &out_len);
|
|
||||||
size_t copy_len = out_len < SESSION_CHAT_BUF_SIZE
|
|
||||||
? out_len
|
|
||||||
: SESSION_CHAT_BUF_SIZE;
|
|
||||||
for (Session* s = head; s; s = s->next) {
|
|
||||||
s->buf_len = copy_len;
|
|
||||||
memcpy(&s->buf[LWS_PRE], out, copy_len);
|
|
||||||
lws_callback_on_writable(s->wsi);
|
|
||||||
}
|
|
||||||
free(out);
|
|
||||||
yyjson_mut_doc_free(jdoc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// NAME.
|
|
||||||
|
switch (packet->type) {
|
||||||
|
case PACKET_TYPE_JOIN:
|
||||||
|
// #1. Handle join packet, add new session.
|
||||||
|
do_join(sess, packet);
|
||||||
|
//
|
||||||
|
// #2. Welcome the new client.
|
||||||
|
{
|
||||||
|
yyjson_mut_doc* wdoc = yyjson_mut_doc_new(NULL);
|
||||||
|
yyjson_mut_val* wroot = yyjson_mut_obj(wdoc);
|
||||||
|
yyjson_mut_doc_set_root(wdoc, wroot);
|
||||||
|
yyjson_mut_obj_add_str(wdoc, wroot, "type", "welcome");
|
||||||
|
|
||||||
|
yyjson_mut_val* wdata = yyjson_mut_obj(wdoc);
|
||||||
|
yyjson_mut_obj_add_val(wdoc, wroot, "data", wdata);
|
||||||
|
yyjson_mut_obj_add_uint(
|
||||||
|
wdoc, wdata, "you", session_get_id(sess)
|
||||||
|
);
|
||||||
|
|
||||||
|
yyjson_mut_val* wusers = yyjson_mut_arr(wdoc);
|
||||||
|
yyjson_mut_obj_add_val(wdoc, wdata, "users", wusers);
|
||||||
|
|
||||||
|
for (Session* s = head; s; s = s->next) {
|
||||||
|
if (session_has_name(s)) {
|
||||||
|
yyjson_mut_val* uobj = yyjson_mut_obj(wdoc);
|
||||||
|
/* add each user once */
|
||||||
|
yyjson_mut_arr_add_val(wusers, uobj);
|
||||||
|
yyjson_mut_obj_add_uint(
|
||||||
|
wdoc, uobj, "id", session_get_id(s)
|
||||||
|
);
|
||||||
|
yyjson_mut_obj_add_str(
|
||||||
|
wdoc, uobj, "username", session_get_name(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// History will be streamed individually after welcome.
|
||||||
|
|
||||||
|
size_t out_len;
|
||||||
|
char* out = yyjson_mut_write(wdoc, 0, &out_len);
|
||||||
|
size_t copy_len = out_len < SESSION_CHAT_BUF_SIZE
|
||||||
|
? out_len
|
||||||
|
: SESSION_CHAT_BUF_SIZE;
|
||||||
|
sess->buf_len = copy_len;
|
||||||
|
memcpy(&sess->buf[LWS_PRE], out, copy_len);
|
||||||
|
// Schedule the welcome frame for writable callback.
|
||||||
|
lws_callback_on_writable(sess->wsi);
|
||||||
|
|
||||||
|
free(out);
|
||||||
|
yyjson_mut_doc_free(wdoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #2. Introduce our new client to everybody else.
|
||||||
|
{
|
||||||
|
yyjson_mut_doc* jdoc = yyjson_mut_doc_new(NULL);
|
||||||
|
yyjson_mut_val* jroot = yyjson_mut_obj(jdoc);
|
||||||
|
yyjson_mut_doc_set_root(jdoc, jroot);
|
||||||
|
yyjson_mut_obj_add_str(
|
||||||
|
jdoc, jroot, "type", "join-event"
|
||||||
|
);
|
||||||
|
|
||||||
|
yyjson_mut_val* jdata = yyjson_mut_obj(jdoc);
|
||||||
|
yyjson_mut_obj_add_val(jdoc, jroot, "data", jdata);
|
||||||
|
yyjson_mut_obj_add_uint(
|
||||||
|
jdoc, jdata, "id", session_get_id(sess)
|
||||||
|
);
|
||||||
|
yyjson_mut_obj_add_str(
|
||||||
|
jdoc, jdata, "username", session_get_name(sess)
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t out_len;
|
||||||
|
char* out = yyjson_mut_write(jdoc, 0, &out_len);
|
||||||
|
size_t copy_len = out_len < SESSION_CHAT_BUF_SIZE
|
||||||
|
? out_len
|
||||||
|
: SESSION_CHAT_BUF_SIZE;
|
||||||
|
for (Session* s = head; s; s = s->next) {
|
||||||
|
s->buf_len = copy_len;
|
||||||
|
memcpy(&s->buf[LWS_PRE], out, copy_len);
|
||||||
|
lws_callback_on_writable(s->wsi);
|
||||||
|
}
|
||||||
|
free(out);
|
||||||
|
yyjson_mut_doc_free(jdoc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PACKET_TYPE_MSG: do_msg(sess, packet); break;
|
||||||
|
default:
|
||||||
|
lwsl_warn(
|
||||||
|
"Received client-only packet: \"%s\".\n", (char*)in
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
// NAME.
|
||||||
else if (type && strcmp(type, "name") == 0 && sess &&
|
else if (type && strcmp(type, "name") == 0 && sess &&
|
||||||
session_has_username(sess)) {
|
session_has_username(sess)) {
|
||||||
const yyjson_val* dataVal =
|
const yyjson_val* dataVal =
|
||||||
@@ -226,12 +226,12 @@ int cb_chat(
|
|||||||
newname_buf[_j] = '\0';
|
newname_buf[_j] = '\0';
|
||||||
} else {
|
} else {
|
||||||
// Disallow empty names.
|
// Disallow empty names.
|
||||||
strcpy(newname_buf, session_get_username(sess));
|
strcpy(newname_buf, session_get_name(sess));
|
||||||
}
|
}
|
||||||
const char* newname = newname_buf;
|
const char* newname = newname_buf;
|
||||||
// Buffer old name before updating.
|
// Buffer old name before updating.
|
||||||
char oldname_buf[SESSION_USERNAME_MAX_LEN];
|
char oldname_buf[SESSION_USERNAME_MAX_LEN];
|
||||||
const char* current = session_get_username(sess);
|
const char* current = session_get_name(sess);
|
||||||
if (current) {
|
if (current) {
|
||||||
strncpy(oldname_buf, current, SESSION_USERNAME_MAX_LEN - 1);
|
strncpy(oldname_buf, current, SESSION_USERNAME_MAX_LEN - 1);
|
||||||
oldname_buf[SESSION_USERNAME_MAX_LEN - 1] = '\0';
|
oldname_buf[SESSION_USERNAME_MAX_LEN - 1] = '\0';
|
||||||
@@ -239,7 +239,7 @@ int cb_chat(
|
|||||||
oldname_buf[0] = '\0';
|
oldname_buf[0] = '\0';
|
||||||
}
|
}
|
||||||
// Now update to new name.
|
// Now update to new name.
|
||||||
session_set_username(sess, newname);
|
session_set_name(sess, newname);
|
||||||
|
|
||||||
// Broadcast name-event to other clients.
|
// Broadcast name-event to other clients.
|
||||||
{
|
{
|
||||||
@@ -340,7 +340,7 @@ int cb_chat(
|
|||||||
else yyjson_mut_obj_add_null(mdoc, mdat, "parent");
|
else yyjson_mut_obj_add_null(mdoc, mdat, "parent");
|
||||||
yyjson_mut_obj_add_uint(mdoc, mdat, "ts", (uint64_t)now);
|
yyjson_mut_obj_add_uint(mdoc, mdat, "ts", (uint64_t)now);
|
||||||
yyjson_mut_obj_add_str(
|
yyjson_mut_obj_add_str(
|
||||||
mdoc, mdat, "username", session_get_username(sess)
|
mdoc, mdat, "username", session_get_name(sess)
|
||||||
);
|
);
|
||||||
yyjson_mut_obj_add_str(mdoc, mdat, "content", msg);
|
yyjson_mut_obj_add_str(mdoc, mdat, "content", msg);
|
||||||
|
|
||||||
@@ -370,7 +370,8 @@ int cb_chat(
|
|||||||
|
|
||||||
yyjson_doc_free(doc);
|
yyjson_doc_free(doc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||||
if (sess && sess->buf_len > 0) {
|
if (sess && sess->buf_len > 0) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ typedef struct {
|
|||||||
Packet* packet_init(PacketType type, void* data);
|
Packet* packet_init(PacketType type, void* data);
|
||||||
|
|
||||||
// Create a packet from untrusted data.
|
// Create a packet from untrusted data.
|
||||||
Packet* packet_init_safe(char* type, void* data);
|
Packet* packet_init_safe(const char* type, const void* data);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Name name; // Your selected name.
|
Name name; // Your selected name.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef CHAT_H
|
#ifndef CHAT__H
|
||||||
#define CHAT_H
|
#define CHAT__H
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
// Message history (ring).
|
// Message history (ring).
|
||||||
#define CHAT_HISTORY_SZ 128
|
#define CHAT_HISTORY_SZ 128
|
||||||
extern MsgData* chat_history[CHAT_HISTORY_SZ];
|
extern MsgData* chat_history[CHAT_HISTORY_SZ];
|
||||||
extern size_t chat_history_head; // Points to the oldest message, or NULL.
|
extern size_t chat_history_head; // Points to the oldest message, or is NULL.
|
||||||
|
|
||||||
// Add message to history ring.
|
// Add message to history ring.
|
||||||
MsgData* chat_history_msg_add(MsgData* msg);
|
MsgData* chat_history_msg_add(MsgData* msg);
|
||||||
@@ -18,7 +18,6 @@ MsgData* chat_history_msg_add(MsgData* msg);
|
|||||||
// Get a list of messages in order.
|
// Get a list of messages in order.
|
||||||
MsgData** chat_history_nice(void);
|
MsgData** chat_history_nice(void);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cb_chat - libwebsockets protocol callback for COMS chat.
|
* cb_chat - libwebsockets protocol callback for COMS chat.
|
||||||
*
|
*
|
||||||
@@ -38,4 +37,6 @@ int cb_chat(
|
|||||||
size_t len
|
size_t len
|
||||||
);
|
);
|
||||||
|
|
||||||
#endif // CHAT_H
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#ifndef SESSION_H
|
#ifndef SESSION__H
|
||||||
#define SESSION_H
|
#define SESSION__H
|
||||||
|
|
||||||
#include <libwebsockets.h>
|
#include <libwebsockets.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
// Includes terminating null.
|
// Includes terminating null.
|
||||||
#define SESSION_USERNAME_MAX_LEN 32
|
|
||||||
#define SESSION_CHAT_BUF_SIZE 32768
|
#define SESSION_CHAT_BUF_SIZE 32768
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,13 +15,14 @@
|
|||||||
* Represents a single WebSocket client session in the chat server.
|
* Represents a single WebSocket client session in the chat server.
|
||||||
*/
|
*/
|
||||||
typedef struct SESSION {
|
typedef struct SESSION {
|
||||||
struct lws* wsi; // Libwebsockets connection handle.
|
struct lws* wsi; // Libwebsockets connection handle.
|
||||||
uint64_t id; // Unique session ID.
|
uint64_t id; // Unique session ID.
|
||||||
struct SESSION* next; // Next session in the internal list.
|
struct SESSION* next; // Next session in the internal list.
|
||||||
char name[SESSION_USERNAME_MAX_LEN]; // Stored username.
|
Name name; // Stored name.
|
||||||
bool named; // True once username is set.
|
bool named; // True once name is set.
|
||||||
unsigned char buf[LWS_PRE + SESSION_CHAT_BUF_SIZE]; // Outgoing buffer per session
|
unsigned char
|
||||||
size_t buf_len; // Length of data in buf
|
buf[LWS_PRE + SESSION_CHAT_BUF_SIZE]; // Outgoing buffer per session
|
||||||
|
size_t buf_len; // Length of data in buf
|
||||||
} Session;
|
} Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,31 +60,31 @@ Session* session_get_head(void);
|
|||||||
void session_broadcast(void (*cb)(Session* sess, void* user), void* user);
|
void session_broadcast(void (*cb)(Session* sess, void* user), void* user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* session_set_username
|
* session_set_name
|
||||||
* Store a username in the given session.
|
* Store a name with the given session.
|
||||||
*
|
*
|
||||||
* @param sess The session to update.
|
* @param sess The session to update.
|
||||||
* @param username Null-terminated string to copy into the session.
|
* @param name Null-terminated string to copy into the session.
|
||||||
*/
|
*/
|
||||||
void session_set_username(Session* sess, const char* username);
|
void session_set_name(Session* sess, const char* name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* session_get_username
|
* session_get_name
|
||||||
* Fetch the username stored in the session.
|
* Fetch the name stored in the session.
|
||||||
*
|
*
|
||||||
* @param sess The session to query.
|
* @param sess The session to query.
|
||||||
* @return Pointer to the stored username (readonly).
|
* @return Pointer to the stored name (readonly).
|
||||||
*/
|
*/
|
||||||
const char* session_get_username(const Session* sess);
|
const char* session_get_name(const Session* sess);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* session_has_username
|
* session_has_name
|
||||||
* Check whether a session has already set a username.
|
* Check whether a session has already set a name.
|
||||||
*
|
*
|
||||||
* @param sess The session to query.
|
* @param sess The session to query.
|
||||||
* @return True if a username is set, false otherwise.
|
* @return True if a name is set, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool session_has_username(const Session* sess);
|
bool session_has_name(const Session* sess);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* session_get_id
|
* session_get_id
|
||||||
@@ -93,4 +95,4 @@ bool session_has_username(const Session* sess);
|
|||||||
*/
|
*/
|
||||||
uint64_t session_get_id(const Session* sess);
|
uint64_t session_get_id(const Session* sess);
|
||||||
|
|
||||||
#endif // SESSION_H
|
#endif
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "include/session.h"
|
#include "include/session.h"
|
||||||
|
#include "include/data.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -39,19 +41,19 @@ void session_broadcast(void (*cb)(Session* s, void* user), void* user) {
|
|||||||
for (Session* iter = head; iter; iter = iter->next) { cb(iter, user); }
|
for (Session* iter = head; iter; iter = iter->next) { cb(iter, user); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_set_username(Session* sess, const char* username) {
|
void session_set_name(Session* sess, const char* name) {
|
||||||
if (!sess || !username) { return; }
|
if (!sess || !name) { return; }
|
||||||
strncpy(sess->name, username, SESSION_USERNAME_MAX_LEN - 1);
|
strncpy(sess->name, name, NAME_MAX_LENGTH - 1);
|
||||||
sess->name[SESSION_USERNAME_MAX_LEN - 1] = '\0';
|
sess->name[NAME_MAX_LENGTH - 1] = '\0';
|
||||||
sess->named = true;
|
sess->named = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* session_get_username(const Session* sess) {
|
const char* session_get_name(const Session* sess) {
|
||||||
if (!sess) { return NULL; }
|
if (!sess) { return NULL; }
|
||||||
return sess->name;
|
return sess->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool session_has_username(const Session* sess) {
|
bool session_has_name(const Session* sess) {
|
||||||
return sess ? sess->named : false;
|
return sess ? sess->named : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user