@scriptc/runtime 0.0.22 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/scr_array.c +65 -0
- package/src/scr_async.c +291 -7
- package/src/scr_bytes.c +634 -4
- package/src/scr_bytes_io.c +4 -2
- package/src/scr_cycle.c +403 -62
- package/src/scr_fetch.c +652 -77
- package/src/scr_file_handle.c +491 -0
- package/src/scr_island.c +10 -0
- package/src/scr_json.c +137 -21
- package/src/scr_lib.c +957 -48
- package/src/scr_number.c +9 -0
- package/src/scr_regex.c +12 -1
- package/src/scr_runtime.h +189 -19
- package/src/scr_string.c +13 -2
- package/src/scr_text_decoder_data.h +7389 -0
- package/src/scr_util.c +984 -0
- package/src/scr_win_stats.h +23 -0
package/src/scr_fetch.c
CHANGED
|
@@ -156,6 +156,10 @@ struct SfSignal {
|
|
|
156
156
|
bool aborted;
|
|
157
157
|
ScrDyn *reason;
|
|
158
158
|
ScrError *error_reason;
|
|
159
|
+
bool reason_self;
|
|
160
|
+
ScrDynHandleTag reason_self_tag;
|
|
161
|
+
bool reason_tracked;
|
|
162
|
+
SfSignal *reason_next;
|
|
159
163
|
ScrDyn *onabort;
|
|
160
164
|
size_t onabort_order;
|
|
161
165
|
SfAbortListener *listeners;
|
|
@@ -270,6 +274,7 @@ struct SfStream {
|
|
|
270
274
|
struct SfHeaders {
|
|
271
275
|
size_t rc;
|
|
272
276
|
ScrArr *pairs;
|
|
277
|
+
bool immutable;
|
|
273
278
|
};
|
|
274
279
|
|
|
275
280
|
struct SfResponse {
|
|
@@ -318,9 +323,10 @@ struct SfTransfer {
|
|
|
318
323
|
|
|
319
324
|
static SfTransfer *sf_live;
|
|
320
325
|
/* Weak registries: owners unlink themselves on destruction. They let the
|
|
321
|
-
* fetch teardown sever native→
|
|
322
|
-
* including
|
|
326
|
+
* fetch teardown sever opaque native→dyn edges before final cycle collection,
|
|
327
|
+
* including otherwise-untraceable callback/reason→handle backedges. */
|
|
323
328
|
static SfSignal *sf_callback_signals;
|
|
329
|
+
static SfSignal *sf_reason_signals;
|
|
324
330
|
static SfStream *sf_callback_streams;
|
|
325
331
|
|
|
326
332
|
static void sf_oom(void) {
|
|
@@ -428,6 +434,53 @@ static void sf_signal_untrack_callbacks(SfSignal *s) {
|
|
|
428
434
|
s->callbacks_next = NULL;
|
|
429
435
|
}
|
|
430
436
|
|
|
437
|
+
static void sf_signal_track_reason(SfSignal *s) {
|
|
438
|
+
if (s->reason_tracked) return;
|
|
439
|
+
s->reason_tracked = true;
|
|
440
|
+
s->reason_next = sf_reason_signals;
|
|
441
|
+
sf_reason_signals = s;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
static void sf_signal_untrack_reason(SfSignal *s) {
|
|
445
|
+
if (!s->reason_tracked) return;
|
|
446
|
+
for (SfSignal **at = &sf_reason_signals; *at;
|
|
447
|
+
at = &(*at)->reason_next) {
|
|
448
|
+
if (*at == s) {
|
|
449
|
+
*at = s->reason_next;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
s->reason_tracked = false;
|
|
454
|
+
s->reason_next = NULL;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/* A controller may abort with itself or its own signal as the reason.
|
|
458
|
+
* Those handles point back to this SfSignal, so retaining their ScrDyn box
|
|
459
|
+
* would create an opaque RC cycle. Remember the identity as tag + pointer
|
|
460
|
+
* instead and mint an equivalent handle whenever the reason is observed;
|
|
461
|
+
* checked-dynamic strict equality uses that same pair. Other reasons stay
|
|
462
|
+
* strongly owned and join the teardown registry so larger reason/handle
|
|
463
|
+
* cycles can be severed once no Web object remains observable. */
|
|
464
|
+
static ScrDyn *sf_signal_reason_ref(SfSignal *s) {
|
|
465
|
+
if (s->reason_self) {
|
|
466
|
+
return scr_dyn_new_handle(s, s->reason_self_tag);
|
|
467
|
+
}
|
|
468
|
+
return scr_dyn_retain(s->reason);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
static void sf_signal_drop_reason(SfSignal *s) {
|
|
472
|
+
ScrDyn *reason = s->reason;
|
|
473
|
+
ScrError *error_reason = s->error_reason;
|
|
474
|
+
sf_signal_untrack_reason(s);
|
|
475
|
+
s->reason = NULL;
|
|
476
|
+
s->error_reason = NULL;
|
|
477
|
+
s->reason_self = false;
|
|
478
|
+
scr_error_release(error_reason);
|
|
479
|
+
/* May release a handle that owns s and destroy it reentrantly. All s
|
|
480
|
+
* fields used by this cleanup are therefore cleared before this call. */
|
|
481
|
+
scr_dyn_release(reason);
|
|
482
|
+
}
|
|
483
|
+
|
|
431
484
|
static SfSignal *sf_signal_new(void) {
|
|
432
485
|
SfSignal *s = calloc(1, sizeof *s);
|
|
433
486
|
if (!s) sf_oom();
|
|
@@ -490,8 +543,7 @@ static void sf_signal_release(SfSignal *s) {
|
|
|
490
543
|
free(s->source_watches);
|
|
491
544
|
free(s->sources);
|
|
492
545
|
sf_signal_drop_callbacks(s);
|
|
493
|
-
|
|
494
|
-
scr_error_release(s->error_reason);
|
|
546
|
+
sf_signal_drop_reason(s);
|
|
495
547
|
free(s);
|
|
496
548
|
}
|
|
497
549
|
|
|
@@ -876,7 +928,16 @@ static void sf_signal_abort_full(SfSignal *s, ScrDyn *reason,
|
|
|
876
928
|
ScrError *error_reason) {
|
|
877
929
|
if (s->aborted) return;
|
|
878
930
|
s->aborted = true;
|
|
879
|
-
|
|
931
|
+
if (reason && reason->kind == SCR_DYN_HANDLE &&
|
|
932
|
+
reason->v.handle.ptr == s &&
|
|
933
|
+
(reason->v.handle.tag == SCR_DYNH_ABORT_SIGNAL ||
|
|
934
|
+
reason->v.handle.tag == SCR_DYNH_ABORT_CONTROLLER)) {
|
|
935
|
+
s->reason_self = true;
|
|
936
|
+
s->reason_self_tag = reason->v.handle.tag;
|
|
937
|
+
} else {
|
|
938
|
+
s->reason = scr_dyn_retain(reason);
|
|
939
|
+
sf_signal_track_reason(s);
|
|
940
|
+
}
|
|
880
941
|
s->error_reason = error_reason ? scr_error_retain(error_reason) : NULL;
|
|
881
942
|
while (s->watchers) {
|
|
882
943
|
SfSignalWatch *w = s->watchers;
|
|
@@ -975,7 +1036,9 @@ ScrDyn *scr_fetch_abort_now(ScrDyn *reason) {
|
|
|
975
1036
|
|
|
976
1037
|
static void sf_any_source_abort(SfSignalWatch *w, SfSignal *source) {
|
|
977
1038
|
SfSignal *out = (SfSignal *)w->owner;
|
|
978
|
-
|
|
1039
|
+
ScrDyn *reason = sf_signal_reason_ref(source);
|
|
1040
|
+
sf_signal_abort_full(out, reason, source->error_reason);
|
|
1041
|
+
scr_dyn_release(reason);
|
|
979
1042
|
}
|
|
980
1043
|
|
|
981
1044
|
ScrDyn *scr_fetch_abort_any(ScrDyn *signals) {
|
|
@@ -1004,7 +1067,9 @@ ScrDyn *scr_fetch_abort_any(ScrDyn *signals) {
|
|
|
1004
1067
|
}
|
|
1005
1068
|
out->sources[i] = sf_signal_retain(source);
|
|
1006
1069
|
if (source->aborted) {
|
|
1007
|
-
|
|
1070
|
+
ScrDyn *reason = sf_signal_reason_ref(source);
|
|
1071
|
+
sf_signal_abort_full(out, reason, source->error_reason);
|
|
1072
|
+
scr_dyn_release(reason);
|
|
1008
1073
|
} else {
|
|
1009
1074
|
out->source_watches[i] =
|
|
1010
1075
|
sf_signal_watch(source, out, &sf_any_source_abort);
|
|
@@ -1015,6 +1080,47 @@ ScrDyn *scr_fetch_abort_any(ScrDyn *signals) {
|
|
|
1015
1080
|
return boxed;
|
|
1016
1081
|
}
|
|
1017
1082
|
|
|
1083
|
+
/* AbortController is a distinct public handle over the same native signal
|
|
1084
|
+
* state fetch and AbortSignal.any observe. Reading `.signal` boxes that
|
|
1085
|
+
* state with the AbortSignal tag, so repeated reads preserve JS identity
|
|
1086
|
+
* through the checked-dynamic handle equality rule (tag + pointer). */
|
|
1087
|
+
ScrDyn *scr_fetch_abort_controller_new(void) {
|
|
1088
|
+
SfSignal *s = sf_signal_new();
|
|
1089
|
+
ScrDyn *out = scr_dyn_new_handle(s, SCR_DYNH_ABORT_CONTROLLER);
|
|
1090
|
+
sf_signal_release(s);
|
|
1091
|
+
return out;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
static ScrDyn *sf_abort_controller_invoke(
|
|
1095
|
+
void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args,
|
|
1096
|
+
size_t argc, const char *what) {
|
|
1097
|
+
SfSignal *s = ptr;
|
|
1098
|
+
if (strcmp(method, "abort") == 0) {
|
|
1099
|
+
if (!s->aborted) {
|
|
1100
|
+
ScrDyn *reason = argc > 0 ? args[0] : NULL;
|
|
1101
|
+
if (!reason || reason->kind == SCR_DYN_UNDEF) {
|
|
1102
|
+
sf_signal_abort_default(s, false);
|
|
1103
|
+
} else {
|
|
1104
|
+
ScrError *e = sf_is_error_dyn(reason) ? scr_error_from_dyn(reason) : NULL;
|
|
1105
|
+
sf_signal_abort_full(s, reason, e);
|
|
1106
|
+
scr_error_release(e);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return scr_dyn_retain(scr_dyn_undefined());
|
|
1110
|
+
}
|
|
1111
|
+
(void)self;
|
|
1112
|
+
sf_not_function(what);
|
|
1113
|
+
return NULL;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
static ScrDyn *sf_abort_controller_get(
|
|
1117
|
+
void *ptr, const char *key, size_t len) {
|
|
1118
|
+
if (sf_name(key, len, "signal")) {
|
|
1119
|
+
return scr_dyn_new_handle(ptr, SCR_DYNH_ABORT_SIGNAL);
|
|
1120
|
+
}
|
|
1121
|
+
return NULL;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1018
1124
|
static bool sf_signal_listener_options(
|
|
1019
1125
|
ScrDyn *const *args, size_t argc, bool *capture, bool *once,
|
|
1020
1126
|
SfSignal **option_signal) {
|
|
@@ -1078,7 +1184,9 @@ static ScrDyn *sf_signal_invoke(void *ptr, ScrDyn *self, const char *method,
|
|
|
1078
1184
|
SfSignal *s = ptr;
|
|
1079
1185
|
if (strcmp(method, "throwIfAborted") == 0) {
|
|
1080
1186
|
if (s->aborted) {
|
|
1081
|
-
|
|
1187
|
+
ScrDyn *reason = sf_signal_reason_ref(s);
|
|
1188
|
+
sf_throw_dyn_reason(reason);
|
|
1189
|
+
scr_dyn_release(reason);
|
|
1082
1190
|
return NULL;
|
|
1083
1191
|
}
|
|
1084
1192
|
return scr_dyn_retain(scr_dyn_undefined());
|
|
@@ -1199,7 +1307,7 @@ static ScrDyn *sf_signal_get(void *ptr, const char *key, size_t len) {
|
|
|
1199
1307
|
SfSignal *s = ptr;
|
|
1200
1308
|
if (sf_name(key, len, "aborted")) return scr_dyn_new_bool(s->aborted);
|
|
1201
1309
|
if (sf_name(key, len, "reason")) {
|
|
1202
|
-
return s->aborted ?
|
|
1310
|
+
return s->aborted ? sf_signal_reason_ref(s)
|
|
1203
1311
|
: scr_dyn_retain(scr_dyn_undefined());
|
|
1204
1312
|
}
|
|
1205
1313
|
if (sf_name(key, len, "onabort")) {
|
|
@@ -2627,6 +2735,18 @@ static ScrDyn *sf_controller_get(void *ptr, const char *key, size_t len) {
|
|
|
2627
2735
|
|
|
2628
2736
|
static bool sf_header_name_ok(const char *s, size_t len);
|
|
2629
2737
|
|
|
2738
|
+
static ScrStr *sf_header_name_lower(const char *data, size_t len) {
|
|
2739
|
+
char *lower = malloc(len ? len : 1);
|
|
2740
|
+
if (!lower) sf_oom();
|
|
2741
|
+
for (size_t i = 0; i < len; i++) {
|
|
2742
|
+
char c = data[i];
|
|
2743
|
+
lower[i] = c >= 'A' && c <= 'Z' ? (char)(c - 'A' + 'a') : c;
|
|
2744
|
+
}
|
|
2745
|
+
ScrStr *out = scr_str_new(lower, len);
|
|
2746
|
+
free(lower);
|
|
2747
|
+
return out;
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2630
2750
|
/* HTTP field values are ByteStrings: each wire octet becomes the same
|
|
2631
2751
|
* U+00XX code point in the JS string. ScrStr stores UTF-8, so obs-text
|
|
2632
2752
|
* bytes need a real Latin-1→UTF-8 expansion before they enter a Headers
|
|
@@ -2670,11 +2790,12 @@ static ScrStr *sf_location_to_utf8(const ScrStr *raw) {
|
|
|
2670
2790
|
return out;
|
|
2671
2791
|
}
|
|
2672
2792
|
|
|
2673
|
-
static SfHeaders *sf_headers_new(ScrArr *pairs) {
|
|
2793
|
+
static SfHeaders *sf_headers_new(ScrArr *pairs, bool immutable) {
|
|
2674
2794
|
SfHeaders *h = calloc(1, sizeof *h);
|
|
2675
2795
|
if (!h) sf_oom();
|
|
2676
2796
|
h->rc = 1;
|
|
2677
2797
|
h->pairs = pairs;
|
|
2798
|
+
h->immutable = immutable;
|
|
2678
2799
|
return h;
|
|
2679
2800
|
}
|
|
2680
2801
|
|
|
@@ -2682,13 +2803,16 @@ static SfHeaders *sf_headers_new_response(ScrArr *raw_pairs) {
|
|
|
2682
2803
|
size_t n = (size_t)scr_arr_len(raw_pairs);
|
|
2683
2804
|
ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, n);
|
|
2684
2805
|
for (size_t i = 0; i + 1 < n; i += 2) {
|
|
2685
|
-
|
|
2806
|
+
ScrStr *raw_name = scr_arr_get_ref(raw_pairs, (double)i);
|
|
2807
|
+
scr_arr_push_ref(
|
|
2808
|
+
pairs, sf_header_name_lower(raw_name->data, raw_name->len));
|
|
2809
|
+
scr_str_release(raw_name);
|
|
2686
2810
|
ScrStr *raw = scr_arr_get_ref(raw_pairs, (double)(i + 1));
|
|
2687
2811
|
scr_arr_push_ref(pairs, sf_latin1_to_utf8(raw));
|
|
2688
2812
|
scr_str_release(raw);
|
|
2689
2813
|
}
|
|
2690
2814
|
scr_arr_release(raw_pairs);
|
|
2691
|
-
return sf_headers_new(pairs);
|
|
2815
|
+
return sf_headers_new(pairs, true);
|
|
2692
2816
|
}
|
|
2693
2817
|
|
|
2694
2818
|
static SfHeaders *sf_headers_retain(SfHeaders *h) {
|
|
@@ -2710,24 +2834,38 @@ static void sf_headers_release_v(void *p) {
|
|
|
2710
2834
|
sf_headers_release((SfHeaders *)p);
|
|
2711
2835
|
}
|
|
2712
2836
|
|
|
2713
|
-
static
|
|
2837
|
+
static bool sf_header_value_ok(const ScrStr *value);
|
|
2838
|
+
static ScrStr *sf_header_value_bytestring(ScrStr *value);
|
|
2839
|
+
static ScrStr *sf_trim_header_value(const ScrStr *value);
|
|
2840
|
+
|
|
2841
|
+
static void sf_headers_invalid(
|
|
2842
|
+
const char *method, const ScrStr *value, const char *kind) {
|
|
2843
|
+
size_t cap = strlen(method) + value->len + strlen(kind) + 48;
|
|
2844
|
+
char *message = malloc(cap);
|
|
2845
|
+
if (!message) sf_oom();
|
|
2846
|
+
int head = snprintf(message, cap, "Headers.%s: \"", method);
|
|
2847
|
+
if (head < 0 || (size_t)head >= cap) sf_oom();
|
|
2848
|
+
size_t at = (size_t)head;
|
|
2849
|
+
memcpy(message + at, value->data, value->len);
|
|
2850
|
+
at += value->len;
|
|
2851
|
+
int tail = snprintf(
|
|
2852
|
+
message + at, cap - at,
|
|
2853
|
+
"\" is an invalid header %s.", kind);
|
|
2854
|
+
if (tail < 0 || (size_t)tail >= cap - at) sf_oom();
|
|
2855
|
+
scr_throw_error_msg(SCR_ERR_TYPE, message, at + (size_t)tail);
|
|
2856
|
+
free(message);
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
static ScrStr *sf_headers_name(const ScrDyn *value, const char *method) {
|
|
2714
2860
|
ScrStr *raw =
|
|
2715
2861
|
scr_dyn_string_coerce_js(value ? value : scr_dyn_undefined());
|
|
2716
2862
|
if (!raw) return NULL;
|
|
2717
2863
|
if (!sf_header_name_ok(raw->data, raw->len)) {
|
|
2864
|
+
sf_headers_invalid(method, raw, "name");
|
|
2718
2865
|
scr_str_release(raw);
|
|
2719
|
-
sf_type_error("Headers: invalid header name");
|
|
2720
2866
|
return NULL;
|
|
2721
2867
|
}
|
|
2722
|
-
|
|
2723
|
-
if (!lower) sf_oom();
|
|
2724
|
-
for (size_t i = 0; i < raw->len; i++) {
|
|
2725
|
-
char c = raw->data[i];
|
|
2726
|
-
lower[i] =
|
|
2727
|
-
c >= 'A' && c <= 'Z' ? (char)(c - 'A' + 'a') : c;
|
|
2728
|
-
}
|
|
2729
|
-
ScrStr *out = scr_str_new(lower, raw->len);
|
|
2730
|
-
free(lower);
|
|
2868
|
+
ScrStr *out = sf_header_name_lower(raw->data, raw->len);
|
|
2731
2869
|
scr_str_release(raw);
|
|
2732
2870
|
return out;
|
|
2733
2871
|
}
|
|
@@ -2778,6 +2916,92 @@ static ScrStr *sf_headers_get_value(SfHeaders *h, const ScrStr *name) {
|
|
|
2778
2916
|
return out;
|
|
2779
2917
|
}
|
|
2780
2918
|
|
|
2919
|
+
static void sf_headers_delete_name(SfHeaders *h, const ScrStr *name) {
|
|
2920
|
+
ScrArr *old = h->pairs;
|
|
2921
|
+
size_t n = (size_t)scr_arr_len(old);
|
|
2922
|
+
ScrArr *next = scr_arr_new(SCR_ELEM_STR, n);
|
|
2923
|
+
for (size_t i = 0; i + 1 < n; i += 2) {
|
|
2924
|
+
ScrStr *key = scr_arr_get_ref(old, (double)i);
|
|
2925
|
+
ScrStr *value = scr_arr_get_ref(old, (double)(i + 1));
|
|
2926
|
+
if (sf_headers_key_eq(key, name)) {
|
|
2927
|
+
scr_str_release(key);
|
|
2928
|
+
scr_str_release(value);
|
|
2929
|
+
continue;
|
|
2930
|
+
}
|
|
2931
|
+
scr_arr_push_ref(next, key);
|
|
2932
|
+
scr_arr_push_ref(next, value);
|
|
2933
|
+
}
|
|
2934
|
+
h->pairs = next;
|
|
2935
|
+
scr_arr_release(old);
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
static bool sf_headers_convert_pair(
|
|
2939
|
+
const ScrDyn *name_value, const ScrDyn *value_value,
|
|
2940
|
+
const char *method, ScrStr **name_out, ScrStr **value_out) {
|
|
2941
|
+
ScrStr *name = sf_headers_name(name_value, method);
|
|
2942
|
+
if (!name) return false;
|
|
2943
|
+
ScrStr *raw = scr_dyn_string_coerce_js(
|
|
2944
|
+
value_value ? value_value : scr_dyn_undefined());
|
|
2945
|
+
ScrStr *bytes = raw ? sf_header_value_bytestring(raw) : NULL;
|
|
2946
|
+
scr_str_release(raw);
|
|
2947
|
+
if (!bytes) {
|
|
2948
|
+
scr_str_release(name);
|
|
2949
|
+
return false;
|
|
2950
|
+
}
|
|
2951
|
+
if (!sf_header_value_ok(bytes)) {
|
|
2952
|
+
ScrStr *display = sf_latin1_to_utf8(bytes);
|
|
2953
|
+
sf_headers_invalid(method, display, "value");
|
|
2954
|
+
scr_str_release(display);
|
|
2955
|
+
scr_str_release(bytes);
|
|
2956
|
+
scr_str_release(name);
|
|
2957
|
+
return false;
|
|
2958
|
+
}
|
|
2959
|
+
ScrStr *trimmed = sf_trim_header_value(bytes);
|
|
2960
|
+
scr_str_release(bytes);
|
|
2961
|
+
ScrStr *normalized = sf_latin1_to_utf8(trimmed);
|
|
2962
|
+
scr_str_release(trimmed);
|
|
2963
|
+
*name_out = name;
|
|
2964
|
+
*value_out = normalized;
|
|
2965
|
+
return true;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
/* Consumes a normalized lowercase name and UTF-8 value. Headers stores one
|
|
2969
|
+
* joined value for ordinary fields, separate values for Set-Cookie, and the
|
|
2970
|
+
* Fetch-specific semicolon separator for Cookie. */
|
|
2971
|
+
static void sf_headers_append_pair(
|
|
2972
|
+
SfHeaders *h, ScrStr *name, ScrStr *value) {
|
|
2973
|
+
if (!(name->len == 10 && memcmp(name->data, "set-cookie", 10) == 0)) {
|
|
2974
|
+
size_t n = (size_t)scr_arr_len(h->pairs);
|
|
2975
|
+
for (size_t i = 0; i + 1 < n; i += 2) {
|
|
2976
|
+
ScrStr *key = scr_arr_get_ref(h->pairs, (double)i);
|
|
2977
|
+
bool match = sf_headers_key_eq(key, name);
|
|
2978
|
+
scr_str_release(key);
|
|
2979
|
+
if (!match) continue;
|
|
2980
|
+
|
|
2981
|
+
ScrStr *previous = scr_arr_get_ref(h->pairs, (double)(i + 1));
|
|
2982
|
+
const char *separator =
|
|
2983
|
+
name->len == 6 && memcmp(name->data, "cookie", 6) == 0
|
|
2984
|
+
? "; "
|
|
2985
|
+
: ", ";
|
|
2986
|
+
size_t joined_len = previous->len + 2 + value->len;
|
|
2987
|
+
char *joined_data = malloc(joined_len ? joined_len : 1);
|
|
2988
|
+
if (!joined_data) sf_oom();
|
|
2989
|
+
memcpy(joined_data, previous->data, previous->len);
|
|
2990
|
+
memcpy(joined_data + previous->len, separator, 2);
|
|
2991
|
+
memcpy(joined_data + previous->len + 2, value->data, value->len);
|
|
2992
|
+
ScrStr *joined = scr_str_new(joined_data, joined_len);
|
|
2993
|
+
free(joined_data);
|
|
2994
|
+
scr_str_release(previous);
|
|
2995
|
+
scr_str_release(name);
|
|
2996
|
+
scr_str_release(value);
|
|
2997
|
+
scr_arr_set_ref(h->pairs, (double)(i + 1), joined);
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
scr_arr_push_ref(h->pairs, name);
|
|
3002
|
+
scr_arr_push_ref(h->pairs, value);
|
|
3003
|
+
}
|
|
3004
|
+
|
|
2781
3005
|
static int sf_headers_name_cmp(const void *left, const void *right) {
|
|
2782
3006
|
const ScrStr *a = *(ScrStr *const *)left;
|
|
2783
3007
|
const ScrStr *b = *(ScrStr *const *)right;
|
|
@@ -2822,7 +3046,7 @@ static ScrDyn *sf_headers_invoke(void *ptr, ScrDyn *self,
|
|
|
2822
3046
|
sf_type_error("Headers.get/has requires a header name");
|
|
2823
3047
|
return NULL;
|
|
2824
3048
|
}
|
|
2825
|
-
ScrStr *name = sf_headers_name(args[0]);
|
|
3049
|
+
ScrStr *name = sf_headers_name(args[0], method);
|
|
2826
3050
|
if (!name) return NULL;
|
|
2827
3051
|
ScrStr *value = sf_headers_get_value(h, name);
|
|
2828
3052
|
scr_str_release(name);
|
|
@@ -2859,12 +3083,25 @@ static ScrDyn *sf_headers_invoke(void *ptr, ScrDyn *self,
|
|
|
2859
3083
|
ScrDyn *callback = args[0];
|
|
2860
3084
|
const ScrDyn *this_arg =
|
|
2861
3085
|
argc > 1 ? args[1] : scr_dyn_undefined();
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
3086
|
+
/* Headers iteration is live. Re-sort the current list for each numeric
|
|
3087
|
+
* index so callback mutations have the same effects as Node: deleting
|
|
3088
|
+
* a future name skips it, inserting a later name visits it, and
|
|
3089
|
+
* inserting before the current index can make the current name appear
|
|
3090
|
+
* again. The old one-time snapshot was only safe while every native
|
|
3091
|
+
* Headers handle was immutable. */
|
|
3092
|
+
for (size_t i = 0;; i++) {
|
|
3093
|
+
size_t count = 0;
|
|
3094
|
+
ScrStr **names = sf_headers_sorted_names(h, &count);
|
|
3095
|
+
if (i >= count) {
|
|
3096
|
+
for (size_t j = 0; j < count; j++) scr_str_release(names[j]);
|
|
3097
|
+
free(names);
|
|
3098
|
+
break;
|
|
3099
|
+
}
|
|
2865
3100
|
ScrStr *value = sf_headers_get_value(h, names[i]);
|
|
2866
3101
|
ScrDyn *boxed_value = scr_dyn_new_str(value);
|
|
2867
3102
|
ScrDyn *boxed_name = scr_dyn_new_str(names[i]);
|
|
3103
|
+
for (size_t j = 0; j < count; j++) scr_str_release(names[j]);
|
|
3104
|
+
free(names);
|
|
2868
3105
|
ScrDyn *call_args[3] = {boxed_value, boxed_name, self};
|
|
2869
3106
|
scr_dyn_this_push_dyn(this_arg);
|
|
2870
3107
|
ScrDyn *result =
|
|
@@ -2873,17 +3110,11 @@ static ScrDyn *sf_headers_invoke(void *ptr, ScrDyn *self,
|
|
|
2873
3110
|
scr_dyn_release(boxed_value);
|
|
2874
3111
|
scr_dyn_release(boxed_name);
|
|
2875
3112
|
scr_str_release(value);
|
|
2876
|
-
scr_str_release(names[i]);
|
|
2877
3113
|
if (!result) {
|
|
2878
|
-
for (size_t j = i + 1; j < count; j++) {
|
|
2879
|
-
scr_str_release(names[j]);
|
|
2880
|
-
}
|
|
2881
|
-
free(names);
|
|
2882
3114
|
return NULL;
|
|
2883
3115
|
}
|
|
2884
3116
|
scr_dyn_release(result);
|
|
2885
3117
|
}
|
|
2886
|
-
free(names);
|
|
2887
3118
|
return scr_dyn_retain(scr_dyn_undefined());
|
|
2888
3119
|
}
|
|
2889
3120
|
if (strcmp(method, "append") == 0 || strcmp(method, "delete") == 0 ||
|
|
@@ -2893,8 +3124,33 @@ static ScrDyn *sf_headers_invoke(void *ptr, ScrDyn *self,
|
|
|
2893
3124
|
sf_type_error("Headers mutation requires its declared arguments");
|
|
2894
3125
|
return NULL;
|
|
2895
3126
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
3127
|
+
if (strcmp(method, "delete") == 0) {
|
|
3128
|
+
ScrStr *name = sf_headers_name(args[0], method);
|
|
3129
|
+
if (!name) return NULL;
|
|
3130
|
+
if (h->immutable) {
|
|
3131
|
+
scr_str_release(name);
|
|
3132
|
+
sf_type_error("immutable");
|
|
3133
|
+
return NULL;
|
|
3134
|
+
}
|
|
3135
|
+
sf_headers_delete_name(h, name);
|
|
3136
|
+
scr_str_release(name);
|
|
3137
|
+
return scr_dyn_retain(scr_dyn_undefined());
|
|
3138
|
+
}
|
|
3139
|
+
ScrStr *name = NULL;
|
|
3140
|
+
ScrStr *value = NULL;
|
|
3141
|
+
if (!sf_headers_convert_pair(
|
|
3142
|
+
args[0], args[1], method, &name, &value)) {
|
|
3143
|
+
return NULL;
|
|
3144
|
+
}
|
|
3145
|
+
if (h->immutable) {
|
|
3146
|
+
scr_str_release(name);
|
|
3147
|
+
scr_str_release(value);
|
|
3148
|
+
sf_type_error("immutable");
|
|
3149
|
+
return NULL;
|
|
3150
|
+
}
|
|
3151
|
+
if (strcmp(method, "set") == 0) sf_headers_delete_name(h, name);
|
|
3152
|
+
sf_headers_append_pair(h, name, value);
|
|
3153
|
+
return scr_dyn_retain(scr_dyn_undefined());
|
|
2898
3154
|
}
|
|
2899
3155
|
sf_not_function(what);
|
|
2900
3156
|
return NULL;
|
|
@@ -3121,6 +3377,7 @@ static void sf_reject_reason(SfTransfer *t, ScrDyn *reason) {
|
|
|
3121
3377
|
static void sf_transfer_abort_watch(SfSignalWatch *w, SfSignal *signal) {
|
|
3122
3378
|
SfTransfer *t = w->owner;
|
|
3123
3379
|
if (t->done) return;
|
|
3380
|
+
ScrDyn *reason = sf_signal_reason_ref(signal);
|
|
3124
3381
|
if (t->request_stream && t->request_stream->request_owner == t) {
|
|
3125
3382
|
SfStream *request = t->request_stream;
|
|
3126
3383
|
request->request_owner = NULL;
|
|
@@ -3132,11 +3389,12 @@ static void sf_transfer_abort_watch(SfSignalWatch *w, SfSignal *signal) {
|
|
|
3132
3389
|
}
|
|
3133
3390
|
if (t->client) scr_http_client_destroy(t->client);
|
|
3134
3391
|
if (t->response_sent && t->response_stream) {
|
|
3135
|
-
sf_stream_error(t->response_stream,
|
|
3392
|
+
sf_stream_error(t->response_stream, reason);
|
|
3136
3393
|
sf_settle(t);
|
|
3137
3394
|
} else {
|
|
3138
|
-
sf_reject_reason(t,
|
|
3395
|
+
sf_reject_reason(t, reason);
|
|
3139
3396
|
}
|
|
3397
|
+
scr_dyn_release(reason);
|
|
3140
3398
|
}
|
|
3141
3399
|
|
|
3142
3400
|
static void sf_transfer_stream_error(SfTransfer *t, ScrDyn *reason) {
|
|
@@ -3649,20 +3907,29 @@ static ScrStr *sf_trim_header_value(const ScrStr *value) {
|
|
|
3649
3907
|
}
|
|
3650
3908
|
|
|
3651
3909
|
static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len,
|
|
3652
|
-
ScrStr *value) {
|
|
3910
|
+
ScrStr *value, bool request_guard) {
|
|
3653
3911
|
if (!sf_header_name_ok(name, name_len)) {
|
|
3654
|
-
|
|
3912
|
+
ScrStr *display = scr_str_new(name, name_len);
|
|
3913
|
+
sf_headers_invalid("append", display, "name");
|
|
3914
|
+
scr_str_release(display);
|
|
3655
3915
|
return false;
|
|
3656
3916
|
}
|
|
3657
3917
|
ScrStr *byte_value = sf_header_value_bytestring(value);
|
|
3658
3918
|
if (!byte_value) return false;
|
|
3659
3919
|
if (!sf_header_value_ok(byte_value)) {
|
|
3920
|
+
ScrStr *display = sf_latin1_to_utf8(byte_value);
|
|
3921
|
+
sf_headers_invalid("append", display, "value");
|
|
3922
|
+
scr_str_release(display);
|
|
3660
3923
|
scr_str_release(byte_value);
|
|
3661
|
-
sf_type_error("fetch failed");
|
|
3662
3924
|
return false;
|
|
3663
3925
|
}
|
|
3664
3926
|
ScrStr *normalized = sf_trim_header_value(byte_value);
|
|
3665
3927
|
scr_str_release(byte_value);
|
|
3928
|
+
if (!request_guard) {
|
|
3929
|
+
ScrStr *utf8 = sf_latin1_to_utf8(normalized);
|
|
3930
|
+
scr_str_release(normalized);
|
|
3931
|
+
normalized = utf8;
|
|
3932
|
+
}
|
|
3666
3933
|
if (!sf_header_name_ci(name, name_len, "set-cookie")) {
|
|
3667
3934
|
size_t n = (size_t)scr_arr_len(pairs);
|
|
3668
3935
|
for (size_t i = 0; i + 1 < n; i += 2) {
|
|
@@ -3695,7 +3962,7 @@ static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len,
|
|
|
3695
3962
|
free(joined_data);
|
|
3696
3963
|
scr_str_release(previous);
|
|
3697
3964
|
scr_str_release(normalized);
|
|
3698
|
-
if (!sf_request_header_ok(name, name_len, joined)) {
|
|
3965
|
+
if (request_guard && !sf_request_header_ok(name, name_len, joined)) {
|
|
3699
3966
|
scr_str_release(joined);
|
|
3700
3967
|
sf_type_error("fetch failed");
|
|
3701
3968
|
return false;
|
|
@@ -3704,63 +3971,125 @@ static bool sf_push_header(ScrArr *pairs, const char *name, size_t name_len,
|
|
|
3704
3971
|
return true;
|
|
3705
3972
|
}
|
|
3706
3973
|
}
|
|
3707
|
-
if (!sf_request_header_ok(name, name_len, normalized)) {
|
|
3974
|
+
if (request_guard && !sf_request_header_ok(name, name_len, normalized)) {
|
|
3708
3975
|
scr_str_release(normalized);
|
|
3709
3976
|
sf_type_error("fetch failed");
|
|
3710
3977
|
return false;
|
|
3711
3978
|
}
|
|
3712
|
-
scr_arr_push_ref(
|
|
3979
|
+
scr_arr_push_ref(
|
|
3980
|
+
pairs,
|
|
3981
|
+
request_guard
|
|
3982
|
+
? scr_str_new(name, name_len)
|
|
3983
|
+
: sf_header_name_lower(name, name_len));
|
|
3713
3984
|
scr_arr_push_ref(pairs, normalized);
|
|
3714
3985
|
return true;
|
|
3715
3986
|
}
|
|
3716
3987
|
|
|
3717
|
-
static
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3988
|
+
static void sf_headers_constructor_type_error(void) {
|
|
3989
|
+
static const char message[] =
|
|
3990
|
+
"Headers constructor: Argument 1 could not be converted to one of: "
|
|
3991
|
+
"sequence<sequence<ByteString>>, record<ByteString, ByteString>.";
|
|
3992
|
+
sf_type_error(message);
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3995
|
+
static void sf_headers_pair_length_error(size_t len) {
|
|
3996
|
+
char message[96];
|
|
3997
|
+
int n = snprintf(
|
|
3998
|
+
message, sizeof message,
|
|
3999
|
+
"Headers constructor: expected name/value pair to be length 2, "
|
|
4000
|
+
"found %zu.", len);
|
|
4001
|
+
if (n < 0 || (size_t)n >= sizeof message) sf_oom();
|
|
4002
|
+
sf_type_error(message);
|
|
4003
|
+
}
|
|
4004
|
+
|
|
4005
|
+
/* The WebIDL HeadersInit conversion phase. It performs the observable
|
|
4006
|
+
* sequence/record walk and every ByteString coercion, but deliberately
|
|
4007
|
+
* leaves header-name/value syntax validation for initialization. Response
|
|
4008
|
+
* construction converts this snapshot before status/statusText and only
|
|
4009
|
+
* validates it after body extraction, matching Undici's two phases. */
|
|
4010
|
+
static ScrArr *sf_headers_convert_init(const ScrDyn *headers) {
|
|
4011
|
+
if (headers && headers->kind == SCR_DYN_TYPED_REF) {
|
|
4012
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(headers);
|
|
4013
|
+
ScrArr *converted = sf_headers_convert_init(materialized);
|
|
4014
|
+
scr_dyn_release(materialized);
|
|
4015
|
+
return converted;
|
|
4016
|
+
}
|
|
4017
|
+
ScrArr *converted = scr_arr_new(SCR_ELEM_STR, 8);
|
|
4018
|
+
if (!headers || headers->kind == SCR_DYN_UNDEF) {
|
|
4019
|
+
return converted;
|
|
3721
4020
|
}
|
|
3722
4021
|
if (headers->kind == SCR_DYN_OBJ) {
|
|
3723
4022
|
for (size_t i = 0; i < headers->v.obj.len; i++) {
|
|
3724
4023
|
const ScrDynEntry *e = &headers->v.obj.entries[i];
|
|
3725
|
-
ScrStr *
|
|
4024
|
+
ScrStr *raw_name = scr_str_new(e->key, e->key_len);
|
|
4025
|
+
ScrStr *name_bytes = sf_header_value_bytestring(raw_name);
|
|
4026
|
+
ScrStr *name = name_bytes ? sf_latin1_to_utf8(name_bytes) : NULL;
|
|
4027
|
+
scr_str_release(name_bytes);
|
|
4028
|
+
scr_str_release(raw_name);
|
|
3726
4029
|
ScrDyn *source = scr_dyn_retain(e->value);
|
|
3727
|
-
ScrStr *
|
|
4030
|
+
ScrStr *raw_value = name ? scr_dyn_string_coerce_js(source) : NULL;
|
|
3728
4031
|
scr_dyn_release(source);
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
4032
|
+
ScrStr *value_bytes = raw_value
|
|
4033
|
+
? sf_header_value_bytestring(raw_value)
|
|
4034
|
+
: NULL;
|
|
4035
|
+
ScrStr *value = value_bytes ? sf_latin1_to_utf8(value_bytes) : NULL;
|
|
4036
|
+
scr_str_release(value_bytes);
|
|
4037
|
+
scr_str_release(raw_value);
|
|
4038
|
+
if (!name || !value) {
|
|
4039
|
+
scr_str_release(name);
|
|
4040
|
+
scr_str_release(value);
|
|
4041
|
+
goto fail;
|
|
3735
4042
|
}
|
|
4043
|
+
scr_arr_push_ref(converted, name);
|
|
4044
|
+
scr_arr_push_ref(converted, value);
|
|
3736
4045
|
}
|
|
3737
|
-
return
|
|
4046
|
+
return converted;
|
|
3738
4047
|
}
|
|
3739
4048
|
if (headers->kind == SCR_DYN_ARR) {
|
|
3740
4049
|
for (size_t i = 0; i < headers->v.arr.len; i++) {
|
|
3741
4050
|
ScrDyn *pair = scr_dyn_retain(headers->v.arr.items[i]);
|
|
3742
4051
|
if (pair->kind != SCR_DYN_ARR || pair->v.arr.len != 2) {
|
|
4052
|
+
size_t pair_len = pair->kind == SCR_DYN_ARR ? pair->v.arr.len : 0;
|
|
3743
4053
|
scr_dyn_release(pair);
|
|
3744
|
-
if (!scr_exc_pending())
|
|
3745
|
-
|
|
4054
|
+
if (!scr_exc_pending()) {
|
|
4055
|
+
if (pair_len > 0 || headers->v.arr.items[i]->kind == SCR_DYN_ARR) {
|
|
4056
|
+
sf_headers_pair_length_error(pair_len);
|
|
4057
|
+
} else {
|
|
4058
|
+
sf_headers_constructor_type_error();
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
goto fail;
|
|
3746
4062
|
}
|
|
3747
4063
|
ScrDyn *name_source = scr_dyn_retain(pair->v.arr.items[0]);
|
|
3748
4064
|
ScrDyn *value_source = scr_dyn_retain(pair->v.arr.items[1]);
|
|
3749
4065
|
scr_dyn_release(pair);
|
|
3750
|
-
ScrStr *
|
|
4066
|
+
ScrStr *raw_name = scr_dyn_string_coerce_js(name_source);
|
|
3751
4067
|
scr_dyn_release(name_source);
|
|
3752
|
-
ScrStr *
|
|
4068
|
+
ScrStr *name_bytes = raw_name
|
|
4069
|
+
? sf_header_value_bytestring(raw_name)
|
|
4070
|
+
: NULL;
|
|
4071
|
+
ScrStr *name = name_bytes ? sf_latin1_to_utf8(name_bytes) : NULL;
|
|
4072
|
+
scr_str_release(name_bytes);
|
|
4073
|
+
scr_str_release(raw_name);
|
|
4074
|
+
ScrStr *raw_value = name
|
|
4075
|
+
? scr_dyn_string_coerce_js(value_source)
|
|
4076
|
+
: NULL;
|
|
3753
4077
|
scr_dyn_release(value_source);
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
4078
|
+
ScrStr *value_bytes = raw_value
|
|
4079
|
+
? sf_header_value_bytestring(raw_value)
|
|
4080
|
+
: NULL;
|
|
4081
|
+
ScrStr *value = value_bytes ? sf_latin1_to_utf8(value_bytes) : NULL;
|
|
4082
|
+
scr_str_release(value_bytes);
|
|
4083
|
+
scr_str_release(raw_value);
|
|
4084
|
+
if (!name || !value) {
|
|
4085
|
+
scr_str_release(name);
|
|
4086
|
+
scr_str_release(value);
|
|
4087
|
+
goto fail;
|
|
3761
4088
|
}
|
|
4089
|
+
scr_arr_push_ref(converted, name);
|
|
4090
|
+
scr_arr_push_ref(converted, value);
|
|
3762
4091
|
}
|
|
3763
|
-
return
|
|
4092
|
+
return converted;
|
|
3764
4093
|
}
|
|
3765
4094
|
if (headers->kind == SCR_DYN_HANDLE &&
|
|
3766
4095
|
headers->v.handle.tag == SCR_DYNH_FETCH_HEADERS) {
|
|
@@ -3770,15 +4099,44 @@ static bool sf_add_headers(ScrArr *pairs, const ScrDyn *headers) {
|
|
|
3770
4099
|
ScrStr *name = scr_arr_get_ref(source->pairs, (double)i);
|
|
3771
4100
|
ScrStr *value =
|
|
3772
4101
|
scr_arr_get_ref(source->pairs, (double)(i + 1));
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
scr_str_release(value);
|
|
3776
|
-
if (!ok) return false;
|
|
4102
|
+
scr_arr_push_ref(converted, name);
|
|
4103
|
+
scr_arr_push_ref(converted, value);
|
|
3777
4104
|
}
|
|
3778
|
-
return
|
|
4105
|
+
return converted;
|
|
3779
4106
|
}
|
|
3780
|
-
|
|
3781
|
-
|
|
4107
|
+
/* Functions are WebIDL objects and an ordinary function has no
|
|
4108
|
+
* enumerable HeadersInit entries. The checked-dynamic function model's
|
|
4109
|
+
* expando properties are non-enumerable by construction. */
|
|
4110
|
+
if (headers->kind == SCR_DYN_FUNC) return converted;
|
|
4111
|
+
sf_headers_constructor_type_error();
|
|
4112
|
+
|
|
4113
|
+
fail:
|
|
4114
|
+
scr_arr_release(converted);
|
|
4115
|
+
return NULL;
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
static bool sf_add_converted_headers(
|
|
4119
|
+
ScrArr *pairs, ScrArr *converted, bool request_guard) {
|
|
4120
|
+
size_t n = (size_t)scr_arr_len(converted);
|
|
4121
|
+
for (size_t i = 0; i + 1 < n; i += 2) {
|
|
4122
|
+
ScrStr *name = scr_arr_get_ref(converted, (double)i);
|
|
4123
|
+
ScrStr *value = scr_arr_get_ref(converted, (double)(i + 1));
|
|
4124
|
+
bool ok = sf_push_header(
|
|
4125
|
+
pairs, name->data, name->len, value, request_guard);
|
|
4126
|
+
scr_str_release(name);
|
|
4127
|
+
scr_str_release(value);
|
|
4128
|
+
if (!ok) return false;
|
|
4129
|
+
}
|
|
4130
|
+
return true;
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
static bool sf_add_headers(
|
|
4134
|
+
ScrArr *pairs, const ScrDyn *headers, bool request_guard) {
|
|
4135
|
+
ScrArr *converted = sf_headers_convert_init(headers);
|
|
4136
|
+
if (!converted) return false;
|
|
4137
|
+
bool ok = sf_add_converted_headers(pairs, converted, request_guard);
|
|
4138
|
+
scr_arr_release(converted);
|
|
4139
|
+
return ok;
|
|
3782
4140
|
}
|
|
3783
4141
|
|
|
3784
4142
|
static void sf_push_header_text(ScrArr *pairs, const char *name,
|
|
@@ -4595,6 +4953,213 @@ static bool sf_start_hop(SfTransfer *t) {
|
|
|
4595
4953
|
return true;
|
|
4596
4954
|
}
|
|
4597
4955
|
|
|
4956
|
+
static bool sf_response_init_object_like(const ScrDyn *init) {
|
|
4957
|
+
if (!init) return false;
|
|
4958
|
+
switch (init->kind) {
|
|
4959
|
+
case SCR_DYN_OBJ:
|
|
4960
|
+
case SCR_DYN_ARR:
|
|
4961
|
+
case SCR_DYN_BYTES:
|
|
4962
|
+
case SCR_DYN_FUNC:
|
|
4963
|
+
case SCR_DYN_HANDLE:
|
|
4964
|
+
case SCR_DYN_PROMISE:
|
|
4965
|
+
case SCR_DYN_TYPED_REF:
|
|
4966
|
+
case SCR_DYN_JSVAL:
|
|
4967
|
+
return true;
|
|
4968
|
+
default:
|
|
4969
|
+
return false;
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
|
|
4973
|
+
/* Owned property read for the checked-dynamic object kinds that can carry
|
|
4974
|
+
* ResponseInit dictionary members. Arrays/bytes/native handles/promises
|
|
4975
|
+
* have no such expandos in the static model and therefore answer absent. */
|
|
4976
|
+
static ScrDyn *sf_response_init_get(
|
|
4977
|
+
const ScrDyn *init, const char *key, size_t len) {
|
|
4978
|
+
if (!init) return NULL;
|
|
4979
|
+
if (init->kind == SCR_DYN_OBJ) {
|
|
4980
|
+
ScrDyn *value = scr_dyn_obj_get(init, key, len);
|
|
4981
|
+
return value ? scr_dyn_retain(value) : NULL;
|
|
4982
|
+
}
|
|
4983
|
+
if (init->kind == SCR_DYN_FUNC) {
|
|
4984
|
+
return scr_dyn_fn_get(init, key, len);
|
|
4985
|
+
}
|
|
4986
|
+
if (init->kind == SCR_DYN_HANDLE &&
|
|
4987
|
+
init->v.handle.tag == SCR_DYNH_FETCH_RESPONSE) {
|
|
4988
|
+
return sf_response_get(init->v.handle.ptr, key, len);
|
|
4989
|
+
}
|
|
4990
|
+
if (init->kind == SCR_DYN_TYPED_REF) {
|
|
4991
|
+
ScrDyn *materialized = scr_dyn_typed_ref_materialize(init);
|
|
4992
|
+
ScrDyn *value = sf_response_init_get(materialized, key, len);
|
|
4993
|
+
scr_dyn_release(materialized);
|
|
4994
|
+
return value;
|
|
4995
|
+
}
|
|
4996
|
+
return NULL;
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
ScrDyn *scr_fetch_response_new(ScrDyn *body, ScrDyn *init) {
|
|
5000
|
+
ScrArr *header_pairs = NULL;
|
|
5001
|
+
ScrArr *converted_headers = NULL;
|
|
5002
|
+
ScrBytes *body_bytes = NULL;
|
|
5003
|
+
ScrStr *status_text = NULL;
|
|
5004
|
+
SfStream *stream = NULL;
|
|
5005
|
+
bool null_body =
|
|
5006
|
+
!body || body->kind == SCR_DYN_UNDEF || body->kind == SCR_DYN_NULL;
|
|
5007
|
+
bool text_body = false;
|
|
5008
|
+
int status = 200;
|
|
5009
|
+
bool status_range_error = false;
|
|
5010
|
+
|
|
5011
|
+
/* WebIDL converts constructor arguments from left to right. BodyInit's
|
|
5012
|
+
* string arm therefore runs before ResponseInit's dictionary conversion,
|
|
5013
|
+
* while a ReadableStream is only brand-converted here — its locked/
|
|
5014
|
+
* disturbed state belongs to the later body-extraction phase. */
|
|
5015
|
+
if (!null_body &&
|
|
5016
|
+
!(body->kind == SCR_DYN_HANDLE &&
|
|
5017
|
+
body->v.handle.tag == SCR_DYNH_WEB_STREAM)) {
|
|
5018
|
+
if (body->kind == SCR_DYN_BYTES) {
|
|
5019
|
+
body_bytes = scr_bytes_copy(body->v.bytes);
|
|
5020
|
+
} else {
|
|
5021
|
+
ScrStr *text =
|
|
5022
|
+
body->kind == SCR_DYN_STR
|
|
5023
|
+
? scr_str_retain(body->v.str)
|
|
5024
|
+
: scr_dyn_string_coerce_js(body);
|
|
5025
|
+
if (!text) goto fail;
|
|
5026
|
+
ScrStr *encoding = scr_str_new("utf8", 4);
|
|
5027
|
+
body_bytes = scr_bytes_from_str(text, encoding);
|
|
5028
|
+
scr_str_release(encoding);
|
|
5029
|
+
scr_str_release(text);
|
|
5030
|
+
text_body = true;
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
|
|
5034
|
+
if (init && init->kind != SCR_DYN_UNDEF && init->kind != SCR_DYN_NULL &&
|
|
5035
|
+
!sf_response_init_object_like(init)) {
|
|
5036
|
+
sf_type_error("Response constructor init must be an object");
|
|
5037
|
+
goto fail;
|
|
5038
|
+
}
|
|
5039
|
+
|
|
5040
|
+
/* ResponseInit's WebIDL dictionary conversion is lexicographic and the
|
|
5041
|
+
* nested HeadersInit conversion is observable: headers' ByteString
|
|
5042
|
+
* coercions precede status and statusText. Header syntax validation is a
|
|
5043
|
+
* later response-initialization step, after body extraction. */
|
|
5044
|
+
ScrDyn *init_headers = sf_response_init_get(init, "headers", 7);
|
|
5045
|
+
converted_headers = sf_headers_convert_init(init_headers);
|
|
5046
|
+
scr_dyn_release(init_headers);
|
|
5047
|
+
if (!converted_headers) goto fail;
|
|
5048
|
+
|
|
5049
|
+
if (init && sf_response_init_object_like(init)) {
|
|
5050
|
+
ScrDyn *status_value = sf_response_init_get(init, "status", 6);
|
|
5051
|
+
if (status_value && status_value->kind != SCR_DYN_UNDEF) {
|
|
5052
|
+
double converted;
|
|
5053
|
+
if (!scr_dyn_number_coerce_js(status_value, &converted)) {
|
|
5054
|
+
scr_dyn_release(status_value);
|
|
5055
|
+
goto fail;
|
|
5056
|
+
}
|
|
5057
|
+
converted = trunc(converted);
|
|
5058
|
+
if (!isfinite(converted) || converted < 0.0) {
|
|
5059
|
+
status_range_error = true;
|
|
5060
|
+
} else {
|
|
5061
|
+
if (converted > 65535.0) converted = fmod(converted, 65536.0);
|
|
5062
|
+
status = (int)converted;
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
scr_dyn_release(status_value);
|
|
5066
|
+
|
|
5067
|
+
ScrDyn *status_text_value =
|
|
5068
|
+
sf_response_init_get(init, "statusText", 10);
|
|
5069
|
+
if (status_text_value && status_text_value->kind != SCR_DYN_UNDEF) {
|
|
5070
|
+
ScrStr *raw = scr_dyn_string_coerce_js(status_text_value);
|
|
5071
|
+
ScrStr *bytes = raw ? sf_header_value_bytestring(raw) : NULL;
|
|
5072
|
+
scr_str_release(raw);
|
|
5073
|
+
status_text = bytes ? sf_latin1_to_utf8(bytes) : NULL;
|
|
5074
|
+
scr_str_release(bytes);
|
|
5075
|
+
if (!status_text) {
|
|
5076
|
+
scr_dyn_release(status_text_value);
|
|
5077
|
+
goto fail;
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
scr_dyn_release(status_text_value);
|
|
5081
|
+
}
|
|
5082
|
+
if (!status_text) status_text = scr_str_new("", 0);
|
|
5083
|
+
|
|
5084
|
+
/* Extract BodyInit before validating the response metadata. This makes a
|
|
5085
|
+
* locked/disturbed stream win over the later status range, statusText HTTP
|
|
5086
|
+
* reason-phrase, and HeadersInit validation exactly as in pinned Node. */
|
|
5087
|
+
if (null_body) {
|
|
5088
|
+
stream = sf_stream_new_native();
|
|
5089
|
+
sf_stream_close(stream);
|
|
5090
|
+
} else if (body->kind == SCR_DYN_HANDLE &&
|
|
5091
|
+
body->v.handle.tag == SCR_DYNH_WEB_STREAM) {
|
|
5092
|
+
stream = (SfStream *)body->v.handle.ptr;
|
|
5093
|
+
if (stream->reader || stream->internal_lock || stream->disturbed) {
|
|
5094
|
+
stream = NULL; /* borrowed until the successful retain below */
|
|
5095
|
+
sf_type_error("Response body object should not be disturbed or locked");
|
|
5096
|
+
goto fail;
|
|
5097
|
+
}
|
|
5098
|
+
sf_stream_retain(stream);
|
|
5099
|
+
} else {
|
|
5100
|
+
stream = sf_stream_new_native();
|
|
5101
|
+
sf_stream_enqueue_bytes(stream, body_bytes);
|
|
5102
|
+
sf_stream_close(stream);
|
|
5103
|
+
scr_bytes_release(body_bytes);
|
|
5104
|
+
body_bytes = NULL;
|
|
5105
|
+
}
|
|
5106
|
+
|
|
5107
|
+
if (status_range_error || status < 200 || status > 599) {
|
|
5108
|
+
static const char message[] =
|
|
5109
|
+
"init[\"status\"] must be in the range of 200 to 599, inclusive.";
|
|
5110
|
+
scr_throw_error_msg(SCR_ERR_RANGE, message, sizeof message - 1);
|
|
5111
|
+
goto fail;
|
|
5112
|
+
}
|
|
5113
|
+
if (!sf_header_value_ok(status_text)) {
|
|
5114
|
+
sf_type_error("Invalid statusText");
|
|
5115
|
+
goto fail;
|
|
5116
|
+
}
|
|
5117
|
+
|
|
5118
|
+
header_pairs = scr_arr_new(SCR_ELEM_STR, 8);
|
|
5119
|
+
if (!sf_add_converted_headers(
|
|
5120
|
+
header_pairs, converted_headers, false)) goto fail;
|
|
5121
|
+
scr_arr_release(converted_headers);
|
|
5122
|
+
converted_headers = NULL;
|
|
5123
|
+
|
|
5124
|
+
if (!null_body &&
|
|
5125
|
+
(status == 204 || status == 205 || status == 304)) {
|
|
5126
|
+
char message[80];
|
|
5127
|
+
int len = snprintf(message, sizeof message,
|
|
5128
|
+
"Response constructor: Invalid response status code %d",
|
|
5129
|
+
status);
|
|
5130
|
+
if (len < 0 || (size_t)len >= sizeof message) sf_oom();
|
|
5131
|
+
sf_type_error(message);
|
|
5132
|
+
goto fail;
|
|
5133
|
+
}
|
|
5134
|
+
if (text_body && !sf_pairs_have(header_pairs, "content-type")) {
|
|
5135
|
+
sf_push_header_text(header_pairs, "content-type",
|
|
5136
|
+
"text/plain;charset=UTF-8", 24);
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5139
|
+
SfResponse *response = calloc(1, sizeof *response);
|
|
5140
|
+
if (!response) sf_oom();
|
|
5141
|
+
response->rc = 1;
|
|
5142
|
+
response->body = stream;
|
|
5143
|
+
response->headers = sf_headers_new(header_pairs, false);
|
|
5144
|
+
response->url = scr_str_new("", 0);
|
|
5145
|
+
response->status_text = status_text;
|
|
5146
|
+
response->status = status;
|
|
5147
|
+
response->redirected = false;
|
|
5148
|
+
response->null_body = null_body;
|
|
5149
|
+
ScrDyn *out =
|
|
5150
|
+
scr_dyn_new_handle(response, SCR_DYNH_FETCH_RESPONSE);
|
|
5151
|
+
sf_response_release(response);
|
|
5152
|
+
return out;
|
|
5153
|
+
|
|
5154
|
+
fail:
|
|
5155
|
+
sf_stream_release(stream);
|
|
5156
|
+
scr_bytes_release(body_bytes);
|
|
5157
|
+
scr_str_release(status_text);
|
|
5158
|
+
scr_arr_release(header_pairs);
|
|
5159
|
+
scr_arr_release(converted_headers);
|
|
5160
|
+
return NULL;
|
|
5161
|
+
}
|
|
5162
|
+
|
|
4598
5163
|
ScrPromise *scr_fetch_static(ScrStr *url, ScrDyn *init) {
|
|
4599
5164
|
ScrPromise *promise = scr_promise_new();
|
|
4600
5165
|
if (init && init->kind != SCR_DYN_OBJ &&
|
|
@@ -4686,7 +5251,7 @@ ScrPromise *scr_fetch_static(ScrStr *url, ScrDyn *init) {
|
|
|
4686
5251
|
init && init->kind == SCR_DYN_OBJ
|
|
4687
5252
|
? scr_dyn_obj_get(init, "headers", 7)
|
|
4688
5253
|
: NULL;
|
|
4689
|
-
if (!sf_add_headers(headers, init_headers)) {
|
|
5254
|
+
if (!sf_add_headers(headers, init_headers, true)) {
|
|
4690
5255
|
scr_dyn_release(coerced_body);
|
|
4691
5256
|
scr_arr_release(headers);
|
|
4692
5257
|
return sf_reject_now(promise, "fetch failed");
|
|
@@ -4826,7 +5391,9 @@ ScrPromise *scr_fetch_static(ScrStr *url, ScrDyn *init) {
|
|
|
4826
5391
|
scr_arr_release(headers);
|
|
4827
5392
|
scr_str_release(method);
|
|
4828
5393
|
scr_url_release(u);
|
|
4829
|
-
|
|
5394
|
+
ScrDyn *reason = sf_signal_reason_ref(signal);
|
|
5395
|
+
sf_reject_promise_reason(promise, reason);
|
|
5396
|
+
scr_dyn_release(reason);
|
|
4830
5397
|
return promise;
|
|
4831
5398
|
}
|
|
4832
5399
|
|
|
@@ -4867,6 +5434,9 @@ static bool sf_no_set(void *ptr, const char *key, size_t len,
|
|
|
4867
5434
|
static const ScrDynHandleOps sf_signal_ops = {
|
|
4868
5435
|
"AbortSignal", &sf_signal_retain_v, &sf_signal_release_v,
|
|
4869
5436
|
&sf_signal_invoke, &sf_signal_get, &sf_signal_set, NULL};
|
|
5437
|
+
static const ScrDynHandleOps sf_abort_controller_ops = {
|
|
5438
|
+
"AbortController", &sf_signal_retain_v, &sf_signal_release_v,
|
|
5439
|
+
&sf_abort_controller_invoke, &sf_abort_controller_get, &sf_no_set, NULL};
|
|
4870
5440
|
static const ScrDynHandleOps sf_stream_ops = {
|
|
4871
5441
|
"ReadableStream", &sf_stream_retain_v, &sf_stream_release_v,
|
|
4872
5442
|
&sf_stream_invoke, &sf_stream_get, &sf_no_set, NULL};
|
|
@@ -4907,6 +5477,9 @@ static void sf_teardown(void) {
|
|
|
4907
5477
|
while (sf_callback_signals) {
|
|
4908
5478
|
sf_signal_drop_callbacks(sf_callback_signals);
|
|
4909
5479
|
}
|
|
5480
|
+
while (sf_reason_signals) {
|
|
5481
|
+
sf_signal_drop_reason(sf_reason_signals);
|
|
5482
|
+
}
|
|
4910
5483
|
sf_proxy_snapshot_free();
|
|
4911
5484
|
}
|
|
4912
5485
|
|
|
@@ -4918,6 +5491,8 @@ void scr_fetch_install(void) {
|
|
|
4918
5491
|
sf_proxy_snapshot();
|
|
4919
5492
|
scr_net_install();
|
|
4920
5493
|
scr_dyn_handle_install(SCR_DYNH_ABORT_SIGNAL, &sf_signal_ops);
|
|
5494
|
+
scr_dyn_handle_install(SCR_DYNH_ABORT_CONTROLLER,
|
|
5495
|
+
&sf_abort_controller_ops);
|
|
4921
5496
|
scr_dyn_handle_install(SCR_DYNH_WEB_STREAM, &sf_stream_ops);
|
|
4922
5497
|
scr_dyn_handle_install(SCR_DYNH_WEB_READER, &sf_reader_ops);
|
|
4923
5498
|
scr_dyn_handle_install(SCR_DYNH_WEB_CONTROLLER, &sf_controller_ops);
|