changeset 131:b230a743a01e

Added blog.
author June Park <parkjune1995@gmail.com>
date Fri, 09 Jan 2026 07:42:04 -0800
parents f7860f491a8c (diff) 3a564ffb2092 (current diff)
children 7a63e41a21fb
files mrjunejune/src/blog/websocket-demystified/index.md mrjunejune/src/public/web-socket-header.png
diffstat 54 files changed, 1367 insertions(+), 2572 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.bazelrc	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,5 @@
+# Auto-select config based on host OS
+common --enable_platform_specific_config
+
+# Suppress duplicate library warnings from openssl BCR module on macOS
+build:macos --linkopt=-Wl,-no_warn_duplicate_libraries
--- a/gara/BUILD	Fri Jan 09 07:19:09 2026 -0800
+++ b/gara/BUILD	Fri Jan 09 07:42:04 2026 -0800
@@ -3,7 +3,7 @@
 cc_binary(
   name = "gara_c",
   srcs = ["main.c"],
-  deps = ["//seobeo:seobeo"],
+  deps = ["//seobeo:seobeo_min"],
 )
 
 
Binary file gara/gara has changed
--- a/gui_ze/gui_ze.bzl	Fri Jan 09 07:19:09 2026 -0800
+++ b/gui_ze/gui_ze.bzl	Fri Jan 09 07:42:04 2026 -0800
@@ -32,7 +32,6 @@
     for directory in f.path.split("/"):
       if directory == binary.short_path.split("/")[0]:
         break
-      print("\n\n equals: ", directory, binary.short_path.split("/")[0]);
       start += 1
 
     # Remove the first folder (output) and last file (actaul files that needed to be copied)
@@ -49,9 +48,6 @@
     command = " && ".join(copy_cmd),
     progress_message = "Bundling {}".format(ctx.label.name),
   )
-
-  print("[INFO] See {}".format(out_dir.path))
-
   return [DefaultInfo(files = depset([out_dir]))]
 
 bundle = rule(
--- a/hg-web/BUILD	Fri Jan 09 07:19:09 2026 -0800
+++ b/hg-web/BUILD	Fri Jan 09 07:42:04 2026 -0800
@@ -26,11 +26,3 @@
   name = "hg_web_server_bundle",
   binary = ":hg_web_server",
 )
-
-cc_binary(
-  name = "hg_web_server_dev",
-  srcs = ["main.c"],
-  deps = ["//seobeo:seobeo_server_dev"],
-  data = [":src_files"],
-  defines = ["REPO_ROOT=\\\"\"/Users/mrjunejune/zenbu\"\\\""],
-)
--- a/mrjunejune/BUILD	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/BUILD	Fri Jan 09 07:42:04 2026 -0800
@@ -1,8 +1,8 @@
 load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
 load("@rules_cc//cc:cc_library.bzl", "cc_library")
-# load("@rules_python//python:py_binary.bzl", "py_binary")
 load("//gui_ze:gui_ze.bzl", "move_files_into_dir", "bundle")
 
