Mercurial
comparison seobeo/s_web.c @ 110:99c4530e4629
[Seobeo] Small Syntax fixes.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sat, 03 Jan 2026 21:16:17 -0800 |
| parents | 3468e2fe8d88 |
| children | c39582f937e5 |
comparison
equal
deleted
inserted
replaced
| 109:1c446ab6f945 | 110:99c4530e4629 |
|---|---|
| 1 #include "seobeo/seobeo.h" | 1 #include "seobeo/seobeo.h" |
| 2 | 2 |
| 3 // Global folder path for serving files | |
| 4 static char g_folder_path[512] = "."; | 3 static char g_folder_path[512] = "."; |
| 5 | 4 |
| 6 int Seobeo_Web_GenerateRequestHeader( | 5 int Seobeo_Web_GenerateRequestHeader( |
| 7 void *buffer, const char *host, | 6 void *buffer, const char *host, |
| 8 const char *path) | 7 const char *path) |
| 157 (uint32)strlen(p_response_header)); | 156 (uint32)strlen(p_response_header)); |
| 158 Seobeo_Handle_Flush(p_cli_handle); | 157 Seobeo_Handle_Flush(p_cli_handle); |
| 159 goto clean_up; | 158 goto clean_up; |
| 160 } | 159 } |
| 161 | 160 |
| 162 // Extract path | |
| 163 void *p_path_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); | 161 void *p_path_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); |
| 164 const char *path = p_path_kv ? ((Seobeo_Request_Entry*)p_path_kv)->value : "/"; | 162 const char *path = p_path_kv ? ((Seobeo_Request_Entry*)p_path_kv)->value : "/"; |
| 165 | 163 |
| 166 // --- Try to match API route first --- | 164 // --- Try to match API route first --- |
| 167 Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena); | 165 Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena); |
| 170 Seobeo_Request_Entry *p_response_map = handler(p_req_map, p_response_arena); | 168 Seobeo_Request_Entry *p_response_map = handler(p_req_map, p_response_arena); |
| 171 Seobeo_Router_Send_Response(p_cli_handle, p_response_map, p_response_arena); | 169 Seobeo_Router_Send_Response(p_cli_handle, p_response_map, p_response_arena); |
| 172 goto clean_up; | 170 goto clean_up; |
| 173 } | 171 } |
| 174 | 172 |
| 175 // --- Handle different HTTP methods (static files fallback) --- | 173 // --- Static files fallback for GET --- |
| 176 if (strcmp(method, "GET") == 0) | 174 if (strcmp(method, "GET") == 0) |
| 177 { | 175 { |
| 178 void *p_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); | 176 void *p_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); |
| 179 const char *path = p_kv ? ((Seobeo_Request_Entry*)p_kv)->value : NULL; | 177 const char *path = p_kv ? ((Seobeo_Request_Entry*)p_kv)->value : NULL; |
| 180 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024); // 5Kb only for path | 178 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024); // 5Kb only for path |
| 280 } | 278 } |
| 281 | 279 |
| 282 | 280 |
| 283 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena) | 281 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena) |
| 284 { | 282 { |
| 285 // 1) Fill read_buffer until we see "\r\n\r\n" | |
| 286 while (1) | 283 while (1) |
| 287 { | 284 { |
| 288 int r = Seobeo_Handle_Read(p_handle); | 285 int r = Seobeo_Handle_Read(p_handle); |
| 289 if (r < 0) | 286 if (r < 0) |
| 290 return -1; // fatal error | 287 return -1; // fatal error |
| 291 if (r == -2) | 288 if (r == -2) |
| 292 return -2; // connection closed TODO: Add this as part of Handle struct. | 289 return -2; // connection closed TODO: Add this as part of Handle struct. |
| 293 | 290 |
| 294 if (p_handle->read_buffer_len >= 4 && | 291 if (p_handle->read_buffer_len >= 4 && |
| 295 strstr((char*)p_handle->read_buffer, "\r\n\r\n") != NULL) | 292 strstr((char*)p_handle->read_buffer, "\r\n\r\n") != NULL) |
| 296 { | |
| 297 break; | 293 break; |
| 298 } | 294 |
| 299 if (r == 0) | 295 if (r == 0) |
| 300 return 1; // EAGAIN, try again later TODO: Add this as part of Handle struct. | 296 return 1; // EAGAIN, try again later TODO: Add this as part of Handle struct. |
| 301 } | 297 } |
| 302 | 298 |
| 303 // 2) Parse request‐line "METHOD SP PATH SP VERSION CRLF" | 299 // "METHOD SP PATH SP VERSION CRLF" |
| 304 char *buf = (char*)p_handle->read_buffer; | 300 char *buf = (char*)p_handle->read_buffer; |
| 305 char *hdr_end = strstr(buf, "\r\n\r\n"); | 301 char *hdr_end = strstr(buf, "\r\n\r\n"); |
| 306 size_t hdr_len = hdr_end - buf + 4; | 302 size_t hdr_len = hdr_end - buf + 4; |
| 307 | 303 |
| 308 // Debug: Print the first line of the request | 304 // Debug: Print the first line of the request |
| 326 { | 322 { |
| 327 Seobeo_Log(SEOBEO_ERROR, "Failed to parse request line\n"); | 323 Seobeo_Log(SEOBEO_ERROR, "Failed to parse request line\n"); |
| 328 return -1; | 324 return -1; |
| 329 } | 325 } |
| 330 | 326 |
| 331 // Copy strings to arena and store in hashmap | |
| 332 Seobeo_Log(SEOBEO_DEBUG, "Allocating method_copy\n"); | 327 Seobeo_Log(SEOBEO_DEBUG, "Allocating method_copy\n"); |
| 333 char *method_copy = Dowa_Arena_Allocate(p_arena, strlen(method) + 1); | 328 char *method_copy = Dowa_Arena_Allocate(p_arena, strlen(method) + 1); |
| 334 if (!method_copy) { Seobeo_Log(SEOBEO_ERROR, "Failed to allocate method_copy\n"); return -1; } | 329 if (!method_copy) { Seobeo_Log(SEOBEO_ERROR, "Failed to allocate method_copy\n"); return -1; } |
| 335 strcpy(method_copy, method); | 330 strcpy(method_copy, method); |
| 336 | 331 |
| 342 Seobeo_Log(SEOBEO_DEBUG, "Pushing HTTP_Method and Version to map\n"); | 337 Seobeo_Log(SEOBEO_DEBUG, "Pushing HTTP_Method and Version to map\n"); |
| 343 Dowa_HashMap_Push_Arena(*pp_map, "HTTP_Method", method_copy, p_arena); | 338 Dowa_HashMap_Push_Arena(*pp_map, "HTTP_Method", method_copy, p_arena); |
| 344 Dowa_HashMap_Push_Arena(*pp_map, "Version", version_copy, p_arena); | 339 Dowa_HashMap_Push_Arena(*pp_map, "Version", version_copy, p_arena); |
| 345 Seobeo_Log(SEOBEO_DEBUG, "Map now has %zu entries\n", Dowa_Array_Length(*pp_map)); | 340 Seobeo_Log(SEOBEO_DEBUG, "Map now has %zu entries\n", Dowa_Array_Length(*pp_map)); |
| 346 | 341 |
| 347 // 1) Separate raw path and query string | |
| 348 char *raw_path = path; | 342 char *raw_path = path; |
| 349 char *query_start = strchr(raw_path, '?'); | 343 char *query_start = strchr(raw_path, '?'); |
| 350 char *query_str = NULL; | 344 char *query_str = NULL; |
| 351 | 345 |
| 352 if (query_start) | 346 if (query_start) |
| 353 { | 347 { |
| 354 *query_start = '\0'; // now raw_path ends before '?' | 348 *query_start = '\0'; // now raw_path ends before '?' |
| 355 query_str = query_start + 1; | 349 query_str = query_start + 1; |
| 356 } | 350 } |
| 357 | 351 |
| 358 // push only the clean path | |
| 359 char *path_copy = Dowa_Arena_Allocate(p_arena, strlen(raw_path) + 1); | 352 char *path_copy = Dowa_Arena_Allocate(p_arena, strlen(raw_path) + 1); |
| 360 if (!path_copy) return -1; | 353 if (!path_copy) return -1; |
| 361 strcpy(path_copy, raw_path); | 354 strcpy(path_copy, raw_path); |
| 362 Dowa_HashMap_Push_Arena(*pp_map, "Path", path_copy, p_arena); | 355 Dowa_HashMap_Push_Arena(*pp_map, "Path", path_copy, p_arena); |
| 363 | 356 |
| 364 // 2) If there *is* a query, tokenize into the same map with "query_" prefix | 357 // GET Params |
| 365 if (query_str && *query_str) | 358 if (query_str && *query_str) |
| 366 { | 359 { |
| 367 char *cur = query_str; | 360 char *cur = query_str; |
| 368 while (cur && *cur) | 361 while (cur && *cur) |
| 369 { | 362 { |
| 375 { | 368 { |
| 376 size_t key_len = eq - cur; | 369 size_t key_len = eq - cur; |
| 377 size_t val_len = pair_end - (eq + 1); | 370 size_t val_len = pair_end - (eq + 1); |
| 378 | 371 |
| 379 char key_buf[256]; | 372 char key_buf[256]; |
| 373 // Adding query_ in front to separate GET params | |
| 380 snprintf(key_buf, sizeof(key_buf), "query_%.*s", (int)key_len, cur); | 374 snprintf(key_buf, sizeof(key_buf), "query_%.*s", (int)key_len, cur); |
| 381 | 375 |
| 382 char *val_copy = Dowa_Arena_Allocate(p_arena, val_len + 1); | 376 char *val_copy = Dowa_Arena_Allocate(p_arena, val_len + 1); |
| 383 if (!val_copy) return -1; | 377 if (!val_copy) return -1; |
| 384 memcpy(val_copy, eq + 1, val_len); | 378 memcpy(val_copy, eq + 1, val_len); |
| 394 // int qp = Dowa_HashMap_Get_Position(map, "QueryParams"); | 388 // int qp = Dowa_HashMap_Get_Position(map, "QueryParams"); |
| 395 // Dowa_HashEntry *p_qp_entry = map->entries[qp]; | 389 // Dowa_HashEntry *p_qp_entry = map->entries[qp]; |
| 396 // printf("query param key: %s\n", p_qp_entry->key); | 390 // printf("query param key: %s\n", p_qp_entry->key); |
| 397 // printf("query param value: %s\n",(char *)Dowa_HashMap_Get(p_qp_entry->buffer, "hello")); | 391 // printf("query param value: %s\n",(char *)Dowa_HashMap_Get(p_qp_entry->buffer, "hello")); |
| 398 | 392 |
| 399 // 3) Parse each header line until the blank line | 393 // Parse headers |
| 400 char *line = buf + strlen(method) + 1 + strlen(path) + 1 + strlen(version) + 2; | 394 char *line = buf + strlen(method) + 1 + strlen(path) + 1 + strlen(version) + 2; |
| 401 while (line < hdr_end) | 395 while (line < hdr_end) |
| 402 { | 396 { |
| 403 char *next = strstr(line, "\r\n"); | 397 char *next = strstr(line, "\r\n"); |
| 404 if (!next) break; | 398 if (!next) break; |
| 434 line = next + 2; | 428 line = next + 2; |
| 435 } | 429 } |
| 436 | 430 |
| 437 Seobeo_Handle_Consume(p_handle, (uint32)hdr_len); | 431 Seobeo_Handle_Consume(p_handle, (uint32)hdr_len); |
| 438 | 432 |
| 439 // 4) If Content-Length was provided, read that much body | 433 // Reading Body |
| 440 void *p_cl_kv = Dowa_HashMap_Get_Ptr(*pp_map, "Content-Length"); | 434 void *p_cl_kv = Dowa_HashMap_Get_Ptr(*pp_map, "Content-Length"); |
| 441 if (p_cl_kv) | 435 if (p_cl_kv) |
| 442 { | 436 { |
| 443 const char *content_length_str = ((Seobeo_Request_Entry*)p_cl_kv)->value; | 437 const char *content_length_str = ((Seobeo_Request_Entry*)p_cl_kv)->value; |
| 444 size_t body_len = atoi(content_length_str); | 438 size_t body_len = atoi(content_length_str); |
| 453 return -1; | 447 return -1; |
| 454 } | 448 } |
| 455 | 449 |
| 456 size_t total_read = 0; | 450 size_t total_read = 0; |
| 457 | 451 |
| 458 // Read body in chunks | |
| 459 while (total_read < body_len) | 452 while (total_read < body_len) |
| 460 { | 453 { |
| 461 // Copy what's currently in the read buffer | |
| 462 size_t available = p_handle->read_buffer_len; | 454 size_t available = p_handle->read_buffer_len; |
| 463 size_t to_copy = (body_len - total_read) < available ? (body_len - total_read) : available; | 455 size_t to_copy = (body_len - total_read) < available ? (body_len - total_read) : available; |
| 464 | 456 |
| 465 if (to_copy > 0) | 457 if (to_copy > 0) |
| 466 { | 458 { |
| 469 Seobeo_Handle_Consume(p_handle, (uint32)to_copy); | 461 Seobeo_Handle_Consume(p_handle, (uint32)to_copy); |
| 470 | 462 |
| 471 Seobeo_Log(SEOBEO_DEBUG, "Copied %zu bytes, total %zu/%zu\n", to_copy, total_read, body_len); | 463 Seobeo_Log(SEOBEO_DEBUG, "Copied %zu bytes, total %zu/%zu\n", to_copy, total_read, body_len); |
| 472 } | 464 } |
| 473 | 465 |
| 474 // If we still need more data, read another chunk | |
| 475 if (total_read < body_len) | 466 if (total_read < body_len) |
| 476 { | 467 { |
| 477 int r = Seobeo_Handle_Read(p_handle); | 468 int r = Seobeo_Handle_Read(p_handle); |
| 478 if (r < 0) | 469 if (r < 0) |
| 479 { | 470 { |
| 490 } | 481 } |
| 491 | 482 |
| 492 body[body_len] = '\0'; | 483 body[body_len] = '\0'; |
| 493 Seobeo_Log(SEOBEO_DEBUG, "Body fully received (%zu bytes)\n", body_len); | 484 Seobeo_Log(SEOBEO_DEBUG, "Body fully received (%zu bytes)\n", body_len); |
| 494 | 485 |
| 495 // Body is arena-allocated | |
| 496 Dowa_HashMap_Push_Arena(*pp_map, "Body", body, p_arena); | 486 Dowa_HashMap_Push_Arena(*pp_map, "Body", body, p_arena); |
| 497 } | 487 } |
| 498 | 488 |
| 499 return 0; // success; map now holds Method, Path, Version, headers, and optional Body | 489 return 0; |
| 500 } | 490 } |
| 501 | 491 |
| 502 // TODO: Do epoll or kqueue depending on the OS. | |
| 503 void SigchildHandler(int s) | 492 void SigchildHandler(int s) |
| 504 { | 493 { |
| 505 (void)s; // quiet unused variable warning | 494 (void)s; |
| 506 | 495 |
| 507 // waitpid() might overwrite errno, so we save and restore it: | 496 // waitpid() might overwrite errno, so we save and restore it: |
| 508 int saved_errno = errno; | 497 int saved_errno = errno; |
| 509 | 498 |
| 510 while(waitpid(-1, NULL, WNOHANG) > 0); | 499 while(waitpid(-1, NULL, WNOHANG) > 0); |
| 516 const char *folder_path, | 505 const char *folder_path, |
| 517 const char *port, | 506 const char *port, |
| 518 Seobeo_ServerMode mode, | 507 Seobeo_ServerMode mode, |
| 519 int thread_count) | 508 int thread_count) |
| 520 { | 509 { |
| 521 // Store folder path globally | |
| 522 if (folder_path) | 510 if (folder_path) |
| 523 strncpy(g_folder_path, folder_path, sizeof(g_folder_path) - 1); | 511 strncpy(g_folder_path, folder_path, sizeof(g_folder_path) - 1); |
| 524 | 512 |
| 525 // Initialize empty cache - files will be loaded on-demand | |
| 526 Seobeo_Cache_Entry *p_html_cache = NULL; | 513 Seobeo_Cache_Entry *p_html_cache = NULL; |
| 527 | 514 |
| 528 Seobeo_Handle *p_server_handle = | 515 Seobeo_Handle *p_server_handle = |
| 529 Seobeo_Stream_Handle_Server_Create(NULL, port); | 516 Seobeo_Stream_Handle_Server_Create(NULL, port); |
| 530 if (p_server_handle->socket < 0) return 1; | 517 if (p_server_handle->socket < 0) return 1; |
| 531 | 518 |
| 532 Seobeo_Log(SEOBEO_INFO, "Listening on port %s\n", port); | 519 Seobeo_Log(SEOBEO_INFO, "Listening on port %s\n", port); |
| 533 | 520 |
| 534 // Fork‐based fallback | |
| 535 if (mode == SEOBEO_MODE_FORK) | 521 if (mode == SEOBEO_MODE_FORK) |
| 536 { | 522 { |
| 537 Seobeo_Log(SEOBEO_INFO, "Server mode: FORK\n"); | 523 Seobeo_Log(SEOBEO_INFO, "Server mode: FORK\n"); |
| 538 struct sigaction sa; | 524 struct sigaction sa; |
| 539 sa.sa_handler = SigchildHandler; | 525 sa.sa_handler = SigchildHandler; |
| 605 { | 591 { |
| 606 // TODO: Maybe directly use arena inside of the struct? idk if that is useful or not... | 592 // TODO: Maybe directly use arena inside of the struct? idk if that is useful or not... |
| 607 memcpy(p_request_body + used, h->read_buffer, h->read_buffer_len); | 593 memcpy(p_request_body + used, h->read_buffer, h->read_buffer_len); |
| 608 used += h->read_buffer_len; | 594 used += h->read_buffer_len; |
| 609 Seobeo_Handle_Consume(h, (uint32)h->read_buffer_len); | 595 Seobeo_Handle_Consume(h, (uint32)h->read_buffer_len); |
| 610 }else if (n == 0) | 596 } |
| 597 else if (n == 0) | |
| 611 { | 598 { |
| 612 // Wait | 599 // Wait |
| 613 continue; | 600 continue; |
| 614 }else if (n == -2) | 601 } |
| 615 { | 602 else if (n == -2) |
| 616 // Debug | 603 { |
| 617 // peer closed; we’ve got everything | |
| 618 Seobeo_Log(SEOBEO_DEBUG, "Connection closed by client\n"); | 604 Seobeo_Log(SEOBEO_DEBUG, "Connection closed by client\n"); |
| 619 break; | 605 break; |
| 620 }else | 606 } |
| 607 else | |
| 621 { | 608 { |
| 622 Dowa_Arena_Free(p_request_arena); | 609 Dowa_Arena_Free(p_request_arena); |
| 623 Seobeo_Handle_Destroy(h); | 610 Seobeo_Handle_Destroy(h); |
| 624 return -1; | 611 return -1; |
| 625 } | 612 } |
| 626 } | 613 } |
| 627 | 614 |
| 628 // Debug | |
| 629 Seobeo_Log(SEOBEO_DEBUG, "Request body: %s\n", p_request_body); | 615 Seobeo_Log(SEOBEO_DEBUG, "Request body: %s\n", p_request_body); |
| 630 Dowa_Arena_Free(p_request_arena); | 616 Dowa_Arena_Free(p_request_arena); |
| 631 Seobeo_Handle_Destroy(h); | 617 Seobeo_Handle_Destroy(h); |
| 632 return 0; | 618 return 0; |
| 633 } | 619 } |
| 666 route.is_param[i] = (route.path_segments[i][0] == ':'); | 652 route.is_param[i] = (route.path_segments[i][0] == ':'); |
| 667 | 653 |
| 668 Dowa_Array_Push(g_routes, route); | 654 Dowa_Array_Push(g_routes, route); |
| 669 } | 655 } |
| 670 | 656 |
| 671 // Match route and extract path parameters | |
| 672 static boolean match_route_and_extract( | 657 static boolean match_route_and_extract( |
| 673 Seobeo_Route *route, | 658 Seobeo_Route *route, |
| 674 const char *request_path, | 659 const char *request_path, |
| 675 Seobeo_Request_Entry **pp_request_map, | 660 Seobeo_Request_Entry **pp_request_map, |
| 676 Dowa_Arena *p_arena) | 661 Dowa_Arena *p_arena) |
| 677 { | 662 { |
| 678 Dowa_Arena *p_temp_arena = Dowa_Arena_Create(1024); | 663 Dowa_Arena *p_temp_arena = Dowa_Arena_Create(1024); |
| 679 char **request_segments = Dowa_String_Split(request_path, "/", strlen(request_path), 1, p_temp_arena); | 664 char **request_segments = Dowa_String_Split(request_path, "/", strlen(request_path), 1, p_temp_arena); |
| 680 size_t request_segment_count = Dowa_Array_Length(request_segments); | 665 size_t request_segment_count = Dowa_Array_Length(request_segments); |
| 666 | |
| 681 // Check segment count matches | 667 // Check segment count matches |
| 682 if (request_segment_count != route->segment_count) | 668 if (request_segment_count != route->segment_count) |
| 683 { | 669 { |
| 684 Dowa_Arena_Free(p_temp_arena); | 670 Dowa_Arena_Free(p_temp_arena); |
| 685 return FALSE; | 671 return FALSE; |
| 753 Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); | 739 Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); |
| 754 Seobeo_Handle_Flush(p_handle); | 740 Seobeo_Handle_Flush(p_handle); |
| 755 return; | 741 return; |
| 756 } | 742 } |
| 757 | 743 |
| 758 // Header | |
| 759 int status = HTTP_OK; | 744 int status = HTTP_OK; |
| 760 void *p_status_kv = Dowa_HashMap_Get_Ptr(p_response_map, "status"); | 745 void *p_status_kv = Dowa_HashMap_Get_Ptr(p_response_map, "status"); |
| 761 if (p_status_kv) | 746 if (p_status_kv) |
| 762 { | 747 { |
| 763 const char *status_str = ((Seobeo_Request_Entry*)p_status_kv)->value; | 748 const char *status_str = ((Seobeo_Request_Entry*)p_status_kv)->value; |
| 764 status = atoi(status_str); | 749 status = atoi(status_str); |
| 765 } | 750 } |
| 766 | 751 |
| 767 // Body | |
| 768 const char *body = ""; | 752 const char *body = ""; |
| 769 void *p_body_kv = Dowa_HashMap_Get_Ptr(p_response_map, "body"); | 753 void *p_body_kv = Dowa_HashMap_Get_Ptr(p_response_map, "body"); |
| 770 if (p_body_kv) | 754 if (p_body_kv) |
| 771 { | 755 { |
| 772 body = ((Seobeo_Request_Entry*)p_body_kv)->value; | 756 body = ((Seobeo_Request_Entry*)p_body_kv)->value; |
| 773 } | 757 } |
| 774 | 758 |
| 775 // Default text plain | |
| 776 const char *content_type = "text/html"; | 759 const char *content_type = "text/html"; |
| 777 void *p_content_type_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-type"); | 760 void *p_content_type_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-type"); |
| 778 if (p_content_type_kv) | 761 if (p_content_type_kv) |
| 779 { | 762 { |
| 780 content_type = ((Seobeo_Request_Entry*)p_content_type_kv)->value; | 763 content_type = ((Seobeo_Request_Entry*)p_content_type_kv)->value; |
| 781 } | 764 } |
| 782 | 765 |
| 783 // Check for custom content-length (for binary data) | |
| 784 size_t body_length = strlen(body); | 766 size_t body_length = strlen(body); |
| 785 void *p_content_length_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-length"); | 767 void *p_content_length_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-length"); |
| 786 if (p_content_length_kv) | 768 if (p_content_length_kv) |
| 787 { | 769 { |
| 788 const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value; | 770 const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value; |
| 789 body_length = atoi(content_length_str); | 771 body_length = atoi(content_length_str); |
| 790 } | 772 } |
| 791 | 773 |
| 792 // All other types are default thoguht to be headers. | |
| 793 char *header = Dowa_Arena_Allocate(p_arena, 4096); | 774 char *header = Dowa_Arena_Allocate(p_arena, 4096); |
| 794 Seobeo_Web_Header_Generate(header, status, content_type, body_length); | 775 Seobeo_Web_Header_Generate(header, status, content_type, body_length); |
| 795 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++) | 776 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++) |
| 796 { | 777 { |
| 797 if ( | 778 if ( |
| 798 strstr(p_response_map[i].key, "status") || | 779 strstr(p_response_map[i].key, "status") || |
| 799 strstr(p_response_map[i].key, "body") || | 780 strstr(p_response_map[i].key, "body") || |
| 800 strstr(p_response_map[i].key, "content-type") || | 781 strstr(p_response_map[i].key, "content-type") || |
| 801 strstr(p_response_map[i].key, "content-length") // Skip custom content-length | 782 strstr(p_response_map[i].key, "content-length") |
| 802 ) | 783 ) |
| 803 continue; | 784 continue; |
| 804 | 785 |
| 805 int32 current_header_len = strlen(header); | 786 int32 current_header_len = strlen(header); |
| 806 char *temp = malloc(sizeof(char) * 1024); | 787 char *temp = malloc(sizeof(char) * 1024); |
| 808 memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp)); | 789 memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp)); |
| 809 free(temp); | 790 free(temp); |
| 810 } | 791 } |
| 811 | 792 |
| 812 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); | 793 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); |
| 813 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); // Use actual body length | 794 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); |
| 814 Seobeo_Handle_Flush(p_handle); | 795 Seobeo_Handle_Flush(p_handle); |
| 815 } | 796 } |
| 816 | 797 |
| 817 void Seobeo_Router_Destroy() | 798 void Seobeo_Router_Destroy() |
| 818 { | 799 { |