Mercurial
diff seobeo/s_web.c @ 257:609d3c6aff4e
[seobeo] Add persistent SSE streams
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 04 Aug 2026 16:49:11 -0700 |
| parents | 117c4d53c9a4 |
| children | 1f9877b637e9 |
line wrap: on
line diff
--- a/seobeo/s_web.c Tue Aug 04 16:49:01 2026 -0700 +++ b/seobeo/s_web.c Tue Aug 04 16:49:11 2026 -0700 @@ -136,7 +136,7 @@ } token = strtok(NULL, ", \t"); } - free(copy); + Dowa_Free(copy); return found; } @@ -243,6 +243,29 @@ #endif // --- Try to match streaming route first --- + Seobeo_SSE_Handler sse_handler = Seobeo_Router_Find_SSE_Handler( + method, + path, + &p_req_map, + p_request_arena); + if (sse_handler != NULL) + { + Seobeo_SSE_Stream *p_stream = Seobeo_SSE_Server_Attach( + p_cli_handle, + path); + if (p_stream) + { + sse_handler(p_stream, p_req_map, p_response_arena); + should_keep_alive = Seobeo_SSE_Is_Open(p_stream); + } + else + { + should_keep_alive = FALSE; + } + goto clean_up_arenas; + } + + // --- Try to match generic streaming route --- Seobeo_Stream_Handler stream_handler = Seobeo_Router_Find_Stream_Handler(method, path, &p_req_map, p_request_arena); if (stream_handler != NULL) { @@ -356,7 +379,7 @@ (const uint8*)file_content, (uint32)body_size); Seobeo_Handle_Flush(p_cli_handle); - free(file_content); + Dowa_Free(file_content); } else { @@ -690,6 +713,7 @@ char *path_pattern; // "/v1/users/:id/posts/:post_id" Seobeo_Route_Handler handler; Seobeo_Stream_Handler stream_handler; // For streaming responses + Seobeo_SSE_Handler sse_handler; // Pre-parsed path segments for efficient matching char **path_segments; // ["v1", "users", ":id", "posts", ":post_id"] @@ -712,6 +736,7 @@ route.path_pattern = strdup(path_pattern); route.handler = handler; route.stream_handler = NULL; + route.sse_handler = NULL; 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); @@ -730,6 +755,7 @@ route.path_pattern = strdup(path_pattern); route.handler = NULL; route.stream_handler = handler; + route.sse_handler = NULL; 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); @@ -740,6 +766,33 @@ Dowa_Array_Push(g_routes, route); } +void Seobeo_Router_Register_SSE( + const char *path_pattern, + Seobeo_SSE_Handler handler) +{ + Seobeo_Route route = {0}; + + route.method = strdup("GET"); + route.path_pattern = strdup(path_pattern); + route.handler = NULL; + route.stream_handler = NULL; + route.sse_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); +} + static boolean match_route_and_extract( Seobeo_Route *route, const char *request_path, @@ -759,20 +812,8 @@ for (size_t i = 0; i < route->segment_count; i++) { - // parameters - if (route->is_param[i]) + 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); @@ -780,7 +821,16 @@ } } } - + + for (size_t i = 0; i < route->segment_count; i++) + { + if (!route->is_param[i]) + continue; + char *key = Dowa_String_Copy_Arena(route->path_segments[i], p_arena); + char *value = Dowa_String_Copy_Arena(request_segments[i], p_arena); + Dowa_HashMap_Push_Arena(*pp_request_map, key, value, p_arena); + } + Dowa_Arena_Free(p_temp_arena); return TRUE; } @@ -835,6 +885,34 @@ return NULL; } +Seobeo_SSE_Handler Seobeo_Router_Find_SSE_Handler( + const char *method, + const char *path, + Seobeo_Request_Entry **pp_request_map, + Dowa_Arena *p_arena) +{ + if (g_routes == NULL || method == NULL || path == 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 (route->sse_handler && + match_route_and_extract( + route, + path, + pp_request_map, + p_arena)) + { + return route->sse_handler; + } + } + return NULL; +} + void Seobeo_Router_Send_Response( Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, @@ -958,6 +1036,7 @@ void Seobeo_Router_Destroy() { + Seobeo_SSE_Server_Destroy(); if (g_routes == NULL) return; @@ -965,10 +1044,10 @@ 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->method) Dowa_Free(route->method); + if (route->path_pattern) Dowa_Free(route->path_pattern); if (route->path_segments) Dowa_Array_Free(route->path_segments); - if (route->is_param) free(route->is_param); + if (route->is_param) Dowa_Free(route->is_param); } Dowa_Array_Free(g_routes); g_routes = NULL;