diff seobeo/s_web.c @ 251:117c4d53c9a4

[ui] Add HTML-first Web Component system Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 09:14:57 -0700
parents b8aa08503378
children 609d3c6aff4e
line wrap: on
line diff
--- a/seobeo/s_web.c	Tue Aug 04 06:23:37 2026 -0700
+++ b/seobeo/s_web.c	Tue Aug 04 09:14:57 2026 -0700
@@ -4,6 +4,27 @@
 
 static char g_folder_path[512] = ".";
 
+static boolean Seobeo_Static_Path_Is_Safe(const char *path)
+{
+  if (!path || strchr(path, '\\'))
+    return FALSE;
+  const char *segment = path;
+  while (*segment)
+  {
+    while (*segment == '/')
+      segment++;
+    const char *end = segment;
+    while (*end && *end != '/')
+      end++;
+    if ((size_t)(end - segment) == 2 &&
+        segment[0] == '.' &&
+        segment[1] == '.')
+      return FALSE;
+    segment = end;
+  }
+  return TRUE;
+}
+
 static char *canonical_request_header(char *header)
 {
   if (strcasecmp(header, "content-length") == 0) return "Content-Length";
@@ -241,6 +262,22 @@
   // --- Static files fallback for GET (use original large arena logic) ---
   if (strcmp(method, "GET") == 0)
   {
+    if (!Seobeo_Static_Path_Is_Safe(path))
+    {
+      Seobeo_Web_Header_Generate_KeepAlive(
+          p_response_header,
+          HTTP_BAD_REQUEST,
+          "text/plain",
+          0,
+          should_keep_alive);
+      Seobeo_Handle_Queue(
+          p_cli_handle,
+          (const uint8 *)p_response_header,
+          (uint32)strlen(p_response_header));
+      Seobeo_Handle_Flush(p_cli_handle);
+      goto clean_up_arenas;
+    }
+
     char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024);
 
     if (!path || strcmp(path, "/") == 0)
@@ -261,27 +298,10 @@
         strcpy(file_path, path);
     }
 
-    void *p_file_kv = Dowa_HashMap_Get_Ptr(p_html_cache, file_path);
-    const char *file_content = NULL;
+    char *file_content = NULL;
     size_t body_size = 0;
-
-    if (p_file_kv)
-    {
-      Seobeo_Cached_File *cached = ((Seobeo_Cache_Entry*)p_file_kv)->value;
-      file_content = cached->content;
-      body_size = cached->size;
-    }
-    else
-    {
-      file_content = Seobeo_Web_LoadFile(file_path, &body_size);
-      if (file_content)
-      {
-        Seobeo_Cached_File *cached = malloc(sizeof(Seobeo_Cached_File));
-        cached->content = (char*)file_content;
-        cached->size = body_size;
-        Dowa_HashMap_Push(p_html_cache, file_path, cached);
-      }
-    }
+    (void)p_html_cache;
+    file_content = Seobeo_Web_LoadFile(file_path, &body_size);
 
     if (!file_content)
     {
@@ -336,6 +356,7 @@
                         (const uint8*)file_content,
                         (uint32)body_size);
     Seobeo_Handle_Flush(p_cli_handle);
+    free(file_content);
   }
   else
   {
@@ -600,14 +621,34 @@
     Seobeo_ServerMode mode,
     int                thread_count)
 {
+  return Seobeo_Web_Server_Start_On(
+      NULL,
+      folder_path,
+      port,
+      mode,
+      thread_count);
+}
+
+int Seobeo_Web_Server_Start_On(
+    const char       *host,
+    const char       *folder_path,
+    const char       *port,
+    Seobeo_ServerMode mode,
+    int                thread_count)
+{
   if (folder_path)
     strncpy(g_folder_path, folder_path, sizeof(g_folder_path) - 1);
 
   Seobeo_Cache_Entry *p_html_cache = NULL;
 
   Seobeo_Handle *p_server_handle =
-    Seobeo_Stream_Handle_Server_Create(NULL, port);
-  if (p_server_handle->socket < 0) return 1;
+    Seobeo_Stream_Handle_Server_Create(host, port);
+  if (!p_server_handle || p_server_handle->socket < 0)
+  {
+    if (p_server_handle)
+      Seobeo_Handle_Destroy(p_server_handle);
+    return 1;
+  }
 
   Seobeo_Log(SEOBEO_INFO, "Listening on port %s\n", port);