Mercurial
view seobeo/s_router.c @ 88:a3962e681490
Fixed routing issues.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Thu, 01 Jan 2026 14:29:36 -0800 |
| parents | 5710108c949e |
| children | 655ea0b661fd |
line wrap: on
line source
#include "seobeo/seobeo.h" #include <string.h> #include <stdio.h> #include <stdlib.h> struct Seobeo_Route_Struct { char *method; // "GET", "POST", "PUT", "DELETE" char *path_pattern; // "/v1/users/:id/posts/:post_id" Seobeo_Route_Handler handler; // Pre-parsed path segments for efficient matching char **path_segments; // ["v1", "users", ":id", "posts", ":post_id"] boolean *is_param; // [false, false, true, false, true] size_t segment_count; }; static Seobeo_Route *g_routes = NULL; void Seobeo_Router_Init() { Dowa_Array_Reserve(g_routes, 20); } void Seobeo_Router_Register(const char *method, const char *path_pattern, Seobeo_Route_Handler handler) { Seobeo_Route route = {0}; route.method = strdup(method); route.path_pattern = strdup(path_pattern); route.handler = handler; route.path_segments = Dowa_String_Split(path_pattern, "/", strlen(path_pattern), 1, NULL); route.segment_count = Dowa_Array_Length(route.path_segments); route.is_param = (boolean*)malloc(sizeof(boolean) * route.segment_count); for (size_t i = 0; i < route.segment_count; i++) route.is_param[i] = (route.path_segments[i][0] == ':'); Dowa_Array_Push(g_routes, route); } // Match route and extract path parameters static boolean match_route_and_extract( Seobeo_Route *route, const char *request_path, Seobeo_Request_Entry **pp_request_map, Dowa_Arena *p_arena) { Dowa_Arena *p_temp_arena = Dowa_Arena_Create(1024); char **request_segments = Dowa_String_Split(request_path, "/", strlen(request_path), 1, p_temp_arena); size_t request_segment_count = Dowa_Array_Length(request_segments); // Check segment count matches if (request_segment_count != route->segment_count) { Dowa_Arena_Free(p_temp_arena); return FALSE; } for (size_t i = 0; i < route->segment_count; i++) { // parameters if (route->is_param[i]) { char *param_name = route->path_segments[i]; // e.g., ":id" char *param_value = request_segments[i]; // e.g., "123" // Should Copy to arena char *key = Dowa_String_Copy_Arena(param_name, p_arena); char *value = Dowa_String_Copy_Arena(param_value, p_arena); Dowa_HashMap_Push_Arena(*pp_request_map, key, value, p_arena); } else { // Does not match. if (strcmp(route->path_segments[i], request_segments[i]) != 0) { Dowa_Arena_Free(p_temp_arena); return FALSE; } } } Dowa_Arena_Free(p_temp_arena); return TRUE; } Seobeo_Route_Handler Seobeo_Router_Find_Handler(const char *method, const char *path, Seobeo_Request_Entry **pp_request_map, Dowa_Arena *p_arena) { if (g_routes == NULL) { return NULL; } size_t route_count = Dowa_Array_Length(g_routes); for (size_t i = 0; i < route_count; i++) { Seobeo_Route *route = &g_routes[i]; if (strcmp(route->method, method) != 0) { continue; } if (match_route_and_extract(route, path, pp_request_map, p_arena)) { return route->handler; } } return NULL; } void Seobeo_Router_Send_Response( Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, Dowa_Arena *p_arena) { if (p_response_map == NULL) { char *header = Dowa_Arena_Allocate(p_arena, 1024); Seobeo_Web_Header_Generate(header, HTTP_INTERNAL_ERROR, "text/plain", 21); Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); Seobeo_Handle_Flush(p_handle); return; } // Header int status = HTTP_OK; void *p_status_kv = Dowa_HashMap_Get_Ptr(p_response_map, "status"); if (p_status_kv) { const char *status_str = ((Seobeo_Request_Entry*)p_status_kv)->value; status = atoi(status_str); } // Body const char *body = ""; void *p_body_kv = Dowa_HashMap_Get_Ptr(p_response_map, "body"); if (p_body_kv) { body = ((Seobeo_Request_Entry*)p_body_kv)->value; } // Default text plain const char *content_type = "text/html"; void *p_content_type_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-type"); if (p_content_type_kv) { content_type = ((Seobeo_Request_Entry*)p_content_type_kv)->value; } // 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)); 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") ) continue; int32 current_header_len = strlen(header); char *temp = malloc(sizeof(char) * 1024); sprintf(temp, "%s: %s\r\n\r\n", p_response_map[i].key, p_response_map[i].value); memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp)); free(temp); } Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); Seobeo_Handle_Queue(p_handle, (uint8_t*)body, strlen(body)); Seobeo_Handle_Flush(p_handle); } void Seobeo_Router_Destroy() { if (g_routes == NULL) return; size_t route_count = Dowa_Array_Length(g_routes); for (size_t i = 0; i < route_count; i++) { Seobeo_Route *route = &g_routes[i]; if (route->method) free(route->method); if (route->path_pattern) free(route->path_pattern); if (route->path_segments) Dowa_Array_Free(route->path_segments); if (route->is_param) free(route->is_param); } Dowa_Array_Free(g_routes); g_routes = NULL; }