+# Files
 move_files_into_dir(
   name = "compiled_ts_games",
   srcs = [
@@ -14,7 +14,6 @@
 move_files_into_dir(
   name = "compiled_ts",
   srcs = [
-    "//playground:hello",
     "//markdown_converter:markdown_to_html",
   ],
   dest = "src",
@@ -23,22 +22,26 @@
 filegroup(
   name = "src_files",
   srcs = glob(["src/**"]) + [":compiled_ts", ":compiled_ts_games"],
+  visibility = ["//mrjunejune/test:__pkg__"],
 )
 
+# Server binary
 cc_binary(
   name = "mrjunejune_server",
   srcs = ["main.c"],
-  deps = ["//seobeo:seobeo_server"],
+  deps = ["//seobeo:seobeo_tcp_server_ws"],
   data = [":src_files"],
+  visibility = ["//mrjunejune/test:__pkg__"],
 )
 
 cc_binary(
   name = "mrjunejune_server_dev",
   srcs = ["main.c"],
-  deps = ["//seobeo:seobeo_server_dev"],
+  deps = ["//seobeo:seobeo_tcp_server_ws"],
   data = [":src_files"],
 )
 
+# Rlease bundle
 bundle(
   name = "mrjunejune_server_bundle",
   binary = ":mrjunejune_server",
@@ -49,45 +52,8 @@
   binary = ":mrjunejune_server_dev",
 )
 
-cc_test(
-  name = "integration_test",
-  srcs = ["test/integration_test.c"],
-  deps = ["//seobeo:seobeo_client"],
-  data = [
-    "//mrjunejune:mrjunejune_server",
-    "//mrjunejune:src_files",
-    "//mrjunejune:test_snapshots",
-    "//mrjunejune:test_files",
-  ],
-  size = "large",
-  timeout = "long",
-  args = ["$(location //mrjunejune:mrjunejune_server)"],
-)
-
-cc_binary(
-  name = "create_snapshots",
-  srcs = ["test/create_snapshots.c"],
-  deps = ["//seobeo:seobeo_client"],
-  data = [
-    "//mrjunejune:mrjunejune_server",
-    "//mrjunejune:src_files",
-  ],
-  args = ["$(location //mrjunejune:mrjunejune_server)"],
-)
-
-filegroup(
-  name = "test_snapshots",
-  srcs = glob(["test/snapshots/**"]),
-)
-
-filegroup(
-  name = "test_files",
-  srcs = [
-    "test/shiba.webp",
-    "test/test_avi.avi",
-  ],
-)
-
+# Experimenting with python to see if I can call it as ffi.
+# load("@rules_python//python:py_binary.bzl", "py_binary")
 # This was to use python ffi, but w/e
 # cc_library(
 #   name = "mrjunejune_server_lib",
--- a/mrjunejune/main.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/main.c	Fri Jan 09 07:42:04 2026 -0800
@@ -1,3 +1,4 @@
+#define SEOBEO_ENABLE_DEBUG
 #include "seobeo/seobeo.h"
 #include <time.h>
 
@@ -473,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);
 
@@ -507,5 +532,15 @@
   Seobeo_Router_Register("GET", "/blog", RenderBlogList);
   Seobeo_Router_Register("GET", "/blog/:blog_id", RenderBlog);
 
+  // -- Talk --/
+  Seobeo_Router_Register("GET", "/talk", GetTalk);
+  // TODO: Bug where I can't est redriect huh...
+  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/python_server.py	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-from cffi import FFI
-import os
-
-ffi = FFI()
-ffi.cdef("void start_server(void);")
-
-# Bazel runs binaries from a sandbox, so use runfiles to locate the .so
-import pathlib
-runfiles_dir = pathlib.Path(__file__).parent
-libpath = runfiles_dir / "mrjunejune_server_so.so"
-
-C = ffi.dlopen(str(libpath))
-C.start_server()
-
--- a/mrjunejune/server_entry.c	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-#include "seobeo/seobeo.h"
-
-void start_server(void) {
-  Seobeo_Web_Server_Start("mrjunejune/pages", "6969", SEOBEO_MODE_EDGE, 2);
-}
--- a/mrjunejune/src/blog/websocket-demystified/index.md	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/src/blog/websocket-demystified/index.md	Fri Jan 09 07:42:04 2026 -0800
@@ -31,9 +31,11 @@
 
 To start the upgrade from HTTP to WebSocket, the client sends a standard GET request but with some very specific headers.
 
+<div class="center"> <img src="/public/white-noise-grass.png" /> </div>
+
 I’m assuming you know how HTTP works. If not, you can open a developer tool by right clicking on your browser and seeing into network tab and refershign the page. The only interesting values here is the `Sec-WebSocket-Key`. This key is usually a 16-byte random value encoded in **Base64**.
 
-> **Note:** It’s not for security—it’s to prevent intermediate caches from accidentally serving a cached WebSocket response to a different client.
+**Note:** It’s not for security—it’s to prevent intermediate caches from accidentally serving a cached WebSocket response to a different client.
 
 But before we jump into that, we need to construct that Base64 key.
 
@@ -43,6 +45,7 @@
 
 > "Base64 is a binary-to-text encoding scheme that represents data in an ASCII string format by translating it into a radix-64 representation, using a specific set of 64 printable characters." — Gemini
 
+
 Nice, it didn't halluciante. Let's constracut these. Here they are 64 characters that are safe to print in ASCII.:
 
 ```c
--- a/mrjunejune/src/parts/base_head.html	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/src/parts/base_head.html	Fri Jan 09 07:42:04 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>
 
Binary file mrjunejune/src/public/web-socket-header.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/src/talk/index.html	Fri Jan 09 07:42:04 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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/test/BUILD	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,48 @@
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+load("//gui_ze:gui_ze.bzl", "move_files_into_dir", "bundle")
+
+# Files needed for test
+filegroup(
+  name = "test_snapshots",
+  srcs = glob(["snapshots/**"]),
+)
+
+filegroup(
+  name = "test_files",
+  srcs = [
+    "shiba.webp",
+    "test_avi.avi",
+  ],
+)
+
+# To create a snapsho to compare
+cc_binary(
+  name = "create_snapshots",
+  srcs = ["auto_generated_test.c", "test.h"],
+  deps = ["//seobeo:seobeo_tcp_client"],
+  data = [
+    "//mrjunejune:mrjunejune_server",
+    "//mrjunejune:src_files",
+  ],
+  args = ["$(location //mrjunejune:mrjunejune_server)"],
+)
+
+# Tests
+cc_test(
+  name = "integration_test",
+  srcs = [
+    "integration_test.c",
+    "test.h"
+  ],
+  deps = ["//seobeo:seobeo_tcp_client"],
+  data = [
+    "//mrjunejune:mrjunejune_server",
+    "//mrjunejune:src_files",
+    ":test_snapshots",
+    ":test_files",
+  ],
+  size = "large",
+  timeout = "long",
+  args = ["$(location //mrjunejune:mrjunejune_server)"],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/test/auto_generated_test.c	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,45 @@
+// Auto-generated test file
+// Source: /Users/mrjunejune/zenbu/mrjunejune/main.c
+// DO NOT EDIT - Regenerate with: bazel run //seobeo:test_generator
+
+#include "mrjunejune/test/test.h"
+
+#define TEST_HOST "127.0.0.1"
+#define TEST_PORT "6969"
+
+int main(int argc, char *argv[])
+{
+  printf("=== Auto-generated Snapshot Creator ===\n\n");
+
+  const char *server_binary = "./server";
+  if (argc > 1) server_binary = argv[1];
+
+  pid_t server_pid = start_test_server(server_binary);
+  if (server_pid < 0) return 1;
+
+  SnapshotConfig configs[] = {
+    {"/", 200, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/index.html", 301, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/resume", 200, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/resume/index.html", 301, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/tools", 200, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/tools/index.html", 301, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/tools/markdown_to_html", 200, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/tools/markdown_to_html/index.html", 301, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/tools/file_converter", 200, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/tools/file_converter/index.html", 301, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    // TODO: POST route - POST /api/convert/image-to-webp - requires request body
+    // TODO: POST route - POST /api/convert/video-to-mp4 - requires request body
+    // TODO: Dynamic route - GET /api/download/:filename - fill in actual path
+    {"/blog", 200, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    // TODO: Dynamic route - GET /blog/:blog_id - fill in actual path
+    {"/talk", 200, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+    {"/talk/index.html", 301, SNAPSHOT_DIR, TEST_HOST, TEST_PORT},
+  };
+
+  int count = sizeof(configs) / sizeof(configs[0]);
+  int result = Seobeo_Snapshots_Create_Batch(configs, count);
+
+  stop_test_server(server_pid);
+  return result;
+}
--- a/mrjunejune/test/create_snapshots.c	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-#include "seobeo/seobeo.h"
-#include "seobeo/snapshot_creator.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-
-#define TEST_PORT "6969"
-#define TEST_HOST "127.0.0.1"
-#define SNAPSHOT_DIR "mrjunejune/test/snapshots"
-
-// Start the server process
-static pid_t start_server(const char *server_binary)
-{
-  pid_t server_pid = fork();
-
-  if (server_pid < 0)
-  {
-    perror("fork");
-    return -1;
-  }
-
-  if (server_pid == 0)
-  {
-    // Child process - run server
-    printf("Starting server on port %s...\n", TEST_PORT);
-    execl(server_binary, server_binary, NULL);
-    perror("execl failed");
-    exit(1);
-  }
-
-  // Parent - verify server started
-  printf("Server started (PID: %d)\n", server_pid);
-
-  usleep(100000);
-  int status;
-  pid_t result = waitpid(server_pid, &status, WNOHANG);
-  if (result != 0)
-  {
-    if (WIFEXITED(status))
-    {
-      fprintf(stderr, "Server exited with code: %d\n", WEXITSTATUS(status));
-    }
-    else if (WIFSIGNALED(status))
-    {
-      fprintf(stderr, "Server killed by signal: %d\n", WTERMSIG(status));
-    }
-    return -1;
-  }
-
-  sleep(2);
-  printf("Server ready\n\n");
-
-  return server_pid;
-}
-
-// Stop the server process
-static void stop_server_test(pid_t server_pid)
-{
-  if (server_pid > 0)
-  {
-    printf("\nStopping server (PID: %d)...\n", server_pid);
-    kill(server_pid, SIGTERM);
-    waitpid(server_pid, NULL, 0);
-    printf("Server stopped\n");
-  }
-}
-
-int main(int argc, char *argv[])
-{
-  printf("=== Seobeo Snapshot Creator ===\n\n");
-
-  // Get workspace directory (where the source files are)
-  const char *workspace_dir = getenv("BUILD_WORKSPACE_DIRECTORY");
-  if (!workspace_dir)
-  {
-    fprintf(stderr, "Error: BUILD_WORKSPACE_DIRECTORY not set\n");
-    fprintf(stderr, "This binary must be run with 'bazel run'\n");
-    return 1;
-  }
-
-  // Construct full path to snapshot directory
-  char snapshot_path[1024];
-  snprintf(snapshot_path, sizeof(snapshot_path), "%s/%s", workspace_dir, SNAPSHOT_DIR);
-
-  // Get server binary path
-  const char *server_binary = "./mrjunejune_server";
-  if (argc > 1)
-  {
-    server_binary = argv[1];
-  }
-
-  printf("Workspace: %s\n", workspace_dir);
-  printf("Server binary: %s\n", server_binary);
-  printf("Snapshot directory: %s\n\n", snapshot_path);
-
-  // Start server
-  pid_t server_pid = start_server(server_binary);
-  if (server_pid < 0)
-  {
-    fprintf(stderr, "Failed to start server\n");
-    return 1;
-  }
-
-  // Define snapshots to create - paths that should succeed (200 OK)
-  SnapshotConfig success_snapshots[] = {
-    {"/", 200, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/resume", 200, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/tools", 200, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/tools/markdown_to_html", 200, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/tools/file_converter", 200, snapshot_path, TEST_HOST, TEST_PORT},
-  };
-  int num_success = sizeof(success_snapshots) / sizeof(success_snapshots[0]);
-
-  // Define snapshots for redirect endpoints (301)
-  SnapshotConfig redirect_snapshots[] = {
-    {"/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/resume/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/tools/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/tools/markdown_to_html/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/tools/file_converter/index.html", 301, snapshot_path, TEST_HOST, TEST_PORT},
-  };
-  int num_redirects = sizeof(redirect_snapshots) / sizeof(redirect_snapshots[0]);
-
-  SnapshotConfig error_snapshots[] = {
-    {"/nonexistent", 404, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/does/not/exist", 404, snapshot_path, TEST_HOST, TEST_PORT},
-    {"/missing.html", 404, snapshot_path, TEST_HOST, TEST_PORT},
-  };
-  int num_errors = sizeof(error_snapshots) / sizeof(error_snapshots[0]);
-
-  int total_failed = 0;
-  int total_passed = 0;
-
-  // Create success snapshots
-  printf("Creating snapshots for successful paths:\n\n");
-  for (int i = 0; i < num_success; i++)
-  {
-    if (Seobeo_Snapshot_Create(&success_snapshots[i]) == 0)
-    {
-      total_passed++;
-    }
-    else
-    {
-      total_failed++;
-    }
-  }
-
-  // Create redirect snapshots
-  printf("\nCreating snapshots for redirect paths:\n\n");
-  for (int i = 0; i < num_redirects; i++)
-  {
-    if (Seobeo_Snapshot_Create(&redirect_snapshots[i]) == 0)
-    {
-      total_passed++;
-    }
-    else
-    {
-      total_failed++;
-    }
-  }
-
-  // Create error snapshots
-  printf("\nCreating snapshots for error paths:\n\n");
-  for (int i = 0; i < num_errors; i++)
-  {
-    if (Seobeo_Snapshot_Create(&error_snapshots[i]) == 0)
-    {
-      total_passed++;
-    }
-    else
-    {
-      total_failed++;
-    }
-  }
-
-  // Stop server
-  stop_server_test(server_pid);
-
-  // Print summary
-  printf("\n=== Summary ===\n");
-  printf("Snapshots created: %d\n", total_passed);
-  printf("Failed: %d\n", total_failed);
-
-  if (total_failed == 0)
-  {
-    printf("\n✓ All snapshots created successfully!\n");
-    printf("\nSnapshots saved to: %s/\n", snapshot_path);
-    printf("\nRun tests to verify:\n");
-    printf("  bazel test //mrjunejune:integration_test\n");
-    return 0;
-  }
-  else
-  {
-    printf("\n✗ Some snapshots failed\n");
-    return 1;
-  }
-}
--- a/mrjunejune/test/integration_test.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/integration_test.c	Fri Jan 09 07:42:04 2026 -0800
@@ -1,19 +1,4 @@
-#include "seobeo/seobeo.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <assert.h>
-
-#define TEST_PORT "6969"
-#define TEST_HOST "127.0.0.1"
-#define MAX_RESPONSE_SIZE (1024 * 1024)
-#ifndef SNAPSHOT_DIR
-  // TODO: Make it as current directory /snapshots...
-  #define SNAPSHOT_DIR "mrjunejune/test/snapshots"
-#endif
+#include "mrjunejune/test/test.h"
 
 // Test case structure
 typedef struct {
@@ -25,6 +10,37 @@
   size_t response_len;
 } TestCase;
 
+void debug_diff(const char *expected, const char *actual)
+{
+    printf("\n--- DIFF (Expected vs Actual) ---\n");
+    
+    // Create copies to use with strtok (strtok modifies the string)
+    char *exp_copy = strdup(expected);
+    char *act_copy = strdup(actual);
+    
+    char *exp_line = strtok(exp_copy, "\n");
+    char *act_line = strtok(act_copy, "\n");
+    
+    int line_num = 1;
+    while (exp_line != NULL || act_line != NULL) {
+        if (exp_line && act_line && strcmp(exp_line, act_line) == 0) {
+            // Lines match - optional: print nothing or a dot
+        } else {
+            printf("Line %d mismatch:\n", line_num);
+            printf("  EXP: [%s]\n", exp_line ? exp_line : "(end of string)");
+            printf("  ACT: [%s]\n", act_line ? act_line : "(end of string)");
+            printf("  -----------------------------------\n");
+        }
+        
+        exp_line = strtok(NULL, "\n");
+        act_line = strtok(NULL, "\n");
+        line_num++;
+    }
+    
+    free(exp_copy);
+    free(act_copy);
+}
+
 // Helper: Convert URL path to filename
 // "/" -> "root.snapshot"
 // "/index.html" -> "index.html.snapshot"
@@ -117,274 +133,68 @@
   return 0;
 }
 
-// Helper: Create test client
-Seobeo_Handle* create_test_client()
-{
-  Seobeo_Handle *client = Seobeo_Stream_Handle_Client_Create(TEST_HOST, TEST_PORT, FALSE);
-  if (!client || client->socket < 0)
-  {
-    if (client)
-    {
-      Seobeo_Handle_Destroy(client);
-    }
-    return NULL;
-  }
-  return client;
-}
-
-// Helper: Generate default HTTP GET request
-int generate_http_get_request(char *buffer, size_t buffer_size, const char *path)
+int execute_test_case(TestCase *test, pid_t server_pid)
 {
-  return snprintf(
-    buffer, buffer_size,
-    "GET %s HTTP/1.1\r\n"
-    "Host: %s\r\n"
-    "Connection: close\r\n"
-    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
-    "User-Agent: SeobeoTestClient/1.0\r\n"
-    "\r\n",
-    path, TEST_HOST
-  );
-}
-
-// Helper: Send HTTP request
-int send_http_request(Seobeo_Handle *client, const char *path, const char *custom_request)
-{
-  char request_buffer[4096];
-  int request_len;
-
-  if (custom_request)
+  printf("  Testing: GET %s (expecting %d)\n", test->path, test->expected_status);
+  
+  int32 max_url_length = 1024*3;
+  char *url = malloc(sizeof(char)*max_url_length);
+  snprintf(url, max_url_length, "%s%s", TEST_URL, test->path);
+  Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url);
+  if (!p_req)
   {
-    request_len = snprintf(request_buffer, sizeof(request_buffer), "%s", custom_request);
-  }
-  else
-  {
-    request_len = generate_http_get_request(request_buffer, sizeof(request_buffer), path);
-  }
-
-  if (request_len < 0 || request_len >= sizeof(request_buffer))
-  {
-    fprintf(stderr, "Request buffer too small\n");
-    return -1;
-  }
-
-  Seobeo_Handle_Queue(client, (uint8*)request_buffer, (uint32)request_len);
-  return Seobeo_Handle_Flush(client);
-}
-
-// Helper: Read HTTP response
-int read_http_response(Seobeo_Handle *client, char **response_out, size_t *response_len_out)
-{
-  char *response = malloc(MAX_RESPONSE_SIZE);
-  if (!response)
-  {
+    printf("Can't create requests");
     return -1;
   }
 
-  size_t total_bytes = 0;
-  int attempts = 0;
-  const int max_attempts = 100;
-
-  while (attempts++ < max_attempts && total_bytes < MAX_RESPONSE_SIZE - 1)
+  Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req);
+  if (!p_resp)
   {
-    int bytes_read = Seobeo_Handle_Read(client);
-
-    if (bytes_read > 0)
-    {
-      size_t to_copy = client->read_buffer_len;
-      if (total_bytes + to_copy > MAX_RESPONSE_SIZE - 1)
-      {
-        to_copy = MAX_RESPONSE_SIZE - 1 - total_bytes;
-      }
-
-      memcpy(response + total_bytes, client->read_buffer, to_copy);
-      total_bytes += to_copy;
-      Seobeo_Handle_Consume(client, (uint32)to_copy);
-    }
-    else if (bytes_read == -2)
-    {
-      // Connection closed
-      break;
-    }
-    else if (bytes_read == 0)
-    {
-      // Would block
-      usleep(10000);
-      continue;
-    }
-    else
-    {
-      free(response);
-      return -1;
-    }
-  }
-
-  response[total_bytes] = '\0';
-  *response_out = response;
-  *response_len_out = total_bytes;
-
-  return (total_bytes > 0) ? 0 : -1;
-}
-
-// Helper: Parse HTTP status code
-int parse_http_status(const char *response)
-{
-  if (!response || strlen(response) < 12)
-  {
-    return -1;
-  }
-
-  const char *status_start = strstr(response, "HTTP/1.1 ");
-  if (!status_start)
-  {
-    status_start = strstr(response, "HTTP/1.0 ");
-  }
-
-  if (!status_start)
-  {
+    printf("No response");
     return -1;
   }
 
-  int status_code;
-  if (sscanf(status_start + 9, "%d", &status_code) == 1)
+  if (p_resp->status_code != test->expected_status)
   {
-    return status_code;
-  }
-
-  return -1;
-}
-
-// Helper: Check if status is a redirect
-int is_redirect_status(int status)
-{
-  return (status >= 300 && status < 400);
-}
-
-// Helper: Execute a test case
-int execute_test_case(TestCase *test, pid_t server_pid)
-{
-  printf("  Testing: GET %s (expecting %d)\n", test->path, test->expected_status);
-
-  Seobeo_Handle *client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client connection\n");
-    return -1;
-  }
-
-  if (send_http_request(client, test->path, NULL) < 0)
-  {
-    printf("    ✗ Failed to send request\n");
-    Seobeo_Handle_Destroy(client);
+    printf("    ✗ Status mismatch: expected %d, got %d\n",
+           test->expected_status, p_resp->status_code);
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  char *response = NULL;
-  size_t response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read response\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  test->actual_response = response;
-  test->response_len = response_len;
+  printf("    ✓ Status code: %d\n", p_resp->status_code);
 
-  int actual_status = parse_http_status(response);
-  if (actual_status != test->expected_status)
-  {
-    printf("    ✗ Status mismatch: expected %d, got %d\n",
-           test->expected_status, actual_status);
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  printf("    ✓ Status code: %d\n", actual_status);
-
-  // For redirects, skip content comparison
-  if (is_redirect_status(actual_status))
-  {
-    printf("    ⚠ Redirect status - skipping content comparison\n");
-    Seobeo_Handle_Destroy(client);
-    return 0;
-  }
-
-  // Only verify 200 OK responses against snapshots
-  if (actual_status == 200)
+  if (p_resp->status_code == 200)
   {
     if (!test->expected_content)
     {
       printf("    ✗ No expected snapshot found: %s\n", test->expected_file_path);
       printf("    → Run: bazel run //mrjunejune:create_snapshots\n");
-      Seobeo_Handle_Destroy(client);
+      Seobeo_Client_Response_Destroy(p_resp);
+      Seobeo_Client_Request_Destroy(p_req);
       return -1;
     }
 
-    if (strcmp(response, test->expected_content) != 0)
+    if (strcmp(p_resp->body, test->expected_content) != 0)
     {
       printf("    ✗ Response does not match expected snapshot\n");
       printf("    Expected file: %s\n", test->expected_file_path);
-      Seobeo_Handle_Destroy(client);
+      debug_diff(p_resp->body, test->expected_content);
+      Seobeo_Client_Response_Destroy(p_resp);
+      Seobeo_Client_Request_Destroy(p_req);
       return -1;
     }
 
-    printf("    ✓ Response matches snapshot (%zu bytes)\n", response_len);
+    printf("    ✓ Response matches snapshot (%zu bytes)\n", p_resp->body_length);
   }
 
-  Seobeo_Handle_Destroy(client);
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
   return 0;
 }
 
-// Helper: Execute custom request test
-int execute_custom_request_test(const char *name, const char *custom_request,
-                                 int expected_status, pid_t server_pid)
-{
-  printf("  Testing: %s (expecting %d)\n", name, expected_status);
-
-  Seobeo_Handle *client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client connection\n");
-    return -1;
-  }
-
-  if (send_http_request(client, NULL, custom_request) < 0)
-  {
-    printf("    ✗ Failed to send request\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  char *response = NULL;
-  size_t response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read response\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  int actual_status = parse_http_status(response);
-  if (actual_status != expected_status)
-  {
-    printf("    ✗ Status mismatch: expected %d, got %d\n",
-           expected_status, actual_status);
-    free(response);
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  printf("    ✓ Status code: %d\n", actual_status);
-  printf("    ✓ Response received (%zu bytes)\n", response_len);
-
-  free(response);
-  Seobeo_Handle_Destroy(client);
-  return 0;
-}
-
-// Helper: Send POST request with file data
-int send_post_file(Seobeo_Handle *client, const char *path, const char *file_data, size_t file_size)
+int send_post_file(Seobeo_Handle *p_req, const char *path, const char *file_data, size_t file_size)
 {
   char request_buffer[8192];
   int header_len = snprintf(
@@ -405,7 +215,7 @@
   }
 
   // Send headers
-  Seobeo_Handle_Queue(client, (uint8*)request_buffer, (uint32)header_len);
+  Seobeo_Handle_Queue(p_req, (uint8*)request_buffer, (uint32)header_len);
 
   // Send file data in chunks if needed
   size_t remaining = file_size;
@@ -413,15 +223,14 @@
   while (remaining > 0)
   {
     size_t chunk_size = remaining > 4096 ? 4096 : remaining;
-    Seobeo_Handle_Queue(client, (uint8*)ptr, (uint32)chunk_size);
+    Seobeo_Handle_Queue(p_req, (uint8*)ptr, (uint32)chunk_size);
     ptr += chunk_size;
     remaining -= chunk_size;
   }
 
-  return Seobeo_Handle_Flush(client);
+  return Seobeo_Handle_Flush(p_req);
 }
 
-// Helper: Extract JSON field value from response body
 char* extract_json_field(const char *json, const char *field, char *buffer, size_t buffer_size)
 {
   char search_pattern[256];
@@ -452,7 +261,6 @@
   return buffer;
 }
 
-// Helper: Test POST file conversion
 int test_file_conversion(const char *endpoint, const char *test_file_path,
                          const char *expected_format, pid_t server_pid)
 {
@@ -461,204 +269,64 @@
   // Read test file
   size_t file_size;
   char *file_data = read_file(test_file_path, &file_size);
-  if (!file_data)
-  {
+  if (!file_data) {
     printf("    ✗ Failed to read test file: %s\n", test_file_path);
     return -1;
   }
 
-  printf("    → Loaded test file (%zu bytes)\n", file_size);
+  char url[1024];
+  snprintf(url, sizeof(url), "%s%s", TEST_URL, endpoint);
 
-  // Create client and send request
-  Seobeo_Handle *client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client connection\n");
-    free(file_data);
-    return -1;
-  }
+  Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url);
+  Seobeo_Client_Request_Set_Method(p_req, "POST");
+  Seobeo_Client_Request_Set_Body(p_req, (uint8*)file_data, (uint32)file_size);
+  Seobeo_Client_Request_Add_Header_Array(p_req, "Content-Type: application/octet-stream");
 
-  if (send_post_file(client, endpoint, file_data, file_size) < 0)
-  {
-    printf("    ✗ Failed to send POST request\n");
-    free(file_data);
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
+  Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req);
   free(file_data);
 
-  // Read response
-  char *response = NULL;
-  size_t response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read response\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  Seobeo_Handle_Destroy(client);
-
-  // Parse status
-  int status = parse_http_status(response);
-  if (status != 200)
-  {
-    printf("    ✗ Conversion failed with status: %d\n", status);
-    printf("    Response: %s\n", response);
-    free(response);
-    return -1;
-  }
-
-  printf("    ✓ Status code: 200\n");
-
-  // Extract download URL from JSON response
-  const char *body = strstr(response, "\r\n\r\n");
-  if (!body)
-  {
-    printf("    ✗ No response body found\n");
-    free(response);
-    return -1;
-  }
-  body += 4;
+  if (!p_resp || p_resp->status_code != 200) {
+    printf("    ✗ Conversion failed with status: %d\n", p_resp ? p_resp->status_code : 0);
+    if (p_resp) Seobeo_Client_Response_Destroy(p_resp);
 
-  char download_url[512];
-  if (!extract_json_field(body, "download_url", download_url, sizeof(download_url)))
-  {
-    printf("    ✗ Failed to extract download_url from response\n");
-    printf("    Response body: %s\n", body);
-    free(response);
-    return -1;
-  }
-
-  printf("    ✓ Conversion succeeded\n");
-  printf("    ✓ Download URL: %s\n", download_url);
-  free(response);
-
-  // Test downloading the converted file
-  printf("    → Testing download: GET %s\n", download_url);
-
-  client = create_test_client();
-  if (!client)
-  {
-    printf("    ✗ Failed to create client for download\n");
-    return -1;
-  }
-
-  if (send_http_request(client, download_url, NULL) < 0)
-  {
-    printf("    ✗ Failed to send download request\n");
-    Seobeo_Handle_Destroy(client);
-    return -1;
-  }
-
-  response = NULL;
-  response_len = 0;
-  if (read_http_response(client, &response, &response_len) < 0)
-  {
-    printf("    ✗ Failed to read download response\n");
-    Seobeo_Handle_Destroy(client);
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  Seobeo_Handle_Destroy(client);
-
-  status = parse_http_status(response);
-  if (status != 200)
-  {
-    printf("    ✗ Download failed with status: %d\n", status);
-    free(response);
-    return -1;
-  }
-
-  // Find body in download response
-  body = strstr(response, "\r\n\r\n");
-  if (!body)
-  {
-    printf("    ✗ No file data in download response\n");
-    free(response);
-    return -1;
-  }
-  body += 4;
-
-  size_t downloaded_size = response_len - (body - response);
-
-  // Verify content type in response headers
-  const char *content_type = strstr(response, "Content-Type: ");
-  if (!content_type)
-  {
-    printf("    ✗ No Content-Type header in download\n");
-    free(response);
-    return -1;
-  }
-
-  if (strstr(content_type, expected_format) == NULL)
-  {
-    printf("    ✗ Wrong content type (expected %s)\n", expected_format);
-    free(response);
+  // Extract download URL from JSON body
+  char download_url_path[512];
+  if (!extract_json_field((char*)p_resp->body, "download_url", download_url_path, sizeof(download_url_path))) {
+    printf("    ✗ Failed to extract download_url\n");
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  printf("    ✓ Downloaded converted file (%zu bytes)\n", downloaded_size);
-  printf("    ✓ Content-Type: %s\n", expected_format);
-
-  free(response);
-  return 0;
-}
+  printf("    ✓ Conversion succeeded. Download URL: %s\n", download_url_path);
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
 
-// Helper: Start test server
-pid_t start_test_server(const char *server_binary)
-{
-  pid_t server_pid = fork();
+  printf("    → Testing download: GET %s\n", download_url_path);
+  char full_download_url[1024];
+  snprintf(full_download_url, sizeof(full_download_url), "%s%s", TEST_URL, download_url_path);
+  
+  p_req = Seobeo_Client_Request_Create(full_download_url);
+  p_resp = Seobeo_Client_Request_Execute(p_req);
 
-  if (server_pid < 0)
+  if (!p_resp || p_resp->status_code != 200)
   {
-    perror("fork");
+    printf("    ✗ Download failed\n");
+    if (p_resp) Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  if (server_pid == 0)
-  {
-    printf("Starting server on port %s...\n", TEST_PORT);
-    execl(server_binary, server_binary, NULL);
-    perror("execl failed");
-    exit(1);
-  }
-
-  printf("Server started (PID: %d)\n", server_pid);
+  printf("    ✓ Downloaded converted file (%u bytes)\n", p_resp->body_length);
 
-  usleep(100000);
-  int status;
-  pid_t result = waitpid(server_pid, &status, WNOHANG);
-  if (result != 0)
-  {
-    if (WIFEXITED(status))
-    {
-      fprintf(stderr, "Server exited immediately with code: %d\n", WEXITSTATUS(status));
-    }
-    else if (WIFSIGNALED(status))
-    {
-      fprintf(stderr, "Server was killed by signal: %d\n", WTERMSIG(status));
-    }
-    return -1;
-  }
-
-  sleep(2);
-  printf("Server ready\n\n");
-
-  return server_pid;
-}
-
-// Helper: Stop test server
-void stop_test_server(pid_t server_pid)
-{
-  if (server_pid > 0)
-  {
-    printf("\nStopping server (PID: %d)...\n", server_pid);
-    kill(server_pid, SIGTERM);
-    waitpid(server_pid, NULL, 0);
-    printf("Server stopped\n");
-  }
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
+  return 0;
 }
 
 // Helper: Initialize test case with snapshot file
@@ -694,14 +362,12 @@
 // Main integration test
 int test_server_client_integration(const char *server_binary)
 {
-  printf("=== Server-Client Integration Test ===\n");
+  printf("=== Server-p_req Integration Test ===\n");
   printf("MODE: Verifying Against Snapshots\n\n");
 
   char cwd[1024];
   if (getcwd(cwd, sizeof(cwd)) != NULL)
-  {
     printf("Working directory: %s\n", cwd);
-  }
 
   if (access(server_binary, X_OK) != 0)
   {
@@ -714,24 +380,21 @@
 
   pid_t server_pid = start_test_server(server_binary);
   if (server_pid < 0)
-  {
     return -1;
-  }
 
   int failed_tests = 0;
   int passed_tests = 0;
 
-  // Define test cases - paths that should succeed (200 OK)
   TestCase success_tests[] = {
     {"/", 200, NULL, NULL, NULL, 0},
     {"/resume", 200, NULL, NULL, NULL, 0},
     {"/tools", 200, NULL, NULL, NULL, 0},
     {"/tools/markdown_to_html", 200, NULL, NULL, NULL, 0},
     {"/tools/file_converter", 200, NULL, NULL, NULL, 0},
+    {"/talk", 200, NULL, NULL, NULL, 0},
   };
   int num_success_tests = sizeof(success_tests) / sizeof(success_tests[0]);
 
-  // Define test cases - paths that should redirect (301)
   TestCase redirect_tests[] = {
     {"/index.html", 301, NULL, NULL, NULL, 0},
     {"/resume/index.html", 301, NULL, NULL, NULL, 0},
@@ -741,7 +404,6 @@
   };
   int num_redirect_tests = sizeof(redirect_tests) / sizeof(redirect_tests[0]);
 
-  // Define test cases - paths that should fail (404)
   TestCase failure_tests[] = {
     {"/nonexistent", 404, NULL, NULL, NULL, 0},
     {"/does/not/exist", 404, NULL, NULL, NULL, 0},
@@ -749,134 +411,73 @@
   };
   int num_failure_tests = sizeof(failure_tests) / sizeof(failure_tests[0]);
 
-  // Initialize all test cases
   for (int i = 0; i < num_success_tests; i++)
-  {
     init_test_case(&success_tests[i]);
-  }
+
   for (int i = 0; i < num_redirect_tests; i++)
-  {
     init_test_case(&redirect_tests[i]);
-  }
+
   for (int i = 0; i < num_failure_tests; i++)
-  {
     init_test_case(&failure_tests[i]);
-  }
 
-  // Run success tests
   printf("Running tests for paths that should succeed:\n");
   for (int i = 0; i < num_success_tests; i++)
   {
     if (execute_test_case(&success_tests[i], server_pid) == 0)
-    {
       passed_tests++;
-    }
     else
-    {
       failed_tests++;
-    }
   }
 
   printf("\n");
 
-  // Run redirect tests
   printf("Running tests for paths that should redirect:\n");
   for (int i = 0; i < num_redirect_tests; i++)
   {
     if (execute_test_case(&redirect_tests[i], server_pid) == 0)
-    {
       passed_tests++;
-    }
     else
-    {
       failed_tests++;
-    }
-  }
-
-  printf("\n");
-
-  // Run failure tests
-  printf("Running tests for paths that should fail:\n");
-  for (int i = 0; i < num_failure_tests; i++)
-  {
-    if (execute_test_case(&failure_tests[i], server_pid) == 0)
-    {
-      passed_tests++;
-    }
-    else
-    {
-      failed_tests++;
-    }
   }
 
   printf("\n");
 
-  // Test with custom request
-  printf("Running tests with custom requests:\n");
-  char custom_request[4096];
-  snprintf(custom_request, sizeof(custom_request),
-           "GET / HTTP/1.1\r\n"
-           "Host: %s\r\n"
-           "Connection: close\r\n"
-           "X-Custom-Header: TestValue\r\n"
-           "\r\n",
-           TEST_HOST);
-
-  if (execute_custom_request_test("Custom headers GET /", custom_request, 200, server_pid) == 0)
+  printf("Running tests for paths that should fail:\n");
+  for (int i = 0; i < num_failure_tests; i++)
   {
-    passed_tests++;
-  }
-  else
-  {
-    failed_tests++;
+    if (execute_test_case(&failure_tests[i], server_pid) == 0)
+      passed_tests++;
+    else
+      failed_tests++;
   }
 
   printf("\n");
 
-  // Test POST endpoints
   printf("Running tests for POST conversion endpoints:\n");
-
-  // Test image-to-webp conversion
   if (test_file_conversion("/api/convert/image-to-webp",
                            "mrjunejune/test/shiba.webp",
                            "image/webp",
                            server_pid) == 0)
-  {
     passed_tests++;
-  }
   else
-  {
     failed_tests++;
-  }
 
   printf("\n");
 
-  // Test video-to-mp4 conversion
   if (test_file_conversion("/api/convert/video-to-mp4",
                            "mrjunejune/test/test_avi.avi",
                            "video/mp4",
                            server_pid) == 0)
-  {
     passed_tests++;
-  }
   else
-  {
     failed_tests++;
-  }
 
-  // Cleanup test cases
   for (int i = 0; i < num_success_tests; i++)
-  {
     cleanup_test_case(&success_tests[i]);
-  }
   for (int i = 0; i < num_redirect_tests; i++)
-  {
     cleanup_test_case(&redirect_tests[i]);
-  }
   for (int i = 0; i < num_failure_tests; i++)
-  {
     cleanup_test_case(&failure_tests[i]);
-  }
 
   stop_test_server(server_pid);
 
@@ -893,20 +494,14 @@
 
   const char *server_binary = "./mrjunejune_server";
   if (argc > 1)
-  {
     server_binary = argv[1];
-  }
 
   int result = test_server_client_integration(server_binary);
 
   if (result == 0)
-  {
     printf("\n✓ All tests passed!\n");
-  }
   else
-  {
     printf("\n✗ Some tests failed\n");
-  }
 
   return result;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/test/snapshots/blog.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,208 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg">
+
+<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/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>
+
+<link rel="preload" href="/base.css" as="style" />
+<link rel="stylesheet" href="/base.css" />
+
+
+  </head>
+  <body>
+      <style>
+  :root {
+    --header-background: var(--white);
+    --header-color: rgb(var(--black));
+    --link-hover-accent: var(--awesome);
+  }
+
+  /* Fixed icon in top left corner */
+  #themeToggle {
+    position: fixed;
+    top: 20px;
+    left: 20px;
+    background: var(--header-background);
+    display: flex;
+    align-items: center;
+    border-radius: 50%;
+    cursor: pointer;
+    z-index: 1000;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+    transition: transform 0.2s ease;
+  }
+
+  #themeToggle:hover {
+    transform: scale(1.05);
+  }
+
+  /* Professional header */
+  header {
+    margin: auto;
+    padding: 1.5em 1em;
+    font-family: "More", sans-serif;
+    box-shadow: 0 2px 8px rgba(var(--black), 5%);
+    width: 720px;
+    max-width: calc(100% - 2em);
+    text-align: center;
+  }
+
+  header h1 {
+    margin: 0;
+    font-size: 1.8em;
+    font-weight: 700;
+    letter-spacing: -0.5px;
+  }
+
+  header h1 a {
+    text-decoration: none;
+    color: var(--header-color);
+  }
+
+  header h1 a::before {
+    display: none;
+  }
+
+  /* Mobile responsiveness */
+  @media (max-width: 720px) {
+    #themeToggle {
+      top: 15px;
+      left: 15px;
+    }
+
+    header {
+      padding: 1em;
+    }
+
+    header h1 {
+      font-size: 1.5em;
+    }
+  }
+
+  @media (max-width: 480px) {
+    #themeToggle {
+      top: 10px;
+      left: 10px;
+    }
+
+    #themeToggle img {
+      height: 40px;
+      width: 40px;
+    }
+
+    header h1 {
+      font-size: 1.3em;
+    }
+  }
+
+  #logo {
+    width: 300px;
+  }
+
+  /* 1. DEFINE THE DEFAULTS (Light Mode) */
+  :root {
+    --logo-invert: invert(0);
+    --epi-grayscale: grayscale(0) brightness(1);
+  }
+  
+  /* 2. MANUAL DARK OVERRIDE */
+  html.dark {
+    --logo-invert: invert(1);
+    --epi-grayscale: grayscale(1);
+  }
+  
+  /* 3. MANUAL LIGHT OVERRIDE */
+  html.light-mode {
+    --logo-invert: invert(0);
+    --epi-grayscale: brightness(2.9) grayscale(1);
+  }
+  
+  /* 4. SYSTEM PREFERENCE */
+  @media (prefers-color-scheme: dark) {
+    :root:not(.light-mode) {
+      --logo-invert: invert(1);
+    }
+  }
+  
+  /* 5. APPLY TO ELEMENTS */
+  #logo {
+    -webkit-filter: var(--logo-invert);
+    filter: var(--logo-invert);
+    transition: filter 0.3s ease;
+  }
+  
+  .epi-logo {
+    -webkit-filter: var(--epi-grayscale);
+    filter: var(--epi-grayscale);
+    transition: filter 0.3s ease;
+  }
+</style>
+
+<div id="themeToggle">
+  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
+</div>
+
+<header>
+  <h1><a href="/">MrJuneJune</a></h1>
+</header>
+<script src="/index.js"></script>
+
+
+     <main>
+       <h1 class="title" style="margin-bottom: 20px;"> Blogs </h1>
+       <ul style="list-style: none;">
+         <li style="margin-bottom: 20px;">
+           <span> January 08 2026 </span>
+           <p><h4><a href="/blog/websocket-demystified">Websocket Demystified</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> January 02 2026 </span>
+           <p><h4><a href="/blog/my-seobeo-journey">Creating Network Library in C</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> Apr 12 2025 </span>
+           <p><h4><a href="/blog/wsl2-ssh">WSL2 Cloudtop Setup</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> Dec 10 2024 </span>
+           <p><h4><a href="/blog/multithread-in-js">MultiThreading in JS</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> Nov 23 2024 </span>
+           <p><h4><a href="/blog/thoughts-on-tdd">My thoughts on TDD</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> Nov 21 2024 </span>
+           <p><h4><a href="/blog/optimizing-grass-rendering">Optimizing Random Placement with Colour Noise</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> Nov 17 2024 </span>
+           <p><h4><a href="/blog/thoughts-on-ide">My thoughts on IDE</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> Nov 16 2024 </span>
+           <p><h4><a href="/blog/optimizing-data-structures">Optimizing Data Structure for Performance</a></h4></p>
+         </li>
+         <li style="margin-bottom: 20px;">
+           <span> Nov 13 2024 </span>
+           <p><h4><a href="/blog/wasm-bunny">WASM using c3</a></h4></p>
+         </li>
+       </ul>
+     </main>
+     <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
+  <small>&copy; 2026 June Park</small>
+</div>
+
+  </body>
+</html>
--- a/mrjunejune/test/snapshots/index.html.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/index.html.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,7 +0,0 @@
-HTTP/1.1 301 Moved Permanently
-Content-Type: text/plain
-Content-Length: 0
-Connection: close
-Body: 
-Location: /
-
--- a/mrjunejune/test/snapshots/resume.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/resume.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,8 +1,3 @@
-HTTP/1.1 200 OK
-Content-Type: text/html
-Content-Length: 14484
-Connection: close
-
 <!doctype html>
 <html lang="en">
   <head>
@@ -13,10 +8,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>
 
--- a/mrjunejune/test/snapshots/resume_index.html.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/resume_index.html.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,7 +0,0 @@
-HTTP/1.1 301 Moved Permanently
-Content-Type: text/plain
-Content-Length: 0
-Connection: close
-Body: 
-Location: /resume
-
--- a/mrjunejune/test/snapshots/root.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/root.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,8 +1,3 @@
-HTTP/1.1 200 OK
-Content-Type: text/html
-Content-Length: 5723
-Connection: close
-
 <!doctype html>
 <html lang="en">
   <head>
@@ -14,10 +9,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/test/snapshots/talk.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,223 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Talk!</title>
+  <meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="icon" type="image/svg+xml" href="/public/epi_all_colors.svg">
+
+<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/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>
+
+<link rel="preload" href="/base.css" as="style" />
+<link rel="stylesheet" href="/base.css" />
+
+
+  <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>
+  <style>
+  :root {
+    --header-background: var(--white);
+    --header-color: rgb(var(--black));
+    --link-hover-accent: var(--awesome);
+  }
+
+  /* Fixed icon in top left corner */
+  #themeToggle {
+    position: fixed;
+    top: 20px;
+    left: 20px;
+    background: var(--header-background);
+    display: flex;
+    align-items: center;
+    border-radius: 50%;
+    cursor: pointer;
+    z-index: 1000;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+    transition: transform 0.2s ease;
+  }
+
+  #themeToggle:hover {
+    transform: scale(1.05);
+  }
+
+  /* Professional header */
+  header {
+    margin: auto;
+    padding: 1.5em 1em;
+    font-family: "More", sans-serif;
+    box-shadow: 0 2px 8px rgba(var(--black), 5%);
+    width: 720px;
+    max-width: calc(100% - 2em);
+    text-align: center;
+  }
+
+  header h1 {
+    margin: 0;
+    font-size: 1.8em;
+    font-weight: 700;
+    letter-spacing: -0.5px;
+  }
+
+  header h1 a {
+    text-decoration: none;
+    color: var(--header-color);
+  }
+
+  header h1 a::before {
+    display: none;
+  }
+
+  /* Mobile responsiveness */
+  @media (max-width: 720px) {
+    #themeToggle {
+      top: 15px;
+      left: 15px;
+    }
+
+    header {
+      padding: 1em;
+    }
+
+    header h1 {
+      font-size: 1.5em;
+    }
+  }
+
+  @media (max-width: 480px) {
+    #themeToggle {
+      top: 10px;
+      left: 10px;
+    }
+
+    #themeToggle img {
+      height: 40px;
+      width: 40px;
+    }
+
+    header h1 {
+      font-size: 1.3em;
+    }
+  }
+
+  #logo {
+    width: 300px;
+  }
+
+  /* 1. DEFINE THE DEFAULTS (Light Mode) */
+  :root {
+    --logo-invert: invert(0);
+    --epi-grayscale: grayscale(0) brightness(1);
+  }
+  
+  /* 2. MANUAL DARK OVERRIDE */
+  html.dark {
+    --logo-invert: invert(1);
+    --epi-grayscale: grayscale(1);
+  }
+  
+  /* 3. MANUAL LIGHT OVERRIDE */
+  html.light-mode {
+    --logo-invert: invert(0);
+    --epi-grayscale: brightness(2.9) grayscale(1);
+  }
+  
+  /* 4. SYSTEM PREFERENCE */
+  @media (prefers-color-scheme: dark) {
+    :root:not(.light-mode) {
+      --logo-invert: invert(1);
+    }
+  }
+  
+  /* 5. APPLY TO ELEMENTS */
+  #logo {
+    -webkit-filter: var(--logo-invert);
+    filter: var(--logo-invert);
+    transition: filter 0.3s ease;
+  }
+  
+  .epi-logo {
+    -webkit-filter: var(--epi-grayscale);
+    filter: var(--epi-grayscale);
+    transition: filter 0.3s ease;
+  }
+</style>
+
+<div id="themeToggle">
+  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
+</div>
+
+<header>
+  <h1><a href="/">MrJuneJune</a></h1>
+</header>
+<script src="/index.js"></script>
+
+
+  <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>
+  <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
+  <small>&copy; 2026 June Park</small>
+</div>
+
+  <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/mrjunejune/test/snapshots/tools.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/tools.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,8 +1,3 @@
-HTTP/1.1 200 OK
-Content-Type: text/html
-Content-Length: 4246
-Connection: close
-
 <!doctype html>
 <html lang="en">
   <head>
@@ -13,10 +8,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>
 
--- a/mrjunejune/test/snapshots/tools_file_converter.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_file_converter.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,8 +1,3 @@
-HTTP/1.1 200 OK
-Content-Type: text/html
-Content-Length: 8526
-Connection: close
-
 <!DOCTYPE html>
 <html lang="en">
 <head>
@@ -16,10 +11,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>
 
--- a/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_file_converter_index.html.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,447 +0,0 @@
-HTTP/1.1 301 Moved Permanently
-Content-Type: text/plain
-Content-Length: 0
-Connection: close
-Body: 
-
-"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.regular.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
-
-<link rel="preload" href="/base.css" as="style" />
-<link rel="stylesheet" href="/base.css" />
-
-
-    <link rel="stylesheet" href="markdown_to_html/index.css" />
-</head>
-<body>
-    <style>
-  :root {
-    --header-background: var(--white);
-    --header-color: rgb(var(--black));
-    --link-hover-accent: var(--awesome);
-  }
-
-  /* Fixed icon in top left corner */
-  #themeToggle {
-    position: fixed;
-    top: 20px;
-    left: 20px;
-    background: var(--header-background);
-    display: flex;
-    align-items: center;
-    border-radius: 50%;
-    cursor: pointer;
-    z-index: 1000;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    transition: transform 0.2s ease;
-  }
-
-  #themeToggle:hover {
-    transform: scale(1.05);
-  }
-
-  /* Professional header */
-  header {
-    margin: auto;
-    padding: 1.5em 1em;
-    font-family: "More", sans-serif;
-    box-shadow: 0 2px 8px rgba(var(--black), 5%);
-    width: 720px;
-    max-width: calc(100% - 2em);
-    text-align: center;
-  }
-
-  header h1 {
-    margin: 0;
-    font-size: 1.8em;
-    font-weight: 700;
-    letter-spacing: -0.5px;
-  }
-
-  header h1 a {
-    text-decoration: none;
-    color: var(--header-color);
-  }
-
-  header h1 a::before {
-    display: none;
-  }
-
-  /* Mobile responsiveness */
-  @media (max-width: 720px) {
-    #themeToggle {
-      top: 15px;
-      left: 15px;
-    }
-
-    header {
-      padding: 1em;
-    }
-
-    header h1 {
-      font-size: 1.5em;
-    }
-  }
-
-  @media (max-width: 480px) {
-    #themeToggle {
-      top: 10px;
-      left: 10px;
-    }
-
-    #themeToggle img {
-      height: 40px;
-      width: 40px;
-    }
-
-    header h1 {
-      font-size: 1.3em;
-    }
-  }
-
-  #logo {
-    width: 300px;
-  }
-
-  /* 1. DEFINE THE DEFAULTS (Light Mode) */
-  :root {
-    --logo-invert: invert(0);
-    --epi-grayscale: grayscale(0) brightness(1);
-  }
-  
-  /* 2. MANUAL DARK OVERRIDE */
-  html.dark {
-    --logo-invert: invert(1);
-    --epi-grayscale: grayscale(1);
-  }
-  
-  /* 3. MANUAL LIGHT OVERRIDE */
-  html.light-mode {
-    --logo-invert: invert(0);
-    --epi-grayscale: brightness(2.9) grayscale(1);
-  }
-  
-  /* 4. SYSTEM PREFERENCE */
-  @media (prefers-color-scheme: dark) {
-    :root:not(.light-mode) {
-      --logo-invert: invert(1);
-    }
-  }
-  
-  /* 5. APPLY TO ELEMENTS */
-  #logo {
-    -webkit-filter: var(--logo-invert);
-    filter: var(--logo-invert);
-    transition: filter 0.3s ease;
-  }
-  
-  .epi-logo {
-    -webkit-filter: var(--epi-grayscale);
-    filter: var(--epi-grayscale);
-    transition: filter 0.3s ease;
-  }
-</style>
-
-<div id="themeToggle">
-  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
-</div>
-
-<header>
-  <h1><a href="/">MrJuneJune</a></h1>
-</header>
-<script src="/index.js"></script>
-
-
-    <div class="header">
-        <h1>Markdown to HTML Converter</h1>
-    </div>
-    
-    <div class="container">
-        <div class="panel">
-            <div class="label">Markdown Input</div>
-            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
-
-## Features
-
-This converter supports:
-
-- **Bold text** and *italic text*
-- [Links](https://example.com)
-- `inline code`
-- ~~strikethrough~~
-
-### Lists
-
-1. Ordered lists
-2. Like this one
-3. With numbers
-
-- Unordered lists
-- Use dashes
-- Or asterisks
-
-### Code Blocks
-
-```
-function example() {
-    return "Hello World";
-}
-```
-
-### Blockquotes
-
-> This is a blockquote
-> It can span multiple lines
-
----
-
-### Images
-
-![Alt text](https://via.placeholder.com/150)
-
-**Try editing this text!**</textarea>
-        </div>
-        
-        <div class="panel">
-            <div class="title">
-              <div class="label">HTML Output</div>
-              <button id="copy"> Copy </button>
-            </div>
-            <div id="output"></div>
-        </div>
-    </div>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script src="/markdown_to_html.js"></script>
-    <script>
-        function convert() {
-          output.innerHTML = '';
-          const markdown = input.value;
-          renderMarkdown(output, markdown);
-        }
-        input.addEventListener('input', convert);
-        
-        convert();
-
-        copy.addEventListener('click', () => {
-          const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'});
-          const textBlob = new Blob([output.innerText], { type: 'text/plain'});
-          const data = [new ClipboardItem({
-            'text/html': htmlBlob,
-            'text/plain': textBlob
-          })];
-          navigator.clipboard.write(data).then(() => {
-            copy.textContent = "Copied!";
-            setTimeout(() => {
-              copy.textContent = "Copy";
-              copy.classList.remove('success');
-            }, 1000);
-          }).catch(err => {
-            console.error('Failed to copy: ', err);
-          });
-        });
-    </script>
-</body>
-</htmlLocation: /tools
-
-
-          <span class="entry-title-style">Tools & Platforms:</span>
-          <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Web Frameworks: </span>
-          <span class="skill-type-style"> Django, Rails, React, Flask</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> DevOp:</span>
-          <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Language:</span>
-          <span class="skill-type-style"> English, Korean, Japanese </span>
-        </p>
-      </div>
-
-      <!-- Experiences -->
-     <div class="sub-header">
-        <h2 class="section-style"> Experience </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.meta.com/">Meta</a>
-        </p>
-        <p class="entry-location-style">San Francisco, CA, USA</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Oct, 2024 - Present</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI.
-        </li>
-        <li>
-          Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems.
-        </li>
-        <li>
-          Partnered with data science to design A/B tests and analyze revenue impact of ads destination.
-        </li>
-        <li>
-          Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity.
-        </li>
-      </ul>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.wmg.com/">Warner Music Group</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p>
-        <p class="entry-date-style">July, 2023 - Sept, 2024</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and
-        </li>
-        <li>
-          TypeScript testing and code structures.
-        </li>
-        <li>
-          Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values.
-        </li>
-        <li>
-          Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes.
-        </li>
-        <li>
-          Developed CI/CD pipelines for backend structures.
-        </li>
-        <li>
-          Designed infrastructure for pub/sub, caching, and media processing logic.
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.google.com/">Google</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Feb, 2022 - July 2023</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li>
-        <li>
-          Improved a response time and render time of App Script hover card components.</li>
-        <li>
-          Collaborated with a team of developers to ensure timely and accurate delivery of features.</li>
-        <li>
-          Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.everlywell.com/">Everlywell</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">December, 2020 - Jan, 2022</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li>
-        <li>
-          Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li>
-        <li>
-          Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li>
-        <li>
-          Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.spiria.com/">Spiria</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">October, 2018 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger.
-        </li>
-        <li>
-          Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs.
-        </li>
-        <li>
-          Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers
-        </li>
-        <li>
-          Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.apexscore.ai/">Apex Score</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">September, 2019 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy.
-        </li>
-        <li>
-          Created custom image uploader to Amazon s3 bucket using boto3 library.
-        </li>
-        <li>
-          Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base.
-        </li>
-        <li>
-          Created an interactive graph using D3.js in Vue.js with data from Flask backend API. 
-        </li>
-      </ul>
-
-     <div class="sub-header">
-        <h2 class="section-style"> Education </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          University of British Columbia
-        </p>
-        <p class="entry-location-style">Kelowna, British Columbia</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p>
-        <p class="entry-date-style">2014 - 2018</p>
-      </div>
-      <div id="footer"></div>
-    </main>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script href="index.js"></script>
-  </body>
-</htmlLocation: /tools/markdown_to_html
-Location: /tools/file_converter
-
--- a/mrjunejune/test/snapshots/tools_index.html.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_index.html.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,445 +0,0 @@
-HTTP/1.1 301 Moved Permanently
-Content-Type: text/plain
-Content-Length: 0
-Connection: close
-Body: 
-
-"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.regular.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
-
-<link rel="preload" href="/base.css" as="style" />
-<link rel="stylesheet" href="/base.css" />
-
-
-    <link rel="stylesheet" href="markdown_to_html/index.css" />
-</head>
-<body>
-    <style>
-  :root {
-    --header-background: var(--white);
-    --header-color: rgb(var(--black));
-    --link-hover-accent: var(--awesome);
-  }
-
-  /* Fixed icon in top left corner */
-  #themeToggle {
-    position: fixed;
-    top: 20px;
-    left: 20px;
-    background: var(--header-background);
-    display: flex;
-    align-items: center;
-    border-radius: 50%;
-    cursor: pointer;
-    z-index: 1000;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    transition: transform 0.2s ease;
-  }
-
-  #themeToggle:hover {
-    transform: scale(1.05);
-  }
-
-  /* Professional header */
-  header {
-    margin: auto;
-    padding: 1.5em 1em;
-    font-family: "More", sans-serif;
-    box-shadow: 0 2px 8px rgba(var(--black), 5%);
-    width: 720px;
-    max-width: calc(100% - 2em);
-    text-align: center;
-  }
-
-  header h1 {
-    margin: 0;
-    font-size: 1.8em;
-    font-weight: 700;
-    letter-spacing: -0.5px;
-  }
-
-  header h1 a {
-    text-decoration: none;
-    color: var(--header-color);
-  }
-
-  header h1 a::before {
-    display: none;
-  }
-
-  /* Mobile responsiveness */
-  @media (max-width: 720px) {
-    #themeToggle {
-      top: 15px;
-      left: 15px;
-    }
-
-    header {
-      padding: 1em;
-    }
-
-    header h1 {
-      font-size: 1.5em;
-    }
-  }
-
-  @media (max-width: 480px) {
-    #themeToggle {
-      top: 10px;
-      left: 10px;
-    }
-
-    #themeToggle img {
-      height: 40px;
-      width: 40px;
-    }
-
-    header h1 {
-      font-size: 1.3em;
-    }
-  }
-
-  #logo {
-    width: 300px;
-  }
-
-  /* 1. DEFINE THE DEFAULTS (Light Mode) */
-  :root {
-    --logo-invert: invert(0);
-    --epi-grayscale: grayscale(0) brightness(1);
-  }
-  
-  /* 2. MANUAL DARK OVERRIDE */
-  html.dark {
-    --logo-invert: invert(1);
-    --epi-grayscale: grayscale(1);
-  }
-  
-  /* 3. MANUAL LIGHT OVERRIDE */
-  html.light-mode {
-    --logo-invert: invert(0);
-    --epi-grayscale: brightness(2.9) grayscale(1);
-  }
-  
-  /* 4. SYSTEM PREFERENCE */
-  @media (prefers-color-scheme: dark) {
-    :root:not(.light-mode) {
-      --logo-invert: invert(1);
-    }
-  }
-  
-  /* 5. APPLY TO ELEMENTS */
-  #logo {
-    -webkit-filter: var(--logo-invert);
-    filter: var(--logo-invert);
-    transition: filter 0.3s ease;
-  }
-  
-  .epi-logo {
-    -webkit-filter: var(--epi-grayscale);
-    filter: var(--epi-grayscale);
-    transition: filter 0.3s ease;
-  }
-</style>
-
-<div id="themeToggle">
-  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
-</div>
-
-<header>
-  <h1><a href="/">MrJuneJune</a></h1>
-</header>
-<script src="/index.js"></script>
-
-
-    <div class="header">
-        <h1>Markdown to HTML Converter</h1>
-    </div>
-    
-    <div class="container">
-        <div class="panel">
-            <div class="label">Markdown Input</div>
-            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
-
-## Features
-
-This converter supports:
-
-- **Bold text** and *italic text*
-- [Links](https://example.com)
-- `inline code`
-- ~~strikethrough~~
-
-### Lists
-
-1. Ordered lists
-2. Like this one
-3. With numbers
-
-- Unordered lists
-- Use dashes
-- Or asterisks
-
-### Code Blocks
-
-```
-function example() {
-    return "Hello World";
-}
-```
-
-### Blockquotes
-
-> This is a blockquote
-> It can span multiple lines
-
----
-
-### Images
-
-![Alt text](https://via.placeholder.com/150)
-
-**Try editing this text!**</textarea>
-        </div>
-        
-        <div class="panel">
-            <div class="title">
-              <div class="label">HTML Output</div>
-              <button id="copy"> Copy </button>
-            </div>
-            <div id="output"></div>
-        </div>
-    </div>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script src="/markdown_to_html.js"></script>
-    <script>
-        function convert() {
-          output.innerHTML = '';
-          const markdown = input.value;
-          renderMarkdown(output, markdown);
-        }
-        input.addEventListener('input', convert);
-        
-        convert();
-
-        copy.addEventListener('click', () => {
-          const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'});
-          const textBlob = new Blob([output.innerText], { type: 'text/plain'});
-          const data = [new ClipboardItem({
-            'text/html': htmlBlob,
-            'text/plain': textBlob
-          })];
-          navigator.clipboard.write(data).then(() => {
-            copy.textContent = "Copied!";
-            setTimeout(() => {
-              copy.textContent = "Copy";
-              copy.classList.remove('success');
-            }, 1000);
-          }).catch(err => {
-            console.error('Failed to copy: ', err);
-          });
-        });
-    </script>
-</body>
-</htmlLocation: /tools
-
-
-          <span class="entry-title-style">Tools & Platforms:</span>
-          <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Web Frameworks: </span>
-          <span class="skill-type-style"> Django, Rails, React, Flask</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> DevOp:</span>
-          <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Language:</span>
-          <span class="skill-type-style"> English, Korean, Japanese </span>
-        </p>
-      </div>
-
-      <!-- Experiences -->
-     <div class="sub-header">
-        <h2 class="section-style"> Experience </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.meta.com/">Meta</a>
-        </p>
-        <p class="entry-location-style">San Francisco, CA, USA</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Oct, 2024 - Present</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI.
-        </li>
-        <li>
-          Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems.
-        </li>
-        <li>
-          Partnered with data science to design A/B tests and analyze revenue impact of ads destination.
-        </li>
-        <li>
-          Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity.
-        </li>
-      </ul>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.wmg.com/">Warner Music Group</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p>
-        <p class="entry-date-style">July, 2023 - Sept, 2024</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and
-        </li>
-        <li>
-          TypeScript testing and code structures.
-        </li>
-        <li>
-          Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values.
-        </li>
-        <li>
-          Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes.
-        </li>
-        <li>
-          Developed CI/CD pipelines for backend structures.
-        </li>
-        <li>
-          Designed infrastructure for pub/sub, caching, and media processing logic.
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.google.com/">Google</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Feb, 2022 - July 2023</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li>
-        <li>
-          Improved a response time and render time of App Script hover card components.</li>
-        <li>
-          Collaborated with a team of developers to ensure timely and accurate delivery of features.</li>
-        <li>
-          Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.everlywell.com/">Everlywell</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">December, 2020 - Jan, 2022</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li>
-        <li>
-          Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li>
-        <li>
-          Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li>
-        <li>
-          Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.spiria.com/">Spiria</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">October, 2018 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger.
-        </li>
-        <li>
-          Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs.
-        </li>
-        <li>
-          Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers
-        </li>
-        <li>
-          Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.apexscore.ai/">Apex Score</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">September, 2019 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy.
-        </li>
-        <li>
-          Created custom image uploader to Amazon s3 bucket using boto3 library.
-        </li>
-        <li>
-          Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base.
-        </li>
-        <li>
-          Created an interactive graph using D3.js in Vue.js with data from Flask backend API. 
-        </li>
-      </ul>
-
-     <div class="sub-header">
-        <h2 class="section-style"> Education </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          University of British Columbia
-        </p>
-        <p class="entry-location-style">Kelowna, British Columbia</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p>
-        <p class="entry-date-style">2014 - 2018</p>
-      </div>
-      <div id="footer"></div>
-    </main>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script href="index.js"></script>
-  </body>
-</html>
--- a/mrjunejune/test/snapshots/tools_markdown_to_html.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_markdown_to_html.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,8 +1,3 @@
-HTTP/1.1 200 OK
-Content-Type: text/html
-Content-Length: 5985
-Connection: close
-
 <!DOCTYPE html>
 <html lang="en">
 <head>
@@ -16,10 +11,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>
 
--- a/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot	Fri Jan 09 07:19:09 2026 -0800
+++ b/mrjunejune/test/snapshots/tools_markdown_to_html_index.html.snapshot	Fri Jan 09 07:42:04 2026 -0800
@@ -1,446 +0,0 @@
-HTTP/1.1 301 Moved Permanently
-Content-Type: text/plain
-Content-Length: 0
-Connection: close
-Body: 
-
-"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.regular.otf" as="font" type="font/otf" crossorigin>
-<link rel="preload" href="/public/fonts/more-sugar.thin.otf" as="font" type="font/otf" crossorigin>
-
-<link rel="preload" href="/base.css" as="style" />
-<link rel="stylesheet" href="/base.css" />
-
-
-    <link rel="stylesheet" href="markdown_to_html/index.css" />
-</head>
-<body>
-    <style>
-  :root {
-    --header-background: var(--white);
-    --header-color: rgb(var(--black));
-    --link-hover-accent: var(--awesome);
-  }
-
-  /* Fixed icon in top left corner */
-  #themeToggle {
-    position: fixed;
-    top: 20px;
-    left: 20px;
-    background: var(--header-background);
-    display: flex;
-    align-items: center;
-    border-radius: 50%;
-    cursor: pointer;
-    z-index: 1000;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    transition: transform 0.2s ease;
-  }
-
-  #themeToggle:hover {
-    transform: scale(1.05);
-  }
-
-  /* Professional header */
-  header {
-    margin: auto;
-    padding: 1.5em 1em;
-    font-family: "More", sans-serif;
-    box-shadow: 0 2px 8px rgba(var(--black), 5%);
-    width: 720px;
-    max-width: calc(100% - 2em);
-    text-align: center;
-  }
-
-  header h1 {
-    margin: 0;
-    font-size: 1.8em;
-    font-weight: 700;
-    letter-spacing: -0.5px;
-  }
-
-  header h1 a {
-    text-decoration: none;
-    color: var(--header-color);
-  }
-
-  header h1 a::before {
-    display: none;
-  }
-
-  /* Mobile responsiveness */
-  @media (max-width: 720px) {
-    #themeToggle {
-      top: 15px;
-      left: 15px;
-    }
-
-    header {
-      padding: 1em;
-    }
-
-    header h1 {
-      font-size: 1.5em;
-    }
-  }
-
-  @media (max-width: 480px) {
-    #themeToggle {
-      top: 10px;
-      left: 10px;
-    }
-
-    #themeToggle img {
-      height: 40px;
-      width: 40px;
-    }
-
-    header h1 {
-      font-size: 1.3em;
-    }
-  }
-
-  #logo {
-    width: 300px;
-  }
-
-  /* 1. DEFINE THE DEFAULTS (Light Mode) */
-  :root {
-    --logo-invert: invert(0);
-    --epi-grayscale: grayscale(0) brightness(1);
-  }
-  
-  /* 2. MANUAL DARK OVERRIDE */
-  html.dark {
-    --logo-invert: invert(1);
-    --epi-grayscale: grayscale(1);
-  }
-  
-  /* 3. MANUAL LIGHT OVERRIDE */
-  html.light-mode {
-    --logo-invert: invert(0);
-    --epi-grayscale: brightness(2.9) grayscale(1);
-  }
-  
-  /* 4. SYSTEM PREFERENCE */
-  @media (prefers-color-scheme: dark) {
-    :root:not(.light-mode) {
-      --logo-invert: invert(1);
-    }
-  }
-  
-  /* 5. APPLY TO ELEMENTS */
-  #logo {
-    -webkit-filter: var(--logo-invert);
-    filter: var(--logo-invert);
-    transition: filter 0.3s ease;
-  }
-  
-  .epi-logo {
-    -webkit-filter: var(--epi-grayscale);
-    filter: var(--epi-grayscale);
-    transition: filter 0.3s ease;
-  }
-</style>
-
-<div id="themeToggle">
-  <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_all_colors.svg" height="50" width="50">
-</div>
-
-<header>
-  <h1><a href="/">MrJuneJune</a></h1>
-</header>
-<script src="/index.js"></script>
-
-
-    <div class="header">
-        <h1>Markdown to HTML Converter</h1>
-    </div>
-    
-    <div class="container">
-        <div class="panel">
-            <div class="label">Markdown Input</div>
-            <textarea id="input" placeholder="Type your markdown here..."># Welcome to Markdown Converter
-
-## Features
-
-This converter supports:
-
-- **Bold text** and *italic text*
-- [Links](https://example.com)
-- `inline code`
-- ~~strikethrough~~
-
-### Lists
-
-1. Ordered lists
-2. Like this one
-3. With numbers
-
-- Unordered lists
-- Use dashes
-- Or asterisks
-
-### Code Blocks
-
-```
-function example() {
-    return "Hello World";
-}
-```
-
-### Blockquotes
-
-> This is a blockquote
-> It can span multiple lines
-
----
-
-### Images
-
-![Alt text](https://via.placeholder.com/150)
-
-**Try editing this text!**</textarea>
-        </div>
-        
-        <div class="panel">
-            <div class="title">
-              <div class="label">HTML Output</div>
-              <button id="copy"> Copy </button>
-            </div>
-            <div id="output"></div>
-        </div>
-    </div>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script src="/markdown_to_html.js"></script>
-    <script>
-        function convert() {
-          output.innerHTML = '';
-          const markdown = input.value;
-          renderMarkdown(output, markdown);
-        }
-        input.addEventListener('input', convert);
-        
-        convert();
-
-        copy.addEventListener('click', () => {
-          const htmlBlob = new Blob([output.innerHTML], { type: 'text/html'});
-          const textBlob = new Blob([output.innerText], { type: 'text/plain'});
-          const data = [new ClipboardItem({
-            'text/html': htmlBlob,
-            'text/plain': textBlob
-          })];
-          navigator.clipboard.write(data).then(() => {
-            copy.textContent = "Copied!";
-            setTimeout(() => {
-              copy.textContent = "Copy";
-              copy.classList.remove('success');
-            }, 1000);
-          }).catch(err => {
-            console.error('Failed to copy: ', err);
-          });
-        });
-    </script>
-</body>
-</htmlLocation: /tools
-
-
-          <span class="entry-title-style">Tools & Platforms:</span>
-          <span class="skill-type-style">Bazel, PostgresSQL, Mercurial, Git, Pands, Raylib, XCode</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Web Frameworks: </span>
-          <span class="skill-type-style"> Django, Rails, React, Flask</span>
-        </p>
-        <p>
-          <span class="entry-title-style"> DevOp:</span>
-          <span class="skill-type-style"> Plummi, Heroku, DigitalOcean, AWS, Google Cloud </span>
-        </p>
-        <p>
-          <span class="entry-title-style"> Language:</span>
-          <span class="skill-type-style"> English, Korean, Japanese </span>
-        </p>
-      </div>
-
-      <!-- Experiences -->
-     <div class="sub-header">
-        <h2 class="section-style"> Experience </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.meta.com/">Meta</a>
-        </p>
-        <p class="entry-location-style">San Francisco, CA, USA</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Oct, 2024 - Present</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Took initiative on Channel Value Rule, targeting the 16% of ad traffic with both app and web destinations to improve value attribution and ROI.
-        </li>
-        <li>
-          Built full-stack features using React and Hack/GraphQL, contributing to scalable, production-ready systems.
-        </li>
-        <li>
-          Partnered with data science to design A/B tests and analyze revenue impact of ads destination.
-        </li>
-        <li>
-          Proposed and implemented alpha improvements to internal testing infrastructure, reducing test time by 50% and enhancing developer velocity.
-        </li>
-      </ul>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.wmg.com/">Warner Music Group</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">TECHNICAL LEAD ENGINEER</p>
-        <p class="entry-date-style">July, 2023 - Sept, 2024</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements <a href="https://bazel.build/">bazel </a>structure for the company for TypeScript and JavaScript code base for hermiticity and stablishing standards for JavaScript and
-        </li>
-        <li>
-          TypeScript testing and code structures.
-        </li>
-        <li>
-          Led a team of five engineers in building GraphQL endpoints for client-facing applications using Apollo and AppSync, supporting over 2000 RPS and auto scaling depending on request values.
-        </li>
-        <li>
-          Improved application response times by up to 85% for graphQL response by updating database schema and SQL queries, eliminating N+1 queries and lack of indexes.
-        </li>
-        <li>
-          Developed CI/CD pipelines for backend structures.
-        </li>
-        <li>
-          Designed infrastructure for pub/sub, caching, and media processing logic.
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.google.com/">Google</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">Feb, 2022 - July 2023</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Implements and maintained new features relating to App Script across google workspace platform including Gmail, sheets, and Docs.</li>
-        <li>
-          Improved a response time and render time of App Script hover card components.</li>
-        <li>
-          Collaborated with a team of developers to ensure timely and accurate delivery of features.</li>
-        <li>
-          Conducted user testing and gathered feedback to iterate on features for optimal user experience.</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.everlywell.com/">Everlywell</a>
-        </p>
-        <p class="entry-location-style">Toronto, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">December, 2020 - Jan, 2022</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Maintained Amazon amplify apps to create and deploy React web applications for companies such as <a href="https://brooklynnets.everlywell.com/">NBA</a>, <a href="https://tinder.everlywell.com/">Tinder</a>, and other companies for COVID-19 at-home test kits.</li>
-        <li>
-          Implemented a script that helps accurately access and refund unused covid test kits; helping company save up to 200,000 USD.</li>
-        <li>
-          Created several Rails controllers for internal purposes; mocking end to end user experience for QA, mass refund features for CX department, and more, ultimately reducing support tickets amount by 50 percent.</li>
-        <li>
-          Implemented an audit table to help debug problems and logged which process was responsible for the change of the record using PaperTrail gems</li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.spiria.com/">Spiria</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">October, 2018 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Constructed RESTful API endpoints in multiple different frameworks such as Django, Ruby on Rails, and Flask and automated API documentation process using swagger.
-        </li>
-        <li>
-          Designed custom rake tasks for importing production data into newly updated data structure to meet client's needs.
-        </li>
-        <li>
-          Maintained or updated staging/productions servers. Debugged problems in production postgres database using ssh and postgres console on Heroku or AWS servers
-        </li>
-        <li>
-          Collaborated in creating automation python scripts for websites and application using selenium covering for QA eliminating 80% of QA's manual work
-        </li>
-      </ul>
-
-      <div class="flex-box">
-        <p class="entry-title-style">
-          <a href="https://www.apexscore.ai/">Apex Score</a>
-        </p>
-        <p class="entry-location-style">Oakville, ON, Canada</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">SOFTWARE ENGINEER</p>
-        <p class="entry-date-style">September, 2019 - October, 2020</p>
-      </div>
-      <ul class="description-style">
-        <li>
-          Developed custom Shapley value regression model to calculate importance of independent variables of data sets using sklearn, pandas, and numpy.
-        </li>
-        <li>
-          Created custom image uploader to Amazon s3 bucket using boto3 library.
-        </li>
-        <li>
-          Built RESTful API application using Flask framework and automated extensive API documentation pages using flask-restplus, pytest, and swagger, covering 95% of the code base.
-        </li>
-        <li>
-          Created an interactive graph using D3.js in Vue.js with data from Flask backend API. 
-        </li>
-      </ul>
-
-     <div class="sub-header">
-        <h2 class="section-style"> Education </h2>
-        <div class="line"></div>
-      </div>
-      <div class="flex-box">
-        <p class="entry-title-style">
-          University of British Columbia
-        </p>
-        <p class="entry-location-style">Kelowna, British Columbia</p>
-      </div>
-      <div class="flex-box">
-        <p class="entry-position-style">BACHELOR OF SCIENCE IN PHYSICS</p>
-        <p class="entry-date-style">2014 - 2018</p>
-      </div>
-      <div id="footer"></div>
-    </main>
-    <div style="display: flex; align-items: center; justify-content: center; margin: 30px 0px;">
-  <small>&copy; 2026 June Park</small>
-</div>
-
-    <script href="index.js"></script>
-  </body>
-</htmlLocation: /tools/markdown_to_html
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/test/test.h	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,74 @@
+#include "seobeo/seobeo.h"
+#include "seobeo/snapshot_creator.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#define TEST_PORT "6969"
+#define TEST_HOST "127.0.0.1"
+#define TEST_URL "http://127.0.0.1:6969"
+#define MAX_RESPONSE_SIZE (1024 * 1024)
+#ifndef SNAPSHOT_DIR
+  #define SNAPSHOT_DIR "mrjunejune/test/snapshots"
+#endif
+
+pid_t start_test_server(const char *server_binary);
+void stop_test_server(pid_t server_pid);
+
+
+pid_t start_test_server(const char *server_binary)
+{
+  pid_t server_pid = fork();
+
+  if (server_pid < 0)
+  {
+    perror("fork");
+    return -1;
+  }
+
+  if (server_pid == 0)
+  {
+    printf("Starting server on port %s...\n", TEST_PORT);
+    execl(server_binary, server_binary, NULL);
+    perror("execl failed");
+    exit(1);
+  }
+
+  printf("Server started (PID: %d)\n", server_pid);
+
+  usleep(100000);
+  int status;
+  pid_t result = waitpid(server_pid, &status, WNOHANG);
+  if (result != 0)
+  {
+    if (WIFEXITED(status))
+    {
+      fprintf(stderr, "Server exited immediately with code: %d\n", WEXITSTATUS(status));
+    }
+    else if (WIFSIGNALED(status))
+    {
+      fprintf(stderr, "Server was killed by signal: %d\n", WTERMSIG(status));
+    }
+    return -1;
+  }
+
+  sleep(2);
+  printf("Server ready\n\n");
+
+  return server_pid;
+}
+
+void stop_test_server(pid_t server_pid)
+{
+  if (server_pid > 0)
+  {
+    printf("\nStopping server (PID: %d)...\n", server_pid);
+    kill(server_pid, SIGTERM);
+    waitpid(server_pid, NULL, 0);
+    printf("Server stopped\n");
+  }
+}
+
+
--- a/playground/.gitignore	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-# dependencies (bun install)
-node_modules
-
-# output
-out
-dist
-*.tgz
-
-# code coverage
-coverage
-*.lcov
-
-# logs
-logs
-_.log
-report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
-
-# dotenv environment variable files
-.env
-.env.development.local
-.env.test.local
-.env.production.local
-.env.local
-
-# caches
-.eslintcache
-.cache
-*.tsbuildinfo
-
-# IntelliJ based IDEs
-.idea
-
-# Finder (MacOS) folder config
-.DS_Store
--- a/playground/BUILD	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
-load("//gui_ze:gui_ze.bzl", "bun_build")
-
-cc_binary(
-  name = "main",
-  srcs = ["main.c"],
-)
-
-filegroup(
-  name = "all_ts_files",
-  srcs = glob([
-      "**/*.ts",
-      "**/*.tsx",
-      "**/*.js",
-      "**/*.jsx",
-  ], allow_empty=True)
-)
-
-bun_build(
-  name = "hello",
-  src = "main.ts",
-  src_folder = "playground",
-  data = [
-    "//third_party/bun:bun_files",
-    ":all_ts_files",
-  ],
-  visibility = ["//visibility:public"],
-)
--- a/playground/README.md	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-# playground
-
-To install dependencies:
-
-```bash
-bun install
-```
-
-To run:
-
-```bash
-bun run 
-```
-
-This project was created using `bun init` in bun v1.2.23. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
--- a/playground/foo/foo.ts	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-export const foo = () => 10;
--- a/playground/hello.ts	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-const JUNE = "JUNE";
-
-export {
-  JUNE,
-}
--- a/playground/june.tsx	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-import react from "React"
-
-const Foo = () => {
-  return (
-    <> hehexd </>
-  );
-}
-
-export {
-  Foo,
-}
Binary file playground/main has changed
--- a/playground/main.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/playground/main.c	Fri Jan 09 07:42:04 2026 -0800
@@ -1,42 +1,65 @@
+#include <stdlib.h>
 #include <stdio.h>
-#include <stdlib.h>
-
-#define REPO_ROOT "/Users/mrjunejune/zenbu"
+#include <string.h>
 
-int main()
-{
-  char command[512];
-  snprintf(command, sizeof(command), "hg -R %s serve --stdio", REPO_ROOT);
-  printf("command: %s\n", command);
-  
-  FILE *hg_pipe = popen(command, "r+");
-  if (!hg_pipe)
-  {
-    printf("Failed to open pipe\n");
-    return -1;
-  }
+int main() {
+    char *input[5] = {"Hello ", "Foo <<E", "N", "DD>", "Park <END> "};
+    char *key = "<END>";
+    int key_len = strlen(key);
 
-  fprintf(hg_pipe, "capabilities\n");
-  fflush(hg_pipe);
+    char **buffers = malloc(sizeof(char *) * 100);
+    int buffer_index = 0;
+    int key_ptr = 0;
+    for (int i = 0; i < 5; i++) {
+        char *packet = input[i];
+        int p_len = strlen(packet);
 
-  char *output = malloc(sizeof(char) * 2048);
-  char *curr = output;
-  int c;
-  int number_of_breakline = 0;
-  while ((c = fgetc(hg_pipe))  != NULL)
-  {
-    *curr++ = c;
-    printf("output: %s\n", output);
-    if (c == '\n')
-      number_of_breakline++;
-    if (number_of_breakline == 2)
-      break;
-    printf("char: %c\n", c);
-  }
-  pclose(hg_pipe);
+        for (int j = 0; j < p_len; j++) {
+            if (packet[j] == key[key_ptr]) {
+                key_ptr++;
+                
+                // If the WHOLE keyword is found
+                if (key_ptr == key_len) {
+                    // 1. Print all previous "safe" buffers
+                    // (The ones before the one where the keyword started)
+                    for (int b = 0; b < buffer_index; b++)
+                        printf("%s", buffers[b]);
+                    
+                    // 2. Handle the "Current" packet truncation
+                    // Calculate where the match started in THIS packet
+                    // If key_ptr was satisfied across multiple packets, 
+                    // 'j' is the end of the match in the current packet.
+                    int match_end_in_packet = j + 1;
+                    int match_len_in_this_packet = (key_ptr <= match_end_in_packet) ? key_ptr : match_end_in_packet;
+                    
+                    printf("%.*s\n", (match_end_in_packet - match_len_in_this_packet), packet);
+                    
+                    free(buffers);
+                    return 0;
+                }
+            } else {
+                // If a match fails, we must "flush" the buffers we were holding
+                if (key_ptr > 0) {
+                    for (int b = 0; b < buffer_index; b++) printf("%s", buffers[b]);
+                    buffer_index = 0;
+                    
+                    if (packet[j] == key[0]) key_ptr = 1;
+                    else {
+                        printf("%c", packet[j]);
+                        key_ptr = 0;
+                    }
+                } else {
+                    printf("%c", packet[j]);
+                }
+            }
+        }
 
+        // If we finish a packet and we are in the middle of a match, buffer it
+        if (key_ptr > 0) {
+            buffers[buffer_index++] = packet;
+        }
+    }
 
-  return 0;
+    free(buffers);
+    return 0;
 }
-
-
--- a/playground/main.ts	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-import { Foo } from "./june";
-import ReactDOM from 'react-dom/client';
-
-ReactDOM.createRoot(document.getElementById('root')!).render(Foo());
-
--- a/playground/new.txt	Fri Jan 09 07:19:09 2026 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-hello good sir
--- a/seobeo/BUILD	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/BUILD	Fri Jan 09 07:42:04 2026 -0800
@@ -1,7 +1,6 @@
-load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
 load("@rules_cc//cc:cc_library.bzl", "cc_library")
-load("@rules_cc//cc:cc_test.bzl", "cc_test")
 
+# File group
 filegroup(
   name = "seobeo_hdrs",
   srcs = [
@@ -12,31 +11,72 @@
   visibility = ["//visibility:public"],
 )
 
+# Minimal TCP/SSL handling only (no HTTP, no WebSocket)
 alias(
-  name = "seobeo_server",
+  name = "seobeo_min",
   actual = select({
-    "//config:macos":  ":seobeo_server_macos",
-    "//config:linux":  ":seobeo_server_linux",
-    "//conditions:default": ":seobeo_server_linux",
-  }),
-  visibility = ["//visibility:public"],
-)
-
-alias(
-  name = "seobeo_server_dev",
-  actual = select({
-    "//config:macos":  ":seobeo_server_macos_dev",
-    "//config:linux":  ":seobeo_server_linux_dev",
-    "//conditions:default": ":seobeo_server_linux_dev",
+    "//config:macos":  ":seobeo_min_macos",
+    "//config:linux":  ":seobeo_min_linux",
+    "//conditions:default": ":seobeo_min_linux",
   }),
   visibility = ["//visibility:public"],
 )
 
 cc_library(
-  name = "seobeo_server_macos",
+  name = "seobeo_min_macos",
+  srcs = [
+    "s_network.c",
+    "s_logging.c",
+    "s_ssl.c",
+    "os/s_macos_edge.c",
+  ],
+  hdrs = [":seobeo_hdrs"],
+  deps = [
+    "//dowa:dowa",
+    "@openssl//:ssl",
+  ],
+  target_compatible_with = [
+    "@platforms//os:osx",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+cc_library(
+  name = "seobeo_min_linux",
   srcs = [
-    "s__network.c",
+    "s_network.c",
+    "s_logging.c",
+    "s_ssl.c",
+    "os/s_linux_edge.c",
+  ],
+  hdrs = [":seobeo_hdrs"],
+  deps = [
+    "//dowa:dowa",
+    "@openssl//:ssl",
+  ],
+  target_compatible_with = [
+    "@platforms//os:linux",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+# TCP Server with HTTP (no WebSocket, no SSL)
+alias(
+  name = "seobeo_tcp_server",
+  actual = select({
+    "//config:macos":  ":seobeo_tcp_server_macos",
+    "//config:linux":  ":seobeo_tcp_server_linux",
+    "//conditions:default": ":seobeo_tcp_server_linux",
+  }),
+  visibility = ["//visibility:public"],
+)
+
+cc_library(
+  name = "seobeo_tcp_server_macos",
+  srcs = [
+    "s_network.c",
     "s_web.c",
+    "s_logging.c",
     "s_ssl.c",
     "os/s_macos_edge.c",
   ],
@@ -52,29 +92,11 @@
 )
 
 cc_library(
-  name = "seobeo_server_macos_dev",
+  name = "seobeo_tcp_server_linux",
   srcs = [
     "s_network.c",
     "s_web.c",
-    "s_ssl.c",
-    "os/s_macos_edge.c",
-  ],
-  hdrs = [":seobeo_hdrs"],
-  deps = [
-    "//dowa:dowa",
-  ],
-  defines = ["SEOBEO_NO_SSL", "SEOBEO_ENABLE_DEBUG"],
-  target_compatible_with = [
-    "@platforms//os:osx",
-  ],
-  visibility = ["//visibility:public"],
-)
-
-cc_library(
-  name = "seobeo_server_linux",
-  srcs = [
-    "s_network.c",
-    "s_web.c",
+    "s_logging.c",
     "s_ssl.c",
     "os/s_linux_edge.c",
   ],
@@ -89,45 +111,81 @@
   visibility = ["//visibility:public"],
 )
 
+# TCP Server with HTTP + WebSocket
+alias(
+  name = "seobeo_tcp_server_ws",
+  actual = select({
+    "//config:macos":  ":seobeo_tcp_server_ws_macos",
+    "//config:linux":  ":seobeo_tcp_server_ws_linux",
+    "//conditions:default": ":seobeo_tcp_server_ws_linux",
+  }),
+  visibility = ["//visibility:public"],
+)
+
 cc_library(
-  name = "seobeo_server_linux_dev",
+  name = "seobeo_tcp_server_ws_macos",
   srcs = [
     "s_network.c",
     "s_web.c",
+    "s_logging.c",
     "s_ssl.c",
+    "s_websocket_common.c",
+    "s_websocket_server.c",
+    "os/s_macos_edge.c",
+  ],
+  hdrs = [":seobeo_hdrs"],
+  deps = [
+    "//dowa:dowa",
+    "@openssl//:ssl",
+  ],
+  defines = ["SEOBEO_WEBSOCKET_SERVER"],
+  target_compatible_with = [
+    "@platforms//os:osx",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+cc_library(
+  name = "seobeo_tcp_server_ws_linux",
+  srcs = [
+    "s_network.c",
+    "s_web.c",
+    "s_logging.c",
+    "s_ssl.c",
+    "s_websocket_common.c",
+    "s_websocket_server.c",
     "os/s_linux_edge.c",
   ],
   hdrs = [":seobeo_hdrs"],
   deps = [
     "//dowa:dowa",
+    "@openssl//:ssl",
   ],
-  defines = ["SEOBEO_NO_SSL", "SEOBEO_ENABLE_DEBUG"],
+  defines = ["SEOBEO_WEBSOCKET_SERVER"],
   target_compatible_with = [
     "@platforms//os:linux",
   ],
   visibility = ["//visibility:public"],
 )
 
-# Client target with SSL support (full functionality)
+# TCP Client with HTTP
 alias(
-  name = "seobeo_client",
+  name = "seobeo_tcp_client",
   actual = select({
-    "//config:macos":  ":seobeo_client_macos",
-    "//config:linux":  ":seobeo_client_linux",
-    "//conditions:default": ":seobeo_client_linux",
+    "//config:macos":  ":seobeo_tcp_client_macos",
+    "//config:linux":  ":seobeo_tcp_client_linux",
+    "//conditions:default": ":seobeo_tcp_client_linux",
   }),
   visibility = ["//visibility:public"],
 )
 
 cc_library(
-  name = "seobeo_client_macos",
+  name = "seobeo_tcp_client_macos",
   srcs = [
     "s_network.c",
-    "s_web.c",
+    "s_logging.c",
     "s_ssl.c",
     "s_http_client.c",
-    "s_websocket.c",
-    "s_websocket_server.c",
     "snapshot_creator.c",
     "os/s_macos_edge.c",
   ],
@@ -143,14 +201,69 @@
 )
 
 cc_library(
-  name = "seobeo_client_linux",
+  name = "seobeo_tcp_client_linux",
   srcs = [
     "s_network.c",
-    "s_web.c",
+    "s_logging.c",
     "s_ssl.c",
     "s_http_client.c",
+    "snapshot_creator.c",
+    "os/s_linux_edge.c",
+  ],
+  hdrs = [":seobeo_hdrs"],
+  deps = [
+    "//dowa:dowa",
+    "@openssl//:ssl",
+  ],
+  target_compatible_with = [
+    "@platforms//os:linux",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+# TCP Client with HTTP + WebSocket
+alias(
+  name = "seobeo_tcp_client_ws",
+  actual = select({
+    "//config:macos":  ":seobeo_tcp_client_ws_macos",
+    "//config:linux":  ":seobeo_tcp_client_ws_linux",
+    "//conditions:default": ":seobeo_tcp_client_ws_linux",
+  }),
+  visibility = ["//visibility:public"],
+)
+
+cc_library(
+  name = "seobeo_tcp_client_ws_macos",
+  srcs = [
+    "s_network.c",
+    "s_logging.c",
+    "s_ssl.c",
+    "s_http_client.c",
+    "s_websocket_common.c",
     "s_websocket.c",
-    "s_websocket_server.c",
+    "snapshot_creator.c",
+    "os/s_macos_edge.c",
+  ],
+  hdrs = [":seobeo_hdrs"],
+  deps = [
+    "//dowa:dowa",
+    "@openssl//:ssl",
+  ],
+  target_compatible_with = [
+    "@platforms//os:osx",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+cc_library(
+  name = "seobeo_tcp_client_ws_linux",
+  srcs = [
+    "s_network.c",
+    "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",
   ],
@@ -165,46 +278,126 @@
   visibility = ["//visibility:public"],
 )
 
-# Default target for backward compatibility (points to client with SSL)
+# All combined  only used this if you don't want to deal with imports and what not...
 alias(
   name = "seobeo",
-  actual = ":seobeo_client",
+  actual = select({
+    "//config:macos":  ":seobeo_macos",
+    "//config:linux":  ":seobeo_linux",
+    "//conditions:default": ":seobeo_linux",
+  }),
   visibility = ["//visibility:public"],
 )
 
-# Examples
-cc_binary(
-  name = "websocket_server_example",
-  srcs = ["examples/websocket_server_example.c"],
-  deps = [":seobeo_client"],
+cc_library(
+  name = "seobeo_macos",
+  srcs = [
+    "s_network.c",
+    "s_web.c",
+    "s_logging.c",
+    "s_ssl.c",
+    "s_http_client.c",
+    "s_websocket_common.c",
+    "s_websocket.c",
+    "s_websocket_server.c",
+    "snapshot_creator.c",
+    "os/s_macos_edge.c",
+  ],
+  hdrs = [":seobeo_hdrs"],
+  deps = [
+    "//dowa:dowa",
+    "@openssl//:ssl",
+  ],
+  defines = ["SEOBEO_WEBSOCKET_SERVER"],
+  target_compatible_with = [
+    "@platforms//os:osx",
+  ],
   visibility = ["//visibility:public"],
 )
 
-# Tests
-cc_test(
-  name = "seobeo_client_test",
-  srcs = ["tests/seobeo_client_test.c"],
-  deps = [":seobeo_client"],
+cc_library(
+  name = "seobeo_linux",
+  srcs = [
+    "s_network.c",
+    "s_web.c",
+    "s_logging.c",
+    "s_ssl.c",
+    "s_http_client.c",
+    "s_websocket_common.c",
+    "s_websocket.c",
+    "s_websocket_server.c",
+    "snapshot_creator.c",
+    "os/s_linux_edge.c",
+  ],
+  hdrs = [":seobeo_hdrs"],
+  deps = [
+    "//dowa:dowa",
+    "@openssl//:ssl",
+  ],
+  defines = ["SEOBEO_WEBSOCKET_SERVER"],
+  target_compatible_with = [
+    "@platforms//os:linux",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+# Names I often use 
+alias(
+  name = "seobeo_server",
+  actual = ":seobeo_tcp_server",
   visibility = ["//visibility:public"],
 )
 
-cc_test(
-  name = "seobeo_websocket_test",
-  srcs = ["tests/seobeo_websocket_test.c"],
-  deps = [":seobeo_client"],
-  size = "small",
-  timeout = "short",
+alias(
+  name = "seobeo_client",
+  actual = ":seobeo",
+  visibility = ["//visibility:public"],
+)
+# ============================================================================
+# Testing Utilities
+# ============================================================================
+
+# Testing library with snapshot and test generation tools
+alias(
+  name = "seobeo_test",
+  actual = select({
+    "//config:macos":  ":seobeo_test_macos",
+    "//config:linux":  ":seobeo_test_linux",
+    "//conditions:default": ":seobeo_test_linux",
+  }),
   visibility = ["//visibility:public"],
 )
 
-cc_test(
-  name = "seobeo_websocket_server_test",
-  srcs = ["tests/seobeo_web_server_test.c"],
-  deps = [":seobeo_client"],
-  data = [
-    ":websocket_server_example",
+cc_library(
+  name = "seobeo_test_macos",
+  srcs = [
+    "snapshot_creator.c",
+  ],
+  hdrs = [
+    "snapshot_creator.h",
+  ],
+  deps = [
+    ":seobeo_tcp_client_macos",
+  ],
+  target_compatible_with = [
+    "@platforms//os:osx",
   ],
-  size = "large",
-  timeout = "long",
-  args = ["$(location //seobeo:websocket_server_example)"],
+  visibility = ["//visibility:public"],
 )
+
+cc_library(
+  name = "seobeo_test_linux",
+  srcs = [
+    "snapshot_creator.c",
+  ],
+  hdrs = [
+    "snapshot_creator.h",
+  ],
+  deps = [
+    ":seobeo_tcp_client_linux",
+  ],
+  target_compatible_with = [
+    "@platforms//os:linux",
+  ],
+  visibility = ["//visibility:public"],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/seobeo/examples/BUILD	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,8 @@
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+
+cc_binary(
+  name = "websocket_server_example",
+  srcs = ["websocket_server_example.c"],
+  deps = ["//seobeo:seobeo"],
+  visibility = ["//visibility:public"],
+)
--- a/seobeo/examples/websocket_server_example.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/examples/websocket_server_example.c	Fri Jan 09 07:42:04 2026 -0800
@@ -29,7 +29,7 @@
     snprintf(message, sizeof(message), "[%s]: %.*s", p_conn->client_id, (int)p_msg->length, (char*)p_msg->data);
 
     printf("[Chat] Broadcasting: %s\n", message);
-    Seobeo_WebSocket_Server_Broadcast_Text(message);
+    Seobeo_WebSocket_Server_Broadcast_Text(message, p_conn);
   }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/seobeo/s_logging.c	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,30 @@
+#include "seobeo/seobeo.h"
+
+static char *Seobeo_Log_Level_String(Seobeo_Log_Level level)
+{
+  switch(level)
+  {
+    case SEOBEO_DEBUG:   return "DEBUG";
+    case SEOBEO_INFO:    return "INFO";
+    case SEOBEO_WARNING: return "WARNING";
+    case SEOBEO_ERROR:   return "ERROR";
+    default:          return "INFO";
+  }
+}
+
+int Seobeo_Log(Seobeo_Log_Level level, const char * restrict format, ...)
+{
+  #ifndef SEOBEO_ENABLE_DEBUG
+  if (level == SEOBEO_DEBUG)
+    return 0;
+  #endif
+
+  int result;
+  va_list args;
+  printf("[%s] ", Seobeo_Log_Level_String(level));
+  va_start(args, format);
+  result = vprintf(format, args);
+  va_end(args);
+
+  return result;
+}
--- a/seobeo/s_network.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/s_network.c	Fri Jan 09 07:42:04 2026 -0800
@@ -103,6 +103,11 @@
   { perror("connect"); return NULL; }
   freeaddrinfo(server_infos);
 
+  // Set non-blocking
+  int flags = fcntl(socket_fd, F_GETFL, 0);
+  if (flags == -1 || fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) != 0)
+  { perror("fcntl"); close(socket_fd); free(p_handle); return NULL; }
+
   p_handle->socket = socket_fd;
   p_handle->type = SEOBEO_STREAM_TYPE_CLIENT;
 
--- a/seobeo/s_web.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/s_web.c	Fri Jan 09 07:42:04 2026 -0800
@@ -65,13 +65,14 @@
 void Seobeo_Web_HandleClientRequest(Seobeo_Handle  *p_cli_handle,
                                     Seobeo_Cache_Entry *p_html_cache)
 {
-  Dowa_Arena *p_request_arena = Dowa_Arena_Create(1*1024*1024); // 1MB for request parsing
+  // TODO: This should be splitted up instead of handling up to 50 MB as it will fail more often... 
+  Dowa_Arena *p_request_arena = Dowa_Arena_Create(50*1024*1024); // 50 MB because of files... 
   if (!p_request_arena) { perror("Dowa_Arena_Create request"); goto clean_up; }
 
-  Dowa_Arena *p_response_arena = Dowa_Arena_Create(5*1024*1024); // 5MB for response
+  Dowa_Arena *p_response_arena = Dowa_Arena_Create(50*1024*1024); // 50 MB for response
   if (!p_response_arena) { perror("Dowa_Arena_Create response"); goto clean_up; }
 
-  void *p_response_header = Dowa_Arena_Allocate(p_response_arena, (size_t)1024*5); // 5Kb
+  void *p_response_header = Dowa_Arena_Allocate(p_response_arena, 1024*5); // 5Kb
   if (!p_response_header) { perror("Dowa_Arena_Allocate"); goto clean_up; }
 
   Seobeo_Request_Entry *p_req_map = NULL;
@@ -135,6 +136,7 @@
   const char *path = p_path_kv ? ((Seobeo_Request_Entry*)p_path_kv)->value : "/";
 
   // --- Check for WebSocket upgrade request ---
+  #ifdef SEOBEO_WEBSOCKET_SERVER
   if (Seobeo_WebSocket_Server_Handle_Upgrade(p_cli_handle, p_req_map, path))
   {
     Seobeo_Log(SEOBEO_INFO, "WebSocket connection established\n");
@@ -144,6 +146,7 @@
       Dowa_Arena_Free(p_response_arena);
     return;
   }
+  #endif
 
   // --- Try to match API route first ---
   Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena);
@@ -730,31 +733,5 @@
   g_routes = NULL;
 }
 
-static char *Seobeo_Log_Level_String(Seobeo_Log_Level level)
-{
-  switch(level) {
-    case SEOBEO_DEBUG:   return "DEBUG";
-    case SEOBEO_INFO:    return "INFO";
-    case SEOBEO_WARNING: return "WARNING";
-    case SEOBEO_ERROR:   return "ERROR";
-    default:          return "INFO";
-  }
-}
+// Logging functions moved to s_logging.c
 
-int Seobeo_Log(Seobeo_Log_Level level, const char * restrict format, ...)
-{
-  #ifndef SEOBEO_ENABLE_DEBUG
-  if (level == SEOBEO_DEBUG)
-    return 0;
-  #endif
-
-  int result;
-  va_list args;
-  printf("[%s] ", Seobeo_Log_Level_String(level));
-  va_start(args, format);
-  result = vprintf(format, args);
-  va_end(args);
-
-  return result;
-}
-
--- a/seobeo/s_websocket.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/s_websocket.c	Fri Jan 09 07:42:04 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	Fri Jan 09 07:42:04 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	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/s_websocket_server.c	Fri Jan 09 07:42:04 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	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/seobeo.h	Fri Jan 09 07:42:04 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	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/seobeo_internal.h	Fri Jan 09 07:42:04 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);
--- a/seobeo/snapshot_creator.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/snapshot_creator.c	Fri Jan 09 07:42:04 2026 -0800
@@ -1,13 +1,11 @@
 #include "seobeo/snapshot_creator.h"
+#include "seobeo/seobeo_internal.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
-#define MAX_RESPONSE_SIZE (1024 * 1024)
-
-// Helper: Convert URL path to filename
 static void path_to_filename(const char *path, char *filename, size_t max_len)
 {
   if (strcmp(path, "/") == 0)
@@ -18,9 +16,7 @@
 
   const char *p = path;
   if (*p == '/')
-  {
     p++;
-  }
 
   char *out = filename;
   size_t remaining = max_len - 1;
@@ -69,68 +65,61 @@
   return mkdir(tmp, 0755);
 }
 
-// Helper: Generate HTTP GET request
-static int generate_http_get(char *buffer, size_t size, const char *path, const char *host)
+// Helper: Build full HTTP response from Seobeo_Client_Response
+static char* build_full_response(Seobeo_Client_Response *p_resp, size_t *len_out)
 {
-  return snprintf(
-    buffer, size,
-    "GET %s HTTP/1.1\r\n"
-    "Host: %s\r\n"
-    "Connection: close\r\n"
-    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
-    "User-Agent: SeobeoSnapshotCreator/1.0\r\n"
-    "\r\n",
-    path, host
-  );
-}
-
-// Helper: Read full HTTP response
-static char* read_full_response(Seobeo_Handle *client, size_t *len_out)
-{
-  char *response = malloc(MAX_RESPONSE_SIZE);
-  if (!response)
-  {
+  if (!p_resp)
     return NULL;
-  }
-
-  size_t total = 0;
-  int attempts = 0;
-  const int max_attempts = 100;
 
-  while (attempts++ < max_attempts && total < MAX_RESPONSE_SIZE - 1)
-  {
-    int n = Seobeo_Handle_Read(client);
-
-    if (n > 0)
-    {
-      size_t to_copy = client->read_buffer_len;
-      if (total + to_copy > MAX_RESPONSE_SIZE - 1)
-      {
-        to_copy = MAX_RESPONSE_SIZE - 1 - total;
-      }
+  // Calculate total response size
+  size_t status_line_len = 100; // "HTTP/1.1 200 OK\r\n"
+  size_t headers_len = 0;
+  size_t body_len = p_resp->body_length;
 
-      memcpy(response + total, client->read_buffer, to_copy);
-      total += to_copy;
-      Seobeo_Handle_Consume(client, (uint32)to_copy);
-    }
-    else if (n == -2)
+  // Estimate header size
+  if (p_resp->headers)
+  {
+    size_t header_count = Dowa_Array_Length(p_resp->headers);
+    for (size_t i = 0; i < header_count; i++)
     {
-      break;
-    }
-    else if (n == 0)
-    {
-      usleep(10000);
-      continue;
-    }
-    else
-    {
-      free(response);
-      return NULL;
+      headers_len += strlen(p_resp->headers[i].key) + strlen(p_resp->headers[i].value) + 4; // ": " + "\r\n"
     }
   }
 
-  response[total] = '\0';
-  *len_out = total;
+  size_t total_size = status_line_len + headers_len + 2 + body_len + 1; // +2 for "\r\n", +1 for null terminator
+  char *response = malloc(total_size);
+  if (!response)
+    return NULL;
+
+  // Build status line
+  int offset = snprintf(response, total_size, "HTTP/1.1 %d %s\r\n",
+                        p_resp->status_code,
+                        p_resp->status_text ? p_resp->status_text : "OK");
+
+  // Add headers
+  if (p_resp->headers)
+  {
+    size_t header_count = Dowa_Array_Length(p_resp->headers);
+    for (size_t i = 0; i < header_count; i++)
+    {
+      offset += snprintf(response + offset, total_size - offset, "%s: %s\r\n",
+                        p_resp->headers[i].key,
+                        p_resp->headers[i].value);
+    }
+  }
+
+  // End of headers
+  offset += snprintf(response + offset, total_size - offset, "\r\n");
+
+  // Add body
+  if (p_resp->body && body_len > 0)
+  {
+    memcpy(response + offset, p_resp->body, body_len);
+    offset += body_len;
+  }
+
+  response[offset] = '\0';
+  *len_out = offset;
   return response;
 }
 
@@ -171,66 +160,39 @@
 
   printf("Creating snapshot: %s (expecting %d)\n", config->path, config->expected_status);
 
-  // Connect to server
-  Seobeo_Handle *client = Seobeo_Stream_Handle_Client_Create(config->host, config->port, FALSE);
-  if (!client || client->socket < 0)
+  // Build URL
+  char url[2048];
+  snprintf(url, sizeof(url), "http://%s:%s%s", config->host, config->port, config->path);
+
+  // Create HTTP request
+  Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url);
+  if (!p_req)
   {
-    fprintf(stderr, "  ✗ Failed to connect to %s:%s\n", config->host, config->port);
-    if (client)
-    {
-      Seobeo_Handle_Destroy(client);
-    }
-    return -1;
-  }
-
-  // Send GET request
-  char request[4096];
-  int req_len = generate_http_get(request, sizeof(request), config->path, config->host);
-  Seobeo_Handle_Queue(client, (uint8*)request, (uint32)req_len);
-
-  if (Seobeo_Handle_Flush(client) < 0)
-  {
-    fprintf(stderr, "  ✗ Failed to send request\n");
-    Seobeo_Handle_Destroy(client);
+    fprintf(stderr, "  ✗ Failed to create request for %s\n", url);
     return -1;
   }
 
-  // Read response
-  size_t response_len = 0;
-  char *response = read_full_response(client, &response_len);
-  Seobeo_Handle_Destroy(client);
-
-  if (!response || response_len == 0)
+  // Execute request
+  Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req);
+  if (!p_resp)
   {
-    fprintf(stderr, "  ✗ Failed to read response\n");
-    if (response)
-    {
-      free(response);
-    }
+    fprintf(stderr, "  ✗ Failed to get response\n");
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  // Parse status code
-  int status = -1;
-  const char *status_line = strstr(response, "HTTP/1.1 ");
-  if (!status_line)
-  {
-    status_line = strstr(response, "HTTP/1.0 ");
-  }
-  if (status_line)
-  {
-    sscanf(status_line + 9, "%d", &status);
-  }
-
-  if (status != config->expected_status)
+  // Check status code
+  if (p_resp->status_code != config->expected_status)
   {
     fprintf(stderr, "  ✗ Status mismatch: expected %d, got %d\n",
-            config->expected_status, status);
-    free(response);
+            config->expected_status, p_resp->status_code);
+    // fprintf(stderr, "%s", p_resp->body);
+    Seobeo_Client_Response_Destroy(p_resp);
+    Seobeo_Client_Request_Destroy(p_req);
     return -1;
   }
 
-  printf("  ✓ Status: %d\n", status);
+  printf("  ✓ Status: %d\n", p_resp->status_code);
 
   // Generate snapshot filename
   char filename[256];
@@ -240,18 +202,19 @@
   snprintf(filepath, sizeof(filepath), "%s/%s", config->snapshot_dir, filename);
 
   // Write snapshot
-  if (write_snapshot(filepath, response, response_len) == 0)
+  if (write_snapshot(filepath, p_resp->body, p_resp->body_length) == 0)
   {
-    printf("  ✓ Snapshot saved: %s (%zu bytes)\n", filepath, response_len);
-    free(response);
+    printf("  ✓ Snapshot saved: %s (%zu bytes)\n", filepath, p_resp->body_length);
     return 0;
   }
   else
   {
     fprintf(stderr, "  ✗ Failed to write snapshot: %s\n", filepath);
-    free(response);
     return -1;
   }
+
+  Seobeo_Client_Response_Destroy(p_resp);
+  Seobeo_Client_Request_Destroy(p_req);
 }
 
 int Seobeo_Snapshots_Create_Batch(const SnapshotConfig configs[], int count)
@@ -262,13 +225,9 @@
   for (int i = 0; i < count; i++)
   {
     if (Seobeo_Snapshot_Create(&configs[i]) == 0)
-    {
       passed++;
-    }
     else
-    {
       failed++;
-    }
     printf("\n");
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/seobeo/tests/BUILD	Fri Jan 09 07:42:04 2026 -0800
@@ -0,0 +1,30 @@
+load("@rules_cc//cc:cc_test.bzl", "cc_test")
+
+cc_test(
+  name = "seobeo_client_test",
+  srcs = ["seobeo_client_test.c"],
+  deps = ["//seobeo:seobeo"],
+  visibility = ["//visibility:public"],
+)
+
+cc_test(
+  name = "seobeo_websocket_test",
+  srcs = ["seobeo_websocket_test.c"],
+  deps = ["//seobeo:seobeo"],
+  size = "small",
+  timeout = "short",
+  visibility = ["//visibility:public"],
+)
+
+cc_test(
+  name = "seobeo_websocket_server_test",
+  srcs = ["seobeo_web_server_test.c"],
+  deps = ["//seobeo:seobeo"],
+  data = [
+    "//seobeo/examples:websocket_server_example",
+  ],
+  size = "large",
+  timeout = "long",
+  args = ["$(location //seobeo/examples:websocket_server_example)"],
+  visibility = ["//visibility:public"],
+)
--- a/seobeo/tests/seobeo_web_server_test.c	Fri Jan 09 07:19:09 2026 -0800
+++ b/seobeo/tests/seobeo_web_server_test.c	Fri Jan 09 07:42:04 2026 -0800
@@ -12,8 +12,8 @@
 
   if (server_pid == 0)
   {
-    printf("Starting server on port 8000...\n");
-    execlp(server_binary, NULL);
+    printf("Starting server on port 8080...\n");
+    execl(server_binary, server_binary, NULL);
     perror("execl failed");
     exit(1);
   }