Mercurial
comparison seobeo/s_web.c @ 183:a8976a008a9d
[BenchMark] Added bun bench mark to test seoboe vs other popular benchmarks.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 21:19:08 -0800 |
| parents | bdcc610eeed8 |
| children | 8c74204fd362 |
comparison
equal
deleted
inserted
replaced
| 179:8d17f6e6e290 | 183:a8976a008a9d |
|---|---|
| 1 #include "seobeo/seobeo.h" | 1 #include "seobeo/seobeo.h" |
| 2 #include <strings.h> // for strcasecmp | |
| 3 #include <time.h> // for time_t | |
| 2 | 4 |
| 3 static char g_folder_path[512] = "."; | 5 static char g_folder_path[512] = "."; |
| 4 | 6 |
| 5 char* Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size) | 7 char* Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size) |
| 6 { | 8 { |
| 33 } | 35 } |
| 34 | 36 |
| 35 void Seobeo_Web_Header_Generate( | 37 void Seobeo_Web_Header_Generate( |
| 36 void *buffer, int status, | 38 void *buffer, int status, |
| 37 const char *content_type, const int content_length) | 39 const char *content_type, const int content_length) |
| 40 { | |
| 41 Seobeo_Web_Header_Generate_KeepAlive(buffer, status, content_type, content_length, FALSE); | |
| 42 } | |
| 43 | |
| 44 void Seobeo_Web_Header_Generate_KeepAlive( | |
| 45 void *buffer, int status, | |
| 46 const char *content_type, const int content_length, | |
| 47 boolean keep_alive) | |
| 38 { | 48 { |
| 39 const char *status_text; | 49 const char *status_text; |
| 40 switch(status) | 50 switch(status) |
| 41 { | 51 { |
| 42 case HTTP_OK: status_text = "OK"; break; | 52 case HTTP_OK: status_text = "OK"; break; |
| 48 case HTTP_FORBIDDEN: status_text = "Forbidden"; break; | 58 case HTTP_FORBIDDEN: status_text = "Forbidden"; break; |
| 49 case HTTP_NOT_FOUND: status_text = "Not Found"; break; | 59 case HTTP_NOT_FOUND: status_text = "Not Found"; break; |
| 50 case HTTP_INTERNAL_ERROR: status_text = "Internal Server Error"; break; | 60 case HTTP_INTERNAL_ERROR: status_text = "Internal Server Error"; break; |
| 51 default: status_text = "Unknown"; break; | 61 default: status_text = "Unknown"; break; |
| 52 } | 62 } |
| 53 | 63 |
| 54 sprintf( | 64 sprintf( |
| 55 buffer, | 65 buffer, |
| 56 "HTTP/1.1 %d %s\r\n" | 66 "HTTP/1.1 %d %s\r\n" |
| 57 "Content-Type: %s\r\n" | 67 "Content-Type: %s\r\n" |
| 58 "Content-Length: %d\r\n" | 68 "Content-Length: %d\r\n" |
| 59 "Connection: close\r\n" | 69 "Connection: %s\r\n" |
| 60 "\r\n", | 70 "\r\n", |
| 61 status, status_text, content_type, content_length | 71 status, status_text, content_type, content_length, |
| 72 keep_alive ? "keep-alive" : "close" | |
| 62 ); | 73 ); |
| 63 } | 74 } |
| 64 | 75 |
| 65 void Seobeo_Web_HandleClientRequest(Seobeo_Handle *p_cli_handle, | 76 // Default arena size (5MB) - will allocate more if Content-Length requires it |
| 66 Seobeo_Cache_Entry *p_html_cache) | 77 #define DEFAULT_ARENA_SIZE (5 * 1024 * 1024) |
| 67 { | 78 |
| 68 // TODO: This should be splitted up instead of handling up to 50 MB as it will fail more often... | 79 // Helper to check if Connection header contains a specific value (case-insensitive) |
| 69 Dowa_Arena *p_request_arena = Dowa_Arena_Create(50*1024*1024); // 50 MB because of files... | 80 static boolean connection_header_contains(const char *header_value, const char *target) |
| 70 if (!p_request_arena) { perror("Dowa_Arena_Create request"); goto clean_up; } | 81 { |
| 71 | 82 if (!header_value || !target) return FALSE; |
| 72 Dowa_Arena *p_response_arena = Dowa_Arena_Create(50*1024*1024); // 50 MB for response | 83 |
| 73 if (!p_response_arena) { perror("Dowa_Arena_Create response"); goto clean_up; } | 84 // Make a copy to tokenize |
| 85 char *copy = strdup(header_value); | |
| 86 if (!copy) return FALSE; | |
| 87 | |
| 88 boolean found = FALSE; | |
| 89 char *token = strtok(copy, ", \t"); | |
| 90 while (token) { | |
| 91 if (strcasecmp(token, target) == 0) { | |
| 92 found = TRUE; | |
| 93 break; | |
| 94 } | |
| 95 token = strtok(NULL, ", \t"); | |
| 96 } | |
| 97 free(copy); | |
| 98 return found; | |
| 99 } | |
| 100 | |
| 101 // Returns TRUE if connection should be kept alive, FALSE if it should be closed | |
| 102 boolean Seobeo_Web_ClientHandle_Request(Seobeo_Handle *p_cli_handle, | |
| 103 Seobeo_Cache_Entry *p_html_cache, | |
| 104 boolean use_keep_alive) | |
| 105 { | |
| 106 boolean should_keep_alive = FALSE; | |
| 107 | |
| 108 // Start with default arena size (5MB) | |
| 109 size_t arena_size = DEFAULT_ARENA_SIZE; | |
| 110 | |
| 111 // We'll peek at Content-Length to potentially allocate larger arena | |
| 112 // For now, start with default and handle body reading separately | |
| 113 Dowa_Arena *p_request_arena = Dowa_Arena_Create(arena_size); | |
| 114 if (!p_request_arena) { perror("Dowa_Arena_Create request"); return FALSE; } | |
| 115 | |
| 116 Dowa_Arena *p_response_arena = Dowa_Arena_Create(arena_size); | |
| 117 if (!p_response_arena) { perror("Dowa_Arena_Create response"); Dowa_Arena_Free(p_request_arena); return FALSE; } | |
| 74 | 118 |
| 75 void *p_response_header = Dowa_Arena_Allocate(p_response_arena, 1024*5); // 5Kb | 119 void *p_response_header = Dowa_Arena_Allocate(p_response_arena, 1024*5); // 5Kb |
| 76 if (!p_response_header) { perror("Dowa_Arena_Allocate"); goto clean_up; } | 120 if (!p_response_header) { perror("Dowa_Arena_Allocate"); goto clean_up_arenas; } |
| 77 | 121 |
| 78 Seobeo_Request_Entry *p_req_map = NULL; | 122 Seobeo_Request_Entry *p_req_map = NULL; |
| 79 int parse_result = Seobeo_Web_Header_Parse(p_cli_handle, &p_req_map, p_request_arena); | 123 int parse_result = Seobeo_Web_Header_Parse(p_cli_handle, &p_req_map, p_request_arena); |
| 80 | 124 |
| 81 if (parse_result != 0 && parse_result != 1) | 125 // Handle parse errors |
| 82 { | 126 if (parse_result < 0) { |
| 83 Seobeo_Log(SEOBEO_ERROR, "Seobeo_Web_Header_Parse failed with code %d\n", parse_result); | 127 // Fatal error or connection closed |
| 128 Seobeo_Log(SEOBEO_DEBUG, "Seobeo_Web_Header_Parse failed with code %d\n", parse_result); | |
| 129 if (parse_result == -2) { | |
| 130 // Connection closed by client - don't send error response | |
| 131 goto clean_up_arenas; | |
| 132 } | |
| 84 Seobeo_Web_Header_Generate(p_response_header, | 133 Seobeo_Web_Header_Generate(p_response_header, |
| 85 HTTP_BAD_REQUEST, | 134 HTTP_BAD_REQUEST, |
| 86 "text/plain", 0); | 135 "text/plain", 0); |
| 87 Seobeo_Handle_Queue(p_cli_handle, | 136 Seobeo_Handle_Queue(p_cli_handle, |
| 88 (const uint8*)p_response_header, | 137 (const uint8*)p_response_header, |
| 89 (uint32)strlen(p_response_header)); | 138 (uint32)strlen(p_response_header)); |
| 90 Seobeo_Handle_Flush(p_cli_handle); | 139 Seobeo_Handle_Flush(p_cli_handle); |
| 91 goto clean_up; | 140 goto clean_up_arenas; |
| 92 } | 141 } |
| 93 | 142 |
| 94 Seobeo_Log(SEOBEO_DEBUG, "Parse completed with code %d\n", parse_result); | 143 // Determine keep-alive based on HTTP version and Connection header |
| 95 | 144 // HTTP/1.1: keep-alive by default unless "Connection: close" |
| 96 // Recording IP to see who is ddosing or any web scrappers... | 145 // HTTP/1.0: close by default unless "Connection: keep-alive" |
| 97 void *p_real_ip_kv = Dowa_HashMap_Get_Ptr(p_req_map, "X-Real-IP"); | 146 |
| 98 const char *real_ip = p_real_ip_kv ? ((Seobeo_Request_Entry*)p_real_ip_kv)->value : NULL; | 147 void *p_ver_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Version"); |
| 99 // Fallback | 148 const char *http_version = p_ver_kv ? ((Seobeo_Request_Entry*)p_ver_kv)->value : "HTTP/1.0"; |
| 100 if (!real_ip) | 149 boolean is_http11 = (strstr(http_version, "1.1") != NULL); |
| 101 { | 150 |
| 102 void *p_forwarded_kv = Dowa_HashMap_Get_Ptr(p_req_map, "X-Forwarded-For"); | 151 void *p_conn_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Connection"); |
| 103 real_ip = p_forwarded_kv ? ((Seobeo_Request_Entry*)p_forwarded_kv)->value : NULL; | 152 const char *conn_header = p_conn_kv ? ((Seobeo_Request_Entry*)p_conn_kv)->value : NULL; |
| 104 } | 153 |
| 105 // Fallback | 154 if (conn_header) { |
| 106 if (!real_ip) | 155 // Explicit Connection header - check for keep-alive or close |
| 107 real_ip = p_cli_handle->host; | 156 if (connection_header_contains(conn_header, "close")) { |
| 157 should_keep_alive = FALSE; | |
| 158 } else if (connection_header_contains(conn_header, "keep-alive")) { | |
| 159 should_keep_alive = use_keep_alive; | |
| 160 } else { | |
| 161 // Unknown value, use version default | |
| 162 should_keep_alive = is_http11 && use_keep_alive; | |
| 163 } | |
| 164 } else { | |
| 165 // No Connection header - use HTTP version defaults | |
| 166 should_keep_alive = is_http11 && use_keep_alive; | |
| 167 } | |
| 108 | 168 |
| 109 void *p_method_kv = Dowa_HashMap_Get_Ptr(p_req_map, "HTTP_Method"); | 169 void *p_method_kv = Dowa_HashMap_Get_Ptr(p_req_map, "HTTP_Method"); |
| 110 const char *method = p_method_kv ? ((Seobeo_Request_Entry*)p_method_kv)->value : NULL; | 170 const char *method = p_method_kv ? ((Seobeo_Request_Entry*)p_method_kv)->value : NULL; |
| 111 | 171 |
| 112 void *p_path_kv_log = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); | 172 void *p_path_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); |
| 113 const char *path_log = p_path_kv_log ? ((Seobeo_Request_Entry*)p_path_kv_log)->value : "/"; | 173 const char *path = p_path_kv ? ((Seobeo_Request_Entry*)p_path_kv)->value : "/"; |
| 114 | |
| 115 Seobeo_Log(SEOBEO_INFO, "%s - %s %s\n", | |
| 116 real_ip ? real_ip : "unknown", | |
| 117 method ? method : "UNKNOWN", | |
| 118 path_log); | |
| 119 | |
| 120 Seobeo_Log(SEOBEO_DEBUG, "Parsed request, method=%s\n", method ? method : "NULL"); | |
| 121 | 174 |
| 122 if (!method) | 175 if (!method) |
| 123 { | 176 { |
| 124 Seobeo_Log(SEOBEO_ERROR, "No HTTP method found in request\n"); | 177 Seobeo_Log(SEOBEO_DEBUG, "No HTTP method found in request\n"); |
| 125 Seobeo_Web_Header_Generate(p_response_header, | 178 Seobeo_Web_Header_Generate(p_response_header, |
| 126 HTTP_BAD_REQUEST, | 179 HTTP_BAD_REQUEST, |
| 127 "text/plain", 0); | 180 "text/plain", 0); |
| 128 Seobeo_Handle_Queue(p_cli_handle, | 181 Seobeo_Handle_Queue(p_cli_handle, |
| 129 (const uint8*)p_response_header, | 182 (const uint8*)p_response_header, |
| 130 (uint32)strlen(p_response_header)); | 183 (uint32)strlen(p_response_header)); |
| 131 Seobeo_Handle_Flush(p_cli_handle); | 184 Seobeo_Handle_Flush(p_cli_handle); |
| 132 goto clean_up; | 185 should_keep_alive = FALSE; |
| 133 } | 186 goto clean_up_arenas; |
| 134 | 187 } |
| 135 void *p_path_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); | |
| 136 const char *path = p_path_kv ? ((Seobeo_Request_Entry*)p_path_kv)->value : "/"; | |
| 137 | 188 |
| 138 // --- Check for WebSocket upgrade request --- | 189 // --- Check for WebSocket upgrade request --- |
| 139 #ifdef SEOBEO_WEBSOCKET_SERVER | 190 #ifdef SEOBEO_WEBSOCKET_SERVER |
| 140 Seobeo_Log(SEOBEO_DEBUG, "Web soceket path \n"); | |
| 141 if (Seobeo_WebSocket_Server_Handle_Upgrade(p_cli_handle, p_req_map, path)) | 191 if (Seobeo_WebSocket_Server_Handle_Upgrade(p_cli_handle, p_req_map, path)) |
| 142 { | 192 { |
| 143 Seobeo_Log(SEOBEO_INFO, "WebSocket connection established\n"); | 193 Seobeo_Log(SEOBEO_INFO, "WebSocket connection established\n"); |
| 144 if (p_request_arena) | 194 Dowa_Arena_Free(p_request_arena); |
| 145 Dowa_Arena_Free(p_request_arena); | 195 Dowa_Arena_Free(p_response_arena); |
| 146 if (p_response_arena) | 196 return FALSE; // WebSocket takes over, don't keep-alive in HTTP sense |
| 147 Dowa_Arena_Free(p_response_arena); | |
| 148 return; | |
| 149 } | 197 } |
| 150 #endif | 198 #endif |
| 151 | 199 |
| 152 // --- Try to match API route first --- | 200 // --- Try to match API route first --- |
| 153 Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena); | 201 Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena); |
| 154 if (handler != NULL) | 202 if (handler != NULL) |
| 155 { | 203 { |
| 156 Seobeo_Request_Entry *p_response_map = handler(p_req_map, p_response_arena); | 204 Seobeo_Request_Entry *p_response_map = handler(p_req_map, p_response_arena); |
| 157 Seobeo_Router_Send_Response(p_cli_handle, p_response_map, p_response_arena); | 205 Seobeo_Router_Send_Response_KeepAlive(p_cli_handle, p_response_map, p_response_arena, should_keep_alive); |
| 158 goto clean_up; | 206 goto clean_up_arenas; |
| 159 } | 207 } |
| 160 | 208 |
| 161 // --- Static files fallback for GET --- | 209 // --- Static files fallback for GET (use original large arena logic) --- |
| 162 if (strcmp(method, "GET") == 0) | 210 if (strcmp(method, "GET") == 0) |
| 163 { | 211 { |
| 164 void *p_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); | 212 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024); |
| 165 const char *path = p_kv ? ((Seobeo_Request_Entry*)p_kv)->value : NULL; | |
| 166 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)5 * 1024); // 5Kb only for path | |
| 167 | 213 |
| 168 if (!path || strcmp(path, "/") == 0) | 214 if (!path || strcmp(path, "/") == 0) |
| 169 { | 215 { |
| 170 strcpy(file_path, "index.html"); | 216 strcpy(file_path, "index.html"); |
| 171 } | 217 } |
| 181 } | 227 } |
| 182 else | 228 else |
| 183 strcpy(file_path, path); | 229 strcpy(file_path, path); |
| 184 } | 230 } |
| 185 | 231 |
| 186 // Check if file is in cache, load if not | |
| 187 void *p_file_kv = Dowa_HashMap_Get_Ptr(p_html_cache, file_path); | 232 void *p_file_kv = Dowa_HashMap_Get_Ptr(p_html_cache, file_path); |
| 188 const char *file_content = NULL; | 233 const char *file_content = NULL; |
| 189 size_t body_size = 0; | 234 size_t body_size = 0; |
| 190 | 235 |
| 191 if (p_file_kv) | 236 if (p_file_kv) |
| 192 { | 237 { |
| 193 // File is cached - use stored size for binary file support | |
| 194 Seobeo_Cached_File *cached = ((Seobeo_Cache_Entry*)p_file_kv)->value; | 238 Seobeo_Cached_File *cached = ((Seobeo_Cache_Entry*)p_file_kv)->value; |
| 195 file_content = cached->content; | 239 file_content = cached->content; |
| 196 body_size = cached->size; | 240 body_size = cached->size; |
| 197 } | 241 } |
| 198 else | 242 else |
| 207 } | 251 } |
| 208 } | 252 } |
| 209 | 253 |
| 210 if (!file_content) | 254 if (!file_content) |
| 211 { | 255 { |
| 212 Seobeo_Web_Header_Generate(p_response_header, | 256 Seobeo_Web_Header_Generate_KeepAlive(p_response_header, |
| 213 HTTP_NOT_FOUND, | 257 HTTP_NOT_FOUND, |
| 214 "text/html", 0); | 258 "text/html", 0, should_keep_alive); |
| 215 Seobeo_Handle_Queue(p_cli_handle, | 259 Seobeo_Handle_Queue(p_cli_handle, |
| 216 (const uint8*)p_response_header, | 260 (const uint8*)p_response_header, |
| 217 (uint32)strlen(p_response_header)); | 261 (uint32)strlen(p_response_header)); |
| 218 Seobeo_Handle_Flush(p_cli_handle); | 262 Seobeo_Handle_Flush(p_cli_handle); |
| 219 goto clean_up; | 263 goto clean_up_arenas; |
| 220 } | 264 } |
| 221 | 265 |
| 222 Seobeo_Log(SEOBEO_DEBUG, "Serving Static Files\n"); | |
| 223 // Serve static file | |
| 224 const char *mime = "application/octet-stream"; | 266 const char *mime = "application/octet-stream"; |
| 225 if (strstr(file_path, ".html")) mime = "text/html; charset=utf-8"; | 267 if (strstr(file_path, ".html")) mime = "text/html; charset=utf-8"; |
| 226 else if (strstr(file_path, ".css")) mime = "text/css"; | 268 else if (strstr(file_path, ".css")) mime = "text/css"; |
| 227 else if (strstr(file_path, ".js")) mime = "application/javascript"; | 269 else if (strstr(file_path, ".js")) mime = "application/javascript"; |
| 228 else if (strstr(file_path, ".png")) mime = "image/png"; | 270 else if (strstr(file_path, ".png")) mime = "image/png"; |
| 229 else if (strstr(file_path, ".jpg") || strstr(file_path, ".jpeg")) mime = "image/jpeg"; | 271 else if (strstr(file_path, ".jpg") || strstr(file_path, ".jpeg")) mime = "image/jpeg"; |
| 230 else if (strstr(file_path, ".webp")) mime = "image/webp"; | |
| 231 else if (strstr(file_path, ".gif")) mime = "image/gif"; | |
| 232 else if (strstr(file_path, ".svg")) mime = "image/svg+xml"; | |
| 233 else if (strstr(file_path, ".ico")) mime = "image/x-icon"; | |
| 234 else if (strstr(file_path, ".json")) mime = "application/json"; | 272 else if (strstr(file_path, ".json")) mime = "application/json"; |
| 235 else if (strstr(file_path, ".wasm")) mime = "application/wasm"; | 273 |
| 236 else if (strstr(file_path, ".mp4")) mime = "video/mp4"; | 274 Seobeo_Web_Header_Generate_KeepAlive(p_response_header, |
| 237 else if (strstr(file_path, ".webm")) mime = "video/webm"; | |
| 238 else if (strstr(file_path, ".glb")) mime = "model/gltf-binary"; | |
| 239 else if (strstr(file_path, ".gltf")) mime = "model/gltf+json"; | |
| 240 | |
| 241 Seobeo_Log(SEOBEO_DEBUG, "File path: %s\nBody Size: %zu\n", file_path, body_size); | |
| 242 | |
| 243 Seobeo_Web_Header_Generate(p_response_header, | |
| 244 HTTP_OK, | 275 HTTP_OK, |
| 245 mime, | 276 mime, |
| 246 body_size); | 277 body_size, should_keep_alive); |
| 247 | 278 |
| 248 Seobeo_Handle_Queue(p_cli_handle, | 279 Seobeo_Handle_Queue(p_cli_handle, |
| 249 (const uint8*)p_response_header, | 280 (const uint8*)p_response_header, |
| 250 (uint32)strlen(p_response_header)); | 281 (uint32)strlen(p_response_header)); |
| 251 Seobeo_Handle_Queue(p_cli_handle, | 282 Seobeo_Handle_Queue(p_cli_handle, |
| 252 (const uint8*)file_content, | 283 (const uint8*)file_content, |
| 253 (uint32)body_size); | 284 (uint32)body_size); |
| 254 Seobeo_Handle_Flush(p_cli_handle); | 285 Seobeo_Handle_Flush(p_cli_handle); |
| 255 Seobeo_Log(SEOBEO_DEBUG, "Request handled successfully\n"); | |
| 256 } | 286 } |
| 257 else | 287 else |
| 258 { | 288 { |
| 259 Seobeo_Web_Header_Generate(p_response_header, | 289 Seobeo_Web_Header_Generate_KeepAlive(p_response_header, |
| 260 HTTP_FORBIDDEN, | 290 HTTP_NOT_FOUND, |
| 261 "text/plain", 0); | 291 "text/plain", 0, should_keep_alive); |
| 262 Seobeo_Handle_Queue(p_cli_handle, | 292 Seobeo_Handle_Queue(p_cli_handle, |
| 263 (const uint8*)p_response_header, | 293 (const uint8*)p_response_header, |
| 264 (uint32)strlen(p_response_header)); | 294 (uint32)strlen(p_response_header)); |
| 265 Seobeo_Handle_Flush(p_cli_handle); | 295 Seobeo_Handle_Flush(p_cli_handle); |
| 266 } | 296 } |
| 267 goto clean_up; | 297 |
| 268 | 298 clean_up_arenas: |
| 269 clean_up: | |
| 270 Seobeo_Log(SEOBEO_INFO, "Clean up all Arenas\n"); | |
| 271 if (p_cli_handle) | |
| 272 Seobeo_Handle_Destroy(p_cli_handle); | |
| 273 if (p_request_arena) | 299 if (p_request_arena) |
| 274 Dowa_Arena_Free(p_request_arena); | 300 Dowa_Arena_Free(p_request_arena); |
| 275 if (p_response_arena) | 301 if (p_response_arena) |
| 276 Dowa_Arena_Free(p_response_arena); | 302 Dowa_Arena_Free(p_response_arena); |
| 277 return; | 303 return should_keep_alive; |
| 278 } | 304 } |
| 279 | |
| 280 | 305 |
| 281 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena) | 306 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena) |
| 282 { | 307 { |
| 283 while (1) | 308 while (1) |
| 284 { | 309 { |
| 544 Seobeo_Stream_Handle_Server_Accept(p_server_handle); | 569 Seobeo_Stream_Handle_Server_Accept(p_server_handle); |
| 545 if (!p_cli_handle) continue; | 570 if (!p_cli_handle) continue; |
| 546 | 571 |
| 547 if (fork() == 0) | 572 if (fork() == 0) |
| 548 { | 573 { |
| 549 Seobeo_Web_HandleClientRequest(p_cli_handle, | 574 Seobeo_Web_ClientHandle_Request(p_cli_handle, p_html_cache, FALSE); |
| 550 p_html_cache); | |
| 551 _exit(0); | 575 _exit(0); |
| 552 } | 576 } |
| 553 } | 577 } |
| 554 } | 578 } |
| 555 | 579 |
| 673 void Seobeo_Router_Send_Response( | 697 void Seobeo_Router_Send_Response( |
| 674 Seobeo_Handle *p_handle, | 698 Seobeo_Handle *p_handle, |
| 675 Seobeo_Request_Entry *p_response_map, | 699 Seobeo_Request_Entry *p_response_map, |
| 676 Dowa_Arena *p_arena) | 700 Dowa_Arena *p_arena) |
| 677 { | 701 { |
| 702 Seobeo_Router_Send_Response_KeepAlive(p_handle, p_response_map, p_arena, FALSE); | |
| 703 } | |
| 704 | |
| 705 void Seobeo_Router_Send_Response_KeepAlive( | |
| 706 Seobeo_Handle *p_handle, | |
| 707 Seobeo_Request_Entry *p_response_map, | |
| 708 Dowa_Arena *p_arena, | |
| 709 boolean keep_alive) | |
| 710 { | |
| 678 if (p_response_map == NULL) | 711 if (p_response_map == NULL) |
| 679 { | 712 { |
| 680 char *header = Dowa_Arena_Allocate(p_arena, 1024); | 713 char *header = Dowa_Arena_Allocate(p_arena, 1024); |
| 681 Seobeo_Web_Header_Generate(header, HTTP_INTERNAL_ERROR, "text/plain", 21); | 714 Seobeo_Web_Header_Generate_KeepAlive(header, HTTP_INTERNAL_ERROR, "text/plain", 21, keep_alive); |
| 682 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); | 715 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); |
| 683 Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); | 716 Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); |
| 684 Seobeo_Handle_Flush(p_handle); | 717 Seobeo_Handle_Flush(p_handle); |
| 685 return; | 718 return; |
| 686 } | 719 } |
| 714 const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value; | 747 const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value; |
| 715 body_length = atoi(content_length_str); | 748 body_length = atoi(content_length_str); |
| 716 } | 749 } |
| 717 | 750 |
| 718 char *header = Dowa_Arena_Allocate(p_arena, 4096); | 751 char *header = Dowa_Arena_Allocate(p_arena, 4096); |
| 719 Seobeo_Web_Header_Generate(header, status, content_type, body_length); | 752 Seobeo_Web_Header_Generate_KeepAlive(header, status, content_type, body_length, keep_alive); |
| 720 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++) | 753 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++) |
| 721 { | 754 { |
| 722 if ( | 755 if ( |
| 723 strstr(p_response_map[i].key, "status") || | 756 strstr(p_response_map[i].key, "status") || |
| 724 strstr(p_response_map[i].key, "body") || | 757 strstr(p_response_map[i].key, "body") || |
| 725 strstr(p_response_map[i].key, "content-type") || | 758 strstr(p_response_map[i].key, "content-type") || |
| 726 strstr(p_response_map[i].key, "content-length") | 759 strstr(p_response_map[i].key, "content-length") |
| 727 ) | 760 ) |
| 728 continue; | 761 continue; |
| 729 | 762 |
| 730 int32 current_header_len = strlen(header); | 763 int32 current_header_len = strlen(header); |
| 731 char *temp = malloc(sizeof(char) * 1024); | 764 char *temp = malloc(sizeof(char) * 1024); |
| 733 memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp)); | 766 memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp)); |
| 734 free(temp); | 767 free(temp); |
| 735 } | 768 } |
| 736 | 769 |
| 737 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); | 770 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); |
| 738 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); | 771 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); |
| 739 Seobeo_Handle_Flush(p_handle); | 772 Seobeo_Handle_Flush(p_handle); |
| 740 } | 773 } |
| 741 | 774 |
| 742 void Seobeo_Router_Destroy() | 775 void Seobeo_Router_Destroy() |
| 743 { | 776 { |