Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111#include <stdlib.h>#include <string.h>#include <ctype.h>#include <stdio.h>
#include "ant.h"#include "errors.h"#include "runtime.h"#include "internal.h"#include "descriptors.h"#include "silver/engine.h"
#include "modules/headers.h"#include "modules/symbol.h"
typedef struct hdr_entry { char *name; char *value; struct hdr_entry *next;} hdr_entry_t;
typedef struct { hdr_entry_t *head; hdr_entry_t **tail; size_t count;} hdr_list_t;
typedef struct { char *name; char *value;} sorted_pair_t;
typedef struct { hdr_list_t *list; size_t index; int kind;} hdr_iter_t;
enum { ITER_ENTRIES = 0, ITER_KEYS = 1, ITER_VALUES = 2};
ant_value_t g_headers_proto = 0;ant_value_t g_headers_iter_proto = 0;
static hdr_list_t *list_new(void) { hdr_list_t *l = ant_calloc(sizeof(hdr_list_t)); if (!l) return NULL; l->head = NULL; l->tail = &l->head; return l;}
static void list_free(hdr_list_t *l) { if (!l) return; for (hdr_entry_t *e = l->head; e; ) { hdr_entry_t *n = e->next; free(e->name); free(e->value); free(e); e = n; } free(l);}
static hdr_list_t *get_list(ant_value_t obj) { ant_value_t slot = js_get_slot(obj, SLOT_DATA); if (vtype(slot) != T_NUM) return NULL; return (hdr_list_t *)(uintptr_t)(size_t)js_getnum(slot);}
static headers_guard_t get_guard(ant_value_t obj) { ant_value_t slot = js_get_slot(obj, SLOT_HEADERS_GUARD); if (vtype(slot) != T_NUM) return HEADERS_GUARD_NONE; return (headers_guard_t)(int)js_getnum(slot);}
bool headers_is_headers(ant_value_t obj) { return js_check_brand(obj, BRAND_HEADERS);}
static bool is_token_char(unsigned char c) { if (c > 127) return false; static const char ok[] = "!#$%&'*+-.^_`|~" "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; return strchr(ok, (char)c) != NULL;}
static bool is_valid_name(const char *s) { if (!s || !*s) return false; for (const unsigned char *p = (const unsigned char *)s; *p; p++) if (!is_token_char(*p)) return false; return true;}
static bool is_valid_value(const char *s) { if (!s) return false; for (const unsigned char *p = (const unsigned char *)s; *p; p++) { unsigned char c = *p; if (c == 0 || c == '\r' || c == '\n' || c > 127) return false; } return true;}
static char *normalize_value(const char *s) { if (!s) return strdup(""); while (*s == ' ' || *s == '\t') s++; size_t len = strlen(s); while (len > 0 && (s[len - 1] == ' ' || s[len - 1] == '\t')) len--; char *out = malloc(len + 1); if (!out) return NULL; memcpy(out, s, len); out[len] = '\0'; return out;}
static char *lowercase_dup(const char *s) { if (!s) return strdup(""); size_t len = strlen(s); char *out = malloc(len + 1); if (!out) return NULL; for (size_t i = 0; i <= len; i++) out[i] = (char)tolower((unsigned char)s[i]); return out;}
typedef struct { const char *name; bool prefix;} header_rule_t;
static const header_rule_t k_forbidden_request_headers[] = { { "accept-charset", false }, { "accept-encoding", false }, { "access-control-request-headers", false }, { "access-control-request-method", false }, { "connection", false }, { "content-length", false }, { "cookie", false }, { "cookie2", false }, { "date", false }, { "dnt", false }, { "expect", false }, { "host", false }, { "keep-alive", false }, { "origin", false }, { "referer", false }, { "set-cookie", false }, { "te", false }, { "trailer", false }, { "transfer-encoding", false }, { "upgrade", false }, { "via", false }, { "proxy-", true }, { "sec-", true },};
static const header_rule_t k_forbidden_response_headers[] = { { "set-cookie", false }, { "set-cookie2", false },};
static const char *k_cors_safelisted_content_types[] = { "application/x-www-form-urlencoded", "multipart/form-data", "text/plain",};
static const char *k_no_cors_safelisted_names[] = { "accept", "accept-language", "content-language",};
static bool matches_rule(const char *name, const header_rule_t *rules, size_t count) { for (size_t i = 0; i < count; i++) { size_t len = strlen(rules[i].name); if (rules[i].prefix) { if (strncmp(name, rules[i].name, len) == 0) return true; } else if (strcmp(name, rules[i].name) == 0) return true; } return false;}
static bool matches_string(const char *value, const char *const *list, size_t count) { for (size_t i = 0; i < count; i++) { if (strcmp(value, list[i]) == 0) return true; } return false;}
static bool is_forbidden_request_header_name(const char *lower_name) { return matches_rule(lower_name, k_forbidden_request_headers, sizeof(k_forbidden_request_headers) / sizeof(k_forbidden_request_headers[0]));}
static bool is_forbidden_response_header_name(const char *lower_name) { return matches_rule(lower_name, k_forbidden_response_headers, sizeof(k_forbidden_response_headers) / sizeof(k_forbidden_response_headers[0]));}
static bool is_cors_safelisted_content_type_value(const char *value) { char *lower = lowercase_dup(value ? value : ""); if (!lower) return false; char *semi = strchr(lower, ';'); if (!semi) { bool ok = matches_string( lower, k_cors_safelisted_content_types, sizeof(k_cors_safelisted_content_types) / sizeof(k_cors_safelisted_content_types[0]) ); free(lower); return ok; }
*semi++ = '\0'; while (*semi == ' ' || *semi == '\t') semi++; bool essence_ok = matches_string( lower, k_cors_safelisted_content_types, sizeof(k_cors_safelisted_content_types) / sizeof(k_cors_safelisted_content_types[0]) ); bool param_ok = strcmp(semi, "charset=utf-8") == 0; free(lower); return essence_ok && param_ok;}
static bool is_no_cors_safelisted_name_value(const char *lower_name, const char *value) { if ( matches_string( lower_name, k_no_cors_safelisted_names, sizeof(k_no_cors_safelisted_names) / sizeof(k_no_cors_safelisted_names[0])) ) return true; if (strcmp(lower_name, "content-type") == 0) return value && value[0] && is_cors_safelisted_content_type_value(value); return false;}
static bool header_allowed_for_guard(const char *lower_name, const char *value, headers_guard_t guard) { if (guard == HEADERS_GUARD_NONE) return true; if (guard == HEADERS_GUARD_IMMUTABLE) return true; if (is_forbidden_request_header_name(lower_name)) return false; if (guard == HEADERS_GUARD_RESPONSE) return !is_forbidden_response_header_name(lower_name); if (guard == HEADERS_GUARD_REQUEST_NO_CORS) return is_no_cors_safelisted_name_value(lower_name, value); return true;}
static ant_value_t headers_guard_error(ant_t *js, headers_guard_t guard) { if (guard != HEADERS_GUARD_IMMUTABLE) return js_mkundef(); return js_mkerr_typed(js, JS_ERR_TYPE, "Headers are immutable");}
static void list_apply_guard(hdr_list_t *l, headers_guard_t guard) { if (!l || guard == HEADERS_GUARD_NONE || guard == HEADERS_GUARD_IMMUTABLE) return;
hdr_entry_t **pp = &l->head; l->tail = &l->head; while (*pp) { hdr_entry_t *cur = *pp; if (!header_allowed_for_guard(cur->name, cur->value, guard)) { *pp = cur->next; free(cur->name); free(cur->value); free(cur); l->count--; continue; } l->tail = &cur->next; pp = &cur->next; }}
static void list_append_raw(hdr_list_t *l, const char *lower_name, const char *value) { hdr_entry_t *e = ant_calloc(sizeof(hdr_entry_t)); if (!e) return; e->name = strdup(lower_name); e->value = strdup(value); *l->tail = e; l->tail = &e->next; l->count++;}
static void list_delete_name(hdr_list_t *l, const char *lower_name) { hdr_entry_t **pp = &l->head; l->tail = &l->head; while (*pp) { if (strcmp((*pp)->name, lower_name) == 0) { hdr_entry_t *dead = *pp; *pp = dead->next; free(dead->name); free(dead->value); free(dead); l->count--; } else { l->tail = &(*pp)->next; pp = &(*pp)->next; }}}
static int cmp_pairs(const void *a, const void *b) { return strcmp(((const sorted_pair_t *)a)->name, ((const sorted_pair_t *)b)->name);}
static sorted_pair_t *build_sorted_view(hdr_list_t *l, size_t *out) { *out = 0; if (!l || l->count == 0) return NULL;
sorted_pair_t *raw = malloc(l->count * sizeof(sorted_pair_t)); if (!raw) return NULL;
size_t n = 0; for (hdr_entry_t *e = l->head; e; e = e->next) { raw[n].name = e->name; raw[n].value = e->value; n++; } qsort(raw, n, sizeof(sorted_pair_t), cmp_pairs); sorted_pair_t *res = malloc(n * sizeof(sorted_pair_t)); if (!res) { free(raw); return NULL; }
size_t ri = 0; for (size_t i = 0; i < n; ) { if (strcmp(raw[i].name, "set-cookie") == 0) { res[ri].name = strdup(raw[i].name); res[ri].value = strdup(raw[i].value); ri++; i++; } else { size_t j = i + 1; size_t total = strlen(raw[i].value); while (j < n && strcmp(raw[j].name, raw[i].name) == 0) { total += 2 + strlen(raw[j].value); j++; } char *combined = malloc(total + 1); if (!combined) combined = strdup(""); size_t pos = 0; for (size_t k = i; k < j; k++) { if (k > i) { combined[pos++] = ','; combined[pos++] = ' '; } size_t vl = strlen(raw[k].value); memcpy(combined + pos, raw[k].value, vl); pos += vl; } combined[pos] = '\0'; res[ri].name = strdup(raw[i].name); res[ri].value = combined; ri++; i = j; }}
free(raw); *out = ri; return res;}
static void free_sorted_view(sorted_pair_t *v, size_t n) { if (!v) return; for (size_t i = 0; i < n; i++) { free(v[i].name); free(v[i].value); } free(v);}
static ant_value_t headers_append_name_value(ant_t *js, hdr_list_t *l, const char *name, const char *value) { if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name: %s", name ? name : "");
char *norm = normalize_value(value); if (!norm) return js_mkerr(js, "out of memory"); if (!is_valid_value(norm)) { free(norm); return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); }
char *lower = lowercase_dup(name); if (!lower) { free(norm); return js_mkerr(js, "out of memory"); }
list_append_raw(l, lower, norm); free(lower); free(norm); return js_mkundef();}
static ant_value_t headers_append_pair(ant_t *js, hdr_list_t *l, ant_value_t name_v, ant_value_t value_v) { const char *name = NULL; const char *value = NULL;
if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } if (vtype(value_v) != T_STR) { value_v = js_tostring_val(js, value_v); if (is_err(value_v)) return value_v; }
name = js_getstr(js, name_v, NULL); value = js_getstr(js, value_v, NULL); return headers_append_name_value(js, l, name, value);}
ant_value_t headers_append_value(ant_t *js, ant_value_t hdrs, ant_value_t name_v, ant_value_t value_v) { hdr_list_t *l = get_list(hdrs); ant_value_t r = 0;
if (!l) return js_mkerr(js, "Invalid Headers object"); r = headers_append_pair(js, l, name_v, value_v); if (is_err(r)) return r; list_apply_guard(l, get_guard(hdrs)); return js_mkundef();}
ant_value_t headers_append_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value) { hdr_list_t *l = get_list(hdrs); ant_value_t r = 0;
if (!l) return js_mkerr(js, "Invalid Headers object"); r = headers_append_name_value(js, l, name, value); if (is_err(r)) return r; list_apply_guard(l, get_guard(hdrs)); return js_mkundef();}
static ant_value_t init_from_sequence(ant_t *js, hdr_list_t *l, ant_value_t seq) { js_iter_t it; if (!js_iter_open(js, seq, &it)) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers init is not iterable");
ant_value_t pair; while (js_iter_next(js, &it, &pair)) { uint8_t pt = vtype(pair); if (pt != T_ARR && pt != T_OBJ) { js_iter_close(js, &it); return js_mkerr_typed(js, JS_ERR_TYPE, "Each header init pair must be a sequence"); } if (js_arr_len(js, pair) != 2) { js_iter_close(js, &it); return js_mkerr_typed(js, JS_ERR_TYPE, "Each header init pair must have exactly 2 elements"); } ant_value_t r = headers_append_pair(js, l, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); if (is_err(r)) { js_iter_close(js, &it); return r; } } return js_mkundef();}
static ant_value_t init_from_record(ant_t *js, hdr_list_t *l, ant_value_t obj) { ant_iter_t it = js_prop_iter_begin(js, obj); const char *key; size_t key_len; ant_value_t val;
while (js_prop_iter_next(&it, &key, &key_len, &val)) { ant_value_t r = headers_append_pair(js, l, js_mkstr(js, key, key_len), val); if (is_err(r)) { js_prop_iter_end(&it); return r; } } js_prop_iter_end(&it); return js_mkundef();}
bool advance_headers(ant_t *js, js_iter_t *it, ant_value_t *out) { ant_value_t state_val = js_get_slot(it->iterator, SLOT_ITER_STATE); if (vtype(state_val) == T_UNDEF) return false; hdr_iter_t *st = (hdr_iter_t *)(uintptr_t)(size_t)js_getnum(state_val);
size_t count = 0; sorted_pair_t *view = build_sorted_view(st->list, &count);
if (st->index >= count) { free_sorted_view(view, count); return false; }
sorted_pair_t *e = &view[st->index]; switch (st->kind) { case ITER_KEYS: *out = js_mkstr(js, e->name, strlen(e->name)); break; case ITER_VALUES: *out = js_mkstr(js, e->value, strlen(e->value)); break; default: { *out = js_mkarr(js); js_arr_push(js, *out, js_mkstr(js, e->name, strlen(e->name))); js_arr_push(js, *out, js_mkstr(js, e->value, strlen(e->value))); break; }}
free_sorted_view(view, count); st->index++; return true;}
static ant_value_t headers_iter_next(ant_t *js, ant_value_t *args, int nargs) { js_iter_t it = { .iterator = js->this_val }; ant_value_t value; return js_iter_result(js, advance_headers(js, &it, &value), value);}
static ant_value_t make_headers_iter(ant_t *js, ant_value_t headers_obj, int kind) { hdr_list_t *l = get_list(headers_obj);
hdr_iter_t *st = ant_calloc(sizeof(hdr_iter_t)); if (!st) return js_mkerr(js, "out of memory"); st->list = l ? l : list_new(); st->kind = kind;
ant_value_t iter = js_mkobj(js); js_set_proto_init(iter, g_headers_iter_proto); js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(st)); return iter;}
static ant_value_t js_headers_append(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.append requires 2 arguments"); hdr_list_t *l = get_list(js->this_val); if (!l) return js_mkerr(js, "Invalid Headers object"); ant_value_t guard_err = headers_guard_error(js, get_guard(js->this_val)); if (is_err(guard_err)) return guard_err; ant_value_t r = headers_append_pair(js, l, args[0], args[1]); if (is_err(r)) return r; list_apply_guard(l, get_guard(js->this_val)); return js_mkundef();}
static ant_value_t js_headers_set(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.set requires 2 arguments"); hdr_list_t *l = get_list(js->this_val); if (!l) return js_mkerr(js, "Invalid Headers object"); ant_value_t guard_err = headers_guard_error(js, get_guard(js->this_val)); if (is_err(guard_err)) return guard_err;
ant_value_t name_v = args[0]; ant_value_t value_v = args[1]; if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } if (vtype(value_v) != T_STR) { value_v = js_tostring_val(js, value_v); if (is_err(value_v)) return value_v; }
const char *name = js_getstr(js, name_v, NULL); const char *value = js_getstr(js, value_v, NULL);
if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name: %s", name ? name : "");
char *norm = normalize_value(value); if (!norm) return js_mkerr(js, "out of memory"); if (!is_valid_value(norm)) { free(norm); return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); }
char *lower = lowercase_dup(name); if (!lower) { free(norm); return js_mkerr(js, "out of memory"); }
list_delete_name(l, lower); if (header_allowed_for_guard(lower, norm, get_guard(js->this_val))) list_append_raw(l, lower, norm); free(lower); free(norm); return js_mkundef();}
static ant_value_t js_headers_get(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.get requires 1 argument"); hdr_list_t *l = get_list(js->this_val); if (!l) return js_mknull();
ant_value_t name_v = args[0]; if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } const char *name = js_getstr(js, name_v, NULL); if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *lower = lowercase_dup(name); if (!lower) return js_mkerr(js, "out of memory");
// set-cookie is never combined per Fetch spec if (strcmp(lower, "set-cookie") == 0) { for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { ant_value_t ret = js_mkstr(js, e->value, strlen(e->value)); free(lower); return ret; } } free(lower); return js_mknull(); }
size_t total = 0; int count = 0; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { if (count > 0) total += 2; total += strlen(e->value); count++; } }
if (count == 0) { free(lower); return js_mknull(); }
char *combined = malloc(total + 1); if (!combined) { free(lower); return js_mkerr(js, "out of memory"); }
size_t pos = 0; int seen = 0; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { if (seen > 0) { combined[pos++] = ','; combined[pos++] = ' '; } size_t vl = strlen(e->value); memcpy(combined + pos, e->value, vl); pos += vl; seen++; } } combined[pos] = '\0'; free(lower);
ant_value_t ret = js_mkstr(js, combined, pos); free(combined); return ret;}
static ant_value_t js_headers_has(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.has requires 1 argument"); hdr_list_t *l = get_list(js->this_val); if (!l) return js_false;
ant_value_t name_v = args[0]; if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } const char *name = js_getstr(js, name_v, NULL); if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *lower = lowercase_dup(name); if (!lower) return js_mkerr(js, "out of memory");
bool found = false; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { found = true; break; } } free(lower); return js_bool(found);}
static ant_value_t js_headers_delete(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.delete requires 1 argument"); hdr_list_t *l = get_list(js->this_val); if (!l) return js_mkundef(); ant_value_t guard_err = headers_guard_error(js, get_guard(js->this_val)); if (is_err(guard_err)) return guard_err;
ant_value_t name_v = args[0]; if (vtype(name_v) != T_STR) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } const char *name = js_getstr(js, name_v, NULL); if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *lower = lowercase_dup(name); if (!lower) return js_mkerr(js, "out of memory"); list_delete_name(l, lower); free(lower); return js_mkundef();}
static ant_value_t js_headers_get_set_cookie(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; hdr_list_t *l = get_list(js->this_val); ant_value_t arr = js_mkarr(js); if (!l) return arr; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, "set-cookie") == 0) js_arr_push(js, arr, js_mkstr(js, e->value, strlen(e->value))); } return arr;}
static ant_value_t js_headers_for_each(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.forEach requires 1 argument");
ant_value_t cb = args[0]; uint8_t cbt = vtype(cb); if (cbt != T_FUNC && cbt != T_CFUNC) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.forEach callback must be callable");
ant_value_t this_obj = js->this_val; hdr_list_t *l = get_list(this_obj); if (!l) return js_mkundef();
ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
size_t count = 0; sorted_pair_t *view = build_sorted_view(l, &count);
for (size_t i = 0; i < count; i++) { ant_value_t call_args[3] = { js_mkstr(js, view[i].value, strlen(view[i].value)), js_mkstr(js, view[i].name, strlen(view[i].name)), this_obj }; ant_value_t r = sv_vm_call(js->vm, js, cb, this_arg, call_args, 3, NULL, false); if (is_err(r)) { free_sorted_view(view, count); return r; } }
free_sorted_view(view, count); return js_mkundef();}
static ant_value_t js_headers_keys(ant_t *js, ant_value_t *args, int nargs) { return make_headers_iter(js, js->this_val, ITER_KEYS);}
static ant_value_t js_headers_values(ant_t *js, ant_value_t *args, int nargs) { return make_headers_iter(js, js->this_val, ITER_VALUES);}
static ant_value_t js_headers_entries(ant_t *js, ant_value_t *args, int nargs) { return make_headers_iter(js, js->this_val, ITER_ENTRIES);}
static ant_value_t headers_inspect_finish(ant_t *js, ant_value_t this_obj, ant_value_t body_obj) { ant_value_t tag_val = js_get_sym(js, this_obj, get_toStringTag_sym()); const char *tag = vtype(tag_val) == T_STR ? js_getstr(js, tag_val, NULL) : "Headers";
js_inspect_builder_t builder; if (!js_inspect_builder_init_dynamic(&builder, js, 128)) { return js_mkerr(js, "out of memory"); }
bool ok = js_inspect_header_for(&builder, body_obj, "%s", tag); if (ok) ok = js_inspect_object_body(&builder, body_obj); if (ok) ok = js_inspect_close(&builder);
if (!ok) { js_inspect_builder_dispose(&builder); return js_mkerr(js, "out of memory"); }
return js_inspect_builder_result(&builder);}
static ant_value_t headers_inspect(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); hdr_list_t *list = get_list(this_obj); ant_value_t out = js_mkobj(js);
if (!list) return js_mkerr(js, "Invalid Headers object");
for (hdr_entry_t *e = list->head; e; e = e->next) { ant_value_t existing = js_get(js, out, e->name); if (vtype(existing) == T_UNDEF) { js_set(js, out, e->name, js_mkstr(js, e->value, strlen(e->value))); continue; } size_t existing_len = 0; const char *existing_str = js_getstr(js, existing, &existing_len); size_t value_len = strlen(e->value); size_t combined_len = existing_len + 2 + value_len; char *combined = malloc(combined_len + 1); if (!combined) return js_mkerr(js, "out of memory"); memcpy(combined, existing_str, existing_len); combined[existing_len] = ','; combined[existing_len + 1] = ' '; memcpy(combined + existing_len + 2, e->value, value_len); combined[combined_len] = '\0'; js_set(js, out, e->name, js_mkstr(js, combined, combined_len)); free(combined); }
return headers_inspect_finish(js, this_obj, out);}
static ant_value_t js_headers_ctor(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers constructor requires 'new'");
hdr_list_t *l = list_new(); if (!l) return js_mkerr(js, "out of memory");
ant_value_t init = (nargs >= 1) ? args[0] : js_mkundef();
if (vtype(init) != T_UNDEF) { uint8_t t = vtype(init);
if (t == T_NULL || (t != T_OBJ && t != T_ARR && t != T_FUNC && t != T_CFUNC)) { list_free(l); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Headers': The provided value is not of type 'HeadersInit'"); }
ant_value_t iter_fn = js_get_sym(js, init, get_iterator_sym()); bool has_iter = (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC);
ant_value_t r; if (t == T_ARR || has_iter) r = init_from_sequence(js, l, init); else r = init_from_record(js, l, init); if (is_err(r)) { list_free(l); return r; } }
ant_value_t obj = js_mkobj(js); ant_value_t proto = js_instance_proto_from_new_target(js, g_headers_proto); if (is_object_type(proto)) js_set_proto_init(obj, proto);
js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); js_set_slot(obj, SLOT_DATA, ANT_PTR(l)); js_set_slot(obj, SLOT_HEADERS_GUARD, js_mknum(HEADERS_GUARD_NONE)); return obj;}
ant_value_t headers_create_empty(ant_t *js) { hdr_list_t *l = list_new(); if (!l) return js_mkerr(js, "out of memory"); ant_value_t obj = js_mkobj(js); js_set_proto_init(obj, g_headers_proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); js_set_slot(obj, SLOT_DATA, ANT_PTR(l)); js_set_slot(obj, SLOT_HEADERS_GUARD, js_mknum(HEADERS_GUARD_NONE)); return obj;}
bool headers_copy_from(ant_t *js, ant_value_t dst, ant_value_t src) { hdr_list_t *src_list = get_list(src); hdr_list_t *dst_list = get_list(dst); if (!dst_list) return false; if (!src_list) return true; for (hdr_entry_t *e = src_list->head; e; e = e->next) list_append_raw(dst_list, e->name, e->value); return true;}
void headers_set_guard(ant_value_t hdrs, headers_guard_t guard) { js_set_slot(hdrs, SLOT_HEADERS_GUARD, js_mknum(guard));}
headers_guard_t headers_get_guard(ant_value_t hdrs) { return get_guard(hdrs);}
void headers_apply_guard(ant_value_t hdrs) { list_apply_guard(get_list(hdrs), get_guard(hdrs));}
void headers_append_if_missing(ant_value_t hdrs, const char *name, const char *value) { hdr_list_t *l = get_list(hdrs); if (!l || !name || !value) return; char *lower = lowercase_dup(name); if (!lower) return; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { free(lower); return; } } list_append_raw(l, lower, value); free(lower);}
void headers_for_each(ant_value_t hdrs, headers_foreach_cb cb, void *ctx) { hdr_list_t *l = get_list(hdrs); if (!l || !cb) return; for (hdr_entry_t *e = l->head; e; e = e->next) cb(e->name, e->value, ctx);}
bool headers_set_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value) { hdr_list_t *l = get_list(hdrs); headers_guard_t guard = 0; char *norm = NULL; char *lower = NULL;
if (!l || !name || !value) return false; if (!is_valid_name(name)) return false;
norm = normalize_value(value); if (!norm) return false; if (!is_valid_value(norm)) { free(norm); return false; }
lower = lowercase_dup(name); if (!lower) { free(norm); return false; }
guard = get_guard(hdrs); if (guard == HEADERS_GUARD_IMMUTABLE) { free(lower); free(norm); return false; }
list_delete_name(l, lower); if (header_allowed_for_guard(lower, norm, guard)) list_append_raw(l, lower, norm); free(lower); free(norm); return true;}
ant_value_t headers_create_from_init(ant_t *js, ant_value_t init) { ant_value_t new_hdrs = 0; uint8_t ht = vtype(init);
new_hdrs = headers_create_empty(js); if (is_err(new_hdrs)) return new_hdrs; if (ht == T_UNDEF) return new_hdrs;
if (headers_is_headers(init)) { headers_copy_from(js, new_hdrs, init); return new_hdrs; }
if (ht == T_ARR) { ant_offset_t len = js_arr_len(js, init); for (ant_offset_t i = 0; i < len; i++) { ant_value_t pair = js_arr_get(js, init, i); ant_value_t r = 0; if (js_arr_len(js, pair) < 2) continue; r = headers_append_value(js, new_hdrs, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); if (is_err(r)) return r; } return new_hdrs; }
if (ht == T_OBJ) { ant_iter_t it = js_prop_iter_begin(js, init); const char *key = NULL; size_t key_len = 0; ant_value_t val = 0;
while (js_prop_iter_next(&it, &key, &key_len, &val)) { ant_value_t r = headers_append_value(js, new_hdrs, js_mkstr(js, key, key_len), val); if (is_err(r)) { js_prop_iter_end(&it); return r; } }
js_prop_iter_end(&it); }
return new_hdrs;}
bool headers_init_has_name(ant_t *js, ant_value_t init, const char *name) { uint8_t ht = vtype(init);
if (ht == T_UNDEF) return false; if (headers_is_headers(init)) { ant_value_t value = headers_get_value(js, init, name); return !is_err(value) && vtype(value) != T_NULL; }
if (ht == T_ARR) { ant_offset_t len = js_arr_len(js, init); for (ant_offset_t i = 0; i < len; i++) { ant_value_t pair = js_arr_get(js, init, i); ant_value_t key_v = 0; const char *key = NULL; if (js_arr_len(js, pair) < 1) continue; key_v = js_arr_get(js, pair, 0); if (vtype(key_v) != T_STR) { key_v = js_tostring_val(js, key_v); if (is_err(key_v)) continue; } key = js_getstr(js, key_v, NULL); if (key && strcasecmp(key, name) == 0) return true; } return false; }
if (ht == T_OBJ) { ant_iter_t it = js_prop_iter_begin(js, init); const char *key = NULL; size_t key_len = 0; ant_value_t value = 0; bool found = false;
while (js_prop_iter_next(&it, &key, &key_len, &value)) { (void)value; if (key && strcasecmp(key, name) == 0) { found = true; break; } }
js_prop_iter_end(&it); return found; }
return false;}
ant_value_t headers_get_value(ant_t *js, ant_value_t hdrs, const char *name) { hdr_list_t *l = get_list(hdrs); if (!l) return js_mknull(); if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *lower = lowercase_dup(name); if (!lower) return js_mkerr(js, "out of memory");
if (strcmp(lower, "set-cookie") == 0) { for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { ant_value_t ret = js_mkstr(js, e->value, strlen(e->value)); free(lower); return ret; }} free(lower); return js_mknull(); }
size_t total = 0; int count = 0; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { if (count > 0) total += 2; total += strlen(e->value); count++; }}
if (count == 0) { free(lower); return js_mknull(); }
char *combined = malloc(total + 1); if (!combined) { free(lower); return js_mkerr(js, "out of memory"); }
size_t pos = 0; int seen = 0; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { if (seen > 0) { combined[pos++] = ','; combined[pos++] = ' '; } size_t vl = strlen(e->value); memcpy(combined + pos, e->value, vl); pos += vl; seen++; }} combined[pos] = '\0'; free(lower);
ant_value_t ret = js_mkstr(js, combined, pos); free(combined); return ret;}
void init_headers_module(void) { ant_t *js = rt->js; ant_value_t g = js_glob(js);
g_headers_iter_proto = js_mkobj(js); js_set_proto_init(g_headers_iter_proto, js->sym.iterator_proto); js_set(js, g_headers_iter_proto, "next", js_mkfun(headers_iter_next)); js_set_descriptor(js, g_headers_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); js_set_sym(js, g_headers_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb)); js_iter_register_advance(g_headers_iter_proto, advance_headers);
g_headers_proto = js_mkobj(js);
js_set(js, g_headers_proto, "append", js_mkfun(js_headers_append)); js_set(js, g_headers_proto, "set", js_mkfun(js_headers_set)); js_set(js, g_headers_proto, "get", js_mkfun(js_headers_get)); js_set(js, g_headers_proto, "has", js_mkfun(js_headers_has)); js_set(js, g_headers_proto, "delete", js_mkfun(js_headers_delete)); js_set(js, g_headers_proto, "forEach", js_mkfun(js_headers_for_each)); js_set(js, g_headers_proto, "keys", js_mkfun(js_headers_keys)); js_set(js, g_headers_proto, "values", js_mkfun(js_headers_values)); js_set(js, g_headers_proto, "entries", js_mkfun(js_headers_entries)); js_set(js, g_headers_proto, "getSetCookie", js_mkfun(js_headers_get_set_cookie)); js_set_sym(js, g_headers_proto, get_iterator_sym(), js_get(js, g_headers_proto, "entries")); js_set_sym(js, g_headers_proto, get_inspect_sym(), js_mkfun(headers_inspect)); js_set_sym(js, g_headers_proto, get_toStringTag_sym(), js_mkstr(js, "Headers", 7));
ant_value_t ctor_obj = js_mkobj(js); js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(js_headers_ctor)); js_mkprop_fast(js, ctor_obj, "prototype", 9, g_headers_proto); js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, "Headers", 7)); js_set_descriptor(js, ctor_obj, "name", 4, 0); ant_value_t ctor = js_obj_to_func(ctor_obj); js_set(js, g_headers_proto, "constructor", ctor); js_set_descriptor(js, g_headers_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
js_set(js, g, "Headers", ctor); js_set_descriptor(js, g, "Headers", 7, JS_DESC_W | JS_DESC_C);}