MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 1110 lines 33 kB view raw
1#include <stdlib.h> 2#include <string.h> 3#include <ctype.h> 4#include <stdio.h> 5 6#include "ant.h" 7#include "errors.h" 8#include "runtime.h" 9#include "internal.h" 10#include "descriptors.h" 11#include "silver/engine.h" 12 13#include "modules/headers.h" 14#include "modules/symbol.h" 15 16typedef struct hdr_entry { 17 char *name; 18 char *value; 19 struct hdr_entry *next; 20} hdr_entry_t; 21 22typedef struct { 23 hdr_entry_t *head; 24 hdr_entry_t **tail; 25 size_t count; 26} hdr_list_t; 27 28typedef struct { 29 char *name; 30 char *value; 31} sorted_pair_t; 32 33typedef struct { 34 hdr_list_t *list; 35 size_t index; 36 int kind; 37} hdr_iter_t; 38 39enum { 40 ITER_ENTRIES = 0, 41 ITER_KEYS = 1, 42 ITER_VALUES = 2 43}; 44 45ant_value_t g_headers_proto = 0; 46ant_value_t g_headers_iter_proto = 0; 47 48static hdr_list_t *list_new(void) { 49 hdr_list_t *l = ant_calloc(sizeof(hdr_list_t)); 50 if (!l) return NULL; 51 l->head = NULL; 52 l->tail = &l->head; 53 return l; 54} 55 56static void list_free(hdr_list_t *l) { 57 if (!l) return; 58 for (hdr_entry_t *e = l->head; e; ) { 59 hdr_entry_t *n = e->next; 60 free(e->name); free(e->value); free(e); 61 e = n; 62 } 63 free(l); 64} 65 66static hdr_list_t *get_list(ant_value_t obj) { 67 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 68 if (vtype(slot) != T_NUM) return NULL; 69 return (hdr_list_t *)(uintptr_t)(size_t)js_getnum(slot); 70} 71 72static headers_guard_t get_guard(ant_value_t obj) { 73 ant_value_t slot = js_get_slot(obj, SLOT_HEADERS_GUARD); 74 if (vtype(slot) != T_NUM) return HEADERS_GUARD_NONE; 75 return (headers_guard_t)(int)js_getnum(slot); 76} 77 78bool headers_is_headers(ant_value_t obj) { 79 return js_check_brand(obj, BRAND_HEADERS); 80} 81 82static bool is_token_char(unsigned char c) { 83 if (c > 127) return false; 84 static const char ok[] = 85 "!#$%&'*+-.^_`|~" 86 "0123456789" 87 "abcdefghijklmnopqrstuvwxyz" 88 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 89 return strchr(ok, (char)c) != NULL; 90} 91 92static bool is_valid_name(const char *s) { 93 if (!s || !*s) return false; 94 for (const unsigned char *p = (const unsigned char *)s; *p; p++) 95 if (!is_token_char(*p)) return false; 96 return true; 97} 98 99static bool is_valid_value(const char *s) { 100 if (!s) return false; 101 for (const unsigned char *p = (const unsigned char *)s; *p; p++) { 102 unsigned char c = *p; 103 if (c == 0 || c == '\r' || c == '\n' || c > 127) return false; 104 } 105 return true; 106} 107 108static char *normalize_value(const char *s) { 109 if (!s) return strdup(""); 110 while (*s == ' ' || *s == '\t') s++; 111 112 size_t len = strlen(s); 113 while (len > 0 && (s[len - 1] == ' ' || s[len - 1] == '\t')) len--; 114 115 char *out = malloc(len + 1); 116 if (!out) return NULL; 117 118 memcpy(out, s, len); 119 out[len] = '\0'; 120 121 return out; 122} 123 124static char *lowercase_dup(const char *s) { 125 if (!s) return strdup(""); 126 size_t len = strlen(s); 127 char *out = malloc(len + 1); 128 if (!out) return NULL; 129 for (size_t i = 0; i <= len; i++) 130 out[i] = (char)tolower((unsigned char)s[i]); 131 return out; 132} 133 134typedef struct { 135 const char *name; 136 bool prefix; 137} header_rule_t; 138 139static const header_rule_t k_forbidden_request_headers[] = { 140 { "accept-charset", false }, 141 { "accept-encoding", false }, 142 { "access-control-request-headers", false }, 143 { "access-control-request-method", false }, 144 { "connection", false }, 145 { "content-length", false }, 146 { "cookie", false }, 147 { "cookie2", false }, 148 { "date", false }, 149 { "dnt", false }, 150 { "expect", false }, 151 { "host", false }, 152 { "keep-alive", false }, 153 { "origin", false }, 154 { "referer", false }, 155 { "set-cookie", false }, 156 { "te", false }, 157 { "trailer", false }, 158 { "transfer-encoding", false }, 159 { "upgrade", false }, 160 { "via", false }, 161 { "proxy-", true }, 162 { "sec-", true }, 163}; 164 165static const header_rule_t k_forbidden_response_headers[] = { 166 { "set-cookie", false }, 167 { "set-cookie2", false }, 168}; 169 170static const char *k_cors_safelisted_content_types[] = { 171 "application/x-www-form-urlencoded", 172 "multipart/form-data", 173 "text/plain", 174}; 175 176static const char *k_no_cors_safelisted_names[] = { 177 "accept", 178 "accept-language", 179 "content-language", 180}; 181 182static bool matches_rule(const char *name, const header_rule_t *rules, size_t count) { 183 for (size_t i = 0; i < count; i++) { 184 size_t len = strlen(rules[i].name); 185 if (rules[i].prefix) { if (strncmp(name, rules[i].name, len) == 0) return true; } 186 else if (strcmp(name, rules[i].name) == 0) return true; 187 } 188 return false; 189} 190 191static bool matches_string(const char *value, const char *const *list, size_t count) { 192 for (size_t i = 0; i < count; i++) { 193 if (strcmp(value, list[i]) == 0) return true; 194 } 195 return false; 196} 197 198static bool is_forbidden_request_header_name(const char *lower_name) { 199 return matches_rule(lower_name, k_forbidden_request_headers, 200 sizeof(k_forbidden_request_headers) / sizeof(k_forbidden_request_headers[0])); 201} 202 203static bool is_forbidden_response_header_name(const char *lower_name) { 204 return matches_rule(lower_name, k_forbidden_response_headers, 205 sizeof(k_forbidden_response_headers) / sizeof(k_forbidden_response_headers[0])); 206} 207 208static bool is_cors_safelisted_content_type_value(const char *value) { 209 char *lower = lowercase_dup(value ? value : ""); 210 if (!lower) return false; 211 char *semi = strchr(lower, ';'); 212 213 if (!semi) { 214 bool ok = matches_string( 215 lower, 216 k_cors_safelisted_content_types, 217 sizeof(k_cors_safelisted_content_types) / sizeof(k_cors_safelisted_content_types[0]) 218 ); 219 free(lower); 220 return ok; 221 } 222 223 *semi++ = '\0'; 224 while (*semi == ' ' || *semi == '\t') semi++; 225 bool essence_ok = matches_string( 226 lower, 227 k_cors_safelisted_content_types, 228 sizeof(k_cors_safelisted_content_types) / sizeof(k_cors_safelisted_content_types[0]) 229 ); 230 231 bool param_ok = strcmp(semi, "charset=utf-8") == 0; 232 free(lower); 233 234 return essence_ok && param_ok; 235} 236 237static bool is_no_cors_safelisted_name_value(const char *lower_name, const char *value) { 238 if ( 239 matches_string( 240 lower_name, k_no_cors_safelisted_names, 241 sizeof(k_no_cors_safelisted_names) / sizeof(k_no_cors_safelisted_names[0])) 242 ) return true; 243 244 if (strcmp(lower_name, "content-type") == 0) 245 return value && value[0] && is_cors_safelisted_content_type_value(value); 246 247 return false; 248} 249 250static bool header_allowed_for_guard(const char *lower_name, const char *value, headers_guard_t guard) { 251 if (guard == HEADERS_GUARD_NONE) return true; 252 if (guard == HEADERS_GUARD_IMMUTABLE) return true; 253 if (is_forbidden_request_header_name(lower_name)) return false; 254 if (guard == HEADERS_GUARD_RESPONSE) return !is_forbidden_response_header_name(lower_name); 255 if (guard == HEADERS_GUARD_REQUEST_NO_CORS) return is_no_cors_safelisted_name_value(lower_name, value); 256 return true; 257} 258 259static ant_value_t headers_guard_error(ant_t *js, headers_guard_t guard) { 260 if (guard != HEADERS_GUARD_IMMUTABLE) return js_mkundef(); 261 return js_mkerr_typed(js, JS_ERR_TYPE, "Headers are immutable"); 262} 263 264static void list_apply_guard(hdr_list_t *l, headers_guard_t guard) { 265 if (!l || guard == HEADERS_GUARD_NONE || guard == HEADERS_GUARD_IMMUTABLE) return; 266 267 hdr_entry_t **pp = &l->head; 268 l->tail = &l->head; 269 270 while (*pp) { 271 hdr_entry_t *cur = *pp; 272 if (!header_allowed_for_guard(cur->name, cur->value, guard)) { 273 *pp = cur->next; 274 free(cur->name); 275 free(cur->value); 276 free(cur); 277 l->count--; 278 continue; 279 } 280 281 l->tail = &cur->next; 282 pp = &cur->next; 283 } 284} 285 286static void list_append_raw(hdr_list_t *l, const char *lower_name, const char *value) { 287 hdr_entry_t *e = ant_calloc(sizeof(hdr_entry_t)); 288 if (!e) return; 289 e->name = strdup(lower_name); 290 e->value = strdup(value); 291 *l->tail = e; 292 l->tail = &e->next; 293 l->count++; 294} 295 296static void list_delete_name(hdr_list_t *l, const char *lower_name) { 297 hdr_entry_t **pp = &l->head; 298 l->tail = &l->head; 299 while (*pp) { 300 if (strcmp((*pp)->name, lower_name) == 0) { 301 hdr_entry_t *dead = *pp; 302 *pp = dead->next; 303 free(dead->name); free(dead->value); free(dead); 304 l->count--; 305 } else { 306 l->tail = &(*pp)->next; 307 pp = &(*pp)->next; 308 }} 309} 310 311static int cmp_pairs(const void *a, const void *b) { 312 return strcmp(((const sorted_pair_t *)a)->name, ((const sorted_pair_t *)b)->name); 313} 314 315static sorted_pair_t *build_sorted_view(hdr_list_t *l, size_t *out) { 316 *out = 0; 317 if (!l || l->count == 0) return NULL; 318 319 sorted_pair_t *raw = malloc(l->count * sizeof(sorted_pair_t)); 320 if (!raw) return NULL; 321 322 size_t n = 0; 323 for (hdr_entry_t *e = l->head; e; e = e->next) { 324 raw[n].name = e->name; 325 raw[n].value = e->value; 326 n++; 327 } 328 329 qsort(raw, n, sizeof(sorted_pair_t), cmp_pairs); 330 sorted_pair_t *res = malloc(n * sizeof(sorted_pair_t)); 331 if (!res) { free(raw); return NULL; } 332 333 size_t ri = 0; 334 for (size_t i = 0; i < n; ) { 335 if (strcmp(raw[i].name, "set-cookie") == 0) { 336 res[ri].name = strdup(raw[i].name); 337 res[ri].value = strdup(raw[i].value); 338 ri++; i++; 339 } else { 340 size_t j = i + 1; 341 size_t total = strlen(raw[i].value); 342 while (j < n && strcmp(raw[j].name, raw[i].name) == 0) { 343 total += 2 + strlen(raw[j].value); 344 j++; 345 } 346 char *combined = malloc(total + 1); 347 if (!combined) combined = strdup(""); 348 size_t pos = 0; 349 for (size_t k = i; k < j; k++) { 350 if (k > i) { combined[pos++] = ','; combined[pos++] = ' '; } 351 size_t vl = strlen(raw[k].value); 352 memcpy(combined + pos, raw[k].value, vl); 353 pos += vl; 354 } 355 combined[pos] = '\0'; 356 res[ri].name = strdup(raw[i].name); 357 res[ri].value = combined; 358 ri++; i = j; 359 }} 360 361 free(raw); 362 *out = ri; 363 364 return res; 365} 366 367static void free_sorted_view(sorted_pair_t *v, size_t n) { 368 if (!v) return; 369 for (size_t i = 0; i < n; i++) { free(v[i].name); free(v[i].value); } 370 free(v); 371} 372 373static ant_value_t headers_append_name_value(ant_t *js, hdr_list_t *l, const char *name, const char *value) { 374 if (!is_valid_name(name)) 375 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name: %s", name ? name : ""); 376 377 char *norm = normalize_value(value); 378 if (!norm) return js_mkerr(js, "out of memory"); 379 if (!is_valid_value(norm)) { 380 free(norm); 381 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); 382 } 383 384 char *lower = lowercase_dup(name); 385 if (!lower) { free(norm); return js_mkerr(js, "out of memory"); } 386 387 list_append_raw(l, lower, norm); 388 free(lower); free(norm); 389 return js_mkundef(); 390} 391 392static ant_value_t headers_append_pair(ant_t *js, hdr_list_t *l, ant_value_t name_v, ant_value_t value_v) { 393 const char *name = NULL; 394 const char *value = NULL; 395 396 if (vtype(name_v) != T_STR) { 397 name_v = js_tostring_val(js, name_v); 398 if (is_err(name_v)) return name_v; 399 } 400 401 if (vtype(value_v) != T_STR) { 402 value_v = js_tostring_val(js, value_v); 403 if (is_err(value_v)) return value_v; 404 } 405 406 name = js_getstr(js, name_v, NULL); 407 value = js_getstr(js, value_v, NULL); 408 return headers_append_name_value(js, l, name, value); 409} 410 411ant_value_t headers_append_value(ant_t *js, ant_value_t hdrs, ant_value_t name_v, ant_value_t value_v) { 412 hdr_list_t *l = get_list(hdrs); 413 ant_value_t r = 0; 414 415 if (!l) return js_mkerr(js, "Invalid Headers object"); 416 r = headers_append_pair(js, l, name_v, value_v); 417 418 if (is_err(r)) return r; 419 list_apply_guard(l, get_guard(hdrs)); 420 421 return js_mkundef(); 422} 423 424ant_value_t headers_append_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value) { 425 hdr_list_t *l = get_list(hdrs); 426 ant_value_t r = 0; 427 428 if (!l) return js_mkerr(js, "Invalid Headers object"); 429 r = headers_append_name_value(js, l, name, value); 430 431 if (is_err(r)) return r; 432 list_apply_guard(l, get_guard(hdrs)); 433 434 return js_mkundef(); 435} 436 437static ant_value_t init_from_sequence(ant_t *js, hdr_list_t *l, ant_value_t seq) { 438 js_iter_t it; 439 if (!js_iter_open(js, seq, &it)) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers init is not iterable"); 440 441 ant_value_t pair; 442 while (js_iter_next(js, &it, &pair)) { 443 uint8_t pt = vtype(pair); 444 if (pt != T_ARR && pt != T_OBJ) { 445 js_iter_close(js, &it); 446 return js_mkerr_typed(js, JS_ERR_TYPE, "Each header init pair must be a sequence"); 447 } 448 449 if (js_arr_len(js, pair) != 2) { 450 js_iter_close(js, &it); 451 return js_mkerr_typed(js, JS_ERR_TYPE, "Each header init pair must have exactly 2 elements"); 452 } 453 454 ant_value_t r = headers_append_pair(js, l, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); 455 if (is_err(r)) { js_iter_close(js, &it); return r; } 456 } 457 458 return js_mkundef(); 459} 460 461static ant_value_t init_from_record(ant_t *js, hdr_list_t *l, ant_value_t obj) { 462 ant_iter_t it = js_prop_iter_begin(js, obj); 463 const char *key; 464 size_t key_len; 465 ant_value_t val; 466 467 while (js_prop_iter_next(&it, &key, &key_len, &val)) { 468 ant_value_t r = headers_append_pair(js, l, js_mkstr(js, key, key_len), val); 469 if (is_err(r)) { js_prop_iter_end(&it); return r; } 470 } 471 472 js_prop_iter_end(&it); 473 return js_mkundef(); 474} 475 476bool advance_headers(ant_t *js, js_iter_t *it, ant_value_t *out) { 477 ant_value_t state_val = js_get_slot(it->iterator, SLOT_ITER_STATE); 478 if (vtype(state_val) == T_UNDEF) return false; 479 hdr_iter_t *st = (hdr_iter_t *)(uintptr_t)(size_t)js_getnum(state_val); 480 481 size_t count = 0; 482 sorted_pair_t *view = build_sorted_view(st->list, &count); 483 484 if (st->index >= count) { 485 free_sorted_view(view, count); 486 return false; 487 } 488 489 sorted_pair_t *e = &view[st->index]; 490 switch (st->kind) { 491 case ITER_KEYS: 492 *out = js_mkstr(js, e->name, strlen(e->name)); 493 break; 494 case ITER_VALUES: 495 *out = js_mkstr(js, e->value, strlen(e->value)); 496 break; 497 default: { 498 *out = js_mkarr(js); 499 js_arr_push(js, *out, js_mkstr(js, e->name, strlen(e->name))); 500 js_arr_push(js, *out, js_mkstr(js, e->value, strlen(e->value))); 501 break; 502 }} 503 504 free_sorted_view(view, count); 505 st->index++; 506 return true; 507} 508 509static ant_value_t headers_iter_next(ant_t *js, ant_value_t *args, int nargs) { 510 js_iter_t it = { .iterator = js->this_val }; 511 ant_value_t value; 512 return js_iter_result(js, advance_headers(js, &it, &value), value); 513} 514 515static ant_value_t make_headers_iter(ant_t *js, ant_value_t headers_obj, int kind) { 516 hdr_list_t *l = get_list(headers_obj); 517 518 hdr_iter_t *st = ant_calloc(sizeof(hdr_iter_t)); 519 if (!st) return js_mkerr(js, "out of memory"); 520 st->list = l ? l : list_new(); 521 st->kind = kind; 522 523 ant_value_t iter = js_mkobj(js); 524 js_set_proto_init(iter, g_headers_iter_proto); 525 js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(st)); 526 return iter; 527} 528 529static ant_value_t js_headers_append(ant_t *js, ant_value_t *args, int nargs) { 530 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.append requires 2 arguments"); 531 hdr_list_t *l = get_list(js->this_val); 532 if (!l) return js_mkerr(js, "Invalid Headers object"); 533 ant_value_t guard_err = headers_guard_error(js, get_guard(js->this_val)); 534 if (is_err(guard_err)) return guard_err; 535 ant_value_t r = headers_append_pair(js, l, args[0], args[1]); 536 if (is_err(r)) return r; 537 list_apply_guard(l, get_guard(js->this_val)); 538 return js_mkundef(); 539} 540 541static ant_value_t js_headers_set(ant_t *js, ant_value_t *args, int nargs) { 542 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.set requires 2 arguments"); 543 hdr_list_t *l = get_list(js->this_val); 544 if (!l) return js_mkerr(js, "Invalid Headers object"); 545 ant_value_t guard_err = headers_guard_error(js, get_guard(js->this_val)); 546 if (is_err(guard_err)) return guard_err; 547 548 ant_value_t name_v = args[0]; 549 ant_value_t value_v = args[1]; 550 if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 551 if (vtype(value_v) != T_STR) { value_v = js_tostring_val(js, value_v); if (is_err(value_v)) return value_v; } 552 553 const char *name = js_getstr(js, name_v, NULL); 554 const char *value = js_getstr(js, value_v, NULL); 555 556 if (!is_valid_name(name)) 557 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name: %s", name ? name : ""); 558 559 char *norm = normalize_value(value); 560 if (!norm) return js_mkerr(js, "out of memory"); 561 if (!is_valid_value(norm)) { free(norm); return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); } 562 563 char *lower = lowercase_dup(name); 564 if (!lower) { free(norm); return js_mkerr(js, "out of memory"); } 565 566 list_delete_name(l, lower); 567 if (header_allowed_for_guard(lower, norm, get_guard(js->this_val))) 568 list_append_raw(l, lower, norm); 569 free(lower); free(norm); 570 return js_mkundef(); 571} 572 573static ant_value_t js_headers_get(ant_t *js, ant_value_t *args, int nargs) { 574 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.get requires 1 argument"); 575 hdr_list_t *l = get_list(js->this_val); 576 if (!l) return js_mknull(); 577 578 ant_value_t name_v = args[0]; 579 if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 580 const char *name = js_getstr(js, name_v, NULL); 581 if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 582 583 char *lower = lowercase_dup(name); 584 if (!lower) return js_mkerr(js, "out of memory"); 585 586 // set-cookie is never combined per Fetch spec 587 if (strcmp(lower, "set-cookie") == 0) { 588 for (hdr_entry_t *e = l->head; e; e = e->next) { 589 if (strcmp(e->name, lower) == 0) { 590 ant_value_t ret = js_mkstr(js, e->value, strlen(e->value)); 591 free(lower); 592 return ret; 593 } 594 } 595 free(lower); 596 return js_mknull(); 597 } 598 599 size_t total = 0; 600 int count = 0; 601 for (hdr_entry_t *e = l->head; e; e = e->next) { 602 if (strcmp(e->name, lower) == 0) { 603 if (count > 0) total += 2; 604 total += strlen(e->value); 605 count++; 606 } 607 } 608 609 if (count == 0) { free(lower); return js_mknull(); } 610 611 char *combined = malloc(total + 1); 612 if (!combined) { free(lower); return js_mkerr(js, "out of memory"); } 613 614 size_t pos = 0; 615 int seen = 0; 616 for (hdr_entry_t *e = l->head; e; e = e->next) { 617 if (strcmp(e->name, lower) == 0) { 618 if (seen > 0) { combined[pos++] = ','; combined[pos++] = ' '; } 619 size_t vl = strlen(e->value); 620 memcpy(combined + pos, e->value, vl); 621 pos += vl; 622 seen++; 623 } 624 } 625 combined[pos] = '\0'; 626 free(lower); 627 628 ant_value_t ret = js_mkstr(js, combined, pos); 629 free(combined); 630 return ret; 631} 632 633static ant_value_t js_headers_has(ant_t *js, ant_value_t *args, int nargs) { 634 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.has requires 1 argument"); 635 hdr_list_t *l = get_list(js->this_val); 636 if (!l) return js_false; 637 638 ant_value_t name_v = args[0]; 639 if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 640 const char *name = js_getstr(js, name_v, NULL); 641 if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 642 643 char *lower = lowercase_dup(name); 644 if (!lower) return js_mkerr(js, "out of memory"); 645 646 bool found = false; 647 for (hdr_entry_t *e = l->head; e; e = e->next) { 648 if (strcmp(e->name, lower) == 0) { found = true; break; } 649 } 650 free(lower); 651 return js_bool(found); 652} 653 654static ant_value_t js_headers_delete(ant_t *js, ant_value_t *args, int nargs) { 655 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.delete requires 1 argument"); 656 hdr_list_t *l = get_list(js->this_val); 657 if (!l) return js_mkundef(); 658 ant_value_t guard_err = headers_guard_error(js, get_guard(js->this_val)); 659 if (is_err(guard_err)) return guard_err; 660 661 ant_value_t name_v = args[0]; 662 if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } 663 const char *name = js_getstr(js, name_v, NULL); 664 if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 665 666 char *lower = lowercase_dup(name); 667 if (!lower) return js_mkerr(js, "out of memory"); 668 list_delete_name(l, lower); 669 free(lower); 670 return js_mkundef(); 671} 672 673static ant_value_t js_headers_get_set_cookie(ant_t *js, ant_value_t *args, int nargs) { 674 (void)args; (void)nargs; 675 hdr_list_t *l = get_list(js->this_val); 676 ant_value_t arr = js_mkarr(js); 677 if (!l) return arr; 678 for (hdr_entry_t *e = l->head; e; e = e->next) { 679 if (strcmp(e->name, "set-cookie") == 0) 680 js_arr_push(js, arr, js_mkstr(js, e->value, strlen(e->value))); 681 } 682 return arr; 683} 684 685static ant_value_t js_headers_for_each(ant_t *js, ant_value_t *args, int nargs) { 686 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.forEach requires 1 argument"); 687 688 ant_value_t cb = args[0]; 689 uint8_t cbt = vtype(cb); 690 if (cbt != T_FUNC && cbt != T_CFUNC) 691 return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.forEach callback must be callable"); 692 693 ant_value_t this_obj = js->this_val; 694 hdr_list_t *l = get_list(this_obj); 695 if (!l) return js_mkundef(); 696 697 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef(); 698 699 size_t count = 0; 700 sorted_pair_t *view = build_sorted_view(l, &count); 701 702 for (size_t i = 0; i < count; i++) { 703 ant_value_t call_args[3] = { 704 js_mkstr(js, view[i].value, strlen(view[i].value)), 705 js_mkstr(js, view[i].name, strlen(view[i].name)), 706 this_obj 707 }; 708 ant_value_t r = sv_vm_call(js->vm, js, cb, this_arg, call_args, 3, NULL, false); 709 if (is_err(r)) { free_sorted_view(view, count); return r; } 710 } 711 712 free_sorted_view(view, count); 713 return js_mkundef(); 714} 715 716static ant_value_t js_headers_keys(ant_t *js, ant_value_t *args, int nargs) { 717 return make_headers_iter(js, js->this_val, ITER_KEYS); 718} 719 720static ant_value_t js_headers_values(ant_t *js, ant_value_t *args, int nargs) { 721 return make_headers_iter(js, js->this_val, ITER_VALUES); 722} 723 724static ant_value_t js_headers_entries(ant_t *js, ant_value_t *args, int nargs) { 725 return make_headers_iter(js, js->this_val, ITER_ENTRIES); 726} 727 728static ant_value_t headers_inspect_finish(ant_t *js, ant_value_t this_obj, ant_value_t body_obj) { 729 ant_value_t tag_val = js_get_sym(js, this_obj, get_toStringTag_sym()); 730 const char *tag = vtype(tag_val) == T_STR ? js_getstr(js, tag_val, NULL) : "Headers"; 731 732 js_inspect_builder_t builder; 733 if (!js_inspect_builder_init_dynamic(&builder, js, 128)) { 734 return js_mkerr(js, "out of memory"); 735 } 736 737 bool ok = js_inspect_header_for(&builder, body_obj, "%s", tag); 738 if (ok) ok = js_inspect_object_body(&builder, body_obj); 739 if (ok) ok = js_inspect_close(&builder); 740 741 if (!ok) { 742 js_inspect_builder_dispose(&builder); 743 return js_mkerr(js, "out of memory"); 744 } 745 746 return js_inspect_builder_result(&builder); 747} 748 749static ant_value_t headers_inspect(ant_t *js, ant_value_t *args, int nargs) { 750 ant_value_t this_obj = js_getthis(js); 751 hdr_list_t *list = get_list(this_obj); 752 ant_value_t out = js_mkobj(js); 753 754 if (!list) return js_mkerr(js, "Invalid Headers object"); 755 756 for (hdr_entry_t *e = list->head; e; e = e->next) { 757 ant_value_t existing = js_get(js, out, e->name); 758 if (vtype(existing) == T_UNDEF) { 759 js_set(js, out, e->name, js_mkstr(js, e->value, strlen(e->value))); 760 continue; 761 } 762 763 size_t existing_len = 0; 764 const char *existing_str = js_getstr(js, existing, &existing_len); 765 size_t value_len = strlen(e->value); 766 size_t combined_len = existing_len + 2 + value_len; 767 char *combined = malloc(combined_len + 1); 768 if (!combined) return js_mkerr(js, "out of memory"); 769 770 memcpy(combined, existing_str, existing_len); 771 combined[existing_len] = ','; 772 combined[existing_len + 1] = ' '; 773 memcpy(combined + existing_len + 2, e->value, value_len); 774 combined[combined_len] = '\0'; 775 776 js_set(js, out, e->name, js_mkstr(js, combined, combined_len)); 777 free(combined); 778 } 779 780 return headers_inspect_finish(js, this_obj, out); 781} 782 783static ant_value_t js_headers_ctor(ant_t *js, ant_value_t *args, int nargs) { 784 if (vtype(js->new_target) == T_UNDEF) 785 return js_mkerr_typed(js, JS_ERR_TYPE, "Headers constructor requires 'new'"); 786 787 hdr_list_t *l = list_new(); 788 if (!l) return js_mkerr(js, "out of memory"); 789 790 ant_value_t init = (nargs >= 1) ? args[0] : js_mkundef(); 791 792 if (vtype(init) != T_UNDEF) { 793 uint8_t t = vtype(init); 794 795 if (t == T_NULL || (t != T_OBJ && t != T_ARR && t != T_FUNC && t != T_CFUNC)) { 796 list_free(l); 797 return js_mkerr_typed(js, JS_ERR_TYPE, 798 "Failed to construct 'Headers': The provided value is not of type 'HeadersInit'"); 799 } 800 801 ant_value_t iter_fn = js_get_sym(js, init, get_iterator_sym()); 802 bool has_iter = (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC); 803 804 ant_value_t r; 805 if (t == T_ARR || has_iter) r = init_from_sequence(js, l, init); 806 else r = init_from_record(js, l, init); 807 if (is_err(r)) { list_free(l); return r; } 808 } 809 810 ant_value_t obj = js_mkobj(js); 811 ant_value_t proto = js_instance_proto_from_new_target(js, g_headers_proto); 812 if (is_object_type(proto)) js_set_proto_init(obj, proto); 813 814 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); 815 js_set_slot(obj, SLOT_DATA, ANT_PTR(l)); 816 js_set_slot(obj, SLOT_HEADERS_GUARD, js_mknum(HEADERS_GUARD_NONE)); 817 818 return obj; 819} 820 821ant_value_t headers_create_empty(ant_t *js) { 822 hdr_list_t *l = list_new(); 823 if (!l) return js_mkerr(js, "out of memory"); 824 825 ant_value_t obj = js_mkobj(js); 826 js_set_proto_init(obj, g_headers_proto); 827 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); 828 js_set_slot(obj, SLOT_DATA, ANT_PTR(l)); 829 js_set_slot(obj, SLOT_HEADERS_GUARD, js_mknum(HEADERS_GUARD_NONE)); 830 831 return obj; 832} 833 834bool headers_copy_from(ant_t *js, ant_value_t dst, ant_value_t src) { 835 hdr_list_t *src_list = get_list(src); 836 hdr_list_t *dst_list = get_list(dst); 837 838 if (!dst_list) return false; 839 if (!src_list) return true; 840 841 for (hdr_entry_t *e = src_list->head; e; e = e->next) 842 list_append_raw(dst_list, e->name, e->value); 843 return true; 844} 845 846void headers_set_guard(ant_value_t hdrs, headers_guard_t guard) { 847 js_set_slot(hdrs, SLOT_HEADERS_GUARD, js_mknum(guard)); 848} 849 850headers_guard_t headers_get_guard(ant_value_t hdrs) { 851 return get_guard(hdrs); 852} 853 854void headers_apply_guard(ant_value_t hdrs) { 855 list_apply_guard(get_list(hdrs), get_guard(hdrs)); 856} 857 858void headers_append_if_missing(ant_value_t hdrs, const char *name, const char *value) { 859 hdr_list_t *l = get_list(hdrs); 860 if (!l || !name || !value) return; 861 char *lower = lowercase_dup(name); 862 if (!lower) return; 863 for (hdr_entry_t *e = l->head; e; e = e->next) { 864 if (strcmp(e->name, lower) == 0) { free(lower); return; } 865 } 866 list_append_raw(l, lower, value); 867 free(lower); 868} 869 870void headers_for_each(ant_value_t hdrs, headers_foreach_cb cb, void *ctx) { 871 hdr_list_t *l = get_list(hdrs); 872 if (!l || !cb) return; 873 for (hdr_entry_t *e = l->head; e; e = e->next) cb(e->name, e->value, ctx); 874} 875 876bool headers_set_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value) { 877 hdr_list_t *l = get_list(hdrs); 878 headers_guard_t guard = 0; 879 880 char *norm = NULL; 881 char *lower = NULL; 882 883 if (!l || !name || !value) return false; 884 if (!is_valid_name(name)) return false; 885 886 norm = normalize_value(value); 887 if (!norm) return false; 888 if (!is_valid_value(norm)) { 889 free(norm); 890 return false; 891 } 892 893 lower = lowercase_dup(name); 894 if (!lower) { 895 free(norm); 896 return false; 897 } 898 899 guard = get_guard(hdrs); 900 if (guard == HEADERS_GUARD_IMMUTABLE) { 901 free(lower); 902 free(norm); 903 return false; 904 } 905 906 list_delete_name(l, lower); 907 if (header_allowed_for_guard(lower, norm, guard)) list_append_raw(l, lower, norm); 908 free(lower); 909 free(norm); 910 911 return true; 912} 913 914ant_value_t headers_create_from_init(ant_t *js, ant_value_t init) { 915 ant_value_t new_hdrs = 0; 916 uint8_t ht = vtype(init); 917 918 new_hdrs = headers_create_empty(js); 919 if (is_err(new_hdrs)) return new_hdrs; 920 if (ht == T_UNDEF) return new_hdrs; 921 922 if (headers_is_headers(init)) { 923 headers_copy_from(js, new_hdrs, init); 924 return new_hdrs; 925 } 926 927 if (ht == T_ARR) { 928 ant_offset_t len = js_arr_len(js, init); 929 for (ant_offset_t i = 0; i < len; i++) { 930 ant_value_t pair = js_arr_get(js, init, i); 931 ant_value_t r = 0; 932 if (js_arr_len(js, pair) < 2) continue; 933 r = headers_append_value(js, new_hdrs, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); 934 if (is_err(r)) return r; 935 } 936 return new_hdrs; 937 } 938 939 if (ht == T_OBJ) { 940 ant_iter_t it = js_prop_iter_begin(js, init); 941 const char *key = NULL; 942 size_t key_len = 0; 943 ant_value_t val = 0; 944 945 while (js_prop_iter_next(&it, &key, &key_len, &val)) { 946 ant_value_t r = headers_append_value(js, new_hdrs, js_mkstr(js, key, key_len), val); 947 if (is_err(r)) { 948 js_prop_iter_end(&it); 949 return r; 950 } 951 } 952 953 js_prop_iter_end(&it); 954 } 955 956 return new_hdrs; 957} 958 959bool headers_init_has_name(ant_t *js, ant_value_t init, const char *name) { 960 uint8_t ht = vtype(init); 961 962 if (ht == T_UNDEF) return false; 963 if (headers_is_headers(init)) { 964 ant_value_t value = headers_get_value(js, init, name); 965 return !is_err(value) && vtype(value) != T_NULL; 966 } 967 968 if (ht == T_ARR) { 969 ant_offset_t len = js_arr_len(js, init); 970 for (ant_offset_t i = 0; i < len; i++) { 971 ant_value_t pair = js_arr_get(js, init, i); 972 ant_value_t key_v = 0; 973 const char *key = NULL; 974 if (js_arr_len(js, pair) < 1) continue; 975 key_v = js_arr_get(js, pair, 0); 976 if (vtype(key_v) != T_STR) { 977 key_v = js_tostring_val(js, key_v); 978 if (is_err(key_v)) continue; 979 } 980 key = js_getstr(js, key_v, NULL); 981 if (key && strcasecmp(key, name) == 0) return true; 982 } 983 return false; 984 } 985 986 if (ht == T_OBJ) { 987 ant_iter_t it = js_prop_iter_begin(js, init); 988 const char *key = NULL; 989 size_t key_len = 0; 990 ant_value_t value = 0; 991 bool found = false; 992 993 while (js_prop_iter_next(&it, &key, &key_len, &value)) { 994 (void)value; 995 if (key && strcasecmp(key, name) == 0) { 996 found = true; 997 break; 998 } 999 } 1000 1001 js_prop_iter_end(&it); 1002 return found; 1003 } 1004 1005 return false; 1006} 1007 1008ant_value_t headers_get_value(ant_t *js, ant_value_t hdrs, const char *name) { 1009 hdr_list_t *l = get_list(hdrs); 1010 1011 if (!l) return js_mknull(); 1012 if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 1013 1014 char *lower = lowercase_dup(name); 1015 if (!lower) return js_mkerr(js, "out of memory"); 1016 1017 if (strcmp(lower, "set-cookie") == 0) { 1018 for (hdr_entry_t *e = l->head; e; e = e->next) { 1019 if (strcmp(e->name, lower) == 0) { 1020 ant_value_t ret = js_mkstr(js, e->value, strlen(e->value)); 1021 free(lower); 1022 return ret; 1023 }} 1024 free(lower); 1025 return js_mknull(); 1026 } 1027 1028 size_t total = 0; 1029 int count = 0; 1030 1031 for (hdr_entry_t *e = l->head; e; e = e->next) { 1032 if (strcmp(e->name, lower) == 0) { 1033 if (count > 0) total += 2; 1034 total += strlen(e->value); 1035 count++; 1036 }} 1037 1038 if (count == 0) { 1039 free(lower); 1040 return js_mknull(); 1041 } 1042 1043 char *combined = malloc(total + 1); 1044 if (!combined) { 1045 free(lower); 1046 return js_mkerr(js, "out of memory"); 1047 } 1048 1049 size_t pos = 0; 1050 int seen = 0; 1051 1052 for (hdr_entry_t *e = l->head; e; e = e->next) { 1053 if (strcmp(e->name, lower) == 0) { 1054 if (seen > 0) { combined[pos++] = ','; combined[pos++] = ' '; } 1055 size_t vl = strlen(e->value); 1056 memcpy(combined + pos, e->value, vl); 1057 pos += vl; 1058 seen++; 1059 }} 1060 1061 combined[pos] = '\0'; 1062 free(lower); 1063 1064 ant_value_t ret = js_mkstr(js, combined, pos); 1065 free(combined); 1066 1067 return ret; 1068} 1069 1070void init_headers_module(void) { 1071 ant_t *js = rt->js; 1072 ant_value_t g = js_glob(js); 1073 1074 g_headers_iter_proto = js_mkobj(js); 1075 js_set_proto_init(g_headers_iter_proto, js->sym.iterator_proto); 1076 js_set(js, g_headers_iter_proto, "next", js_mkfun(headers_iter_next)); 1077 js_set_descriptor(js, g_headers_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); 1078 js_set_sym(js, g_headers_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb)); 1079 js_iter_register_advance(g_headers_iter_proto, advance_headers); 1080 1081 g_headers_proto = js_mkobj(js); 1082 1083 js_set(js, g_headers_proto, "append", js_mkfun(js_headers_append)); 1084 js_set(js, g_headers_proto, "set", js_mkfun(js_headers_set)); 1085 js_set(js, g_headers_proto, "get", js_mkfun(js_headers_get)); 1086 js_set(js, g_headers_proto, "has", js_mkfun(js_headers_has)); 1087 js_set(js, g_headers_proto, "delete", js_mkfun(js_headers_delete)); 1088 js_set(js, g_headers_proto, "forEach", js_mkfun(js_headers_for_each)); 1089 js_set(js, g_headers_proto, "keys", js_mkfun(js_headers_keys)); 1090 js_set(js, g_headers_proto, "values", js_mkfun(js_headers_values)); 1091 js_set(js, g_headers_proto, "entries", js_mkfun(js_headers_entries)); 1092 js_set(js, g_headers_proto, "getSetCookie", js_mkfun(js_headers_get_set_cookie)); 1093 1094 js_set_sym(js, g_headers_proto, get_iterator_sym(), js_get(js, g_headers_proto, "entries")); 1095 js_set_sym(js, g_headers_proto, get_inspect_sym(), js_mkfun(headers_inspect)); 1096 js_set_sym(js, g_headers_proto, get_toStringTag_sym(), js_mkstr(js, "Headers", 7)); 1097 1098 ant_value_t ctor_obj = js_mkobj(js); 1099 js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(js_headers_ctor)); 1100 js_mkprop_fast(js, ctor_obj, "prototype", 9, g_headers_proto); 1101 js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, "Headers", 7)); 1102 js_set_descriptor(js, ctor_obj, "name", 4, 0); 1103 1104 ant_value_t ctor = js_obj_to_func(ctor_obj); 1105 js_set(js, g_headers_proto, "constructor", ctor); 1106 js_set_descriptor(js, g_headers_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 1107 1108 js_set(js, g, "Headers", ctor); 1109 js_set_descriptor(js, g, "Headers", 7, JS_DESC_W | JS_DESC_C); 1110}