Page MenuHomePhorge

headers.c
No OneTemporary

Size
30 KB
Referenced Files
None
Subscribers
None

headers.c

#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 js_headers_symbol_iterator(ant_t *js, ant_value_t *args, int nargs) {
return make_headers_iter(js, js->this_val, ITER_ENTRIES);
}
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_mkfun(js_headers_symbol_iterator));
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);
}

File Metadata

Mime Type
text/x-c
Expires
Fri, Apr 3, 4:18 PM (1 d, 23 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
521444
Default Alt Text
headers.c (30 KB)

Event Timeline