changeset 125:f236c895604e

[MrJuneJune] Added web socket for chat to this.
author June Park <parkjune1995@gmail.com>
date Thu, 08 Jan 2026 08:46:49 -0800
parents dbf14f84d51c
children e7899c93da77
files mrjunejune/BUILD mrjunejune/main.c mrjunejune/src/parts/base_head.html mrjunejune/src/talk/index.html seobeo/BUILD seobeo/s_websocket.c seobeo/s_websocket_common.c seobeo/s_websocket_server.c seobeo/seobeo.h seobeo/seobeo_internal.h
diffstat 10 files changed, 145 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/mrjunejune/BUILD	Thu Jan 08 07:31:32 2026 -0800
+++ b/mrjunejune/BUILD	Thu Jan 08 08:46:49 2026 -0800
@@ -29,14 +29,14 @@
 cc_binary(
   name = "mrjunejune_server",
   srcs = ["main.c"],
-  deps = ["//seobeo:seobeo_server"],
+  deps = ["//seobeo:seobeo_tcp_server_ws"],
   data = [":src_files"],
 )
 
 cc_binary(
   name = "mrjunejune_server_dev",
   srcs = ["main.c"],
-  deps = ["//seobeo:seobeo_server"],
+  deps = ["//seobeo:seobeo_tcp_server_ws"],
   data = [":src_files"],
 )
 
--- a/mrjunejune/main.c	Thu Jan 08 07:31:32 2026 -0800
+++ b/mrjunejune/main.c	Thu Jan 08 08:46:49 2026 -0800
@@ -474,16 +474,40 @@
   return resp;
 }
 
+void Chat_Handler(Seobeo_WebSocket_Server_Connection *p_conn, Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  (void)p_user_data;
+
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+  {
+    char message[2048];
+    snprintf(message, sizeof(message), "[%s]: %.*s", p_conn->client_id, (int)p_msg->length, (char*)p_msg->data);
+
+    Seobeo_Log(SEOBEO_INFO, "[Chat] Broadcasting: %s\n", message);
+    Seobeo_WebSocket_Server_Broadcast_Text(message, p_conn);
+  }
+}
+
+Seobeo_Request_Entry *GetTalk(Seobeo_Request_Entry *req, Dowa_Arena *arena)
+{
+  Seobeo_Request_Entry *resp = NULL;
+  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
+  Seobeo_ServerSideRender(final_body, "/talk/index.html", arena);
+  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
+  return resp;
+}
 
 CREATE_REDIRECT_HANDLER(HomePage, "/")
 CREATE_REDIRECT_HANDLER(Resume, "/resume")
 CREATE_REDIRECT_HANDLER(Tools, "/tools")
 CREATE_REDIRECT_HANDLER(MarkDownToHtml, "/tools/markdown_to_html")
 CREATE_REDIRECT_HANDLER(FileConverter, "/tools/file_converter")
+CREATE_REDIRECT_HANDLER(Talk, "/talk")
 
 int main(void)
 {
   Seobeo_Router_Init();
+
   Seobeo_Router_Register("GET", "/", GetHomePage);
   Seobeo_Router_Register("GET", "/index.html", GetRedirectHomePage);
 
@@ -508,5 +532,14 @@
   Seobeo_Router_Register("GET", "/blog", RenderBlogList);
   Seobeo_Router_Register("GET", "/blog/:blog_id", RenderBlog);
 
+  // -- Talk --/
+  Seobeo_Router_Register("GET", "/talk", GetTalk);
+  // Seobeo_Router_Register("GET", "/talk/index.html", GetRedirectTalk);
+
+  printf("Registered Websockets\n");
+
+  Seobeo_WebSocket_Server_Init();
+  Seobeo_WebSocket_Server_Register("/chat", Chat_Handler, NULL);
+
   Seobeo_Web_Server_Start("mrjunejune/src", "6969", SEOBEO_MODE_EDGE, 3);
 }
