comparison 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
comparison
equal deleted inserted replaced
250:745fd127b2a1 251:117c4d53c9a4
1 #include "seobeo/seobeo.h" 1 #include "seobeo/seobeo.h"
2 #include <strings.h> 2 #include <strings.h>
3 #include <time.h> 3 #include <time.h>
4 4
5 static char g_folder_path[512] = "."; 5 static char g_folder_path[512] = ".";
6
7 static boolean Seobeo_Static_Path_Is_Safe(const char *path)
8 {
9 if (!path || strchr(path, '\\'))
10 return FALSE;
11 const char *segment = path;
12 while (*segment)
13 {
14 while (*segment == '/')
15 segment++;
16 const char *end = segment;
17 while (*end && *end != '/')
18 end++;
19 if ((size_t)(end - segment) == 2 &&
20 segment[0] == '.' &&
21 segment[1] == '.')
22 return FALSE;
23 segment = end;
24 }
25 return TRUE;
26 }
6 27
7 static char *canonical_request_header(char *header) 28 static char *canonical_request_header(char *header)
8 { 29 {
9 if (strcasecmp(header, "content-length") == 0) return "Content-Length"; 30 if (strcasecmp(header, "content-length") == 0) return "Content-Length";
10 if (strcasecmp(header, "content-type") == 0) return "Content-Type"; 31 if (strcasecmp(header, "content-type") == 0) return "Content-Type";
239 } 260 }
240 261
241 // --- Static files fallback for GET (use original large arena logic) --- 262 // --- Static files fallback for GET (use original large arena logic) ---
242 if (strcmp(method, "GET") == 0) 263 if (strcmp(method, "GET") == 0)
243 { 264 {
265 if (!Seobeo_Static_Path_Is_Safe(path))
266 {
267 Seobeo_Web_Header_Generate_KeepAlive(
268 p_response_header,
269 HTTP_BAD_REQUEST,
270 "text/plain",
271 0,
272 should_keep_alive);
273 Seobeo_Handle_Queue(
274 p_cli_handle,
275 (const uint8 *)p_response_header,
276 (uint32)strlen(p_response_header));
277 Seobeo_Handle_Flush(p_cli_handle);
278 goto clean_up_arenas;
279 }
280
244 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024); 281 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024);
245 282
246 if (!path || strcmp(path, "/") == 0) 283 if (!path || strcmp(path, "/") == 0)
247 { 284 {
248 strcpy(file_path, "index.html"); 285 strcpy(file_path, "index.html");
259 } 296 }
260 else 297 else
261 strcpy(file_path, path); 298 strcpy(file_path, path);
262 } 299 }
263 300
264 void *p_file_kv = Dowa_HashMap_Get_Ptr(p_html_cache, file_path); 301 char *file_content = NULL;
265 const char *file_content = NULL;
266 size_t body_size = 0; 302 size_t body_size = 0;
267 303 (void)p_html_cache;
268 if (p_file_kv) 304 file_content = Seobeo_Web_LoadFile(file_path, &body_size);
269 {
270 Seobeo_Cached_File *cached = ((Seobeo_Cache_Entry*)p_file_kv)->value;
271 file_content = cached->content;
272 body_size = cached->size;
273 }
274 else
275 {
276 file_content = Seobeo_Web_LoadFile(file_path, &body_size);
277 if (file_content)
278 {
279 Seobeo_Cached_File *cached = malloc(sizeof(Seobeo_Cached_File));
280 cached->content = (char*)file_content;
281 cached->size = body_size;
282 Dowa_HashMap_Push(p_html_cache, file_path, cached);
283 }
284 }
285 305
286 if (!file_content) 306 if (!file_content)
287 { 307 {
288 Seobeo_Web_Header_Generate_KeepAlive(p_response_header, 308 Seobeo_Web_Header_Generate_KeepAlive(p_response_header,
289 HTTP_NOT_FOUND, 309 HTTP_NOT_FOUND,
334 (uint32)strlen(p_response_header)); 354 (uint32)strlen(p_response_header));
335 Seobeo_Handle_Queue(p_cli_handle, 355 Seobeo_Handle_Queue(p_cli_handle,
336 (const uint8*)file_content, 356 (const uint8*)file_content,
337 (uint32)body_size); 357 (uint32)body_size);
338 Seobeo_Handle_Flush(p_cli_handle); 358 Seobeo_Handle_Flush(p_cli_handle);
359 free(file_content);
339 } 360 }
340 else 361 else
341 { 362 {
342 Seobeo_Web_Header_Generate_KeepAlive(p_response_header, 363 Seobeo_Web_Header_Generate_KeepAlive(p_response_header,
343 HTTP_NOT_FOUND, 364 HTTP_NOT_FOUND,
598 const char *folder_path, 619 const char *folder_path,
599 const char *port, 620 const char *port,
600 Seobeo_ServerMode mode, 621 Seobeo_ServerMode mode,
601 int thread_count) 622 int thread_count)
602 { 623 {
624 return Seobeo_Web_Server_Start_On(
625 NULL,
626 folder_path,
627 port,
628 mode,
629 thread_count);
630 }
631
632 int Seobeo_Web_Server_Start_On(
633 const char *host,
634 const char *folder_path,
635 const char *port,
636 Seobeo_ServerMode mode,
637 int thread_count)
638 {
603 if (folder_path) 639 if (folder_path)
604 strncpy(g_folder_path, folder_path, sizeof(g_folder_path) - 1); 640 strncpy(g_folder_path, folder_path, sizeof(g_folder_path) - 1);
605 641
606 Seobeo_Cache_Entry *p_html_cache = NULL; 642 Seobeo_Cache_Entry *p_html_cache = NULL;
607 643
608 Seobeo_Handle *p_server_handle = 644 Seobeo_Handle *p_server_handle =
609 Seobeo_Stream_Handle_Server_Create(NULL, port); 645 Seobeo_Stream_Handle_Server_Create(host, port);
610 if (p_server_handle->socket < 0) return 1; 646 if (!p_server_handle || p_server_handle->socket < 0)
647 {
648 if (p_server_handle)
649 Seobeo_Handle_Destroy(p_server_handle);
650 return 1;
651 }
611 652
612 Seobeo_Log(SEOBEO_INFO, "Listening on port %s\n", port); 653 Seobeo_Log(SEOBEO_INFO, "Listening on port %s\n", port);
613 654
614 if (mode == SEOBEO_MODE_FORK) 655 if (mode == SEOBEO_MODE_FORK)
615 { 656 {