Mercurial
comparison seobeo/s_router.c @ 72:4532ce6d9eb8
[Seobeo] Added router to the server logic. Few dowa string manipulation logics.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Mon, 29 Dec 2025 07:50:07 -0800 |
| parents | |
| children | e7bf9e002850 |
comparison
equal
deleted
inserted
replaced
| 71:75de5903355c | 72:4532ce6d9eb8 |
|---|---|
| 1 #include "seobeo/seobeo.h" | |
| 2 #include <string.h> | |
| 3 #include <stdio.h> | |
| 4 #include <stdlib.h> | |
| 5 | |
| 6 struct Seobeo_Route_Struct { | |
| 7 char *method; // "GET", "POST", "PUT", "DELETE" | |
| 8 char *path_pattern; // "/v1/users/:id/posts/:post_id" | |
| 9 Seobeo_Route_Handler handler; | |
| 10 | |
| 11 // Pre-parsed path segments for efficient matching | |
| 12 char **path_segments; // ["v1", "users", ":id", "posts", ":post_id"] | |
| 13 boolean *is_param; // [false, false, true, false, true] | |
| 14 size_t segment_count; | |
| 15 }; | |
| 16 | |
| 17 static Seobeo_Route *g_routes = NULL; | |
| 18 | |
| 19 void Seobeo_Router_Init() | |
| 20 { | |
| 21 g_routes = NULL; | |
| 22 } | |
| 23 | |
| 24 void Seobeo_Router_Register(const char *method, const char *path_pattern, Seobeo_Route_Handler handler) | |
| 25 { | |
| 26 Seobeo_Route route = {0}; | |
| 27 | |
| 28 route.method = strdup(method); | |
| 29 route.path_pattern = strdup(path_pattern); | |
| 30 route.handler = handler; | |
| 31 | |
| 32 // save it as global variable | |
| 33 route.path_segments = Dowa_String_Split(path_pattern, "/", strlen(path_pattern), 1, NULL); | |
| 34 route.segment_count = Dowa_Array_Length(route.path_segments); | |
| 35 route.is_param = (boolean*)malloc(sizeof(boolean) * route.segment_count); | |
| 36 | |
| 37 for (size_t i = 0; i < route.segment_count; i++) | |
| 38 route.is_param[i] = (route.path_segments[i][0] == ':'); | |
| 39 | |
| 40 Dowa_Array_Push(g_routes, route); | |
| 41 } | |
| 42 | |
| 43 // Match route and extract path parameters | |
| 44 static boolean match_route_and_extract( | |
| 45 Seobeo_Route *route, | |
| 46 const char *request_path, | |
| 47 Seobeo_Request_Entry **pp_request_map, | |
| 48 Dowa_Arena *p_arena) | |
| 49 { | |
| 50 Dowa_Arena *p_temp_arena = Dowa_Arena_Create(1024); | |
| 51 char **request_segments = Dowa_String_Split(request_path, "/", strlen(request_path), 1, p_temp_arena); | |
| 52 size_t request_segment_count = Dowa_Array_Length(request_segments); | |
| 53 // Check segment count matches | |
| 54 if (request_segment_count != route->segment_count) | |
| 55 { | |
| 56 Dowa_Arena_Free(p_temp_arena); | |
| 57 return FALSE; | |
| 58 } | |
| 59 | |
| 60 for (size_t i = 0; i < route->segment_count; i++) | |
| 61 { | |
| 62 // parameters | |
| 63 if (route->is_param[i]) | |
| 64 { | |
| 65 char *param_name = route->path_segments[i]; // e.g., ":id" | |
| 66 char *param_value = request_segments[i]; // e.g., "123" | |
| 67 | |
| 68 // Should Copy to arena | |
| 69 char *key = Dowa_String_Copy_Arena(param_name, p_arena); | |
| 70 char *value = Dowa_String_Copy_Arena(param_value, p_arena); | |
| 71 Dowa_HashMap_Push_Arena(*pp_request_map, key, value, p_arena); | |
| 72 } | |
| 73 else | |
| 74 { | |
| 75 // Does not match. | |
| 76 if (strcmp(route->path_segments[i], request_segments[i]) != 0) | |
| 77 { | |
| 78 Dowa_Arena_Free(p_temp_arena); | |
| 79 return FALSE; | |
| 80 } | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 Dowa_Arena_Free(p_temp_arena); | |
| 85 return TRUE; | |
| 86 } | |
| 87 | |
| 88 Seobeo_Route_Handler Seobeo_Router_Find_Handler(const char *method, | |
| 89 const char *path, | |
| 90 Seobeo_Request_Entry **pp_request_map, | |
| 91 Dowa_Arena *p_arena) { | |
| 92 if (g_routes == NULL) | |
| 93 { | |
| 94 return NULL; | |
| 95 } | |
| 96 | |
| 97 size_t route_count = Dowa_Array_Length(g_routes); | |
| 98 for (size_t i = 0; i < route_count; i++) | |
| 99 { | |
| 100 Seobeo_Route *route = &g_routes[i]; | |
| 101 if (strcmp(route->method, method) != 0) | |
| 102 { | |
| 103 continue; | |
| 104 } | |
| 105 | |
| 106 if (match_route_and_extract(route, path, pp_request_map, p_arena)) | |
| 107 { | |
| 108 return route->handler; | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 return NULL; | |
| 113 } | |
| 114 | |
| 115 void Seobeo_Router_Send_Response(Seobeo_Handle *p_handle, | |
| 116 Seobeo_Request_Entry *p_response_map, | |
| 117 Dowa_Arena *p_arena) | |
| 118 { | |
| 119 if (p_response_map == NULL) | |
| 120 { | |
| 121 char *header = Dowa_Arena_Allocate(p_arena, 1024); | |
| 122 Seobeo_Web_Header_Generate(header, HTTP_INTERNAL_ERROR, "text/plain", 21); | |
| 123 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); | |
| 124 Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); | |
| 125 Seobeo_Handle_Flush(p_handle); | |
| 126 return; | |
| 127 } | |
| 128 | |
| 129 // Header | |
| 130 int status = HTTP_OK; | |
| 131 void *p_status_kv = Dowa_HashMap_Get_Ptr(p_response_map, "status"); | |
| 132 if (p_status_kv) | |
| 133 { | |
| 134 const char *status_str = ((Seobeo_Request_Entry*)p_status_kv)->value; | |
| 135 status = atoi(status_str); | |
| 136 } | |
| 137 | |
| 138 // Body | |
| 139 const char *body = ""; | |
| 140 void *p_body_kv = Dowa_HashMap_Get_Ptr(p_response_map, "body"); | |
| 141 if (p_body_kv) | |
| 142 { | |
| 143 body = ((Seobeo_Request_Entry*)p_body_kv)->value; | |
| 144 } | |
| 145 | |
| 146 // Default text plain | |
| 147 const char *content_type = "text/plain"; | |
| 148 void *p_content_type_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-type"); | |
| 149 if (p_content_type_kv) | |
| 150 { | |
| 151 content_type = ((Seobeo_Request_Entry*)p_content_type_kv)->value; | |
| 152 } | |
| 153 | |
| 154 char *header = Dowa_Arena_Allocate(p_arena, 1024); | |
| 155 Seobeo_Web_Header_Generate(header, status, content_type, strlen(body)); | |
| 156 | |
| 157 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); | |
| 158 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, strlen(body)); | |
| 159 Seobeo_Handle_Flush(p_handle); | |
| 160 } | |
| 161 | |
| 162 void Seobeo_Router_Destroy() | |
| 163 { | |
| 164 if (g_routes == NULL) | |
| 165 return; | |
| 166 | |
| 167 size_t route_count = Dowa_Array_Length(g_routes); | |
| 168 for (size_t i = 0; i < route_count; i++) | |
| 169 { | |
| 170 Seobeo_Route *route = &g_routes[i]; | |
| 171 if (route->method) free(route->method); | |
| 172 if (route->path_pattern) free(route->path_pattern); | |
| 173 if (route->path_segments) Dowa_Array_Free(route->path_segments); | |
| 174 if (route->is_param) free(route->is_param); | |
| 175 } | |
| 176 Dowa_Array_Free(g_routes); | |
| 177 g_routes = NULL; | |
| 178 } |