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.

prepare for fetch module

+353 -33
+49
examples/spec/fetch.js
··· 1 + import { test, summary } from './helpers.js'; 2 + 3 + console.log('Fetch Tests\n'); 4 + 5 + async function testRejects(name, fn) { 6 + try { 7 + await fn(); 8 + test(`${name} rejects`, false, true); 9 + } catch (_err) { 10 + test(`${name} rejects`, true, true); 11 + } 12 + } 13 + 14 + test('fetch typeof', typeof fetch, 'function'); 15 + 16 + const textResponse = await fetch('data:,hello%20world'); 17 + test('fetch data url returns Response', textResponse instanceof Response, true); 18 + test('fetch data url status', textResponse.status, 200); 19 + test('fetch data url ok', textResponse.ok, true); 20 + test('fetch data url url', textResponse.url, 'data:,hello%20world'); 21 + test('fetch data url text', await textResponse.text(), 'hello world'); 22 + 23 + const jsonResponse = await fetch('data:application/json,%7B%22ok%22%3Atrue%7D'); 24 + test('fetch data url json content-type', jsonResponse.headers.get('content-type'), 'application/json'); 25 + test('fetch data url json body', (await jsonResponse.json()).ok, true); 26 + test('fetch response headers immutable', (() => { 27 + try { 28 + jsonResponse.headers.append('x-test', '1'); 29 + return false; 30 + } catch (_err) { 31 + return true; 32 + } 33 + })(), true); 34 + 35 + const requestInput = new Request('data:text/plain,request-body'); 36 + const requestResponse = await fetch(requestInput); 37 + test('fetch accepts Request input', await requestResponse.text(), 'request-body'); 38 + 39 + const abortedController = new AbortController(); 40 + abortedController.abort(new Error('aborted')); 41 + await testRejects('fetch with aborted signal', () => 42 + fetch('data:,ignored', { signal: abortedController.signal }) 43 + ); 44 + 45 + await testRejects('fetch unsupported scheme', () => 46 + fetch('file:///tmp/ant-fetch-unsupported') 47 + ); 48 + 49 + summary();
+1
include/common.h
··· 78 78 SLOT_REQUEST_BODY_STREAM, 79 79 SLOT_RESPONSE_HEADERS, 80 80 SLOT_RESPONSE_BODY_STREAM, 81 + SLOT_PIPE_ABORT_LISTENER, 81 82 SLOT_MAX = 255 82 83 } internal_slot_t; 83 84
+1
include/modules/abort.h
··· 9 9 void gc_mark_abort(ant_t *js, gc_mark_fn mark); 10 10 void signal_do_abort(ant_t *js, ant_value_t signal, ant_value_t reason); 11 11 void abort_signal_add_listener(ant_t *js, ant_value_t signal, ant_value_t callback); 12 + void abort_signal_remove_listener(ant_t *js, ant_value_t signal, ant_value_t callback); 12 13 13 14 bool abort_signal_is_signal(ant_value_t signal); 14 15 bool abort_signal_is_aborted(ant_value_t signal);
+8 -1
include/modules/headers.h
··· 15 15 HEADERS_GUARD_IMMUTABLE 16 16 } headers_guard_t; 17 17 18 - headers_guard_t headers_get_guard(ant_value_t hdrs); 18 + typedef void (*headers_foreach_cb)( 19 + const char *name, 20 + const char *value, 21 + void *ctx 22 + ); 19 23 20 24 void init_headers_module(void); 21 25 void headers_apply_guard(ant_value_t hdrs); 22 26 void headers_set_guard(ant_value_t hdrs, headers_guard_t guard); 23 27 void headers_append_if_missing(ant_value_t hdrs, const char *name, const char *value); 28 + void headers_for_each(ant_value_t hdrs, headers_foreach_cb cb, void *ctx); 24 29 25 30 bool headers_is_headers(ant_value_t obj); 26 31 bool headers_copy_from(ant_t *js, ant_value_t dst, ant_value_t src); ··· 32 37 ant_value_t headers_create_from_init(ant_t *js, ant_value_t init); 33 38 ant_value_t headers_get_value(ant_t *js, ant_value_t hdrs, const char *name); 34 39 ant_value_t headers_append_value(ant_t *js, ant_value_t hdrs, ant_value_t name_v, ant_value_t value_v); 40 + 41 + headers_guard_t headers_get_guard(ant_value_t hdrs); 35 42 36 43 #endif
+63
include/modules/http.h
··· 1 + #ifndef ANT_HTTP_H 2 + #define ANT_HTTP_H 3 + 4 + #include <uv.h> 5 + #include <types.h> 6 + #include <stddef.h> 7 + #include <stdint.h> 8 + 9 + typedef struct ant_http_header_s { 10 + char *name; 11 + char *value; 12 + struct ant_http_header_s *next; 13 + } ant_http_header_t; 14 + 15 + typedef struct { 16 + const char *method; 17 + const char *url; 18 + const ant_http_header_t *headers; 19 + const uint8_t *body; 20 + size_t body_len; 21 + } ant_http_request_options_t; 22 + 23 + typedef struct { 24 + int status; 25 + const char *status_text; 26 + const ant_http_header_t *headers; 27 + } ant_http_response_t; 28 + 29 + typedef void (*ant_http_response_cb)( 30 + ant_http_request_t *req, 31 + const ant_http_response_t *resp, 32 + void *user_data 33 + ); 34 + 35 + typedef void (*ant_http_body_cb)( 36 + ant_http_request_t *req, 37 + const uint8_t *chunk, 38 + size_t len, 39 + void *user_data 40 + ); 41 + 42 + typedef void (*ant_http_complete_cb)( 43 + ant_http_request_t *req, 44 + int error_code, 45 + const char *error_message, 46 + void *user_data 47 + ); 48 + 49 + int ant_http_request_start( 50 + uv_loop_t *loop, 51 + const ant_http_request_options_t *options, 52 + ant_http_response_cb on_response, 53 + ant_http_body_cb on_body, 54 + ant_http_complete_cb on_complete, 55 + void *user_data, 56 + ant_http_request_t **out_req 57 + ); 58 + 59 + int ant_http_request_cancel(ant_http_request_t *req); 60 + void ant_http_headers_free(ant_http_header_t *headers); 61 + const ant_http_response_t *ant_http_request_response(ant_http_request_t *req); 62 + 63 + #endif
+6
include/modules/request.h
··· 45 45 const char *body_type 46 46 ); 47 47 48 + ant_value_t request_create_from_input_init( 49 + ant_t *js, 50 + ant_value_t input, 51 + ant_value_t init 52 + ); 53 + 48 54 #endif
+12 -1
include/modules/response.h
··· 24 24 } response_data_t; 25 25 26 26 extern ant_value_t g_response_proto; 27 - 28 27 void init_response_module(void); 29 28 30 29 response_data_t *response_get_data(ant_value_t obj); ··· 40 39 size_t body_len, 41 40 const char *body_type, 42 41 headers_guard_t guard 42 + ); 43 + 44 + ant_value_t response_create_fetched( 45 + ant_t *js, 46 + int status, 47 + const char *status_text, 48 + const char *url, 49 + ant_value_t headers_obj, 50 + const uint8_t *body, 51 + size_t body_len, 52 + ant_value_t body_stream, 53 + const char *body_type 43 54 ); 44 55 45 56 #endif
+3 -2
include/types.h
··· 10 10 struct ant_shape; 11 11 struct ant_isolate_t; 12 12 13 - typedef struct ant_isolate_t ant_t; 14 - typedef struct ant_pool_block ant_pool_block_t; 13 + typedef struct ant_isolate_t ant_t; 14 + typedef struct ant_pool_block ant_pool_block_t; 15 + typedef struct ant_http_request_s ant_http_request_t; 15 16 16 17 typedef struct ant_object ant_object_t; 17 18 typedef struct ant_shape ant_shape_t;
+13
src/modules/abort.c
··· 130 130 utarray_free(to_fire); 131 131 } 132 132 133 + void abort_signal_remove_listener(ant_t *js, ant_value_t signal, ant_value_t callback) { 134 + abort_signal_data_t *data = get_signal_data(signal); 135 + if (!data) return; 136 + 137 + unsigned int n = utarray_len(data->listeners); 138 + for (unsigned int i = 0; i < n; i++) { 139 + abort_listener_t *entry = (abort_listener_t *)utarray_eltptr(data->listeners, i); 140 + if (entry->callback != callback) continue; 141 + utarray_erase(data->listeners, i, 1); 142 + return; 143 + } 144 + } 145 + 133 146 static ant_value_t make_new_signal(ant_t *js) { 134 147 abort_signal_data_t *data = ant_calloc(sizeof(abort_signal_data_t)); 135 148 if (!data) return js_mkerr(js, "AbortSignal: out of memory");
+6
src/modules/headers.c
··· 793 793 free(lower); 794 794 } 795 795 796 + void headers_for_each(ant_value_t hdrs, headers_foreach_cb cb, void *ctx) { 797 + hdr_list_t *l = get_list(hdrs); 798 + if (!l || !cb) return; 799 + for (hdr_entry_t *e = l->head; e; e = e->next) cb(e->name, e->value, ctx); 800 + } 801 + 796 802 bool headers_set_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value) { 797 803 hdr_list_t *l = get_list(hdrs); 798 804 headers_guard_t guard = 0;
+95 -8
src/modules/request.c
··· 1094 1094 } 1095 1095 1096 1096 static ant_value_t js_request_ctor(ant_t *js, ant_value_t *args, int nargs) { 1097 + ant_value_t init = (nargs >= 2 && vtype(args[1]) != T_UNDEF) ? args[1] : js_mkundef(); 1098 + 1097 1099 if (vtype(js->new_target) == T_UNDEF) 1098 1100 return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires 'new'"); 1099 1101 if (nargs < 1) 1100 1102 return js_mkerr_typed(js, JS_ERR_TYPE, "Request constructor requires at least 1 argument"); 1101 1103 1102 1104 ant_value_t input = args[0]; 1103 - ant_value_t init = (nargs >= 2 && vtype(args[1]) != T_UNDEF) ? args[1] : js_mkundef(); 1104 - bool init_provided = (vtype(init) == T_OBJ || vtype(init) == T_ARR); 1105 + ant_value_t obj = 0; 1106 + ant_value_t proto = 0; 1107 + bool init_provided = false; 1105 1108 1106 1109 request_data_t *req = NULL; 1107 1110 request_data_t *src = NULL; 1108 1111 1109 1112 ant_value_t input_signal = js_mkundef(); 1110 - ant_value_t step = request_new_from_input(js, input, &req, &src, &input_signal); 1113 + ant_value_t step = js_mkundef(); 1114 + ant_value_t signal = 0; 1115 + ant_value_t headers = 0; 1111 1116 1117 + bool duplex_provided = false; 1118 + init_provided = (vtype(init) == T_OBJ || vtype(init) == T_ARR); 1119 + 1120 + step = request_new_from_input(js, input, &req, &src, &input_signal); 1112 1121 if (is_err(step)) return step; 1113 1122 1114 1123 if (init_provided) { ··· 1125 1134 "Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"); 1126 1135 } 1127 1136 1128 - ant_value_t obj = js_mkobj(js); 1129 - ant_value_t proto = js_instance_proto_from_new_target(js, g_request_proto); 1137 + obj = js_mkobj(js); 1138 + proto = js_instance_proto_from_new_target(js, g_request_proto); 1130 1139 1131 1140 if (is_object_type(proto)) js_set_proto_init(obj, proto); 1141 + else js_set_proto_init(obj, g_request_proto); 1142 + 1132 1143 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 1133 1144 js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 1134 1145 1135 - ant_value_t signal = abort_signal_create_dependent(js, input_signal); 1146 + signal = abort_signal_create_dependent(js, input_signal); 1136 1147 if (is_err(signal)) { data_free(req); return signal; } 1137 1148 js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 1138 1149 1139 - ant_value_t headers = request_create_ctor_headers(js, input); 1150 + headers = request_create_ctor_headers(js, input); 1151 + if (is_err(headers)) { data_free(req); return headers; } 1152 + 1153 + if (init_provided) { 1154 + headers = request_apply_init_headers(js, init, headers); 1155 + if (is_err(headers)) { data_free(req); return headers; } 1156 + } 1157 + 1158 + headers_set_guard(headers, 1159 + strcmp(req->mode, "no-cors") == 0 1160 + ? HEADERS_GUARD_REQUEST_NO_CORS 1161 + : HEADERS_GUARD_REQUEST 1162 + ); 1163 + 1164 + headers_apply_guard(headers); 1165 + js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, headers); 1166 + 1167 + if (init_provided) { 1168 + step = request_parse_duplex(js, init, &duplex_provided); 1169 + if (is_err(step)) { data_free(req); return step; } 1170 + } 1171 + 1172 + step = request_apply_ctor_body( 1173 + js, obj, input, init, init_provided, 1174 + duplex_provided, req, src, headers 1175 + ); 1176 + 1177 + if (is_err(step)) { 1178 + data_free(req); 1179 + return step; 1180 + } 1181 + 1182 + if (src && src->has_body && !src->body_used) 1183 + src->body_used = true; 1184 + 1185 + return obj; 1186 + } 1187 + 1188 + ant_value_t request_create_from_input_init(ant_t *js, ant_value_t input, ant_value_t init) { 1189 + bool init_provided = (vtype(init) == T_OBJ || vtype(init) == T_ARR); 1190 + 1191 + request_data_t *req = NULL; 1192 + request_data_t *src = NULL; 1193 + 1194 + ant_value_t input_signal = js_mkundef(); 1195 + ant_value_t step = js_mkundef(); 1196 + ant_value_t obj = 0; 1197 + ant_value_t signal = 0; 1198 + ant_value_t headers = 0; 1199 + 1140 1200 bool duplex_provided = false; 1141 - 1201 + step = request_new_from_input(js, input, &req, &src, &input_signal); 1202 + if (is_err(step)) return step; 1203 + 1204 + if (init_provided) { 1205 + step = request_apply_init_options(js, init, req, &input_signal); 1206 + if (is_err(step)) { data_free(req); return step; } 1207 + } 1208 + 1209 + if ( 1210 + strcmp(req->mode, "no-cors") == 0 && 1211 + !is_cors_safelisted_method(req->method) 1212 + ) { 1213 + data_free(req); 1214 + return js_mkerr_typed(js, JS_ERR_TYPE, 1215 + "Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"); 1216 + } 1217 + 1218 + obj = js_mkobj(js); 1219 + js_set_proto_init(obj, g_request_proto); 1220 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 1221 + js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 1222 + 1223 + signal = abort_signal_create_dependent(js, input_signal); 1224 + if (is_err(signal)) { data_free(req); return signal; } 1225 + js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 1226 + 1227 + headers = request_create_ctor_headers(js, input); 1142 1228 if (is_err(headers)) { data_free(req); return headers; } 1229 + 1143 1230 if (init_provided) { 1144 1231 headers = request_apply_init_headers(js, init, headers); 1145 1232 if (is_err(headers)) { data_free(req); return headers; }
+71
src/modules/response.c
··· 1044 1044 return obj; 1045 1045 } 1046 1046 1047 + ant_value_t response_create_fetched( 1048 + ant_t *js, 1049 + int status, 1050 + const char *status_text, 1051 + const char *url, 1052 + ant_value_t headers_obj, 1053 + const uint8_t *body, 1054 + size_t body_len, 1055 + ant_value_t body_stream, 1056 + const char *body_type 1057 + ) { 1058 + ant_value_t obj = response_new(HEADERS_GUARD_IMMUTABLE); 1059 + ant_value_t headers = 0; 1060 + response_data_t *resp = NULL; 1061 + url_state_t parsed = {0}; 1062 + 1063 + if (is_err(obj)) return obj; 1064 + resp = get_data(obj); 1065 + 1066 + resp->status = status; 1067 + free(resp->status_text); 1068 + resp->status_text = strdup(status_text ? status_text : ""); 1069 + if (!resp->status_text) { 1070 + data_free(resp); 1071 + return js_mkerr(js, "out of memory"); 1072 + } 1073 + 1074 + if (url && parse_url_to_state(url, NULL, &parsed) == 0) { 1075 + url_state_clear(&resp->url); 1076 + resp->url = parsed; 1077 + resp->has_url = true; 1078 + resp->url_list_size = 1; 1079 + } else url_state_clear(&parsed); 1080 + 1081 + if (rs_is_stream(body_stream)) { 1082 + resp->body_is_stream = true; 1083 + resp->has_body = true; 1084 + js_set_slot_wb(js, obj, SLOT_RESPONSE_BODY_STREAM, body_stream); 1085 + } else { 1086 + if (body_len > 0) { 1087 + resp->body_data = malloc(body_len); 1088 + if (!resp->body_data) { 1089 + data_free(resp); 1090 + return js_mkerr(js, "out of memory"); 1091 + } 1092 + memcpy(resp->body_data, body, body_len); 1093 + } 1094 + resp->body_size = body_len; 1095 + resp->body_is_stream = false; 1096 + resp->has_body = body || body_len > 0; 1097 + } 1098 + 1099 + resp->body_type = body_type ? strdup(body_type) : NULL; 1100 + if (body_type && !resp->body_type) { 1101 + data_free(resp); 1102 + return js_mkerr(js, "out of memory"); 1103 + } 1104 + 1105 + headers = is_object_type(headers_obj) ? headers_obj : headers_create_empty(js); 1106 + if (is_err(headers)) { 1107 + data_free(resp); 1108 + return headers; 1109 + } 1110 + 1111 + headers_set_guard(headers, HEADERS_GUARD_IMMUTABLE); 1112 + headers_apply_guard(headers); 1113 + js_set_slot_wb(js, obj, SLOT_RESPONSE_HEADERS, headers); 1114 + 1115 + return obj; 1116 + } 1117 + 1047 1118 void init_response_module(void) { 1048 1119 ant_t *js = rt->js; 1049 1120 ant_value_t g = js_glob(js);
+23 -7
src/modules/url.c
··· 316 316 } 317 317 318 318 char *build_href(const url_state_t *s) { 319 - size_t len = strlen(s->protocol) + 2; 320 - len += strlen(s->hostname) + strlen(s->pathname) + strlen(s->search) + strlen(s->hash) + 32; 321 - 319 + bool has_authority = 320 + (s->hostname && *s->hostname) || 321 + (s->username && *s->username) || 322 + (s->password && *s->password) || 323 + (s->port && *s->port); 324 + 325 + bool opaque_like = !has_authority && !is_special_scheme(s->protocol); 326 + const char *pathname = s->pathname ? s->pathname : ""; 327 + size_t len = strlen(s->protocol) + strlen(pathname) + strlen(s->search) + strlen(s->hash) + 32; 328 + 329 + if (has_authority) len += strlen(s->hostname) + 2; 322 330 if (s->username && *s->username) len += strlen(s->username) + 1; 323 331 if (s->password && *s->password) len += strlen(s->password) + 1; 324 332 if (s->port && *s->port) len += strlen(s->port) + 1; 325 333 326 334 char *href = malloc(len); 327 335 if (!href) return strdup(""); 336 + 328 337 size_t pos = 0; 329 - pos += (size_t)sprintf(href + pos, "%s//", s->protocol); 338 + pos += (size_t)sprintf(href + pos, "%s", s->protocol); 339 + 340 + if (opaque_like) { 341 + if (pathname[0] == '/') pathname++; 342 + pos += (size_t)sprintf(href + pos, "%s%s%s", pathname, s->search, s->hash); 343 + href[pos] = '\0'; 344 + return href; 345 + } 330 346 347 + pos += (size_t)sprintf(href + pos, "//"); 331 348 if (s->username && *s->username) { 332 349 pos += (size_t)sprintf(href + pos, "%s", s->username); 333 350 if (s->password && *s->password) ··· 338 355 pos += (size_t)sprintf(href + pos, "%s", s->hostname); 339 356 if (s->port && *s->port) 340 357 pos += (size_t)sprintf(href + pos, ":%s", s->port); 341 - 342 - pos += (size_t)sprintf(href + pos, "%s%s%s", s->pathname, s->search, s->hash); 358 + 359 + pos += (size_t)sprintf(href + pos, "%s%s%s", pathname, s->search, s->hash); 343 360 href[pos] = '\0'; 344 - 345 361 return href; 346 362 } 347 363
+1
src/streams/pipes.c
··· 414 414 415 415 if (abort_signal_is_signal(signal)) { 416 416 ant_value_t listener = js_heavy_mkfun(js, pipe_abort_listener, state); 417 + js_set_slot(state, SLOT_PIPE_ABORT_LISTENER, listener); 417 418 abort_signal_add_listener(js, signal, listener); 418 419 } 419 420
+1 -14
src/types/fetch.d.ts
··· 1 - interface FetchResponse { 2 - status: number; 3 - ok: boolean; 4 - body: string; 5 - json(): unknown; 6 - } 7 - 8 - interface FetchOptions { 9 - method?: string; 10 - headers?: Record<string, string>; 11 - body?: string; 12 - } 13 - 14 - declare function fetch(url: string, options?: FetchOptions): Promise<FetchResponse>; 1 + declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;