# HG changeset patch # User June Park # Date 1767404830 28800 # Node ID 655ea0b661fdc8dc6c45c2e759aeba65e381dfe8 # Parent 19cccf6e866a8d1c17986a6773ee4ed51e33b823 [Seobeo] Added few endpoints for handling files. [Dowa] Added few functions for random number and generating uuids diff -r 19cccf6e866a -r 655ea0b661fd color_game/BUILD --- a/color_game/BUILD Thu Jan 01 16:34:51 2026 -0800 +++ b/color_game/BUILD Fri Jan 02 17:47:10 2026 -0800 @@ -5,7 +5,7 @@ srcs = ["main.c"], deps = [ "//third_party/raylib:raylib", - "//dowa:dowa_generic", + "//dowa:dowa", ], static = True ) diff -r 19cccf6e866a -r 655ea0b661fd dowa/BUILD --- a/dowa/BUILD Thu Jan 01 16:34:51 2026 -0800 +++ b/dowa/BUILD Fri Jan 02 17:47:10 2026 -0800 @@ -8,12 +8,12 @@ visibility = ["//visibility:public"], ) -# TODO: Fix so that we can just define in the header instead of defining in the cc_library because this is ass. cc_library( - name = "dowa_generic", + name = "dowa", srcs = [ "d_string.c", "d_memory.c", + "d_math.c", ], hdrs = [ "dowa.h", @@ -22,20 +22,6 @@ visibility = ["//visibility:public"], ) -cc_library( - name = "dowa", - srcs = [ - "d_string.c", - "d_memory.c", - ], - hdrs = [ - "dowa.h", - "dowa_internal.h", - ], - copts = ["-DDIRECTORY"], - visibility = ["//visibility:public"], -) - cc_test( name = "dowa_test", srcs = ["dowa_test.c"], @@ -44,7 +30,6 @@ data = glob([ "test_folder/**", ]), - # TODO: Fix this lmao? copts = ["-fsanitize=address", "-g"], linkopts = ["-fsanitize=address"], ) diff -r 19cccf6e866a -r 655ea0b661fd dowa/d_math.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dowa/d_math.c Fri Jan 02 17:47:10 2026 -0800 @@ -0,0 +1,13 @@ +#include "dowa.h" + +/* Thank you Eskil */ +uint32 Dowa_Math_Random_Uint32(uint32 seed_number) +{ + seed_number ^= seed_number << 13; + seed_number ^= seed_number >> 17; + seed_number ^= seed_number << 5; + return seed_number & 0x7fffffff; + + //seed_number = (seed_number << 13) ^ seed_number; + //return ((seed_number * (seed_number * seed_number * 15731 + 789221) + 1376312589) & 0x7fffffff); +} diff -r 19cccf6e866a -r 655ea0b661fd dowa/d_memory.c --- a/dowa/d_memory.c Thu Jan 01 16:34:51 2026 -0800 +++ b/dowa/d_memory.c Fri Jan 02 17:47:10 2026 -0800 @@ -1,5 +1,9 @@ #include "dowa.h" +#ifndef DOWA_ALIGNMENT + #define DOWA_ALIGNMENT sizeof(void*) * 2 +#endif + // --- Arena --- // Dowa_Arena *Dowa_Arena_Create(size_t capacity) { @@ -23,7 +27,7 @@ void *Dowa_Arena_Allocate(Dowa_Arena *p_arena, size_t size) { - return Dowa_Arena_Allocate_Aligned(p_arena, size, sizeof(void*) * 2); + return Dowa_Arena_Allocate_Aligned(p_arena, size, DOWA_ALIGNMENT); } void Dowa_Arena_Free(Dowa_Arena *p_arena) @@ -85,9 +89,9 @@ return p_result; } -// --- NEW stb_ds-style Array Implementation --- // +// --- Array Implementation --- // -void* dowa__array_grow(void* p_array, size_t element_size, size_t minimum_capacity, Dowa_Arena* p_arena) +void *dowa__array_grow(void* p_array, size_t element_size, size_t minimum_capacity, Dowa_Arena* p_arena) { Dowa_Array_Header* p_header; size_t new_capacity; @@ -109,7 +113,7 @@ current_capacity = 0; } - // Calculate needed capacity: if minimum_capacity is 0, we need room for at least one more element + // if minimum_capacity is 0, we need room for at least one more element size_t needed_capacity = minimum_capacity; if (p_array && needed_capacity == 0) needed_capacity = p_header->length + 1; @@ -127,9 +131,7 @@ if (p_arena) { - // Array header and data must be properly aligned - size_t alignment = element_size >= 16 ? 16 : (element_size >= 8 ? 8 : element_size); - Dowa_Array_Header* p_new_header = (Dowa_Array_Header*)Dowa_Arena_Allocate_Aligned(p_arena, total_size, alignment); + Dowa_Array_Header* p_new_header = (Dowa_Array_Header*)Dowa_Arena_Allocate_Aligned(p_arena, total_size, DOWA_ALIGNMENT); if (!p_new_header) return p_array; @@ -191,7 +193,7 @@ free(p_header); } -// --- NEW stb_ds-style HashMap Implementation --- // +// --- HashMap Implementation --- // #define DOWA_HASH_EMPTY 0xFFFFFFFF #define DOWA_HASH_TOMBSTONE 0xFFFFFFFE @@ -219,7 +221,7 @@ return (Dowa_Hash_Index*)p_header->p_hash; } -static void* dowa__hashmap_find_slot(void* p_map, size_t element_size, void* p_key, size_t key_size, uint32 hash, boolean* p_found) +static void *dowa__hashmap_find_slot(void *p_map, size_t element_size, void *p_key, size_t key_size, uint32 hash, boolean *p_found) { Dowa_Hash_Index* p_index = dowa__hashmap_get_index(p_map); if (!p_index || !p_index->p_buckets) @@ -247,11 +249,11 @@ if (p_bucket->hash[i] == hash && p_bucket->index[i] != DOWA_HASH_TOMBSTONE) { uint32 array_index = p_bucket->index[i]; - char* p_element = (char*)p_map + (array_index * element_size); + char *p_element = (char*)p_map + (array_index * element_size); - char** p_stored_key_ptr = (char**)p_element; - char* p_stored_key = *p_stored_key_ptr; - char* p_search_key = (char*)p_key; + char **p_stored_key_ptr = (char**)p_element; + char *p_stored_key = *p_stored_key_ptr; + char *p_search_key = (char*)p_key; if (memcmp(p_stored_key, p_search_key, key_size) == 0) { @@ -272,7 +274,7 @@ return NULL; } -void* dowa__hashmap_get_ptr(void* p_map, size_t element_size, void* p_key, size_t key_size) +void *dowa__hashmap_get_ptr(void *p_map, size_t element_size, void *p_key, size_t key_size) { if (!p_map) return NULL; @@ -284,21 +286,21 @@ return found ? p_result : NULL; } -void* dowa__hashmap_get(void* p_map, size_t element_size, void* p_key, size_t key_size) +void *dowa__hashmap_get(void *p_map, size_t element_size, void *p_key, size_t key_size) { - void* p_kv = dowa__hashmap_get_ptr(p_map, element_size, p_key, key_size); + void *p_kv = dowa__hashmap_get_ptr(p_map, element_size, p_key, key_size); if (!p_kv) return NULL; return (char*)p_kv + key_size; } -boolean dowa__hashmap_has_key(void* p_map, size_t element_size, void* p_key, size_t key_size) +boolean dowa__hashmap_has_key(void *p_map, size_t element_size, void *p_key, size_t key_size) { return dowa__hashmap_get_ptr(p_map, element_size, p_key, key_size) != NULL; } -static void* dowa__hashmap_rehash(void* p_map, size_t element_size, size_t new_bucket_count, Dowa_Arena* p_arena) +static void* dowa__hashmap_rehash(void *p_map, size_t element_size, size_t new_bucket_count, Dowa_Arena* p_arena) { Dowa_Hash_Index* p_old_index = dowa__hashmap_get_index(p_map); if (!p_old_index) @@ -397,7 +399,7 @@ return p_map; } -void* dowa__hashmap_push(void* p_map, size_t element_size, void* p_key, size_t key_size, Dowa_Arena* p_arena) +void *dowa__hashmap_push(void *p_map, size_t element_size, void *p_key, size_t key_size, Dowa_Arena* p_arena) { uint32 hash = dowa__hash_bytes(p_key, key_size); @@ -541,7 +543,7 @@ return p_map; } -void dowa__hashmap_delete(void* p_map, size_t element_size, void* p_key, size_t key_size) +void dowa__hashmap_delete(void *p_map, size_t element_size, void *p_key, size_t key_size) { if (!p_map) return; @@ -592,7 +594,7 @@ } } -void dowa__hashmap_clear(void* p_map, size_t element_size) +void dowa__hashmap_clear(void *p_map, size_t element_size) { if (!p_map) return; @@ -616,7 +618,7 @@ } } -void dowa__hashmap_free(void* p_map) +void dowa__hashmap_free(void *p_map) { if (!p_map) return; @@ -632,7 +634,7 @@ dowa__array_free(p_map); } -size_t dowa__hashmap_count(void* p_map) +size_t dowa__hashmap_count(void *p_map) { if (!p_map) return 0; diff -r 19cccf6e866a -r 655ea0b661fd dowa/d_string.c --- a/dowa/d_string.c Thu Jan 01 16:34:51 2026 -0800 +++ b/dowa/d_string.c Fri Jan 02 17:47:10 2026 -0800 @@ -141,7 +141,29 @@ { char *buffer = Dowa_Arena_Allocate(p_arena, sizeof(char*) * strlen(from) + 1); if (buffer); - memcpy(buffer, from, strlen(from) + 1); + memcpy(buffer, from, strlen(from)); + buffer[strlen(from)] = '\0'; return buffer; } +char *Dowa_String_UUID(uint32 seed, void *buffer) +{ + char *res = buffer ? buffer : malloc(sizeof(char)*37); + if (!res) + return NULL; + + const char *POOL = "abcdef0123456789"; + int32 i = 0; + while (i < 37) + { + if (i == 8 || i == 13 || i == 18 || i == 23) + { + res[i++] = '-'; + continue; + } + seed = Dowa_Math_Random_Uint32(seed); + res[i++] = POOL[seed % 16]; + } + res[36] = '\0'; + return res; +} diff -r 19cccf6e866a -r 655ea0b661fd dowa/dowa.h --- a/dowa/dowa.h Thu Jan 01 16:34:51 2026 -0800 +++ b/dowa/dowa.h Fri Jan 02 17:47:10 2026 -0800 @@ -1,18 +1,12 @@ #ifndef DOWA #define DOWA -#include +#include // printfs... #include // strdup, strlen, memcmp, memcpy #include // malloc, free, realloc #include // mostly for TODO #include // size_t -#ifdef DIRECTORY -// Only for linux and mac since window do not support. -// Maybe move this into seobeo file directly? -#include // some functions loop through files -#endif - #include // I am not re-writing stuff I guess... #include @@ -47,7 +41,6 @@ } \ } while (0) -// Fixed-width integer types typedef unsigned long long uint64; typedef long long int64; typedef unsigned int uint32; @@ -74,30 +67,26 @@ DLAPI size_t Dowa_Arena_Get_Used(Dowa_Arena *p_arena); DLAPI size_t Dowa_Arena_Get_Remaining(Dowa_Arena *p_arena); -// --- New stb_ds-style Data Structures --- // +// --- Array and HashMap --- // -// Allocator type enum typedef enum { DOWA_ALLOCATOR_MALLOC = 0, DOWA_ALLOCATOR_ARENA = 1 } Dowa_Allocator_Type; -// Array header (prefix to actual array data) typedef struct { size_t length; size_t capacity; uint8 allocator_type; - Dowa_Arena* p_arena; - void* p_hash; + Dowa_Arena *p_arena; + void *p_hash; } Dowa_Array_Header; -// Hash bucket (64 bytes for cache line alignment) typedef struct { uint32 hash[DOWA_HASH_BUCKET_SIZE]; uint32 index[DOWA_HASH_BUCKET_SIZE]; } Dowa_Hash_Bucket; -// Hash index structure (separate from value array) typedef struct { size_t bucket_count; size_t item_count; @@ -107,10 +96,8 @@ Dowa_Hash_Bucket* p_buckets; } Dowa_Hash_Index; -// Key-Value pair declaration macro #define Dowa_KV(K, V) struct { K key; V value; } -// Internal header accessor #define dowa__header(a) ((Dowa_Array_Header*)(a) - 1) // --- Array Macros --- // @@ -149,7 +136,7 @@ #define Dowa_HashMap_Push(m, k, v) \ do { \ (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), strlen(k) + 1, NULL); \ - void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), strlen(k) + 1); \ + void *p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), strlen(k) + 1); \ if (p_kv_temp) \ ((typeof(m))p_kv_temp)->value = (v); \ } while (0) @@ -157,7 +144,7 @@ #define Dowa_HashMap_Push_Arena(m, k, v, arena) \ do { \ (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), strlen(k) + 1, (arena)); \ - void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), strlen(k) + 1); \ + void *p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), strlen(k) + 1); \ if (p_kv_temp) \ ((typeof(m))p_kv_temp)->value = (v); \ } while (0) @@ -183,7 +170,7 @@ #define Dowa_HashMap_Push_Binary(m, k, ksize, v) \ do { \ (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), (ksize), NULL); \ - void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), (ksize)); \ + void *p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), (ksize)); \ if (p_kv_temp) \ ((typeof(m))p_kv_temp)->value = (v); \ } while (0) @@ -191,37 +178,40 @@ #define Dowa_HashMap_Push_Binary_Arena(m, k, ksize, v, arena) \ do { \ (m) = dowa__hashmap_push((m), sizeof(*(m)), (k), (ksize), (arena)); \ - void* p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), (ksize)); \ + void *p_kv_temp = dowa__hashmap_get_ptr((m), sizeof(*(m)), (k), (ksize)); \ if (p_kv_temp) \ ((typeof(m))p_kv_temp)->value = (v); \ } while (0) // --- Array Core Functions --- // -DLAPI void* dowa__array_grow(void* p_array, size_t element_size, size_t minimum_capacity, Dowa_Arena* p_arena); -DLAPI void dowa__array_free(void* p_array); +DLAPI void *dowa__array_grow(void *p_array, size_t element_size, size_t minimum_capacity, Dowa_Arena* p_arena); +DLAPI void dowa__array_free(void *p_array); // --- HashMap Core Functions --- // -DLAPI uint32 dowa__hash_bytes(void* p_key, size_t key_size); -DLAPI void* dowa__hashmap_get(void* p_map, size_t element_size, void* p_key, size_t key_size); -DLAPI void* dowa__hashmap_get_ptr(void* p_map, size_t element_size, void* p_key, size_t key_size); -DLAPI void* dowa__hashmap_push(void* p_map, size_t element_size, void* p_key, size_t key_size, Dowa_Arena* p_arena); -DLAPI boolean dowa__hashmap_has_key(void* p_map, size_t element_size, void* p_key, size_t key_size); -DLAPI void dowa__hashmap_delete(void* p_map, size_t element_size, void* p_key, size_t key_size); -DLAPI void dowa__hashmap_clear(void* p_map, size_t element_size); -DLAPI void dowa__hashmap_free(void* p_map); -DLAPI size_t dowa__hashmap_count(void* p_map); +DLAPI uint32 dowa__hash_bytes(void *p_key, size_t key_size); +DLAPI void *dowa__hashmap_get(void *p_map, size_t element_size, void *p_key, size_t key_size); +DLAPI void *dowa__hashmap_get_ptr(void *p_map, size_t element_size, void *p_key, size_t key_size); +DLAPI void *dowa__hashmap_push(void *p_map, size_t element_size, void *p_key, size_t key_size, Dowa_Arena* p_arena); +DLAPI boolean dowa__hashmap_has_key(void *p_map, size_t element_size, void *p_key, size_t key_size); +DLAPI void dowa__hashmap_delete(void *p_map, size_t element_size, void *p_key, size_t key_size); +DLAPI void dowa__hashmap_clear(void *p_map, size_t element_size); +DLAPI void dowa__hashmap_free(void *p_map); +DLAPI size_t dowa__hashmap_count(void *p_map); // --- String Manipulation --- // -DLAPI char *Dowa_String_Slice(char *from, size_t start, size_t end, Dowa_Arena *p_arena); -DLAPI char **Dowa_String_Split(char *from, char *token, int32 from_length, int32 token_length, Dowa_Arena *p_arena); -DLAPI char *Dowa_String_Copy_Arena(char *from, Dowa_Arena *p_arena); +DLAPI char *Dowa_String_Slice(char *from, size_t start, size_t end, Dowa_Arena *p_arena); +DLAPI char **Dowa_String_Split(char *from, char *token, int32 from_length, int32 token_length, Dowa_Arena *p_arena); +DLAPI char *Dowa_String_Copy_Arena(char *from, Dowa_Arena *p_arena); DLAPI int32 Dowa_String_Pos_Find(const char *p_from, const char *p_value, const size_t from_length, const size_t value_length); DLAPI char *Dowa_String_Find(const char *p_from, const char *p_value, const size_t from_length, const size_t value_length); DLAPI int32 Dowa_String_Pos_Find_Char(const char *p_from, int c, int32 from_length); DLAPI char *Dowa_String_Find_Char(const char *p_from, int c, int32 from_length); +DLAPI char *Dowa_String_UUID(uint32 seed, void *buffer); +// --- Math --- // +DLAPI uint32 Dowa_Math_Random_Uint32(uint32 seed_number); #endif diff -r 19cccf6e866a -r 655ea0b661fd dowa/dowa_test.c --- a/dowa/dowa_test.c Thu Jan 01 16:34:51 2026 -0800 +++ b/dowa/dowa_test.c Fri Jan 02 17:47:10 2026 -0800 @@ -2,8 +2,8 @@ #include #include #include -#define DIRECTORY #include "dowa.h" +#include int main(void) { @@ -296,6 +296,36 @@ Dowa_Arena_Free(p_arena); } + printf("\n Arena Copy string\n\n"); + { + char *from = "copy_this"; + Dowa_Arena *p_arena = Dowa_Arena_Create(1024); + + char *value = Dowa_String_Copy_Arena(from, p_arena); + assert(strcmp(value, from) == 0); + assert(value[9] == '\0'); + + Dowa_Arena_Free(p_arena); + } + + + printf("\n UUID\n\n"); + { + char *uuid = Dowa_String_UUID((uint32)time(NULL), NULL); + printf("UUID %s\n", uuid); + } + + printf("=== Math/Random === \n\n"); + + printf("\n RandomNumberGenerator\n\n"); + { + uint32 seed_number = 32; + uint32 random_number = Dowa_Math_Random_Uint32(seed_number); + uint32 random_number2 = Dowa_Math_Random_Uint32(random_number); + printf("randon_number 1: %i\n", random_number); + printf("randon_number 2: %i\n", random_number2); + } + printf("=== All tests passed! ===\n"); return 0; } diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/main.c --- a/mrjunejune/main.c Thu Jan 01 16:34:51 2026 -0800 +++ b/mrjunejune/main.c Fri Jan 02 17:47:10 2026 -0800 @@ -1,6 +1,12 @@ #include "seobeo/seobeo.h" +#include + +// UUID + /tmp/ + format (max 4) +#define TMP_FILE_LENGTH 47 +#define UUID_LEN 37 volatile sig_atomic_t stop_server = 0; +static _Atomic uint32_t counter = 0; void handle_sigint(int sig) { @@ -84,23 +90,348 @@ Seobeo_Request_Entry* GetMDToHTML(Seobeo_Request_Entry *req, Dowa_Arena *arena) { - Seobeo_Request_Entry *resp = NULL; + Seobeo_Request_Entry *resp = NULL; char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024); Seobeo_ServerSideRender(final_body, "/tools/markdown_to_html/index.html", arena); Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); return resp; } -Seobeo_Request_Entry* GetUser(Seobeo_Request_Entry *req, Dowa_Arena *arena) +Seobeo_Request_Entry* GetFileConverter(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, "/tools/file_converter/index.html", arena); + Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); + return resp; +} + +Seobeo_Request_Entry *ConvertImageToWebP(Seobeo_Request_Entry *req, Dowa_Arena *arena) { - void *id_kv = Dowa_HashMap_Get_Ptr(req, ":id"); - const char *user_id = id_kv ? ((Seobeo_Request_Entry*)id_kv)->value : "unknown"; + Seobeo_Request_Entry *resp = NULL; + + if (!req) + { + printf("ERROR: Request is NULL\n"); + char *error_msg = "Internal error: no request data"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + size_t req_length = Dowa_Array_Length(req); + printf("Request has %zu entries\n", req_length); + + for (size_t i = 0; i < req_length; i++) + { + printf(" Key[%zu]: '%s'\n", i, req[i].key); + } + + void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body"); + if (!body_kv) + { + printf("ERROR: No 'Body' key found in request\n"); + + char *error_msg = "No file data provided"; + Dowa_HashMap_Push_Arena(resp, "status", "400", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + void *cl_kv = Dowa_HashMap_Get_Ptr(req, "Content-Length"); + if (!cl_kv) + { + char *error_msg = "No Content-Length header"; + Dowa_HashMap_Push_Arena(resp, "status", "400", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + const char *file_data = ((Seobeo_Request_Entry*)body_kv)->value; + const char *content_length_str = ((Seobeo_Request_Entry*)cl_kv)->value; + size_t file_size = atoi(content_length_str); + + printf("DEBUG: Converting image, file_size=%zu bytes\n", file_size); + + + int open_flags = O_RDWR | O_CREAT | O_EXCL; + + char *uuid4 = (char *)Dowa_Arena_Allocate(arena, UUID_LEN); + uint32 seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++; + Dowa_String_UUID(seed, uuid4); + char *input_path = Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH);; + snprintf(input_path, TMP_FILE_LENGTH, "/tmp/%s", uuid4); + int input_fd = open(input_path, open_flags, 0600); + if (input_fd == -1) + { + char *error_msg = "Failed to create temporary file"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + write(input_fd, file_data, file_size); + close(input_fd); + + + uuid4 = (char *)Dowa_Arena_Allocate(arena, UUID_LEN); + seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++; + Dowa_String_UUID(seed, uuid4); + char *output_path = (char *)Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH);; + snprintf(output_path, TMP_FILE_LENGTH, "/tmp/%s.webp", uuid4); + printf("[DEBUG] output_path %s\n", output_path); + printf("[DEBUG] open_flags: 0x%x\n", open_flags); + printf("[DEBUG] input_path: %s\n", input_path); + int output_fd = open(output_path, open_flags, 0600); + printf("[DEBUG] output_fd: %d\n", output_fd); + if (output_fd == -1) + { + unlink(input_path); + printf("[DEBUG] errno: %d (%s)\n", errno, strerror(errno)); + char *error_msg = "Failed to create output file"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + close(output_fd); + + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "ffmpeg -y -i %s -quality 80 %s 2>/tmp/error_log", + input_path, output_path); + int result = system(cmd); + if (result != 0) + { + unlink(input_path); + unlink(output_path); + char *error_msg = "FFmpeg conversion failed"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + size_t converted_size = 0; + FILE *out_file = fopen(output_path, "rb"); + if (!out_file) + { + unlink(input_path); + unlink(output_path); + char *error_msg = "Failed to read converted file"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + fclose(out_file); + + unlink(input_path); + + char *filename = strrchr(output_path, '/') + 1; + char *response_body = Dowa_Arena_Allocate(arena, 512); + snprintf(response_body, 512, + "{\"success\":true,\"download_url\":\"/api/download/%s\",\"expires\":\"10 minutes\"}", + filename); + Dowa_HashMap_Push_Arena(resp, "status", "200", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena); + Dowa_HashMap_Push_Arena(resp, "body", response_body, arena); + + printf("DEBUG: Image converted, available at /api/download/%s\n", filename); + + return resp; +} + +Seobeo_Request_Entry *ConvertVideoToMP4(Seobeo_Request_Entry *req, Dowa_Arena *arena) +{ + Seobeo_Request_Entry *resp = NULL; + + void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body"); + if (!body_kv) + { + char *error_msg = "No file data provided"; + Dowa_HashMap_Push_Arena(resp, "status", "400", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + // Get Content-Length to know the actual binary size + void *cl_kv = Dowa_HashMap_Get_Ptr(req, "Content-Length"); + if (!cl_kv) + { + char *error_msg = "No Content-Length header"; + Dowa_HashMap_Push_Arena(resp, "status", "400", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + const char *file_data = ((Seobeo_Request_Entry*)body_kv)->value; + const char *content_length_str = ((Seobeo_Request_Entry*)cl_kv)->value; + size_t file_size = atoi(content_length_str); + + printf("DEBUG: Converting video, file_size=%zu bytes\n", file_size); + + char *uuid4 = (char *)Dowa_Arena_Allocate(arena, UUID_LEN); + Dowa_String_UUID((uint32)time(NULL), uuid4); + char *input_path = Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH); + snprintf(input_path, TMP_FILE_LENGTH, "/tmp/%s", uuid4); + int input_fd = mkstemp(input_path); + if (input_fd == -1) + { + char *error_msg = "Failed to create temporary file"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + write(input_fd, file_data, file_size); + close(input_fd); + + int open_flags = O_RDWR | O_CREAT | O_EXCL; + + uint32 seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;; + Dowa_String_UUID(seed, uuid4); + char *output_path = (char *)Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH);; + snprintf(output_path, TMP_FILE_LENGTH, "/tmp/%s.mp4", uuid4); + int output_fd = open(output_path, open_flags, 0600); + if (output_fd == -1) + { + unlink(input_path); + char *error_msg = "Failed to create output file"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + close(output_fd); + + char cmd[512]; + snprintf(cmd, sizeof(cmd), + "ffmpeg -y -i %s -c:v libx264 -preset fast -crf 23 -c:a aac %s 2>/tmp/error_log", + input_path, output_path); + int result = system(cmd); + if (result != 0) + { + unlink(input_path); + unlink(output_path); + char *error_msg = "FFmpeg conversion failed"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + unlink(input_path); + char *filename = strrchr(output_path, '/') + 1; + char *response_body = Dowa_Arena_Allocate(arena, 512); + snprintf(response_body, 512, + "{\"success\":true,\"download_url\":\"/api/download/%s\",\"expires\":\"10 minutes\"}", + filename); + Dowa_HashMap_Push_Arena(resp, "status", "200", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena); + Dowa_HashMap_Push_Arena(resp, "body", response_body, arena); + + printf("DEBUG: Video converted, available at /api/download/%s\n", filename); + return resp; +} + +Seobeo_Request_Entry *DownloadConvertedFile(Seobeo_Request_Entry *req, Dowa_Arena *arena) +{ Seobeo_Request_Entry *resp = NULL; - char *body = Dowa_Arena_Allocate(arena, 256); - snprintf(body, 256, "{\"id\":\"%s\",\"name\":\"John Doe\"}", user_id); + + void *filename_kv = Dowa_HashMap_Get_Ptr(req, ":filename"); + if (!filename_kv) + { + char *error_msg = "No filename specified"; + Dowa_HashMap_Push_Arena(resp, "status", "404", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + const char *filename = ((Seobeo_Request_Entry*)filename_kv)->value; + + // TODO: Maybe check if the uuid is allowed or not? + // if (strlen(filename) != TMP_FILE_LENGTH) + // { + // char *error_msg = "Not Allowed Filename"; + // Dowa_HashMap_Push_Arena(resp, "status", "404", arena); + // Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + // Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + // return resp; + // } + // boolean allowed = FALSE; + // for (int i = 0; i < Dowa_Array_Length(g_uuid4_array); i++) + // { + // if strcmp(g_uuid4_array, filename) + // { + // allowed = TRUE; + // break; + // } + // g_uuid4_array++; + // } + + char filepath[512]; + snprintf(filepath, sizeof(filepath), "/tmp/%s", filename); + FILE *file = fopen(filepath, "rb"); + if (!file) + { + char *error_msg = "File not found or expired"; + Dowa_HashMap_Push_Arena(resp, "status", "404", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + char *file_data = malloc(file_size + 1); + if (!file_data) + { + fclose(file); + char *error_msg = "Memory allocation failed"; + Dowa_HashMap_Push_Arena(resp, "status", "500", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); + Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena); + return resp; + } + + fread(file_data, 1, file_size, file); + file_data[file_size] = '\0'; + fclose(file); + + const char *content_type = "application/octet-stream"; + if (strstr(filename, ".webp")) + content_type = "image/webp"; + else if (strstr(filename, ".mp4")) + content_type = "video/mp4"; + + char *body = Dowa_Arena_Allocate(arena, file_size + 1); + memcpy(body, file_data, file_size); + body[file_size] = '\0'; + free(file_data); + + unlink(filepath); + + printf("DEBUG: Served and deleted file: %s (%zu bytes)\n", filename, file_size); + + // Set proper Content-Length for binary data + char *content_length = Dowa_Arena_Allocate(arena, 32); + snprintf(content_length, 32, "%zu", file_size); + + Dowa_HashMap_Push_Arena(resp, "status", "200", arena); + Dowa_HashMap_Push_Arena(resp, "content-type", content_type, arena); + Dowa_HashMap_Push_Arena(resp, "content-length", content_length, arena); Dowa_HashMap_Push_Arena(resp, "body", body, arena); + return resp; } @@ -108,6 +439,7 @@ 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") int main(void) { @@ -124,5 +456,12 @@ Seobeo_Router_Register("GET", "/tools/markdown_to_html", GetMDToHTML); Seobeo_Router_Register("GET", "/tools/markdown_to_html/index.html", GetRedirectMarkDownToHtml); + Seobeo_Router_Register("GET", "/tools/file_converter", GetFileConverter); + Seobeo_Router_Register("GET", "/tools/file_converter/index.html", GetRedirectFileConverter); + + Seobeo_Router_Register("POST", "/api/convert/image-to-webp", ConvertImageToWebP); + Seobeo_Router_Register("POST", "/api/convert/video-to-mp4", ConvertVideoToMP4); + Seobeo_Router_Register("GET", "/api/download/:filename", DownloadConvertedFile); + Seobeo_Web_Server_Start("mrjunejune/src", "6969", SEOBEO_MODE_EDGE, 3); } diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/base.css --- a/mrjunejune/src/base.css Thu Jan 01 16:34:51 2026 -0800 +++ b/mrjunejune/src/base.css Fri Jan 02 17:47:10 2026 -0800 @@ -1,4 +1,26 @@ -/* Base Colors */ +/* Reset CSS: https://meyerweb.com/eric/tools/css/reset/ */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* --- Colors ---*/ :root { --white: #ffffff; --black: hsl(224, 12%, 4%); @@ -49,11 +71,32 @@ --box-shadow: 0 -2px -6px rgba(159, 140, 96, 25%), 0 -8px -24px rgba(159, 140, 96, 33%), 0 -16px -32px rgba(159, 140, 96, 33%); } - -html { - background: var(--white); +@media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --white: hsl(224, 10%, 10%); + --black: hsl(0, 0%, 90%); + --darkgray: #cccccc; + --lightgray: #666666; + --green: #3d1ea0; + --orange: #025ccc; + --purple: #2b5b06; + --red: #04bb7a; + --blue: #932f0e; + --darktext: #bebebe; + --awesome: #23cade; + --accent: #ffcc00; + --accent-dark: #ffb275; + --gray: 159, 140, 96; + --gray-light: 26, 22, 15; + --gray-dark: 221, 214, 198; + --gray-gradient: rgba(26, 22, 15, 50%), #000; + --box-shadow: 0 -2px -6px rgba(159, 140, 96, 25%), + 0 -8px -24px rgba(159, 140, 96, 33%), 0 -16px -32px rgba(159, 140, 96, 33%); + } } +/* --- fonts --- */ +/* @font-face { font-family: "Atkinson"; src: url("/public/fonts/atkinson-regular.woff") format("woff"); @@ -66,6 +109,7 @@ font-weight: 700; font-style: normal; } +*/ @font-face { font-family: "Roboto"; src: url("/public/fonts/Roboto-Regular.ttf"); @@ -83,6 +127,11 @@ src: url("/public/fonts/more-sugar.regular.otf"); } +/* -- HTML Tags only --*/ +html { + background: var(--white); +} + a { color: inherit; /* blue colors for links too */ text-decoration: inherit; /* no underline */ @@ -120,125 +169,91 @@ color: rgb(var(--black)); line-height: 1.2; } + h1 { font-size: 3.052em; } + h2 { font-size: 2.441em; } + h3 { font-size: 1.953em; } + h4 { font-size: 1.563em; } + h5 { font-size: 1.25em; } + strong, b { font-weight: 700; } + p { margin-bottom: 1em; } + .prose p { margin-bottom: 2em; } + textarea { width: 100%; font-size: 16px; } + input { font-size: 16px; } + table { width: 100%; } + img { max-width: 100%; height: auto; border-radius: 8px; } + code { padding: 2px 5px; background-color: rgb(var(--gray-light)); border-radius: 2px; } + pre { padding: 1.5em; border-radius: 8px; } + pre > code { all: unset; } + blockquote { border-left: 4px solid var(--accent); padding: 0 0 0 20px; margin: 0px; font-size: 1.333em; } + hr { border: none; border-top: 1px solid rgb(var(--gray-light)); } -@media (max-width: 720px) { - body { - font-size: 18px; - } - main { - padding: 1em; - } -} - -.sr-only { - border: 0; - padding: 0; - margin: 0; - position: absolute !important; - height: 1px; - width: 1px; - overflow: hidden; - /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */ - clip: rect(1px 1px 1px 1px); - /* maybe deprecated but we need to support legacy browsers */ - clip: rect(1px, 1px, 1px, 1px); - /* modern browsers, clip-path works inwards from each corner */ - clip-path: inset(50%); - /* added line to stop words getting smushed together (as they go onto separate lines and some screen readers do not understand line feeds as a space */ - white-space: nowrap; -} .center { display: flex; justify-content: center; } -/* Auto dark mode based on system preference */ -@media (prefers-color-scheme: dark) { - :root:not(.light-mode) { - --white: hsl(224, 10%, 10%); - --black: hsl(0, 0%, 90%); - --darkgray: #cccccc; - --lightgray: #666666; - --green: #3d1ea0; - --orange: #025ccc; - --purple: #2b5b06; - --red: #04bb7a; - --blue: #932f0e; - --darktext: #bebebe; - --awesome: #23cade; - --accent: #ffcc00; - --accent-dark: #ffb275; - --gray: 159, 140, 96; - --gray-light: 26, 22, 15; - --gray-dark: 221, 214, 198; - --gray-gradient: rgba(26, 22, 15, 50%), #000; - --box-shadow: 0 -2px -6px rgba(159, 140, 96, 25%), - 0 -8px -24px rgba(159, 140, 96, 33%), 0 -16px -32px rgba(159, 140, 96, 33%); - } -} - a { text-decoration: underline; } @@ -250,13 +265,9 @@ height: 1em; margin-right: 8px; vertical-align: middle; - - /* This treats the paw like a stencil */ - background-color: #FF69B4; /* Change your color here! (e.g., Hot Pink) */ + background-color: #FF69B4; -webkit-mask: url('/public/paw.svg') no-repeat center; mask: url('/public/paw.svg') no-repeat center; - - /* Your 2D Animation */ animation: pawStep 0.6s ease-out forwards; } @@ -273,3 +284,16 @@ 0%, 100% { transform: rotate(-15deg); } 50% { transform: rotate(15deg); } } + + +/* -- mobile -- */ +@media (max-width: 720px) { + body { + font-size: 18px; + } + main { + padding: 1em; + } +} + + diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/index.html --- a/mrjunejune/src/index.html Thu Jan 01 16:34:51 2026 -0800 +++ b/mrjunejune/src/index.html Fri Jan 02 17:47:10 2026 -0800 @@ -7,6 +7,7 @@ .epi-photo { display: flex; justify-content: center; + margin-bottom: 10px; } @@ -22,7 +23,7 @@ -

