comparison seobeo/s_web.c @ 185:dfdd66825396

Merged in keep alive changes and mrjunejune changes.
author MrJuneJune <me@mrjunejune.com>
date Fri, 23 Jan 2026 22:22:30 -0800
parents 8c74204fd362
children 8cf4ec5e2191
comparison
equal deleted inserted replaced
182:d6ab5921fedc 185:dfdd66825396
1 #include "seobeo/seobeo.h" 1 #include "seobeo/seobeo.h"
2 #include <strings.h>
3 #include <time.h>
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 {
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 should_keep_alive = is_http11 && use_keep_alive; // Unknown value, use version default
162 }
163 else
164 should_keep_alive = is_http11 && use_keep_alive;
108 165
109 void *p_method_kv = Dowa_HashMap_Get_Ptr(p_req_map, "HTTP_Method"); 166 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; 167 const char *method = p_method_kv ? ((Seobeo_Request_Entry*)p_method_kv)->value : NULL;
111 168
112 void *p_path_kv_log = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); 169 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 : "/"; 170 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 171
122 if (!method) 172 if (!method)
123 { 173 {
124 Seobeo_Log(SEOBEO_ERROR, "No HTTP method found in request\n"); 174 Seobeo_Log(SEOBEO_DEBUG, "No HTTP method found in request\n");
125 Seobeo_Web_Header_Generate(p_response_header, 175 Seobeo_Web_Header_Generate(p_response_header,
126 HTTP_BAD_REQUEST, 176 HTTP_BAD_REQUEST,
127 "text/plain", 0); 177 "text/plain", 0);
128 Seobeo_Handle_Queue(p_cli_handle, 178 Seobeo_Handle_Queue(p_cli_handle,
129 (const uint8*)p_response_header, 179 (const uint8*)p_response_header,
130 (uint32)strlen(p_response_header)); 180 (uint32)strlen(p_response_header));
131 Seobeo_Handle_Flush(p_cli_handle); 181 Seobeo_Handle_Flush(p_cli_handle);
132 goto clean_up; 182 should_keep_alive = FALSE;
133 } 183 goto clean_up_arenas;
134 184 }
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 185
138 // --- Check for WebSocket upgrade request --- 186 // --- Check for WebSocket upgrade request ---
139 #ifdef SEOBEO_WEBSOCKET_SERVER 187 #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)) 188 if (Seobeo_WebSocket_Server_Handle_Upgrade(p_cli_handle, p_req_map, path))
142 { 189 {
143 Seobeo_Log(SEOBEO_INFO, "WebSocket connection established\n"); 190 Seobeo_Log(SEOBEO_INFO, "WebSocket connection established\n");
144 if (p_request_arena) 191 Dowa_Arena_Free(p_request_arena);
145 Dowa_Arena_Free(p_request_arena); 192 Dowa_Arena_Free(p_response_arena);
146 if (p_response_arena) 193 return FALSE; // WebSocket takes over, don't keep-alive in HTTP sense
147 Dowa_Arena_Free(p_response_arena);
148 return;
149 } 194 }
150 #endif 195 #endif
151 196
152 // --- Try to match API route first --- 197 // --- Try to match API route first ---
153 Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena); 198 Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena);
154 if (handler != NULL) 199 if (handler != NULL)
155 { 200 {
156 Seobeo_Request_Entry *p_response_map = handler(p_req_map, p_response_arena); 201 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); 202 Seobeo_Router_Send_Response_KeepAlive(p_cli_handle, p_response_map, p_response_arena, should_keep_alive);
158 goto clean_up; 203 goto clean_up_arenas;
159 } 204 }
160 205
161 // --- Static files fallback for GET --- 206 // --- Static files fallback for GET (use original large arena logic) ---
162 if (strcmp(method, "GET") == 0) 207 if (strcmp(method, "GET") == 0)
163 { 208 {
164 void *p_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); 209 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 210
168 if (!path || strcmp(path, "/") == 0) 211 if (!path || strcmp(path, "/") == 0)
169 { 212 {
170 strcpy(file_path, "index.html"); 213 strcpy(file_path, "index.html");
171 } 214 }
181 } 224 }
182 else 225 else
183 strcpy(file_path, path); 226 strcpy(file_path, path);
184 } 227 }
185 228
186 // Check if file is in cache, load if not
187 void *p_file_kv = Dowa_HashMap_Get_Ptr(p_html_cache, file_path); 229 void *p_file_kv = Dowa_HashMap_Get_Ptr(p_html_cache, file_path);
188 const char *file_content = NULL; 230 const char *file_content = NULL;
189 size_t body_size = 0; 231 size_t body_size = 0;
190 232
191 if (p_file_kv) 233 if (p_file_kv)
192 { 234 {
193 // File is cached - use stored size for binary file support
194 Seobeo_Cached_File *cached = ((Seobeo_Cache_Entry*)p_file_kv)->value; 235 Seobeo_Cached_File *cached = ((Seobeo_Cache_Entry*)p_file_kv)->value;
195 file_content = cached->content; 236 file_content = cached->content;
196 body_size = cached->size; 237 body_size = cached->size;
197 } 238 }
198 else 239 else
207 } 248 }
208 } 249 }
209 250
210 if (!file_content) 251 if (!file_content)
211 { 252 {
212 Seobeo_Web_Header_Generate(p_response_header, 253 Seobeo_Web_Header_Generate_KeepAlive(p_response_header,
213 HTTP_NOT_FOUND, 254 HTTP_NOT_FOUND,
214 "text/html", 0); 255 "text/html", 0, should_keep_alive);
215 Seobeo_Handle_Queue(p_cli_handle, 256 Seobeo_Handle_Queue(p_cli_handle,
216 (const uint8*)p_response_header, 257 (const uint8*)p_response_header,
217 (uint32)strlen(p_response_header)); 258 (uint32)strlen(p_response_header));
218 Seobeo_Handle_Flush(p_cli_handle); 259 Seobeo_Handle_Flush(p_cli_handle);
219 goto clean_up; 260 goto clean_up_arenas;
220 } 261 }
221 262
222 Seobeo_Log(SEOBEO_DEBUG, "Serving Static Files\n");
223 // Serve static file
224 const char *mime = "application/octet-stream"; 263 const char *mime = "application/octet-stream";
225 if (strstr(file_path, ".html")) mime = "text/html; charset=utf-8"; 264 if (strstr(file_path, ".html")) mime = "text/html; charset=utf-8";
226 else if (strstr(file_path, ".css")) mime = "text/css"; 265 else if (strstr(file_path, ".css")) mime = "text/css";
227 else if (strstr(file_path, ".js")) mime = "application/javascript"; 266 else if (strstr(file_path, ".js")) mime = "application/javascript";
228 else if (strstr(file_path, ".png")) mime = "image/png"; 267 else if (strstr(file_path, ".png")) mime = "image/png";
229 else if (strstr(file_path, ".jpg") || strstr(file_path, ".jpeg")) mime = "image/jpeg"; 268 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"; 269 else if (strstr(file_path, ".gif")) mime = "image/gif";
232 else if (strstr(file_path, ".svg")) mime = "image/svg+xml"; 270 else if (strstr(file_path, ".svg")) mime = "image/svg+xml";
233 else if (strstr(file_path, ".ico")) mime = "image/x-icon"; 271 else if (strstr(file_path, ".ico")) mime = "image/x-icon";
272 else if (strstr(file_path, ".webp")) mime = "image/webp";
234 else if (strstr(file_path, ".json")) mime = "application/json"; 273 else if (strstr(file_path, ".json")) mime = "application/json";
235 else if (strstr(file_path, ".wasm")) mime = "application/wasm"; 274 else if (strstr(file_path, ".wasm")) mime = "application/wasm";
275 else if (strstr(file_path, ".xml")) mime = "application/xml";
276 else if (strstr(file_path, ".pdf")) mime = "application/pdf";
277 else if (strstr(file_path, ".txt")) mime = "text/plain";
236 else if (strstr(file_path, ".mp4")) mime = "video/mp4"; 278 else if (strstr(file_path, ".mp4")) mime = "video/mp4";
237 else if (strstr(file_path, ".webm")) mime = "video/webm"; 279 else if (strstr(file_path, ".webm")) mime = "video/webm";
280 else if (strstr(file_path, ".mp3")) mime = "audio/mpeg";
281 else if (strstr(file_path, ".woff2")) mime = "font/woff2";
282 else if (strstr(file_path, ".woff")) mime = "font/woff";
283 else if (strstr(file_path, ".ttf")) mime = "font/ttf";
284 else if (strstr(file_path, ".webp")) mime = "image/webp";
238 else if (strstr(file_path, ".glb")) mime = "model/gltf-binary"; 285 else if (strstr(file_path, ".glb")) mime = "model/gltf-binary";
239 else if (strstr(file_path, ".gltf")) mime = "model/gltf+json"; 286 else if (strstr(file_path, ".gltf")) mime = "model/gltf+json";
240 287
241 Seobeo_Log(SEOBEO_DEBUG, "File path: %s\nBody Size: %zu\n", file_path, body_size); 288
242 289 Seobeo_Web_Header_Generate_KeepAlive(p_response_header,
243 Seobeo_Web_Header_Generate(p_response_header,
244 HTTP_OK, 290 HTTP_OK,
245 mime, 291 mime,
246 body_size); 292 body_size, should_keep_alive);
247 293
248 Seobeo_Handle_Queue(p_cli_handle, 294 Seobeo_Handle_Queue(p_cli_handle,
249 (const uint8*)p_response_header, 295 (const uint8*)p_response_header,
250 (uint32)strlen(p_response_header)); 296 (uint32)strlen(p_response_header));
251 Seobeo_Handle_Queue(p_cli_handle, 297 Seobeo_Handle_Queue(p_cli_handle,
252 (const uint8*)file_content, 298 (const uint8*)file_content,
253 (uint32)body_size); 299 (uint32)body_size);
254 Seobeo_Handle_Flush(p_cli_handle); 300 Seobeo_Handle_Flush(p_cli_handle);
255 Seobeo_Log(SEOBEO_DEBUG, "Request handled successfully\n");
256 } 301 }
257 else 302 else
258 { 303 {
259 Seobeo_Web_Header_Generate(p_response_header, 304 Seobeo_Web_Header_Generate_KeepAlive(p_response_header,
260 HTTP_FORBIDDEN, 305 HTTP_NOT_FOUND,
261 "text/plain", 0); 306 "text/plain", 0, should_keep_alive);
262 Seobeo_Handle_Queue(p_cli_handle, 307 Seobeo_Handle_Queue(p_cli_handle,
263 (const uint8*)p_response_header, 308 (const uint8*)p_response_header,
264 (uint32)strlen(p_response_header)); 309 (uint32)strlen(p_response_header));
265 Seobeo_Handle_Flush(p_cli_handle); 310 Seobeo_Handle_Flush(p_cli_handle);
266 } 311 }
267 goto clean_up; 312
268 313 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) 314 if (p_request_arena)
274 Dowa_Arena_Free(p_request_arena); 315 Dowa_Arena_Free(p_request_arena);
275 if (p_response_arena) 316 if (p_response_arena)
276 Dowa_Arena_Free(p_response_arena); 317 Dowa_Arena_Free(p_response_arena);
277 return; 318 return should_keep_alive;
278 } 319 }
279
280 320
281 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena) 321 int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena)
282 { 322 {
283 while (1) 323 while (1)
284 { 324 {
544 Seobeo_Stream_Handle_Server_Accept(p_server_handle); 584 Seobeo_Stream_Handle_Server_Accept(p_server_handle);
545 if (!p_cli_handle) continue; 585 if (!p_cli_handle) continue;
546 586
547 if (fork() == 0) 587 if (fork() == 0)
548 { 588 {
549 Seobeo_Web_HandleClientRequest(p_cli_handle, 589 Seobeo_Web_ClientHandle_Request(p_cli_handle, p_html_cache, FALSE);
550 p_html_cache);
551 _exit(0); 590 _exit(0);
552 } 591 }
553 } 592 }
554 } 593 }
555 594
673 void Seobeo_Router_Send_Response( 712 void Seobeo_Router_Send_Response(
674 Seobeo_Handle *p_handle, 713 Seobeo_Handle *p_handle,
675 Seobeo_Request_Entry *p_response_map, 714 Seobeo_Request_Entry *p_response_map,
676 Dowa_Arena *p_arena) 715 Dowa_Arena *p_arena)
677 { 716 {
717 Seobeo_Router_Send_Response_KeepAlive(p_handle, p_response_map, p_arena, FALSE);
718 }
719
720 void Seobeo_Router_Send_Response_KeepAlive(
721 Seobeo_Handle *p_handle,
722 Seobeo_Request_Entry *p_response_map,
723 Dowa_Arena *p_arena,
724 boolean keep_alive)
725 {
678 if (p_response_map == NULL) 726 if (p_response_map == NULL)
679 { 727 {
680 char *header = Dowa_Arena_Allocate(p_arena, 1024); 728 char *header = Dowa_Arena_Allocate(p_arena, 1024);
681 Seobeo_Web_Header_Generate(header, HTTP_INTERNAL_ERROR, "text/plain", 21); 729 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)); 730 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header));
683 Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); 731 Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21);
684 Seobeo_Handle_Flush(p_handle); 732 Seobeo_Handle_Flush(p_handle);
685 return; 733 return;
686 } 734 }
714 const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value; 762 const char *content_length_str = ((Seobeo_Request_Entry*)p_content_length_kv)->value;
715 body_length = atoi(content_length_str); 763 body_length = atoi(content_length_str);
716 } 764 }
717 765
718 char *header = Dowa_Arena_Allocate(p_arena, 4096); 766 char *header = Dowa_Arena_Allocate(p_arena, 4096);
719 Seobeo_Web_Header_Generate(header, status, content_type, body_length); 767 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++) 768 for (int i = 0; i < Dowa_Array_Length(p_response_map); i++)
721 { 769 {
722 if ( 770 if (
723 strstr(p_response_map[i].key, "status") || 771 strstr(p_response_map[i].key, "status") ||
724 strstr(p_response_map[i].key, "body") || 772 strstr(p_response_map[i].key, "body") ||
725 strstr(p_response_map[i].key, "content-type") || 773 strstr(p_response_map[i].key, "content-type") ||
726 strstr(p_response_map[i].key, "content-length") 774 strstr(p_response_map[i].key, "content-length")
727 ) 775 )
728 continue; 776 continue;
729 777
730 int32 current_header_len = strlen(header); 778 int32 current_header_len = strlen(header);
731 char *temp = malloc(sizeof(char) * 1024); 779 char *temp = malloc(sizeof(char) * 1024);
733 memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp)); 781 memcpy(&header[current_header_len - 2 /* \r\n */], temp, strlen(temp));
734 free(temp); 782 free(temp);
735 } 783 }
736 784
737 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); 785 Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header));
738 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length); 786 Seobeo_Handle_Queue(p_handle, (uint8_t*)body, body_length);
739 Seobeo_Handle_Flush(p_handle); 787 Seobeo_Handle_Flush(p_handle);
740 } 788 }
741 789
742 void Seobeo_Router_Destroy() 790 void Seobeo_Router_Destroy()
743 { 791 {
754 if (route->is_param) free(route->is_param); 802 if (route->is_param) free(route->is_param);
755 } 803 }
756 Dowa_Array_Free(g_routes); 804 Dowa_Array_Free(g_routes);
757 g_routes = NULL; 805 g_routes = NULL;
758 } 806 }
759
760 // Logging functions moved to s_logging.c
761