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.

migrate to buffer for multipart

+711 -477
+180 -147
src/modules/multipart.c
··· 14 14 #include "modules/multipart.h" 15 15 #include "modules/url.h" 16 16 17 + static ant_value_t multipart_invalid(ant_t *js) { 18 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 19 + } 20 + 17 21 static bool ct_is_type(const char *ct, const char *type) { 18 22 size_t type_len = 0; 19 23 ··· 232 236 return NULL; 233 237 } 234 238 239 + typedef struct { 240 + char *name; 241 + char *filename; 242 + char *content_type; 243 + } multipart_part_info_t; 244 + 245 + static void multipart_part_info_clear(multipart_part_info_t *info) { 246 + if (!info) return; 247 + free(info->name); 248 + free(info->filename); 249 + free(info->content_type); 250 + info->name = NULL; 251 + info->filename = NULL; 252 + info->content_type = NULL; 253 + } 254 + 255 + static bool multipart_parse_headers( 256 + ant_t *js, char *headers, multipart_part_info_t *info, ant_value_t *err_out 257 + ) { 258 + char *saveptr = NULL; 259 + 260 + for ( 261 + char *line = strtok_r(headers, "\r\n", &saveptr); 262 + line; line = strtok_r(NULL, "\r\n", &saveptr) 263 + ) { 264 + char *colon = strchr(line, ':'); 265 + char *value = NULL; 266 + 267 + if (!colon) continue; 268 + *colon = '\0'; 269 + 270 + value = colon + 1; 271 + while (*value == ' ' || *value == '\t') value++; 272 + 273 + if (strcasecmp(line, "Content-Disposition") == 0) { 274 + free(info->name); 275 + free(info->filename); 276 + 277 + info->name = ct_get_param_dup(value, "name"); 278 + info->filename = ct_get_param_dup(value, "filename"); 279 + 280 + continue; 281 + } 282 + 283 + if (strcasecmp(line, "Content-Type") == 0) { 284 + free(info->content_type); 285 + info->content_type = strdup(value); 286 + if (!info->content_type) { 287 + *err_out = js_mkerr(js, "out of memory"); 288 + return false; 289 + }} 290 + } 291 + 292 + if (info->name) return true; 293 + *err_out = multipart_invalid(js); 294 + 295 + return false; 296 + } 297 + 298 + static const uint8_t *multipart_find_part_end( 299 + const uint8_t *p, const uint8_t *end, const char *delim, size_t delim_len 300 + ) { 301 + char *marker = malloc(delim_len + 3); 302 + const uint8_t *part_end = NULL; 303 + if (!marker) return NULL; 304 + 305 + snprintf(marker, delim_len + 3, "\r\n%s", delim); 306 + part_end = find_bytes( 307 + p, (size_t)(end - p), 308 + (const uint8_t *)marker, delim_len + 2 309 + ); 310 + 311 + free(marker); 312 + return part_end; 313 + } 314 + 315 + static ant_value_t multipart_append_part( 316 + ant_t *js, ant_value_t fd, const multipart_part_info_t *info, 317 + const uint8_t *data, size_t size 318 + ) { 319 + if (info->filename) { 320 + ant_value_t blob = blob_create( 321 + js, data, size, info->content_type 322 + ? info->content_type : "" 323 + ); 324 + 325 + if (is_err(blob)) return blob; 326 + 327 + return formdata_append_file( 328 + js, fd, js_mkstr(js, info->name, strlen(info->name)), 329 + blob, js_mkstr(js, info->filename, strlen(info->filename)) 330 + ); 331 + } 332 + 333 + return formdata_append_string( 334 + js, fd, 335 + js_mkstr(js, info->name, strlen(info->name)), 336 + js_mkstr(js, data, size) 337 + ); 338 + } 339 + 235 340 static ant_value_t parse_formdata_multipart( 236 341 ant_t *js, const uint8_t *data, size_t size, const char *body_type 237 342 ) { ··· 243 348 const uint8_t *end = data + size; 244 349 size_t delim_len = 0; 245 350 246 - if (!data || size == 0) { 247 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 248 - } 249 - 351 + if (!data || size == 0) return multipart_invalid(js); 250 352 boundary = ct_get_param_dup(body_type, "boundary"); 251 353 if (!boundary || boundary[0] == '\0') goto invalid; 252 354 ··· 272 374 273 375 { 274 376 ant_value_t r = 0; 377 + ant_value_t hdr_err = js_mkundef(); 275 378 const uint8_t *hdr_end = find_bytes( 276 379 p, (size_t)(end - p), (const uint8_t *)"\r\n\r\n", 4 277 380 ); 278 381 279 382 char *headers = NULL; 280 - char *name = NULL; 281 - char *filename = NULL; 282 - char *part_type = NULL; 283 - char *marker = NULL; 383 + multipart_part_info_t info = {0}; 284 384 const uint8_t *part_end = NULL; 285 385 286 386 if (!hdr_end) goto invalid; ··· 292 392 } 293 393 p = hdr_end + 4; 294 394 295 - { 296 - char *saveptr = NULL; 297 - for (char *line = strtok_r(headers, "\r\n", &saveptr); 298 - line; 299 - line = strtok_r(NULL, "\r\n", &saveptr)) { 300 - char *colon = strchr(line, ':'); 301 - if (!colon) continue; 302 - *colon = '\0'; 303 - 304 - char *value = colon + 1; 305 - while (*value == ' ' || *value == '\t') value++; 306 - 307 - if (strcasecmp(line, "Content-Disposition") == 0) { 308 - char *name_pos = strcasestr(value, "name="); 309 - char *file_pos = strcasestr(value, "filename="); 310 - 311 - if (name_pos) { 312 - name_pos += 5; 313 - if (*name_pos == '"') { 314 - char *endq = NULL; 315 - name_pos++; 316 - endq = strchr(name_pos, '"'); 317 - if (endq) name = strndup(name_pos, (size_t)(endq - name_pos)); 318 - } 319 - } 320 - 321 - if (file_pos) { 322 - file_pos += 9; 323 - if (*file_pos == '"') { 324 - char *endq = NULL; 325 - file_pos++; 326 - endq = strchr(file_pos, '"'); 327 - if (endq) filename = strndup(file_pos, (size_t)(endq - file_pos)); 328 - } else { 329 - char *ende = file_pos; 330 - while (*ende && *ende != ';') ende++; 331 - filename = strndup(file_pos, (size_t)(ende - file_pos)); 332 - } 333 - } 334 - } else if (strcasecmp(line, "Content-Type") == 0) { 335 - part_type = strdup(value); 336 - } 337 - } 395 + if (!multipart_parse_headers(js, headers, &info, &hdr_err)) { 396 + free(headers); 397 + multipart_part_info_clear(&info); 398 + if (is_err(hdr_err)) { fd = hdr_err; goto done; } 399 + goto invalid; 338 400 } 339 401 free(headers); 340 - headers = NULL; 341 402 342 - if (!name) { 343 - free(filename); 344 - free(part_type); 345 - goto invalid; 346 - } 347 - 348 - marker = malloc(delim_len + 3); 349 - if (!marker) { 350 - free(name); 351 - free(filename); 352 - free(part_type); 353 - fd = js_mkerr(js, "out of memory"); 354 - goto done; 355 - } 356 - snprintf(marker, delim_len + 3, "\r\n%s", delim); 357 - part_end = find_bytes( 358 - p, (size_t)(end - p), (const uint8_t *)marker, delim_len + 2 359 - ); 360 - free(marker); 361 - marker = NULL; 362 - 403 + part_end = multipart_find_part_end(p, end, delim, delim_len); 363 404 if (!part_end) { 364 - free(name); 365 - free(filename); 366 - free(part_type); 405 + multipart_part_info_clear(&info); 367 406 goto invalid; 368 407 } 369 408 370 - if (filename) { 371 - ant_value_t blob = blob_create( 372 - js, p, (size_t)(part_end - p), part_type ? part_type : "" 373 - ); 374 - r = is_err(blob) 375 - ? blob 376 - : formdata_append_file( 377 - js, fd, 378 - js_mkstr(js, name, strlen(name)), 379 - blob, 380 - js_mkstr(js, filename, strlen(filename)) 381 - ); 382 - } else { 383 - r = formdata_append_string( 384 - js, fd, 385 - js_mkstr(js, name, strlen(name)), 386 - js_mkstr(js, p, (size_t)(part_end - p)) 387 - ); 388 - } 389 - 390 - free(name); 391 - free(filename); 392 - free(part_type); 393 - if (is_err(r)) { 394 - fd = r; 395 - goto done; 396 - } 409 + r = multipart_append_part(js, fd, &info, p, (size_t)(part_end - p)); 410 + multipart_part_info_clear(&info); 411 + if (is_err(r)) { fd = r; goto done; } 397 412 398 413 p = part_end + 2 + delim_len; 399 414 if ((size_t)(end - p) >= 2 && memcmp(p, "--", 2) == 0) goto done; ··· 402 417 goto next_part; 403 418 404 419 invalid: 405 - fd = js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 420 + fd = multipart_invalid(js); 406 421 407 422 done: 408 423 free(boundary); ··· 419 434 } 420 435 421 436 if (body_type && ct_is_type(body_type, "multipart/form-data")) { 422 - if (!has_body || !data || size == 0) { 423 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 424 - } 437 + if (!has_body || !data || size == 0) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 425 438 return parse_formdata_multipart(js, data, size, body_type); 426 439 } 427 440 ··· 461 474 return mp_append(b, s, strlen(s)); 462 475 } 463 476 477 + static bool mp_append_quoted(mp_buf_t *b, const char *s) { 478 + const char *p = s ? s : ""; 479 + if (!mp_append_str(b, "\"")) return false; 480 + 481 + while (*p) { 482 + if (*p == '"' || *p == '\\') if (!mp_append(b, "\\", 1)) return false; 483 + if (!mp_append(b, p, 1)) return false; 484 + p++; 485 + } 486 + 487 + return mp_append_str(b, "\""); 488 + } 489 + 490 + static bool mp_append_boundary(mp_buf_t *b, const char *boundary, bool closing) { 491 + if (!mp_append_str(b, "--")) return false; 492 + if (!mp_append_str(b, boundary)) return false; 493 + return mp_append_str(b, closing ? "--\r\n" : "\r\n"); 494 + } 495 + 496 + static bool mp_append_text_part(mp_buf_t *b, const fd_entry_t *e) { 497 + const char *val = e->str_value ? e->str_value : ""; 498 + 499 + if (!mp_append_str(b, "Content-Disposition: form-data; name=")) return false; 500 + if (!mp_append_quoted(b, e->name ? e->name : "")) return false; 501 + if (!mp_append_str(b, "\r\n\r\n")) return false; 502 + 503 + return mp_append_str(b, val); 504 + } 505 + 506 + static bool mp_append_file_part(ant_t *js, mp_buf_t *b, ant_value_t values_arr, const fd_entry_t *e) { 507 + ant_value_t file_val = js_arr_get(js, values_arr, (ant_offset_t)e->val_idx); 508 + blob_data_t *bd = blob_get_data(file_val); 509 + 510 + const char *filename = (bd && bd->name) ? bd->name : "blob"; 511 + const char *mime = (bd && bd->type && bd->type[0]) 512 + ? bd->type 513 + : "application/octet-stream"; 514 + 515 + if (!mp_append_str(b, "Content-Disposition: form-data; name=")) return false; 516 + if (!mp_append_quoted(b, e->name ? e->name : "")) return false; 517 + if (!mp_append_str(b, "; filename=")) return false; 518 + if (!mp_append_quoted(b, filename)) return false; 519 + if (!mp_append_str(b, "\r\nContent-Type: ")) return false; 520 + if (!mp_append_str(b, mime)) return false; 521 + if (!mp_append_str(b, "\r\n\r\n")) return false; 522 + if (!bd || !bd->data || bd->size == 0) return true; 523 + 524 + return mp_append(b, bd->data, bd->size); 525 + } 526 + 464 527 uint8_t *formdata_serialize_multipart( 465 528 ant_t *js, ant_value_t fd, size_t *out_size, char **out_boundary 466 529 ) { 467 530 ant_value_t values_arr = js_get_slot(fd, SLOT_ENTRIES); 468 531 ant_value_t data_slot = js_get_slot(fd, SLOT_DATA); 469 532 char boundary[49]; 533 + 470 534 mp_buf_t b = {NULL, 0, 0}; 471 535 fd_data_t *d = NULL; 536 + 472 537 if (vtype(data_slot) != T_NUM) return NULL; 473 538 d = (fd_data_t *)(uintptr_t)(size_t)js_getnum(data_slot); 474 539 if (!d) return NULL; ··· 481 546 if (d->count == 0) { 482 547 uint8_t *empty = malloc(1); 483 548 if (!empty) return NULL; 549 + 484 550 *out_size = 0; 485 551 *out_boundary = strdup(boundary); 552 + 486 553 if (!*out_boundary) { 487 554 free(empty); 488 555 return NULL; 489 556 } 557 + 490 558 return empty; 491 559 } 492 560 493 561 for (fd_entry_t *e = d->head; e; e = e->next) { 494 - if (!mp_append_str(&b, "--") || 495 - !mp_append_str(&b, boundary) || 496 - !mp_append_str(&b, "\r\n")) { 497 - goto oom; 498 - } 499 - 500 - if (!e->is_file) { 501 - char disp[512]; 502 - const char *val = e->str_value ? e->str_value : ""; 503 - 504 - snprintf( 505 - disp, sizeof(disp), 506 - "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", 507 - e->name ? e->name : "" 508 - ); 509 - if (!mp_append_str(&b, disp) || !mp_append_str(&b, val)) goto oom; 510 - } else { 511 - ant_value_t file_val = js_arr_get(js, values_arr, (ant_offset_t)e->val_idx); 512 - blob_data_t *bd = blob_get_data(file_val); 513 - const char *filename = (bd && bd->name) ? bd->name : "blob"; 514 - const char *mime = (bd && bd->type && bd->type[0]) 515 - ? bd->type 516 - : "application/octet-stream"; 517 - char disp[1024]; 518 - 519 - snprintf( 520 - disp, sizeof(disp), 521 - "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n" 522 - "Content-Type: %s\r\n\r\n", 523 - e->name ? e->name : "", filename, mime 524 - ); 525 - if (!mp_append_str(&b, disp)) goto oom; 526 - if (bd && bd->data && bd->size > 0 && !mp_append(&b, bd->data, bd->size)) goto oom; 527 - } 528 - 562 + if (!mp_append_boundary(&b, boundary, false)) goto oom; 563 + if (!e->is_file && !mp_append_text_part(&b, e)) goto oom; 564 + if (e->is_file && !mp_append_file_part(js, &b, values_arr, e)) goto oom; 529 565 if (!mp_append_str(&b, "\r\n")) goto oom; 530 566 } 531 567 532 - if (!mp_append_str(&b, "--") || 533 - !mp_append_str(&b, boundary) || 534 - !mp_append_str(&b, "--\r\n")) { 535 - goto oom; 536 - } 568 + if (!mp_append_boundary(&b, boundary, true)) goto oom; 537 569 538 570 *out_size = b.size; 539 571 *out_boundary = strdup(boundary); 572 + 540 573 if (!*out_boundary) goto oom; 541 574 return b.buf; 542 575
+531 -330
src/modules/request.c
··· 175 175 return d ? d->body_type : NULL; 176 176 } 177 177 178 - static bool extract_body( 179 - ant_t *js, ant_value_t body_val, 180 - uint8_t **out_data, size_t *out_size, char **out_type, 181 - ant_value_t *out_stream, ant_value_t *err_out 178 + static bool copy_body_bytes( 179 + ant_t *js, const uint8_t *src, size_t src_len, 180 + uint8_t **out_data, size_t *out_size, ant_value_t *err_out 182 181 ) { 183 - *out_data = NULL; 184 - *out_size = 0; 185 - *out_type = NULL; 186 - *out_stream = js_mkundef(); 187 - *err_out = js_mkundef(); 182 + uint8_t *buf = NULL; 183 + 184 + *out_data = NULL; 185 + *out_size = 0; 186 + if (src_len == 0) return true; 187 + 188 + buf = malloc(src_len); 189 + if (!buf) { 190 + *err_out = js_mkerr(js, "out of memory"); 191 + return false; 192 + } 188 193 189 - uint8_t t = vtype(body_val); 194 + memcpy(buf, src, src_len); 195 + *out_data = buf; 196 + *out_size = src_len; 197 + return true; 198 + } 199 + 200 + static bool extract_buffer_source_body( 201 + ant_t *js, ant_value_t body_val, 202 + uint8_t **out_data, size_t *out_size, ant_value_t *err_out 203 + ) { 190 204 const uint8_t *src = NULL; 191 205 size_t src_len = 0; 192 206 193 - if (t == T_NULL || t == T_UNDEF) return true; 207 + if (!(( 208 + vtype(body_val) == T_TYPEDARRAY || vtype(body_val) == T_OBJ) && 209 + buffer_source_get_bytes(js, body_val, &src, &src_len)) 210 + ) return false; 211 + 212 + return copy_body_bytes(js, src, src_len, out_data, out_size, err_out); 213 + } 194 214 195 - if ((t == T_TYPEDARRAY || t == T_OBJ) && buffer_source_get_bytes(js, body_val, &src, &src_len)) { 196 - if (src_len > 0) { 197 - uint8_t *buf = malloc(src_len); 198 - if (!buf) { *err_out = js_mkerr(js, "out of memory"); return false; } 199 - memcpy(buf, src, src_len); 200 - *out_data = buf; 201 - *out_size = src_len; 202 - } 203 - return true; 215 + static bool extract_stream_body( 216 + ant_t *js, ant_value_t body_val, 217 + ant_value_t *out_stream, ant_value_t *err_out 218 + ) { 219 + if (!rs_is_stream(body_val)) return false; 220 + if (rs_stream_unusable(body_val)) { 221 + *err_out = js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); 222 + return false; 204 223 } 205 224 206 - if (t == T_OBJ) { 207 - if (rs_is_stream(body_val)) { 208 - if (rs_stream_unusable(body_val)) { 209 - *err_out = js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); 210 - return false; 211 - } 212 - *out_stream = body_val; 213 - return true; 214 - } 225 + *out_stream = body_val; 226 + return true; 227 + } 215 228 216 - blob_data_t *bd = blob_is_blob(js, body_val) ? blob_get_data(body_val) : NULL; 217 - if (bd) { 218 - if (bd->size > 0) { 219 - uint8_t *buf = malloc(bd->size); 220 - if (!buf) { *err_out = js_mkerr(js, "out of memory"); return false; } 221 - memcpy(buf, bd->data, bd->size); 222 - *out_data = buf; 223 - *out_size = bd->size; 224 - } 225 - if (bd->type && bd->type[0]) *out_type = strdup(bd->type); 226 - return true; 227 - } 229 + static bool extract_blob_body( 230 + ant_t *js, ant_value_t body_val, 231 + uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out 232 + ) { 233 + blob_data_t *bd = blob_is_blob(js, body_val) ? blob_get_data(body_val) : NULL; 234 + if (!bd) return false; 228 235 229 - if (usp_is_urlsearchparams(js, body_val)) { 230 - char *serialized = usp_serialize(js, body_val); 231 - if (serialized) { 232 - *out_data = (uint8_t *)serialized; 233 - *out_size = strlen(serialized); 234 - *out_type = strdup("application/x-www-form-urlencoded;charset=UTF-8"); 235 - } 236 - return true; 237 - } 236 + if (!copy_body_bytes(js, bd->data, bd->size, out_data, out_size, err_out)) return false; 237 + if (bd->type && bd->type[0]) *out_type = strdup(bd->type); 238 + 239 + return true; 240 + } 238 241 239 - if (formdata_is_formdata(js, body_val)) { 240 - char *boundary = NULL; 241 - size_t mp_size = 0; 242 - uint8_t *mp = formdata_serialize_multipart(js, body_val, &mp_size, &boundary); 243 - 244 - if (!mp) { *err_out = js_mkerr(js, "out of memory"); return false; } 245 - if (mp_size > 0) *out_data = mp; 246 - else { free(mp); *out_data = NULL; } 247 - 248 - *out_size = mp_size; 249 - if (boundary) { 250 - char ct[256]; 251 - snprintf(ct, sizeof(ct), "multipart/form-data; boundary=%s", boundary); 252 - free(boundary); 253 - *out_type = strdup(ct); 254 - } 255 - 256 - return true; 242 + static bool extract_urlsearchparams_body( 243 + ant_t *js, ant_value_t body_val, 244 + uint8_t **out_data, size_t *out_size, char **out_type 245 + ) { 246 + char *serialized = NULL; 247 + if (!usp_is_urlsearchparams(js, body_val)) return false; 248 + 249 + serialized = usp_serialize(js, body_val); 250 + if (serialized) { 251 + *out_data = (uint8_t *)serialized; 252 + *out_size = strlen(serialized); 253 + *out_type = strdup("application/x-www-form-urlencoded;charset=UTF-8"); 254 + } 255 + 256 + return true; 257 + } 258 + 259 + static bool extract_formdata_body( 260 + ant_t *js, ant_value_t body_val, 261 + uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out 262 + ) { 263 + char *boundary = NULL; 264 + char *content_type = NULL; 265 + size_t mp_size = 0; 266 + uint8_t *mp = NULL; 267 + 268 + if (!formdata_is_formdata(js, body_val)) return false; 269 + mp = formdata_serialize_multipart(js, body_val, &mp_size, &boundary); 270 + 271 + if (!mp) { 272 + *err_out = js_mkerr(js, "out of memory"); 273 + return false; 274 + } 275 + 276 + if (mp_size > 0) *out_data = mp; 277 + else free(mp); 278 + 279 + *out_size = mp_size; 280 + if (boundary) { 281 + size_t ct_len = snprintf(NULL, 0, "multipart/form-data; boundary=%s", boundary); 282 + content_type = malloc(ct_len + 1); 283 + if (!content_type) { 284 + free(boundary); 285 + if (mp_size > 0) free(mp); 286 + *out_data = NULL; 287 + *out_size = 0; 288 + *err_out = js_mkerr(js, "out of memory"); 289 + return false; 257 290 } 291 + snprintf(content_type, ct_len + 1, "multipart/form-data; boundary=%s", boundary); 292 + free(boundary); 293 + *out_type = content_type; 258 294 } 259 295 260 - if (t != T_STR) { 296 + return true; 297 + } 298 + 299 + static bool extract_string_body( 300 + ant_t *js, ant_value_t body_val, 301 + uint8_t **out_data, size_t *out_size, char **out_type, ant_value_t *err_out 302 + ) { 303 + size_t len = 0; 304 + const char *s = NULL; 305 + 306 + if (vtype(body_val) != T_STR) { 261 307 body_val = js_tostring_val(js, body_val); 262 308 if (is_err(body_val)) { 263 309 *err_out = body_val; 264 310 return false; 265 311 }} 266 312 267 - size_t len; 268 - const char *s = js_getstr(js, body_val, &len); 313 + s = js_getstr(js, body_val, &len); 314 + if (!copy_body_bytes(js, (const uint8_t *)s, len, out_data, out_size, err_out)) return false; 315 + *out_type = strdup("text/plain;charset=UTF-8"); 269 316 270 - if (len > 0) { 271 - uint8_t *buf = malloc(len); 272 - if (!buf) { *err_out = js_mkerr(js, "out of memory"); return false; } 273 - memcpy(buf, s, len); 274 - *out_data = buf; 275 - *out_size = len; 276 - } 317 + return true; 318 + } 319 + 320 + static bool extract_body( 321 + ant_t *js, ant_value_t body_val, 322 + uint8_t **out_data, size_t *out_size, char **out_type, 323 + ant_value_t *out_stream, ant_value_t *err_out 324 + ) { 325 + *out_data = NULL; 326 + *out_size = 0; 327 + *out_type = NULL; 328 + *out_stream = js_mkundef(); 329 + *err_out = js_mkundef(); 330 + 331 + if (vtype(body_val) == T_NULL || vtype(body_val) == T_UNDEF) return true; 332 + if (extract_buffer_source_body(js, body_val, out_data, out_size, err_out)) return true; 333 + if (vtype(body_val) == T_OBJ && rs_is_stream(body_val)) return extract_stream_body(js, body_val, out_stream, err_out); 334 + if (vtype(body_val) == T_OBJ && extract_blob_body(js, body_val, out_data, out_size, out_type, err_out)) return true; 335 + if (vtype(body_val) == T_OBJ && extract_urlsearchparams_body(js, body_val, out_data, out_size, out_type)) return true; 336 + if (vtype(body_val) == T_OBJ && extract_formdata_body(js, body_val, out_data, out_size, out_type, err_out)) return true; 277 337 278 - *out_type = strdup("text/plain;charset=UTF-8"); 279 - return true; 338 + return extract_string_body(js, body_val, out_data, out_size, out_type, err_out); 280 339 } 281 340 282 341 enum { ··· 496 555 return consume_body(js, BODY_FORMDATA); 497 556 } 498 557 558 + static ant_value_t request_set_extracted_body( 559 + ant_t *js, ant_value_t req_obj, ant_value_t headers, request_data_t *req, 560 + uint8_t *body_data, size_t body_size, char *body_type, 561 + ant_value_t body_stream, bool duplex_provided 562 + ) { 563 + free(req->body_data); 564 + free(req->body_type); 565 + 566 + req->body_data = body_data; 567 + req->body_size = body_size; 568 + req->body_type = body_type; 569 + req->body_is_stream = rs_is_stream(body_stream); 570 + req->has_body = true; 571 + 572 + if (!req->body_is_stream) { 573 + if (body_type && body_type[0]) headers_append_if_missing(headers, "content-type", body_type); 574 + return js_mkundef(); 575 + } 576 + 577 + if (req->keepalive) { 578 + return js_mkerr_typed(js, JS_ERR_TYPE, 579 + "Failed to construct 'Request': keepalive cannot be used with a ReadableStream body"); 580 + } 581 + if (!duplex_provided) { 582 + return js_mkerr_typed(js, JS_ERR_TYPE, 583 + "Failed to construct 'Request': duplex must be provided for a ReadableStream body"); 584 + } 585 + 586 + js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, body_stream); 587 + if (body_type && body_type[0]) headers_append_if_missing(headers, "content-type", body_type); 588 + return js_mkundef(); 589 + } 590 + 591 + static void request_clear_body(ant_t *js, ant_value_t req_obj, request_data_t *req) { 592 + free(req->body_data); 593 + free(req->body_type); 594 + req->body_data = NULL; 595 + req->body_size = 0; 596 + req->body_type = NULL; 597 + req->body_is_stream = false; 598 + req->has_body = false; 599 + js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, js_mkundef()); 600 + } 601 + 602 + 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) { 603 + ant_value_t src_stream = js_get_slot(input, SLOT_REQUEST_BODY_STREAM); 604 + 605 + if (src->body_used) { 606 + return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot construct Request with unusable body"); 607 + } 608 + 609 + if (rs_is_stream(src_stream) && rs_stream_unusable(src_stream)) { 610 + return js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); 611 + } 612 + 613 + if (!src->body_is_stream) { 614 + req->body_data = malloc(src->body_size); 615 + if (src->body_size > 0 && !req->body_data) return js_mkerr(js, "out of memory"); 616 + if (src->body_size > 0) memcpy(req->body_data, src->body_data, src->body_size); 617 + req->body_size = src->body_size; 618 + req->body_type = src->body_type ? strdup(src->body_type) : NULL; 619 + req->body_is_stream = false; 620 + req->has_body = true; 621 + return js_mkundef(); 622 + } 623 + 624 + if (!rs_is_stream(src_stream)) return js_mkundef(); 625 + ant_value_t branches = readable_stream_tee(js, src_stream); 626 + 627 + if (is_err(branches)) return branches; 628 + if (vtype(branches) != T_ARR) { 629 + return js_mkerr_typed(js, JS_ERR_TYPE, 630 + "Failed to construct 'Request': tee() did not return branches"); 631 + } 632 + 633 + js_set_slot_wb(js, req_obj, SLOT_REQUEST_BODY_STREAM, js_arr_get(js, branches, 1)); 634 + req->body_is_stream = true; 635 + req->has_body = true; 636 + 637 + return js_mkundef(); 638 + } 639 + 499 640 #define REQ_GETTER_START(name) \ 500 641 static ant_value_t js_req_get_##name(ant_t *js, ant_value_t *args, int nargs) { \ 501 642 ant_value_t this = js_getthis(js); \ ··· 673 814 return js_getstr(js, v, NULL); 674 815 } 675 816 676 - static ant_value_t js_request_ctor(ant_t *js, ant_value_t *args, int nargs) { 677 - if (vtype(js->new_target) == T_UNDEF) 678 - return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires 'new'"); 679 - if (nargs < 1) 680 - return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires at least 1 argument"); 817 + static ant_value_t request_new_from_input( 818 + ant_t *js, ant_value_t input, 819 + request_data_t **out_req, request_data_t **out_src, 820 + ant_value_t *out_input_signal 821 + ) { 822 + request_data_t *req = NULL; 823 + request_data_t *src = NULL; 681 824 682 - ant_value_t input = args[0]; 683 - ant_value_t init = (nargs >= 2 && vtype(args[1]) != T_UNDEF) ? args[1] : js_mkundef(); 684 - bool init_provided = (vtype(init) == T_OBJ || vtype(init) == T_ARR); 825 + *out_req = NULL; 826 + *out_src = NULL; 827 + *out_input_signal = js_mkundef(); 685 828 686 - request_data_t *req = NULL; 687 - ant_value_t input_signal = js_mkundef(); 688 - 689 - request_data_t *src = NULL; 690 829 if (vtype(input) == T_OBJ) src = get_data(input); 691 830 692 831 if (!src) { 832 + size_t ulen = 0; 833 + const char *url_str = NULL; 834 + url_state_t parsed = {0}; 835 + 693 836 if (vtype(input) != T_STR) { 694 837 input = js_tostring_val(js, input); 695 838 if (is_err(input)) return input; 696 839 } 697 - size_t ulen; 698 - const char *url_str = js_getstr(js, input, &ulen); 699 - url_state_t parsed = {0}; 840 + 841 + url_str = js_getstr(js, input, &ulen); 700 842 if (parse_url_to_state(url_str, NULL, &parsed) != 0) { 701 843 url_state_clear(&parsed); 702 844 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid URL"); 703 845 } 846 + 704 847 if ((parsed.username && parsed.username[0]) || (parsed.password && parsed.password[0])) { 705 848 url_state_clear(&parsed); 706 849 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': URL includes credentials"); 707 850 } 851 + 708 852 req = data_new(); 709 - if (!req) { url_state_clear(&parsed); return js_mkerr(js, "out of memory"); } 853 + if (!req) { 854 + url_state_clear(&parsed); 855 + return js_mkerr(js, "out of memory"); 856 + } 857 + 710 858 req->url = parsed; 711 - } else if (vtype(input) == T_OBJ) { 859 + } else { 712 860 req = data_dup(src); 713 861 if (!req) return js_mkerr(js, "out of memory"); 714 862 req->body_used = false; 715 - input_signal = js_get_slot(input, SLOT_REQUEST_SIGNAL); 863 + *out_input_signal = js_get_slot(input, SLOT_REQUEST_SIGNAL); 716 864 } 717 865 866 + *out_req = req; 867 + *out_src = src; 868 + return js_mkundef(); 869 + } 870 + 871 + static ant_value_t request_apply_init_options( 872 + ant_t *js, ant_value_t init, request_data_t *req, ant_value_t *input_signal 873 + ) { 718 874 ant_value_t err = js_mkundef(); 875 + ant_value_t win = js_get(js, init, "window"); 876 + 877 + const char *ref = NULL; 878 + const char *rp = NULL; 879 + const char *mode_val = NULL; 880 + const char *cred = NULL; 881 + const char *cache_val = NULL; 882 + const char *redir = NULL; 883 + const char *integ = NULL; 884 + const char *method_val = NULL; 719 885 720 - if (init_provided) { 721 - ant_value_t win = js_get(js, init, "window"); 722 - if (vtype(win) != T_UNDEF && vtype(win) != T_NULL) { 723 - data_free(req); 724 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': 'window' must be null"); 725 - } 886 + if (vtype(win) != T_UNDEF && vtype(win) != T_NULL) { 887 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': 'window' must be null"); 888 + } 889 + 890 + if (strcmp(req->mode, "navigate") == 0) { 891 + free(req->mode); 892 + req->mode = strdup("same-origin"); 893 + } 894 + 895 + req->reload_navigation = false; 896 + req->history_navigation = false; 897 + free(req->referrer); 898 + req->referrer = strdup("client"); 899 + free(req->referrer_policy); 900 + req->referrer_policy = strdup(""); 726 901 727 - if (strcmp(req->mode, "navigate") == 0) { 728 - free(req->mode); req->mode = strdup("same-origin"); 902 + ref = init_str(js, init, "referrer", 8, &err); 903 + if (is_err(err)) return err; 904 + 905 + if (ref) { 906 + if (ref[0] == '\0') { 907 + free(req->referrer); 908 + req->referrer = strdup("no-referrer"); 909 + } else { 910 + url_state_t rs = {0}; 911 + if (parse_url_to_state(ref, NULL, &rs) != 0) { 912 + url_state_clear(&rs); 913 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid referrer URL"); 729 914 } 730 - req->reload_navigation = false; 731 - req->history_navigation = false; 732 - free(req->referrer); req->referrer = strdup("client"); 733 - free(req->referrer_policy); req->referrer_policy = strdup(""); 915 + free(req->referrer); 916 + req->referrer = build_href(&rs); 917 + url_state_clear(&rs); 918 + if (!req->referrer) req->referrer = strdup("client"); 919 + }} 734 920 735 - const char *ref = init_str(js, init, "referrer", 8, &err); 736 - if (is_err(err)) { data_free(req); return err; } 737 - 738 - if (ref) { 739 - if (ref[0] == '\0') { 740 - free(req->referrer); 741 - req->referrer = strdup("no-referrer"); 742 - } else { 743 - url_state_t rs = {0}; 744 - if (parse_url_to_state(ref, NULL, &rs) == 0) { 745 - char *href = build_href(&rs); 746 - url_state_clear(&rs); 747 - free(req->referrer); 748 - req->referrer = href ? href : strdup("client"); 749 - } else { 750 - url_state_clear(&rs); 751 - data_free(req); 752 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid referrer URL"); 753 - } 754 - }} 921 + rp = init_str(js, init, "referrerPolicy", 14, &err); 922 + if (is_err(err)) return err; 923 + if (rp) { 924 + free(req->referrer_policy); 925 + req->referrer_policy = strdup(rp); 926 + } 755 927 756 - const char *rp = init_str(js, init, "referrerPolicy", 14, &err); 757 - if (is_err(err)) { data_free(req); return err; } 758 - if (rp) { free(req->referrer_policy); req->referrer_policy = strdup(rp); } 759 - 760 - const char *mode_val = init_str(js, init, "mode", 4, &err); 761 - if (is_err(err)) { data_free(req); return err; } 762 - if (mode_val) { 763 - if (strcmp(mode_val, "navigate") == 0) { 764 - data_free(req); 765 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': mode 'navigate' is not allowed"); 766 - } 767 - free(req->mode); req->mode = strdup(mode_val); 928 + mode_val = init_str(js, init, "mode", 4, &err); 929 + if (is_err(err)) return err; 930 + if (mode_val) { 931 + if (strcmp(mode_val, "navigate") == 0) { 932 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': mode 'navigate' is not allowed"); 768 933 } 934 + free(req->mode); 935 + req->mode = strdup(mode_val); 936 + } 769 937 770 - const char *cred = init_str(js, init, "credentials", 11, &err); 771 - if (is_err(err)) { data_free(req); return err; } 772 - if (cred) { free(req->credentials); req->credentials = strdup(cred); } 938 + cred = init_str(js, init, "credentials", 11, &err); 939 + if (is_err(err)) return err; 940 + if (cred) { 941 + free(req->credentials); 942 + req->credentials = strdup(cred); 943 + } 773 944 774 - const char *cache_val = init_str(js, init, "cache", 5, &err); 775 - if (is_err(err)) { data_free(req); return err; } 776 - if (cache_val) { 777 - free(req->cache); req->cache = strdup(cache_val); 945 + cache_val = init_str(js, init, "cache", 5, &err); 946 + if (is_err(err)) return err; 947 + 948 + if (cache_val) { 949 + free(req->cache); 950 + req->cache = strdup(cache_val); 778 951 if ( 779 952 strcmp(req->cache, "only-if-cached") == 0 && 780 953 strcmp(req->mode, "same-origin") != 0 781 - ) { 782 - data_free(req); 783 - return js_mkerr_typed(js, JS_ERR_TYPE, 954 + ) return js_mkerr_typed(js, JS_ERR_TYPE, 784 955 "Failed to construct 'Request': cache mode 'only-if-cached' requires mode 'same-origin'"); 785 - }} 956 + } 957 + 958 + redir = init_str(js, init, "redirect", 8, &err); 959 + if (is_err(err)) return err; 960 + 961 + if (redir) { 962 + free(req->redirect); 963 + req->redirect = strdup(redir); 964 + } 786 965 787 - const char *redir = init_str(js, init, "redirect", 8, &err); 788 - if (is_err(err)) { data_free(req); return err; } 789 - if (redir) { free(req->redirect); req->redirect = strdup(redir); } 966 + integ = init_str(js, init, "integrity", 9, &err); 967 + if (is_err(err)) return err; 968 + 969 + if (integ) { 970 + free(req->integrity); 971 + req->integrity = strdup(integ); 972 + } 973 + 974 + ant_value_t ka = js_get(js, init, "keepalive"); 975 + if (vtype(ka) != T_UNDEF) req->keepalive = js_truthy(js, ka); 976 + 977 + method_val = init_str(js, init, "method", 6, &err); 978 + if (is_err(err)) return err; 979 + 980 + if (method_val) { 981 + if (!is_valid_method(method_val)) { 982 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid method"); 983 + } 984 + if (is_forbidden_method(method_val)) { 985 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Forbidden method"); 986 + } 987 + 988 + free(req->method); 989 + req->method = strdup(method_val); 990 + normalize_method(req->method); 991 + } 992 + 993 + ant_value_t sig_val = js_get(js, init, "signal"); 994 + if (vtype(sig_val) == T_UNDEF) return js_mkundef(); 995 + 996 + if (vtype(sig_val) == T_NULL) { 997 + *input_signal = js_mkundef(); 998 + return js_mkundef(); 999 + } 1000 + 1001 + if (abort_signal_is_signal(sig_val)) { 1002 + *input_signal = sig_val; 1003 + return js_mkundef(); 1004 + } 1005 + 1006 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': signal must be an AbortSignal"); 1007 + } 1008 + 1009 + static ant_value_t request_create_ctor_headers(ant_t *js, ant_value_t input) { 1010 + ant_value_t headers = headers_create_empty(js); 1011 + if (is_err(headers)) return headers; 1012 + if (vtype(input) != T_OBJ) return headers; 1013 + 1014 + ant_value_t src_hdrs = js_get_slot(input, SLOT_REQUEST_HEADERS); 1015 + headers_copy_from(js, headers, src_hdrs); 1016 + return headers; 1017 + } 1018 + 1019 + static ant_value_t request_apply_init_headers(ant_t *js, ant_value_t init, ant_value_t headers) { 1020 + ant_value_t init_headers = js_get(js, init, "headers"); 1021 + ant_value_t new_hdrs = 0; 1022 + 1023 + uint8_t ht = vtype(init_headers); 1024 + if (vtype(init_headers) == T_UNDEF) return headers; 790 1025 791 - const char *integ = init_str(js, init, "integrity", 9, &err); 792 - if (is_err(err)) { data_free(req); return err; } 793 - if (integ) { free(req->integrity); req->integrity = strdup(integ); } 1026 + new_hdrs = headers_create_empty(js); 1027 + if (is_err(new_hdrs)) return new_hdrs; 794 1028 795 - ant_value_t ka = js_get(js, init, "keepalive"); 796 - if (vtype(ka) != T_UNDEF) req->keepalive = js_truthy(js, ka); 1029 + if (headers_is_headers(init_headers)) { 1030 + headers_copy_from(js, new_hdrs, init_headers); 1031 + return new_hdrs; 1032 + } 797 1033 798 - const char *method_val = init_str(js, init, "method", 6, &err); 799 - if (is_err(err)) { data_free(req); return err; } 800 - if (method_val) { 801 - if (!is_valid_method(method_val)) { 802 - data_free(req); 803 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid method"); 804 - } 805 - if (is_forbidden_method(method_val)) { 806 - data_free(req); 807 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Forbidden method"); 808 - } 809 - free(req->method); 810 - req->method = strdup(method_val); 811 - normalize_method(req->method); 1034 + if (ht == T_ARR) { 1035 + ant_offset_t len = js_arr_len(js, init_headers); 1036 + for (ant_offset_t i = 0; i < len; i++) { 1037 + ant_value_t pair = js_arr_get(js, init_headers, i); 1038 + ant_value_t r = 0; 1039 + if (js_arr_len(js, pair) < 2) continue; 1040 + r = headers_append_value(js, new_hdrs, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); 1041 + if (is_err(r)) return r; 812 1042 } 1043 + return new_hdrs; 1044 + } 813 1045 814 - ant_value_t sig_val = js_get(js, init, "signal"); 815 - if (vtype(sig_val) != T_UNDEF) { 816 - if (vtype(sig_val) == T_NULL) input_signal = js_mkundef(); 817 - else if (abort_signal_is_signal(sig_val)) input_signal = sig_val; else { 818 - data_free(req); 819 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': signal must be an AbortSignal"); 1046 + if (ht == T_OBJ) { 1047 + ant_iter_t it = js_prop_iter_begin(js, init_headers); 1048 + const char *key = NULL; 1049 + 1050 + size_t key_len = 0; 1051 + ant_value_t val = 0; 1052 + 1053 + while (js_prop_iter_next(&it, &key, &key_len, &val)) { 1054 + ant_value_t r = headers_append_value(js, new_hdrs, js_mkstr(js, key, key_len), val); 1055 + if (is_err(r)) { 1056 + js_prop_iter_end(&it); 1057 + return r; 820 1058 }} 1059 + 1060 + js_prop_iter_end(&it); 1061 + } 1062 + 1063 + return new_hdrs; 1064 + } 1065 + 1066 + static ant_value_t request_parse_duplex(ant_t *js, ant_value_t init, bool *out_duplex_provided) { 1067 + ant_value_t duplex_val = js_get(js, init, "duplex"); 1068 + ant_value_t duplex_str_v = duplex_val; 1069 + const char *duplex_str = NULL; 1070 + 1071 + *out_duplex_provided = vtype(duplex_val) != T_UNDEF; 1072 + if (!*out_duplex_provided) return js_mkundef(); 1073 + 1074 + if (vtype(duplex_str_v) != T_STR) { 1075 + duplex_str_v = js_tostring_val(js, duplex_str_v); 1076 + if (is_err(duplex_str_v)) return duplex_str_v; 1077 + } 1078 + 1079 + duplex_str = js_getstr(js, duplex_str_v, NULL); 1080 + if (duplex_str && strcmp(duplex_str, "half") == 0) return js_mkundef(); 1081 + 1082 + return js_mkerr_typed(js, JS_ERR_TYPE, 1083 + "Failed to construct 'Request': duplex must be 'half'"); 1084 + } 1085 + 1086 + static ant_value_t request_apply_ctor_body( 1087 + ant_t *js, ant_value_t req_obj, ant_value_t input, ant_value_t init, 1088 + bool init_provided, bool duplex_provided, 1089 + request_data_t *req, request_data_t *src, ant_value_t headers 1090 + ) { 1091 + if (init_provided) { 1092 + ant_value_t body_val = js_get(js, init, "body"); 1093 + bool init_body_present = vtype(body_val) != T_UNDEF; 1094 + bool input_body_present = src && src->has_body; 1095 + bool effective_body_present = 1096 + (init_body_present && vtype(body_val) != T_NULL) || 1097 + (input_body_present && (!init_body_present || vtype(body_val) == T_NULL)); 1098 + 1099 + if ((strcmp(req->method, "GET") == 0 || strcmp(req->method, "HEAD") == 0) && effective_body_present) { 1100 + return js_mkerr_typed(js, JS_ERR_TYPE, 1101 + "Failed to construct 'Request': Request with GET/HEAD method cannot have body"); 1102 + } 1103 + 1104 + if (vtype(body_val) == T_UNDEF) return js_mkundef(); 1105 + if (vtype(body_val) == T_NULL) { 1106 + request_clear_body(js, req_obj, req); 1107 + return js_mkundef(); 1108 + } 1109 + 1110 + request_data_t *init_req = get_data(init); 1111 + ant_value_t body_err = js_mkundef(); 1112 + ant_value_t body_stream = js_mkundef(); 1113 + 1114 + uint8_t *bd = NULL; 1115 + size_t bs = 0; 1116 + char *bt = NULL; 1117 + 1118 + if (init_req && !init_req->body_used && !init_req->body_is_stream && init_req->has_body) { 1119 + bd = malloc(init_req->body_size); 1120 + if (init_req->body_size > 0 && !bd) return js_mkerr(js, "out of memory"); 1121 + if (init_req->body_size > 0) memcpy(bd, init_req->body_data, init_req->body_size); 1122 + bs = init_req->body_size; 1123 + bt = init_req->body_type ? strdup(init_req->body_type) : NULL; 1124 + } else if (!extract_body(js, body_val, &bd, &bs, &bt, &body_stream, &body_err)) 1125 + return is_err(body_err) ? body_err : js_mkerr(js, "Failed to extract body"); 1126 + 1127 + return request_set_extracted_body( 1128 + js, req_obj, headers, req, bd, 1129 + bs, bt, body_stream, duplex_provided 1130 + ); 1131 + } 1132 + 1133 + if (!src) return js_mkundef(); 1134 + return request_copy_source_body(js, req_obj, input, req, src); 1135 + } 1136 + 1137 + static ant_value_t js_request_ctor(ant_t *js, ant_value_t *args, int nargs) { 1138 + if (vtype(js->new_target) == T_UNDEF) 1139 + return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires 'new'"); 1140 + if (nargs < 1) 1141 + return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires at least 1 argument"); 1142 + 1143 + ant_value_t input = args[0]; 1144 + ant_value_t init = (nargs >= 2 && vtype(args[1]) != T_UNDEF) ? args[1] : js_mkundef(); 1145 + bool init_provided = (vtype(init) == T_OBJ || vtype(init) == T_ARR); 1146 + 1147 + request_data_t *req = NULL; 1148 + request_data_t *src = NULL; 1149 + 1150 + ant_value_t input_signal = js_mkundef(); 1151 + ant_value_t step = request_new_from_input(js, input, &req, &src, &input_signal); 1152 + 1153 + if (is_err(step)) return step; 1154 + 1155 + if (init_provided) { 1156 + step = request_apply_init_options(js, init, req, &input_signal); 1157 + if (is_err(step)) { data_free(req); return step; } 821 1158 } 822 1159 823 1160 if ( ··· 840 1177 if (is_err(signal)) { data_free(req); return signal; } 841 1178 js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 842 1179 843 - ant_value_t headers; 844 - if (vtype(input) == T_OBJ) { 845 - ant_value_t src_hdrs = js_get_slot(input, SLOT_REQUEST_HEADERS); 846 - headers = headers_create_empty(js); 847 - if (is_err(headers)) { data_free(req); return headers; } 848 - headers_copy_from(js, headers, src_hdrs); 849 - } else { 850 - headers = headers_create_empty(js); 1180 + ant_value_t headers = request_create_ctor_headers(js, input); 1181 + bool duplex_provided = false; 1182 + 1183 + if (is_err(headers)) { data_free(req); return headers; } 1184 + if (init_provided) { 1185 + headers = request_apply_init_headers(js, init, headers); 851 1186 if (is_err(headers)) { data_free(req); return headers; } 852 1187 } 853 1188 854 - if (init_provided) { 855 - ant_value_t init_headers = js_get(js, init, "headers"); 856 - 857 - if (vtype(init_headers) != T_UNDEF) { 858 - ant_value_t new_hdrs = headers_create_empty(js); 859 - if (is_err(new_hdrs)) { data_free(req); return new_hdrs; } 860 - 861 - uint8_t ht = vtype(init_headers); 862 - if (headers_is_headers(init_headers)) { 863 - headers_copy_from(js, new_hdrs, init_headers); 864 - } else if (ht == T_ARR) { 865 - ant_offset_t len = js_arr_len(js, init_headers); 866 - 867 - for (ant_offset_t i = 0; i < len; i++) { 868 - ant_value_t pair = js_arr_get(js, init_headers, i); 869 - if (js_arr_len(js, pair) < 2) continue; 870 - ant_value_t r = headers_append_value(js, new_hdrs, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); 871 - if (is_err(r)) { data_free(req); return r; } 872 - } 873 - } else if (ht == T_OBJ) { 874 - ant_iter_t it = js_prop_iter_begin(js, init_headers); 875 - const char *key; size_t key_len; ant_value_t val; 876 - 877 - while (js_prop_iter_next(&it, &key, &key_len, &val)) { 878 - ant_value_t r = headers_append_value(js, new_hdrs, js_mkstr(js, key, key_len), val); 879 - if (is_err(r)) { js_prop_iter_end(&it); data_free(req); return r; } 880 - } js_prop_iter_end(&it); 881 - } headers = new_hdrs; 882 - }} 883 - 884 1189 headers_set_guard(headers, 885 1190 strcmp(req->mode, "no-cors") == 0 886 1191 ? HEADERS_GUARD_REQUEST_NO_CORS ··· 890 1195 js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, headers); 891 1196 892 1197 if (init_provided) { 893 - ant_value_t duplex_val = js_get(js, init, "duplex"); 894 - bool duplex_provided = vtype(duplex_val) != T_UNDEF; 895 - if (duplex_provided) { 896 - ant_value_t duplex_str_v = duplex_val; 897 - if (vtype(duplex_str_v) != T_STR) { 898 - duplex_str_v = js_tostring_val(js, duplex_str_v); 899 - if (is_err(duplex_str_v)) { data_free(req); return duplex_str_v; } 900 - } 901 - const char *duplex_str = js_getstr(js, duplex_str_v, NULL); 902 - if (!duplex_str || strcmp(duplex_str, "half") != 0) { 903 - data_free(req); 904 - return js_mkerr_typed(js, JS_ERR_TYPE, 905 - "Failed to construct 'Request': duplex must be 'half'"); 906 - }} 1198 + step = request_parse_duplex(js, init, &duplex_provided); 1199 + if (is_err(step)) { data_free(req); return step; } 1200 + } 907 1201 908 - ant_value_t body_val = js_get(js, init, "body"); 909 - bool init_body_present = vtype(body_val) != T_UNDEF; 910 - bool input_body_present = src && src->has_body; 911 - bool effective_body_present = (init_body_present && vtype(body_val) != T_NULL) 912 - 913 - || (input_body_present && (!init_body_present || vtype(body_val) == T_NULL)); 914 - if ((strcmp(req->method, "GET") == 0 || strcmp(req->method, "HEAD") == 0) && 915 - effective_body_present) { 916 - data_free(req); 917 - return js_mkerr_typed(js, JS_ERR_TYPE, 918 - "Failed to construct 'Request': Request with GET/HEAD method cannot have body"); 919 - } 920 - if (vtype(body_val) != T_UNDEF) { 921 - if (vtype(body_val) != T_NULL) { 922 - request_data_t *init_req = get_data(init); 923 - ant_value_t body_err = js_mkundef(); 924 - uint8_t *bd = NULL; size_t bs = 0; char *bt = NULL; 925 - ant_value_t body_stream = js_mkundef(); 926 - if (init_req && !init_req->body_used && !init_req->body_is_stream && init_req->has_body) { 927 - bd = malloc(init_req->body_size); 928 - if (init_req->body_size > 0 && !bd) { 929 - data_free(req); 930 - return js_mkerr(js, "out of memory"); 931 - } 932 - if (init_req->body_size > 0) memcpy(bd, init_req->body_data, init_req->body_size); 933 - bs = init_req->body_size; 934 - bt = init_req->body_type ? strdup(init_req->body_type) : NULL; 935 - } else if (!extract_body(js, body_val, &bd, &bs, &bt, &body_stream, &body_err)) { 936 - data_free(req); 937 - return is_err(body_err) ? body_err : js_mkerr(js, "Failed to extract body"); 938 - } 939 - free(req->body_data); free(req->body_type); 940 - req->body_data = bd; 941 - req->body_size = bs; 942 - req->body_type = bt; 943 - req->body_is_stream = rs_is_stream(body_stream); 944 - req->has_body = true; 945 - if (rs_is_stream(body_stream)) { 946 - if (req->keepalive) { 947 - data_free(req); 948 - return js_mkerr_typed(js, JS_ERR_TYPE, 949 - "Failed to construct 'Request': keepalive cannot be used with a ReadableStream body"); 950 - } 951 - if (!duplex_provided) { 952 - data_free(req); 953 - return js_mkerr_typed(js, JS_ERR_TYPE, 954 - "Failed to construct 'Request': duplex must be provided for a ReadableStream body"); 955 - } 956 - js_set_slot_wb(js, obj, SLOT_REQUEST_BODY_STREAM, body_stream); 957 - } 958 - 959 - if (bt && bt[0]) 960 - headers_append_if_missing(headers, "content-type", bt); 961 - } else { 962 - free(req->body_data); req->body_data = NULL; 963 - req->body_size = 0; 964 - free(req->body_type); req->body_type = NULL; 965 - req->body_is_stream = false; 966 - req->has_body = false; 967 - js_set_slot_wb(js, obj, SLOT_REQUEST_BODY_STREAM, js_mkundef()); 968 - } 969 - } 970 - } else if (src) { 971 - if (src && src->body_used) { 972 - data_free(req); 973 - return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot construct Request with unusable body"); 974 - } 975 - ant_value_t src_stream = js_get_slot(input, SLOT_REQUEST_BODY_STREAM); 976 - if (rs_is_stream(src_stream) && rs_stream_unusable(src_stream)) { 977 - data_free(req); 978 - return js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); 979 - } 980 - if (!src->body_is_stream) { 981 - req->body_data = malloc(src->body_size); 982 - if (src->body_size > 0 && !req->body_data) { data_free(req); return js_mkerr(js, "out of memory"); } 983 - if (src->body_size > 0) memcpy(req->body_data, src->body_data, src->body_size); 984 - req->body_size = src->body_size; 985 - req->body_type = src->body_type ? strdup(src->body_type) : NULL; 986 - req->body_is_stream = false; 987 - req->has_body = true; 988 - } else if (rs_is_stream(src_stream)) { 989 - if (rs_stream_unusable(src_stream)) { 990 - data_free(req); 991 - return js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"); 992 - } 993 - ant_value_t branches = readable_stream_tee(js, src_stream); 994 - if (is_err(branches)) { data_free(req); return branches; } 995 - if (vtype(branches) != T_ARR) { 996 - data_free(req); 997 - return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': tee() did not return branches"); 998 - } 999 - ant_value_t b2 = js_arr_get(js, branches, 1); 1000 - js_set_slot_wb(js, obj, SLOT_REQUEST_BODY_STREAM, b2); 1001 - 1002 - req->body_is_stream = true; 1003 - req->has_body = true; 1004 - } 1202 + step = request_apply_ctor_body(js, obj, input, init, init_provided, duplex_provided, req, src, headers); 1203 + if (is_err(step)) { 1204 + data_free(req); 1205 + return step; 1005 1206 } 1006 1207 1007 1208 if (src && src->has_body && !src->body_used)