Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556#include <stdlib.h>#include <string.h>#include <strings.h>#include <ctype.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 "common.h"#include "descriptors.h"
#include "modules/blob.h"#include "modules/buffer.h"#include "modules/abort.h"#include "modules/formdata.h"#include "modules/headers.h"#include "modules/multipart.h"#include "modules/request.h"#include "modules/url.h"#include "modules/json.h"#include "streams/pipes.h"#include "streams/readable.h"
static constexpr int REQUEST_RESERVED_SLOTS = 7;static constexpr uint32_t REQUEST_NATIVE_TAG = Ant_NativeTag("REQS");
request_data_t *request_get_data(ant_value_t obj) { return (request_data_t *)js_get_native(obj, REQUEST_NATIVE_TAG);}
ant_value_t request_get_headers(ant_value_t obj) { return js_get_slot(obj, SLOT_REQUEST_HEADERS);}
ant_value_t request_get_signal(ant_t *js, ant_value_t obj) { ant_value_t signal = js_get_slot(obj, SLOT_REQUEST_SIGNAL);
if (vtype(signal) != kTypeUndefined) return signal; signal = abort_signal_create_dependent(js, js_mkundef()); if (is_err(signal)) return signal; ant_value_t abort_reason = js_get_slot(obj, SLOT_REQUEST_ABORT_REASON); if (vtype(abort_reason) != kTypeUndefined) signal_do_abort(js, signal, abort_reason); js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); return signal;}
static void data_free(request_data_t *d) { if (!d) return; url_state_clear(&d->url); if (!d->shared_defaults) { free(d->method); free(d->referrer); free(d->referrer_policy); free(d->mode); free(d->credentials); free(d->cache); free(d->redirect); free(d->integrity); } free(d->body_data); free(d->body_type); free(d);}
static void request_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); request_data_t *data = request_get_data(value); data_free(data); js_clear_native(value, REQUEST_NATIVE_TAG);}
static void request_clear_and_free(ant_value_t obj, request_data_t *data) { if (obj) js_clear_native(obj, REQUEST_NATIVE_TAG); data_free(data);}
static request_data_t *data_new(void) { request_data_t *d = calloc(1, sizeof(request_data_t)); if (!d) return NULL;
d->method = strdup("GET"); d->referrer = strdup("client"); d->referrer_policy = strdup(""); d->mode = strdup("cors"); d->credentials = strdup("same-origin"); d->cache = strdup("default"); d->redirect = strdup("follow"); d->integrity = strdup(""); if (!d->method || !d->referrer || !d->referrer_policy || !d->mode || !d->credentials || !d->cache || !d->redirect || !d->integrity ) { data_free(d); return NULL; }
return d;}
static request_data_t *data_new_server(const char *method) { request_data_t *d = calloc(1, sizeof(request_data_t)); if (!d) return NULL;
d->method = (char *)(method ? method : "GET"); d->referrer = (char *)"client"; d->referrer_policy = (char *)""; d->mode = (char *)"same-origin"; d->credentials = (char *)"same-origin"; d->cache = (char *)"default"; d->redirect = (char *)"follow"; d->integrity = (char *)""; d->shared_defaults = true; return d;}
static ant_value_t request_create_object(ant_t *js, request_data_t *req, ant_value_t headers_obj, bool create_signal) { ant_value_t obj = Ant_Object_CreateBare(js); (void)js_reserve_slots(obj, REQUEST_RESERVED_SLOTS); ant_value_t hdrs = is_object_type(headers_obj) ? headers_obj : headers_create_empty(js);
if (is_err(hdrs)) { data_free(req); return hdrs; }
js_set_proto_init(obj, js->builtins.request_proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); js_set_native(obj, req, REQUEST_NATIVE_TAG); js_set_finalizer(obj, request_finalize);
js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, hdrs); js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef()); js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, create_signal ? abort_signal_create_dependent(js, js_mkundef()) : js_mkundef() ); return obj;}
static char *request_build_server_base_url(const char *host, const char *server_hostname, int server_port) { const char *authority = (host && host[0]) ? host : server_hostname; size_t authority_len = authority ? strlen(authority) : 0; bool include_port = (!host || !host[0]) && server_port > 0 && server_port != 80; size_t cap = sizeof("http://") - 1 + authority_len + 1 + 16 + 1; char *base = malloc(cap); int written = 0;
if (!base) return NULL; if (include_port) written = snprintf(base, cap, "http://%s:%d/", authority ? authority : "", server_port); else written = snprintf(base, cap, "http://%s/", authority ? authority : ""); if (written < 0 || (size_t)written >= cap) { free(base); return NULL; }
return base;}
static int request_build_canonical_server_root_url( const char *host, url_state_t *out) { static const char prefix[] = "http://"; size_t host_len = strlen(host); size_t href_len = (sizeof(prefix) - 1) + host_len + 1;
out->href = malloc(href_len + 1); if (!out->href) { url_state_clear(out); return -1; }
memcpy(out->href, prefix, sizeof(prefix) - 1); memcpy(out->href + sizeof(prefix) - 1, host, host_len); out->href[href_len - 1] = '/'; out->href[href_len] = '\0'; return 0;}
static int request_parse_server_url( const char *target, bool absolute_target, const char *host, bool canonical_host, const char *server_hostname, int server_port, url_state_t *out) { char *base = NULL; char *absolute = NULL; int rc = 0;
if (absolute_target) return parse_url_to_state(target, NULL, out);
if (target[0] == '/' && target[1] == '\0' && canonical_host) return request_build_canonical_server_root_url(host, out);
if (host && host[0] && target[0] == '/' && target[1] != '/') { static const char prefix[] = "http://"; size_t host_len = strlen(host); size_t target_len = strlen(target); if (host_len > SIZE_MAX - (sizeof(prefix) - 1) - target_len - 1) return -1;
size_t absolute_len = (sizeof(prefix) - 1) + host_len + target_len; absolute = malloc(absolute_len + 1); if (!absolute) return -1; memcpy(absolute, prefix, sizeof(prefix) - 1); memcpy(absolute + sizeof(prefix) - 1, host, host_len); memcpy(absolute + sizeof(prefix) - 1 + host_len, target, target_len + 1); rc = parse_url_to_state(absolute, NULL, out); free(absolute); return rc; }
base = request_build_server_base_url(host, server_hostname, server_port); if (!base) return -1; rc = parse_url_to_state(target, base, out); free(base); return rc;}
static request_data_t *data_dup(const request_data_t *src) { request_data_t *d = calloc(1, sizeof(request_data_t)); if (!d) return NULL;
#define DUP_STR(f) do { d->f = src->f ? strdup(src->f) : NULL; } while(0) DUP_STR(method); DUP_STR(referrer); DUP_STR(referrer_policy); DUP_STR(mode); DUP_STR(credentials); DUP_STR(cache); DUP_STR(redirect); DUP_STR(integrity); DUP_STR(body_type);#undef DUP_STR url_state_t *su = (url_state_t *)&src->url; url_state_t *du = &d->url;#define DUP_US(f) do { du->f = su->f ? strdup(su->f) : NULL; } while(0) DUP_US(href); DUP_US(protocol); DUP_US(username); DUP_US(password); DUP_US(hostname); DUP_US(port); DUP_US(pathname); DUP_US(search); DUP_US(hash);#undef DUP_US d->keepalive = src->keepalive; d->reload_navigation = src->reload_navigation; d->history_navigation = src->history_navigation; d->has_body = src->has_body; d->body_is_stream = src->body_is_stream; d->body_used = src->body_used; d->body_size = src->body_size;
if (src->body_data && src->body_size > 0) { d->body_data = malloc(src->body_size); if (!d->body_data) { data_free(d); return NULL; } memcpy(d->body_data, src->body_data, src->body_size); }
return d;}
static bool is_valid_method(const char *m) { if (!m || !*m) return false; for (const unsigned char *p = (const unsigned char *)m; *p; p++) if (!ant_ascii_is_tchar(*p)) return false; return true;}
static bool is_forbidden_method(const char *m) { return strcasecmp(m, "CONNECT") == 0 || strcasecmp(m, "TRACE") == 0 || strcasecmp(m, "TRACK") == 0;}
static bool is_cors_safelisted_method(const char *m) { return strcasecmp(m, "GET") == 0 || strcasecmp(m, "HEAD") == 0 || strcasecmp(m, "POST") == 0;}
static void normalize_method(char *m) { static const char *norm[] = { "DELETE","GET","HEAD", "OPTIONS","POST","PUT" }; for (int i = 0; i < 6; i++) if (strcasecmp(m, norm[i]) == 0) { strcpy(m, norm[i]); return; }}
static const char *request_effective_body_type(ant_t *js, ant_value_t req_obj, request_data_t *d) { ant_value_t headers = js_get_slot(req_obj, SLOT_REQUEST_HEADERS); if (!headers_is_headers(headers)) return d ? d->body_type : NULL; ant_value_t ct = headers_get_value(js, headers, "content-type"); if (vtype(ct) == kTypeString) return js_getstr(js, ct, NULL); return d ? d->body_type : NULL;}
static bool copy_body_bytes( ant_t *js, const uint8_t *src, size_t src_len, uint8_t **out_data, size_t *out_size, ant_value_t *err_out) { uint8_t *buf = NULL;
*out_data = NULL; *out_size = 0; if (src_len == 0) return true;
buf = malloc(src_len); if (!buf) { *err_out = js_mkerr(js, "out of memory"); return false; }
memcpy(buf, src, src_len); *out_data = buf; *out_size = src_len; return true;}
static bool extract_buffer_source_body( ant_t *js, ant_value_t body_val, uint8_t **out_data, size_t *out_size, ant_value_t *err_out) { const uint8_t *src = NULL; size_t src_len = 0;
if (!(( vtype(body_val) == kTypeTypedArray || vtype(body_val) == kTypeObject) && buffer_source_get_bytes(js, body_val, &src, &src_len)) ) return false;
return copy_body_bytes(js, src, src_len, out_data, out_size, err_out);}
static bool extract_stream_body( ant_t *js, ant_value_t body_val, ant_value_t *out_stream, ant_value_t *err_out) { if (!rs_is_stream(body_val)) return false; if (rs_stream_unusable(body_val)) { *err_out = js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); return false; }
*out_stream = body_val; return true;}
static bool extract_blob_body( ant_t *js, ant_value_t body_val, uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out) { blob_data_t *bd = blob_is_blob(js, body_val) ? blob_get_data(body_val) : NULL; if (!bd) return false;
if (!copy_body_bytes(js, bd->data, bd->size, out_data, out_size, err_out)) return false; if (bd->type && bd->type[0]) *out_type = strdup(bd->type); return true;}
static bool extract_urlsearchparams_body( ant_t *js, ant_value_t body_val, uint8_t **out_data, size_t *out_size, char **out_type) { char *serialized = NULL; if (!usp_is_urlsearchparams(js, body_val)) return false;
serialized = usp_serialize(js, body_val); if (serialized) { *out_data = (uint8_t *)serialized; *out_size = strlen(serialized); *out_type = strdup("application/x-www-form-urlencoded;charset=UTF-8"); } return true;}
static bool extract_formdata_body( ant_t *js, ant_value_t body_val, uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out) { char *boundary = NULL; char *content_type = NULL; size_t mp_size = 0; uint8_t *mp = NULL;
if (!formdata_is_formdata(js, body_val)) return false; mp = formdata_serialize_multipart(js, body_val, &mp_size, &boundary); if (!mp) { *err_out = js_mkerr(js, "out of memory"); return false; }
if (mp_size > 0) *out_data = mp; else free(mp);
*out_size = mp_size; if (boundary) { size_t ct_len = snprintf(NULL, 0, "multipart/form-data; boundary=%s", boundary); content_type = malloc(ct_len + 1); if (!content_type) { free(boundary); if (mp_size > 0) free(mp); *out_data = NULL; *out_size = 0; *err_out = js_mkerr(js, "out of memory"); return false; } snprintf(content_type, ct_len + 1, "multipart/form-data; boundary=%s", boundary); free(boundary); *out_type = content_type; }
return true;}
static bool extract_string_body( ant_t *js, ant_value_t body_val, uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out) { size_t len = 0; const char *s = NULL;
if (vtype(body_val) != kTypeString) { body_val = js_tostring_val(js, body_val); if (is_err(body_val)) { *err_out = body_val; return false; }}
s = js_getstr(js, body_val, &len); if (!copy_body_bytes(js, (const uint8_t *)s, len, out_data, out_size, err_out)) return false; *out_type = strdup("text/plain;charset=UTF-8"); return true;}
static bool extract_body( ant_t *js, ant_value_t body_val, uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *out_stream, ant_value_t *err_out) { *out_data = NULL; *out_size = 0; *out_type = NULL; *out_stream = js_mkundef(); *err_out = js_mkundef();
if (vtype(body_val) == kTypeNull || vtype(body_val) == kTypeUndefined) return true; if (extract_buffer_source_body(js, body_val, out_data, out_size, err_out)) return true; if (vtype(body_val) == kTypeObject && rs_is_stream(body_val)) return extract_stream_body(js, body_val, out_stream, err_out); if (vtype(body_val) == kTypeObject && extract_blob_body(js, body_val, out_data, out_size, out_type, err_out)) return true; if (vtype(body_val) == kTypeObject && extract_urlsearchparams_body(js, body_val, out_data, out_size, out_type)) return true; if (vtype(body_val) == kTypeObject && extract_formdata_body(js, body_val, out_data, out_size, out_type, err_out)) return true; return extract_string_body(js, body_val, out_data, out_size, out_type, err_out);}
enum { BODY_TEXT = 0, BODY_JSON, BODY_ARRAYBUFFER, BODY_BLOB, BODY_BYTES, BODY_FORMDATA};
static void resolve_body_promise( ant_t *js, ant_value_t promise, const uint8_t *data, size_t size, const char *body_type, int mode, bool has_body) { switch (mode) { case BODY_TEXT: { ant_value_t str = (data && size > 0) ? js_mkstr(js, (const char *)data, size) : js_mkstr(js, "", 0); js_resolve_promise(js, promise, str); break; } case BODY_JSON: { ant_value_t str = (data && size > 0) ? js_mkstr(js, (const char *)data, size) : js_mkstr(js, "", 0); ant_value_t parsed = json_parse_value(js, str); if (is_err(parsed)) js_reject_promise(js, promise, parsed); else js_resolve_promise(js, promise, parsed); break; } case BODY_ARRAYBUFFER: { ant_value_t ab = create_arraybuffer_copy(js, data, size); if (is_err(ab)) js_reject_promise(js, promise, ab); else js_resolve_promise(js, promise, ab); break; } case BODY_BLOB: { const char *type = body_type ? body_type : ""; ant_value_t blob = blob_create(js, data, size, type); if (is_err(blob)) js_reject_promise(js, promise, blob); else js_resolve_promise(js, promise, blob); break; } case BODY_BYTES: { ArrayBufferData *ab = create_array_buffer_data(js, size); if (!ab) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); break; } if (data && size > 0) memcpy(ab->data, data, size); ant_value_t bytes = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, size, js->builtins.typedarray_protos[TYPED_ARRAY_UINT8]); if (is_err(bytes)) js_reject_promise(js, promise, bytes); else js_resolve_promise(js, promise, bytes); break; } case BODY_FORMDATA: { ant_value_t fd = formdata_parse_body(js, data, size, body_type, has_body); if (is_err(fd)) js_reject_promise(js, promise, fd); else js_resolve_promise(js, promise, fd); break; }}}
static bool chunk_bytes(ant_t *js, ant_value_t chunk, uint8_t **bytes, size_t *len) { if (vtype(chunk) != kTypeTypedArray) return false; return Ant_Buffer_GetViewBytes((TypedArrayData *)js_gettypedarray(chunk), bytes, len);}
static uint8_t *concat_chunks(ant_t *js, ant_value_t chunks, size_t *out_size) { ant_offset_t n = js_arr_len(js, chunks); uint8_t *bytes; size_t len, total = 0;
for (ant_offset_t i = 0; i < n; i++) if (chunk_bytes(js, js_arr_get(js, chunks, i), &bytes, &len)) total += len;
uint8_t *buf = total > 0 ? malloc(total) : NULL; if (total > 0 && !buf) return NULL;
size_t pos = 0; for (ant_offset_t i = 0; i < n; i++) { if (!chunk_bytes(js, js_arr_get(js, chunks, i), &bytes, &len) || len == 0) continue; memcpy(buf + pos, bytes, len); pos += len; }
*out_size = pos; return buf;}
static ant_value_t stream_body_read(ant_params_t);
static ant_value_t stream_body_rejected(ant_params_t) { ant_value_t state = js_get_slot(js->current_func, SLOT_DATA); ant_value_t promise = js_get(js, state, "promise"); ant_value_t reason = (nargs > 0) ? args[0] : js_mkundef(); js_reject_promise(js, promise, reason); return js_mkundef();}
static void stream_schedule_next_read(ant_t *js, ant_value_t state, ant_value_t read_fn, ant_value_t reader) { ant_value_t next_p = rs_default_reader_read(js, reader); ant_value_t fulfill = js_heavy_mkfun(js, stream_body_read, state); ant_value_t reject = js_heavy_mkfun(js, stream_body_rejected, state); Ant_Promise_Observe(js, next_p, fulfill, reject);}
static ant_value_t stream_body_read(ant_params_t) { ant_value_t state = js_get_slot(js->current_func, SLOT_DATA); ant_value_t result = (nargs > 0) ? args[0] : js_mkundef(); ant_value_t promise = js_get(js, state, "promise"); ant_value_t reader = js_get(js, state, "reader"); ant_value_t chunks = js_get(js, state, "chunks"); int mode = (int)js_getnum(js_get(js, state, "mode"));
ant_value_t done_val = js_get(js, result, "done"); ant_value_t value = js_get(js, result, "value");
if (vtype(done_val) == kTypeBool && done_val == js_true) { size_t size = 0; uint8_t *data = concat_chunks(js, chunks, &size); ant_value_t type_v = js_get(js, state, "type"); const char *body_type = (vtype(type_v) == kTypeString) ? js_getstr(js, type_v, NULL) : NULL; resolve_body_promise(js, promise, data, size, body_type, mode, true); free(data); return js_mkundef(); }
if (vtype(value) != kTypeUndefined && vtype(value) != kTypeNull) js_arr_push(js, chunks, value);
stream_schedule_next_read(js, state, js_mkundef(), reader); return js_mkundef();}
static ant_value_t consume_body_from_stream( ant_t *js, ant_value_t stream, ant_value_t promise, int mode, const char *body_type) { ant_value_t reader_args[1] = { stream }; ant_value_t reader = js_construct_native(js, js_rs_reader_ctor, reader_args, 1);
if (is_err(reader)) { js_reject_promise(js, promise, reader); return promise; }
ant_value_t state = Ant_Object_CreateBare(js); js_set(js, state, "promise", promise); js_set(js, state, "reader", reader); js_set(js, state, "chunks", js_mkarr(js)); js_set(js, state, "mode", js_mknum(mode)); js_set(js, state, "type", body_type ? js_mkstr(js, body_type, strlen(body_type)) : js_mkundef());
stream_schedule_next_read(js, state, js_mkundef(), reader); return promise;}
static ant_value_t consume_body(ant_t *js, int mode) { ant_value_t this = js_getthis(js); request_data_t *d = request_get_data(this); ant_value_t promise = js_mkpromise(js);
if (!d) { js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Request object")); return promise; } if (!d->has_body) { resolve_body_promise(js, promise, NULL, 0, request_effective_body_type(js, this, d), mode, false); return promise; } if (d->body_used) { js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked")); return promise; } d->body_used = true; ant_value_t stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); if (rs_is_stream(stream) && d->body_is_stream) return consume_body_from_stream(js, stream, promise, mode, request_effective_body_type(js, this, d)); resolve_body_promise(js, promise, d->body_data, d->body_size, request_effective_body_type(js, this, d), mode, true); return promise;}
static ant_value_t js_req_text(ant_params_t) { return consume_body(js, BODY_TEXT);}
static ant_value_t js_req_json(ant_params_t) { return consume_body(js, BODY_JSON);}
static ant_value_t js_req_array_buffer(ant_params_t) { return consume_body(js, BODY_ARRAYBUFFER);}
static ant_value_t js_req_blob(ant_params_t) { return consume_body(js, BODY_BLOB);}
static ant_value_t js_req_bytes(ant_params_t) { return consume_body(js, BODY_BYTES);}
static ant_value_t js_req_form_data(ant_params_t) { return consume_body(js, BODY_FORMDATA);}
static ant_value_t request_set_extracted_body( ant_t *js, ant_value_t req_obj, ant_value_t headers, request_data_t *req, uint8_t *body_data, size_t body_size, char *body_type, ant_value_t body_stream, bool duplex_provided) { free(req->body_data); free(req->body_type);
req->body_data = body_data; req->body_size = body_size; req->body_type = body_type; req->body_is_stream = rs_is_stream(body_stream); req->has_body = true;
if (!req->body_is_stream) { if (body_type && body_type[0] && !headers_data_append_if_missing(headers_get_data(headers), "content-type", body_type)) return js_mkerr(js, "out of memory"); return js_mkundef(); }
if (req->keepalive) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': keepalive cannot be used with a ReadableStream body"); } if (!duplex_provided) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': duplex must be provided for a ReadableStream body"); }
js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, body_stream); if (body_type && body_type[0] && !headers_data_append_if_missing(headers_get_data(headers), "content-type", body_type)) return js_mkerr(js, "out of memory"); return js_mkundef();}
static void request_clear_body(ant_t *js, ant_value_t req_obj, request_data_t *req) { free(req->body_data); free(req->body_type); req->body_data = NULL; req->body_size = 0; req->body_type = NULL; req->body_is_stream = false; req->has_body = false; js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, js_mkundef());}
static ant_value_t request_copy_source_body(ant_t *js, ant_value_t req_obj, ant_value_t input, request_data_t *req, request_data_t *src) { ant_value_t src_stream = js_get_slot(input, SLOT_REQUEST_BODY_STREAM);
if (src->body_used) { return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot construct Request with unusable body"); }
if (rs_is_stream(src_stream) && rs_stream_unusable(src_stream)) { return js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); }
if (!src->body_is_stream) { req->body_data = malloc(src->body_size); if (src->body_size > 0 && !req->body_data) return js_mkerr(js, "out of memory"); if (src->body_size > 0) memcpy(req->body_data, src->body_data, src->body_size); req->body_size = src->body_size; req->body_type = src->body_type ? strdup(src->body_type) : NULL; req->body_is_stream = false; req->has_body = true; return js_mkundef(); }
if (!rs_is_stream(src_stream)) return js_mkundef(); ant_value_t branches = readable_stream_tee(js, src_stream); if (is_err(branches)) return branches; if (vtype(branches) != kTypeArray) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': tee() did not return branches"); }
js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, js_arr_get(js, branches, 1)); req->body_is_stream = true; req->has_body = true; return js_mkundef();}
#define REQ_GETTER_START(name) \ static ant_value_t js_req_get_##name(ant_params_t) { \ ant_value_t this = js_getthis(js); \ request_data_t *d = request_get_data(this); \ if (!d) return js_mkundef();
#define REQ_GETTER_END }
REQ_GETTER_START(method) ant_value_t cached = js_get_slot(this, SLOT_REQUEST_METHOD); if (vtype(cached) == kTypeString) return cached; cached = js_mkstr(js, d->method, strlen(d->method)); if (!is_err(cached)) js_set_slot_wb(js, this, SLOT_REQUEST_METHOD, cached); return cached;REQ_GETTER_END
REQ_GETTER_START(url) ant_value_t cached = js_get_slot(this, SLOT_REQUEST_URL); if (vtype(cached) == kTypeString) return cached; const char *href = d->url.href; char *serialized = NULL; if (!href) { serialized = build_href(&d->url); href = serialized; } cached = href ? js_mkstr(js, href, strlen(href)) : js_mkstr(js, "", 0); free(serialized); if (!is_err(cached)) js_set_slot_wb(js, this, SLOT_REQUEST_URL, cached); return cached;REQ_GETTER_END
REQ_GETTER_START(headers) return js_get_slot(this, SLOT_REQUEST_HEADERS);REQ_GETTER_END
REQ_GETTER_START(destination) (void)d; return js_mkstr(js, "", 0);REQ_GETTER_END
REQ_GETTER_START(referrer) if (!d->referrer || strcmp(d->referrer, "no-referrer") == 0) return js_mkstr(js, "", 0); if (strcmp(d->referrer, "client") == 0) return js_mkstr(js, "about:client", 12); return js_mkstr(js, d->referrer, strlen(d->referrer));REQ_GETTER_END
REQ_GETTER_START(referrer_policy) const char *p = d->referrer_policy ? d->referrer_policy : ""; return js_mkstr(js, p, strlen(p));REQ_GETTER_END
REQ_GETTER_START(mode) return js_mkstr(js, d->mode, strlen(d->mode));REQ_GETTER_END
REQ_GETTER_START(credentials) return js_mkstr(js, d->credentials, strlen(d->credentials));REQ_GETTER_END
REQ_GETTER_START(cache) return js_mkstr(js, d->cache, strlen(d->cache));REQ_GETTER_END
REQ_GETTER_START(redirect) return js_mkstr(js, d->redirect, strlen(d->redirect));REQ_GETTER_END
REQ_GETTER_START(integrity) const char *ig = d->integrity ? d->integrity : ""; return js_mkstr(js, ig, strlen(ig));REQ_GETTER_END
REQ_GETTER_START(keepalive) return js_bool(d->keepalive);REQ_GETTER_END
REQ_GETTER_START(is_reload_navigation) return js_bool(d->reload_navigation);REQ_GETTER_END
REQ_GETTER_START(is_history_navigation) return js_bool(d->history_navigation);REQ_GETTER_END
REQ_GETTER_START(signal) return request_get_signal(js, this);REQ_GETTER_END
REQ_GETTER_START(duplex) return js_mkstr(js, "half", 4);REQ_GETTER_END
static ant_value_t req_body_pull(ant_params_t) { ant_value_t req_obj = js_get_slot(js->current_func, SLOT_DATA); request_data_t *d = request_get_data(req_obj); ant_value_t ctrl = (nargs > 0) ? args[0] : js_mkundef();
if (d && d->body_data && d->body_size > 0) { ArrayBufferData *ab = create_array_buffer_data(js, d->body_size); if (!ab) return js_mkerr(js, "out of memory"); memcpy(ab->data, d->body_data, d->body_size);
ant_value_t chunk = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, d->body_size, js->builtins.typedarray_protos[TYPED_ARRAY_UINT8]); if (is_err(chunk)) return chunk;
ant_value_t enqueued = rs_controller_enqueue(js, ctrl, chunk); if (is_err(enqueued)) return enqueued; } rs_controller_close(js, ctrl); return js_mkundef();}
REQ_GETTER_START(body) if (!d->has_body) return js_mknull(); ant_value_t stored_stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); if (rs_is_stream(stored_stream)) return stored_stream; if (d->body_used) return js_mknull(); ant_value_t pull = js_heavy_mkfun(js, req_body_pull, this); ant_value_t stream = rs_create_stream(js, pull, js_mkundef(), 1.0); if (!is_err(stream)) js_set_slot_wb(js, this, SLOT_REQUEST_BODY_STREAM, stream); return stream;REQ_GETTER_END
REQ_GETTER_START(body_used) return js_bool(d->body_used);REQ_GETTER_END
#undef REQ_GETTER_START#undef REQ_GETTER_END
static ant_value_t request_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) : "Request";
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);}
// TODO: make drystatic bool request_inspect_set( ant_t *js, ant_value_t obj, const char *key, ant_value_t value, ant_value_t *err_out) { if (is_err(value)) { *err_out = value; return false; }
js_set(js, obj, key, value); return true;}
static ant_value_t request_inspect(ant_params_t) { ant_value_t this_obj = js_getthis(js); ant_value_t out = Ant_Object_CreateBare(js); ant_value_t err = 0;
if (!request_inspect_set(js, out, "method", js_req_get_method(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "url", js_req_get_url(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "headers", js_req_get_headers(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "destination", js_req_get_destination(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "referrer", js_req_get_referrer(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "referrerPolicy", js_req_get_referrer_policy(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "mode", js_req_get_mode(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "credentials", js_req_get_credentials(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "cache", js_req_get_cache(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "redirect", js_req_get_redirect(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "integrity", js_req_get_integrity(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "keepalive", js_req_get_keepalive(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "isReloadNavigation", js_req_get_is_reload_navigation(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "isHistoryNavigation", js_req_get_is_history_navigation(js, NULL, 0, js_mkundef()), &err)) return err; if (!request_inspect_set(js, out, "signal", js_req_get_signal(js, NULL, 0, js_mkundef()), &err)) return err;
return request_inspect_finish(js, this_obj, out);}
static ant_value_t js_request_clone(ant_params_t) { ant_value_t this = js_getthis(js); request_data_t *d = request_get_data(this); if (!d) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Request object"); if (d->body_used) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot clone a Request whose body is unusable");
request_data_t *nd = data_dup(d); if (!nd) return js_mkerr(js, "out of memory");
ant_value_t src_headers = js_get_slot(this, SLOT_REQUEST_HEADERS); ant_value_t src_signal = request_get_signal(js, this);
ant_value_t new_headers = headers_create_empty(js); if (is_err(new_headers)) { data_free(nd); return new_headers; } if (!headers_copy_from(new_headers, src_headers)) { data_free(nd); return js_mkerr(js, "out of memory"); } ant_value_t new_signal = abort_signal_create_dependent(js, src_signal); if (is_err(new_signal)) { data_free(nd); return new_signal; }
ant_value_t obj = Ant_Object_CreateBare(js); (void)js_reserve_slots(obj, REQUEST_RESERVED_SLOTS); js_set_proto_init(obj, js->builtins.request_proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); js_set_native(obj, nd, REQUEST_NATIVE_TAG); js_set_finalizer(obj, request_finalize); js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, new_headers); js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef()); js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, new_signal);
ant_value_t src_stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); if (rs_is_stream(src_stream)) { ant_value_t branches = readable_stream_tee(js, src_stream); if (!is_err(branches) && vtype(branches) == kTypeArray) { ant_value_t b1 = js_arr_get(js, branches, 0); ant_value_t b2 = js_arr_get(js, branches, 1); js_set_slot_wb(js, this, SLOT_REQUEST_BODY_STREAM, b1); js_set_slot_wb(js, obj, SLOT_REQUEST_BODY_STREAM, b2); }}
return obj;}
static const char *init_str(ant_t *js, ant_value_t init, const char *key, size_t klen, ant_value_t *err_out) { ant_value_t v = js_get(js, init, key); if (vtype(v) == kTypeUndefined) return NULL; if (vtype(v) != kTypeString) { v = js_tostring_val(js, v); if (is_err(v)) { *err_out = v; return NULL; } } return js_getstr(js, v, NULL);}
static ant_value_t request_new_from_input( ant_t *js, ant_value_t input, request_data_t **out_req, request_data_t **out_src, ant_value_t *out_input_signal) { request_data_t *req = NULL; request_data_t *src = NULL;
*out_req = NULL; *out_src = NULL; *out_input_signal = js_mkundef();
if ( vtype(input) == kTypeObject && js_check_brand(input, BRAND_REQUEST) ) src = request_get_data(input);
if (!src) { size_t ulen = 0; const char *url_str = NULL; url_state_t parsed = {0};
if (vtype(input) != kTypeString) { input = js_tostring_val(js, input); if (is_err(input)) return input; }
url_str = js_getstr(js, input, &ulen); if (Ant_URL_ParseDocumentRelative(js, url_str, &parsed) != 0) { url_state_clear(&parsed); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid URL"); } if ((parsed.username && parsed.username[0]) || (parsed.password && parsed.password[0])) { url_state_clear(&parsed); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': URL includes credentials"); }
req = data_new(); if (!req) { url_state_clear(&parsed); return js_mkerr(js, "out of memory"); } req->url = parsed; } else { req = data_dup(src); if (!req) return js_mkerr(js, "out of memory"); req->body_used = false; *out_input_signal = request_get_signal(js, input); }
*out_req = req; *out_src = src; return js_mkundef();}
static ant_value_t request_apply_init_options( ant_t *js, ant_value_t init, request_data_t *req, ant_value_t *input_signal) { ant_value_t err = js_mkundef(); ant_value_t win = js_get(js, init, "window"); const char *ref = NULL; const char *rp = NULL; const char *mode_val = NULL; const char *cred = NULL; const char *cache_val = NULL; const char *redir = NULL; const char *integ = NULL; const char *method_val = NULL;
if (vtype(win) != kTypeUndefined && vtype(win) != kTypeNull) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': 'window' must be null"); }
if (strcmp(req->mode, "navigate") == 0) { free(req->mode); req->mode = strdup("same-origin"); } req->reload_navigation = false; req->history_navigation = false; free(req->referrer); req->referrer = strdup("client"); free(req->referrer_policy); req->referrer_policy = strdup("");
ref = init_str(js, init, "referrer", 8, &err); if (is_err(err)) return err; if (ref) { if (ref[0] == '\0') { free(req->referrer); req->referrer = strdup("no-referrer"); } else { url_state_t rs = {0}; if (parse_url_to_state(ref, NULL, &rs) != 0) { url_state_clear(&rs); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid referrer URL"); } free(req->referrer); req->referrer = build_href(&rs); url_state_clear(&rs); if (!req->referrer) req->referrer = strdup("client"); }}
rp = init_str(js, init, "referrerPolicy", 14, &err); if (is_err(err)) return err; if (rp) { free(req->referrer_policy); req->referrer_policy = strdup(rp); }
mode_val = init_str(js, init, "mode", 4, &err); if (is_err(err)) return err; if (mode_val) { if (strcmp(mode_val, "navigate") == 0) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': mode 'navigate' is not allowed"); } free(req->mode); req->mode = strdup(mode_val); }
cred = init_str(js, init, "credentials", 11, &err); if (is_err(err)) return err; if (cred) { free(req->credentials); req->credentials = strdup(cred); }
cache_val = init_str(js, init, "cache", 5, &err); if (is_err(err)) return err; if (cache_val) { free(req->cache); req->cache = strdup(cache_val); if ( strcmp(req->cache, "only-if-cached") == 0 && strcmp(req->mode, "same-origin") != 0 ) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': cache mode 'only-if-cached' requires mode 'same-origin'"); }
redir = init_str(js, init, "redirect", 8, &err); if (is_err(err)) return err; if (redir) { free(req->redirect); req->redirect = strdup(redir); }
integ = init_str(js, init, "integrity", 9, &err); if (is_err(err)) return err; if (integ) { free(req->integrity); req->integrity = strdup(integ); }
ant_value_t ka = js_get(js, init, "keepalive"); if (vtype(ka) != kTypeUndefined) req->keepalive = js_truthy(js, ka);
method_val = init_str(js, init, "method", 6, &err); if (is_err(err)) return err; if (method_val) { if (!is_valid_method(method_val)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid method"); } if (is_forbidden_method(method_val)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Forbidden method"); } free(req->method); req->method = strdup(method_val); normalize_method(req->method); }
ant_value_t sig_val = js_get(js, init, "signal"); if (vtype(sig_val) == kTypeUndefined) return js_mkundef(); if (vtype(sig_val) == kTypeNull) { *input_signal = js_mkundef(); return js_mkundef(); } if (abort_signal_is_signal(sig_val)) { *input_signal = sig_val; return js_mkundef(); }
return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': signal must be an AbortSignal");}
static ant_value_t request_create_ctor_headers(ant_t *js, ant_value_t input) { ant_value_t headers = headers_create_empty(js); if (is_err(headers)) return headers; if (vtype(input) != kTypeObject) return headers;
ant_value_t src_hdrs = js_get_slot(input, SLOT_REQUEST_HEADERS); if (!headers_copy_from(headers, src_hdrs)) return js_mkerr(js, "out of memory"); return headers;}
static ant_value_t request_apply_init_headers(ant_t *js, ant_value_t init, ant_value_t headers) { ant_value_t init_headers = js_get(js, init, "headers"); if (vtype(init_headers) == kTypeUndefined) return headers; return headers_create_from_init(js, init_headers);}
static ant_value_t request_parse_duplex(ant_t *js, ant_value_t init, bool *out_duplex_provided) { ant_value_t duplex_val = js_get(js, init, "duplex"); ant_value_t duplex_str_v = duplex_val; const char *duplex_str = NULL;
*out_duplex_provided = vtype(duplex_val) != kTypeUndefined; if (!*out_duplex_provided) return js_mkundef();
if (vtype(duplex_str_v) != kTypeString) { duplex_str_v = js_tostring_val(js, duplex_str_v); if (is_err(duplex_str_v)) return duplex_str_v; }
duplex_str = js_getstr(js, duplex_str_v, NULL); if (duplex_str && strcmp(duplex_str, "half") == 0) return js_mkundef();
return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': duplex must be 'half'");}
static ant_value_t request_apply_ctor_body( ant_t *js, ant_value_t req_obj, ant_value_t input, ant_value_t init, bool init_provided, bool duplex_provided, request_data_t *req, request_data_t *src, ant_value_t headers) { if (init_provided) { ant_value_t body_val = js_get(js, init, "body"); bool init_body_present = vtype(body_val) != kTypeUndefined; bool input_body_present = src && src->has_body; bool effective_body_present = (init_body_present && vtype(body_val) != kTypeNull) || (input_body_present && (!init_body_present || vtype(body_val) == kTypeNull));
if ((strcmp(req->method, "GET") == 0 || strcmp(req->method, "HEAD") == 0) && effective_body_present) { return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Request with GET/HEAD method cannot have body"); }
if (vtype(body_val) == kTypeUndefined) return js_mkundef(); if (vtype(body_val) == kTypeNull) { request_clear_body(js, req_obj, req); return js_mkundef(); }
request_data_t *init_req = request_get_data(init); ant_value_t body_err = js_mkundef(); ant_value_t body_stream = js_mkundef(); uint8_t *bd = NULL; size_t bs = 0; char *bt = NULL; if (init_req && !init_req->body_used && !init_req->body_is_stream && init_req->has_body) { bd = malloc(init_req->body_size); if (init_req->body_size > 0 && !bd) return js_mkerr(js, "out of memory"); if (init_req->body_size > 0) memcpy(bd, init_req->body_data, init_req->body_size); bs = init_req->body_size; bt = init_req->body_type ? strdup(init_req->body_type) : NULL; } else if (!extract_body(js, body_val, &bd, &bs, &bt, &body_stream, &body_err)) return is_err(body_err) ? body_err : js_mkerr(js, "Failed to extract body"); return request_set_extracted_body( js, req_obj, headers, req, bd, bs, bt, body_stream, duplex_provided ); }
if (!src) return js_mkundef(); return request_copy_source_body(js, req_obj, input, req, src);}
static ant_value_t js_request_ctor(ant_params_t) { ant_value_t init = (nargs >= 2 && vtype(args[1]) != kTypeUndefined) ? args[1] : js_mkundef();
if (vtype(call_new_target) == kTypeUndefined) return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires 'new'"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires at least 1 argument");
ant_value_t input = args[0]; ant_value_t obj = 0; ant_value_t proto = 0; bool init_provided = false; request_data_t *req = NULL; request_data_t *src = NULL; ant_value_t input_signal = js_mkundef(); ant_value_t step = js_mkundef(); ant_value_t signal = 0; ant_value_t headers = 0; bool duplex_provided = false; init_provided = (vtype(init) == kTypeObject || vtype(init) == kTypeArray); step = request_new_from_input(js, input, &req, &src, &input_signal); if (is_err(step)) return step;
if (init_provided) { step = request_apply_init_options(js, init, req, &input_signal); if (is_err(step)) { data_free(req); return step; } }
if ( strcmp(req->mode, "no-cors") == 0 && !is_cors_safelisted_method(req->method) ) { data_free(req); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"); }
obj = Ant_Object_CreateBare(js); (void)js_reserve_slots(obj, REQUEST_RESERVED_SLOTS); proto = js_instance_proto_from_new_target(js, js->builtins.request_proto, call_new_target); if (is_object_type(proto)) js_set_proto_init(obj, proto); else js_set_proto_init(obj, js->builtins.request_proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); js_set_native(obj, req, REQUEST_NATIVE_TAG); js_set_finalizer(obj, request_finalize); js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef());
signal = abort_signal_create_dependent(js, input_signal); if (is_err(signal)) { request_clear_and_free(obj, req); return signal; } js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal);
headers = request_create_ctor_headers(js, input); if (is_err(headers)) { request_clear_and_free(obj, req); return headers; } if (init_provided) { headers = request_apply_init_headers(js, init, headers); if (is_err(headers)) { request_clear_and_free(obj, req); return headers; } }
js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, headers);
if (init_provided) { step = request_parse_duplex(js, init, &duplex_provided); if (is_err(step)) { request_clear_and_free(obj, req); return step; } }
step = request_apply_ctor_body( js, obj, input, init, init_provided, duplex_provided, req, src, headers ); if (is_err(step)) { request_clear_and_free(obj, req); return step; }
if (src && src->has_body && !src->body_used) src->body_used = true;
return obj;}
ant_value_t request_create_from_input_init(ant_t *js, ant_value_t input, ant_value_t init) { bool init_provided = (vtype(init) == kTypeObject || vtype(init) == kTypeArray); request_data_t *req = NULL; request_data_t *src = NULL; ant_value_t input_signal = js_mkundef(); ant_value_t step = js_mkundef(); ant_value_t obj = 0; ant_value_t signal = 0; ant_value_t headers = 0; bool duplex_provided = false; step = request_new_from_input(js, input, &req, &src, &input_signal); if (is_err(step)) return step;
if (init_provided) { step = request_apply_init_options(js, init, req, &input_signal); if (is_err(step)) { data_free(req); return step; } }
if ( strcmp(req->mode, "no-cors") == 0 && !is_cors_safelisted_method(req->method) ) { data_free(req); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"); }
obj = Ant_Object_CreateBare(js); (void)js_reserve_slots(obj, REQUEST_RESERVED_SLOTS); js_set_proto_init(obj, js->builtins.request_proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); js_set_native(obj, req, REQUEST_NATIVE_TAG); js_set_finalizer(obj, request_finalize); js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef());
signal = abort_signal_create_dependent(js, input_signal); if (is_err(signal)) { request_clear_and_free(obj, req); return signal; } js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal);
headers = request_create_ctor_headers(js, input); if (is_err(headers)) { request_clear_and_free(obj, req); return headers; } if (init_provided) { headers = request_apply_init_headers(js, init, headers); if (is_err(headers)) { request_clear_and_free(obj, req); return headers; } }
js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, headers);
if (init_provided) { step = request_parse_duplex(js, init, &duplex_provided); if (is_err(step)) { request_clear_and_free(obj, req); return step; } }
step = request_apply_ctor_body(js, obj, input, init, init_provided, duplex_provided, req, src, headers); if (is_err(step)) { request_clear_and_free(obj, req); return step; }
if (src && src->has_body && !src->body_used) src->body_used = true;
return obj;}
ant_value_t request_create_server( ant_t *js, const char *method, const char *target, bool absolute_target, const char *host, bool canonical_host, const char *server_hostname, int server_port, ant_value_t headers_obj, const uint8_t *body, size_t body_len, const char *body_type) { request_data_t *req = data_new_server(method); if (!req) return js_mkerr(js, "out of memory");
if (!target || request_parse_server_url( target, absolute_target, host, canonical_host, server_hostname, server_port, &req->url) != 0) { data_free(req); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid URL"); }
if (body) req->has_body = true;
if (body && body_len > 0) { req->body_data = malloc(body_len); if (!req->body_data) { data_free(req); return js_mkerr(js, "out of memory"); } memcpy(req->body_data, body, body_len); req->body_size = body_len; req->body_type = body_type ? strdup(body_type) : NULL; } req->body_is_stream = false;
return request_create_object(js, req, headers_obj, false);}
void init_request_module(ant_t *js) { ant_value_t g = js_glob(js); js->builtins.request_proto = Ant_Object_CreateBare(js);
js_set(js, js->builtins.request_proto, "text", Ant_Function_Create(js_req_text)); js_set(js, js->builtins.request_proto, "json", Ant_Function_Create(js_req_json)); js_set(js, js->builtins.request_proto, "arrayBuffer", Ant_Function_Create(js_req_array_buffer)); js_set(js, js->builtins.request_proto, "blob", Ant_Function_Create(js_req_blob)); js_set(js, js->builtins.request_proto, "formData", Ant_Function_Create(js_req_form_data)); js_set(js, js->builtins.request_proto, "bytes", Ant_Function_Create(js_req_bytes)); js_set(js, js->builtins.request_proto, "clone", Ant_Function_Create(js_request_clone));
#define GETTER(prop, fn) \ js_set_getter_desc(js, js->builtins.request_proto, prop, sizeof(prop)-1, Ant_Function_Create(js_req_get_##fn), JS_DESC_C) GETTER("method", method); GETTER("url", url); GETTER("headers", headers); GETTER("destination", destination); GETTER("referrer", referrer); GETTER("referrerPolicy", referrer_policy); GETTER("mode", mode); GETTER("credentials", credentials); GETTER("cache", cache); GETTER("redirect", redirect); GETTER("integrity", integrity); GETTER("keepalive", keepalive); GETTER("isReloadNavigation",is_reload_navigation); GETTER("isHistoryNavigation",is_history_navigation); GETTER("signal", signal); GETTER("duplex", duplex); GETTER("body", body); GETTER("bodyUsed", body_used);#undef GETTER
js_set_sym(js, js->builtins.request_proto, js->sym.inspect_sym, Ant_Function_Create(request_inspect)); js_set_tostringtag(js, js->builtins.request_proto, js_mkstr(js, "Request", 7)); ant_value_t ctor = Ant_Function_CreateIntrinsicConstructor(js, js_request_ctor, js->builtins.request_proto, "Request", 1); js_set(js, g, "Request", ctor); js_set_descriptor(js, g, "Request", 7, JS_DESC_W | JS_DESC_C);}