Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217#include <stdlib.h>#include <stddef.h>#include <string.h>#include <stdio.h>
#include "ant.h"#include "cfunc.h"#include "ascii.h"#include "builder.h"#include "ptr.h"#include "errors.h"#include "internal.h"#include "descriptors.h"#include "utf8.h"#include "silver/call.h"
#include "modules/headers.h"#include "modules/http.h"#include "modules/symbol.h"#include "modules/iterator.h"
typedef ant_http_header_t hdr_entry_t;typedef struct headers_data hdr_list_t;
struct headers_data { hdr_entry_t *head; hdr_entry_t **tail; size_t count; bool inline_used; union { max_align_t align; unsigned char bytes[96]; } inline_entry;};
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};
static constexpr uint32_t HEADERS_NATIVE_TAG = Ant_NativeTag("HDRS");static constexpr uint32_t HEADERS_ITER_NATIVE_TAG = Ant_NativeTag("HDIT");
headers_data_t *headers_data_create(void) { hdr_list_t *l = calloc(1, sizeof(hdr_list_t)); if (!l) return NULL; l->head = NULL; l->tail = &l->head; return l;}
void headers_data_destroy(headers_data_t *data) { if (!data) return; for (hdr_entry_t *e = data->head; e; ) { hdr_entry_t *n = e->next; if ((void *)e != (void *)data->inline_entry.bytes) free(e); e = n; } free(data);}
headers_data_t *headers_get_data(ant_value_t obj) { return (headers_data_t *)js_get_native(obj, HEADERS_NATIVE_TAG);}
static void headers_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); headers_data_destroy(headers_get_data(value)); js_clear_native(value, HEADERS_NATIVE_TAG);}
static void headers_iter_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); free(js_get_native(value, HEADERS_ITER_NATIVE_TAG)); js_clear_native(value, HEADERS_ITER_NATIVE_TAG);}
bool headers_is_immutable(ant_value_t obj) { return js_get_slot(obj, SLOT_HEADERS_GUARD) == js_true;}
bool headers_is_headers(ant_value_t obj) { return js_check_brand(obj, BRAND_HEADERS);}
static bool ascii_case_equal(const char *a, const char *b) { while (*a && *b) { if (ant_ascii_to_lower((unsigned char)*a) != ant_ascii_to_lower((unsigned char)*b)) return false; a++; b++; } return *a == *b;}
static bool is_valid_name_n(const char *s, size_t len) { if (!s || len == 0) return false; for (size_t i = 0; i < len; i++) if (!ant_ascii_is_tchar((unsigned char)s[i])) return false; return true;}
static bool is_valid_value_n(const char *s, size_t len) { if (!s) return false; for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)s[i]; if (c == 0 || c == '\r' || c == '\n') return false; } return true;}
headers_data_t *headers_data_take_http_headers(ant_http_header_t **headers) { hdr_list_t *list = NULL; hdr_entry_t **tail = NULL; size_t count = 0;
if (!headers) return NULL; list = headers_data_create(); if (!list) return NULL;
for (hdr_entry_t *entry = *headers; entry; entry = entry->next) { if (!entry->name || !entry->value || entry->name != entry->storage) { headers_data_destroy(list); return NULL; } size_t name_len = strlen(entry->name); size_t value_len = strlen(entry->value);
if ( !is_valid_name_n(entry->name, name_len) || !is_valid_value_n(entry->value, value_len) ) { headers_data_destroy(list); return NULL; } }
tail = &list->head; for (hdr_entry_t *entry = *headers; entry; entry = entry->next) { size_t name_len = strlen(entry->name); size_t value_len = strlen(entry->value); char *value_start = entry->value;
for (size_t i = 0; i < name_len; i++) entry->name[i] = (char)ant_ascii_to_lower((unsigned char)entry->name[i]); while (value_len > 0 && (*value_start == ' ' || *value_start == '\t')) { value_start++; value_len--; } while (value_len > 0 && (value_start[value_len - 1] == ' ' || value_start[value_len - 1] == '\t')) value_len--; if (value_start != entry->value) memmove(entry->value, value_start, value_len); entry->value[value_len] = '\0';
*tail = entry; tail = &entry->next; count++; }
list->tail = tail; list->count = count; *headers = NULL; return list;}
const ant_http_header_t *headers_data_http_view( const headers_data_t *data) { return data ? data->head : NULL;}
static const char *lowercase_name_view( const char *name, size_t len, char **owned) { bool needs_copy = false;
*owned = NULL; for (size_t i = 0; i < len; i++) { if (name[i] >= 'A' && name[i] <= 'Z') { needs_copy = true; break; } } if (!needs_copy) return name;
*owned = malloc(len + 1); if (!*owned) return NULL; for (size_t i = 0; i < len; i++) (*owned)[i] = (char)ant_ascii_to_lower((unsigned char)name[i]); (*owned)[len] = '\0'; return *owned;}
static ant_value_t headers_require_mutable(ant_t *js, ant_value_t headers) { if (!headers_is_immutable(headers)) return js_mkundef(); return js_mkerr_typed(js, JS_ERR_TYPE, "Headers are immutable");}
static bool list_append_parts( hdr_list_t *l, const char *name, size_t name_len, const char *value, size_t value_len, bool lowercase_name) { if (!l || !name || !value) return false; if (name_len > SIZE_MAX - sizeof(hdr_entry_t) - 2) return false; if (value_len > SIZE_MAX - sizeof(hdr_entry_t) - name_len - 2) return false;
size_t required = sizeof(hdr_entry_t) + name_len + value_len + 2; hdr_entry_t *e = NULL; if (!l->inline_used && required <= sizeof(l->inline_entry.bytes)) { e = (hdr_entry_t *)l->inline_entry.bytes; l->inline_used = true; } else { e = malloc(required); if (!e) return false; }
e->name = e->storage; e->value = e->storage + name_len + 1; e->next = NULL; for (size_t i = 0; i < name_len; i++) { unsigned char c = (unsigned char)name[i]; e->name[i] = lowercase_name ? (char)ant_ascii_to_lower(c) : (char)c; } e->name[name_len] = '\0'; memcpy(e->value, value, value_len); e->value[value_len] = '\0';
*l->tail = e; l->tail = &e->next; l->count++; return true;}
static bool list_append_raw(hdr_list_t *l, const char *lower_name, const char *value) { if (!lower_name || !value) return false; return list_append_parts( l, lower_name, strlen(lower_name), value, strlen(value), false);}
headers_data_t *headers_data_copy(const headers_data_t *src) { headers_data_t *dst = NULL; if (!src) return NULL;
dst = headers_data_create(); if (!dst) return NULL; for (const hdr_entry_t *e = src->head; e; e = e->next) { if (!list_append_raw(dst, e->name, e->value)) { headers_data_destroy(dst); return NULL; } } return dst;}
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; if ((void *)dead == (void *)l->inline_entry.bytes) l->inline_used = false; else 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_bytes_n( ant_t *js, hdr_list_t *l, const char *name, size_t name_len, const char *value, size_t value_len) { const char *value_start = value ? value : "";
if (!is_valid_name_n(name, name_len)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
while (value_len > 0 && (*value_start == ' ' || *value_start == '\t')) { value_start++; value_len--; } while (value_len > 0 && (value_start[value_len - 1] == ' ' || value_start[value_len - 1] == '\t')) value_len--; if (!is_valid_value_n(value_start, value_len)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value");
if (!list_append_parts(l, name, name_len, value_start, value_len, true)) return js_mkerr(js, "out of memory"); return js_mkundef();}
static ant_value_t header_value_from_js( ant_t *js, const char *utf8, size_t utf8_len, const char **out, size_t *out_len, uint8_t **owned) { *out = utf8 ? utf8 : ""; *out_len = utf8 ? utf8_len : 0; *owned = NULL;
bool has_non_ascii = false; for (size_t i = 0; i < utf8_len; i++) { unsigned char c = (unsigned char)utf8[i]; if (c >= 0x80) has_non_ascii = true; else if (c == 0 || c == '\r' || c == '\n') return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); } if (!utf8 || !has_non_ascii) return js_mkundef();
bool is_latin1 = true; uint8_t *bytes = utf8_to_latin1(utf8, utf8_len, out_len, &is_latin1); if (!bytes) { if (!is_latin1) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header value"); return js_mkerr(js, "out of memory"); }
*out = (const char *)bytes; *owned = bytes; return js_mkundef();}
static ant_value_t header_value_to_js(ant_t *js, const char *bytes, size_t len) { bool ascii = true; for (size_t i = 0; i < len; i++) { if ((unsigned char)bytes[i] >= 0x80) { ascii = false; break; } } if (ascii) return js_mkstr(js, bytes, len);
size_t utf8_len = 0; char *utf8 = latin1_to_utf8((const uint8_t *)bytes, len, &utf8_len); if (!utf8) return js_mkerr(js, "out of memory"); ant_value_t result = js_mkstr(js, utf8, utf8_len); free(utf8); return result;}
static ant_value_t headers_append_name_value_js_n( ant_t *js, hdr_list_t *l, const char *name, size_t name_len, const char *value, size_t value_len) { const char *bytes = NULL; size_t bytes_len = 0; uint8_t *owned = NULL; ant_value_t converted = header_value_from_js( js, value, value_len, &bytes, &bytes_len, &owned); if (is_err(converted)) return converted;
ant_value_t result = headers_append_name_value_bytes_n( js, l, name, name_len, bytes, bytes_len); free(owned); return result;}
static ant_value_t headers_append_name_value( ant_t *js, hdr_list_t *l, const char *name, const char *value) { return headers_append_name_value_bytes_n( js, l, name, name ? strlen(name) : 0, value, value ? strlen(value) : 0);}
static ant_value_t headers_append_record_value( ant_t *js, hdr_list_t *l, const char *name, size_t name_len, ant_value_t value_v) { if (vtype(value_v) != kTypeString) { value_v = js_tostring_val(js, value_v); if (is_err(value_v)) return value_v; }
size_t value_len = 0; const char *value = js_getstr(js, value_v, &value_len); return headers_append_name_value_js_n(js, l, name, name_len, value, value_len);}
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; size_t name_len = 0; size_t value_len = 0; if (vtype(name_v) != kTypeString) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } if (vtype(value_v) != kTypeString) { value_v = js_tostring_val(js, value_v); if (is_err(value_v)) return value_v; }
name = js_getstr(js, name_v, &name_len); value = js_getstr(js, value_v, &value_len); return headers_append_name_value_js_n(js, l, name, name_len, value, value_len);}
ant_value_t headers_append_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value) { hdr_list_t *l = headers_get_data(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; return js_mkundef();}
static ant_value_t init_from_sequence(ant_t *js, hdr_list_t *l, ant_value_t seq) { iterator_t it;
if (!js_iter_open(js, seq, &it)) return Ant_Exception_Pending(js) ? Ant_Exception_Current(js) : 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 != kTypeArray && pt != kTypeObject) { 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 Ant_Exception_Pending(js) ? Ant_Exception_Current(js) : js_mkundef();}
static ant_value_t init_from_record(ant_t *js, hdr_list_t *l, ant_value_t obj) { ant_object_t *ptr = vtype(obj) == kTypeObject ? js_obj_ptr(obj) : NULL; const ant_shape_prop_t *only_prop = ptr && ptr->shape && ptr->prop_count == 1 && ant_shape_count(ptr->shape) == 1 ? ant_shape_prop_at(ptr->shape, 0) : NULL; if (ptr && !ptr->flags.is_exotic && only_prop && only_prop->type == ANT_SHAPE_KEY_STRING && !only_prop->has_getter && !only_prop->has_setter) { ant_value_t value = ant_object_prop_get_unchecked(ptr, 0); if (vtype(value) == kTypeString) { return headers_append_record_value( js, l, only_prop->key.interned, intern_length(only_prop->key.interned), value); } }
ant_iter_t it = js_prop_iter_begin(js, obj); ant_iter_key_t key = {0}; ant_value_t iter_value = js_mkundef();
while (js_prop_iter_next_key(&it, &key, &iter_value)) { if (key.is_symbol) continue;
const ant_shape_prop_t *prop = it.obj && it.obj->shape ? ant_shape_prop_at(it.obj->shape, key.slot) : NULL; bool direct_string = it.obj && !it.obj->flags.is_exotic && prop && !prop->has_getter && !prop->has_setter && vtype(iter_value) == kTypeString; ant_value_t val = direct_string ? iter_value : js_get(js, obj, key.str); if (is_err(val)) { js_prop_iter_end(&it); return val; }
ant_value_t r = headers_append_record_value( js, l, key.str, 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, iterator_t *it, ant_value_t *out) { hdr_iter_t *st = (hdr_iter_t *)js_get_native(it->iterator, HEADERS_ITER_NATIVE_TAG); if (!st) return false;
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 = header_value_to_js(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, header_value_to_js(js, e->value, strlen(e->value))); break; }}
free_sorted_view(view, count); st->index++; return true;}
static ant_value_t headers_iter_next(ant_params_t) { return js_iter_next_result(js, advance_headers);}
static ant_value_t make_headers_iter(ant_t *js, ant_value_t headers_obj, int kind) { hdr_list_t *l = headers_get_data(headers_obj); if (!l) return js_mkerr(js, "Invalid Headers object");
hdr_iter_t *st = calloc(1, sizeof(hdr_iter_t)); if (!st) return js_mkerr(js, "out of memory"); st->list = l; st->kind = kind;
ant_value_t iter = Ant_Object_CreateBare(js); js_set_proto_init(iter, js->builtins.headers_iter_proto); js_set_native(iter, st, HEADERS_ITER_NATIVE_TAG); js_set_finalizer(iter, headers_iter_finalize); js_set_slot_wb(js, iter, SLOT_AUX, headers_obj); return iter;}
static ant_value_t js_headers_append(ant_params_t) { if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.append requires 2 arguments"); hdr_list_t *l = headers_get_data(js->this_val); if (!l) return js_mkerr(js, "Invalid Headers object"); ant_value_t guard_err = headers_require_mutable(js, 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; return js_mkundef();}
static ant_value_t js_headers_set(ant_params_t) { if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.set requires 2 arguments"); hdr_list_t *l = headers_get_data(js->this_val); if (!l) return js_mkerr(js, "Invalid Headers object"); ant_value_t guard_err = headers_require_mutable(js, 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) != kTypeString) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } if (vtype(value_v) != kTypeString) { value_v = js_tostring_val(js, value_v); if (is_err(value_v)) return value_v; }
size_t name_len = 0; size_t value_len = 0; const char *name = js_getstr(js, name_v, &name_len); const char *value = js_getstr(js, value_v, &value_len);
const char *value_bytes = NULL; size_t value_bytes_len = 0; uint8_t *owned_value = NULL; ant_value_t converted = header_value_from_js( js, value, value_len, &value_bytes, &value_bytes_len, &owned_value); if (is_err(converted)) return converted;
if (!is_valid_name_n(name, name_len)) { free(owned_value); return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); } while (value_bytes_len > 0 && (*value_bytes == ' ' || *value_bytes == '\t')) { value_bytes++; value_bytes_len--; } while (value_bytes_len > 0 && (value_bytes[value_bytes_len - 1] == ' ' || value_bytes[value_bytes_len - 1] == '\t')) value_bytes_len--;
char *owned_lower = NULL; const char *lower = lowercase_name_view(name, name_len, &owned_lower); if (!lower) { free(owned_value); return js_mkerr(js, "out of memory"); }
list_delete_name(l, lower); bool appended = list_append_parts( l, lower, name_len, value_bytes, value_bytes_len, false);
free(owned_lower); free(owned_value); if (!appended) return js_mkerr(js, "out of memory"); return js_mkundef();}
static ant_value_t js_headers_get(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.get requires 1 argument"); hdr_list_t *l = headers_get_data(js->this_val); if (!l) return js_mknull();
ant_value_t name_v = args[0]; if (vtype(name_v) != kTypeString) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } size_t name_len = 0; const char *name = js_getstr(js, name_v, &name_len); if (!is_valid_name_n(name, name_len)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *owned_lower = NULL; const char *lower = lowercase_name_view(name, name_len, &owned_lower); 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 = header_value_to_js(js, e->value, strlen(e->value)); free(owned_lower); return ret; } } free(owned_lower); return js_mknull(); }
size_t total = 0; int count = 0; const char *first_value = NULL; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { if (count == 0) first_value = e->value; if (count > 0) total += 2; total += strlen(e->value); count++; } }
if (count == 0) { free(owned_lower); return js_mknull(); } if (count == 1) { ant_value_t ret = header_value_to_js(js, first_value, total); free(owned_lower); return ret; }
char *combined = malloc(total + 1); if (!combined) { free(owned_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(owned_lower);
ant_value_t ret = header_value_to_js(js, combined, pos); free(combined); return ret;}
static ant_value_t js_headers_has(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.has requires 1 argument"); hdr_list_t *l = headers_get_data(js->this_val); if (!l) return js_false;
ant_value_t name_v = args[0]; if (vtype(name_v) != kTypeString) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } size_t name_len = 0; const char *name = js_getstr(js, name_v, &name_len); if (!is_valid_name_n(name, name_len)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *owned_lower = NULL; const char *lower = lowercase_name_view(name, name_len, &owned_lower); 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(owned_lower); return js_bool(found);}
static ant_value_t js_headers_delete(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.delete requires 1 argument"); hdr_list_t *l = headers_get_data(js->this_val); if (!l) return js_mkundef(); ant_value_t guard_err = headers_require_mutable(js, js->this_val); if (is_err(guard_err)) return guard_err;
ant_value_t name_v = args[0]; if (vtype(name_v) != kTypeString) { name_v = js_tostring_val(js, name_v); if (is_err(name_v)) return name_v; } size_t name_len = 0; const char *name = js_getstr(js, name_v, &name_len); if (!is_valid_name_n(name, name_len)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *owned_lower = NULL; const char *lower = lowercase_name_view(name, name_len, &owned_lower); if (!lower) return js_mkerr(js, "out of memory"); list_delete_name(l, lower); free(owned_lower); return js_mkundef();}
static ant_value_t js_headers_get_set_cookie(ant_params_t) { hdr_list_t *l = headers_get_data(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, header_value_to_js(js, e->value, strlen(e->value))); } return arr;}
static ant_value_t js_headers_for_each(ant_params_t) { 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 != kTypeFunction && cbt != kTypeBuiltin) 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 = headers_get_data(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] = { header_value_to_js(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, js_mkundef()); 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_params_t) { return make_headers_iter(js, js->this_val, ITER_KEYS);}
static ant_value_t js_headers_values(ant_params_t) { return make_headers_iter(js, js->this_val, ITER_VALUES);}
static ant_value_t js_headers_entries(ant_params_t) { 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, js->sym.toStringTag_sym); const char *tag = vtype(tag_val) == kTypeString ? 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_params_t) { ant_value_t this_obj = js_getthis(js); hdr_list_t *list = headers_get_data(this_obj); ant_value_t out = Ant_Object_CreateBare(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); ant_value_t value_js = header_value_to_js(js, e->value, strlen(e->value)); if (is_err(value_js)) return value_js; if (vtype(existing) == kTypeUndefined) { js_set(js, out, e->name, value_js); continue; } size_t existing_len = 0; size_t value_len = 0; const char *existing_str = js_getstr(js, existing, &existing_len); const char *value_str = js_getstr(js, value_js, &value_len); 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, value_str, 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_params_t) { if (vtype(call_new_target) == kTypeUndefined) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers constructor requires 'new'");
hdr_list_t *l = headers_data_create(); if (!l) return js_mkerr(js, "out of memory");
ant_value_t init = (nargs >= 1) ? args[0] : js_mkundef();
if (vtype(init) != kTypeUndefined) { uint8_t t = vtype(init);
if (t == kTypeNull || (t != kTypeObject && t != kTypeArray && t != kTypeFunction && t != kTypeBuiltin)) { headers_data_destroy(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, js->sym.iterator_sym); if (is_err(iter_fn)) { headers_data_destroy(l); return iter_fn; } bool has_iter = (vtype(iter_fn) == kTypeFunction || vtype(iter_fn) == kTypeBuiltin);
ant_value_t r; if (t == kTypeArray || has_iter) r = init_from_sequence(js, l, init); else r = init_from_record(js, l, init); if (is_err(r)) { headers_data_destroy(l); return r; } }
ant_value_t obj = Ant_Object_CreateBare(js); ant_value_t proto = js_instance_proto_from_new_target(js, js->builtins.headers_proto, call_new_target); if (is_object_type(proto)) js_set_proto_init(obj, proto);
js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); js_set_native(obj, l, HEADERS_NATIVE_TAG); js_set_finalizer(obj, headers_finalize); return obj;}
ant_value_t headers_create_empty(ant_t *js) { hdr_list_t *l = headers_data_create(); if (!l) return js_mkerr(js, "out of memory");
return headers_create_from_data(js, l);}
ant_value_t headers_create_from_data(ant_t *js, headers_data_t *data) { if (!data) return js_mkerr(js, "out of memory");
ant_value_t obj = Ant_Object_CreateBare(js); js_reserve_slots(obj, 2); js_set_proto_init(obj, js->builtins.headers_proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); js_set_native(obj, data, HEADERS_NATIVE_TAG); js_set_finalizer(obj, headers_finalize);
return obj;}
bool headers_copy_from(ant_value_t dst, ant_value_t src) { hdr_list_t *src_list = headers_get_data(src); hdr_list_t *dst_list = headers_get_data(dst); if (!dst_list) return false; if (!src_list) return true; for (hdr_entry_t *e = src_list->head; e; e = e->next) if (!list_append_raw(dst_list, e->name, e->value)) return false; return true;}
size_t headers_data_find_literal( const headers_data_t *data, const char *lower_name, const char **first_value) { size_t count = 0;
if (first_value) *first_value = NULL; if (!data || !lower_name) return 0;
for (const hdr_entry_t *e = data->head; e; e = e->next) { if (strcmp(e->name, lower_name) != 0) continue; if (count == 0 && first_value) *first_value = e->value; count++; }
return count;}
void headers_set_immutable(ant_value_t hdrs, bool immutable) { js_set_slot(hdrs, SLOT_HEADERS_GUARD, js_bool(immutable));}
bool headers_data_append_if_missing( headers_data_t *data, const char *name, const char *value) { if (!data || !name || !value) return false; for (hdr_entry_t *e = data->head; e; e = e->next) { if (ascii_case_equal(e->name, name)) return true; } return list_append_parts( data, name, strlen(name), value, strlen(value), true);}
void headers_data_for_each( const headers_data_t *data, headers_foreach_cb cb, void *ctx) { if (!data || !cb) return; for (const hdr_entry_t *e = data->head; e; e = e->next) cb(e->name, e->value, ctx);}
bool headers_set_literal(ant_value_t hdrs, const char *name, const char *value) { hdr_list_t *l = headers_get_data(hdrs);
if (!l || headers_is_immutable(hdrs)) return false; return headers_data_set_literal(l, name, value);}
bool headers_data_set_literal( headers_data_t *data, const char *name, const char *value) { char *owned_lower = NULL; const char *lower = NULL; size_t name_len = name ? strlen(name) : 0; const char *value_start = value; size_t value_len = value ? strlen(value) : 0;
if (!data || !name || !value) return false; if (!is_valid_name_n(name, name_len)) return false; while (value_len > 0 && (*value_start == ' ' || *value_start == '\t')) { value_start++; value_len--; } while (value_len > 0 && (value_start[value_len - 1] == ' ' || value_start[value_len - 1] == '\t')) value_len--; if (!is_valid_value_n(value_start, value_len)) return false;
lower = lowercase_name_view(name, name_len, &owned_lower); if (!lower) return false;
list_delete_name(data, lower); bool appended = list_append_parts( data, lower, name_len, value_start, value_len, false); free(owned_lower); return appended;}
ant_value_t headers_data_init_from(ant_t *js, headers_data_t *data, ant_value_t init) { uint8_t ht = vtype(init);
if (!data) return js_mkerr(js, "Invalid Headers object"); if (ht == kTypeUndefined) return js_mkundef();
if (headers_is_headers(init)) { hdr_list_t *src = headers_get_data(init); if (src) for (hdr_entry_t *e = src->head; e; e = e->next) if (!list_append_raw(data, e->name, e->value)) return js_mkerr(js, "out of memory"); return js_mkundef(); }
if (ht == kTypeArray) { 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_pair(js, data, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); if (is_err(r)) return r; } return js_mkundef(); }
if (ht == kTypeObject) return init_from_record(js, data, init);
return js_mkundef();}
ant_value_t headers_create_from_init(ant_t *js, ant_value_t init) { ant_value_t new_hdrs = headers_create_empty(js); ant_value_t step = 0;
if (is_err(new_hdrs)) return new_hdrs; step = headers_data_init_from(js, headers_get_data(new_hdrs), init); if (is_err(step)) return step; return new_hdrs;}
ant_value_t headers_get_value(ant_t *js, ant_value_t hdrs, const char *name) { hdr_list_t *l = headers_get_data(hdrs); size_t name_len = name ? strlen(name) : 0; if (!l) return js_mknull(); if (!is_valid_name_n(name, name_len)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name");
char *owned_lower = NULL; const char *lower = lowercase_name_view(name, name_len, &owned_lower); 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 = header_value_to_js(js, e->value, strlen(e->value)); free(owned_lower); return ret; }} free(owned_lower); return js_mknull(); }
size_t total = 0; int count = 0; const char *first_value = NULL; for (hdr_entry_t *e = l->head; e; e = e->next) { if (strcmp(e->name, lower) == 0) { if (count == 0) first_value = e->value; if (count > 0) total += 2; total += strlen(e->value); count++; }}
if (count == 0) { free(owned_lower); return js_mknull(); } if (count == 1) { ant_value_t ret = header_value_to_js(js, first_value, total); free(owned_lower); return ret; }
char *combined = malloc(total + 1); if (!combined) { free(owned_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(owned_lower);
ant_value_t ret = header_value_to_js(js, combined, pos); free(combined); return ret;}
void init_headers_module(ant_t *js) { js->builtins.headers_iter_proto = Ant_Object_CreateBare(js); js_set_proto_init(js->builtins.headers_iter_proto, js->sym.iterator_proto); js_set(js, js->builtins.headers_iter_proto, "next", Ant_Function_Create(headers_iter_next)); js_set_descriptor(js, js->builtins.headers_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); js_set_sym(js, js->builtins.headers_iter_proto, js->sym.iterator_sym, Ant_Function_Create(sym_this_cb)); js_iter_register_advance(js, js->builtins.headers_iter_proto, advance_headers);
js->builtins.headers_proto = Ant_Object_CreateBare(js);
js_set(js, js->builtins.headers_proto, "append", Ant_Function_CreateWithArity(js_headers_append, 2)); js_set(js, js->builtins.headers_proto, "set", Ant_Function_CreateWithArity(js_headers_set, 2)); js_set(js, js->builtins.headers_proto, "get", Ant_Function_CreateWithArity(js_headers_get, 1)); js_set(js, js->builtins.headers_proto, "has", Ant_Function_CreateWithArity(js_headers_has, 1)); js_set(js, js->builtins.headers_proto, "delete", Ant_Function_CreateWithArity(js_headers_delete, 1)); js_set(js, js->builtins.headers_proto, "forEach", Ant_Function_CreateWithArity(js_headers_for_each, 1)); js_set(js, js->builtins.headers_proto, "keys", Ant_Function_Create(js_headers_keys)); js_set(js, js->builtins.headers_proto, "values", Ant_Function_Create(js_headers_values)); js_set(js, js->builtins.headers_proto, "entries", Ant_Function_Create(js_headers_entries)); js_set(js, js->builtins.headers_proto, "getSetCookie", Ant_Function_Create(js_headers_get_set_cookie)); js_set_sym(js, js->builtins.headers_proto, js->sym.iterator_sym, js_get(js, js->builtins.headers_proto, "entries")); js_set_sym(js, js->builtins.headers_proto, js->sym.inspect_sym, Ant_Function_Create(headers_inspect)); js_set_tostringtag(js, js->builtins.headers_proto, js_mkstr(js, "Headers", 7));
js_set_global_builtin(js, "Headers", Ant_Function_CreateIntrinsicConstructor(js, js_headers_ctor, js->builtins.headers_proto, "Headers", 0));}