--- a/mrjunejune/src/parts/base_head.html	Thu Jan 08 07:31:32 2026 -0800
+++ b/mrjunejune/src/parts/base_head.html	Thu Jan 08 08:46:49 2026 -0800
@@ -5,10 +5,10 @@
 <link rel="preload" href="/public/fonts/Roboto-Regular.ttf" as="font"  crossorigin>
 <link rel="preload" href="/public/fonts/Roboto-Thin.ttf"as="font" crossorigin>
 
-<link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin>
-<link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin> -->
+<!-- <link rel="preload" href="/public/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin> -->
 
-<link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin>
+<!-- <link rel="preload" href="/public/fonts/more-sugar.extras.otf" as="font" type="font/otf" crossorigin> -->
 <link rel="preload" href="/public/fonts/more-sugar.regular.otf" as="font" type="font/otf" crossorigin>
 <link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/src/talk/index.html	Thu Jan 08 08:46:49 2026 -0800
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Talk!</title>
+  {{/parts/base_head.html}}
+  <style>
+    body { font-family: sans-serif; padding: 20px; }
+    #messages { height: 200px; border: 1px solid #ccc; overflow-y: scroll; margin-bottom: 10px; padding: 10px; }
+    #chat { display: flex; gap: 10px; }
+    input { flex-grow: 1; }
+  </style>
+</head>
+<body>
+  {{/parts/header.html}}
+  <h1>Talks</h1>
+
+  <div id="messages"></div>
+
+  <div id="chat">
+    <input type="text" id="messageInput" placeholder="Type a message...">
+    <button id="sendBtn">Send</button>
+  </div>
+  {{/parts/footer.html}}
+  <script>
+    const ws = new WebSocket('ws://localhost:6969/echo');
+    const messagesDiv = document.getElementById('messages');
+
+    ws.onopen = () => {
+      console.log('Connected!');
+      appendMessage('System: Connected to server');
+    };
+
+    ws.onmessage = (event) => {
+      console.log('Received:', event.data);
+      appendMessage('Server: ' + event.data);
+    };
+
+    // Function to send message
+    sendBtn.onclick = () => {
+      const message = messageInput.value;
+      if (message) {
+        ws.send(message);
+        appendMessage('You: ' + message);
+        messageInput.value = ''; // Clear input
+      }
+    };
+
+    // Helper to show messages on screen
+    function appendMessage(text) {
+      const msg = document.createElement('p');
+      msg.textContent = text;
+      messagesDiv.appendChild(msg);
+      messagesDiv.scrollTop = messagesDiv.scrollHeight;
+    }
+
+    messageInput.addEventListener('keydown', (event) => {
+      if (event.key === 'Enter' && !event.shiftKey)
+      {
+      event.preventDefault();
+      sendBtn.click();
+      }
+    });
+  </script>
+</body>
+</html>
--- a/seobeo/BUILD	Thu Jan 08 07:31:32 2026 -0800
+++ b/seobeo/BUILD	Thu Jan 08 08:46:49 2026 -0800
@@ -129,6 +129,7 @@
     "s_web.c",
     "s_logging.c",
     "s_ssl.c",
+    "s_websocket_common.c",
     "s_websocket_server.c",
     "os/s_macos_edge.c",
   ],
@@ -151,6 +152,7 @@
     "s_web.c",
     "s_logging.c",
     "s_ssl.c",
+    "s_websocket_common.c",
     "s_websocket_server.c",
     "os/s_linux_edge.c",
   ],
@@ -237,6 +239,7 @@
     "s_logging.c",
     "s_ssl.c",
     "s_http_client.c",
+    "s_websocket_common.c",
     "s_websocket.c",
     "snapshot_creator.c",
     "os/s_macos_edge.c",
@@ -259,6 +262,7 @@
     "s_logging.c",
     "s_ssl.c",
     "s_http_client.c",
+    "s_websocket_common.c",
     "s_websocket.c",
     "snapshot_creator.c",
     "os/s_linux_edge.c",
@@ -293,6 +297,7 @@
     "s_logging.c",
     "s_ssl.c",
     "s_http_client.c",
+    "s_websocket_common.c",
     "s_websocket.c",
     "s_websocket_server.c",
     "snapshot_creator.c",
@@ -318,6 +323,7 @@
     "s_logging.c",
     "s_ssl.c",
     "s_http_client.c",
