Mercurial
diff seobeo/s_web.c @ 195:f8f5004a920a
Merging back hg-web-tip
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 27 Jan 2026 06:51:44 -0800 |
| parents | a69485d9f2e1 |
| children |
line wrap: on
line diff
--- a/seobeo/s_web.c Sat Jan 24 06:37:43 2026 -0800 +++ b/seobeo/s_web.c Tue Jan 27 06:51:44 2026 -0800 @@ -151,6 +151,11 @@ void *p_conn_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Connection"); const char *conn_header = p_conn_kv ? ((Seobeo_Request_Entry*)p_conn_kv)->value : NULL; + void *p_real_ip_kv = Dowa_HashMap_Get_Ptr(p_req_map, "X-Real-IP"); + const char *real_ip = p_real_ip_kv ? ((Seobeo_Request_Entry*)p_real_ip_kv)->value : NULL; + if (!real_ip) + real_ip = p_cli_handle->host; + if (conn_header) { if (connection_header_contains(conn_header, "close")) @@ -185,6 +190,7 @@ // --- Check for WebSocket upgrade request --- #ifdef SEOBEO_WEBSOCKET_SERVER + Seobeo_Log(SEOBEO_DEBUG, "Web socket path \n"); if (Seobeo_WebSocket_Server_Handle_Upgrade(p_cli_handle, p_req_map, path)) { Seobeo_Log(SEOBEO_INFO, "WebSocket connection established\n"); @@ -194,7 +200,15 @@ } #endif - // --- Try to match API route first --- + // --- Try to match streaming route first --- + Seobeo_Stream_Handler stream_handler = Seobeo_Router_Find_Stream_Handler(method, path, &p_req_map, p_request_arena); + if (stream_handler != NULL) + { + stream_handler(p_cli_handle, p_req_map, p_response_arena); + goto clean_up_arenas; + } + + // --- Try to match API route --- Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena); if (handler != NULL) { @@ -333,7 +347,10 @@ break; if (r == 0) - return 1; // EAGAIN, try again later TODO: Add this as part of Handle struct. + { + Seobeo_Log(SEOBEO_INFO, "Waiting?\n"); + continue; // EAGAIN, try again later TODO: Add this as part of Handle struct. + } } // "METHOD SP PATH SP VERSION CRLF" @@ -448,7 +465,6 @@ char *next = strstr(line, "\r\n"); if (!next) break; - // split at colon char *colon = memchr(line, ':', next - line); if (colon) { @@ -472,7 +488,6 @@ memcpy(val, val_start, value_len); val[value_len] = '\0'; - // Both key and value are arena-allocated, hashmap will use them Dowa_HashMap_Push_Arena(*pp_map, key, val, p_arena); } @@ -490,7 +505,6 @@ Seobeo_Log(SEOBEO_DEBUG, "Content-Length=%zu, reading body in chunks...\n", body_len); - // Allocate buffer for entire body char *body = Dowa_Arena_Allocate(p_arena, body_len + 1); if (!body) { @@ -606,6 +620,7 @@ char *method; // "GET", "POST", "PUT", "DELETE" char *path_pattern; // "/v1/users/:id/posts/:post_id" Seobeo_Route_Handler handler; + Seobeo_Stream_Handler stream_handler; // For streaming responses // Pre-parsed path segments for efficient matching char **path_segments; // ["v1", "users", ":id", "posts", ":post_id"] @@ -627,6 +642,25 @@ route.method = strdup(method); route.path_pattern = strdup(path_pattern); route.handler = handler; + route.stream_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); + + 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); +} + +void Seobeo_Router_Register_Stream(const char *method, const char *path_pattern, Seobeo_Stream_Handler handler) +{ + Seobeo_Route route = {0}; + + route.method = strdup(method); + route.path_pattern = strdup(path_pattern); + route.handler = NULL; + route.stream_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); @@ -709,6 +743,29 @@ return NULL; } +Seobeo_Stream_Handler Seobeo_Router_Find_Stream_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->stream_handler && match_route_and_extract(route, path, pp_request_map, p_arena)) + return route->stream_handler; + } + + return NULL; +} + void Seobeo_Router_Send_Response( Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, @@ -744,24 +801,23 @@ 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; - } 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; - } - size_t body_length = strlen(body); + // TODO: Update this to be integer + size_t body_length; void *p_content_length_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-length"); if (p_content_length_kv) { const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value; body_length = atoi(content_length_str); } + else + body_length = strlen(body); char *header = Dowa_Arena_Allocate(p_arena, 4096); Seobeo_Web_Header_Generate_KeepAlive(header, status, content_type, body_length, keep_alive); @@ -782,6 +838,8 @@ free(temp); } + printf("hEADER %s\n", header); + Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); Seobeo_Handle_Flush(p_handle); @@ -804,3 +862,44 @@ Dowa_Array_Free(g_routes); g_routes = NULL; } + +// Written by AI. I don't know what it does. +void Seobeo_Url_Decode(char *dst, const char *src) +{ + char a, b; + while (*src) { + /* Check if we have a % followed by two valid hex characters */ + if (*src == '%' && src[1] && src[2]) { + a = src[1]; + b = src[2]; + + /* Manual isxdigit check and conversion for 'a' */ + int a_val = -1; + if (a >= '0' && a <= '9') a_val = a - '0'; + else if (a >= 'a' && a <= 'f') a_val = a - 'a' + 10; + else if (a >= 'A' && a <= 'F') a_val = a - 'A' + 10; + + /* Manual isxdigit check and conversion for 'b' */ + int b_val = -1; + if (b >= '0' && b <= '9') b_val = b - '0'; + else if (b >= 'a' && b <= 'f') b_val = b - 'a' + 10; + else if (b >= 'A' && b <= 'F') b_val = b - 'A' + 10; + + /* If both were valid hex, combine them */ + if (a_val != -1 && b_val != -1) { + *dst++ = (char)((a_val << 4) | b_val); + src += 3; + continue; + } + } + + /* Handle '+' as space, otherwise copy character literally */ + if (*src == '+') { + *dst++ = ' '; + } else { + *dst++ = *src; + } + src++; + } + *dst = '\0'; +}