visaljs 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/THIRD_PARTY_LICENSES +1 -1
- package/cli.js +2782 -770
- package/node/include/js_node.h +1 -0
- package/node/prelude/_http_agent.js +5 -0
- package/node/prelude/_http_server.js +7 -0
- package/node/prelude/dns.js +7 -1
- package/node/prelude/events.js +7 -3
- package/node/prelude/http.js +693 -285
- package/node/prelude/https.js +14 -3
- package/node/prelude/internal/events_extras.js +2 -5
- package/node/prelude/internal/features.js +17 -0
- package/node/prelude/internal/http_agent.js +88 -2
- package/node/prelude/internal/http_chars.ts +22 -0
- package/node/prelude/internal/http_symbols.js +3 -0
- package/node/prelude/internal/net_bound.js +123 -0
- package/node/prelude/internal/net_onread.js +73 -0
- package/node/prelude/internal/net_tos.js +54 -0
- package/node/prelude/internal/perf_observe.js +7 -1
- package/node/prelude/internal/tls/wrap.js +2 -1
- package/node/prelude/module.js +42 -18
- package/node/prelude/net.js +212 -61
- package/node/prelude/perf_hooks.js +4 -0
- package/node/src/host_http.c +151 -21
- package/node/src/host_net.c +75 -9
- package/node/src/host_sys.c +9 -0
- package/package.json +1 -1
- package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
- package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
- package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
- package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
- package/runtime/include/js_atoms.h +4 -0
- package/runtime/include/js_buffer.h +185 -0
- package/runtime/include/js_builtins.h +53 -0
- package/runtime/include/js_exception.h +3 -0
- package/runtime/include/js_function.h +1 -1
- package/runtime/include/js_object.h +21 -3
- package/runtime/include/js_ops.h +27 -0
- package/runtime/include/js_runtime.h +1 -0
- package/runtime/include/js_web.h +3 -3
- package/runtime/include/jscontext.h +15 -0
- package/runtime/src/atoms.c +37 -3
- package/runtime/src/builtins.c +17 -4
- package/runtime/src/events.c +19 -0
- package/runtime/src/function.c +37 -10
- package/runtime/src/gc.c +7 -5
- package/runtime/src/internal.h +4 -4
- package/runtime/src/iterator.c +5 -1
- package/runtime/src/object.c +42 -6
- package/runtime/src/regexp.c +569 -12
- package/runtime/src/shape.c +68 -1
- package/runtime/src/typedarray.c +23 -0
- package/runtime/src/weak.c +95 -24
package/node/src/host_http.c
CHANGED
|
@@ -50,11 +50,12 @@
|
|
|
50
50
|
static void flush_pending(void);
|
|
51
51
|
typedef struct Conn Conn;
|
|
52
52
|
static void flush_conn(Conn *c);
|
|
53
|
+
static size_t pending_bytes(Conn *c);
|
|
53
54
|
|
|
54
55
|
/* ---- Parsers and connections ------------------------------------------------------------- */
|
|
55
56
|
|
|
56
57
|
typedef struct Parsers {
|
|
57
|
-
JSValue on_request, on_body, on_complete, on_upgrade, on_error; /* roots */
|
|
58
|
+
JSValue on_request, on_body, on_complete, on_upgrade, on_error, on_drain; /* roots */
|
|
58
59
|
} Parsers;
|
|
59
60
|
|
|
60
61
|
typedef struct Buf {
|
|
@@ -70,9 +71,13 @@ struct Conn {
|
|
|
70
71
|
bool detached; /* upgraded, or a parse error: the parser doesn't read any more */
|
|
71
72
|
bool executing; /* llhttp is running on it: it can't be freed yet */
|
|
72
73
|
bool released; /* let go of while executing: freed when llhttp returns */
|
|
74
|
+
bool detach_after; /* detached when llhttp is done with the read (httpDetach's afterRead) */
|
|
73
75
|
bool upgrading; /* JS took the upgrade the head asked for */
|
|
74
76
|
bool active; /* a message is under way (or the connection is new): timeouts count */
|
|
75
77
|
bool head_done; /* its head is complete: what follows are its body and trailers */
|
|
78
|
+
bool in_message; /* a message has begun and isn't complete: the connection isn't idle */
|
|
79
|
+
llhttp_errno_t failed; /* the parse error it stopped at: more bytes report it again, as Node's */
|
|
80
|
+
bool need_drain; /* JS waits for its output to drain (a write over the high-water mark) */
|
|
76
81
|
bool overflow; /* its head is over max_header bytes */
|
|
77
82
|
uint64_t begin_ms; /* when the message began (the loop's time) */
|
|
78
83
|
size_t header_bytes, max_header;
|
|
@@ -150,6 +155,7 @@ static int on_message_begin(llhttp_t *p) {
|
|
|
150
155
|
c->headers = js_new_array(ctx_(), 0);
|
|
151
156
|
c->active = true;
|
|
152
157
|
c->head_done = false;
|
|
158
|
+
c->in_message = true;
|
|
153
159
|
c->header_bytes = 0;
|
|
154
160
|
c->begin_ms = uv_now(js_uv_loop(ctx_()));
|
|
155
161
|
return 0;
|
|
@@ -220,6 +226,7 @@ static int on_message_complete(llhttp_t *p) {
|
|
|
220
226
|
if (c->detached) return -1; /* let go of by an earlier callback */
|
|
221
227
|
if (c->in_value || c->field.n) push_header(c); /* the last trailer */
|
|
222
228
|
c->active = false;
|
|
229
|
+
c->in_message = false;
|
|
223
230
|
JSValue args[2] = { js_int32(c->id), c->headers };
|
|
224
231
|
c->headers = JS_UNDEFINED;
|
|
225
232
|
return js_uv_call(c->parsers->on_complete, 2, args, NULL) ? 0 : -1;
|
|
@@ -239,14 +246,20 @@ static Conn *conn_of(int id) {
|
|
|
239
246
|
return (size_t)id < nconns ? conns[id] : NULL;
|
|
240
247
|
}
|
|
241
248
|
|
|
242
|
-
|
|
249
|
+
/* the parse error, with Node's bytesParsed and rawPacket: how far into the read llhttp got, and
|
|
250
|
+
* the read (none for a message cut short at the end of the stream) */
|
|
251
|
+
static void parse_error(Conn *c, llhttp_errno_t e, const char *bytes, size_t n) {
|
|
243
252
|
c->detached = true;
|
|
253
|
+
c->failed = e;
|
|
244
254
|
c->active = false;
|
|
245
255
|
JSContext *ctx = ctx_();
|
|
246
256
|
const char *code = c->overflow ? "HPE_HEADER_OVERFLOW" : llhttp_errno_name(e);
|
|
247
257
|
const char *reason = c->overflow ? "Header overflow" : llhttp_get_error_reason(&c->parser);
|
|
248
|
-
|
|
249
|
-
(
|
|
258
|
+
const char *pos = bytes ? llhttp_get_error_pos(&c->parser) : NULL;
|
|
259
|
+
int32_t parsed = pos && pos >= bytes && pos <= bytes + n ? (int32_t)(pos - bytes) : 0;
|
|
260
|
+
JSValue args[5] = { js_int32(c->id), js_new_string_cstr(ctx, code), js_new_string_cstr(ctx, reason ? reason : ""), js_int32(parsed),
|
|
261
|
+
bytes ? bytes_value(ctx, bytes, n) : JS_UNDEFINED };
|
|
262
|
+
(void)js_uv_call(c->parsers->on_error, 5, args, NULL);
|
|
250
263
|
}
|
|
251
264
|
|
|
252
265
|
static void free_conn(Conn *c) {
|
|
@@ -270,9 +283,34 @@ static void release_conn(Conn *c) {
|
|
|
270
283
|
else free_conn(c);
|
|
271
284
|
}
|
|
272
285
|
|
|
286
|
+
/* The socket's bytes go to JS again: the corked output first, then the parser let go of. */
|
|
287
|
+
static void detach_conn(Conn *c) {
|
|
288
|
+
if (c->queued) flush_conn(c);
|
|
289
|
+
if (c->id >= 0) js_net_hook(c->id, NULL, NULL);
|
|
290
|
+
release_conn(c);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* llhttp is done with a read: a parser let go of meanwhile is freed, or detached if asked to be. */
|
|
294
|
+
static void read_done(Conn *c) {
|
|
295
|
+
c->executing = false;
|
|
296
|
+
if (c->released) {
|
|
297
|
+
free_conn(c);
|
|
298
|
+
} else if (c->detach_after) {
|
|
299
|
+
c->detach_after = false;
|
|
300
|
+
detach_conn(c);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
273
304
|
/* A connection's bytes: parsed while attached; EOF and errors go on to JS (the socket's 'end'
|
|
274
305
|
* and 'error'). False when the parser doesn't take them. */
|
|
275
306
|
static bool parse_input(Conn *c, ssize_t n, const char *bytes) {
|
|
307
|
+
if (c->detached && c->failed != HPE_OK && n > 0 && !c->executing) {
|
|
308
|
+
/* a parser that failed fails again on what follows, with those bytes (Node's execute) */
|
|
309
|
+
c->executing = true;
|
|
310
|
+
parse_error(c, c->failed, bytes, (size_t)n);
|
|
311
|
+
read_done(c);
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
276
314
|
if (c->detached) return false;
|
|
277
315
|
c->executing = true;
|
|
278
316
|
if (n < 0) {
|
|
@@ -280,10 +318,9 @@ static bool parse_input(Conn *c, ssize_t n, const char *bytes) {
|
|
|
280
318
|
/* a request cut short is the server's parse error; a client's response cut short shows
|
|
281
319
|
* as its 'aborted', when the socket closes (as Node's socketOnEnd ignores finish()) */
|
|
282
320
|
llhttp_errno_t e = llhttp_finish(&c->parser);
|
|
283
|
-
if (e != HPE_OK && !c->detached && !c->response) parse_error(c, e);
|
|
321
|
+
if (e != HPE_OK && !c->detached && !c->response) parse_error(c, e, NULL, 0);
|
|
284
322
|
}
|
|
285
|
-
c
|
|
286
|
-
if (c->released) free_conn(c);
|
|
323
|
+
read_done(c);
|
|
287
324
|
return false;
|
|
288
325
|
}
|
|
289
326
|
const char *at = bytes, *end = bytes + n;
|
|
@@ -291,7 +328,7 @@ static bool parse_input(Conn *c, ssize_t n, const char *bytes) {
|
|
|
291
328
|
llhttp_errno_t e = llhttp_execute(&c->parser, at, (size_t)(end - at));
|
|
292
329
|
if (e == HPE_OK || c->detached) break; /* released or upgraded by a callback: stop here */
|
|
293
330
|
if (e != HPE_PAUSED_UPGRADE) {
|
|
294
|
-
parse_error(c, e); /* nothing runs once a callback failed in JS: the loop is ending */
|
|
331
|
+
parse_error(c, e, bytes, (size_t)n); /* nothing runs once a callback failed in JS: the loop is ending */
|
|
295
332
|
break;
|
|
296
333
|
}
|
|
297
334
|
const char *pos = llhttp_get_error_pos(&c->parser);
|
|
@@ -306,8 +343,7 @@ static bool parse_input(Conn *c, ssize_t n, const char *bytes) {
|
|
|
306
343
|
(void)js_uv_call(c->parsers->on_upgrade, 2, args, NULL);
|
|
307
344
|
break;
|
|
308
345
|
}
|
|
309
|
-
c
|
|
310
|
-
if (c->released) free_conn(c);
|
|
346
|
+
read_done(c);
|
|
311
347
|
return true;
|
|
312
348
|
}
|
|
313
349
|
|
|
@@ -325,7 +361,48 @@ static void hook_close(void *data) { release_conn(data); }
|
|
|
325
361
|
|
|
326
362
|
static const JSNetHook http_hook = { hook_read, hook_close };
|
|
327
363
|
|
|
328
|
-
/*
|
|
364
|
+
/* ---- An idle client socket's guard --------------------------------------------------------- */
|
|
365
|
+
|
|
366
|
+
/* Bytes a socket reads while it waits in an agent's pool (no request on it): a server sending what
|
|
367
|
+
* nobody asked for, which the next request would take for its response. Node's free socket data
|
|
368
|
+
* guard: the bytes are dropped and JS destroys the socket. Its end and errors go on to JS. */
|
|
369
|
+
static JSValue guard_callback = JS_UNDEFINED;
|
|
370
|
+
static bool guard_rooted;
|
|
371
|
+
|
|
372
|
+
static bool guard_read(void *data, ssize_t n, const char *bytes) {
|
|
373
|
+
(void)bytes;
|
|
374
|
+
if (n <= 0) return false;
|
|
375
|
+
int id = (int)(intptr_t)data;
|
|
376
|
+
js_net_hook(id, NULL, NULL);
|
|
377
|
+
if (js_is_callable(guard_callback)) {
|
|
378
|
+
JSValue arg = js_int32(id);
|
|
379
|
+
js_uv_nest(1);
|
|
380
|
+
(void)js_uv_call(guard_callback, 1, &arg, NULL);
|
|
381
|
+
js_uv_nest(-1);
|
|
382
|
+
(void)js_uv_drain();
|
|
383
|
+
}
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
static void guard_close(void *data) { (void)data; }
|
|
388
|
+
|
|
389
|
+
static const JSNetHook guard_hook = { guard_read, guard_close };
|
|
390
|
+
|
|
391
|
+
/* httpGuard(conn, onData) → whether it's guarded: onData(conn) when the socket reads while idle */
|
|
392
|
+
JSValue js_host_http_guard(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
393
|
+
(void)callee, (void)this_val, (void)new_target;
|
|
394
|
+
if (argc < 2 || !js_is_int32(argv[0])) return JS_FALSE;
|
|
395
|
+
int id = js_as_int32(argv[0]);
|
|
396
|
+
if (id < 0 || ((size_t)id < nconns && conns[id])) return JS_FALSE; /* a parser has it */
|
|
397
|
+
if (!guard_rooted) {
|
|
398
|
+
js_add_root(ctx->rt, &guard_callback);
|
|
399
|
+
guard_rooted = true;
|
|
400
|
+
}
|
|
401
|
+
guard_callback = argv[1];
|
|
402
|
+
return js_bool(js_net_hook(id, &guard_hook, (void *)(intptr_t)id));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* httpParsers(onHead, onBody, onComplete, onUpgrade, onError, onDrain) → id */
|
|
329
406
|
JSValue js_host_http_parsers(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
330
407
|
(void)callee, (void)this_val, (void)new_target;
|
|
331
408
|
if (argc < 5) return js_throw_type_error(ctx, "httpParsers: 5 arguments expected");
|
|
@@ -345,8 +422,9 @@ JSValue js_host_http_parsers(JSContext *ctx, JSValue callee, JSValue this_val, u
|
|
|
345
422
|
Parsers *p = malloc(sizeof *p);
|
|
346
423
|
if (!p) js_fatal("out of memory in http");
|
|
347
424
|
p->on_request = argv[0], p->on_body = argv[1], p->on_complete = argv[2], p->on_upgrade = argv[3], p->on_error = argv[4];
|
|
425
|
+
p->on_drain = argc > 5 ? argv[5] : JS_UNDEFINED;
|
|
348
426
|
js_add_root(ctx->rt, &p->on_request), js_add_root(ctx->rt, &p->on_body), js_add_root(ctx->rt, &p->on_complete);
|
|
349
|
-
js_add_root(ctx->rt, &p->on_upgrade), js_add_root(ctx->rt, &p->on_error);
|
|
427
|
+
js_add_root(ctx->rt, &p->on_upgrade), js_add_root(ctx->rt, &p->on_error), js_add_root(ctx->rt, &p->on_drain);
|
|
350
428
|
Parsers **v = realloc(parsers, (nparsers + 1) * sizeof *v);
|
|
351
429
|
if (!v) js_fatal("out of memory in http");
|
|
352
430
|
parsers = v;
|
|
@@ -395,18 +473,43 @@ JSValue js_host_http_attach(JSContext *ctx, JSValue callee, JSValue this_val, ui
|
|
|
395
473
|
return js_int32(0);
|
|
396
474
|
}
|
|
397
475
|
|
|
398
|
-
/* httpDetach(conn): the socket's bytes go to JS again (its corked output goes first)
|
|
476
|
+
/* httpDetach(conn, afterRead): the socket's bytes go to JS again (its corked output goes first);
|
|
477
|
+
* with afterRead, from a parser callback, once llhttp is done with the read, as Node's client frees
|
|
478
|
+
* its parser after the read (what follows a response in it is still parsed) */
|
|
399
479
|
JSValue js_host_http_detach(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
400
480
|
(void)ctx, (void)callee, (void)this_val, (void)new_target;
|
|
401
481
|
Conn *c = argc > 0 && js_is_int32(argv[0]) ? conn_of(js_as_int32(argv[0])) : NULL;
|
|
402
|
-
if (c)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
release_conn(c);
|
|
406
|
-
}
|
|
482
|
+
if (!c) return JS_UNDEFINED;
|
|
483
|
+
if (argc > 1 && js_truthy(argv[1]) && c->executing) c->detach_after = true;
|
|
484
|
+
else detach_conn(c);
|
|
407
485
|
return JS_UNDEFINED;
|
|
408
486
|
}
|
|
409
487
|
|
|
488
|
+
/* httpBusy(conn): whether a message is under way on the connection (begun, not complete), as Node's
|
|
489
|
+
* ConnectionsList tells an idle connection from an active one */
|
|
490
|
+
JSValue js_host_http_busy(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
491
|
+
(void)ctx, (void)callee, (void)this_val, (void)new_target;
|
|
492
|
+
Conn *c = argc > 0 && js_is_int32(argv[0]) ? conn_of(js_as_int32(argv[0])) : NULL;
|
|
493
|
+
return js_bool(c && c->in_message && !c->detached);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* httpPending(conn) → the bytes written to it that haven't gone out yet (a response's backpressure) */
|
|
497
|
+
JSValue js_host_http_pending(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
498
|
+
(void)ctx, (void)callee, (void)this_val, (void)new_target;
|
|
499
|
+
Conn *c = argc > 0 && js_is_int32(argv[0]) ? conn_of(js_as_int32(argv[0])) : NULL;
|
|
500
|
+
return js_number(c ? (double)pending_bytes(c) : 0);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/* httpWatchDrain(conn) → true when nothing is pending (drained already), else the parsers' onDrain
|
|
504
|
+
* is called once it all has gone out */
|
|
505
|
+
JSValue js_host_http_watch_drain(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
506
|
+
(void)ctx, (void)callee, (void)this_val, (void)new_target;
|
|
507
|
+
Conn *c = argc > 0 && js_is_int32(argv[0]) ? conn_of(js_as_int32(argv[0])) : NULL;
|
|
508
|
+
if (!c || pending_bytes(c) == 0) return JS_TRUE;
|
|
509
|
+
c->need_drain = true;
|
|
510
|
+
return JS_FALSE;
|
|
511
|
+
}
|
|
512
|
+
|
|
410
513
|
/* httpExpired(parsers, headersTimeout, requestTimeout) → the connections over time, as Node's
|
|
411
514
|
* ConnectionsList::Expired: a head not complete within headersTimeout, or a request not
|
|
412
515
|
* complete within requestTimeout, from its start (or the connection's); 0 turns one off */
|
|
@@ -436,13 +539,30 @@ JSValue js_host_http_expired(JSContext *ctx, JSValue callee, JSValue this_val, u
|
|
|
436
539
|
typedef struct WriteReq {
|
|
437
540
|
uv_write_t req;
|
|
438
541
|
char *data;
|
|
542
|
+
int id; /* the connection's: whether its output has drained, once written */
|
|
439
543
|
} WriteReq;
|
|
440
544
|
|
|
545
|
+
/* bytes written to the connection but not yet sent: corked, or in libuv's write queue */
|
|
546
|
+
static size_t pending_bytes(Conn *c) {
|
|
547
|
+
uv_stream_t *s = c->id >= 0 ? js_net_stream(c->id) : NULL;
|
|
548
|
+
return c->out.n + (s ? uv_stream_get_write_queue_size(s) : 0);
|
|
549
|
+
}
|
|
550
|
+
|
|
441
551
|
static void on_written(uv_write_t *req, int status) {
|
|
442
552
|
(void)status; /* a failed write shows as the socket's read error, or its close */
|
|
443
553
|
WriteReq *w = (WriteReq *)req;
|
|
554
|
+
int id = w->id;
|
|
444
555
|
free(w->data);
|
|
445
556
|
free(w);
|
|
557
|
+
/* JS waited for the output to drain (Node's 'drain' on the response) */
|
|
558
|
+
Conn *c = conn_of(id);
|
|
559
|
+
if (c && c->need_drain && pending_bytes(c) == 0) {
|
|
560
|
+
c->need_drain = false;
|
|
561
|
+
if (js_is_callable(c->parsers->on_drain)) {
|
|
562
|
+
JSValue arg = js_int32(id);
|
|
563
|
+
(void)js_uv_call(c->parsers->on_drain, 1, &arg, NULL);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
446
566
|
}
|
|
447
567
|
|
|
448
568
|
/* Sends a connection's corked output as one write. */
|
|
@@ -465,6 +585,7 @@ static void flush_conn(Conn *c) {
|
|
|
465
585
|
WriteReq *w = malloc(sizeof *w);
|
|
466
586
|
if (!w) js_fatal("out of memory in http");
|
|
467
587
|
w->data = c->out.v; /* the buffer goes with the write; the connection starts a new one */
|
|
588
|
+
w->id = c->id;
|
|
468
589
|
uv_buf_t b = uv_buf_init(c->out.v, (unsigned)c->out.n);
|
|
469
590
|
c->out = (Buf){ 0 };
|
|
470
591
|
if (uv_write(&w->req, s, &b, 1, on_written) < 0) {
|
|
@@ -488,12 +609,20 @@ static void flush_pending(void) {
|
|
|
488
609
|
}
|
|
489
610
|
}
|
|
490
611
|
|
|
491
|
-
/* httpWrite(conn, data): corked until the current callback (and its microtasks) returns
|
|
612
|
+
/* httpWrite(conn, data, latin1): corked until the current callback (and its microtasks) returns; a
|
|
613
|
+
* string as UTF-8, or with latin1 a byte a character (its low byte), as Node writes a head */
|
|
492
614
|
JSValue js_host_http_write(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
493
615
|
(void)callee, (void)this_val, (void)new_target;
|
|
494
616
|
Conn *c = argc > 0 && js_is_int32(argv[0]) ? conn_of(js_as_int32(argv[0])) : NULL;
|
|
495
617
|
if (!c || argc < 2 || (c->id >= 0 && !js_net_stream(c->id))) return JS_FALSE;
|
|
496
|
-
if (js_is_string(argv[1])) {
|
|
618
|
+
if (js_is_string(argv[1]) && argc > 2 && js_truthy(argv[2])) {
|
|
619
|
+
JSStrView v = js_string_view(ctx, argv[1]);
|
|
620
|
+
if (v.length == 0) return JS_TRUE;
|
|
621
|
+
buf_reserve(&c->out, v.length);
|
|
622
|
+
if (v.latin1) memcpy(c->out.v + c->out.n, v.chars, v.length);
|
|
623
|
+
else for (uint32_t i = 0; i < v.length; i++) c->out.v[c->out.n + i] = (char)(js_view_at(v, i) & 0xff);
|
|
624
|
+
c->out.n += v.length;
|
|
625
|
+
} else if (js_is_string(argv[1])) {
|
|
497
626
|
JSStrView v = js_string_view(ctx, argv[1]);
|
|
498
627
|
size_t n = js_view_to_utf8(v, NULL, 0);
|
|
499
628
|
if (n == 0) return JS_TRUE;
|
|
@@ -555,7 +684,8 @@ JSValue js_host_http_fed_open(JSContext *ctx, JSValue callee, JSValue this_val,
|
|
|
555
684
|
/* httpFeed(conn, bytes | null) → whether the parser took them; null: the input ended */
|
|
556
685
|
JSValue js_host_http_feed(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv, JSValue new_target) {
|
|
557
686
|
(void)callee, (void)this_val, (void)new_target;
|
|
558
|
-
|
|
687
|
+
/* a fed connection's bytes; or a socket's that JS read before the parser took the socket */
|
|
688
|
+
Conn *c = argc > 0 && js_is_int32(argv[0]) ? conn_of(js_as_int32(argv[0])) : NULL;
|
|
559
689
|
if (!c) return JS_FALSE;
|
|
560
690
|
/* called from JS: the parser's callbacks run in it, so no microtask or nextTick runs between
|
|
561
691
|
* two of them (a body and its end come in the same turn, as in Node) */
|
package/node/src/host_net.c
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* NET_CLOSE the handle is closed (netClose or netReset) and its id free
|
|
13
13
|
*
|
|
14
14
|
* netOpen(pipe, cb) → id a TCP handle, or with `pipe` a pipe's
|
|
15
|
-
* netOpenFd(id, fd) → 0 | -errno netGuessHandle(fd) → "TCP" | "PIPE" | "TTY" | …
|
|
15
|
+
* netOpenFd(id, fd) → 0 | -errno, netFd(id) → fd | -errno netGuessHandle(fd) → "TCP" | "PIPE" | "TTY" | …
|
|
16
16
|
* netBind(id, ip | path, port, flags), netListen(id, backlog) → 0 | -errno flags: 1 ipv6Only, 2 reusePort
|
|
17
17
|
* netChmod(id, mode) → 0 | -errno a listening pipe's: 1 readable, 2 writable by all
|
|
18
18
|
* netConnect(id, ip | path, port) → 0 | -errno (a NET_CONNECT follows)
|
|
@@ -22,11 +22,16 @@
|
|
|
22
22
|
* netShutdown(id), netClose(id), netReset(id) → 0 | -errno, netRef(id, ref)
|
|
23
23
|
* netAddress(id, peer) → [address, family, port] | path | -errno
|
|
24
24
|
* netNoDelay(id, on), netKeepAlive(id, on, seconds) → 0 | -errno
|
|
25
|
+
* netTypeOfService(id, tos) → 0 | -errno, and with tos -1 the socket's → tos | -errno
|
|
25
26
|
* ttyOpen(fd, cb) → id | -errno a terminal's handle (node:tty's ReadStream), read as
|
|
26
27
|
* a stream; it answers the pipe-only calls as a pipe
|
|
27
28
|
* ttySetRawMode(id, raw) → 0 | -errno
|
|
28
29
|
* dnsLookup(host, family, cb) → cb(errno, code, [[address, family], …]) */
|
|
29
30
|
#define _POSIX_C_SOURCE 200809L /* libuv's headers: pthread_rwlock_t, struct addrinfo */
|
|
31
|
+
#ifdef __APPLE__
|
|
32
|
+
#define _DARWIN_C_SOURCE /* IP_TOS, which _POSIX_C_SOURCE hides on macOS */
|
|
33
|
+
#endif
|
|
34
|
+
#include <errno.h>
|
|
30
35
|
#include <stdlib.h>
|
|
31
36
|
#include <string.h>
|
|
32
37
|
|
|
@@ -122,7 +127,9 @@ uv_stream_t *js_net_stream(int id) {
|
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
bool js_net_hook(int id, const JSNetHook *hook, void *data) {
|
|
125
|
-
|
|
130
|
+
/* a hook is set on an open socket, but taken off a closing one too: its close calls the hook,
|
|
131
|
+
* whose data (node:http's parser) may be freed by then */
|
|
132
|
+
NetHandle *h = hook ? open_handle(id) : (id >= 0 && (size_t)id < nhandles ? handles[id] : NULL);
|
|
126
133
|
if (!h) return false;
|
|
127
134
|
h->hook = hook;
|
|
128
135
|
h->hook_data = data;
|
|
@@ -134,9 +141,10 @@ static NetHandle *handle_arg(uint32_t argc, const JSValue *argv) {
|
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
/* A string argument as UTF-8 in buf. */
|
|
137
|
-
static
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
static size_t string_arg(JSContext *ctx, JSValue v, char *buf, size_t size) {
|
|
145
|
+
size_t n = js_is_string(v) ? js_view_to_utf8(js_string_view(ctx, v), buf, size - 1) : 0;
|
|
146
|
+
buf[n] = '\0';
|
|
147
|
+
return n;
|
|
140
148
|
}
|
|
141
149
|
|
|
142
150
|
/* An IP address and port; -errno for an address that isn't one. */
|
|
@@ -187,6 +195,20 @@ HOST_FN(js_host_net_open_fd) {
|
|
|
187
195
|
return js_int32(h->is_pipe ? uv_pipe_open(&h->u.pipe, fd) : uv_tcp_open(&h->u.tcp, (uv_os_sock_t)fd));
|
|
188
196
|
}
|
|
189
197
|
|
|
198
|
+
/* netFd(id) → the handle's descriptor | -errno (-1 for a Windows socket's, as Node's) */
|
|
199
|
+
HOST_FN(js_host_net_fd) {
|
|
200
|
+
UNUSED;
|
|
201
|
+
NetHandle *h = handle_arg(argc, argv);
|
|
202
|
+
if (!h) return js_int32(UV_EBADF);
|
|
203
|
+
#ifdef _WIN32
|
|
204
|
+
return js_int32(-1);
|
|
205
|
+
#else
|
|
206
|
+
uv_os_fd_t fd;
|
|
207
|
+
int r = uv_fileno((uv_handle_t *)&h->u.stream, &fd);
|
|
208
|
+
return js_int32(r < 0 ? r : fd);
|
|
209
|
+
#endif
|
|
210
|
+
}
|
|
211
|
+
|
|
190
212
|
/* netBind(id, ip, port, flags 1 ipv6Only | 2 reusePort), or a pipe's netBind(id, path) */
|
|
191
213
|
HOST_FN(js_host_net_bind) {
|
|
192
214
|
UNUSED;
|
|
@@ -194,8 +216,8 @@ HOST_FN(js_host_net_bind) {
|
|
|
194
216
|
if (!h || argc < 2) return js_int32(UV_EBADF);
|
|
195
217
|
if (h->is_pipe) {
|
|
196
218
|
char path[PATH_MAX_LEN];
|
|
197
|
-
string_arg(ctx, argv[1], path, sizeof path);
|
|
198
|
-
return js_int32(uv_pipe_bind2(&h->u.pipe, path,
|
|
219
|
+
size_t n = string_arg(ctx, argv[1], path, sizeof path); /* a Linux abstract path starts with a NUL */
|
|
220
|
+
return js_int32(uv_pipe_bind2(&h->u.pipe, path, n, UV_PIPE_NO_TRUNCATE));
|
|
199
221
|
}
|
|
200
222
|
struct sockaddr_storage addr;
|
|
201
223
|
int r = sockaddr_arg(ctx, argv[1], argc > 2 ? argv[2] : JS_UNDEFINED, &addr);
|
|
@@ -253,8 +275,8 @@ HOST_FN(js_host_net_connect) {
|
|
|
253
275
|
int r;
|
|
254
276
|
if (h->is_pipe) {
|
|
255
277
|
char path[PATH_MAX_LEN];
|
|
256
|
-
string_arg(ctx, argv[1], path, sizeof path);
|
|
257
|
-
r = uv_pipe_connect2(req, &h->u.pipe, path,
|
|
278
|
+
size_t n = string_arg(ctx, argv[1], path, sizeof path);
|
|
279
|
+
r = uv_pipe_connect2(req, &h->u.pipe, path, n, UV_PIPE_NO_TRUNCATE, on_connect);
|
|
258
280
|
} else {
|
|
259
281
|
struct sockaddr_storage addr;
|
|
260
282
|
r = sockaddr_arg(ctx, argv[1], argc > 2 ? argv[2] : JS_UNDEFINED, &addr);
|
|
@@ -454,6 +476,50 @@ HOST_FN(js_host_net_keep_alive) {
|
|
|
454
476
|
return js_int32(uv_tcp_keepalive(&h->u.tcp, argc > 1 && js_truthy(argv[1]), delay));
|
|
455
477
|
}
|
|
456
478
|
|
|
479
|
+
/* netTypeOfService(id, tos): IP_TOS, or IPV6_TCLASS on an IPv6 socket, as Node's TCPWrap sets
|
|
480
|
+
* and reads it; with tos -1, the socket's */
|
|
481
|
+
HOST_FN(js_host_net_type_of_service) {
|
|
482
|
+
UNUSED;
|
|
483
|
+
NetHandle *h = handle_arg(argc, argv);
|
|
484
|
+
if (!h || h->is_pipe) return js_int32(h ? UV_EINVAL : UV_EBADF);
|
|
485
|
+
int tos = argc > 1 && js_is_int32(argv[1]) ? js_as_int32(argv[1]) : -1;
|
|
486
|
+
uv_os_fd_t fd;
|
|
487
|
+
int err = uv_fileno((uv_handle_t *)&h->u.tcp, &fd);
|
|
488
|
+
if (err) return js_int32(err);
|
|
489
|
+
struct sockaddr_storage storage;
|
|
490
|
+
int len = sizeof storage;
|
|
491
|
+
err = uv_tcp_getsockname(&h->u.tcp, (struct sockaddr *)&storage, &len);
|
|
492
|
+
if (err) return js_int32(err);
|
|
493
|
+
int level, option;
|
|
494
|
+
if (storage.ss_family == AF_INET) {
|
|
495
|
+
level = IPPROTO_IP;
|
|
496
|
+
option = IP_TOS;
|
|
497
|
+
} else if (storage.ss_family == AF_INET6) {
|
|
498
|
+
#ifdef IPV6_TCLASS
|
|
499
|
+
level = IPPROTO_IPV6;
|
|
500
|
+
option = IPV6_TCLASS;
|
|
501
|
+
#else
|
|
502
|
+
return js_int32(UV_ENOTSUP);
|
|
503
|
+
#endif
|
|
504
|
+
} else {
|
|
505
|
+
return js_int32(UV_EINVAL);
|
|
506
|
+
}
|
|
507
|
+
#ifdef _WIN32
|
|
508
|
+
if (tos >= 0) {
|
|
509
|
+
return js_int32(setsockopt((SOCKET)fd, level, option, (const char *)&tos, sizeof tos) == 0
|
|
510
|
+
? 0 : uv_translate_sys_error(WSAGetLastError()));
|
|
511
|
+
}
|
|
512
|
+
int got = 0, got_len = sizeof got;
|
|
513
|
+
if (getsockopt((SOCKET)fd, level, option, (char *)&got, &got_len) != 0) return js_int32(uv_translate_sys_error(WSAGetLastError()));
|
|
514
|
+
#else
|
|
515
|
+
if (tos >= 0) return js_int32(setsockopt(fd, level, option, &tos, sizeof tos) == 0 ? 0 : uv_translate_sys_error(errno));
|
|
516
|
+
int got = 0;
|
|
517
|
+
socklen_t got_len = sizeof got;
|
|
518
|
+
if (getsockopt(fd, level, option, &got, &got_len) != 0) return js_int32(uv_translate_sys_error(errno));
|
|
519
|
+
#endif
|
|
520
|
+
return js_int32(got);
|
|
521
|
+
}
|
|
522
|
+
|
|
457
523
|
/* ---- Terminals ----------------------------------------------------------------------------- */
|
|
458
524
|
|
|
459
525
|
static bool raw_mode_set;
|
package/node/src/host_sys.c
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
#include "jscontext.h"
|
|
20
20
|
#include "js_ops.h"
|
|
21
21
|
#include "jsstring.h"
|
|
22
|
+
#include "js_uv.h"
|
|
22
23
|
|
|
23
24
|
/* Each error by its C errno and its libuv code: the same number on POSIX, but on Windows libuv
|
|
24
25
|
* has codes of its own (UV_ECONNREFUSED is -4078) while the C library's errno has the C values. */
|
|
@@ -155,6 +156,14 @@ JSValue js_host_timer_unref(JSContext *ctx, JSValue callee, JSValue this_val, ui
|
|
|
155
156
|
return js_int32((int32_t)js_add_timer(ctx, argv[0], js_as_number(argv[1]), true));
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
/* loopNow() → libuv's time in ms, as of the last poll (Node's getLibuvNow): what a socket's
|
|
160
|
+
* activity is measured with, without a clock read or an allocation per read and write */
|
|
161
|
+
JSValue js_host_loop_now(JSContext *ctx, JSValue callee, JSValue this_val, uint32_t argc, const JSValue *argv,
|
|
162
|
+
JSValue new_target) {
|
|
163
|
+
(void)callee, (void)this_val, (void)argc, (void)argv, (void)new_target;
|
|
164
|
+
return js_number((double)uv_now(js_uv_loop(ctx)));
|
|
165
|
+
}
|
|
166
|
+
|
|
158
167
|
/* the id a timer primitive takes: a positive int32, else 0 */
|
|
159
168
|
static uint32_t timer_id(uint32_t argc, const JSValue *argv, uint32_t i) {
|
|
160
169
|
return i < argc && js_is_int32(argv[i]) && js_as_int32(argv[i]) > 0 ? (uint32_t)js_as_int32(argv[i]) : 0;
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -22,6 +22,10 @@ typedef struct JSRuntime JSRuntime;
|
|
|
22
22
|
typedef struct JSAtomTable {
|
|
23
23
|
struct JSAtomEntry { JSString *str; uint32_t hash; } *entries; /* open addressing; str NULL = empty */
|
|
24
24
|
size_t cap, count;
|
|
25
|
+
/* The atoms added since the last collection: the only ones a young collection can find dead,
|
|
26
|
+
* since an old cell stays marked until a full collection. malloc'd. */
|
|
27
|
+
JSString **young;
|
|
28
|
+
size_t young_count, young_cap;
|
|
25
29
|
} JSAtomTable;
|
|
26
30
|
|
|
27
31
|
/* ---- The runtime's predefined atoms ------------------------------------ */
|