+    "s_websocket_common.c",
     "s_websocket.c",
     "s_websocket_server.c",
     "snapshot_creator.c",
--- a/seobeo/s_websocket.c	Thu Jan 08 07:31:32 2026 -0800
+++ b/seobeo/s_websocket.c	Thu Jan 08 08:46:49 2026 -0800
@@ -190,11 +190,7 @@
   return p_ws;
 }
 
-static void Seobeo_WebSocket_Mask_Data(uint8 *data, size_t length, const uint8 *mask)
-{
-  for (size_t i = 0; i < length; i++)
-    data[i] ^= mask[i % 4];
-}
+// Seobeo_WebSocket_Mask_Data moved to s_websocket_common.c
 
 static int32 Seobeo_WebSocket_Send_Frame(Seobeo_WebSocket *p_ws, Seobeo_WebSocket_Opcode opcode,
     const uint8 *payload, size_t payload_length, boolean fin)
@@ -484,16 +480,7 @@
   return p_msg;
 }
 
-void Seobeo_WebSocket_Message_Destroy(Seobeo_WebSocket_Message *p_msg)
-{
-  if (!p_msg)
-    return;
-
-  if (p_msg->data)
-    free(p_msg->data);
-
-  free(p_msg);
-}
+// Seobeo_WebSocket_Message_Destroy moved to s_websocket_common.c
 
 int32 Seobeo_WebSocket_Close(Seobeo_WebSocket *p_ws, uint16 code, const char *reason)
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/seobeo/s_websocket_common.c	Thu Jan 08 08:46:49 2026 -0800
@@ -0,0 +1,23 @@
+#include "seobeo/seobeo.h"
+#include "seobeo/seobeo_internal.h"
+
+// Mask/unmask data with XOR operation (same for both directions)
+void Seobeo_WebSocket_Mask_Data(uint8 *data, size_t length, const uint8 *mask)
+{
+  for (size_t i = 0; i < length; i++)
+  {
+    data[i] ^= mask[i % 4];
+  }
+}
+
+// Destroy a WebSocket message
+void Seobeo_WebSocket_Message_Destroy(Seobeo_WebSocket_Message *p_msg)
+{
+  if (!p_msg)
+    return;
+
+  if (p_msg->data)
+    free(p_msg->data);
+
+  free(p_msg);
+}
--- a/seobeo/s_websocket_server.c	Thu Jan 08 07:31:32 2026 -0800
+++ b/seobeo/s_websocket_server.c	Thu Jan 08 08:46:49 2026 -0800
@@ -120,11 +120,7 @@
   return TRUE;
 }
 
-static void Seobeo_WebSocket_Unmask_Data(uint8 *data, size_t length, const uint8 *mask)
-{
-  for (size_t i = 0; i < length; i++)
-    data[i] ^= mask[i % 4];
-}
+// Seobeo_WebSocket_Unmask_Data removed - using Seobeo_WebSocket_Mask_Data from s_websocket_common.c (XOR is symmetric)
 
 static int32 Seobeo_WebSocket_Server_Send_Frame(Seobeo_WebSocket_Server_Connection *p_conn, Seobeo_WebSocket_Opcode opcode, const uint8 *payload, size_t payload_length, boolean fin)
 {
@@ -285,7 +281,7 @@
     memcpy(payload, buf + header_len, payload_len);
 
     if (masked)
-      Seobeo_WebSocket_Unmask_Data(payload, payload_len, mask_key);
+      Seobeo_WebSocket_Mask_Data(payload, payload_len, mask_key);
   }
 
   Seobeo_Handle_Consume(p_conn->p_handle, (uint32)(header_len + payload_len));
@@ -414,7 +410,7 @@
   Seobeo_WebSocket_Server_Connection_Destroy(p_conn);
 }
 