During my free time, I like to write codes mostly in C, typescript and python.

+

During my free time, I like to write codes mostly in C, Python, and Typescript.

This website is hosted using my own server library which I wrote in C.

Links

@@ -30,6 +31,7 @@
  • Repository - Check out my code (MIT License)
  • Resume - My professional experiences
  • Tools - Tools
  • + diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/parts/header.html --- a/mrjunejune/src/parts/header.html Thu Jan 01 16:34:51 2026 -0800 +++ b/mrjunejune/src/parts/header.html Fri Jan 02 17:47:10 2026 -0800 @@ -28,7 +28,7 @@ header { margin: auto; padding: 1.5em 1em; - font-family: "Atkinson", sans-serif; + font-family: "More", sans-serif; box-shadow: 0 2px 8px rgba(var(--black), 5%); width: 720px; max-width: calc(100% - 2em); @@ -36,7 +36,6 @@ } header h1 { - font-family: "More", sans-serif; margin: 0; font-size: 1.8em; font-weight: 700; diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/public/.DS_Store Binary file mrjunejune/src/public/.DS_Store has changed diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/resume/index.html --- a/mrjunejune/src/resume/index.html Thu Jan 01 16:34:51 2026 -0800 +++ b/mrjunejune/src/resume/index.html Fri Jan 02 17:47:10 2026 -0800 @@ -6,9 +6,11 @@ + {{/parts/header.html}}
    Download PDF +

    JUNTAE PARK

    SOFTWARE ENGINEER

    diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/tools/file_converter/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/src/tools/file_converter/index.html Fri Jan 02 17:47:10 2026 -0800 @@ -0,0 +1,267 @@ + + + + + + File Format Converter + {{/parts/base_head.html}} + + + + {{/parts/header.html}} + +
    +

    File Format Converter

    +

    Convert your images and videos to different formats using FFmpeg

    + +
    +

    Image to WebP Converter

    +

    Upload an image file (PNG, JPG, GIF, etc.) to convert it to WebP format

    + +
    + + +
    + + + +
    Converting... Please wait.
    + + +
    + +
    +

    Video to MP4 Converter

    +

    Upload a video file (AVI, MOV, MKV, etc.) to convert it to MP4 format

    + +
    + + +
    + + + +
    Converting... Please wait.
    + + +
    +
    + + + + diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/tools/index.html --- a/mrjunejune/src/tools/index.html Thu Jan 01 16:34:51 2026 -0800 +++ b/mrjunejune/src/tools/index.html Fri Jan 02 17:47:10 2026 -0800 @@ -9,7 +9,8 @@

    Tools

    diff -r 19cccf6e866a -r 655ea0b661fd mrjunejune/src/tools/markdown_to_html/index.css --- a/mrjunejune/src/tools/markdown_to_html/index.css Thu Jan 01 16:34:51 2026 -0800 +++ b/mrjunejune/src/tools/markdown_to_html/index.css Fri Jan 02 17:47:10 2026 -0800 @@ -64,8 +64,8 @@ .panel { background: var(--white); border-radius: 8px; + border: 1px dotted #ccc; padding: 20px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); } #output { diff -r 19cccf6e866a -r 655ea0b661fd seobeo/s_router.c --- a/seobeo/s_router.c Thu Jan 01 16:34:51 2026 -0800 +++ b/seobeo/s_router.c Fri Jan 02 17:47:10 2026 -0800 @@ -150,15 +150,25 @@ content_type = ((Seobeo_Request_Entry*)p_content_type_kv)->value; } + // Check for custom content-length (for binary data) + size_t body_length = strlen(body); + void *p_content_length_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-length"); + if (p_content_length_kv) + { + const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value; + body_length = atoi(content_length_str); + } + // All other types are default thoguht to be headers. char *header = Dowa_Arena_Allocate(p_arena, 4096); - Seobeo_Web_Header_Generate(header, status, content_type, strlen(body)); + Seobeo_Web_Header_Generate(header, status, content_type, body_length); for (int i = 0; i < Dowa_Array_Length(p_response_map); i++) { if ( strstr(p_response_map[i].key, "status") || strstr(p_response_map[i].key, "body") || - strstr(p_response_map[i].key, "content-type") + strstr(p_response_map[i].key, "content-type") || + strstr(p_response_map[i].key, "content-length") // Skip custom content-length ) continue; @@ -170,7 +180,7 @@ } Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); - Seobeo_Handle_Queue(p_handle, (uint8_t*)body, strlen(body)); + Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); // Use actual body length Seobeo_Handle_Flush(p_handle); } diff -r 19cccf6e866a -r 655ea0b661fd seobeo/s_web.c --- a/seobeo/s_web.c Thu Jan 01 16:34:51 2026 -0800 +++ b/seobeo/s_web.c Fri Jan 02 17:47:10 2026 -0800 @@ -104,8 +104,13 @@ // Parse request headers into hashmap using arena Seobeo_Request_Entry *p_req_map = NULL; - if (Seobeo_Web_Header_Parse(p_cli_handle, &p_req_map, p_request_arena) != 0) + int parse_result = Seobeo_Web_Header_Parse(p_cli_handle, &p_req_map, p_request_arena); + + // Treat EAGAIN (return code 1) as success - headers were parsed, body may still be coming + if (parse_result != 0 && parse_result != 1) { + printf("ERROR: Seobeo_Web_Header_Parse failed with code %d\n", parse_result); + fflush(stdout); Seobeo_Web_Header_Generate(p_response_header, HTTP_BAD_REQUEST, "text/plain", 0); @@ -116,12 +121,20 @@ goto clean_up; } + printf("DEBUG: Parse completed with code %d\n", parse_result); + fflush(stdout); + // Extract method (GET, POST, etc.) void *p_method_kv = Dowa_HashMap_Get_Ptr(p_req_map, "HTTP_Method"); const char *method = p_method_kv ? ((Seobeo_Request_Entry*)p_method_kv)->value : NULL; + printf("DEBUG: Parsed request, method=%s\n", method ? method : "NULL"); + fflush(stdout); + if (!method) { + printf("ERROR: No HTTP method found in request\n"); + fflush(stdout); Seobeo_Web_Header_Generate(p_response_header, HTTP_BAD_REQUEST, "text/plain", 0); @@ -277,24 +290,53 @@ char *hdr_end = strstr(buf, "\r\n\r\n"); size_t hdr_len = hdr_end - buf + 4; + // Debug: Print the first line of the request + char *first_line_end = strstr(buf, "\r\n"); + if (first_line_end) + { + size_t first_line_len = first_line_end - buf; + printf("DEBUG: Request line (first %zu bytes): '", first_line_len > 200 ? 200 : first_line_len); + fwrite(buf, 1, first_line_len > 200 ? 200 : first_line_len, stdout); + printf("'\n"); + fflush(stdout); + } + // This seems kinda bad ? char method[16], path[256], version[16]; - if (sscanf(buf, "%15s %255s %15s", method, path, version) != 3) + int scan_result = sscanf(buf, "%15s %255s %15s", method, path, version); + printf("DEBUG: sscanf returned %d (method='%s', path='%s', version='%s')\n", + scan_result, + scan_result >= 1 ? method : "N/A", + scan_result >= 2 ? path : "N/A", + scan_result >= 3 ? version : "N/A"); + fflush(stdout); + + if (scan_result != 3) { + printf("ERROR: Failed to parse request line\n"); + fflush(stdout); return -1; } // Copy strings to arena and store in hashmap + printf("DEBUG: Allocating method_copy\n"); + fflush(stdout); char *method_copy = Dowa_Arena_Allocate(p_arena, strlen(method) + 1); - if (!method_copy) return -1; + if (!method_copy) { printf("ERROR: Failed to allocate method_copy\n"); return -1; } strcpy(method_copy, method); + printf("DEBUG: Allocating version_copy\n"); + fflush(stdout); char *version_copy = Dowa_Arena_Allocate(p_arena, strlen(version) + 1); - if (!version_copy) return -1; + if (!version_copy) { printf("ERROR: Failed to allocate version_copy\n"); return -1; } strcpy(version_copy, version); + printf("DEBUG: Pushing HTTP_Method and Version to map\n"); + fflush(stdout); Dowa_HashMap_Push_Arena(*pp_map, "HTTP_Method", method_copy, p_arena); Dowa_HashMap_Push_Arena(*pp_map, "Version", version_copy, p_arena); + printf("DEBUG: Map now has %zu entries\n", Dowa_Array_Length(*pp_map)); + fflush(stdout); // 1) Separate raw path and query string char *raw_path = path; @@ -394,22 +436,64 @@ { const char *content_length_str = ((Seobeo_Request_Entry*)p_cl_kv)->value; size_t body_len = atoi(content_length_str); - while (p_handle->read_buffer_len < body_len) + + printf("DEBUG: Content-Length=%zu, reading body in chunks...\n", body_len); + fflush(stdout); + + // Allocate buffer for entire body + char *body = Dowa_Arena_Allocate(p_arena, body_len + 1); + if (!body) { - int r = Seobeo_Handle_Read(p_handle); - if (r < 0) return -1; - if (r == 0) return 1; // wait for more data + printf("ERROR: Failed to allocate %zu bytes for body\n", body_len); + fflush(stdout); + return -1; } - char *body = Dowa_Arena_Allocate(p_arena, body_len + 1); - if (!body) return -1; - memcpy(body, p_handle->read_buffer, body_len); + size_t total_read = 0; + + // Read body in chunks + while (total_read < body_len) + { + // Copy what's currently in the read buffer + size_t available = p_handle->read_buffer_len; + size_t to_copy = (body_len - total_read) < available ? (body_len - total_read) : available; + + if (to_copy > 0) + { + memcpy(body + total_read, p_handle->read_buffer, to_copy); + total_read += to_copy; + Seobeo_Handle_Consume(p_handle, (uint32)to_copy); + + printf("DEBUG: Copied %zu bytes, total %zu/%zu\n", to_copy, total_read, body_len); + fflush(stdout); + } + + // If we still need more data, read another chunk + if (total_read < body_len) + { + int r = Seobeo_Handle_Read(p_handle); + if (r < 0) + { + printf("ERROR: Read failed with %d\n", r); + fflush(stdout); + return -1; + } + if (r == 0) + { + // No data available yet, continue waiting + // printf("DEBUG: Waiting for more data... (have %zu/%zu bytes)\n", total_read, body_len); + fflush(stdout); + continue; + } + } + } + body[body_len] = '\0'; + printf("DEBUG: Body fully received (%zu bytes)\n", body_len); + fflush(stdout); // Body is arena-allocated Dowa_HashMap_Push_Arena(*pp_map, "Body", body, p_arena); - - Seobeo_Handle_Consume(p_handle, (uint32)body_len); } return 0; // success; map now holds Method, Path, Version, headers, and optional Body diff -r 19cccf6e866a -r 655ea0b661fd sori/BUILD --- a/sori/BUILD Thu Jan 01 16:34:51 2026 -0800 +++ b/sori/BUILD Fri Jan 02 17:47:10 2026 -0800 @@ -4,7 +4,7 @@ name = "game", srcs = ["main.c"], deps = [ - "//dowa:dowa_generic", + "//dowa:dowa", "//third_party/raylib:raylib", ], )