comparison seobeo/s_web.c @ 22:947b81010aba

[Dowa & Seobeo] Updated so that Dowa hashmaps can use arena and not be broken. Split up web so taht it can handle different paths. Also fixes issues with hash collisions which was pain in the ass.
author June Park <parkjune1995@gmail.com>
date Tue, 07 Oct 2025 07:11:02 -0700
parents 09def63429b9
children c0f6c8c7829f
comparison
equal deleted inserted replaced
21:09def63429b9 22:947b81010aba
60 { 60 {
61 Dowa_PHashEntry entry = NULL; 61 Dowa_PHashEntry entry = NULL;
62 Dowa_PHashMap p_current = p_html_cache; 62 Dowa_PHashMap p_current = p_html_cache;
63 char *slash; 63 char *slash;
64 64
65 Dowa_PArena p_response_arena = Dowa_Arena_Create(8192); 65 Dowa_PArena p_response_arena = Dowa_Arena_Create(1*1024*1024);
66 if (!p_response_arena) { perror("Dowa_Arena_Initialize"); goto clean_up; } 66 if (!p_response_arena) { perror("Dowa_Arena_Initialize"); goto clean_up; }
67 67
68 void *p_response_header = Dowa_Arena_Allocate(p_response_arena, (size_t)2048); 68 void *p_response_header = Dowa_Arena_Allocate(p_response_arena, (size_t)2048);
69 if (!p_response_header) { perror("Dowa_Arena_Allocate"); goto clean_up; } 69 if (!p_response_header) { perror("Dowa_Arena_Allocate"); goto clean_up; }
70 70
71 Dowa_PHashMap p_req_map = Dowa_HashMap_Create_With_Arena(32, p_response_arena); 71 // Parse request headers into hashmap
72 Dowa_PHashMap p_req_map = Dowa_HashMap_Create_With_Arena(100, p_response_arena);
72 if (Seobeo_Web_Header_Parse(p_cli_handle, p_req_map) != 0) 73 if (Seobeo_Web_Header_Parse(p_cli_handle, p_req_map) != 0)
73 { 74 {
74 // malformed request or closed — respond 400 75 Seobeo_Web_Header_Generate(p_response_header,
75 Seobeo_Web_Header_Generate(p_response_header, 76 HTTP_BAD_REQUEST,
76 HTTP_BAD_REQUEST, 77 "text/plain", 0);
77 "text/plain", 0); 78 Seobeo_Handle_Queue(p_cli_handle,
78 Seobeo_Handle_Queue(p_cli_handle, 79 (const uint8*)p_response_header,
79 (const uint8*)p_response_header, 80 (uint32)strlen(p_response_header));
80 (uint32)strlen(p_response_header));
81 Seobeo_Handle_Flush(p_cli_handle); 81 Seobeo_Handle_Flush(p_cli_handle);
82 goto clean_up; 82 goto clean_up;
83 } 83 }
84 84
85 // DEBUG 85 Dowa_HashMap_Print(p_req_map);
86 // Dowa_HashMap_Print(p_req_map); 86
87 87 // Extract method (GET, POST, etc.)
88 const char *path = (const char*)Dowa_HashMap_Get(p_req_map, "Path"); 88 const char *method = (const char*)Dowa_HashMap_Get(p_req_map, "HTTP_Method");
89 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)512); 89 printf("Method: %s Pointer %p\n\n", method, p_req_map);
90 90 if (!method)
91 if (!path || strcmp(path, "/") == 0) 91 {
92 { 92 printf("?? wtf\n\n");
93 strcpy(file_path, "index.html"); 93 Seobeo_Web_Header_Generate(p_response_header,
94 }else 94 HTTP_BAD_REQUEST,
95 { 95 "text/plain", 0);
96 size_t L = strlen(path); 96 Seobeo_Handle_Queue(p_cli_handle,
97 // strip leading '/' 97 (const uint8*)p_response_header,
98 if (path[0] == '/') 98 (uint32)strlen(p_response_header));
99 { 99 Seobeo_Handle_Flush(p_cli_handle);
100 if (strchr(path, '.') == NULL) 100 goto clean_up;
101 snprintf(file_path, 512, "%.*s/index.html", (int)(L-1), path+1); 101 }
102
103 // --- Separate GET map for caching or routing ---
104 Dowa_PHashMap p_get_map = Dowa_HashMap_Create(64);
105 if (!p_get_map)
106 {
107 perror("Dowa_HashMap_Create (p_get_map)");
108 goto clean_up;
109 }
110
111 // --- Handle different HTTP methods ---
112 if (strcmp(method, "GET") == 0)
113 {
114 const char *path = (const char*)Dowa_HashMap_Get(p_req_map, "Path");
115 char *file_path = Dowa_Arena_Allocate(p_response_arena, (size_t)512);
116
117 if (!path || strcmp(path, "/") == 0)
118 {
119 strcpy(file_path, "index.html");
120 }
121 else
122 {
123 size_t L = strlen(path);
124 if (path[0] == '/')
125 {
126 if (strchr(path, '.') == NULL)
127 snprintf(file_path, 512, "%.*s/index.html", (int)(L-1), path+1);
128 else
129 snprintf(file_path, 512, "%.*s", (int)(L-1), path+1);
130 }
102 else 131 else
103 snprintf(file_path, 512, "%.*s", (int)(L-1), path+1); 132 {
104 }else 133 strcpy(file_path, path);
105 { 134 }
106 // Probably never get here? 135 }
107 strcpy(file_path, path); 136
108 } 137 // Store path for GET handling map
109 } 138 Dowa_HashMap_Push_Value(p_get_map, "Path", file_path, strlen(file_path) + 1);
110 139
111 // DEBUG 140 // Walk through nested maps to find content
112 // printf("\n\nfile_path: %s\n", file_path); 141 while ((slash = strchr(file_path, '/')))
113 142 {
114 // Recursively go though the path until it gets to a file 143 *slash = '\0';
115 while ((slash = strchr(file_path, '/'))) 144 char *dir = file_path;
116 { 145 file_path = slash + 1;
117 *slash = '\0'; // e.g. file_path="foo", slash+1="index.html" 146
118 char *dir = file_path; // "foo" 147 printf("Directory: %s\n", dir);
119 file_path = slash + 1; // "index.html" 148
120 149 p_current = Dowa_HashMap_Get(p_current, dir);
121 printf("\n\nDirectory: %s\n\n", dir); 150 if (!p_current)
122 151 {
123 p_current = Dowa_HashMap_Get(p_current, dir); 152 fprintf(stderr, "No value in hashmap key: %s\n\n", dir);
124 if (!p_current) 153 Seobeo_Web_Header_Generate(p_response_header,
125 { 154 HTTP_NOT_FOUND,
126 fprintf(stderr, "No value in hashmap key: %s\n\n", dir); 155 "text/html", 0);
156 Seobeo_Handle_Queue(p_cli_handle,
157 (const uint8*)p_response_header,
158 (uint32)strlen(p_response_header));
159 Seobeo_Handle_Flush(p_cli_handle);
160 goto clean_up;
161 }
162 }
163
164 size_t pos = Dowa_HashMap_Get_Position(p_current, file_path);
165 entry = p_current->entries[pos];
166
167 if (!entry)
168 {
127 Seobeo_Web_Header_Generate(p_response_header, 169 Seobeo_Web_Header_Generate(p_response_header,
128 HTTP_NOT_FOUND, 170 HTTP_NOT_FOUND,
129 "text/html", 0); 171 "text/html", 0);
130 Seobeo_Handle_Queue(p_cli_handle, 172 Seobeo_Handle_Queue(p_cli_handle,
131 (const uint8*)p_response_header, 173 (const uint8*)p_response_header,
132 (uint32)strlen(p_response_header)); 174 (uint32)strlen(p_response_header));
133 Seobeo_Handle_Flush(p_cli_handle); 175 Seobeo_Handle_Flush(p_cli_handle);
134 goto clean_up; 176 goto clean_up;
135 } 177 }
136 } 178
137 179 const char *mime = "application/octet-stream";
138 size_t pos = Dowa_HashMap_Get_Position(p_current, file_path); 180 if (strstr(file_path, ".html")) mime = "text/html; charset=utf-8";
139 entry = p_current->entries[pos]; 181 else if (strstr(file_path, ".css")) mime = "text/css";
140 182 else if (strstr(file_path, ".js")) mime = "application/javascript";
141 // Missing so 404 183 else if (strstr(file_path, ".png")) mime = "image/png";
142 if (!entry) 184 else if (strstr(file_path, ".jpg") || strstr(file_path, ".jpeg")) mime = "image/jpeg";
143 { 185 else if (strstr(file_path, ".gif")) mime = "image/gif";
144 Seobeo_Web_Header_Generate(p_response_header, 186 else if (strstr(file_path, ".svg")) mime = "image/svg+xml";
145 HTTP_NOT_FOUND, 187 else if (strstr(file_path, ".ico")) mime = "image/x-icon";
146 "text/html", 0); 188 else if (strstr(file_path, ".json")) mime = "application/json";
147 Seobeo_Handle_Queue(p_cli_handle, 189
148 (const uint8*)p_response_header, 190 size_t body_size = entry->capacity;
149 (uint32)strlen(p_response_header)); 191 printf("key: %s\nBody Size: %zu\n", entry->key, body_size);
150 Seobeo_Handle_Flush(p_cli_handle); 192
151 goto clean_up; 193 Seobeo_Web_Header_Generate(p_response_header,
152 } 194 HTTP_OK,
153 195 mime,
154 196 body_size);
155 const char *mime = "application/octet-stream"; // Default binary 197
156 if (strstr(file_path, ".html")) mime = "text/html; charset=utf-8"; 198 Seobeo_Handle_Queue(p_cli_handle,
157 else if (strstr(file_path, ".css")) mime = "text/css"; 199 (const uint8*)p_response_header,
158 else if (strstr(file_path, ".js")) mime = "application/javascript"; 200 (uint32)strlen(p_response_header));
159 else if (strstr(file_path, ".png")) mime = "image/png"; 201 Seobeo_Handle_Queue(p_cli_handle,
160 else if (strstr(file_path, ".jpg") || strstr(file_path, ".jpeg")) mime = "image/jpeg"; 202 (const uint8*)entry->buffer,
161 else if (strstr(file_path, ".gif")) mime = "image/gif"; 203 (uint32)body_size);
162 else if (strstr(file_path, ".svg")) mime = "image/svg+xml"; 204 Seobeo_Handle_Flush(p_cli_handle);
163 else if (strstr(file_path, ".ico")) mime = "image/x-icon"; 205 }
164 else if (strstr(file_path, ".json")) mime = "application/json"; 206 else if (strcmp(method, "POST") == 0)
165 207 {
166 size_t body_size = entry->capacity; 208 // --- TODO: Add POST logic here ---
167 printf("key: %s\n\n", entry->key); 209 Seobeo_Web_Header_Generate(p_response_header,
168 printf("Body Size: %zu\n\n", body_size); 210 HTTP_NOT_FOUND,
169 Seobeo_Web_Header_Generate(p_response_header, 211 "text/plain", 0);
170 HTTP_OK, 212 Seobeo_Handle_Queue(p_cli_handle,
171 mime, 213 (const uint8*)p_response_header,
172 body_size); 214 (uint32)strlen(p_response_header));
173 215 Seobeo_Handle_Flush(p_cli_handle);
174 printf("response header: %s, data: %d\n\n", p_response_header, (uint32)strlen(p_response_header)); 216 }
175 Seobeo_Handle_Queue(p_cli_handle, 217 else if (strcmp(method, "PUT") == 0)
176 (const uint8*)p_response_header, 218 {
177 (uint32)strlen(p_response_header)); 219 // --- TODO: Add PUT logic here ---
178 220 Seobeo_Web_Header_Generate(p_response_header,
179 printf("response body data: %d\n\n", (uint32)body_size); 221 HTTP_NOT_FOUND,
180 Seobeo_Handle_Queue(p_cli_handle, 222 "text/plain", 0);
181 (const uint8*)entry->buffer, 223 Seobeo_Handle_Queue(p_cli_handle,
182 (uint32)body_size); 224 (const uint8*)p_response_header,
183 Seobeo_Handle_Flush(p_cli_handle); 225 (uint32)strlen(p_response_header));
226 Seobeo_Handle_Flush(p_cli_handle);
227 }
228 else if (strcmp(method, "DELETE") == 0)
229 {
230 // --- TODO: Add DELETE logic here ---
231 Seobeo_Web_Header_Generate(p_response_header,
232 HTTP_NOT_FOUND,
233 "text/plain", 0);
234 Seobeo_Handle_Queue(p_cli_handle,
235 (const uint8*)p_response_header,
236 (uint32)strlen(p_response_header));
237 Seobeo_Handle_Flush(p_cli_handle);
238 }
239 else
240 {
241 // Unknown or unsupported method
242 Seobeo_Web_Header_Generate(p_response_header,
243 HTTP_FORBIDDEN,
244 "text/plain", 0);
245 Seobeo_Handle_Queue(p_cli_handle,
246 (const uint8*)p_response_header,
247 (uint32)strlen(p_response_header));
248 Seobeo_Handle_Flush(p_cli_handle);
249 }
184 250
185 clean_up: 251 clean_up:
186 if (p_cli_handle) 252 if (p_cli_handle)
187 Seobeo_Handle_Destroy(p_cli_handle); 253 Seobeo_Handle_Destroy(p_cli_handle);
188 if (p_response_arena) 254 if (p_response_arena)
189 Dowa_Arena_Destroy(p_response_arena); 255 Dowa_Arena_Destroy(p_response_arena);
190 } 256 }
191 257
258
192 int Seobeo_Web_Header_Parse(Seobeo_PHandle p_handle, Dowa_PHashMap map) 259 int Seobeo_Web_Header_Parse(Seobeo_PHandle p_handle, Dowa_PHashMap map)
193 { 260 {
194 // 1) Fill read_buffer until we see "\r\n\r\n" 261 // 1) Fill read_buffer until we see "\r\n\r\n"
195 while (1) 262 while (1)
196 { 263 {
209 // 2) Parse request‐line "METHOD SP PATH SP VERSION CRLF" 276 // 2) Parse request‐line "METHOD SP PATH SP VERSION CRLF"
210 char *buf = (char*)p_handle->read_buffer; 277 char *buf = (char*)p_handle->read_buffer;
211 char *hdr_end = strstr(buf, "\r\n\r\n"); 278 char *hdr_end = strstr(buf, "\r\n\r\n");
212 size_t hdr_len = hdr_end - buf + 4; 279 size_t hdr_len = hdr_end - buf + 4;
213 280
281 // This seems kinda bad ?
214 char method[16], path[256], version[16]; 282 char method[16], path[256], version[16];
215 if (sscanf(buf, "%15s %255s %15s", method, path, version) != 3) 283 if (sscanf(buf, "%15s %255s %15s", method, path, version) != 3)
216 { 284 {
217 return -1; 285 return -1;
218 } 286 }
219 287
220 Dowa_HashMap_Push_Value_With_Type(map, "Method", method, strlen(method) + 1, DOWA_HASH_MAP_TYPE_STRING); 288 Dowa_HashMap_Push_Value_With_Type(map, "HTTP_Method", method, strlen(method) + 1, DOWA_HASH_MAP_TYPE_STRING);
221 Dowa_HashMap_Push_Value_With_Type(map, "Path", path, strlen(path) + 1, DOWA_HASH_MAP_TYPE_STRING); 289 printf("Method: %s Pointer %p\n\n", Dowa_HashMap_Get(map, "HTTP_Method"), map);
222 Dowa_HashMap_Push_Value_With_Type(map, "Version", version, strlen(version) + 1, DOWA_HASH_MAP_TYPE_STRING); 290 Dowa_HashMap_Push_Value_With_Type(map, "Version", version, strlen(version) + 1, DOWA_HASH_MAP_TYPE_STRING);
291
292 // 1) Separate raw path and query string
293 char *raw_path = path;
294 char *query_start = strchr(raw_path, '?');
295 char *query_str = NULL;
296
297 if (query_start)
298 {
299 *query_start = '\0'; // now raw_path ends before '?'
300 query_str = query_start + 1;
301 }
302
303 // push only the clean path
304 Dowa_HashMap_Push_Value_With_Type(
305 map,
306 "Path",
307 raw_path,
308 strlen(raw_path) + 1,
309 DOWA_HASH_MAP_TYPE_STRING);
310
311 // 2) If there *is* a query, tokenize into a sub-map
312 if (query_str && *query_str)
313 {
314 // create nested map for GET params
315 Dowa_PHashMap p_query_map = Dowa_HashMap_Create_With_Arena(100, map->p_arena);
316
317 char *cur = query_str;
318 while (cur && *cur)
319 {
320 // find the next '&'
321 char *next_amp = strchr(cur, '&');
322 // if none, treat end-of-string as the boundary
323 char *pair_end = next_amp ? next_amp : cur + strlen(cur);
324
325 // find '=' in [cur, pair_end)
326 char *eq = memchr(cur, '=', pair_end - cur);
327 if (eq) {
328 size_t key_len = eq - cur;
329 size_t val_len = pair_end - (eq + 1);
330
331 // extract key
332 char key_buf[key_len + 1];
333 memcpy(key_buf, cur, key_len);
334 key_buf[key_len] = '\0';
335
336 // extract value
337 char val_buf[val_len + 1];
338 memcpy(val_buf, eq + 1, val_len);
339 val_buf[val_len] = '\0';
340
341 printf("key: '%s', value: '%s'\n", key_buf, val_buf);
342 // push into map with strlen(val_buf)+1 to include '\0'
343 Dowa_HashMap_Push_Value_With_Type(
344 p_query_map,
345 key_buf,
346 val_buf,
347 (uint32_t)(val_len + 1),
348 DOWA_HASH_MAP_TYPE_STRING);
349 }
350
351 // advance past '&' if present, else end loop
352 cur = next_amp ? next_amp + 1 : NULL;
353 }
354 if (
355 Dowa_HashMap_Push_Value_With_Type_NoCopy(
356 map,
357 "QueryParams",
358 p_query_map,
359 sizeof(p_query_map),
360 DOWA_HASH_MAP_TYPE_HASHMAP) == -1
361 )
362 {
363 printf("Something went wrong...\n\n");
364 }
365 }
366
367 // int qp = Dowa_HashMap_Get_Position(map, "QueryParams");
368 // Dowa_PHashEntry p_qp_entry = map->entries[qp];
369 // printf("query param key: %s\n", p_qp_entry->key);
370 // printf("query param value: %s\n",(char *)Dowa_HashMap_Get(p_qp_entry->buffer, "hello"));
223 371
224 // 3) Parse each header line until the blank line 372 // 3) Parse each header line until the blank line
225 char *line = buf + strlen(method) + 1 + strlen(path) + 1 + strlen(version) + 2; 373 char *line = buf + strlen(method) + 1 + strlen(path) + 1 + strlen(version) + 2;
226 while (line < hdr_end) 374 while (line < hdr_end)
227 { 375 {
228 char *next = strstr(line, "\r\n"); 376 char *next = strstr(line, "\r\n");
229 if (!next) break; 377 if (!next) break;
230 378
231 // split at colon 379 // split at colon
232 char *colon = memchr(line, ':', next - line); 380 char *colon = memchr(line, ':', next - line);
233 if (colon) { 381 if (colon)
382 {
234 size_t key_len = colon - line; 383 size_t key_len = colon - line;
235 size_t value_len = next - colon - 1; 384 size_t value_len = next - colon - 1;
236 385
237 char *val_start = colon + 1; 386 char *val_start = colon + 1;
238 if (*val_start == ' ') 387 if (*val_start == ' ')
247 396
248 char *val = malloc(value_len + 1); 397 char *val = malloc(value_len + 1);
249 memcpy(val, val_start, value_len); 398 memcpy(val, val_start, value_len);
250 val[value_len] = '\0'; 399 val[value_len] = '\0';
251 400
252 Dowa_HashMap_Push_Value(map, key, val, value_len + 1); 401 Dowa_HashMap_Push_Value_With_Type(map, key, val, value_len + 1, DOWA_HASH_MAP_TYPE_STRING);
402
403 printf("Capacity: %d, Length: %d ", (int)map->p_arena->capacity, (int)map->p_arena->offset);
404 printf("value_len: %d, key: %s value %s position: %d\n\n", (int)value_len + 1, key, val, Dowa_HashMap_Get_Position(map, key));
405 printf("Method: %s Position: %d Pointer %p\n\n", Dowa_HashMap_Get(map, "HTTP_Method"), Dowa_HashMap_Get_Position(map, "HTTP_Method"), map);
253 406
254 free(key); 407 free(key);
255 free(val); 408 free(val);
256 } 409 }
257 410
412 Seobeo_Handle_Destroy(h); 565 Seobeo_Handle_Destroy(h);
413 return -1; 566 return -1;
414 } 567 }
415 } 568 }
416 569
417 printf("%s", p_request_body); 570 // Debug
571 printf("Request body %s", p_request_body);
418 Dowa_Arena_Destroy(p_request_arena); 572 Dowa_Arena_Destroy(p_request_arena);
419 Seobeo_Handle_Destroy(h); 573 Seobeo_Handle_Destroy(h);
420 return 0; 574 return 0;
421 } 575 }
422 576