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.

add Request constructor

+2487 -191
+20
examples/spec/formdata.js
··· 117 117 118 118 test('toStringTag', Object.prototype.toString.call(new FormData()), '[object FormData]'); 119 119 120 + console.log('\nmultipart parameter parsing\n'); 121 + 122 + const boundary = 'spec-boundary'; 123 + const multipartBody = 124 + `--${boundary}\r\n` + 125 + `Content-Disposition: form-data; name="field"\r\n\r\n` + 126 + `value\r\n` + 127 + `--${boundary}--\r\n`; 128 + 129 + const multipartReq = new Request('https://example.com/', { 130 + method: 'POST', 131 + headers: { 132 + 'Content-Type': `multipart/form-data; boundaryx=wrong; boundary="${boundary}"`, 133 + }, 134 + body: multipartBody, 135 + }); 136 + 137 + const multipartFd = await multipartReq.formData(); 138 + test('multipart boundary lookup skips longer prefix match', multipartFd.get('field'), 'value'); 139 + 120 140 summary();
+132
examples/spec/request.js
··· 1 + import { test, testThrows, summary } from './helpers.js'; 2 + 3 + console.log('Request Tests\n'); 4 + 5 + test('Request typeof', typeof Request, 'function'); 6 + test('Request toStringTag', Object.prototype.toString.call(new Request('https://example.com/')), '[object Request]'); 7 + testThrows('Request requires new', () => Request('https://example.com/')); 8 + 9 + const req0 = new Request('https://example.com/'); 10 + test('Request method default', req0.method, 'GET'); 11 + test('Request url', req0.url, 'https://example.com/'); 12 + test('Request headers same object', req0.headers === req0.headers, true); 13 + test('Request keepalive default', req0.keepalive, false); 14 + test('Request duplex getter', req0.duplex, 'half'); 15 + 16 + const hdrReq = new Request('https://example.com/', { 17 + headers: { 18 + 'Accept-Charset': 'blocked', 19 + 'X-Custom': 'ok', 20 + }, 21 + }); 22 + test('Request filters forbidden headers', hdrReq.headers.get('accept-charset'), null); 23 + test('Request keeps allowed headers', hdrReq.headers.get('x-custom'), 'ok'); 24 + 25 + const noCorsReq = new Request('https://example.com/', { 26 + mode: 'no-cors', 27 + headers: { 28 + 'Content-Type': 'text/plain;charset=UTF-8', 29 + 'X-Blocked': 'nope', 30 + }, 31 + }); 32 + test('Request no-cors keeps safelisted content-type', noCorsReq.headers.get('content-type'), 'text/plain;charset=UTF-8'); 33 + test('Request no-cors strips non-safelisted header', noCorsReq.headers.get('x-blocked'), null); 34 + 35 + testThrows( 36 + 'Request forbids keepalive with stream body', 37 + () => new Request('https://example.com/', { 38 + method: 'POST', 39 + body: new ReadableStream(), 40 + duplex: 'half', 41 + keepalive: true, 42 + }) 43 + ); 44 + 45 + testThrows( 46 + 'Request requires duplex for stream body', 47 + () => new Request('https://example.com/', { 48 + method: 'POST', 49 + body: new ReadableStream(), 50 + }) 51 + ); 52 + 53 + testThrows( 54 + 'Request rejects duplex full', 55 + () => new Request('https://example.com/', { 56 + method: 'POST', 57 + body: 'hello', 58 + duplex: 'full', 59 + }) 60 + ); 61 + 62 + testThrows( 63 + 'Request rejects forbidden method', 64 + () => new Request('https://example.com/', { method: 'TRACE' }) 65 + ); 66 + 67 + testThrows( 68 + 'Request rejects GET with direct body', 69 + () => new Request('https://example.com/', { method: 'GET', body: 'hello' }) 70 + ); 71 + 72 + const bodyReq = new Request('https://example.com/', { 73 + method: 'POST', 74 + body: 'hello world', 75 + }); 76 + const bodyStream = bodyReq.body; 77 + test('Request body is a ReadableStream', bodyStream instanceof ReadableStream, true); 78 + test('Request body getter is stable', bodyReq.body === bodyStream, true); 79 + test('Request bodyUsed before consume', bodyReq.bodyUsed, false); 80 + test('Request text after body access', await bodyReq.text(), 'hello world'); 81 + test('Request bodyUsed after consume', bodyReq.bodyUsed, true); 82 + test('Request body object preserved after consume', bodyReq.body === bodyStream, true); 83 + 84 + const srcReq = new Request('https://example.com/', { 85 + method: 'POST', 86 + body: 'copied body', 87 + }); 88 + const srcBody = srcReq.body; 89 + const copiedReq = new Request(srcReq); 90 + test('Request-from-Request disturbs source', srcReq.bodyUsed, true); 91 + test('Request-from-Request keeps source body object', srcReq.body === srcBody, true); 92 + test('Request-from-Request creates new body object', copiedReq.body === srcBody, false); 93 + test('Request-from-Request text', await copiedReq.text(), 'copied body'); 94 + 95 + const failReq = new Request('https://example.com/', { 96 + method: 'POST', 97 + body: 'will stay unused', 98 + }); 99 + testThrows( 100 + 'Request GET from Request with body throws', 101 + () => new Request(failReq, { method: 'GET' }) 102 + ); 103 + test('Request failed construction does not disturb source', failReq.bodyUsed, false); 104 + 105 + const usedReq = new Request('https://example.com/', { 106 + method: 'POST', 107 + body: 'replace me', 108 + }); 109 + await usedReq.text(); 110 + const replacedReq = new Request(usedReq, { 111 + body: 'replacement body', 112 + method: 'POST', 113 + }); 114 + test('Request override from disturbed request succeeds', await replacedReq.text(), 'replacement body'); 115 + 116 + const initReq = new Request('https://example.com/', { 117 + method: 'POST', 118 + body: 'init body', 119 + }); 120 + const reqFromInitReq = new Request('https://example.com/', initReq); 121 + test('Request init from Request copies method', reqFromInitReq.method, 'POST'); 122 + test('Request init from Request copies body', await reqFromInitReq.text(), 'init body'); 123 + 124 + const emptyTypeReq = new Request('https://example.com/', { 125 + method: 'POST', 126 + headers: [['Content-Type', '']], 127 + body: 'typed body', 128 + }); 129 + const emptyTypeBlob = await emptyTypeReq.blob(); 130 + test('Request empty content-type preserved on blob', emptyTypeBlob.type, ''); 131 + 132 + summary();
+2
include/ant.h
··· 173 173 void js_set_finalizer(ant_value_t obj, js_finalizer_fn fn); 174 174 175 175 ant_value_t js_get_slot(ant_value_t obj, internal_slot_t slot); 176 + ant_value_t js_promise_then(ant_t *js, ant_value_t promise, ant_value_t on_fulfilled, ant_value_t on_rejected); 177 + 176 178 void js_set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value); 177 179 void js_set_slot_wb(ant_t *, ant_value_t obj, internal_slot_t slot, ant_value_t value); 178 180
+24
include/common.h
··· 30 30 SLOT_PRIMITIVE, 31 31 SLOT_PROXY_REF, 32 32 SLOT_BUILTIN, 33 + SLOT_BRAND, 33 34 SLOT_DATA, 34 35 SLOT_CTOR, 35 36 SLOT_DEFAULT, ··· 71 72 SLOT_WS_ABORT, 72 73 SLOT_WS_READY, 73 74 SLOT_WS_SIGNAL, 75 + SLOT_HEADERS_GUARD, 76 + SLOT_REQUEST_HEADERS, 77 + SLOT_REQUEST_SIGNAL, 78 + SLOT_REQUEST_BODY_STREAM, 74 79 SLOT_MAX = 255 75 80 } internal_slot_t; 76 81 ··· 78 83 BUILTIN_NONE = 0, 79 84 BUILTIN_OBJECT = 1 80 85 } builtin_fn_id_t; 86 + 87 + typedef enum { 88 + BRAND_NONE = 0, 89 + BRAND_BLOB, 90 + BRAND_FILE, 91 + BRAND_HEADERS, 92 + BRAND_FORMDATA, 93 + BRAND_URLSEARCHPARAMS, 94 + BRAND_DATAVIEW, 95 + BRAND_REQUEST, 96 + BRAND_READABLE_STREAM, 97 + BRAND_READABLE_STREAM_READER, 98 + BRAND_READABLE_STREAM_CONTROLLER, 99 + BRAND_WRITABLE_STREAM, 100 + BRAND_WRITABLE_STREAM_WRITER, 101 + BRAND_WRITABLE_STREAM_CONTROLLER, 102 + BRAND_TRANSFORM_STREAM, 103 + BRAND_TRANSFORM_STREAM_CONTROLLER 104 + } object_brand_id_t; 81 105 82 106 static inline void *mantissa_chk(void *p, const char *func) { 83 107 if (!p || ((uintptr_t)p >> 47) == 0) goto ok;
+2
include/modules/abort.h
··· 12 12 13 13 bool abort_signal_is_signal(ant_value_t signal); 14 14 bool abort_signal_is_aborted(ant_value_t signal); 15 + 15 16 ant_value_t abort_signal_get_reason(ant_value_t signal); 17 + ant_value_t abort_signal_create_dependent(ant_t *js, ant_value_t source); 16 18 17 19 #endif
+2 -1
include/modules/blob.h
··· 14 14 } blob_data_t; 15 15 16 16 void init_blob_module(void); 17 + bool blob_is_blob(ant_t *js, ant_value_t obj); 17 18 18 19 blob_data_t *blob_get_data(ant_value_t obj); 19 - ant_value_t blob_create(ant_t *js, const uint8_t *data, size_t size, const char *type); 20 + ant_value_t blob_create(ant_t *js, const uint8_t *data, size_t size, const char *type); 20 21 21 22 extern ant_value_t g_blob_proto; 22 23 extern ant_value_t g_file_proto;
+5 -2
include/modules/buffer.h
··· 42 42 size_t byte_length; 43 43 } DataViewData; 44 44 45 + ant_value_t buffer_library(ant_t *js); 46 + 45 47 void init_buffer_module(void); 46 48 void cleanup_buffer_module(void); 47 49 void free_array_buffer_data(ArrayBufferData *data); 48 - size_t buffer_get_external_memory(void); 49 50 50 51 ArrayBufferData *create_array_buffer_data(size_t length); 51 52 ant_value_t create_arraybuffer_obj(ant_t *js, ArrayBufferData *buffer); ··· 61 62 const char *type_name, ant_value_t arraybuffer_obj 62 63 ); 63 64 64 - ant_value_t buffer_library(ant_t *js); 65 + size_t buffer_get_external_memory(void); 66 + bool buffer_is_dataview(ant_value_t obj); 67 + bool buffer_source_get_bytes(ant_t *js, ant_value_t value, const uint8_t **out, size_t *len); 65 68 66 69 #endif
+25
include/modules/formdata.h
··· 1 1 #ifndef FORMDATA_H 2 2 #define FORMDATA_H 3 3 4 + #include <stdint.h> 5 + #include <stddef.h> 6 + #include <stdbool.h> 7 + #include "types.h" 8 + 9 + typedef struct fd_entry { 10 + char *name; 11 + bool is_file; 12 + char *str_value; 13 + size_t val_idx; 14 + struct fd_entry *next; 15 + } fd_entry_t; 16 + 17 + typedef struct { 18 + fd_entry_t *head; 19 + fd_entry_t **tail; 20 + size_t count; 21 + } fd_data_t; 22 + 4 23 void init_formdata_module(void); 24 + bool formdata_is_empty(ant_value_t fd); 25 + bool formdata_is_formdata(ant_t *js, ant_value_t obj); 26 + 27 + ant_value_t formdata_create_empty(ant_t *js); 28 + ant_value_t formdata_append_string(ant_t *js, ant_value_t fd, ant_value_t name_v, ant_value_t value_v); 29 + ant_value_t formdata_append_file(ant_t *js, ant_value_t fd, ant_value_t name_v, ant_value_t blob_v, ant_value_t filename_v); 5 30 6 31 #endif
+19
include/modules/headers.h
··· 5 5 #include "modules/symbol.h" 6 6 7 7 extern ant_value_t g_headers_iter_proto; 8 + extern ant_value_t g_headers_proto; 9 + 10 + typedef enum { 11 + HEADERS_GUARD_NONE = 0, 12 + HEADERS_GUARD_REQUEST, 13 + HEADERS_GUARD_REQUEST_NO_CORS 14 + } headers_guard_t; 15 + 16 + headers_guard_t headers_get_guard(ant_value_t hdrs); 8 17 9 18 void init_headers_module(void); 19 + void headers_set_guard(ant_value_t hdrs, headers_guard_t guard); 20 + void headers_apply_guard(ant_value_t hdrs); 21 + void headers_append_if_missing(ant_value_t hdrs, const char *name, const char *value); 22 + 23 + bool headers_is_headers(ant_value_t obj); 24 + bool headers_copy_from(ant_t *js, ant_value_t dst, ant_value_t src); 10 25 bool advance_headers(ant_t *js, struct js_iter_t *it, ant_value_t *out); 26 + 27 + ant_value_t headers_create_empty(ant_t *js); 28 + ant_value_t headers_get_value(ant_t *js, ant_value_t hdrs, const char *name); 29 + ant_value_t headers_append_value(ant_t *js, ant_value_t hdrs, ant_value_t name_v, ant_value_t value_v); 11 30 12 31 #endif
+19
include/modules/multipart.h
··· 1 + #ifndef MULTIPART_H 2 + #define MULTIPART_H 3 + 4 + #include <stdbool.h> 5 + #include <stddef.h> 6 + #include <stdint.h> 7 + #include "types.h" 8 + 9 + uint8_t *formdata_serialize_multipart( 10 + ant_t *js, ant_value_t fd, 11 + size_t *out_size, char **out_boundary 12 + ); 13 + 14 + ant_value_t formdata_parse_body( 15 + ant_t *js, const uint8_t *data, size_t size, 16 + const char *body_type, bool has_body 17 + ); 18 + 19 + #endif
+43
include/modules/request.h
··· 1 + #ifndef REQUEST_H 2 + #define REQUEST_H 3 + 4 + #include <stdint.h> 5 + #include <stddef.h> 6 + #include <stdbool.h> 7 + #include "types.h" 8 + #include "modules/url.h" 9 + 10 + typedef struct { 11 + char *method; 12 + url_state_t url; 13 + char *referrer; 14 + char *referrer_policy; 15 + char *mode; 16 + char *credentials; 17 + char *cache; 18 + char *redirect; 19 + char *integrity; 20 + bool keepalive; 21 + bool reload_navigation; 22 + bool history_navigation; 23 + uint8_t *body_data; 24 + size_t body_size; 25 + char *body_type; 26 + bool body_is_stream; 27 + bool has_body; 28 + bool body_used; 29 + } request_data_t; 30 + 31 + extern ant_value_t g_request_proto; 32 + void init_request_module(void); 33 + 34 + request_data_t *request_get_data(ant_value_t obj); 35 + ant_value_t request_get_headers(ant_value_t obj); 36 + ant_value_t request_get_signal(ant_value_t obj); 37 + 38 + ant_value_t request_create( 39 + ant_t *js, const char *method, const char *url, ant_value_t headers, 40 + const uint8_t *body, size_t body_len, const char *body_type 41 + ); 42 + 43 + #endif
+1 -1
include/modules/url.h
··· 19 19 void init_url_module(void); 20 20 void url_state_clear(url_state_t *s); 21 21 void url_free_state(url_state_t *s); 22 + bool usp_is_urlsearchparams(ant_t *js, ant_value_t obj); 22 23 23 24 ant_value_t url_library(ant_t *js); 24 25 url_state_t *url_get_state(ant_value_t obj); ··· 26 27 27 28 char *build_href(const url_state_t *s); 28 29 char *usp_serialize(ant_t *js, ant_value_t usp); 29 - 30 30 char *form_urlencode(const char *str); 31 31 char *form_urlencode_n(const char *str, size_t len); 32 32 char *form_urldecode(const char *str);
+1
include/streams/pipes.h
··· 5 5 #include <stdbool.h> 6 6 7 7 void init_pipes_proto(ant_t *js, ant_value_t rs_proto); 8 + ant_value_t readable_stream_tee(ant_t *js, ant_value_t source); 8 9 9 10 ant_value_t readable_stream_pipe_to( 10 11 ant_t *js, ant_value_t source, ant_value_t dest,
+3
include/streams/readable.h
··· 41 41 bool rs_is_stream(ant_value_t obj); 42 42 bool rs_is_reader(ant_value_t obj); 43 43 bool rs_is_controller(ant_value_t obj); 44 + bool rs_stream_locked(ant_value_t stream_obj); 45 + bool rs_stream_disturbed(ant_value_t stream_obj); 46 + bool rs_stream_unusable(ant_value_t stream_obj); 44 47 45 48 rs_stream_t *rs_get_stream(ant_value_t obj); 46 49 rs_controller_t *rs_get_controller(ant_value_t obj);
+49 -45
src/ant.c
··· 9675 9675 9676 9676 static ant_value_t builtin_resolve_internal(ant_t *js, ant_value_t *args, int nargs); 9677 9677 static ant_value_t builtin_reject_internal(ant_t *js, ant_value_t *args, int nargs); 9678 - static void resolve_promise(ant_t *js, ant_value_t p, ant_value_t val); 9679 - static void reject_promise(ant_t *js, ant_value_t p, ant_value_t val); 9680 9678 9681 9679 static size_t strpromise(ant_t *js, ant_value_t value, char *buf, size_t len) { 9682 9680 uint32_t pid = get_promise_id(js, value); ··· 9756 9754 return func; 9757 9755 } 9758 9756 9759 - static ant_value_t mkpromise(ant_t *js) { 9757 + ant_value_t js_mkpromise(ant_t *js) { 9760 9758 ant_value_t obj = mkobj(js, 0); 9761 9759 if (is_err(obj)) return obj; 9762 9760 if (!get_promise_data(js, mkval(T_PROMISE, vdata(obj)), true)) ··· 9773 9771 } 9774 9772 9775 9773 return mkval(T_PROMISE, vdata(obj)); 9774 + } 9775 + 9776 + ant_value_t js_promise_then(ant_t *js, ant_value_t promise, ant_value_t on_fulfilled, ant_value_t on_rejected) { 9777 + ant_value_t args_then[2] = { on_fulfilled, on_rejected }; 9778 + ant_value_t saved_this = js->this_val; 9779 + 9780 + js->this_val = promise; 9781 + ant_value_t result = builtin_promise_then(js, args_then, 2); 9782 + js->this_val = saved_this; 9783 + 9784 + return result; 9776 9785 } 9777 9786 9778 9787 static inline void trigger_handlers(ant_t *js, ant_value_t p) { ··· 9812 9821 js->thrown_exists = false; 9813 9822 js->thrown_value = js_mkundef(); 9814 9823 js->thrown_stack = js_mkundef(); 9815 - reject_promise(js, h->nextPromise, reject_val); 9816 - } else resolve_promise(js, h->nextPromise, res); 9824 + js_reject_promise(js, h->nextPromise, reject_val); 9825 + } else js_resolve_promise(js, h->nextPromise, res); 9817 9826 } else { 9818 - if (state == 1) resolve_promise(js, h->nextPromise, val); 9819 - else reject_promise(js, h->nextPromise, val); 9827 + if (state == 1) js_resolve_promise(js, h->nextPromise, val); 9828 + else js_reject_promise(js, h->nextPromise, val); 9820 9829 } 9821 9830 } 9822 9831 ··· 9825 9834 gc_unroot_pending_promise(js_obj_ptr(promise)); 9826 9835 } 9827 9836 9828 - static void resolve_promise(ant_t *js, ant_value_t p, ant_value_t val) { 9837 + void js_resolve_promise(ant_t *js, ant_value_t p, ant_value_t val) { 9829 9838 GC_ROOT_SAVE(root_mark, js); 9830 9839 GC_ROOT_PIN(js, p); 9831 9840 GC_ROOT_PIN(js, val); ··· 9840 9849 if (vdata(js_as_obj(val)) == vdata(js_as_obj(p))) { 9841 9850 ant_value_t err = js_mkerr(js, "TypeError: Chaining cycle"); 9842 9851 GC_ROOT_PIN(js, err); 9843 - reject_promise(js, p, err); 9852 + js_reject_promise(js, p, err); 9844 9853 GC_ROOT_RESTORE(js, root_mark); 9845 9854 return; 9846 9855 } ··· 9848 9857 ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal); 9849 9858 GC_ROOT_PIN(js, res_fn); 9850 9859 if (is_err(res_fn)) { 9851 - reject_promise(js, p, res_fn); 9860 + js_reject_promise(js, p, res_fn); 9852 9861 GC_ROOT_RESTORE(js, root_mark); 9853 9862 return; 9854 9863 } ··· 9856 9865 ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal); 9857 9866 GC_ROOT_PIN(js, rej_fn); 9858 9867 if (is_err(rej_fn)) { 9859 - reject_promise(js, p, rej_fn); 9868 + js_reject_promise(js, p, rej_fn); 9860 9869 GC_ROOT_RESTORE(js, root_mark); 9861 9870 return; 9862 9871 } ··· 9879 9888 GC_ROOT_RESTORE(js, root_mark); 9880 9889 } 9881 9890 9882 - static void reject_promise(ant_t *js, ant_value_t p, ant_value_t val) { 9891 + void js_reject_promise(ant_t *js, ant_value_t p, ant_value_t val) { 9883 9892 ant_promise_state_t *pd = get_promise_data(js, p, false); 9884 9893 if (!pd || pd->state != 0) return; 9885 9894 ··· 9895 9904 ant_value_t me = js->current_func; 9896 9905 ant_value_t p = get_slot(me, SLOT_DATA); 9897 9906 if (vtype(p) != T_PROMISE) return js_mkundef(); 9898 - resolve_promise(js, p, nargs > 0 ? args[0] : js_mkundef()); 9907 + js_resolve_promise(js, p, nargs > 0 ? args[0] : js_mkundef()); 9899 9908 return js_mkundef(); 9900 9909 } 9901 9910 ··· 9903 9912 ant_value_t me = js->current_func; 9904 9913 ant_value_t p = get_slot(me, SLOT_DATA); 9905 9914 if (vtype(p) != T_PROMISE) return js_mkundef(); 9906 - reject_promise(js, p, nargs > 0 ? args[0] : js_mkundef()); 9915 + js_reject_promise(js, p, nargs > 0 ? args[0] : js_mkundef()); 9907 9916 return js_mkundef(); 9908 9917 } 9909 9918 ··· 9921 9930 ant_value_t executor = args[0]; 9922 9931 GC_ROOT_PIN(js, executor); 9923 9932 9924 - ant_value_t p = mkpromise(js); 9933 + ant_value_t p = js_mkpromise(js); 9925 9934 if (is_err(p)) { 9926 9935 GC_ROOT_RESTORE(js, root_mark); 9927 9936 return p; ··· 9962 9971 static ant_value_t builtin_Promise_resolve(ant_t *js, ant_value_t *args, int nargs) { 9963 9972 ant_value_t val = nargs > 0 ? args[0] : js_mkundef(); 9964 9973 if (vtype(val) == T_PROMISE) return val; 9965 - ant_value_t p = mkpromise(js); 9966 - resolve_promise(js, p, val); 9974 + ant_value_t p = js_mkpromise(js); 9975 + js_resolve_promise(js, p, val); 9967 9976 return p; 9968 9977 } 9969 9978 9970 9979 static ant_value_t builtin_Promise_reject(ant_t *js, ant_value_t *args, int nargs) { 9971 9980 ant_value_t val = nargs > 0 ? args[0] : js_mkundef(); 9972 - ant_value_t p = mkpromise(js); 9973 - reject_promise(js, p, val); 9981 + ant_value_t p = js_mkpromise(js); 9982 + js_reject_promise(js, p, val); 9974 9983 return p; 9975 9984 } 9976 9985 ··· 10016 10025 return err; 10017 10026 } 10018 10027 10019 - ant_value_t nextP = mkpromise(js); 10028 + ant_value_t nextP = js_mkpromise(js); 10020 10029 GC_ROOT_PIN(js, nextP); 10021 10030 if ((vtype(species_ctor) == T_FUNC || vtype(species_ctor) == T_CFUNC) 10022 10031 && !(vtype(species_ctor) == vtype(promise_ctor) ··· 10218 10227 10219 10228 static ant_value_t builtin_Promise_withResolvers(ant_t *js, ant_value_t *args, int nargs) { 10220 10229 GC_ROOT_SAVE(root_mark, js); 10221 - ant_value_t p = mkpromise(js); 10230 + ant_value_t p = js_mkpromise(js); 10222 10231 GC_ROOT_PIN(js, p); 10223 10232 10224 10233 ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal); ··· 10248 10257 static ant_value_t mkpromise_with_ctor(ant_t *js, ant_value_t ctor) { 10249 10258 GC_ROOT_SAVE(root_mark, js); 10250 10259 GC_ROOT_PIN(js, ctor); 10251 - ant_value_t p = mkpromise(js); 10260 + ant_value_t p = js_mkpromise(js); 10252 10261 GC_ROOT_PIN(js, p); 10253 10262 if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) { 10254 10263 GC_ROOT_RESTORE(js, root_mark); ··· 10286 10295 10287 10296 if (remaining == 0) { 10288 10297 ant_value_t result_promise = get_slot(tracker, SLOT_DATA); 10289 - resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 10298 + js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 10290 10299 } 10291 10300 10292 10301 return js_mkundef(); ··· 10298 10307 ant_value_t result_promise = get_slot(tracker, SLOT_DATA); 10299 10308 10300 10309 ant_value_t reason = nargs > 0 ? args[0] : js_mkundef(); 10301 - reject_promise(js, result_promise, reason); 10310 + js_reject_promise(js, result_promise, reason); 10302 10311 10303 10312 return js_mkundef(); 10304 10313 } ··· 10413 10422 } 10414 10423 10415 10424 if (len == 0) { 10416 - resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 10425 + js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 10417 10426 GC_ROOT_RESTORE(js, root_mark); 10418 10427 return result_promise; 10419 10428 } ··· 10437 10446 GC_ROOT_PIN(js, item); 10438 10447 10439 10448 if (vtype(item) != T_PROMISE) { 10440 - resolve_promise(js, pctx->result_promise, item); 10449 + js_resolve_promise(js, pctx->result_promise, item); 10441 10450 pctx->settled = true; 10442 10451 GC_ROOT_RESTORE(js, root_mark); 10443 10452 return ITER_BREAK; ··· 10445 10454 10446 10455 ant_promise_state_t *pd = get_promise_data(js, item, false); 10447 10456 if (pd) { 10448 - if (pd->state == 1) { 10449 - resolve_promise(js, pctx->result_promise, pd->value); 10450 - pctx->settled = true; 10451 - GC_ROOT_RESTORE(js, root_mark); 10452 - return ITER_BREAK; 10453 - } else if (pd->state == 2) { 10454 - reject_promise(js, pctx->result_promise, pd->value); 10455 - pctx->settled = true; 10456 - GC_ROOT_RESTORE(js, root_mark); 10457 - return ITER_BREAK; 10458 - } 10459 - } 10457 + if (pd->state == 1) { 10458 + js_resolve_promise(js, pctx->result_promise, pd->value); 10459 + pctx->settled = true; 10460 + GC_ROOT_RESTORE(js, root_mark); 10461 + return ITER_BREAK; 10462 + } else if (pd->state == 2) { 10463 + js_reject_promise(js, pctx->result_promise, pd->value); 10464 + pctx->settled = true; 10465 + GC_ROOT_RESTORE(js, root_mark); 10466 + return ITER_BREAK; 10467 + }} 10460 10468 10461 10469 ant_value_t then_args[] = { pctx->resolve_fn, pctx->reject_fn }; 10462 10470 ant_value_t saved_this = js->this_val; ··· 10543 10551 static bool promise_any_try_resolve(ant_t *js, ant_value_t tracker, ant_value_t value) { 10544 10552 if (js_truthy(js, js_get(js, tracker, "resolved"))) return false; 10545 10553 js_set(js, tracker, "resolved", js_true); 10546 - resolve_promise(js, get_slot(tracker, SLOT_DATA), value); 10554 + js_resolve_promise(js, get_slot(tracker, SLOT_DATA), value); 10547 10555 return true; 10548 10556 } 10549 10557 ··· 10554 10562 int remaining = (int)tod(js_get(js, tracker, "remaining")) - 1; 10555 10563 js_set(js, tracker, "remaining", tov((double)remaining)); 10556 10564 10557 - if (remaining == 0) reject_promise(js, get_slot(tracker, SLOT_DATA), mk_aggregate_error(js, errors)); 10565 + if (remaining == 0) js_reject_promise(js, get_slot(tracker, SLOT_DATA), mk_aggregate_error(js, errors)); 10558 10566 } 10559 10567 10560 10568 static ant_value_t builtin_Promise_any_resolve_handler(ant_t *js, ant_value_t *args, int nargs) { ··· 10593 10601 return ret; 10594 10602 } 10595 10603 10596 - ant_value_t result_promise = mkpromise(js); 10604 + ant_value_t result_promise = js_mkpromise(js); 10597 10605 GC_ROOT_PIN(js, result_promise); 10598 10606 ant_value_t tracker = mkobj(js, 0); 10599 10607 GC_ROOT_PIN(js, tracker); ··· 12560 12568 iter->off = 0; 12561 12569 iter->ctx = NULL; 12562 12570 } 12563 - 12564 - ant_value_t js_mkpromise(ant_t *js) { return mkpromise(js); } 12565 - void js_resolve_promise(ant_t *js, ant_value_t promise, ant_value_t value) { resolve_promise(js, promise, value); } 12566 - void js_reject_promise(ant_t *js, ant_value_t promise, ant_value_t value) { reject_promise(js, promise, value); } 12567 12571 12568 12572 static void check_unhandled_rejections_list(ant_t *js, ant_object_t *list) { 12569 12573 for (ant_object_t *obj = list; obj; obj = obj->next) {
+2
src/main.c
··· 39 39 #include "modules/timer.h" 40 40 #include "modules/json.h" 41 41 #include "modules/fetch.h" 42 + #include "modules/request.h" 42 43 #include "modules/shell.h" 43 44 #include "modules/process.h" 44 45 #include "modules/tty.h" ··· 612 613 init_fs_module(); 613 614 init_atomics_module(); 614 615 init_crypto_module(); 616 + init_request_module(); 615 617 init_fetch_module(); 616 618 init_console_module(); 617 619 init_json_module();
+15
src/modules/abort.c
··· 267 267 return composite; 268 268 } 269 269 270 + ant_value_t abort_signal_create_dependent(ant_t *js, ant_value_t source) { 271 + ant_value_t composite = make_new_signal(js); 272 + 273 + if (is_err(composite)) return composite; 274 + if (vtype(source) != T_OBJ && vtype(source) != T_ARR) return composite; 275 + 276 + abort_signal_data_t *d = get_signal_data(source); 277 + if (!d) return composite; 278 + 279 + if (d->aborted) signal_mark_aborted(js, composite, d->reason); 280 + else utarray_push_back(d->followers, &composite); 281 + 282 + return composite; 283 + } 284 + 270 285 static void abort_timeout_close_cb(uv_handle_t *h) { 271 286 abort_timeout_entry_t *entry = (abort_timeout_entry_t *)h->data; 272 287 if (entry) entry->closed = 1;
+13
src/modules/blob.c
··· 20 20 ant_value_t g_blob_proto = 0; 21 21 ant_value_t g_file_proto = 0; 22 22 23 + bool blob_is_blob(ant_t *js, ant_value_t obj) { 24 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 25 + if (vtype(brand) != T_NUM) return false; 26 + int id = (int)js_getnum(brand); 27 + return id == BRAND_BLOB || id == BRAND_FILE; 28 + } 29 + 23 30 blob_data_t *blob_get_data(ant_value_t obj) { 24 31 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 25 32 if (vtype(slot) != T_NUM) return NULL; ··· 167 174 blob_data_t *bd = blob_data_new(data, size, type); 168 175 if (!bd) return js_mkerr(js, "out of memory"); 169 176 ant_value_t obj = js_mkobj(js); 177 + 170 178 js_set_proto_init(obj, g_blob_proto); 179 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_BLOB)); 171 180 js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 172 181 js_set_finalizer(obj, blob_finalize); 182 + 173 183 return obj; 174 184 } 175 185 ··· 347 357 ant_value_t proto = js_instance_proto_from_new_target(js, g_blob_proto); 348 358 if (is_object_type(proto)) js_set_proto_init(obj, proto); 349 359 360 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_BLOB)); 350 361 js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 351 362 js_set_finalizer(obj, blob_finalize); 363 + 352 364 return obj; 353 365 } 354 366 ··· 416 428 ant_value_t proto = js_instance_proto_from_new_target(js, g_file_proto); 417 429 if (is_object_type(proto)) js_set_proto_init(obj, proto); 418 430 431 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FILE)); 419 432 js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 420 433 js_set_finalizer(obj, blob_finalize); 421 434
+40 -2
src/modules/buffer.c
··· 27 27 static uint8_t *ta_arena = NULL; 28 28 static size_t ta_arena_offset = 0; 29 29 30 + static size_t buffer_registry_count = 0; 31 + static size_t buffer_registry_cap = 0; 32 + 30 33 static ArrayBufferData **buffer_registry = NULL; 31 34 static ant_value_t g_typedarray_iter_proto = 0; 32 35 33 - static size_t buffer_registry_count = 0; 34 - static size_t buffer_registry_cap = 0; 36 + bool buffer_is_dataview(ant_value_t obj) { 37 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 38 + return vtype(brand) == T_NUM && (int)js_getnum(brand) == BRAND_DATAVIEW; 39 + } 40 + 41 + bool buffer_source_get_bytes(ant_t *js, ant_value_t value, const uint8_t **out, size_t *len) { 42 + ant_value_t slot = js_get_slot(value, SLOT_BUFFER); 43 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); 44 + 45 + if (ta) { 46 + if (!ta->buffer || ta->buffer->is_detached) { *out = NULL; *len = 0; return true; } 47 + *out = ta->buffer->data + ta->byte_offset; 48 + *len = ta->byte_length; 49 + return true; 50 + } 51 + 52 + if (vtype(slot) == T_NUM) { 53 + ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 54 + if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 55 + *out = ab->data; 56 + *len = ab->length; 57 + return true; 58 + } 59 + 60 + if (buffer_is_dataview(value)) { 61 + ant_value_t dv_data_val = js_get_slot(value, SLOT_DATA); 62 + if (vtype(dv_data_val) != T_NUM) return false; 63 + DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val); 64 + if (!dv || !dv->buffer || dv->buffer->is_detached) { *out = NULL; *len = 0; return true; } 65 + *out = dv->buffer->data + dv->byte_offset; 66 + *len = dv->byte_length; 67 + return true; 68 + } 69 + 70 + return false; 71 + } 35 72 36 73 static bool advance_typedarray(ant_t *js, js_iter_t *it, ant_value_t *out) { 37 74 ant_value_t iter = it->iterator; ··· 1206 1243 ant_value_t proto = js_get_ctor_proto(js, "DataView", 8); 1207 1244 if (is_special_object(proto)) js_set_proto_init(obj, proto); 1208 1245 1246 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_DATAVIEW)); 1209 1247 js_set_slot(obj, SLOT_DATA, ANT_PTR(dv_data)); 1210 1248 js_mkprop_fast(js, obj, "buffer", 6, args[0]); 1211 1249 js_set_descriptor(js, obj, "buffer", 6, 0);
+59 -15
src/modules/formdata.c
··· 1 1 #include <stdlib.h> 2 2 #include <string.h> 3 + #include <strings.h> 3 4 #include <stdint.h> 4 5 #include <time.h> 5 6 ··· 14 15 #include "modules/blob.h" 15 16 #include "modules/symbol.h" 16 17 17 - typedef struct fd_entry { 18 - char *name; 19 - bool is_file; 20 - char *str_value; 21 - size_t val_idx; 22 - struct fd_entry *next; 23 - } fd_entry_t; 24 - 25 - typedef struct { 26 - fd_entry_t *head; 27 - fd_entry_t **tail; 28 - size_t count; 29 - } fd_data_t; 30 - 31 18 typedef struct { 32 19 size_t index; 33 20 int kind; ··· 41 28 42 29 static ant_value_t g_formdata_proto = 0; 43 30 static ant_value_t g_formdata_iter_proto = 0; 31 + static fd_data_t *get_fd_data(ant_value_t obj); 32 + 33 + bool formdata_is_formdata(ant_t *js, ant_value_t obj) { 34 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 35 + return vtype(brand) == T_NUM && (int)js_getnum(brand) == BRAND_FORMDATA; 36 + } 37 + 38 + bool formdata_is_empty(ant_value_t fd) { 39 + fd_data_t *d = get_fd_data(fd); 40 + return d ? d->count == 0 : true; 41 + } 44 42 45 43 static fd_data_t *fd_data_new(void) { 46 44 fd_data_t *d = calloc(1, sizeof(fd_data_t)); ··· 135 133 }} 136 134 } 137 135 136 + ant_value_t formdata_create_empty(ant_t *js) { 137 + fd_data_t *d = fd_data_new(); 138 + if (!d) return js_mkerr(js, "out of memory"); 139 + 140 + ant_value_t obj = js_mkobj(js); 141 + js_set_proto_init(obj, g_formdata_proto); 142 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FORMDATA)); 143 + js_set_slot(obj, SLOT_DATA, ANT_PTR(d)); 144 + 145 + ant_value_t vals = js_mkarr(js); 146 + js_set_slot_wb(js, obj, SLOT_ENTRIES, vals); 147 + js_set_finalizer(obj, formdata_finalize); 148 + 149 + return obj; 150 + } 151 + 138 152 static ant_value_t entry_to_js_value(ant_t *js, ant_value_t values_arr, fd_entry_t *e) { 139 153 if (!e->is_file) 140 154 return js_mkstr(js, e->str_value ? e->str_value : "", strlen(e->str_value ? e->str_value : "")); ··· 203 217 js_arr_push(js, values_arr, stored_val); 204 218 205 219 return fd_append_file(d, name, idx) ? js_mkundef() : js_mkerr(js, "out of memory"); 220 + } 221 + 222 + ant_value_t formdata_append_string(ant_t *js, ant_value_t fd, ant_value_t name_v, ant_value_t value_v) { 223 + fd_data_t *d = get_fd_data(fd); 224 + if (!d) return js_mkerr(js, "Invalid FormData object"); 225 + 226 + const char *name = resolve_name(js, &name_v); 227 + if (!name) return name_v; 228 + 229 + if (vtype(value_v) != T_STR) { 230 + value_v = js_tostring_val(js, value_v); 231 + if (is_err(value_v)) return value_v; 232 + } 233 + 234 + return fd_append_str(d, name, js_getstr(js, value_v, NULL)) 235 + ? js_mkundef() 236 + : js_mkerr(js, "out of memory" 237 + ); 238 + } 239 + 240 + ant_value_t formdata_append_file(ant_t *js, ant_value_t fd, ant_value_t name_v, ant_value_t blob_v, ant_value_t filename_v) { 241 + fd_data_t *d = get_fd_data(fd); 242 + if (!d) return js_mkerr(js, "Invalid FormData object"); 243 + const char *name = resolve_name(js, &name_v); 244 + if (!name) return name_v; 245 + ant_value_t values_arr = get_fd_values(fd); 246 + return extract_file_entry(js, d, values_arr, name, blob_v, filename_v, false); 206 247 } 207 248 208 249 static ant_value_t js_formdata_append(ant_t *js, ant_value_t *args, int nargs) { ··· 419 460 420 461 ant_value_t obj = js_mkobj(js); 421 462 ant_value_t proto = js_instance_proto_from_new_target(js, g_formdata_proto); 463 + 422 464 if (is_object_type(proto)) js_set_proto_init(obj, proto); 423 - 465 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FORMDATA)); 424 466 js_set_slot(obj, SLOT_DATA, ANT_PTR(d)); 467 + 425 468 ant_value_t vals = js_mkarr(js); 426 469 js_set_slot_wb(js, obj, SLOT_ENTRIES, vals); 427 470 js_set_finalizer(obj, formdata_finalize); 471 + 428 472 return obj; 429 473 } 430 474
+277 -4
src/modules/headers.c
··· 42 42 ITER_VALUES = 2 43 43 }; 44 44 45 - static ant_value_t g_headers_proto = 0; 46 - ant_value_t g_headers_iter_proto = 0; 45 + ant_value_t g_headers_proto = 0; 46 + ant_value_t g_headers_iter_proto = 0; 47 47 48 48 static hdr_list_t *list_new(void) { 49 49 hdr_list_t *l = ant_calloc(sizeof(hdr_list_t)); ··· 67 67 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 68 68 if (vtype(slot) != T_NUM) return NULL; 69 69 return (hdr_list_t *)(uintptr_t)(size_t)js_getnum(slot); 70 + } 71 + 72 + static headers_guard_t get_guard(ant_value_t obj) { 73 + ant_value_t slot = js_get_slot(obj, SLOT_HEADERS_GUARD); 74 + if (vtype(slot) != T_NUM) return HEADERS_GUARD_NONE; 75 + return (headers_guard_t)(int)js_getnum(slot); 76 + } 77 + 78 + bool headers_is_headers(ant_value_t obj) { 79 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 80 + return vtype(brand) == T_NUM && (int)js_getnum(brand) == BRAND_HEADERS; 70 81 } 71 82 72 83 static bool is_token_char(unsigned char c) { ··· 121 132 return out; 122 133 } 123 134 135 + typedef struct { 136 + const char *name; 137 + bool prefix; 138 + } header_rule_t; 139 + 140 + static const header_rule_t k_forbidden_request_headers[] = { 141 + { "accept-charset", false }, 142 + { "accept-encoding", false }, 143 + { "access-control-request-headers", false }, 144 + { "access-control-request-method", false }, 145 + { "connection", false }, 146 + { "content-length", false }, 147 + { "cookie", false }, 148 + { "cookie2", false }, 149 + { "date", false }, 150 + { "dnt", false }, 151 + { "expect", false }, 152 + { "host", false }, 153 + { "keep-alive", false }, 154 + { "origin", false }, 155 + { "referer", false }, 156 + { "set-cookie", false }, 157 + { "te", false }, 158 + { "trailer", false }, 159 + { "transfer-encoding", false }, 160 + { "upgrade", false }, 161 + { "via", false }, 162 + { "proxy-", true }, 163 + { "sec-", true }, 164 + }; 165 + 166 + static const char *k_cors_safelisted_content_types[] = { 167 + "application/x-www-form-urlencoded", 168 + "multipart/form-data", 169 + "text/plain", 170 + }; 171 + 172 + static const char *k_no_cors_safelisted_names[] = { 173 + "accept", 174 + "accept-language", 175 + "content-language", 176 + }; 177 + 178 + static bool matches_rule(const char *name, const header_rule_t *rules, size_t count) { 179 + for (size_t i = 0; i < count; i++) { 180 + size_t len = strlen(rules[i].name); 181 + if (rules[i].prefix) { if (strncmp(name, rules[i].name, len) == 0) return true; } 182 + else if (strcmp(name, rules[i].name) == 0) return true; 183 + } 184 + return false; 185 + } 186 + 187 + static bool matches_string(const char *value, const char *const *list, size_t count) { 188 + for (size_t i = 0; i < count; i++) { 189 + if (strcmp(value, list[i]) == 0) return true; 190 + } 191 + return false; 192 + } 193 + 194 + static bool is_forbidden_request_header_name(const char *lower_name) { 195 + return matches_rule(lower_name, k_forbidden_request_headers, 196 + sizeof(k_forbidden_request_headers) / sizeof(k_forbidden_request_headers[0])); 197 + } 198 + 199 + static bool is_cors_safelisted_content_type_value(const char *value) { 200 + char *lower = lowercase_dup(value ? value : ""); 201 + if (!lower) return false; 202 + char *semi = strchr(lower, ';'); 203 + 204 + if (!semi) { 205 + bool ok = matches_string( 206 + lower, 207 + k_cors_safelisted_content_types, 208 + sizeof(k_cors_safelisted_content_types) / sizeof(k_cors_safelisted_content_types[0]) 209 + ); 210 + free(lower); 211 + return ok; 212 + } 213 + 214 + *semi++ = '\0'; 215 + while (*semi == ' ' || *semi == '\t') semi++; 216 + bool essence_ok = matches_string( 217 + lower, 218 + k_cors_safelisted_content_types, 219 + sizeof(k_cors_safelisted_content_types) / sizeof(k_cors_safelisted_content_types[0]) 220 + ); 221 + 222 + bool param_ok = strcmp(semi, "charset=utf-8") == 0; 223 + free(lower); 224 + 225 + return essence_ok && param_ok; 226 + } 227 + 228 + static bool is_no_cors_safelisted_name_value(const char *lower_name, const char *value) { 229 + if ( 230 + matches_string( 231 + lower_name, k_no_cors_safelisted_names, 232 + sizeof(k_no_cors_safelisted_names) / sizeof(k_no_cors_safelisted_names[0])) 233 + ) return true; 234 + 235 + if (strcmp(lower_name, "content-type") == 0) 236 + return value && value[0] && is_cors_safelisted_content_type_value(value); 237 + 238 + return false; 239 + } 240 + 241 + static bool header_allowed_for_guard(const char *lower_name, const char *value, headers_guard_t guard) { 242 + if (guard == HEADERS_GUARD_NONE) return true; 243 + if (is_forbidden_request_header_name(lower_name)) return false; 244 + if (guard == HEADERS_GUARD_REQUEST_NO_CORS) return is_no_cors_safelisted_name_value(lower_name, value); 245 + return true; 246 + } 247 + 248 + static void list_apply_guard(hdr_list_t *l, headers_guard_t guard) { 249 + if (!l || guard == HEADERS_GUARD_NONE) return; 250 + 251 + hdr_entry_t **pp = &l->head; 252 + l->tail = &l->head; 253 + 254 + while (*pp) { 255 + hdr_entry_t *cur = *pp; 256 + if (!header_allowed_for_guard(cur->name, cur->value, guard)) { 257 + *pp = cur->next; 258 + free(cur->name); 259 + free(cur->value); 260 + free(cur); 261 + l->count--; 262 + continue; 263 + } 264 + 265 + l->tail = &cur->next; 266 + pp = &cur->next; 267 + } 268 + } 269 + 124 270 static void list_append_raw(hdr_list_t *l, const char *lower_name, const char *value) { 125 271 hdr_entry_t *e = ant_calloc(sizeof(hdr_entry_t)); 126 272 if (!e) return; ··· 237 383 238 384 list_append_raw(l, lower, norm); 239 385 free(lower); free(norm); 386 + return js_mkundef(); 387 + } 388 + 389 + ant_value_t headers_append_value(ant_t *js, ant_value_t hdrs, ant_value_t name_v, ant_value_t value_v) { 390 + hdr_list_t *l = get_list(hdrs); 391 + if (!l) return js_mkerr(js, "Invalid Headers object"); 392 + ant_value_t r = headers_append_pair(js, l, name_v, value_v); 393 + if (is_err(r)) return r; 394 + list_apply_guard(l, get_guard(hdrs)); 240 395 return js_mkundef(); 241 396 } 242 397 ··· 336 491 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers.append requires 2 arguments"); 337 492 hdr_list_t *l = get_list(js->this_val); 338 493 if (!l) return js_mkerr(js, "Invalid Headers object"); 339 - return headers_append_pair(js, l, args[0], args[1]); 494 + ant_value_t r = headers_append_pair(js, l, args[0], args[1]); 495 + if (is_err(r)) return r; 496 + list_apply_guard(l, get_guard(js->this_val)); 497 + return js_mkundef(); 340 498 } 341 499 342 500 static ant_value_t js_headers_set(ant_t *js, ant_value_t *args, int nargs) { ··· 363 521 if (!lower) { free(norm); return js_mkerr(js, "out of memory"); } 364 522 365 523 list_delete_name(l, lower); 366 - list_append_raw(l, lower, norm); 524 + if (header_allowed_for_guard(lower, norm, get_guard(js->this_val))) 525 + list_append_raw(l, lower, norm); 367 526 free(lower); free(norm); 368 527 return js_mkundef(); 369 528 } ··· 556 715 ant_value_t proto = js_instance_proto_from_new_target(js, g_headers_proto); 557 716 if (is_object_type(proto)) js_set_proto_init(obj, proto); 558 717 718 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); 559 719 js_set_slot(obj, SLOT_DATA, ANT_PTR(l)); 720 + js_set_slot(obj, SLOT_HEADERS_GUARD, js_mknum(HEADERS_GUARD_NONE)); 721 + 560 722 return obj; 723 + } 724 + 725 + ant_value_t headers_create_empty(ant_t *js) { 726 + hdr_list_t *l = list_new(); 727 + if (!l) return js_mkerr(js, "out of memory"); 728 + 729 + ant_value_t obj = js_mkobj(js); 730 + js_set_proto_init(obj, g_headers_proto); 731 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_HEADERS)); 732 + js_set_slot(obj, SLOT_DATA, ANT_PTR(l)); 733 + js_set_slot(obj, SLOT_HEADERS_GUARD, js_mknum(HEADERS_GUARD_NONE)); 734 + 735 + return obj; 736 + } 737 + 738 + bool headers_copy_from(ant_t *js, ant_value_t dst, ant_value_t src) { 739 + hdr_list_t *src_list = get_list(src); 740 + hdr_list_t *dst_list = get_list(dst); 741 + 742 + if (!dst_list) return false; 743 + if (!src_list) return true; 744 + 745 + for (hdr_entry_t *e = src_list->head; e; e = e->next) 746 + list_append_raw(dst_list, e->name, e->value); 747 + return true; 748 + } 749 + 750 + void headers_set_guard(ant_value_t hdrs, headers_guard_t guard) { 751 + js_set_slot(hdrs, SLOT_HEADERS_GUARD, js_mknum(guard)); 752 + } 753 + 754 + headers_guard_t headers_get_guard(ant_value_t hdrs) { 755 + return get_guard(hdrs); 756 + } 757 + 758 + void headers_apply_guard(ant_value_t hdrs) { 759 + list_apply_guard(get_list(hdrs), get_guard(hdrs)); 760 + } 761 + 762 + void headers_append_if_missing(ant_value_t hdrs, const char *name, const char *value) { 763 + hdr_list_t *l = get_list(hdrs); 764 + if (!l || !name || !value) return; 765 + char *lower = lowercase_dup(name); 766 + if (!lower) return; 767 + for (hdr_entry_t *e = l->head; e; e = e->next) { 768 + if (strcmp(e->name, lower) == 0) { free(lower); return; } 769 + } 770 + list_append_raw(l, lower, value); 771 + free(lower); 772 + } 773 + 774 + ant_value_t headers_get_value(ant_t *js, ant_value_t hdrs, const char *name) { 775 + hdr_list_t *l = get_list(hdrs); 776 + 777 + if (!l) return js_mknull(); 778 + if (!is_valid_name(name)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name"); 779 + 780 + char *lower = lowercase_dup(name); 781 + if (!lower) return js_mkerr(js, "out of memory"); 782 + 783 + if (strcmp(lower, "set-cookie") == 0) { 784 + for (hdr_entry_t *e = l->head; e; e = e->next) { 785 + if (strcmp(e->name, lower) == 0) { 786 + ant_value_t ret = js_mkstr(js, e->value, strlen(e->value)); 787 + free(lower); 788 + return ret; 789 + }} 790 + free(lower); 791 + return js_mknull(); 792 + } 793 + 794 + size_t total = 0; 795 + int count = 0; 796 + 797 + for (hdr_entry_t *e = l->head; e; e = e->next) { 798 + if (strcmp(e->name, lower) == 0) { 799 + if (count > 0) total += 2; 800 + total += strlen(e->value); 801 + count++; 802 + }} 803 + 804 + if (count == 0) { 805 + free(lower); 806 + return js_mknull(); 807 + } 808 + 809 + char *combined = malloc(total + 1); 810 + if (!combined) { 811 + free(lower); 812 + return js_mkerr(js, "out of memory"); 813 + } 814 + 815 + size_t pos = 0; 816 + int seen = 0; 817 + 818 + for (hdr_entry_t *e = l->head; e; e = e->next) { 819 + if (strcmp(e->name, lower) == 0) { 820 + if (seen > 0) { combined[pos++] = ','; combined[pos++] = ' '; } 821 + size_t vl = strlen(e->value); 822 + memcpy(combined + pos, e->value, vl); 823 + pos += vl; 824 + seen++; 825 + }} 826 + 827 + combined[pos] = '\0'; 828 + free(lower); 829 + 830 + ant_value_t ret = js_mkstr(js, combined, pos); 831 + free(combined); 832 + 833 + return ret; 561 834 } 562 835 563 836 void init_headers_module(void) {
+546
src/modules/multipart.c
··· 1 + #include <stdbool.h> 2 + #include <stdint.h> 3 + #include <stdio.h> 4 + #include <stdlib.h> 5 + #include <string.h> 6 + #include <strings.h> 7 + 8 + #include "ant.h" 9 + #include "errors.h" 10 + #include "internal.h" 11 + 12 + #include "modules/blob.h" 13 + #include "modules/formdata.h" 14 + #include "modules/multipart.h" 15 + #include "modules/url.h" 16 + 17 + static bool ct_is_type(const char *ct, const char *type) { 18 + size_t type_len = 0; 19 + 20 + if (!ct || !type) return false; 21 + 22 + while (*ct == ' ' || *ct == '\t') ct++; 23 + type_len = strlen(type); 24 + if (strncasecmp(ct, type, type_len) != 0) return false; 25 + 26 + ct += type_len; 27 + return *ct == '\0' || *ct == ';' || *ct == ' ' || *ct == '\t'; 28 + } 29 + 30 + static const char *ct_find_next_param(const char *p) { 31 + scan: 32 + if (*p == '\0') return NULL; 33 + if (*p == ';') return p; 34 + if (*p != '"') { 35 + p++; 36 + goto scan; 37 + } 38 + 39 + quoted: 40 + p++; 41 + if (*p == '\0') return NULL; 42 + if (*p == '\\') goto escaped; 43 + if (*p == '"') { 44 + p++; 45 + goto scan; 46 + } 47 + goto quoted; 48 + 49 + escaped: 50 + p++; 51 + if (*p == '\0') return NULL; 52 + p++; 53 + goto quoted; 54 + } 55 + 56 + static char *ct_get_param_dup(const char *ct, const char *name) { 57 + const char *p = NULL; 58 + const char *param_name = NULL; 59 + const char *param_name_end = NULL; 60 + 61 + char *buf = NULL; 62 + size_t name_len = 0; 63 + size_t len = 0; 64 + size_t cap = 0; 65 + char ch = '\0'; 66 + 67 + if (!ct || !name) return NULL; 68 + 69 + name_len = strlen(name); 70 + if (name_len == 0) return NULL; 71 + 72 + p = strchr(ct, ';'); 73 + 74 + next_param: 75 + if (!p) return NULL; 76 + p++; 77 + 78 + while (*p == ' ' || *p == '\t') p++; 79 + if (*p == '\0') return NULL; 80 + 81 + param_name = p; 82 + while (*p && *p != '=' && *p != ';') p++; 83 + param_name_end = p; 84 + 85 + while ( 86 + param_name_end > param_name && 87 + (param_name_end[-1] == ' ' || param_name_end[-1] == '\t') 88 + ) param_name_end--; 89 + 90 + if (*p != '=') goto skip_param; 91 + if ((size_t)(param_name_end - param_name) != name_len) goto skip_param; 92 + if (strncasecmp(param_name, name, name_len) != 0) goto skip_param; 93 + 94 + p++; 95 + while (*p == ' ' || *p == '\t') p++; 96 + if (*p == '\0') return strdup(""); 97 + 98 + if (*p == '"') { 99 + p++; 100 + goto quoted; 101 + } 102 + 103 + unquoted: 104 + if (*p == '\0' || *p == ';' || *p == ' ' || *p == '\t') { 105 + buf = malloc(len + 1); 106 + if (!buf) return NULL; 107 + if (len != 0) memcpy(buf, p - len, len); 108 + buf[len] = '\0'; 109 + return buf; 110 + } 111 + p++; 112 + len++; 113 + goto unquoted; 114 + 115 + quoted: 116 + if (*p == '\0') goto fail; 117 + if (*p == '"') goto done; 118 + if (*p == '\\') goto quoted_escape; 119 + ch = *p++; 120 + goto append; 121 + 122 + quoted_escape: 123 + p++; 124 + if (*p == '\0') goto fail; 125 + ch = *p++; 126 + goto append; 127 + 128 + append: 129 + if (len == cap) { 130 + size_t next_cap = cap ? cap * 2 : 32; 131 + char *next = realloc(buf, next_cap); 132 + if (!next) goto fail; 133 + buf = next; 134 + cap = next_cap; 135 + } 136 + buf[len++] = ch; 137 + goto quoted; 138 + 139 + done: 140 + p++; 141 + if (len == cap) { 142 + size_t next_cap = cap ? cap + 1 : 1; 143 + char *next = realloc(buf, next_cap); 144 + if (!next) goto fail; 145 + buf = next; 146 + cap = next_cap; 147 + } 148 + buf[len] = '\0'; 149 + return buf; 150 + 151 + skip_param: 152 + p = ct_find_next_param(p); 153 + goto next_param; 154 + 155 + fail: 156 + free(buf); 157 + return NULL; 158 + } 159 + 160 + static ant_value_t parse_formdata_urlencoded(ant_t *js, const uint8_t *data, size_t size) { 161 + ant_value_t fd = 0; 162 + char *body = NULL; 163 + char *cursor = NULL; 164 + 165 + fd = formdata_create_empty(js); 166 + if (is_err(fd)) return fd; 167 + if (!data || size == 0) return fd; 168 + 169 + body = strndup((const char *)data, size); 170 + if (!body) return js_mkerr(js, "out of memory"); 171 + 172 + cursor = body; 173 + while (cursor) { 174 + ant_value_t r = 0; 175 + char *amp = strchr(cursor, '&'); 176 + char *eq = NULL; 177 + char *raw_name = NULL; 178 + char *raw_value = NULL; 179 + char *name = NULL; 180 + char *value = NULL; 181 + 182 + if (amp) *amp = '\0'; 183 + 184 + eq = strchr(cursor, '='); 185 + raw_name = cursor; 186 + raw_value = eq ? (eq + 1) : ""; 187 + if (eq) *eq = '\0'; 188 + 189 + name = form_urldecode(raw_name); 190 + value = form_urldecode(raw_value); 191 + if (!name || !value) { 192 + free(name); 193 + free(value); 194 + free(body); 195 + return js_mkerr(js, "out of memory"); 196 + } 197 + 198 + r = formdata_append_string( 199 + js, fd, 200 + js_mkstr(js, name, strlen(name)), 201 + js_mkstr(js, value, strlen(value)) 202 + ); 203 + 204 + free(name); 205 + free(value); 206 + 207 + if (is_err(r)) { 208 + free(body); 209 + return r; 210 + } 211 + 212 + cursor = amp ? (amp + 1) : NULL; 213 + } 214 + 215 + free(body); 216 + return fd; 217 + } 218 + 219 + static const uint8_t *find_bytes( 220 + const uint8_t *haystack, size_t haystack_len, 221 + const uint8_t *needle, size_t needle_len 222 + ) { 223 + size_t i = 0; 224 + 225 + if (needle_len == 0) return haystack; 226 + if (haystack_len < needle_len) return NULL; 227 + 228 + for (i = 0; i + needle_len <= haystack_len; i++) { 229 + if (memcmp(haystack + i, needle, needle_len) == 0) return haystack + i; 230 + } 231 + 232 + return NULL; 233 + } 234 + 235 + static ant_value_t parse_formdata_multipart( 236 + ant_t *js, const uint8_t *data, size_t size, const char *body_type 237 + ) { 238 + ant_value_t fd = 0; 239 + char *boundary = NULL; 240 + char *delim = NULL; 241 + 242 + const uint8_t *p = data; 243 + const uint8_t *end = data + size; 244 + size_t delim_len = 0; 245 + 246 + if (!data || size == 0) { 247 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 248 + } 249 + 250 + boundary = ct_get_param_dup(body_type, "boundary"); 251 + if (!boundary || boundary[0] == '\0') goto invalid; 252 + 253 + fd = formdata_create_empty(js); 254 + if (is_err(fd)) goto done; 255 + 256 + delim_len = strlen(boundary) + 2; 257 + delim = malloc(delim_len + 1); 258 + if (!delim) { 259 + fd = js_mkerr(js, "out of memory"); 260 + goto done; 261 + } 262 + snprintf(delim, delim_len + 1, "--%s", boundary); 263 + 264 + if ((size_t)(end - p) < delim_len || memcmp(p, delim, delim_len) != 0) goto invalid; 265 + p += delim_len; 266 + 267 + next_part: 268 + if (p > end) goto done; 269 + if ((size_t)(end - p) >= 2 && memcmp(p, "--", 2) == 0) goto done; 270 + if ((size_t)(end - p) < 2 || memcmp(p, "\r\n", 2) != 0) goto invalid; 271 + p += 2; 272 + 273 + { 274 + ant_value_t r = 0; 275 + const uint8_t *hdr_end = find_bytes( 276 + p, (size_t)(end - p), (const uint8_t *)"\r\n\r\n", 4 277 + ); 278 + 279 + char *headers = NULL; 280 + char *name = NULL; 281 + char *filename = NULL; 282 + char *part_type = NULL; 283 + char *marker = NULL; 284 + const uint8_t *part_end = NULL; 285 + 286 + if (!hdr_end) goto invalid; 287 + 288 + headers = strndup((const char *)p, (size_t)(hdr_end - p)); 289 + if (!headers) { 290 + fd = js_mkerr(js, "out of memory"); 291 + goto done; 292 + } 293 + p = hdr_end + 4; 294 + 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 + } 338 + } 339 + free(headers); 340 + headers = NULL; 341 + 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 + 363 + if (!part_end) { 364 + free(name); 365 + free(filename); 366 + free(part_type); 367 + goto invalid; 368 + } 369 + 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 + } 397 + 398 + p = part_end + 2 + delim_len; 399 + if ((size_t)(end - p) >= 2 && memcmp(p, "--", 2) == 0) goto done; 400 + } 401 + 402 + goto next_part; 403 + 404 + invalid: 405 + fd = js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 406 + 407 + done: 408 + free(boundary); 409 + free(delim); 410 + return fd; 411 + } 412 + 413 + ant_value_t formdata_parse_body( 414 + ant_t *js, const uint8_t *data, size_t size, 415 + const char *body_type, bool has_body 416 + ) { 417 + if (body_type && ct_is_type(body_type, "application/x-www-form-urlencoded")) { 418 + return parse_formdata_urlencoded(js, data, size); 419 + } 420 + 421 + 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 + } 425 + return parse_formdata_multipart(js, data, size, body_type); 426 + } 427 + 428 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to parse body as FormData"); 429 + } 430 + 431 + typedef struct { 432 + uint8_t *buf; 433 + size_t size; 434 + size_t cap; 435 + } mp_buf_t; 436 + 437 + static bool mp_grow(mp_buf_t *b, size_t need) { 438 + size_t nc = 0; 439 + uint8_t *nb = NULL; 440 + 441 + if (b->size + need <= b->cap) return true; 442 + 443 + nc = b->cap ? b->cap * 2 : 4096; 444 + while (nc < b->size + need) nc *= 2; 445 + 446 + nb = realloc(b->buf, nc); 447 + if (!nb) return false; 448 + b->buf = nb; 449 + b->cap = nc; 450 + return true; 451 + } 452 + 453 + static bool mp_append(mp_buf_t *b, const void *data, size_t len) { 454 + if (!mp_grow(b, len)) return false; 455 + memcpy(b->buf + b->size, data, len); 456 + b->size += len; 457 + return true; 458 + } 459 + 460 + static bool mp_append_str(mp_buf_t *b, const char *s) { 461 + return mp_append(b, s, strlen(s)); 462 + } 463 + 464 + uint8_t *formdata_serialize_multipart( 465 + ant_t *js, ant_value_t fd, size_t *out_size, char **out_boundary 466 + ) { 467 + ant_value_t values_arr = js_get_slot(fd, SLOT_ENTRIES); 468 + ant_value_t data_slot = js_get_slot(fd, SLOT_DATA); 469 + char boundary[49]; 470 + mp_buf_t b = {NULL, 0, 0}; 471 + fd_data_t *d = NULL; 472 + if (vtype(data_slot) != T_NUM) return NULL; 473 + d = (fd_data_t *)(uintptr_t)(size_t)js_getnum(data_slot); 474 + if (!d) return NULL; 475 + 476 + snprintf( 477 + boundary, sizeof(boundary), 478 + "----AntFormBoundary%08x%08x", (unsigned)rand(), (unsigned)rand() 479 + ); 480 + 481 + if (d->count == 0) { 482 + uint8_t *empty = malloc(1); 483 + if (!empty) return NULL; 484 + *out_size = 0; 485 + *out_boundary = strdup(boundary); 486 + if (!*out_boundary) { 487 + free(empty); 488 + return NULL; 489 + } 490 + return empty; 491 + } 492 + 493 + 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 + 529 + if (!mp_append_str(&b, "\r\n")) goto oom; 530 + } 531 + 532 + if (!mp_append_str(&b, "--") || 533 + !mp_append_str(&b, boundary) || 534 + !mp_append_str(&b, "--\r\n")) { 535 + goto oom; 536 + } 537 + 538 + *out_size = b.size; 539 + *out_boundary = strdup(boundary); 540 + if (!*out_boundary) goto oom; 541 + return b.buf; 542 + 543 + oom: 544 + free(b.buf); 545 + return NULL; 546 + }
+1104
src/modules/request.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + #include <strings.h> 4 + #include <ctype.h> 5 + #include <stdio.h> 6 + 7 + #include "ant.h" 8 + #include "errors.h" 9 + #include "runtime.h" 10 + #include "internal.h" 11 + #include "common.h" 12 + #include "descriptors.h" 13 + 14 + #include "modules/blob.h" 15 + #include "modules/buffer.h" 16 + #include "modules/assert.h" 17 + #include "modules/abort.h" 18 + #include "modules/formdata.h" 19 + #include "modules/headers.h" 20 + #include "modules/multipart.h" 21 + #include "modules/request.h" 22 + #include "modules/symbol.h" 23 + #include "modules/url.h" 24 + #include "modules/json.h" 25 + #include "streams/pipes.h" 26 + #include "streams/readable.h" 27 + 28 + ant_value_t g_request_proto = 0; 29 + 30 + static request_data_t *get_data(ant_value_t obj) { 31 + ant_value_t slot = js_get_slot(obj, SLOT_DATA); 32 + if (vtype(slot) != T_NUM) return NULL; 33 + return (request_data_t *)(uintptr_t)(size_t)js_getnum(slot); 34 + } 35 + 36 + request_data_t *request_get_data(ant_value_t obj) { 37 + return get_data(obj); 38 + } 39 + 40 + ant_value_t request_get_headers(ant_value_t obj) { 41 + return js_get_slot(obj, SLOT_REQUEST_HEADERS); 42 + } 43 + 44 + ant_value_t request_get_signal(ant_value_t obj) { 45 + return js_get_slot(obj, SLOT_REQUEST_SIGNAL); 46 + } 47 + 48 + static void data_free(request_data_t *d) { 49 + if (!d) return; 50 + free(d->method); 51 + url_state_clear(&d->url); 52 + free(d->referrer); 53 + free(d->referrer_policy); 54 + free(d->mode); 55 + free(d->credentials); 56 + free(d->cache); 57 + free(d->redirect); 58 + free(d->integrity); 59 + free(d->body_data); 60 + free(d->body_type); 61 + free(d); 62 + } 63 + 64 + static request_data_t *data_new(void) { 65 + request_data_t *d = calloc(1, sizeof(request_data_t)); 66 + if (!d) return NULL; 67 + d->method = strdup("GET"); 68 + d->referrer = strdup("client"); 69 + d->referrer_policy = strdup(""); 70 + d->mode = strdup("cors"); 71 + d->credentials = strdup("same-origin"); 72 + d->cache = strdup("default"); 73 + d->redirect = strdup("follow"); 74 + d->integrity = strdup(""); 75 + if (!d->method || !d->referrer || !d->referrer_policy || 76 + !d->mode || !d->credentials || !d->cache || !d->redirect || !d->integrity) { 77 + data_free(d); 78 + return NULL; 79 + } 80 + return d; 81 + } 82 + 83 + static request_data_t *data_dup(const request_data_t *src) { 84 + request_data_t *d = calloc(1, sizeof(request_data_t)); 85 + if (!d) return NULL; 86 + 87 + #define DUP_STR(f) do { d->f = src->f ? strdup(src->f) : NULL; } while(0) 88 + DUP_STR(method); 89 + DUP_STR(referrer); 90 + DUP_STR(referrer_policy); 91 + DUP_STR(mode); 92 + DUP_STR(credentials); 93 + DUP_STR(cache); 94 + DUP_STR(redirect); 95 + DUP_STR(integrity); 96 + DUP_STR(body_type); 97 + #undef DUP_STR 98 + url_state_t *su = (url_state_t *)&src->url; 99 + url_state_t *du = &d->url; 100 + #define DUP_US(f) do { du->f = su->f ? strdup(su->f) : NULL; } while(0) 101 + DUP_US(protocol); DUP_US(username); DUP_US(password); 102 + DUP_US(hostname); DUP_US(port); DUP_US(pathname); 103 + DUP_US(search); DUP_US(hash); 104 + #undef DUP_US 105 + d->keepalive = src->keepalive; 106 + d->reload_navigation = src->reload_navigation; 107 + d->history_navigation = src->history_navigation; 108 + d->has_body = src->has_body; 109 + d->body_is_stream = src->body_is_stream; 110 + d->body_used = src->body_used; 111 + d->body_size = src->body_size; 112 + 113 + if (src->body_data && src->body_size > 0) { 114 + d->body_data = malloc(src->body_size); 115 + if (!d->body_data) { data_free(d); return NULL; } 116 + memcpy(d->body_data, src->body_data, src->body_size); 117 + } 118 + 119 + return d; 120 + } 121 + 122 + static bool is_token_char(unsigned char c) { 123 + if (c == 0 || c > 127) return false; 124 + static const char *delimiters = "(),/:;<=>?@[\\]{}\""; 125 + return c > 32 && !strchr(delimiters, (char)c); 126 + } 127 + 128 + static bool is_valid_method(const char *m) { 129 + if (!m || !*m) return false; 130 + for (const unsigned char *p = (const unsigned char *)m; *p; p++) 131 + if (!is_token_char(*p)) return false; 132 + return true; 133 + } 134 + 135 + static bool is_forbidden_method(const char *m) { 136 + return 137 + strcasecmp(m, "CONNECT") == 0 || 138 + strcasecmp(m, "TRACE") == 0 || 139 + strcasecmp(m, "TRACK") == 0; 140 + } 141 + 142 + static bool is_cors_safelisted_method(const char *m) { 143 + return 144 + strcasecmp(m, "GET") == 0 || 145 + strcasecmp(m, "HEAD") == 0 || 146 + strcasecmp(m, "POST") == 0; 147 + } 148 + 149 + static void normalize_method(char *m) { 150 + static const char *norm[] = { 151 + "DELETE","GET","HEAD","OPTIONS","POST","PUT" 152 + }; 153 + 154 + for (int i = 0; i < 6; i++) { 155 + if (strcasecmp(m, norm[i]) == 0) { 156 + strcpy(m, norm[i]); 157 + return; 158 + }} 159 + } 160 + 161 + static ant_value_t request_rejection_reason(ant_t *js, ant_value_t value) { 162 + if (!is_err(value)) return value; 163 + ant_value_t reason = js->thrown_exists ? js->thrown_value : value; 164 + js->thrown_exists = false; 165 + js->thrown_value = js_mkundef(); 166 + js->thrown_stack = js_mkundef(); 167 + return reason; 168 + } 169 + 170 + static const char *request_effective_body_type(ant_t *js, ant_value_t req_obj, request_data_t *d) { 171 + ant_value_t headers = js_get_slot(req_obj, SLOT_REQUEST_HEADERS); 172 + if (!headers_is_headers(headers)) return d ? d->body_type : NULL; 173 + ant_value_t ct = headers_get_value(js, headers, "content-type"); 174 + if (vtype(ct) == T_STR) return js_getstr(js, ct, NULL); 175 + return d ? d->body_type : NULL; 176 + } 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 182 + ) { 183 + *out_data = NULL; 184 + *out_size = 0; 185 + *out_type = NULL; 186 + *out_stream = js_mkundef(); 187 + *err_out = js_mkundef(); 188 + 189 + uint8_t t = vtype(body_val); 190 + const uint8_t *src = NULL; 191 + size_t src_len = 0; 192 + 193 + if (t == T_NULL || t == T_UNDEF) return true; 194 + 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; 204 + } 205 + 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 + } 215 + 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 + } 228 + 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 + } 238 + 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; 257 + } 258 + } 259 + 260 + if (t != T_STR) { 261 + body_val = js_tostring_val(js, body_val); 262 + if (is_err(body_val)) { 263 + *err_out = body_val; 264 + return false; 265 + }} 266 + 267 + size_t len; 268 + const char *s = js_getstr(js, body_val, &len); 269 + 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 + } 277 + 278 + *out_type = strdup("text/plain;charset=UTF-8"); 279 + return true; 280 + } 281 + 282 + enum { 283 + BODY_TEXT = 0, 284 + BODY_JSON, 285 + BODY_ARRAYBUFFER, 286 + BODY_BLOB, 287 + BODY_BYTES, 288 + BODY_FORMDATA 289 + }; 290 + 291 + static void resolve_body_promise( 292 + ant_t *js, ant_value_t promise, 293 + const uint8_t *data, size_t size, 294 + const char *body_type, int mode, bool has_body 295 + ) { 296 + switch (mode) { 297 + case BODY_TEXT: { 298 + ant_value_t str = (data && size > 0) 299 + ? js_mkstr(js, (const char *)data, size) 300 + : js_mkstr(js, "", 0); 301 + js_resolve_promise(js, promise, str); 302 + break; 303 + } 304 + case BODY_JSON: { 305 + ant_value_t str = (data && size > 0) 306 + ? js_mkstr(js, (const char *)data, size) 307 + : js_mkstr(js, "", 0); 308 + ant_value_t parsed = js_json_parse(js, &str, 1); 309 + if (is_err(parsed)) js_reject_promise(js, promise, request_rejection_reason(js, parsed)); 310 + else js_resolve_promise(js, promise, parsed); 311 + break; 312 + } 313 + case BODY_ARRAYBUFFER: { 314 + ArrayBufferData *ab = create_array_buffer_data(size); 315 + if (!ab) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); break; } 316 + if (data && size > 0) memcpy(ab->data, data, size); 317 + js_resolve_promise(js, promise, create_arraybuffer_obj(js, ab)); 318 + break; 319 + } 320 + case BODY_BLOB: { 321 + const char *type = body_type ? body_type : ""; 322 + js_resolve_promise(js, promise, blob_create(js, data, size, type)); 323 + break; 324 + } 325 + case BODY_BYTES: { 326 + ArrayBufferData *ab = create_array_buffer_data(size); 327 + if (!ab) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); break; } 328 + if (data && size > 0) memcpy(ab->data, data, size); 329 + js_resolve_promise(js, promise, 330 + create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, size, "Uint8Array")); 331 + break; 332 + } 333 + case BODY_FORMDATA: { 334 + ant_value_t fd = formdata_parse_body(js, data, size, body_type, has_body); 335 + if (is_err(fd)) js_reject_promise(js, promise, request_rejection_reason(js, fd)); 336 + else js_resolve_promise(js, promise, fd); 337 + break; 338 + }} 339 + } 340 + 341 + static uint8_t *concat_chunks(ant_t *js, ant_value_t chunks, size_t *out_size) { 342 + ant_offset_t n = js_arr_len(js, chunks); 343 + size_t total = 0; 344 + 345 + for (ant_offset_t i = 0; i < n; i++) { 346 + ant_value_t chunk = js_arr_get(js, chunks, i); 347 + if (vtype(chunk) == T_TYPEDARRAY) { 348 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(chunk); 349 + if (ta && ta->buffer && !ta->buffer->is_detached) total += ta->byte_length; 350 + }} 351 + 352 + uint8_t *buf = total > 0 ? malloc(total) : NULL; 353 + if (total > 0 && !buf) return NULL; 354 + 355 + size_t pos = 0; 356 + for (ant_offset_t i = 0; i < n; i++) { 357 + ant_value_t chunk = js_arr_get(js, chunks, i); 358 + 359 + if (vtype(chunk) == T_TYPEDARRAY) { 360 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(chunk); 361 + if (ta && ta->buffer && !ta->buffer->is_detached && ta->byte_length > 0) { 362 + memcpy(buf + pos, ta->buffer->data + ta->byte_offset, ta->byte_length); 363 + pos += ta->byte_length; 364 + }}} 365 + 366 + *out_size = pos; 367 + return buf; 368 + } 369 + 370 + static ant_value_t stream_body_read(ant_t *js, ant_value_t *args, int nargs); 371 + 372 + static ant_value_t stream_body_rejected(ant_t *js, ant_value_t *args, int nargs) { 373 + ant_value_t state = js_get_slot(js->current_func, SLOT_DATA); 374 + ant_value_t promise = js_get(js, state, "promise"); 375 + ant_value_t reason = (nargs > 0) ? args[0] : js_mkundef(); 376 + js_reject_promise(js, promise, reason); 377 + return js_mkundef(); 378 + } 379 + 380 + static void stream_schedule_next_read(ant_t *js, ant_value_t state, ant_value_t read_fn, ant_value_t reader) { 381 + ant_value_t next_p = rs_default_reader_read(js, reader); 382 + ant_value_t fulfill = js_heavy_mkfun(js, stream_body_read, state); 383 + ant_value_t reject = js_heavy_mkfun(js, stream_body_rejected, state); 384 + ant_value_t then_result = js_promise_then(js, next_p, fulfill, reject); 385 + promise_mark_handled(then_result); 386 + } 387 + 388 + static ant_value_t stream_body_read(ant_t *js, ant_value_t *args, int nargs) { 389 + ant_value_t state = js_get_slot(js->current_func, SLOT_DATA); 390 + ant_value_t result = (nargs > 0) ? args[0] : js_mkundef(); 391 + ant_value_t promise = js_get(js, state, "promise"); 392 + ant_value_t reader = js_get(js, state, "reader"); 393 + ant_value_t chunks = js_get(js, state, "chunks"); 394 + int mode = (int)js_getnum(js_get(js, state, "mode")); 395 + 396 + ant_value_t done_val = js_get(js, result, "done"); 397 + ant_value_t value = js_get(js, result, "value"); 398 + 399 + if (vtype(done_val) == T_BOOL && done_val == js_true) { 400 + size_t size = 0; 401 + uint8_t *data = concat_chunks(js, chunks, &size); 402 + ant_value_t type_v = js_get(js, state, "type"); 403 + const char *body_type = (vtype(type_v) == T_STR) ? js_getstr(js, type_v, NULL) : NULL; 404 + resolve_body_promise(js, promise, data, size, body_type, mode, true); 405 + free(data); 406 + return js_mkundef(); 407 + } 408 + 409 + if (vtype(value) != T_UNDEF && vtype(value) != T_NULL) 410 + js_arr_push(js, chunks, value); 411 + 412 + stream_schedule_next_read(js, state, js_mkundef(), reader); 413 + return js_mkundef(); 414 + } 415 + 416 + static ant_value_t consume_body_from_stream( 417 + ant_t *js, ant_value_t stream, 418 + ant_value_t promise, int mode, 419 + const char *body_type 420 + ) { 421 + ant_value_t reader_args[1] = { stream }; 422 + ant_value_t saved = js->new_target; 423 + 424 + js->new_target = g_reader_proto; 425 + ant_value_t reader = js_rs_reader_ctor(js, reader_args, 1); 426 + js->new_target = saved; 427 + 428 + if (is_err(reader)) { 429 + js_reject_promise(js, promise, reader); 430 + return promise; 431 + } 432 + 433 + ant_value_t state = js_mkobj(js); 434 + js_set(js, state, "promise", promise); 435 + js_set(js, state, "reader", reader); 436 + js_set(js, state, "chunks", js_mkarr(js)); 437 + js_set(js, state, "mode", js_mknum(mode)); 438 + js_set(js, state, "type", body_type ? js_mkstr(js, body_type, strlen(body_type)) : js_mkundef()); 439 + 440 + stream_schedule_next_read(js, state, js_mkundef(), reader); 441 + return promise; 442 + } 443 + 444 + static ant_value_t consume_body(ant_t *js, int mode) { 445 + ant_value_t this = js_getthis(js); 446 + request_data_t *d = get_data(this); 447 + ant_value_t promise = js_mkpromise(js); 448 + 449 + if (!d) { 450 + js_reject_promise(js, promise, request_rejection_reason(js, 451 + js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Request object"))); 452 + return promise; 453 + } 454 + 455 + if (!d->has_body) { 456 + resolve_body_promise(js, promise, NULL, 0, request_effective_body_type(js, this, d), mode, false); 457 + return promise; 458 + } 459 + 460 + if (d->body_used) { 461 + js_reject_promise(js, promise, request_rejection_reason(js, 462 + js_mkerr_typed(js, JS_ERR_TYPE, "body stream is disturbed or locked"))); 463 + return promise; 464 + } 465 + 466 + d->body_used = true; 467 + ant_value_t stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); 468 + if (rs_is_stream(stream) && d->body_is_stream) 469 + return consume_body_from_stream(js, stream, promise, mode, request_effective_body_type(js, this, d)); 470 + resolve_body_promise(js, promise, d->body_data, d->body_size, request_effective_body_type(js, this, d), mode, true); 471 + 472 + return promise; 473 + } 474 + 475 + static ant_value_t js_req_text(ant_t *js, ant_value_t *args, int nargs) { 476 + return consume_body(js, BODY_TEXT); 477 + } 478 + 479 + static ant_value_t js_req_json(ant_t *js, ant_value_t *args, int nargs) { 480 + return consume_body(js, BODY_JSON); 481 + } 482 + 483 + static ant_value_t js_req_array_buffer(ant_t *js, ant_value_t *args, int nargs) { 484 + return consume_body(js, BODY_ARRAYBUFFER); 485 + } 486 + 487 + static ant_value_t js_req_blob(ant_t *js, ant_value_t *args, int nargs) { 488 + return consume_body(js, BODY_BLOB); 489 + } 490 + 491 + static ant_value_t js_req_bytes(ant_t *js, ant_value_t *args, int nargs) { 492 + return consume_body(js, BODY_BYTES); 493 + } 494 + 495 + static ant_value_t js_req_form_data(ant_t *js, ant_value_t *args, int nargs) { 496 + return consume_body(js, BODY_FORMDATA); 497 + } 498 + 499 + #define REQ_GETTER_START(name) \ 500 + static ant_value_t js_req_get_##name(ant_t *js, ant_value_t *args, int nargs) { \ 501 + ant_value_t this = js_getthis(js); \ 502 + request_data_t *d = get_data(this); \ 503 + if (!d) return js_mkundef(); 504 + 505 + #define REQ_GETTER_END } 506 + 507 + REQ_GETTER_START(method) 508 + return js_mkstr(js, d->method, strlen(d->method)); 509 + REQ_GETTER_END 510 + 511 + REQ_GETTER_START(url) 512 + char *href = build_href(&d->url); 513 + if (!href) return js_mkstr(js, "", 0); 514 + ant_value_t ret = js_mkstr(js, href, strlen(href)); 515 + free(href); 516 + return ret; 517 + REQ_GETTER_END 518 + 519 + REQ_GETTER_START(headers) 520 + return js_get_slot(this, SLOT_REQUEST_HEADERS); 521 + REQ_GETTER_END 522 + 523 + REQ_GETTER_START(destination) 524 + (void)d; 525 + return js_mkstr(js, "", 0); 526 + REQ_GETTER_END 527 + 528 + REQ_GETTER_START(referrer) 529 + if (!d->referrer || strcmp(d->referrer, "no-referrer") == 0) 530 + return js_mkstr(js, "", 0); 531 + if (strcmp(d->referrer, "client") == 0) 532 + return js_mkstr(js, "about:client", 12); 533 + return js_mkstr(js, d->referrer, strlen(d->referrer)); 534 + REQ_GETTER_END 535 + 536 + REQ_GETTER_START(referrer_policy) 537 + const char *p = d->referrer_policy ? d->referrer_policy : ""; 538 + return js_mkstr(js, p, strlen(p)); 539 + REQ_GETTER_END 540 + 541 + REQ_GETTER_START(mode) 542 + return js_mkstr(js, d->mode, strlen(d->mode)); 543 + REQ_GETTER_END 544 + 545 + REQ_GETTER_START(credentials) 546 + return js_mkstr(js, d->credentials, strlen(d->credentials)); 547 + REQ_GETTER_END 548 + 549 + REQ_GETTER_START(cache) 550 + return js_mkstr(js, d->cache, strlen(d->cache)); 551 + REQ_GETTER_END 552 + 553 + REQ_GETTER_START(redirect) 554 + return js_mkstr(js, d->redirect, strlen(d->redirect)); 555 + REQ_GETTER_END 556 + 557 + REQ_GETTER_START(integrity) 558 + const char *ig = d->integrity ? d->integrity : ""; 559 + return js_mkstr(js, ig, strlen(ig)); 560 + REQ_GETTER_END 561 + 562 + REQ_GETTER_START(keepalive) 563 + return js_bool(d->keepalive); 564 + REQ_GETTER_END 565 + 566 + REQ_GETTER_START(is_reload_navigation) 567 + return js_bool(d->reload_navigation); 568 + REQ_GETTER_END 569 + 570 + REQ_GETTER_START(is_history_navigation) 571 + return js_bool(d->history_navigation); 572 + REQ_GETTER_END 573 + 574 + REQ_GETTER_START(signal) 575 + return js_get_slot(this, SLOT_REQUEST_SIGNAL); 576 + REQ_GETTER_END 577 + 578 + REQ_GETTER_START(duplex) 579 + return js_mkstr(js, "half", 4); 580 + REQ_GETTER_END 581 + 582 + static ant_value_t req_body_pull(ant_t *js, ant_value_t *args, int nargs) { 583 + ant_value_t req_obj = js_get_slot(js->current_func, SLOT_DATA); 584 + request_data_t *d = get_data(req_obj); 585 + ant_value_t ctrl = (nargs > 0) ? args[0] : js_mkundef(); 586 + 587 + if (d && d->body_data && d->body_size > 0) { 588 + ArrayBufferData *ab = create_array_buffer_data(d->body_size); 589 + if (ab) { 590 + memcpy(ab->data, d->body_data, d->body_size); 591 + rs_controller_enqueue(js, ctrl, 592 + create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, d->body_size, "Uint8Array")); 593 + }} 594 + 595 + rs_controller_close(js, ctrl); 596 + return js_mkundef(); 597 + } 598 + 599 + REQ_GETTER_START(body) 600 + if (!d->has_body) return js_mknull(); 601 + ant_value_t stored_stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); 602 + if (rs_is_stream(stored_stream)) return stored_stream; 603 + if (d->body_used) return js_mknull(); 604 + ant_value_t pull = js_heavy_mkfun(js, req_body_pull, this); 605 + ant_value_t stream = rs_create_stream(js, pull, js_mkundef(), 1.0); 606 + if (!is_err(stream)) js_set_slot_wb(js, this, SLOT_REQUEST_BODY_STREAM, stream); 607 + return stream; 608 + REQ_GETTER_END 609 + 610 + REQ_GETTER_START(body_used) 611 + return js_bool(d->body_used); 612 + REQ_GETTER_END 613 + 614 + #undef REQ_GETTER_START 615 + #undef REQ_GETTER_END 616 + 617 + static ant_value_t js_request_clone(ant_t *js, ant_value_t *args, int nargs) { 618 + ant_value_t this = js_getthis(js); 619 + request_data_t *d = get_data(this); 620 + 621 + if (!d) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Request object"); 622 + if (d->body_used) 623 + return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot clone a Request whose body is unusable"); 624 + 625 + request_data_t *nd = data_dup(d); 626 + if (!nd) return js_mkerr(js, "out of memory"); 627 + 628 + ant_value_t src_headers = js_get_slot(this, SLOT_REQUEST_HEADERS); 629 + ant_value_t src_signal = js_get_slot(this, SLOT_REQUEST_SIGNAL); 630 + 631 + ant_value_t new_headers = headers_create_empty(js); 632 + if (is_err(new_headers)) { data_free(nd); return new_headers; } 633 + 634 + headers_copy_from(js, new_headers, src_headers); 635 + headers_set_guard(new_headers, 636 + strcmp(nd->mode, "no-cors") == 0 637 + ? HEADERS_GUARD_REQUEST_NO_CORS 638 + : HEADERS_GUARD_REQUEST 639 + ); 640 + headers_apply_guard(new_headers); 641 + 642 + ant_value_t new_signal = abort_signal_create_dependent(js, src_signal); 643 + if (is_err(new_signal)) { data_free(nd); return new_signal; } 644 + 645 + ant_value_t obj = js_mkobj(js); 646 + js_set_proto_init(obj, g_request_proto); 647 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 648 + js_set_slot(obj, SLOT_DATA, ANT_PTR(nd)); 649 + 650 + js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, new_headers); 651 + js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, new_signal); 652 + 653 + ant_value_t src_stream = js_get_slot(this, SLOT_REQUEST_BODY_STREAM); 654 + if (rs_is_stream(src_stream)) { 655 + ant_value_t branches = readable_stream_tee(js, src_stream); 656 + if (!is_err(branches) && vtype(branches) == T_ARR) { 657 + ant_value_t b1 = js_arr_get(js, branches, 0); 658 + ant_value_t b2 = js_arr_get(js, branches, 1); 659 + js_set_slot_wb(js, this, SLOT_REQUEST_BODY_STREAM, b1); 660 + js_set_slot_wb(js, obj, SLOT_REQUEST_BODY_STREAM, b2); 661 + }} 662 + 663 + return obj; 664 + } 665 + 666 + static const char *init_str(ant_t *js, ant_value_t init, const char *key, size_t klen, ant_value_t *err_out) { 667 + ant_value_t v = js_get(js, init, key); 668 + if (vtype(v) == T_UNDEF) return NULL; 669 + if (vtype(v) != T_STR) { 670 + v = js_tostring_val(js, v); 671 + if (is_err(v)) { *err_out = v; return NULL; } 672 + } 673 + return js_getstr(js, v, NULL); 674 + } 675 + 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"); 681 + 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); 685 + 686 + request_data_t *req = NULL; 687 + ant_value_t input_signal = js_mkundef(); 688 + 689 + request_data_t *src = NULL; 690 + if (vtype(input) == T_OBJ) src = get_data(input); 691 + 692 + if (!src) { 693 + if (vtype(input) != T_STR) { 694 + input = js_tostring_val(js, input); 695 + if (is_err(input)) return input; 696 + } 697 + size_t ulen; 698 + const char *url_str = js_getstr(js, input, &ulen); 699 + url_state_t parsed = {0}; 700 + if (parse_url_to_state(url_str, NULL, &parsed) != 0) { 701 + url_state_clear(&parsed); 702 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': Invalid URL"); 703 + } 704 + if ((parsed.username && parsed.username[0]) || (parsed.password && parsed.password[0])) { 705 + url_state_clear(&parsed); 706 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Request': URL includes credentials"); 707 + } 708 + req = data_new(); 709 + if (!req) { url_state_clear(&parsed); return js_mkerr(js, "out of memory"); } 710 + req->url = parsed; 711 + } else if (vtype(input) == T_OBJ) { 712 + req = data_dup(src); 713 + if (!req) return js_mkerr(js, "out of memory"); 714 + req->body_used = false; 715 + input_signal = js_get_slot(input, SLOT_REQUEST_SIGNAL); 716 + } 717 + 718 + ant_value_t err = js_mkundef(); 719 + 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 + } 726 + 727 + if (strcmp(req->mode, "navigate") == 0) { 728 + free(req->mode); req->mode = strdup("same-origin"); 729 + } 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(""); 734 + 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 + }} 755 + 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); 768 + } 769 + 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); } 773 + 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); 778 + if ( 779 + strcmp(req->cache, "only-if-cached") == 0 && 780 + strcmp(req->mode, "same-origin") != 0 781 + ) { 782 + data_free(req); 783 + return js_mkerr_typed(js, JS_ERR_TYPE, 784 + "Failed to construct 'Request': cache mode 'only-if-cached' requires mode 'same-origin'"); 785 + }} 786 + 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); } 790 + 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); } 794 + 795 + ant_value_t ka = js_get(js, init, "keepalive"); 796 + if (vtype(ka) != T_UNDEF) req->keepalive = js_truthy(js, ka); 797 + 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); 812 + } 813 + 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"); 820 + }} 821 + } 822 + 823 + if ( 824 + strcmp(req->mode, "no-cors") == 0 && 825 + !is_cors_safelisted_method(req->method) 826 + ) { 827 + data_free(req); 828 + return js_mkerr_typed(js, JS_ERR_TYPE, 829 + "Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"); 830 + } 831 + 832 + ant_value_t obj = js_mkobj(js); 833 + ant_value_t proto = js_instance_proto_from_new_target(js, g_request_proto); 834 + 835 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 836 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 837 + js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 838 + 839 + ant_value_t signal = abort_signal_create_dependent(js, input_signal); 840 + if (is_err(signal)) { data_free(req); return signal; } 841 + js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 842 + 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); 851 + if (is_err(headers)) { data_free(req); return headers; } 852 + } 853 + 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 + headers_set_guard(headers, 885 + strcmp(req->mode, "no-cors") == 0 886 + ? HEADERS_GUARD_REQUEST_NO_CORS 887 + : HEADERS_GUARD_REQUEST 888 + ); 889 + headers_apply_guard(headers); 890 + js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, headers); 891 + 892 + 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 + }} 907 + 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 + } 1005 + } 1006 + 1007 + if (src && src->has_body && !src->body_used) 1008 + src->body_used = true; 1009 + 1010 + return obj; 1011 + } 1012 + 1013 + ant_value_t request_create(ant_t *js, 1014 + const char *method, const char *url, 1015 + ant_value_t headers_obj, const uint8_t *body, size_t body_len, 1016 + const char *body_type) { 1017 + request_data_t *req = data_new(); 1018 + if (!req) return js_mkerr(js, "out of memory"); 1019 + 1020 + free(req->method); 1021 + req->method = strdup(method ? method : "GET"); 1022 + free(req->mode); 1023 + req->mode = strdup("same-origin"); 1024 + 1025 + url_state_t parsed = {0}; 1026 + if (url && parse_url_to_state(url, NULL, &parsed) == 0) req->url = parsed; 1027 + else url_state_clear(&parsed); 1028 + 1029 + if (body) req->has_body = true; 1030 + 1031 + if (body && body_len > 0) { 1032 + req->body_data = malloc(body_len); 1033 + if (!req->body_data) { data_free(req); return js_mkerr(js, "out of memory"); } 1034 + memcpy(req->body_data, body, body_len); 1035 + req->body_size = body_len; 1036 + req->body_type = body_type ? strdup(body_type) : NULL; 1037 + } 1038 + req->body_is_stream = false; 1039 + 1040 + ant_value_t obj = js_mkobj(js); 1041 + js_set_proto_init(obj, g_request_proto); 1042 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 1043 + js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 1044 + 1045 + ant_value_t hdrs = is_object_type(headers_obj) 1046 + ? headers_obj 1047 + : headers_create_empty(js); 1048 + 1049 + headers_set_guard(hdrs, 1050 + strcmp(req->mode, "no-cors") == 0 1051 + ? HEADERS_GUARD_REQUEST_NO_CORS 1052 + : HEADERS_GUARD_REQUEST 1053 + ); 1054 + 1055 + headers_apply_guard(hdrs); 1056 + js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, hdrs); 1057 + 1058 + ant_value_t sig = abort_signal_create_dependent(js, js_mkundef()); 1059 + js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, sig); 1060 + 1061 + return obj; 1062 + } 1063 + 1064 + void init_request_module(void) { 1065 + ant_t *js = rt->js; 1066 + ant_value_t g = js_glob(js); 1067 + g_request_proto = js_mkobj(js); 1068 + 1069 + js_set(js, g_request_proto, "text", js_mkfun(js_req_text)); 1070 + js_set(js, g_request_proto, "json", js_mkfun(js_req_json)); 1071 + js_set(js, g_request_proto, "arrayBuffer", js_mkfun(js_req_array_buffer)); 1072 + js_set(js, g_request_proto, "blob", js_mkfun(js_req_blob)); 1073 + js_set(js, g_request_proto, "formData", js_mkfun(js_req_form_data)); 1074 + js_set(js, g_request_proto, "bytes", js_mkfun(js_req_bytes)); 1075 + js_set(js, g_request_proto, "clone", js_mkfun(js_request_clone)); 1076 + 1077 + #define GETTER(prop, fn) \ 1078 + js_set_getter_desc(js, g_request_proto, prop, sizeof(prop)-1, js_mkfun(js_req_get_##fn), JS_DESC_C) 1079 + GETTER("method", method); 1080 + GETTER("url", url); 1081 + GETTER("headers", headers); 1082 + GETTER("destination", destination); 1083 + GETTER("referrer", referrer); 1084 + GETTER("referrerPolicy", referrer_policy); 1085 + GETTER("mode", mode); 1086 + GETTER("credentials", credentials); 1087 + GETTER("cache", cache); 1088 + GETTER("redirect", redirect); 1089 + GETTER("integrity", integrity); 1090 + GETTER("keepalive", keepalive); 1091 + GETTER("isReloadNavigation",is_reload_navigation); 1092 + GETTER("isHistoryNavigation",is_history_navigation); 1093 + GETTER("signal", signal); 1094 + GETTER("duplex", duplex); 1095 + GETTER("body", body); 1096 + GETTER("bodyUsed", body_used); 1097 + #undef GETTER 1098 + 1099 + js_set_sym(js, g_request_proto, get_toStringTag_sym(), js_mkstr(js, "Request", 7)); 1100 + ant_value_t ctor = js_make_ctor(js, js_request_ctor, g_request_proto, "Request", 7); 1101 + 1102 + js_set(js, g, "Request", ctor); 1103 + js_set_descriptor(js, g, "Request", 7, JS_DESC_W | JS_DESC_C); 1104 + }
+1 -38
src/modules/textcodec.c
··· 78 78 return s; 79 79 } 80 80 81 - static bool get_buffer_source(ant_t *js, ant_value_t arg, const uint8_t **out, size_t *len) { 82 - ant_value_t slot = js_get_slot(arg, SLOT_BUFFER); 83 - TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); 84 - if (ta) { 85 - if (!ta->buffer || ta->buffer->is_detached) { *out = NULL; *len = 0; return true; } 86 - *out = ta->buffer->data + ta->byte_offset; 87 - *len = ta->byte_length; 88 - return true; 89 - } 90 - 91 - if (vtype(slot) == T_NUM) { 92 - ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 93 - if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 94 - *out = ab->data; 95 - *len = ab->length; 96 - return true; 97 - } 98 - 99 - ant_value_t buf_prop = js_get(js, arg, "buffer"); 100 - if (is_object_type(buf_prop)) { 101 - ant_value_t buf_slot = js_get_slot(buf_prop, SLOT_BUFFER); 102 - 103 - if (vtype(buf_slot) == T_NUM) { 104 - ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(buf_slot); 105 - if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 106 - ant_value_t off_v = js_get(js, arg, "byteOffset"); 107 - ant_value_t len_v = js_get(js, arg, "byteLength"); 108 - size_t off = (vtype(off_v) == T_NUM) ? (size_t)js_getnum(off_v) : 0; 109 - size_t blen = (vtype(len_v) == T_NUM) ? (size_t)js_getnum(len_v) : ab->length - off; 110 - *out = ab->data + off; 111 - *len = blen; 112 - return true; 113 - }} 114 - 115 - return false; 116 - } 117 - 118 81 static ant_value_t js_textencoder_get_encoding(ant_t *js, ant_value_t *args, int nargs) { 119 82 return js_mkstr(js, "utf-8", 5); 120 83 } ··· 420 383 const uint8_t *input = NULL; 421 384 size_t input_len = 0; 422 385 if (nargs > 0 && is_object_type(args[0])) 423 - get_buffer_source(js, args[0], &input, &input_len); 386 + buffer_source_get_bytes(js, args[0], &input, &input_len); 424 387 425 388 return td_decode(js, st, input, input_len, stream_mode); 426 389 }
+9 -1
src/modules/url.c
··· 32 32 return (url_state_t *)(uintptr_t)(size_t)js_getnum(slot); 33 33 } 34 34 35 + bool usp_is_urlsearchparams(ant_t *js, ant_value_t obj) { 36 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 37 + return vtype(brand) == T_NUM && (int)js_getnum(brand) == BRAND_URLSEARCHPARAMS; 38 + } 39 + 35 40 void url_state_clear(url_state_t *s) { 36 41 free(s->protocol); free(s->username); free(s->password); 37 42 free(s->hostname); free(s->port); free(s->pathname); ··· 441 446 static ant_value_t make_usp_for_url(ant_t *js, ant_value_t url_obj, const char *query) { 442 447 ant_value_t usp = js_mkobj(js); 443 448 js_set_proto_init(usp, g_usp_proto); 449 + js_set_slot(usp, SLOT_BRAND, js_mknum(BRAND_URLSEARCHPARAMS)); 444 450 js_set_slot_wb(js, usp, SLOT_DATA, url_obj); 445 451 js_set_slot(usp, SLOT_ENTRIES, parse_query_to_arr(js, query)); 446 452 return usp; ··· 1116 1122 static ant_value_t js_URLSearchParams(ant_t *js, ant_value_t *args, int nargs) { 1117 1123 if (is_undefined(js->new_target)) 1118 1124 return js_mkerr_typed(js, JS_ERR_TYPE, 1119 - "Failed to construct 'URLSearchParams': Please use the 'new' operator."); 1125 + "Failed to construct 'URLSearchParams': Please use the 'new' operator."); 1120 1126 1121 1127 ant_value_t obj = js_mkobj(js); 1122 1128 js_set_proto_init(obj, g_usp_proto); 1129 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_URLSEARCHPARAMS)); 1123 1130 js_set_slot(obj, SLOT_DATA, js_mkundef()); 1131 + 1124 1132 ant_value_t entries = js_mkarr(js); 1125 1133 js_set_slot(obj, SLOT_ENTRIES, entries); 1126 1134
+1 -37
src/streams/codec.c
··· 77 77 }} 78 78 } 79 79 80 - static bool get_chunk_bytes(ant_t *js, ant_value_t chunk, const uint8_t **out, size_t *len) { 81 - ant_value_t slot = js_get_slot(chunk, SLOT_BUFFER); 82 - TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); 83 - if (ta) { 84 - if (!ta->buffer || ta->buffer->is_detached) { *out = NULL; *len = 0; return true; } 85 - *out = ta->buffer->data + ta->byte_offset; 86 - *len = ta->byte_length; 87 - return true; 88 - } 89 - 90 - if (vtype(slot) == T_NUM) { 91 - ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 92 - if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 93 - *out = ab->data; 94 - *len = ab->length; 95 - return true; 96 - } 97 - 98 - ant_value_t buf_prop = js_get(js, chunk, "buffer"); 99 - if (is_object_type(buf_prop)) { 100 - ant_value_t buf_slot = js_get_slot(buf_prop, SLOT_BUFFER); 101 - if (vtype(buf_slot) == T_NUM) { 102 - ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(buf_slot); 103 - if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 104 - ant_value_t off_v = js_get(js, chunk, "byteOffset"); 105 - ant_value_t len_v = js_get(js, chunk, "byteLength"); 106 - size_t off = (vtype(off_v) == T_NUM) ? (size_t)js_getnum(off_v) : 0; 107 - size_t blen = (vtype(len_v) == T_NUM) ? (size_t)js_getnum(len_v) : ab->length - off; 108 - *out = ab->data + off; 109 - *len = blen; 110 - return true; 111 - }} 112 - 113 - return false; 114 - } 115 - 116 80 static ant_value_t codec_transform_controller(ant_value_t *args, int nargs) { 117 81 return (nargs > 1) ? args[1] : js_mkundef(); 118 82 } ··· 293 257 const uint8_t *input = NULL; 294 258 size_t input_len = 0; 295 259 296 - if (!is_object_type(chunk) || !get_chunk_bytes(js, chunk, &input, &input_len)) 260 + if (!is_object_type(chunk) || !buffer_source_get_bytes(js, chunk, &input, &input_len)) 297 261 return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 298 262 299 263 ant_value_t result = td_decode(js, st, input, input_len, true);
+2 -38
src/streams/compression.c
··· 119 119 }} 120 120 } 121 121 122 - static bool get_chunk_bytes(ant_t *js, ant_value_t chunk, const uint8_t **out, size_t *len) { 123 - ant_value_t slot = js_get_slot(chunk, SLOT_BUFFER); 124 - TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); 125 - if (ta) { 126 - if (!ta->buffer || ta->buffer->is_detached) { *out = NULL; *len = 0; return true; } 127 - *out = ta->buffer->data + ta->byte_offset; 128 - *len = ta->byte_length; 129 - return true; 130 - } 131 - 132 - if (vtype(slot) == T_NUM) { 133 - ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 134 - if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 135 - *out = ab->data; 136 - *len = ab->length; 137 - return true; 138 - } 139 - 140 - ant_value_t buf_prop = js_get(js, chunk, "buffer"); 141 - if (is_object_type(buf_prop)) { 142 - ant_value_t buf_slot = js_get_slot(buf_prop, SLOT_BUFFER); 143 - if (vtype(buf_slot) == T_NUM) { 144 - ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(buf_slot); 145 - if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 146 - ant_value_t off_v = js_get(js, chunk, "byteOffset"); 147 - ant_value_t len_v = js_get(js, chunk, "byteLength"); 148 - size_t off = (vtype(off_v) == T_NUM) ? (size_t)js_getnum(off_v) : 0; 149 - size_t blen = (vtype(len_v) == T_NUM) ? (size_t)js_getnum(len_v) : ab->length - off; 150 - *out = ab->data + off; 151 - *len = blen; 152 - return true; 153 - }} 154 - 155 - return false; 156 - } 157 - 158 122 static ant_value_t enqueue_buffer(ant_t *js, ant_value_t ctrl_obj, const uint8_t *data, size_t len) { 159 123 ArrayBufferData *ab = create_array_buffer_data(len); 160 124 if (!ab) return js_mkerr(js, "out of memory"); ··· 178 142 const uint8_t *input = NULL; 179 143 size_t input_len = 0; 180 144 181 - if (!is_object_type(chunk) || !get_chunk_bytes(js, chunk, &input, &input_len)) 145 + if (!is_object_type(chunk) || !buffer_source_get_bytes(js, chunk, &input, &input_len)) 182 146 return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 183 147 184 148 if (!input || input_len == 0) return js_mkundef(); ··· 322 286 const uint8_t *input = NULL; 323 287 size_t input_len = 0; 324 288 325 - if (!is_object_type(chunk) || !get_chunk_bytes(js, chunk, &input, &input_len)) 289 + if (!is_object_type(chunk) || !buffer_source_get_bytes(js, chunk, &input, &input_len)) 326 290 return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 327 291 328 292 if (!input || input_len == 0) return js_mkundef();
+10
src/streams/pipes.c
··· 774 774 return result; 775 775 } 776 776 777 + ant_value_t readable_stream_tee(ant_t *js, ant_value_t source) { 778 + ant_value_t saved_this = js->this_val; 779 + js->this_val = source; 780 + 781 + ant_value_t result = js_rs_tee(js, NULL, 0); 782 + js->this_val = saved_this; 783 + 784 + return result; 785 + } 786 + 777 787 void init_pipes_proto(ant_t *js, ant_value_t rs_proto) { 778 788 js_set(js, rs_proto, "pipeTo", js_mkfun(js_rs_pipe_to)); 779 789 js_set_descriptor(js, rs_proto, "pipeTo", 6, JS_DESC_W | JS_DESC_C);
+31 -3
src/streams/readable.c
··· 18 18 ant_value_t g_controller_proto; 19 19 20 20 bool rs_is_stream(ant_value_t obj) { 21 - return is_object_type(obj) && rs_get_stream(obj) != NULL; 21 + if (!is_object_type(obj)) return false; 22 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 23 + return vtype(brand) == T_NUM 24 + && (int)js_getnum(brand) == BRAND_READABLE_STREAM 25 + && rs_get_stream(obj) != NULL; 22 26 } 23 27 24 28 bool rs_is_reader(ant_value_t obj) { 25 - return is_object_type(obj) 29 + if (!is_object_type(obj)) return false; 30 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 31 + return vtype(brand) == T_NUM 32 + && (int)js_getnum(brand) == BRAND_READABLE_STREAM_READER 26 33 && vtype(js_get_slot(obj, SLOT_RS_CLOSED)) == T_PROMISE 27 34 && vtype(js_get_slot(obj, SLOT_BUFFER)) == T_ARR; 28 35 } 29 36 30 37 bool rs_is_controller(ant_value_t obj) { 31 - return is_object_type(obj) 38 + if (!is_object_type(obj)) return false; 39 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 40 + return vtype(brand) == T_NUM 41 + && (int)js_getnum(brand) == BRAND_READABLE_STREAM_CONTROLLER 32 42 && rs_get_controller(obj) != NULL 33 43 && rs_is_stream(js_get_slot(obj, SLOT_ENTRIES)); 44 + } 45 + 46 + bool rs_stream_locked(ant_value_t stream_obj) { 47 + return rs_is_stream(stream_obj) && rs_is_reader(rs_stream_reader(stream_obj)); 48 + } 49 + 50 + bool rs_stream_disturbed(ant_value_t stream_obj) { 51 + rs_stream_t *stream = rs_get_stream(stream_obj); 52 + return stream && stream->disturbed; 53 + } 54 + 55 + bool rs_stream_unusable(ant_value_t stream_obj) { 56 + return rs_stream_locked(stream_obj) || rs_stream_disturbed(stream_obj); 34 57 } 35 58 36 59 rs_stream_t *rs_get_stream(ant_value_t obj) { ··· 662 685 ant_value_t proto = js_instance_proto_from_new_target(js, g_reader_proto); 663 686 664 687 if (is_object_type(proto)) js_set_proto_init(obj, proto); 688 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_READABLE_STREAM_READER)); 665 689 js_set_slot(obj, SLOT_ENTRIES, stream_obj); 666 690 js_set_slot(obj, SLOT_RS_CLOSED, closed); 667 691 js_set_slot(obj, SLOT_BUFFER, js_mkarr(js)); ··· 759 783 760 784 ant_value_t ctrl_obj = js_mkobj(js); 761 785 js_set_proto_init(ctrl_obj, g_controller_proto); 786 + js_set_slot(ctrl_obj, SLOT_BRAND, js_mknum(BRAND_READABLE_STREAM_CONTROLLER)); 762 787 js_set_slot(ctrl_obj, SLOT_DATA, ANT_PTR(ctrl)); 763 788 js_set_slot(ctrl_obj, SLOT_ENTRIES, stream_obj); 764 789 js_set_slot(ctrl_obj, SLOT_RS_PULL, pull_fn); ··· 827 852 828 853 ant_value_t obj = js_mkobj(js); 829 854 ant_value_t proto = js_instance_proto_from_new_target(js, g_rs_proto); 855 + 830 856 if (is_object_type(proto)) js_set_proto_init(obj, proto); 857 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_READABLE_STREAM)); 831 858 js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 832 859 js_set_finalizer(obj, rs_stream_finalize); 833 860 ··· 907 934 908 935 ant_value_t obj = js_mkobj(js); 909 936 js_set_proto_init(obj, g_rs_proto); 937 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_READABLE_STREAM)); 910 938 js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 911 939 js_set_finalizer(obj, rs_stream_finalize); 912 940
+12 -1
src/streams/transform.c
··· 19 19 20 20 bool ts_is_controller(ant_value_t obj) { 21 21 if (!is_object_type(obj)) return false; 22 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 23 + if (vtype(brand) != T_NUM || (int)js_getnum(brand) != BRAND_TRANSFORM_STREAM_CONTROLLER) 24 + return false; 22 25 ant_value_t ts_obj = js_get_slot(obj, SLOT_DATA); 23 26 return is_object_type(ts_obj) 24 - && vtype(js_get_slot(ts_obj, SLOT_DATA)) == T_NUM 27 + && ts_is_stream(ts_obj) 25 28 && js_get_slot(ts_obj, SLOT_DEFAULT) == obj; 26 29 } 27 30 28 31 bool ts_is_stream(ant_value_t obj) { 29 32 return is_object_type(obj) 33 + && vtype(js_get_slot(obj, SLOT_BRAND)) == T_NUM 34 + && (int)js_getnum(js_get_slot(obj, SLOT_BRAND)) == BRAND_TRANSFORM_STREAM 30 35 && vtype(js_get_slot(obj, SLOT_DATA)) == T_NUM 31 36 && rs_is_stream(js_get_slot(obj, SLOT_ENTRIES)) 32 37 && ws_is_stream(js_get_slot(obj, SLOT_CTOR)) ··· 919 924 ant_value_t ts_obj = js_mkobj(js); 920 925 ant_value_t proto = js_instance_proto_from_new_target(js, g_ts_proto); 921 926 if (is_object_type(proto)) js_set_proto_init(ts_obj, proto); 927 + js_set_slot(ts_obj, SLOT_BRAND, js_mknum(BRAND_TRANSFORM_STREAM)); 922 928 js_set_slot(ts_obj, SLOT_DATA, js_mknum(0)); 923 929 924 930 ant_value_t ctrl_obj = js_mkobj(js); 925 931 js_set_proto_init(ctrl_obj, g_ts_ctrl_proto); 932 + js_set_slot(ctrl_obj, SLOT_BRAND, js_mknum(BRAND_TRANSFORM_STREAM_CONTROLLER)); 926 933 js_set_slot(ctrl_obj, SLOT_DATA, ts_obj); 927 934 js_set_slot(ctrl_obj, SLOT_ENTRIES, transform_fn); 928 935 js_set_slot(ctrl_obj, SLOT_CTOR, flush_fn); ··· 950 957 951 958 ant_value_t rs_obj = js_mkobj(js); 952 959 js_set_proto_init(rs_obj, g_rs_proto); 960 + js_set_slot(rs_obj, SLOT_BRAND, js_mknum(BRAND_READABLE_STREAM)); 953 961 js_set_slot(rs_obj, SLOT_DATA, ANT_PTR(rst)); 954 962 js_set_finalizer(rs_obj, ts_rs_finalize); 955 963 ··· 959 967 960 968 ant_value_t rs_ctrl_obj = js_mkobj(js); 961 969 js_set_proto_init(rs_ctrl_obj, g_controller_proto); 970 + js_set_slot(rs_ctrl_obj, SLOT_BRAND, js_mknum(BRAND_READABLE_STREAM_CONTROLLER)); 962 971 js_set_slot(rs_ctrl_obj, SLOT_DATA, ANT_PTR(rcc)); 963 972 js_set_slot(rs_ctrl_obj, SLOT_ENTRIES, rs_obj); 964 973 js_set_slot(rs_ctrl_obj, SLOT_RS_PULL, source_pull); ··· 976 985 977 986 ant_value_t ws_obj = js_mkobj(js); 978 987 js_set_proto_init(ws_obj, g_ws_proto); 988 + js_set_slot(ws_obj, SLOT_BRAND, js_mknum(BRAND_WRITABLE_STREAM)); 979 989 js_set_slot(ws_obj, SLOT_DATA, ANT_PTR(wst)); 980 990 js_set_slot(ws_obj, SLOT_SETTLED, js_mkarr(js)); 981 991 js_set_finalizer(ws_obj, ts_ws_finalize); ··· 993 1003 994 1004 ant_value_t ws_ctrl_obj = js_mkobj(js); 995 1005 js_set_proto_init(ws_ctrl_obj, g_ws_controller_proto); 1006 + js_set_slot(ws_ctrl_obj, SLOT_BRAND, js_mknum(BRAND_WRITABLE_STREAM_CONTROLLER)); 996 1007 js_set_slot(ws_ctrl_obj, SLOT_DATA, ANT_PTR(wc)); 997 1008 js_set_slot(ws_ctrl_obj, SLOT_ENTRIES, ws_obj); 998 1009 js_set_slot(ws_ctrl_obj, SLOT_WS_WRITE, sink_write);
+18 -3
src/streams/writable.c
··· 19 19 static ant_value_t g_close_sentinel; 20 20 21 21 bool ws_is_stream(ant_value_t obj) { 22 - return is_object_type(obj) && ws_get_stream(obj) != NULL; 22 + if (!is_object_type(obj)) return false; 23 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 24 + return vtype(brand) == T_NUM 25 + && (int)js_getnum(brand) == BRAND_WRITABLE_STREAM 26 + && ws_get_stream(obj) != NULL; 23 27 } 24 28 25 29 bool ws_is_writer(ant_value_t obj) { 26 - return is_object_type(obj) 30 + if (!is_object_type(obj)) return false; 31 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 32 + return vtype(brand) == T_NUM 33 + && (int)js_getnum(brand) == BRAND_WRITABLE_STREAM_WRITER 27 34 && vtype(js_get_slot(obj, SLOT_RS_CLOSED)) == T_PROMISE 28 35 && vtype(js_get_slot(obj, SLOT_WS_READY)) == T_PROMISE; 29 36 } 30 37 31 38 bool ws_is_controller(ant_value_t obj) { 32 - return is_object_type(obj) 39 + if (!is_object_type(obj)) return false; 40 + ant_value_t brand = js_get_slot(obj, SLOT_BRAND); 41 + return vtype(brand) == T_NUM 42 + && (int)js_getnum(brand) == BRAND_WRITABLE_STREAM_CONTROLLER 33 43 && ws_get_controller(obj) != NULL 34 44 && ws_is_stream(js_get_slot(obj, SLOT_ENTRIES)); 35 45 } ··· 971 981 972 982 ant_value_t obj = js_mkobj(js); 973 983 ant_value_t proto = js_instance_proto_from_new_target(js, g_ws_writer_proto); 984 + 974 985 if (is_object_type(proto)) js_set_proto_init(obj, proto); 986 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WRITABLE_STREAM_WRITER)); 975 987 976 988 ant_value_t closed = js_mkpromise(js); 977 989 ant_value_t ready = js_mkpromise(js); ··· 1072 1084 1073 1085 ant_value_t ctrl_obj = js_mkobj(js); 1074 1086 js_set_proto_init(ctrl_obj, g_ws_controller_proto); 1087 + js_set_slot(ctrl_obj, SLOT_BRAND, js_mknum(BRAND_WRITABLE_STREAM_CONTROLLER)); 1075 1088 js_set_slot(ctrl_obj, SLOT_DATA, ANT_PTR(ctrl)); 1076 1089 js_set_slot(ctrl_obj, SLOT_ENTRIES, stream_obj); 1077 1090 js_set_slot(ctrl_obj, SLOT_WS_WRITE, write_fn); ··· 1151 1164 1152 1165 ant_value_t obj = js_mkobj(js); 1153 1166 ant_value_t proto = js_instance_proto_from_new_target(js, g_ws_proto); 1167 + 1154 1168 if (is_object_type(proto)) js_set_proto_init(obj, proto); 1169 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WRITABLE_STREAM)); 1155 1170 js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 1156 1171 js_set_slot(obj, SLOT_SETTLED, js_mkarr(js)); 1157 1172 js_set_finalizer(obj, ws_stream_finalize);