Mercurial
comparison seobeo/s_web.c @ 71:75de5903355c
Giagantic changes that update Dowa library to be more align with stb style array and hashmap. Updated Seobeo to be caching on server side instead of file level caching. Deleted bunch of things I don't really use.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sun, 28 Dec 2025 20:34:22 -0800 |
| parents | 6626ec933933 |
| children | 4532ce6d9eb8 |
comparison
equal
deleted
inserted
replaced
| 70:4bc56e88e1f3 | 71:75de5903355c |
|---|---|
| 1 #include "seobeo/seobeo.h" | 1 #include "seobeo/seobeo.h" |
| 2 | |
| 3 // Global folder path for serving files | |
| 4 static char g_folder_path[512] = "."; | |
| 2 | 5 |
| 3 int Seobeo_Web_GenerateRequestHeader(void *buffer, const char *host, | 6 int Seobeo_Web_GenerateRequestHeader(void *buffer, const char *host, |
| 4 const char *path) | 7 const char *path) |
| 5 { | 8 { |
| 6 return sprintf( | 9 return sprintf( |
| 24 "\r\n", | 27 "\r\n", |
| 25 path, host | 28 path, host |
| 26 ); | 29 ); |
| 27 } | 30 } |
| 28 | 31 |
| 32 // Load file from disk and cache it | |
| 33 static char* Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size) | |
| 34 { | |
| 35 char full_path[1024]; | |
| 36 snprintf(full_path, sizeof(full_path), "%s/%s", g_folder_path, file_path); | |
| 37 | |
| 38 FILE *p_file = fopen(full_path, "rb"); | |
| 39 if (!p_file) | |
| 40 return NULL; | |
| 41 | |
| 42 fseek(p_file, 0, SEEK_END); | |
| 43 size_t file_size = ftell(p_file); | |
| 44 fseek(p_file, 0, SEEK_SET); | |
| 45 | |
| 46 char *p_content = (char*)malloc(file_size + 1); | |
| 47 if (!p_content) | |
| 48 { | |
| 49 fclose(p_file); | |
| 50 return NULL; | |
| 51 } | |
| 52 | |
| 53 fread(p_content, 1, file_size, p_file); | |
| 54 p_content[file_size] = '\0'; | |
| 55 fclose(p_file); | |
| 56 | |
| 57 if (p_file_size) | |
| 58 *p_file_size = file_size; | |
| 59 | |
| 60 return p_content; | |
| 61 } | |
| 62 | |
| 29 void Seobeo_Web_Header_Generate(void *buffer, int status, | 63 void Seobeo_Web_Header_Generate(void *buffer, int status, |
| 30 const char *content_type, const int content_length) | 64 const char *content_type, const int content_length) |
| 31 { | 65 { |
| 32 const char *status_text; | 66 const char *status_text; |
| 33 switch(status) | 67 switch(status) |
| 34 { | 68 { |
| 35 case HTTP_OK: status_text = "OK"; break; | 69 case HTTP_OK: status_text = "OK"; break; |
| 54 status, status_text, content_type, content_length | 88 status, status_text, content_type, content_length |
| 55 ); | 89 ); |
| 56 } | 90 } |
| 57 | 91 |
| 58 void Seobeo_Web_HandleClientRequest(Seobeo_Handle *p_cli_handle, | 92 void Seobeo_Web_HandleClientRequest(Seobeo_Handle *p_cli_handle, |
| 59 Dowa_HashMap *p_html_cache) | 93 Seobeo_Cache_Entry *p_html_cache) |
| 60 { | 94 { |
| 61 printf("p_cli_handle: %p", p_cli_handle); | 95 Dowa_Arena *p_request_arena = Dowa_Arena_Create(1*1024*1024); // 1MB for request parsing |
| 62 Dowa_HashEntry *entry = NULL; | 96 if (!p_request_arena) { perror("Dowa_Arena_Create request"); goto clean_up; } |
| 63 Dowa_HashMap *p_current = p_html_cache; | 97 |
| 64 char *slash; | 98 Dowa_Arena *p_response_arena = Dowa_Arena_Create(1*1024*1024); // 1MB for response |
| 65 | 99 if (!p_response_arena) { perror("Dowa_Arena_Create response"); goto clean_up; } |
| 66 Dowa_Arena *p_response_arena = Dowa_Arena_Create(1*1024*1024); | 100 |
| 67 if (!p_response_arena) { perror("Dowa_Arena_Initialize"); goto clean_up; } | 101 void *p_response_header = Dowa_Arena_Allocate(p_response_arena, (size_t)1024*5); // 5Kb |
| 68 | |
| 69 void *p_response_header = Dowa_Arena_Allocate(p_response_arena, (size_t)2048); | |
| 70 if (!p_response_header) { perror("Dowa_Arena_Allocate"); goto clean_up; } | 102 if (!p_response_header) { perror("Dowa_Arena_Allocate"); goto clean_up; } |
| 71 | 103 |
| 72 // Parse request headers into hashmap | 104 // Parse request headers into hashmap using arena |
| 73 Dowa_HashMap *p_req_map = Dowa_HashMap_Create_With_Arena(100, p_response_arena); | 105 Seobeo_Request_Entry *p_req_map = NULL; |
| 74 if (Seobeo_Web_Header_Parse(p_cli_handle, p_req_map) != 0) | 106 if (Seobeo_Web_Header_Parse(p_cli_handle, &p_req_map, p_request_arena) != 0) |
| 75 { | 107 { |
| 76 Seobeo_Web_Header_Generate(p_response_header, | 108 Seobeo_Web_Header_Generate(p_response_header, |
| 77 HTTP_BAD_REQUEST, | 109 HTTP_BAD_REQUEST, |
| 78 "text/plain", 0); | 110 "text/plain", 0); |
| 79 Seobeo_Handle_Queue(p_cli_handle, | 111 Seobeo_Handle_Queue(p_cli_handle, |
| 80 (const uint8*)p_response_header, | 112 (const uint8*)p_response_header, |
| 81 (uint32)strlen(p_response_header)); | 113 (uint32)strlen(p_response_header)); |
| 82 Seobeo_Handle_Flush(p_cli_handle); | 114 Seobeo_Handle_Flush(p_cli_handle); |
| 83 goto clean_up; | 115 goto clean_up; |
| 84 return; | 116 } |
| 85 } | |
| 86 | |
| 87 // Dowa_HashMap_Print(p_req_map); | |
| 88 | 117 |
| 89 // Extract method (GET, POST, etc.) | 118 // Extract method (GET, POST, etc.) |
| 90 const char *method = (const char*)Dowa_HashMap_Get(p_req_map, "HTTP_Method"); | 119 void *p_method_kv = Dowa_HashMap_Get_Ptr(p_req_map, "HTTP_Method"); |
| 120 const char *method = p_method_kv ? ((Seobeo_Request_Entry*)p_method_kv)->value : NULL; | |
| 121 | |
| 91 if (!method) | 122 if (!method) |
| 92 { | 123 { |
| 93 Seobeo_Web_Header_Generate(p_response_header, | 124 Seobeo_Web_Header_Generate(p_response_header, |
| 94 HTTP_BAD_REQUEST, | 125 HTTP_BAD_REQUEST, |
| 95 "text/plain", 0); | 126 "text/plain", 0); |
| 98 (uint32)strlen(p_response_header)); | 129 (uint32)strlen(p_response_header)); |
| 99 Seobeo_Handle_Flush(p_cli_handle); | 130 Seobeo_Handle_Flush(p_cli_handle); |
| 100 goto clean_up; | 131 goto clean_up; |
| 101 } | 132 } |
| 102 | 133 |
| 103 // --- Separate GET map for caching or routing --- | |
| 104 Dowa_HashMap *p_get_map = Dowa_HashMap_Create(64); | |
| 105 if (!p_get_map) | |
| 106 { | |
| 107 perror("Dowa_HashMap_Create (p_get_map)"); | |
| 108 goto clean_up; | |
| 109 } | |
| 110 | |
| 111 // --- Handle different HTTP methods --- | 134 // --- Handle different HTTP methods --- |
| 112 if (strcmp(method, "GET") == 0) | 135 if (strcmp(method, "GET") == 0) |
| 113 { | 136 { |
| 114 const char *path = (const char*)Dowa_HashMap_Get(p_req_map, "Path"); | 137 void *p_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); |
| 115 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)512); | 138 const char *path = p_kv ? ((Seobeo_Request_Entry*)p_kv)->value : NULL; |
| 139 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024); // 5Kb only for path | |
| 116 | 140 |
| 117 if (!path || strcmp(path, "/") == 0) | 141 if (!path || strcmp(path, "/") == 0) |
| 118 { | 142 { |
| 119 strcpy(file_path, "index.html"); | 143 strcpy(file_path, "index.html"); |
| 120 } | 144 } |
| 132 { | 156 { |
| 133 strcpy(file_path, path); | 157 strcpy(file_path, path); |
| 134 } | 158 } |
| 135 } | 159 } |
| 136 | 160 |
| 137 // Store path for GET handling map | 161 // Check if file is in cache, load if not |
| 138 Dowa_HashMap_Push_Value(p_get_map, "Path", file_path, strlen(file_path) + 1); | 162 void *p_file_kv = Dowa_HashMap_Get_Ptr(p_html_cache, file_path); |
| 139 | 163 const char *file_content = NULL; |
| 140 // Walk through nested maps to find content | 164 size_t body_size = 0; |
| 141 while ((slash = strchr(file_path, '/'))) | 165 |
| 142 { | 166 if (p_file_kv) |
| 143 *slash = '\0'; | 167 { |
| 144 char *dir = file_path; | 168 // File is cached |
| 145 file_path = slash + 1; | 169 file_content = ((Seobeo_Cache_Entry*)p_file_kv)->value; |
| 146 | 170 body_size = strlen(file_content); |
| 147 printf("Directory: %s\n", dir); | 171 } |
| 148 | 172 else |
| 149 p_current = Dowa_HashMap_Get(p_current, dir); | 173 { |
| 150 if (!p_current) | 174 // Load from disk and cache |
| 175 file_content = Seobeo_Web_LoadFile(file_path, &body_size); | |
| 176 if (file_content) | |
| 151 { | 177 { |
| 152 fprintf(stderr, "No value in hashmap key: %s\n\n", dir); | 178 Dowa_HashMap_Push(p_html_cache, file_path, file_content); |
| 153 Seobeo_Web_Header_Generate(p_response_header, | |
| 154 HTTP_NOT_FOUND, | |
| 155 "text/html", 0); | |
| 156 Seobeo_Handle_Queue(p_cli_handle, | |
| 157 (const uint8*)p_response_header, | |
| 158 (uint32)strlen(p_response_header)); | |
| 159 Seobeo_Handle_Flush(p_cli_handle); | |
| 160 goto clean_up; | |
| 161 } | 179 } |
| 162 } | 180 } |
| 163 | 181 |
| 164 size_t pos = Dowa_HashMap_Get_Position(p_current, file_path); | 182 if (!file_content) |
| 165 entry = p_current->entries[pos]; | |
| 166 | |
| 167 if (!entry) | |
| 168 { | 183 { |
| 169 Seobeo_Web_Header_Generate(p_response_header, | 184 Seobeo_Web_Header_Generate(p_response_header, |
| 170 HTTP_NOT_FOUND, | 185 HTTP_NOT_FOUND, |
| 171 "text/html", 0); | 186 "text/html", 0); |
| 172 Seobeo_Handle_Queue(p_cli_handle, | 187 Seobeo_Handle_Queue(p_cli_handle, |
| 185 else if (strstr(file_path, ".gif")) mime = "image/gif"; | 200 else if (strstr(file_path, ".gif")) mime = "image/gif"; |
| 186 else if (strstr(file_path, ".svg")) mime = "image/svg+xml"; | 201 else if (strstr(file_path, ".svg")) mime = "image/svg+xml"; |
| 187 else if (strstr(file_path, ".ico")) mime = "image/x-icon"; | 202 else if (strstr(file_path, ".ico")) mime = "image/x-icon"; |
| 188 else if (strstr(file_path, ".json")) mime = "application/json"; | 203 else if (strstr(file_path, ".json")) mime = "application/json"; |
| 189 | 204 |
| 190 size_t body_size = entry->capacity; | 205 printf("File path: %s\nBody Size: %zu\n", file_path, body_size); |
| 191 printf("key: %s\nBody Size: %zu\n", entry->key, body_size); | |
| 192 | 206 |
| 193 Seobeo_Web_Header_Generate(p_response_header, | 207 Seobeo_Web_Header_Generate(p_response_header, |
| 194 HTTP_OK, | 208 HTTP_OK, |
| 195 mime, | 209 mime, |
| 196 body_size); | 210 body_size); |
| 197 | 211 |
| 198 Seobeo_Handle_Queue(p_cli_handle, | 212 Seobeo_Handle_Queue(p_cli_handle, |
| 199 (const uint8*)p_response_header, | 213 (const uint8*)p_response_header, |
| 200 (uint32)strlen(p_response_header)); | 214 (uint32)strlen(p_response_header)); |
| 201 Seobeo_Handle_Queue(p_cli_handle, | 215 Seobeo_Handle_Queue(p_cli_handle, |
| 202 (const uint8*)entry->buffer, | 216 (const uint8*)file_content, |
| 203 (uint32)body_size); | 217 (uint32)body_size); |
| 204 Seobeo_Handle_Flush(p_cli_handle); | 218 Seobeo_Handle_Flush(p_cli_handle); |
| 205 printf("DONE\n\n\n"); | 219 printf("DONE\n\n\n"); |
| 206 } | 220 } |
| 207 else if (strcmp(method, "POST") == 0) | 221 else if (strcmp(method, "POST") == 0) |
| 252 | 266 |
| 253 clean_up: | 267 clean_up: |
| 254 printf("clean up\n\n"); | 268 printf("clean up\n\n"); |
| 255 if (p_cli_handle) | 269 if (p_cli_handle) |
| 256 Seobeo_Handle_Destroy(p_cli_handle); | 270 Seobeo_Handle_Destroy(p_cli_handle); |
| 271 if (p_request_arena) | |
| 272 Dowa_Arena_Destroy(p_request_arena); | |
| 257 if (p_response_arena) | 273 if (p_response_arena) |
| 258 Dowa_Arena_Destroy(p_response_arena); | 274 Dowa_Arena_Destroy(p_response_arena); |
| 259 return; | 275 return; |
| 260 } | 276 } |
| 261 | 277 |
| 262 | 278 |
| 263 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Dowa_HashMap *map) | 279 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena) |
| 264 { | 280 { |
| 265 // 1) Fill read_buffer until we see "\r\n\r\n" | 281 // 1) Fill read_buffer until we see "\r\n\r\n" |
| 266 while (1) | 282 while (1) |
| 267 { | 283 { |
| 268 int r = Seobeo_Handle_Read(p_handle); | 284 int r = Seobeo_Handle_Read(p_handle); |
| 269 if (r < 0) return -1; // fatal error | 285 if (r < 0) |
| 270 if (r == -2) return -2; // connection closed TODO: Add this as part of Handle struct. | 286 return -1; // fatal error |
| 287 if (r == -2) | |
| 288 return -2; // connection closed TODO: Add this as part of Handle struct. | |
| 271 | 289 |
| 272 if (p_handle->read_buffer_len >= 4 && | 290 if (p_handle->read_buffer_len >= 4 && |
| 273 strstr((char*)p_handle->read_buffer, "\r\n\r\n") != NULL) | 291 strstr((char*)p_handle->read_buffer, "\r\n\r\n") != NULL) |
| 274 { | 292 { |
| 275 break; | 293 break; |
| 276 } | 294 } |
| 277 if (r == 0) return 1; // EAGAIN, try again later TODO: Add this as part of Handle struct. | 295 if (r == 0) |
| 296 return 1; // EAGAIN, try again later TODO: Add this as part of Handle struct. | |
| 278 } | 297 } |
| 279 | 298 |
| 280 // 2) Parse request‐line "METHOD SP PATH SP VERSION CRLF" | 299 // 2) Parse request‐line "METHOD SP PATH SP VERSION CRLF" |
| 281 char *buf = (char*)p_handle->read_buffer; | 300 char *buf = (char*)p_handle->read_buffer; |
| 282 char *hdr_end = strstr(buf, "\r\n\r\n"); | 301 char *hdr_end = strstr(buf, "\r\n\r\n"); |
| 287 if (sscanf(buf, "%15s %255s %15s", method, path, version) != 3) | 306 if (sscanf(buf, "%15s %255s %15s", method, path, version) != 3) |
| 288 { | 307 { |
| 289 return -1; | 308 return -1; |
| 290 } | 309 } |
| 291 | 310 |
| 292 Dowa_HashMap_Push_Value_With_Type(map, "HTTP_Method", method, strlen(method) + 1, DOWA_HASH_MAP_TYPE_STRING); | 311 // Copy strings to arena and store in hashmap |
| 293 printf("Method: %s Pointer %p\n\n", Dowa_HashMap_Get(map, "HTTP_Method"), map); | 312 char *method_copy = Dowa_Arena_Allocate(p_arena, strlen(method) + 1); |
| 294 Dowa_HashMap_Push_Value_With_Type(map, "Version", version, strlen(version) + 1, DOWA_HASH_MAP_TYPE_STRING); | 313 if (!method_copy) return -1; |
| 314 strcpy(method_copy, method); | |
| 315 | |
| 316 char *version_copy = Dowa_Arena_Allocate(p_arena, strlen(version) + 1); | |
| 317 if (!version_copy) return -1; | |
| 318 strcpy(version_copy, version); | |
| 319 | |
| 320 Dowa_HashMap_Push_Arena(*pp_map, "HTTP_Method", method_copy, p_arena); | |
| 321 Dowa_HashMap_Push_Arena(*pp_map, "Version", version_copy, p_arena); | |
| 295 | 322 |
| 296 // 1) Separate raw path and query string | 323 // 1) Separate raw path and query string |
| 297 char *raw_path = path; | 324 char *raw_path = path; |
| 298 char *query_start = strchr(raw_path, '?'); | 325 char *query_start = strchr(raw_path, '?'); |
| 299 char *query_str = NULL; | 326 char *query_str = NULL; |
| 303 *query_start = '\0'; // now raw_path ends before '?' | 330 *query_start = '\0'; // now raw_path ends before '?' |
| 304 query_str = query_start + 1; | 331 query_str = query_start + 1; |
| 305 } | 332 } |
| 306 | 333 |
| 307 // push only the clean path | 334 // push only the clean path |
| 308 Dowa_HashMap_Push_Value_With_Type( | 335 char *path_copy = Dowa_Arena_Allocate(p_arena, strlen(raw_path) + 1); |
| 309 map, | 336 if (!path_copy) return -1; |
| 310 "Path", | 337 strcpy(path_copy, raw_path); |
| 311 raw_path, | 338 Dowa_HashMap_Push_Arena(*pp_map, "Path", path_copy, p_arena); |
| 312 strlen(raw_path) + 1, | |
| 313 DOWA_HASH_MAP_TYPE_STRING); | |
| 314 | 339 |
| 315 // 2) If there *is* a query, tokenize into a sub-map | 340 // 2) If there *is* a query, tokenize into the same map with "query_" prefix |
| 316 if (query_str && *query_str) | 341 if (query_str && *query_str) |
| 317 { | 342 { |
| 318 // create nested map for GET params | |
| 319 Dowa_HashMap *p_query_map = Dowa_HashMap_Create_With_Arena(100, map->p_arena); | |
| 320 | |
| 321 char *cur = query_str; | 343 char *cur = query_str; |
| 322 while (cur && *cur) | 344 while (cur && *cur) |
| 323 { | 345 { |
| 324 // find the next '&' | |
| 325 char *next_amp = strchr(cur, '&'); | 346 char *next_amp = strchr(cur, '&'); |
| 326 // if none, treat end-of-string as the boundary | |
| 327 char *pair_end = next_amp ? next_amp : cur + strlen(cur); | 347 char *pair_end = next_amp ? next_amp : cur + strlen(cur); |
| 328 | 348 |
| 329 // find '=' in [cur, pair_end) | |
| 330 char *eq = memchr(cur, '=', pair_end - cur); | 349 char *eq = memchr(cur, '=', pair_end - cur); |
| 331 if (eq) { | 350 if (eq) |
| 351 { | |
| 332 size_t key_len = eq - cur; | 352 size_t key_len = eq - cur; |
| 333 size_t val_len = pair_end - (eq + 1); | 353 size_t val_len = pair_end - (eq + 1); |
| 334 | 354 |
| 335 // extract key | 355 char key_buf[256]; |
| 336 char key_buf[key_len + 1]; | 356 snprintf(key_buf, sizeof(key_buf), "query_%.*s", (int)key_len, cur); |
| 337 memcpy(key_buf, cur, key_len); | 357 |
| 338 key_buf[key_len] = '\0'; | 358 char *val_copy = Dowa_Arena_Allocate(p_arena, val_len + 1); |
| 339 | 359 if (!val_copy) return -1; |
| 340 // extract value | 360 memcpy(val_copy, eq + 1, val_len); |
| 341 char val_buf[val_len + 1]; | 361 val_copy[val_len] = '\0'; |
| 342 memcpy(val_buf, eq + 1, val_len); | 362 |
| 343 val_buf[val_len] = '\0'; | 363 Dowa_HashMap_Push_Arena(*pp_map, key_buf, val_copy, p_arena); |
| 344 | |
| 345 printf("key: '%s', value: '%s'\n", key_buf, val_buf); | |
| 346 // push into map with strlen(val_buf)+1 to include '\0' | |
| 347 Dowa_HashMap_Push_Value_With_Type( | |
| 348 p_query_map, | |
| 349 key_buf, | |
| 350 val_buf, | |
| 351 (uint32_t)(val_len + 1), | |
| 352 DOWA_HASH_MAP_TYPE_STRING); | |
| 353 } | 364 } |
| 354 | 365 |
| 355 // advance past '&' if present, else end loop | |
| 356 cur = next_amp ? next_amp + 1 : NULL; | 366 cur = next_amp ? next_amp + 1 : NULL; |
| 357 } | |
| 358 if ( | |
| 359 Dowa_HashMap_Push_Value_With_Type_NoCopy( | |
| 360 map, | |
| 361 "QueryParams", | |
| 362 p_query_map, | |
| 363 sizeof(p_query_map), | |
| 364 DOWA_HASH_MAP_TYPE_HASHMAP) == -1 | |
| 365 ) | |
| 366 { | |
| 367 printf("Something went wrong...\n\n"); | |
| 368 } | 367 } |
| 369 } | 368 } |
| 370 | 369 |
| 371 // int qp = Dowa_HashMap_Get_Position(map, "QueryParams"); | 370 // int qp = Dowa_HashMap_Get_Position(map, "QueryParams"); |
| 372 // Dowa_HashEntry *p_qp_entry = map->entries[qp]; | 371 // Dowa_HashEntry *p_qp_entry = map->entries[qp]; |
| 392 { | 391 { |
| 393 val_start++; | 392 val_start++; |
| 394 value_len--; | 393 value_len--; |
| 395 } | 394 } |
| 396 | 395 |
| 397 char *key = malloc(key_len + 1); | 396 char *key = Dowa_Arena_Allocate(p_arena, key_len + 1); |
| 397 if (!key) return -1; | |
| 398 memcpy(key, line, key_len); | 398 memcpy(key, line, key_len); |
| 399 key[key_len] = '\0'; | 399 key[key_len] = '\0'; |
| 400 | 400 |
| 401 char *val = malloc(value_len + 1); | 401 char *val = Dowa_Arena_Allocate(p_arena, value_len + 1); |
| 402 if (!val) return -1; | |
| 402 memcpy(val, val_start, value_len); | 403 memcpy(val, val_start, value_len); |
| 403 val[value_len] = '\0'; | 404 val[value_len] = '\0'; |
| 404 | 405 |
| 405 Dowa_HashMap_Push_Value_With_Type(map, key, val, value_len + 1, DOWA_HASH_MAP_TYPE_STRING); | 406 // Both key and value are arena-allocated, hashmap will use them |
| 406 | 407 Dowa_HashMap_Push_Arena(*pp_map, key, val, p_arena); |
| 407 // printf("Capacity: %d, Length: %d ", (int)map->p_arena->capacity, (int)map->p_arena->offset); | |
| 408 // printf("value_len: %d, key: %s value %s position: %d\n\n", (int)value_len + 1, key, val, Dowa_HashMap_Get_Position(map, key)); | |
| 409 // printf("Method: %s Position: %d Pointer %p\n\n", Dowa_HashMap_Get(map, "HTTP_Method"), Dowa_HashMap_Get_Position(map, "HTTP_Method"), map); | |
| 410 | |
| 411 Dowa_Free(key); | |
| 412 Dowa_Free(val); | |
| 413 } | 408 } |
| 414 | 409 |
| 415 line = next + 2; | 410 line = next + 2; |
| 416 } | 411 } |
| 412 | |
| 417 Seobeo_Handle_Consume(p_handle, (uint32)hdr_len); | 413 Seobeo_Handle_Consume(p_handle, (uint32)hdr_len); |
| 418 | 414 |
| 419 // 4) If Content-Length was provided, read that much body | 415 // 4) If Content-Length was provided, read that much body |
| 420 int content_length_pos = Dowa_HashMap_Get_Position(map, "Content-Length"); | 416 void *p_cl_kv = Dowa_HashMap_Get_Ptr(*pp_map, "Content-Length"); |
| 421 Dowa_HashEntry *p_content_length_entry = map->entries[content_length_pos]; | 417 if (p_cl_kv) |
| 422 if (p_content_length_entry) | 418 { |
| 423 { | 419 const char *content_length_str = ((Seobeo_Request_Entry*)p_cl_kv)->value; |
| 424 size_t body_len = atoi((char*)p_content_length_entry->buffer); | 420 size_t body_len = atoi(content_length_str); |
| 425 while (p_handle->read_buffer_len < body_len) | 421 while (p_handle->read_buffer_len < body_len) |
| 426 { | 422 { |
| 427 int r = Seobeo_Handle_Read(p_handle); | 423 int r = Seobeo_Handle_Read(p_handle); |
| 428 if (r < 0) return -1; | 424 if (r < 0) return -1; |
| 429 if (r == 0) return 1; // wait for more data | 425 if (r == 0) return 1; // wait for more data |
| 430 } | 426 } |
| 431 | 427 |
| 432 char *body = malloc(body_len + 1); | 428 char *body = Dowa_Arena_Allocate(p_arena, body_len + 1); |
| 429 if (!body) return -1; | |
| 433 memcpy(body, p_handle->read_buffer, body_len); | 430 memcpy(body, p_handle->read_buffer, body_len); |
| 434 body[body_len] = '\0'; | 431 body[body_len] = '\0'; |
| 435 | 432 |
| 436 Dowa_HashMap_Push_Value(map, "Body", body, body_len + 1); | 433 // Body is arena-allocated |
| 437 Dowa_Free(body); | 434 Dowa_HashMap_Push_Arena(*pp_map, "Body", body, p_arena); |
| 438 | 435 |
| 439 Seobeo_Handle_Consume(p_handle, (uint32)body_len); | 436 Seobeo_Handle_Consume(p_handle, (uint32)body_len); |
| 440 } | 437 } |
| 441 | 438 |
| 442 return 0; // success; map now holds Method, Path, Version, headers, and optional Body | 439 return 0; // success; map now holds Method, Path, Version, headers, and optional Body |
| 459 const char *folder_path, | 456 const char *folder_path, |
| 460 const char *port, | 457 const char *port, |
| 461 Seobeo_ServerMode mode, | 458 Seobeo_ServerMode mode, |
| 462 int thread_count) | 459 int thread_count) |
| 463 { | 460 { |
| 464 Dowa_HashMap *p_html_cache = Dowa_HashMap_Create(200); | 461 // Store folder path globally |
| 465 if (Dowa_HashMap_Cache_Folder(p_html_cache, | 462 if (folder_path) |
| 466 folder_path) != 0) | 463 strncpy(g_folder_path, folder_path, sizeof(g_folder_path) - 1); |
| 467 { | 464 |
| 468 perror("Dowa_Cache_Folder"); | 465 // Initialize empty cache - files will be loaded on-demand |
| 469 return -1; | 466 Seobeo_Cache_Entry *p_html_cache = NULL; |
| 470 } | |
| 471 | 467 |
| 472 Seobeo_Handle *p_server_handle = | 468 Seobeo_Handle *p_server_handle = |
| 473 Seobeo_Stream_Handle_Server_Create(NULL, port); | 469 Seobeo_Stream_Handle_Server_Create(NULL, port); |
| 474 if (p_server_handle->socket < 0) return 1; | 470 if (p_server_handle->socket < 0) return 1; |
| 475 | 471 |
| 501 } | 497 } |
| 502 | 498 |
| 503 if (mode == SEOBEO_MODE_EDGE) | 499 if (mode == SEOBEO_MODE_EDGE) |
| 504 { | 500 { |
| 505 printf("EDGE MODE\n"); | 501 printf("EDGE MODE\n"); |
| 506 // Seobeo_Web_Edge_2(p_server_handle, p_html_cache); | |
| 507 Seobeo_Web_Edge(p_server_handle, thread_count, p_html_cache); | 502 Seobeo_Web_Edge(p_server_handle, thread_count, p_html_cache); |
| 508 } | 503 } |
| 509 | 504 |
| 510 return -1; | 505 return -1; |
| 511 } | 506 } |