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

Configure Feed

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

at mir/inline-method 1521 lines 49 kB view raw
1#include <stdlib.h> 2#include <string.h> 3#include <strings.h> 4#include <ctype.h> 5#include <stdio.h> 6 7#include "ant.h" 8#include "errors.h" 9#include "runtime.h" 10#include "internal.h" 11#include "common.h" 12#include "descriptors.h" 13 14#include "modules/blob.h" 15#include "modules/buffer.h" 16#include "modules/assert.h" 17#include "modules/abort.h" 18#include "modules/formdata.h" 19#include "modules/headers.h" 20#include "modules/multipart.h" 21#include "modules/request.h" 22#include "modules/symbol.h" 23#include "modules/url.h" 24#include "modules/json.h" 25#include "streams/pipes.h" 26#include "streams/readable.h" 27 28ant_value_t g_request_proto = 0; 29 30static request_data_t *get_data(ant_value_t obj) { 31 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 32 if (vtype(slot) != T_NUM) return NULL; 33 return (request_data_t *)(uintptr_t)(size_t)js_getnum(slot); 34} 35 36request_data_t *request_get_data(ant_value_t obj) { 37 return get_data(obj); 38} 39 40ant_value_t request_get_headers(ant_value_t obj) { 41 return js_get_slot(obj, SLOT_REQUEST_HEADERS); 42} 43 44ant_value_t request_get_signal(ant_t *js, ant_value_t obj) { 45 ant_value_t signal = js_get_slot(obj, SLOT_REQUEST_SIGNAL); 46 47 if (vtype(signal) != T_UNDEF) return signal; 48 signal = abort_signal_create_dependent(js, js_mkundef()); 49 50 if (is_err(signal)) return signal; 51 ant_value_t abort_reason = js_get_slot(obj, SLOT_REQUEST_ABORT_REASON); 52 53 if (vtype(abort_reason) != T_UNDEF) signal_do_abort(js, signal, abort_reason); 54 js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 55 56 return signal; 57} 58 59static void data_free(request_data_t *d) { 60 if (!d) return; 61 free(d->method); 62 url_state_clear(&d->url); 63 free(d->referrer); 64 free(d->referrer_policy); 65 free(d->mode); 66 free(d->credentials); 67 free(d->cache); 68 free(d->redirect); 69 free(d->integrity); 70 free(d->body_data); 71 free(d->body_type); 72 free(d); 73} 74 75static request_data_t *data_new_with(const char *method, const char *mode) { 76 request_data_t *d = calloc(1, sizeof(request_data_t)); 77 if (!d) return NULL; 78 79 d->method = strdup(method ? method : "GET"); 80 d->referrer = strdup("client"); 81 d->referrer_policy = strdup(""); 82 d->mode = strdup(mode); 83 d->credentials = strdup("same-origin"); 84 d->cache = strdup("default"); 85 d->redirect = strdup("follow"); 86 d->integrity = strdup(""); 87 88 if (!d->method 89 || !d->referrer 90 || !d->referrer_policy 91 || !d->mode 92 || !d->credentials 93 || !d->cache 94 || !d->redirect 95 || !d->integrity 96 ) { data_free(d); return NULL; } 97 98 return d; 99} 100 101static request_data_t *data_new(void) { return data_new_with("GET", "cors"); } 102static request_data_t *data_new_server(const char *method) { return data_new_with(method, "same-origin"); } 103 104static ant_value_t request_create_object(ant_t *js, request_data_t *req, ant_value_t headers_obj, bool create_signal) { 105 ant_value_t obj = js_mkobj(js); 106 ant_value_t hdrs = is_object_type(headers_obj) 107 ? headers_obj 108 : headers_create_empty(js); 109 110 js_set_proto_init(obj, g_request_proto); 111 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 112 js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 113 114 headers_set_guard(hdrs, 115 strcmp(req->mode, "no-cors") == 0 116 ? HEADERS_GUARD_REQUEST_NO_CORS 117 : HEADERS_GUARD_REQUEST 118 ); 119 120 headers_apply_guard(hdrs); 121 js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, hdrs); 122 js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef()); 123 124 js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, create_signal 125 ? abort_signal_create_dependent(js, js_mkundef()) 126 : js_mkundef() 127 ); 128 129 return obj; 130} 131 132static char *request_build_server_base_url(const char *host, const char *server_hostname, int server_port) { 133 const char *authority = (host && host[0]) ? host : server_hostname; 134 size_t authority_len = authority ? strlen(authority) : 0; 135 bool include_port = (!host || !host[0]) && server_port > 0 && server_port != 80; 136 size_t cap = sizeof("http://") - 1 + authority_len + 1 + 16 + 1; 137 138 char *base = malloc(cap); 139 int written = 0; 140 141 if (!base) return NULL; 142 if (include_port) written = snprintf(base, cap, "http://%s:%d/", authority ? authority : "", server_port); 143 else written = snprintf(base, cap, "http://%s/", authority ? authority : ""); 144 145 if (written < 0 || (size_t)written >= cap) { 146 free(base); 147 return NULL; 148 } 149 150 return base; 151} 152 153static int request_parse_server_url( 154 const char *target, 155 bool absolute_target, 156 const char *host, 157 const char *server_hostname, 158 int server_port, 159 url_state_t *out 160) { 161 char *base = NULL; 162 int rc = 0; 163 164 if (absolute_target) 165 return parse_url_to_state(target, NULL, out); 166 167 base = request_build_server_base_url(host, server_hostname, server_port); 168 if (!base) return -1; 169 170 rc = parse_url_to_state(target, base, out); 171 free(base); 172 173 return rc; 174} 175 176static request_data_t *data_dup(const request_data_t *src) { 177 request_data_t *d = calloc(1, sizeof(request_data_t)); 178 if (!d) return NULL; 179 180#define DUP_STR(f) do { d->f = src->f ? strdup(src->f) : NULL; } while(0) 181 DUP_STR(method); 182 DUP_STR(referrer); 183 DUP_STR(referrer_policy); 184 DUP_STR(mode); 185 DUP_STR(credentials); 186 DUP_STR(cache); 187 DUP_STR(redirect); 188 DUP_STR(integrity); 189 DUP_STR(body_type); 190#undef DUP_STR 191 url_state_t *su = (url_state_t *)&src->url; 192 url_state_t *du = &d->url; 193#define DUP_US(f) do { du->f = su->f ? strdup(su->f) : NULL; } while(0) 194 DUP_US(protocol); DUP_US(username); DUP_US(password); 195 DUP_US(hostname); DUP_US(port); DUP_US(pathname); 196 DUP_US(search); DUP_US(hash); 197#undef DUP_US 198 d->keepalive = src->keepalive; 199 d->reload_navigation = src->reload_navigation; 200 d->history_navigation = src->history_navigation; 201 d->has_body = src->has_body; 202 d->body_is_stream = src->body_is_stream; 203 d->body_used = src->body_used; 204 d->body_size = src->body_size; 205 206 if (src->body_data && src->body_size > 0) { 207 d->body_data = malloc(src->body_size); 208 if (!d->body_data) { data_free(d); return NULL; } 209 memcpy(d->body_data, src->body_data, src->body_size); 210 } 211 212 return d; 213} 214 215static bool is_token_char(unsigned char c) { 216 if (c == 0 || c > 127) return false; 217 static const char *delimiters = "(),/:;<=>?@[\\]{}\""; 218 return c > 32 && !strchr(delimiters, (char)c); 219} 220 221static bool is_valid_method(const char *m) { 222 if (!m || !*m) return false; 223 for (const unsigned char *p = (const unsigned char *)m; *p; p++) 224 if (!is_token_char(*p)) return false; 225 return true; 226} 227 228static bool is_forbidden_method(const char *m) { 229 return 230 strcasecmp(m, "CONNECT") == 0 || 231 strcasecmp(m, "TRACE") == 0 || 232 strcasecmp(m, "TRACK") == 0; 233} 234 235static bool is_cors_safelisted_method(const char *m) { 236 return 237 strcasecmp(m, "GET") == 0 || 238 strcasecmp(m, "HEAD") == 0 || 239 strcasecmp(m, "POST") == 0; 240} 241 242static void normalize_method(char *m) { 243 static const char *norm[] = { 244 "DELETE","GET","HEAD","OPTIONS","POST","PUT" 245 }; 246 247 for (int i = 0; i < 6; i++) { 248 if (strcasecmp(m, norm[i]) == 0) { 249 strcpy(m, norm[i]); 250 return; 251 }} 252} 253 254static ant_value_t request_rejection_reason(ant_t *js, ant_value_t value) { 255 if (!is_err(value)) return value; 256 ant_value_t reason = js->thrown_exists ? js->thrown_value : value; 257 js->thrown_exists = false; 258 js->thrown_value = js_mkundef(); 259 js->thrown_stack = js_mkundef(); 260 return reason; 261} 262 263static const char *request_effective_body_type(ant_t *js, ant_value_t req_obj, request_data_t *d) { 264 ant_value_t headers = js_get_slot(req_obj, SLOT_REQUEST_HEADERS); 265 if (!headers_is_headers(headers)) return d ? d->body_type : NULL; 266 ant_value_t ct = headers_get_value(js, headers, "content-type"); 267 if (vtype(ct) == T_STR) return js_getstr(js, ct, NULL); 268 return d ? d->body_type : NULL; 269} 270 271static bool copy_body_bytes( 272 ant_t *js, const uint8_t *src, size_t src_len, 273 uint8_t **out_data, size_t *out_size, ant_value_t *err_out 274) { 275 uint8_t *buf = NULL; 276 277 *out_data = NULL; 278 *out_size = 0; 279 if (src_len == 0) return true; 280 281 buf = malloc(src_len); 282 if (!buf) { 283 *err_out = js_mkerr(js, "out of memory"); 284 return false; 285 } 286 287 memcpy(buf, src, src_len); 288 *out_data = buf; 289 *out_size = src_len; 290 return true; 291} 292 293static bool extract_buffer_source_body( 294 ant_t *js, ant_value_t body_val, 295 uint8_t **out_data, size_t *out_size, ant_value_t *err_out 296) { 297 const uint8_t *src = NULL; 298 size_t src_len = 0; 299 300 if (!(( 301 vtype(body_val) == T_TYPEDARRAY || vtype(body_val) == T_OBJ) && 302 buffer_source_get_bytes(js, body_val, &src, &src_len)) 303 ) return false; 304 305 return copy_body_bytes(js, src, src_len, out_data, out_size, err_out); 306} 307 308static bool extract_stream_body( 309 ant_t *js, ant_value_t body_val, 310 ant_value_t *out_stream, ant_value_t *err_out 311) { 312 if (!rs_is_stream(body_val)) return false; 313 if (rs_stream_unusable(body_val)) { 314 *err_out = js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); 315 return false; 316 } 317 318 *out_stream = body_val; 319 return true; 320} 321 322static bool extract_blob_body( 323 ant_t *js, ant_value_t body_val, 324 uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out 325) { 326 blob_data_t *bd = blob_is_blob(js, body_val) ? blob_get_data(body_val) : NULL; 327 if (!bd) return false; 328 329 if (!copy_body_bytes(js, bd->data, bd->size, out_data, out_size, err_out)) return false; 330 if (bd->type && bd->type[0]) *out_type = strdup(bd->type); 331 332 return true; 333} 334 335static bool extract_urlsearchparams_body( 336 ant_t *js, ant_value_t body_val, 337 uint8_t **out_data, size_t *out_size, char **out_type 338) { 339 char *serialized = NULL; 340 if (!usp_is_urlsearchparams(js, body_val)) return false; 341 342 serialized = usp_serialize(js, body_val); 343 if (serialized) { 344 *out_data = (uint8_t *)serialized; 345 *out_size = strlen(serialized); 346 *out_type = strdup("application/x-www-form-urlencoded;charset=UTF-8"); 347 } 348 349 return true; 350} 351 352static bool extract_formdata_body( 353 ant_t *js, ant_value_t body_val, 354 uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out 355) { 356 char *boundary = NULL; 357 char *content_type = NULL; 358 size_t mp_size = 0; 359 uint8_t *mp = NULL; 360 361 if (!formdata_is_formdata(js, body_val)) return false; 362 mp = formdata_serialize_multipart(js, body_val, &mp_size, &boundary); 363 364 if (!mp) { 365 *err_out = js_mkerr(js, "out of memory"); 366 return false; 367 } 368 369 if (mp_size > 0) *out_data = mp; 370 else free(mp); 371 372 *out_size = mp_size; 373 if (boundary) { 374 size_t ct_len = snprintf(NULL, 0, "multipart/form-data; boundary=%s", boundary); 375 content_type = malloc(ct_len + 1); 376 if (!content_type) { 377 free(boundary); 378 if (mp_size > 0) free(mp); 379 *out_data = NULL; 380 *out_size = 0; 381 *err_out = js_mkerr(js, "out of memory"); 382 return false; 383 } 384 snprintf(content_type, ct_len + 1, "multipart/form-data; boundary=%s", boundary); 385 free(boundary); 386 *out_type = content_type; 387 } 388 389 return true; 390} 391 392static bool extract_string_body( 393 ant_t *js, ant_value_t body_val, 394 uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out 395) { 396 size_t len = 0; 397 const char *s = NULL; 398 399 if (vtype(body_val) != T_STR) { 400 body_val = js_tostring_val(js, body_val); 401 if (is_err(body_val)) { 402 *err_out = body_val; 403 return false; 404 }} 405 406 s = js_getstr(js, body_val, &len); 407 if (!copy_body_bytes(js, (const uint8_t *)s, len, out_data, out_size, err_out)) return false; 408 *out_type = strdup("text/plain;charset=UTF-8"); 409 410 return true; 411} 412 413static bool extract_body( 414 ant_t *js, ant_value_t body_val, 415 uint8_t **out_data, size_t *out_size, char **out_type, 416 ant_value_t *out_stream, ant_value_t *err_out 417) { 418 *out_data = NULL; 419 *out_size = 0; 420 *out_type = NULL; 421 *out_stream = js_mkundef(); 422 *err_out = js_mkundef(); 423 424 if (vtype(body_val) == T_NULL || vtype(body_val) == T_UNDEF) return true; 425 if (extract_buffer_source_body(js, body_val, out_data, out_size, err_out)) return true; 426 if (vtype(body_val) == T_OBJ && rs_is_stream(body_val)) return extract_stream_body(js, body_val, out_stream, err_out); 427 if (vtype(body_val) == T_OBJ && extract_blob_body(js, body_val, out_data, out_size, out_type, err_out)) return true; 428 if (vtype(body_val) == T_OBJ && extract_urlsearchparams_body(js, body_val, out_data, out_size, out_type)) return true; 429 if (vtype(body_val) == T_OBJ && extract_formdata_body(js, body_val, out_data, out_size, out_type, err_out)) return true; 430 431 return extract_string_body(js, body_val, out_data, out_size, out_type, err_out); 432} 433 434enum { 435 BODY_TEXT = 0, 436 BODY_JSON, 437 BODY_ARRAYBUFFER, 438 BODY_BLOB, 439 BODY_BYTES, 440 BODY_FORMDATA 441}; 442 443static void resolve_body_promise( 444 ant_t *js, ant_value_t promise, 445 const uint8_t *data, size_t size, 446 const char *body_type, int mode, bool has_body 447) { 448 switch (mode) { 449 case BODY_TEXT: { 450 ant_value_t str = (data && size > 0) 451 ? js_mkstr(js, (const char *)data, size) 452 : js_mkstr(js, "", 0); 453 js_resolve_promise(js, promise, str); 454 break; 455 } 456 case BODY_JSON: { 457 ant_value_t str = (data && size > 0) 458 ? js_mkstr(js, (const char *)data, size) 459 : js_mkstr(js, "", 0); 460 ant_value_t parsed = json_parse_value(js, str); 461 if (is_err(parsed)) js_reject_promise(js, promise, request_rejection_reason(js, parsed)); 462 else js_resolve_promise(js, promise, parsed); 463 break; 464 } 465 case BODY_ARRAYBUFFER: { 466 ArrayBufferData *ab = create_array_buffer_data(size); 467 if (!ab) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); break; } 468 if (data && size > 0) memcpy(ab->data, data, size); 469 js_resolve_promise(js, promise, create_arraybuffer_obj(js, ab)); 470 break; 471 } 472 case BODY_BLOB: { 473 const char *type = body_type ? body_type : ""; 474 js_resolve_promise(js, promise, blob_create(js, data, size, type)); 475 break; 476 } 477 case BODY_BYTES: { 478 ArrayBufferData *ab = create_array_buffer_data(size); 479 if (!ab) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); break; } 480 if (data && size > 0) memcpy(ab->data, data, size); 481 js_resolve_promise(js, promise, 482 create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, size, "Uint8Array")); 483 break; 484 } 485 case BODY_FORMDATA: { 486 ant_value_t fd = formdata_parse_body(js, data, size, body_type, has_body); 487 if (is_err(fd)) js_reject_promise(js, promise, request_rejection_reason(js, fd)); 488 else js_resolve_promise(js, promise, fd); 489 break; 490 }} 491} 492 493static uint8_t *concat_chunks(ant_t *js, ant_value_t chunks, size_t *out_size) { 494 ant_offset_t n = js_arr_len(js, chunks); 495 size_t total = 0; 496 497 for (ant_offset_t i = 0; i < n; i++) { 498 ant_value_t chunk = js_arr_get(js, chunks, i); 499 if (vtype(chunk) == T_TYPEDARRAY) { 500 TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(chunk); 501 if (ta && ta->buffer && !ta->buffer->is_detached) total += ta->byte_length; 502 }} 503 504 uint8_t *buf = total > 0 ? malloc(total) : NULL; 505 if (total > 0 && !buf) return NULL; 506 507 size_t pos = 0; 508 for (ant_offset_t i = 0; i < n; i++) { 509 ant_value_t chunk = js_arr_get(js, chunks, i); 510 511 if (vtype(chunk) == T_TYPEDARRAY) { 512 TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(chunk); 513 if (ta && ta->buffer && !ta->buffer->is_detached && ta->byte_length > 0) { 514 memcpy(buf + pos, ta->buffer->data + ta->byte_offset, ta->byte_length); 515 pos += ta->byte_length; 516 }}} 517 518 *out_size = pos; 519 return buf; 520} 521 522static ant_value_t stream_body_read(ant_t *js, ant_value_t *args, int nargs); 523 524static ant_value_t stream_body_rejected(ant_t *js, ant_value_t *args, int nargs) { 525 ant_value_t state = js_get_slot(js->current_func, SLOT_DATA); 526 ant_value_t promise = js_get(js, state, "promise"); 527 ant_value_t reason = (nargs > 0) ? args[0] : js_mkundef(); 528 js_reject_promise(js, promise, reason); 529 return js_mkundef(); 530} 531 532static void stream_schedule_next_read(ant_t *js, ant_value_t state, ant_value_t read_fn, ant_value_t reader) { 533 ant_value_t next_p = rs_default_reader_read(js, reader); 534 ant_value_t fulfill = js_heavy_mkfun(js, stream_body_read, state); 535 ant_value_t reject = js_heavy_mkfun(js, stream_body_rejected, state); 536 ant_value_t then_result = js_promise_then(js, next_p, fulfill, reject); 537 promise_mark_handled(then_result); 538} 539 540static ant_value_t stream_body_read(ant_t *js, ant_value_t *args, int nargs) { 541 ant_value_t state = js_get_slot(js->current_func, SLOT_DATA); 542 ant_value_t result = (nargs > 0) ? args[0] : js_mkundef(); 543 ant_value_t promise = js_get(js, state, "promise"); 544 ant_value_t reader = js_get(js, state, "reader"); 545 ant_value_t chunks = js_get(js, state, "chunks"); 546 int mode = (int)js_getnum(js_get(js, state, "mode")); 547 548 ant_value_t done_val = js_get(js, result, "done"); 549 ant_value_t value = js_get(js, result, "value"); 550 551 if (vtype(done_val) == T_BOOL && done_val == js_true) { 552 size_t size = 0; 553 uint8_t *data = concat_chunks(js, chunks, &size); 554 ant_value_t type_v = js_get(js, state, "type"); 555 const char *body_type = (vtype(type_v) == T_STR) ? js_getstr(js, type_v, NULL) : NULL; 556 resolve_body_promise(js, promise, data, size, body_type, mode, true); 557 free(data); 558 return js_mkundef(); 559 } 560 561 if (vtype(value) != T_UNDEF && vtype(value) != T_NULL) 562 js_arr_push(js, chunks, value); 563 564 stream_schedule_next_read(js, state, js_mkundef(), reader); 565 return js_mkundef(); 566} 567 568static ant_value_t consume_body_from_stream( 569 ant_t *js, ant_value_t stream, 570 ant_value_t promise, int mode, 571 const char *body_type 572) { 573 ant_value_t reader_args[1] = { stream }; 574 ant_value_t saved = js->new_target; 575 576 js->new_target = g_reader_proto; 577 ant_value_t reader = js_rs_reader_ctor(js, reader_args, 1); 578 js->new_target = saved; 579 580 if (is_err(reader)) { 581 js_reject_promise(js, promise, reader); 582 return promise; 583 } 584 585 ant_value_t state = js_mkobj(js); 586 js_set(js, state, "promise", promise); 587 js_set(js, state, "reader", reader); 588 js_set(js, state, "chunks", js_mkarr(js)); 589 js_set(js, state, "mode", js_mknum(mode)); 590 js_set(js, state, "type", body_type ? js_mkstr(js, body_type, strlen(body_type)) : js_mkundef()); 591 592 stream_schedule_next_read(js, state, js_mkundef(), reader); 593 return promise; 594} 595 596static ant_value_t consume_body(ant_t *js, int mode) { 597 ant_value_t this = js_getthis(js); 598 request_data_t *d = get_data(this); 599 ant_value_t promise = js_mkpromise(js); 600 601 if (!d) { 602 js_reject_promise(js, promise, request_rejection_reason(js, 603 js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Request object"))); 604 return promise; 605 } 606 607 if (!d->has_body) { 608 resolve_body_promise(js, promise, NULL, 0, request_effective_body_type(js, this, d), mode, false); 609 return promise; 610 } 611 612 if (d->body_used) { 613 js_reject_promise(js, promise, request_rejection_reason(js, 614 js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"))); 615 return promise; 616 } 617 618 d->body_used = true; 619 ant_value_t stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); 620 if (rs_is_stream(stream) && d->body_is_stream) 621 return consume_body_from_stream(js, stream, promise, mode, request_effective_body_type(js, this, d)); 622 resolve_body_promise(js, promise, d->body_data, d->body_size, request_effective_body_type(js, this, d), mode, true); 623 624 return promise; 625} 626 627static ant_value_t js_req_text(ant_t *js, ant_value_t *args, int nargs) { 628 return consume_body(js, BODY_TEXT); 629} 630 631static ant_value_t js_req_json(ant_t *js, ant_value_t *args, int nargs) { 632 return consume_body(js, BODY_JSON); 633} 634 635static ant_value_t js_req_array_buffer(ant_t *js, ant_value_t *args, int nargs) { 636 return consume_body(js, BODY_ARRAYBUFFER); 637} 638 639static ant_value_t js_req_blob(ant_t *js, ant_value_t *args, int nargs) { 640 return consume_body(js, BODY_BLOB); 641} 642 643static ant_value_t js_req_bytes(ant_t *js, ant_value_t *args, int nargs) { 644 return consume_body(js, BODY_BYTES); 645} 646 647static ant_value_t js_req_form_data(ant_t *js, ant_value_t *args, int nargs) { 648 return consume_body(js, BODY_FORMDATA); 649} 650 651static ant_value_t request_set_extracted_body( 652 ant_t *js, ant_value_t req_obj, ant_value_t headers, request_data_t *req, 653 uint8_t *body_data, size_t body_size, char *body_type, 654 ant_value_t body_stream, bool duplex_provided 655) { 656 free(req->body_data); 657 free(req->body_type); 658 659 req->body_data = body_data; 660 req->body_size = body_size; 661 req->body_type = body_type; 662 req->body_is_stream = rs_is_stream(body_stream); 663 req->has_body = true; 664 665 if (!req->body_is_stream) { 666 if (body_type && body_type[0]) headers_append_if_missing(headers, "content-type", body_type); 667 return js_mkundef(); 668 } 669 670 if (req->keepalive) { 671 return js_mkerr_typed(js, JS_ERR_TYPE, 672 "Failed to construct 'Request': keepalive cannot be used with a ReadableStream body"); 673 } 674 if (!duplex_provided) { 675 return js_mkerr_typed(js, JS_ERR_TYPE, 676 "Failed to construct 'Request': duplex must be provided for a ReadableStream body"); 677 } 678 679 js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, body_stream); 680 if (body_type && body_type[0]) headers_append_if_missing(headers, "content-type", body_type); 681 return js_mkundef(); 682} 683 684static void request_clear_body(ant_t *js, ant_value_t req_obj, request_data_t *req) { 685 free(req->body_data); 686 free(req->body_type); 687 req->body_data = NULL; 688 req->body_size = 0; 689 req->body_type = NULL; 690 req->body_is_stream = false; 691 req->has_body = false; 692 js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, js_mkundef()); 693} 694 695static 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) { 696 ant_value_t src_stream = js_get_slot(input, SLOT_REQUEST_BODY_STREAM); 697 698 if (src->body_used) { 699 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot construct Request with unusable body"); 700 } 701 702 if (rs_is_stream(src_stream) && rs_stream_unusable(src_stream)) { 703 return js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); 704 } 705 706 if (!src->body_is_stream) { 707 req->body_data = malloc(src->body_size); 708 if (src->body_size > 0 && !req->body_data) return js_mkerr(js, "out of memory"); 709 if (src->body_size > 0) memcpy(req->body_data, src->body_data, src->body_size); 710 req->body_size = src->body_size; 711 req->body_type = src->body_type ? strdup(src->body_type) : NULL; 712 req->body_is_stream = false; 713 req->has_body = true; 714 return js_mkundef(); 715 } 716 717 if (!rs_is_stream(src_stream)) return js_mkundef(); 718 ant_value_t branches = readable_stream_tee(js, src_stream); 719 720 if (is_err(branches)) return branches; 721 if (vtype(branches) != T_ARR) { 722 return js_mkerr_typed(js, JS_ERR_TYPE, 723 "Failed to construct 'Request': tee() did not return branches"); 724 } 725 726 js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, js_arr_get(js, branches, 1)); 727 req->body_is_stream = true; 728 req->has_body = true; 729 730 return js_mkundef(); 731} 732 733#define REQ_GETTER_START(name) \ 734 static ant_value_t js_req_get_##name(ant_t *js, ant_value_t *args, int nargs) { \ 735 ant_value_t this = js_getthis(js); \ 736 request_data_t *d = get_data(this); \ 737 if (!d) return js_mkundef(); 738 739#define REQ_GETTER_END } 740 741REQ_GETTER_START(method) 742 return js_mkstr(js, d->method, strlen(d->method)); 743REQ_GETTER_END 744 745REQ_GETTER_START(url) 746 char *href = build_href(&d->url); 747 if (!href) return js_mkstr(js, "", 0); 748 ant_value_t ret = js_mkstr(js, href, strlen(href)); 749 free(href); 750 return ret; 751REQ_GETTER_END 752 753REQ_GETTER_START(headers) 754 return js_get_slot(this, SLOT_REQUEST_HEADERS); 755REQ_GETTER_END 756 757REQ_GETTER_START(destination) 758 (void)d; 759 return js_mkstr(js, "", 0); 760REQ_GETTER_END 761 762REQ_GETTER_START(referrer) 763 if (!d->referrer || strcmp(d->referrer, "no-referrer") == 0) 764 return js_mkstr(js, "", 0); 765 if (strcmp(d->referrer, "client") == 0) 766 return js_mkstr(js, "about:client", 12); 767 return js_mkstr(js, d->referrer, strlen(d->referrer)); 768REQ_GETTER_END 769 770REQ_GETTER_START(referrer_policy) 771 const char *p = d->referrer_policy ? d->referrer_policy : ""; 772 return js_mkstr(js, p, strlen(p)); 773REQ_GETTER_END 774 775REQ_GETTER_START(mode) 776 return js_mkstr(js, d->mode, strlen(d->mode)); 777REQ_GETTER_END 778 779REQ_GETTER_START(credentials) 780 return js_mkstr(js, d->credentials, strlen(d->credentials)); 781REQ_GETTER_END 782 783REQ_GETTER_START(cache) 784 return js_mkstr(js, d->cache, strlen(d->cache)); 785REQ_GETTER_END 786 787REQ_GETTER_START(redirect) 788 return js_mkstr(js, d->redirect, strlen(d->redirect)); 789REQ_GETTER_END 790 791REQ_GETTER_START(integrity) 792 const char *ig = d->integrity ? d->integrity : ""; 793 return js_mkstr(js, ig, strlen(ig)); 794REQ_GETTER_END 795 796REQ_GETTER_START(keepalive) 797 return js_bool(d->keepalive); 798REQ_GETTER_END 799 800REQ_GETTER_START(is_reload_navigation) 801 return js_bool(d->reload_navigation); 802REQ_GETTER_END 803 804REQ_GETTER_START(is_history_navigation) 805 return js_bool(d->history_navigation); 806REQ_GETTER_END 807 808REQ_GETTER_START(signal) 809 return request_get_signal(js, this); 810REQ_GETTER_END 811 812REQ_GETTER_START(duplex) 813 return js_mkstr(js, "half", 4); 814REQ_GETTER_END 815 816static ant_value_t req_body_pull(ant_t *js, ant_value_t *args, int nargs) { 817 ant_value_t req_obj = js_get_slot(js->current_func, SLOT_DATA); 818 request_data_t *d = get_data(req_obj); 819 ant_value_t ctrl = (nargs > 0) ? args[0] : js_mkundef(); 820 821 if (d && d->body_data && d->body_size > 0) { 822 ArrayBufferData *ab = create_array_buffer_data(d->body_size); 823 if (ab) { 824 memcpy(ab->data, d->body_data, d->body_size); 825 rs_controller_enqueue(js, ctrl, 826 create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, d->body_size, "Uint8Array")); 827 }} 828 829 rs_controller_close(js, ctrl); 830 return js_mkundef(); 831} 832 833REQ_GETTER_START(body) 834 if (!d->has_body) return js_mknull(); 835 ant_value_t stored_stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); 836 if (rs_is_stream(stored_stream)) return stored_stream; 837 if (d->body_used) return js_mknull(); 838 ant_value_t pull = js_heavy_mkfun(js, req_body_pull, this); 839 ant_value_t stream = rs_create_stream(js, pull, js_mkundef(), 1.0); 840 if (!is_err(stream)) js_set_slot_wb(js, this, SLOT_REQUEST_BODY_STREAM, stream); 841 return stream; 842REQ_GETTER_END 843 844REQ_GETTER_START(body_used) 845 return js_bool(d->body_used); 846REQ_GETTER_END 847 848#undef REQ_GETTER_START 849#undef REQ_GETTER_END 850 851static ant_value_t request_inspect_finish(ant_t *js, ant_value_t this_obj, ant_value_t body_obj) { 852 ant_value_t tag_val = js_get_sym(js, this_obj, get_toStringTag_sym()); 853 const char *tag = vtype(tag_val) == T_STR ? js_getstr(js, tag_val, NULL) : "Request"; 854 855 js_inspect_builder_t builder; 856 if (!js_inspect_builder_init_dynamic(&builder, js, 128)) { 857 return js_mkerr(js, "out of memory"); 858 } 859 860 bool ok = js_inspect_header_for(&builder, body_obj, "%s", tag); 861 if (ok) ok = js_inspect_object_body(&builder, body_obj); 862 if (ok) ok = js_inspect_close(&builder); 863 864 if (!ok) { 865 js_inspect_builder_dispose(&builder); 866 return js_mkerr(js, "out of memory"); 867 } 868 869 return js_inspect_builder_result(&builder); 870} 871 872// TODO: make dry 873static bool request_inspect_set( 874 ant_t *js, ant_value_t obj, const char *key, 875 ant_value_t value, ant_value_t *err_out 876) { 877 if (is_err(value)) { 878 *err_out = value; 879 return false; 880 } 881 882 js_set(js, obj, key, value); 883 return true; 884} 885 886static ant_value_t request_inspect(ant_t *js, ant_value_t *args, int nargs) { 887 ant_value_t this_obj = js_getthis(js); 888 ant_value_t out = js_mkobj(js); 889 ant_value_t err = 0; 890 891 if (!request_inspect_set(js, out, "method", js_req_get_method(js, NULL, 0), &err)) return err; 892 if (!request_inspect_set(js, out, "url", js_req_get_url(js, NULL, 0), &err)) return err; 893 if (!request_inspect_set(js, out, "headers", js_req_get_headers(js, NULL, 0), &err)) return err; 894 if (!request_inspect_set(js, out, "destination", js_req_get_destination(js, NULL, 0), &err)) return err; 895 if (!request_inspect_set(js, out, "referrer", js_req_get_referrer(js, NULL, 0), &err)) return err; 896 if (!request_inspect_set(js, out, "referrerPolicy", js_req_get_referrer_policy(js, NULL, 0), &err)) return err; 897 if (!request_inspect_set(js, out, "mode", js_req_get_mode(js, NULL, 0), &err)) return err; 898 if (!request_inspect_set(js, out, "credentials", js_req_get_credentials(js, NULL, 0), &err)) return err; 899 if (!request_inspect_set(js, out, "cache", js_req_get_cache(js, NULL, 0), &err)) return err; 900 if (!request_inspect_set(js, out, "redirect", js_req_get_redirect(js, NULL, 0), &err)) return err; 901 if (!request_inspect_set(js, out, "integrity", js_req_get_integrity(js, NULL, 0), &err)) return err; 902 if (!request_inspect_set(js, out, "keepalive", js_req_get_keepalive(js, NULL, 0), &err)) return err; 903 if (!request_inspect_set(js, out, "isReloadNavigation", js_req_get_is_reload_navigation(js, NULL, 0), &err)) return err; 904 if (!request_inspect_set(js, out, "isHistoryNavigation", js_req_get_is_history_navigation(js, NULL, 0), &err)) return err; 905 if (!request_inspect_set(js, out, "signal", js_req_get_signal(js, NULL, 0), &err)) return err; 906 907 return request_inspect_finish(js, this_obj, out); 908} 909 910static ant_value_t js_request_clone(ant_t *js, ant_value_t *args, int nargs) { 911 ant_value_t this = js_getthis(js); 912 request_data_t *d = get_data(this); 913 914 if (!d) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Request object"); 915 if (d->body_used) 916 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot clone a Request whose body is unusable"); 917 918 request_data_t *nd = data_dup(d); 919 if (!nd) return js_mkerr(js, "out of memory"); 920 921 ant_value_t src_headers = js_get_slot(this, SLOT_REQUEST_HEADERS); 922 ant_value_t src_signal = request_get_signal(js, this); 923 924 ant_value_t new_headers = headers_create_empty(js); 925 if (is_err(new_headers)) { data_free(nd); return new_headers; } 926 927 headers_copy_from(js, new_headers, src_headers); 928 headers_set_guard(new_headers, 929 strcmp(nd->mode, "no-cors") == 0 930 ? HEADERS_GUARD_REQUEST_NO_CORS 931 : HEADERS_GUARD_REQUEST 932 ); 933 headers_apply_guard(new_headers); 934 935 ant_value_t new_signal = abort_signal_create_dependent(js, src_signal); 936 if (is_err(new_signal)) { data_free(nd); return new_signal; } 937 938 ant_value_t obj = js_mkobj(js); 939 js_set_proto_init(obj, g_request_proto); 940 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 941 js_set_slot(obj, SLOT_DATA, ANT_PTR(nd)); 942 943 js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, new_headers); 944 js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef()); 945 js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, new_signal); 946 947 ant_value_t src_stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); 948 if (rs_is_stream(src_stream)) { 949 ant_value_t branches = readable_stream_tee(js, src_stream); 950 if (!is_err(branches) && vtype(branches) == T_ARR) { 951 ant_value_t b1 = js_arr_get(js, branches, 0); 952 ant_value_t b2 = js_arr_get(js, branches, 1); 953 js_set_slot_wb(js, this, SLOT_REQUEST_BODY_STREAM, b1); 954 js_set_slot_wb(js, obj, SLOT_REQUEST_BODY_STREAM, b2); 955 }} 956 957 return obj; 958} 959 960static const char *init_str(ant_t *js, ant_value_t init, const char *key, size_t klen, ant_value_t *err_out) { 961 ant_value_t v = js_get(js, init, key); 962 if (vtype(v) == T_UNDEF) return NULL; 963 if (vtype(v) != T_STR) { 964 v = js_tostring_val(js, v); 965 if (is_err(v)) { *err_out = v; return NULL; } 966 } 967 return js_getstr(js, v, NULL); 968} 969 970static ant_value_t request_new_from_input( 971 ant_t *js, ant_value_t input, 972 request_data_t **out_req, request_data_t **out_src, 973 ant_value_t *out_input_signal 974) { 975 request_data_t *req = NULL; 976 request_data_t *src = NULL; 977 978 *out_req = NULL; 979 *out_src = NULL; 980 *out_input_signal = js_mkundef(); 981 982 if ( 983 vtype(input) == T_OBJ && 984 js_check_brand(input, BRAND_REQUEST) 985 ) src = get_data(input); 986 987 if (!src) { 988 size_t ulen = 0; 989 const char *url_str = NULL; 990 url_state_t parsed = {0}; 991 992 if (vtype(input) != T_STR) { 993 input = js_tostring_val(js, input); 994 if (is_err(input)) return input; 995 } 996 997 url_str = js_getstr(js, input, &ulen); 998 if (parse_url_to_state(url_str, NULL, &parsed) != 0) { 999 url_state_clear(&parsed); 1000 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid URL"); 1001 } 1002 1003 if ((parsed.username && parsed.username[0]) || (parsed.password && parsed.password[0])) { 1004 url_state_clear(&parsed); 1005 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': URL includes credentials"); 1006 } 1007 1008 req = data_new(); 1009 if (!req) { 1010 url_state_clear(&parsed); 1011 return js_mkerr(js, "out of memory"); 1012 } 1013 1014 req->url = parsed; 1015 } else { 1016 req = data_dup(src); 1017 if (!req) return js_mkerr(js, "out of memory"); 1018 req->body_used = false; 1019 *out_input_signal = request_get_signal(js, input); 1020 } 1021 1022 *out_req = req; 1023 *out_src = src; 1024 return js_mkundef(); 1025} 1026 1027static ant_value_t request_apply_init_options( 1028 ant_t *js, ant_value_t init, request_data_t *req, ant_value_t *input_signal 1029) { 1030 ant_value_t err = js_mkundef(); 1031 ant_value_t win = js_get(js, init, "window"); 1032 1033 const char *ref = NULL; 1034 const char *rp = NULL; 1035 const char *mode_val = NULL; 1036 const char *cred = NULL; 1037 const char *cache_val = NULL; 1038 const char *redir = NULL; 1039 const char *integ = NULL; 1040 const char *method_val = NULL; 1041 1042 if (vtype(win) != T_UNDEF && vtype(win) != T_NULL) { 1043 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': 'window' must be null"); 1044 } 1045 1046 if (strcmp(req->mode, "navigate") == 0) { 1047 free(req->mode); 1048 req->mode = strdup("same-origin"); 1049 } 1050 1051 req->reload_navigation = false; 1052 req->history_navigation = false; 1053 free(req->referrer); 1054 req->referrer = strdup("client"); 1055 free(req->referrer_policy); 1056 req->referrer_policy = strdup(""); 1057 1058 ref = init_str(js, init, "referrer", 8, &err); 1059 if (is_err(err)) return err; 1060 1061 if (ref) { 1062 if (ref[0] == '\0') { 1063 free(req->referrer); 1064 req->referrer = strdup("no-referrer"); 1065 } else { 1066 url_state_t rs = {0}; 1067 if (parse_url_to_state(ref, NULL, &rs) != 0) { 1068 url_state_clear(&rs); 1069 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid referrer URL"); 1070 } 1071 free(req->referrer); 1072 req->referrer = build_href(&rs); 1073 url_state_clear(&rs); 1074 if (!req->referrer) req->referrer = strdup("client"); 1075 }} 1076 1077 rp = init_str(js, init, "referrerPolicy", 14, &err); 1078 if (is_err(err)) return err; 1079 if (rp) { 1080 free(req->referrer_policy); 1081 req->referrer_policy = strdup(rp); 1082 } 1083 1084 mode_val = init_str(js, init, "mode", 4, &err); 1085 if (is_err(err)) return err; 1086 if (mode_val) { 1087 if (strcmp(mode_val, "navigate") == 0) { 1088 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': mode 'navigate' is not allowed"); 1089 } 1090 free(req->mode); 1091 req->mode = strdup(mode_val); 1092 } 1093 1094 cred = init_str(js, init, "credentials", 11, &err); 1095 if (is_err(err)) return err; 1096 if (cred) { 1097 free(req->credentials); 1098 req->credentials = strdup(cred); 1099 } 1100 1101 cache_val = init_str(js, init, "cache", 5, &err); 1102 if (is_err(err)) return err; 1103 1104 if (cache_val) { 1105 free(req->cache); 1106 req->cache = strdup(cache_val); 1107 if ( 1108 strcmp(req->cache, "only-if-cached") == 0 && 1109 strcmp(req->mode, "same-origin") != 0 1110 ) return js_mkerr_typed(js, JS_ERR_TYPE, 1111 "Failed to construct 'Request': cache mode 'only-if-cached' requires mode 'same-origin'"); 1112 } 1113 1114 redir = init_str(js, init, "redirect", 8, &err); 1115 if (is_err(err)) return err; 1116 1117 if (redir) { 1118 free(req->redirect); 1119 req->redirect = strdup(redir); 1120 } 1121 1122 integ = init_str(js, init, "integrity", 9, &err); 1123 if (is_err(err)) return err; 1124 1125 if (integ) { 1126 free(req->integrity); 1127 req->integrity = strdup(integ); 1128 } 1129 1130 ant_value_t ka = js_get(js, init, "keepalive"); 1131 if (vtype(ka) != T_UNDEF) req->keepalive = js_truthy(js, ka); 1132 1133 method_val = init_str(js, init, "method", 6, &err); 1134 if (is_err(err)) return err; 1135 1136 if (method_val) { 1137 if (!is_valid_method(method_val)) { 1138 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid method"); 1139 } 1140 if (is_forbidden_method(method_val)) { 1141 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Forbidden method"); 1142 } 1143 1144 free(req->method); 1145 req->method = strdup(method_val); 1146 normalize_method(req->method); 1147 } 1148 1149 ant_value_t sig_val = js_get(js, init, "signal"); 1150 if (vtype(sig_val) == T_UNDEF) return js_mkundef(); 1151 1152 if (vtype(sig_val) == T_NULL) { 1153 *input_signal = js_mkundef(); 1154 return js_mkundef(); 1155 } 1156 1157 if (abort_signal_is_signal(sig_val)) { 1158 *input_signal = sig_val; 1159 return js_mkundef(); 1160 } 1161 1162 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': signal must be an AbortSignal"); 1163} 1164 1165static ant_value_t request_create_ctor_headers(ant_t *js, ant_value_t input) { 1166 ant_value_t headers = headers_create_empty(js); 1167 if (is_err(headers)) return headers; 1168 if (vtype(input) != T_OBJ) return headers; 1169 1170 ant_value_t src_hdrs = js_get_slot(input, SLOT_REQUEST_HEADERS); 1171 headers_copy_from(js, headers, src_hdrs); 1172 return headers; 1173} 1174 1175static ant_value_t request_apply_init_headers(ant_t *js, ant_value_t init, ant_value_t headers) { 1176 ant_value_t init_headers = js_get(js, init, "headers"); 1177 if (vtype(init_headers) == T_UNDEF) return headers; 1178 return headers_create_from_init(js, init_headers); 1179} 1180 1181static ant_value_t request_parse_duplex(ant_t *js, ant_value_t init, bool *out_duplex_provided) { 1182 ant_value_t duplex_val = js_get(js, init, "duplex"); 1183 ant_value_t duplex_str_v = duplex_val; 1184 const char *duplex_str = NULL; 1185 1186 *out_duplex_provided = vtype(duplex_val) != T_UNDEF; 1187 if (!*out_duplex_provided) return js_mkundef(); 1188 1189 if (vtype(duplex_str_v) != T_STR) { 1190 duplex_str_v = js_tostring_val(js, duplex_str_v); 1191 if (is_err(duplex_str_v)) return duplex_str_v; 1192 } 1193 1194 duplex_str = js_getstr(js, duplex_str_v, NULL); 1195 if (duplex_str && strcmp(duplex_str, "half") == 0) return js_mkundef(); 1196 1197 return js_mkerr_typed(js, JS_ERR_TYPE, 1198 "Failed to construct 'Request': duplex must be 'half'"); 1199} 1200 1201static ant_value_t request_apply_ctor_body( 1202 ant_t *js, ant_value_t req_obj, ant_value_t input, ant_value_t init, 1203 bool init_provided, bool duplex_provided, 1204 request_data_t *req, request_data_t *src, ant_value_t headers 1205) { 1206 if (init_provided) { 1207 ant_value_t body_val = js_get(js, init, "body"); 1208 bool init_body_present = vtype(body_val) != T_UNDEF; 1209 bool input_body_present = src && src->has_body; 1210 bool effective_body_present = 1211 (init_body_present && vtype(body_val) != T_NULL) || 1212 (input_body_present && (!init_body_present || vtype(body_val) == T_NULL)); 1213 1214 if ((strcmp(req->method, "GET") == 0 || strcmp(req->method, "HEAD") == 0) && effective_body_present) { 1215 return js_mkerr_typed(js, JS_ERR_TYPE, 1216 "Failed to construct 'Request': Request with GET/HEAD method cannot have body"); 1217 } 1218 1219 if (vtype(body_val) == T_UNDEF) return js_mkundef(); 1220 if (vtype(body_val) == T_NULL) { 1221 request_clear_body(js, req_obj, req); 1222 return js_mkundef(); 1223 } 1224 1225 request_data_t *init_req = get_data(init); 1226 ant_value_t body_err = js_mkundef(); 1227 ant_value_t body_stream = js_mkundef(); 1228 1229 uint8_t *bd = NULL; 1230 size_t bs = 0; 1231 char *bt = NULL; 1232 1233 if (init_req && !init_req->body_used && !init_req->body_is_stream && init_req->has_body) { 1234 bd = malloc(init_req->body_size); 1235 if (init_req->body_size > 0 && !bd) return js_mkerr(js, "out of memory"); 1236 if (init_req->body_size > 0) memcpy(bd, init_req->body_data, init_req->body_size); 1237 bs = init_req->body_size; 1238 bt = init_req->body_type ? strdup(init_req->body_type) : NULL; 1239 } else if (!extract_body(js, body_val, &bd, &bs, &bt, &body_stream, &body_err)) 1240 return is_err(body_err) ? body_err : js_mkerr(js, "Failed to extract body"); 1241 1242 return request_set_extracted_body( 1243 js, req_obj, headers, req, bd, 1244 bs, bt, body_stream, duplex_provided 1245 ); 1246 } 1247 1248 if (!src) return js_mkundef(); 1249 return request_copy_source_body(js, req_obj, input, req, src); 1250} 1251 1252static ant_value_t js_request_ctor(ant_t *js, ant_value_t *args, int nargs) { 1253 ant_value_t init = (nargs >= 2 && vtype(args[1]) != T_UNDEF) ? args[1] : js_mkundef(); 1254 1255 if (vtype(js->new_target) == T_UNDEF) 1256 return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires 'new'"); 1257 if (nargs < 1) 1258 return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires at least 1 argument"); 1259 1260 ant_value_t input = args[0]; 1261 ant_value_t obj = 0; 1262 ant_value_t proto = 0; 1263 bool init_provided = false; 1264 1265 request_data_t *req = NULL; 1266 request_data_t *src = NULL; 1267 1268 ant_value_t input_signal = js_mkundef(); 1269 ant_value_t step = js_mkundef(); 1270 ant_value_t signal = 0; 1271 ant_value_t headers = 0; 1272 1273 bool duplex_provided = false; 1274 init_provided = (vtype(init) == T_OBJ || vtype(init) == T_ARR); 1275 1276 step = request_new_from_input(js, input, &req, &src, &input_signal); 1277 if (is_err(step)) return step; 1278 1279 if (init_provided) { 1280 step = request_apply_init_options(js, init, req, &input_signal); 1281 if (is_err(step)) { data_free(req); return step; } 1282 } 1283 1284 if ( 1285 strcmp(req->mode, "no-cors") == 0 && 1286 !is_cors_safelisted_method(req->method) 1287 ) { 1288 data_free(req); 1289 return js_mkerr_typed(js, JS_ERR_TYPE, 1290 "Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"); 1291 } 1292 1293 obj = js_mkobj(js); 1294 proto = js_instance_proto_from_new_target(js, g_request_proto); 1295 1296 if (is_object_type(proto)) js_set_proto_init(obj, proto); 1297 else js_set_proto_init(obj, g_request_proto); 1298 1299 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 1300 js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 1301 js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef()); 1302 1303 signal = abort_signal_create_dependent(js, input_signal); 1304 if (is_err(signal)) { data_free(req); return signal; } 1305 js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 1306 1307 headers = request_create_ctor_headers(js, input); 1308 if (is_err(headers)) { data_free(req); return headers; } 1309 1310 if (init_provided) { 1311 headers = request_apply_init_headers(js, init, headers); 1312 if (is_err(headers)) { data_free(req); return headers; } 1313 } 1314 1315 headers_set_guard(headers, 1316 strcmp(req->mode, "no-cors") == 0 1317 ? HEADERS_GUARD_REQUEST_NO_CORS 1318 : HEADERS_GUARD_REQUEST 1319 ); 1320 1321 headers_apply_guard(headers); 1322 js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, headers); 1323 1324 if (init_provided) { 1325 step = request_parse_duplex(js, init, &duplex_provided); 1326 if (is_err(step)) { data_free(req); return step; } 1327 } 1328 1329 step = request_apply_ctor_body( 1330 js, obj, input, init, init_provided, 1331 duplex_provided, req, src, headers 1332 ); 1333 1334 if (is_err(step)) { 1335 data_free(req); 1336 return step; 1337 } 1338 1339 if (src && src->has_body && !src->body_used) 1340 src->body_used = true; 1341 1342 return obj; 1343} 1344 1345ant_value_t request_create_from_input_init(ant_t *js, ant_value_t input, ant_value_t init) { 1346 bool init_provided = (vtype(init) == T_OBJ || vtype(init) == T_ARR); 1347 1348 request_data_t *req = NULL; 1349 request_data_t *src = NULL; 1350 1351 ant_value_t input_signal = js_mkundef(); 1352 ant_value_t step = js_mkundef(); 1353 ant_value_t obj = 0; 1354 ant_value_t signal = 0; 1355 ant_value_t headers = 0; 1356 1357 bool duplex_provided = false; 1358 step = request_new_from_input(js, input, &req, &src, &input_signal); 1359 if (is_err(step)) return step; 1360 1361 if (init_provided) { 1362 step = request_apply_init_options(js, init, req, &input_signal); 1363 if (is_err(step)) { data_free(req); return step; } 1364 } 1365 1366 if ( 1367 strcmp(req->mode, "no-cors") == 0 && 1368 !is_cors_safelisted_method(req->method) 1369 ) { 1370 data_free(req); 1371 return js_mkerr_typed(js, JS_ERR_TYPE, 1372 "Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"); 1373 } 1374 1375 obj = js_mkobj(js); 1376 js_set_proto_init(obj, g_request_proto); 1377 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 1378 js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 1379 js_set_slot(obj, SLOT_REQUEST_ABORT_REASON, js_mkundef()); 1380 1381 signal = abort_signal_create_dependent(js, input_signal); 1382 if (is_err(signal)) { data_free(req); return signal; } 1383 js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 1384 1385 headers = request_create_ctor_headers(js, input); 1386 if (is_err(headers)) { data_free(req); return headers; } 1387 1388 if (init_provided) { 1389 headers = request_apply_init_headers(js, init, headers); 1390 if (is_err(headers)) { data_free(req); return headers; } 1391 } 1392 1393 headers_set_guard(headers, 1394 strcmp(req->mode, "no-cors") == 0 1395 ? HEADERS_GUARD_REQUEST_NO_CORS 1396 : HEADERS_GUARD_REQUEST 1397 ); 1398 headers_apply_guard(headers); 1399 js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, headers); 1400 1401 if (init_provided) { 1402 step = request_parse_duplex(js, init, &duplex_provided); 1403 if (is_err(step)) { data_free(req); return step; } 1404 } 1405 1406 step = request_apply_ctor_body(js, obj, input, init, init_provided, duplex_provided, req, src, headers); 1407 if (is_err(step)) { 1408 data_free(req); 1409 return step; 1410 } 1411 1412 if (src && src->has_body && !src->body_used) 1413 src->body_used = true; 1414 1415 return obj; 1416} 1417 1418ant_value_t request_create(ant_t *js, 1419 const char *method, const char *url, 1420 ant_value_t headers_obj, const uint8_t *body, size_t body_len, 1421 const char *body_type) { 1422 request_data_t *req = data_new(); 1423 if (!req) return js_mkerr(js, "out of memory"); 1424 1425 free(req->method); 1426 req->method = strdup(method ? method : "GET"); 1427 free(req->mode); 1428 req->mode = strdup("same-origin"); 1429 1430 url_state_t parsed = {0}; 1431 if (url && parse_url_to_state(url, NULL, &parsed) == 0) req->url = parsed; 1432 else url_state_clear(&parsed); 1433 1434 if (body) req->has_body = true; 1435 1436 if (body && body_len > 0) { 1437 req->body_data = malloc(body_len); 1438 if (!req->body_data) { data_free(req); return js_mkerr(js, "out of memory"); } 1439 memcpy(req->body_data, body, body_len); 1440 req->body_size = body_len; 1441 req->body_type = body_type ? strdup(body_type) : NULL; 1442 } 1443 req->body_is_stream = false; 1444 return request_create_object(js, req, headers_obj, true); 1445} 1446 1447ant_value_t request_create_server( 1448 ant_t *js, 1449 const char *method, 1450 const char *target, 1451 bool absolute_target, 1452 const char *host, 1453 const char *server_hostname, 1454 int server_port, 1455 ant_value_t headers_obj, 1456 const uint8_t *body, 1457 size_t body_len, 1458 const char *body_type 1459) { 1460 request_data_t *req = data_new_server(method); 1461 if (!req) return js_mkerr(js, "out of memory"); 1462 1463 if (target && request_parse_server_url(target, absolute_target, host, server_hostname, server_port, &req->url) != 0) 1464 url_state_clear(&req->url); 1465 1466 if (body) req->has_body = true; 1467 1468 if (body && body_len > 0) { 1469 req->body_data = malloc(body_len); 1470 if (!req->body_data) { data_free(req); return js_mkerr(js, "out of memory"); } 1471 memcpy(req->body_data, body, body_len); 1472 req->body_size = body_len; 1473 req->body_type = body_type ? strdup(body_type) : NULL; 1474 } 1475 req->body_is_stream = false; 1476 1477 return request_create_object(js, req, headers_obj, false); 1478} 1479 1480void init_request_module(void) { 1481 ant_t *js = rt->js; 1482 ant_value_t g = js_glob(js); 1483 g_request_proto = js_mkobj(js); 1484 1485 js_set(js, g_request_proto, "text", js_mkfun(js_req_text)); 1486 js_set(js, g_request_proto, "json", js_mkfun(js_req_json)); 1487 js_set(js, g_request_proto, "arrayBuffer", js_mkfun(js_req_array_buffer)); 1488 js_set(js, g_request_proto, "blob", js_mkfun(js_req_blob)); 1489 js_set(js, g_request_proto, "formData", js_mkfun(js_req_form_data)); 1490 js_set(js, g_request_proto, "bytes", js_mkfun(js_req_bytes)); 1491 js_set(js, g_request_proto, "clone", js_mkfun(js_request_clone)); 1492 1493#define GETTER(prop, fn) \ 1494 js_set_getter_desc(js, g_request_proto, prop, sizeof(prop)-1, js_mkfun(js_req_get_##fn), JS_DESC_C) 1495 GETTER("method", method); 1496 GETTER("url", url); 1497 GETTER("headers", headers); 1498 GETTER("destination", destination); 1499 GETTER("referrer", referrer); 1500 GETTER("referrerPolicy", referrer_policy); 1501 GETTER("mode", mode); 1502 GETTER("credentials", credentials); 1503 GETTER("cache", cache); 1504 GETTER("redirect", redirect); 1505 GETTER("integrity", integrity); 1506 GETTER("keepalive", keepalive); 1507 GETTER("isReloadNavigation",is_reload_navigation); 1508 GETTER("isHistoryNavigation",is_history_navigation); 1509 GETTER("signal", signal); 1510 GETTER("duplex", duplex); 1511 GETTER("body", body); 1512 GETTER("bodyUsed", body_used); 1513#undef GETTER 1514 1515 js_set_sym(js, g_request_proto, get_inspect_sym(), js_mkfun(request_inspect)); 1516 js_set_sym(js, g_request_proto, get_toStringTag_sym(), js_mkstr(js, "Request", 7)); 1517 ant_value_t ctor = js_make_ctor(js, js_request_ctor, g_request_proto, "Request", 7); 1518 1519 js_set(js, g, "Request", ctor); 1520 js_set_descriptor(js, g, "Request", 7, JS_DESC_W | JS_DESC_C); 1521}