-void Seobeo_WebSocket_Server_Broadcast_Text(const char *text)
+void Seobeo_WebSocket_Server_Broadcast_Text(const char *text, Seobeo_WebSocket_Server_Connection *origin_p_conn)
 {
   if (!text)
     return;
@@ -422,7 +418,7 @@
   Seobeo_WebSocket_Server_Connection *p_conn = g_ws_connections;
   while (p_conn)
   {
-    if (p_conn->is_active)
+    if (p_conn->is_active && p_conn != origin_p_conn)
       Seobeo_WebSocket_Server_Send_Text(p_conn, text);
 
     p_conn = p_conn->next;
--- a/seobeo/seobeo.h	Thu Jan 08 07:31:32 2026 -0800
+++ b/seobeo/seobeo.h	Thu Jan 08 08:46:49 2026 -0800
@@ -313,7 +313,7 @@
 /* Send binary message to specific WebSocket client. */
 extern int32                              Seobeo_WebSocket_Server_Send_Binary(Seobeo_WebSocket_Server_Connection *p_conn, const uint8 *data, size_t length);
 /* Broadcast text message to all connected WebSocket clients. */
-extern void                               Seobeo_WebSocket_Server_Broadcast_Text(const char *text);
+extern void                               Seobeo_WebSocket_Server_Broadcast_Text(const char *text, Seobeo_WebSocket_Server_Connection *origin_p_conn);
 /* Broadcast binary message to all connected WebSocket clients. */
 extern void                               Seobeo_WebSocket_Server_Broadcast_Binary(const uint8 *data, size_t length);
 /* Close WebSocket connection with status code and reason. */
--- a/seobeo/seobeo_internal.h	Thu Jan 08 07:31:32 2026 -0800
+++ b/seobeo/seobeo_internal.h	Thu Jan 08 08:46:49 2026 -0800
@@ -187,6 +187,10 @@
   Dowa_Arena *p_arena;
 } Seobeo_WebSocket;
 
+// --- WebSocket Common Functions --- //
+extern void                      Seobeo_WebSocket_Mask_Data(uint8 *data, size_t length, const uint8 *mask);
+extern void                      Seobeo_WebSocket_Message_Destroy(Seobeo_WebSocket_Message *p_msg);
+
 // --- WebSocket Client Functions --- //
 extern Seobeo_WebSocket         *Seobeo_WebSocket_Connect(const char *url);
 extern int32                     Seobeo_WebSocket_Send_Text(Seobeo_WebSocket *p_ws, const char *text);
@@ -194,7 +198,6 @@
 extern int32                     Seobeo_WebSocket_Send_Ping(Seobeo_WebSocket *p_ws, const char *payload);
 extern int32                     Seobeo_WebSocket_Send_Pong(Seobeo_WebSocket *p_ws, const char *payload);
 extern Seobeo_WebSocket_Message *Seobeo_WebSocket_Receive(Seobeo_WebSocket *p_ws);
-extern void                      Seobeo_WebSocket_Message_Destroy(Seobeo_WebSocket_Message *p_msg);
 extern int32                     Seobeo_WebSocket_Close(Seobeo_WebSocket *p_ws, uint16 code, const char *reason);
 extern void                      Seobeo_WebSocket_Destroy(Seobeo_WebSocket *p_ws);
 
@@ -233,7 +236,7 @@
 extern void                               Seobeo_WebSocket_Server_Handle_Connection(Seobeo_WebSocket_Server_Connection *p_conn);
 extern int32                              Seobeo_WebSocket_Server_Send_Text(Seobeo_WebSocket_Server_Connection *p_conn, const char *text);
 extern int32                              Seobeo_WebSocket_Server_Send_Binary(Seobeo_WebSocket_Server_Connection *p_conn, const uint8 *data, size_t length);
-extern void                               Seobeo_WebSocket_Server_Broadcast_Text(const char *text);
+extern void                               Seobeo_WebSocket_Server_Broadcast_Text(const char *text, Seobeo_WebSocket_Server_Connection *origin_p_conn);
 extern void                               Seobeo_WebSocket_Server_Broadcast_Binary(const uint8 *data, size_t length);
 extern void                               Seobeo_WebSocket_Server_Connection_Close(Seobeo_WebSocket_Server_Connection *p_conn, uint16 code, const char *reason);
 extern void                               Seobeo_WebSocket_Server_Connection_Destroy(Seobeo_WebSocket_Server_Connection *p_conn);