@scriptc/runtime 0.0.25 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/scr_async.c +235 -6
- package/src/scr_child.c +2 -2
- package/src/scr_events.c +48 -2
- package/src/scr_events_emitter.c +4 -4
- package/src/scr_http.c +1 -0
- package/src/scr_island.c +8 -4
- package/src/scr_lib.c +61 -12
- package/src/scr_musl.c +274 -0
- package/src/scr_net.c +118 -3
- package/src/scr_runtime.h +60 -21
- package/src/scr_tls.c +22 -0
- package/vendor/README.md +8 -0
package/src/scr_lib.c
CHANGED
|
@@ -175,6 +175,8 @@ ScrStr *scr_process_platform(void) {
|
|
|
175
175
|
scr_platform_str = scr_str_new("darwin", 6);
|
|
176
176
|
#elif defined(__linux__)
|
|
177
177
|
scr_platform_str = scr_str_new("linux", 5);
|
|
178
|
+
#elif defined(__wasi__)
|
|
179
|
+
scr_platform_str = scr_str_new("wasi", 4);
|
|
178
180
|
#elif defined(_WIN32)
|
|
179
181
|
scr_platform_str = scr_str_new("win32", 5);
|
|
180
182
|
#else
|
|
@@ -188,7 +190,11 @@ ScrStr *scr_process_platform(void) {
|
|
|
188
190
|
* Node spells its own build's arch. Interned like process.platform. */
|
|
189
191
|
ScrStr *scr_process_arch(void) {
|
|
190
192
|
if (!scr_arch_str) {
|
|
191
|
-
#if defined(
|
|
193
|
+
#if defined(__wasm32__)
|
|
194
|
+
scr_arch_str = scr_str_new("wasm32", 6);
|
|
195
|
+
#elif defined(__wasm64__)
|
|
196
|
+
scr_arch_str = scr_str_new("wasm64", 6);
|
|
197
|
+
#elif defined(__aarch64__) || defined(_M_ARM64)
|
|
192
198
|
scr_arch_str = scr_str_new("arm64", 5);
|
|
193
199
|
#elif defined(__x86_64__) || defined(_M_X64)
|
|
194
200
|
scr_arch_str = scr_str_new("x64", 3);
|
|
@@ -350,11 +356,11 @@ ScrArr *scr_env_pairs(void) {
|
|
|
350
356
|
|
|
351
357
|
double scr_process_pid(void) { return (double)getpid(); }
|
|
352
358
|
|
|
353
|
-
#
|
|
359
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
354
360
|
/* No uids/gids exist on Windows: Node's process object simply has no
|
|
355
361
|
* getuid/getgid members there, so a call is the property-access TypeError
|
|
356
362
|
* below — thrown catchably, exactly what `process.getuid()` does under
|
|
357
|
-
* Windows Node. */
|
|
363
|
+
* Windows Node. WASI has no uid/gid process model either. */
|
|
358
364
|
double scr_process_getuid(void) {
|
|
359
365
|
scr_throw_error_msg(SCR_ERR_TYPE, "process.getuid is not a function", 32);
|
|
360
366
|
return 0;
|
|
@@ -532,6 +538,14 @@ static int scr_win_kill(int pid, int sig) {
|
|
|
532
538
|
return 0;
|
|
533
539
|
}
|
|
534
540
|
#define scr_sys_kill(pid, sig) scr_win_kill((int)(pid), (sig))
|
|
541
|
+
#elif defined(__wasi__)
|
|
542
|
+
static int scr_wasi_kill(int pid, int sig) {
|
|
543
|
+
(void)pid;
|
|
544
|
+
(void)sig;
|
|
545
|
+
errno = ENOSYS;
|
|
546
|
+
return -1;
|
|
547
|
+
}
|
|
548
|
+
#define scr_sys_kill(pid, sig) scr_wasi_kill((int)(pid), (sig))
|
|
535
549
|
#else
|
|
536
550
|
#define scr_sys_kill(pid, sig) kill((pid_t)(pid), (sig))
|
|
537
551
|
#endif
|
|
@@ -689,6 +703,27 @@ ScrStr *scr_os_tmpdir(void) {
|
|
|
689
703
|
if (len > 1 && (buf[len - 1] == '\\' || buf[len - 1] == '/')) len--;
|
|
690
704
|
return scr_str_new(buf, len);
|
|
691
705
|
}
|
|
706
|
+
#elif defined(__wasi__)
|
|
707
|
+
/* WASI has an inherited environment but no passwd database, uname, or
|
|
708
|
+
* physical-memory query. Keep the useful environment-backed answers and
|
|
709
|
+
* spell the platform explicitly for the rest. */
|
|
710
|
+
ScrStr *scr_os_homedir(void) {
|
|
711
|
+
const char *home = getenv("HOME");
|
|
712
|
+
return scr_str_new(home ? home : "", home ? strlen(home) : 0);
|
|
713
|
+
}
|
|
714
|
+
ScrStr *scr_os_user_name(void) {
|
|
715
|
+
const char *user = getenv("USER");
|
|
716
|
+
return scr_str_new(user ? user : "", user ? strlen(user) : 0);
|
|
717
|
+
}
|
|
718
|
+
ScrStr *scr_os_user_shell(void) { return scr_str_new("", 0); }
|
|
719
|
+
ScrStr *scr_os_user_homedir(void) { return scr_os_homedir(); }
|
|
720
|
+
ScrStr *scr_os_release(void) { return scr_str_new("", 0); }
|
|
721
|
+
ScrStr *scr_os_type(void) { return scr_str_new("WASI", 4); }
|
|
722
|
+
double scr_os_totalmem(void) { return 0; }
|
|
723
|
+
/* The guest temp namespace is stable across hosts. `scriptc run` preopens
|
|
724
|
+
* the host's /tmp at this path; other WASI hosts can provide the same
|
|
725
|
+
* capability without leaking a host-specific TMPDIR into the module. */
|
|
726
|
+
ScrStr *scr_os_tmpdir(void) { return scr_str_new("/tmp", 4); }
|
|
692
727
|
#else
|
|
693
728
|
ScrStr *scr_os_homedir(void) {
|
|
694
729
|
/* uv_os_homedir: $HOME when set (even empty is "set" only if non-NULL;
|
|
@@ -1226,7 +1261,7 @@ double scr_thread_cpu_system_diff(double prev) { return scr_thread_cpu_system()
|
|
|
1226
1261
|
* Darwin's bytes by 1024; the rest are getrusage's own counters, zero
|
|
1227
1262
|
* where the platform never fills them). */
|
|
1228
1263
|
double scr_process_rusage(double idx) {
|
|
1229
|
-
#
|
|
1264
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
1230
1265
|
/* uv fills only the CPU times and page-fault/maxRSS slice on Windows;
|
|
1231
1266
|
* everything else answers 0 — Node's own shape there. */
|
|
1232
1267
|
switch ((int)idx) {
|
|
@@ -1336,6 +1371,11 @@ double scr_process_umask(double mask) {
|
|
|
1336
1371
|
_umask_s((int)mask, &prev);
|
|
1337
1372
|
}
|
|
1338
1373
|
return (double)prev;
|
|
1374
|
+
#elif defined(__wasi__)
|
|
1375
|
+
static mode_t current = 022;
|
|
1376
|
+
mode_t prev = current;
|
|
1377
|
+
if (mask >= 0) current = (mode_t)mask;
|
|
1378
|
+
return (double)prev;
|
|
1339
1379
|
#else
|
|
1340
1380
|
mode_t prev;
|
|
1341
1381
|
if (mask < 0) {
|
|
@@ -1653,9 +1693,10 @@ void scr_fs_chmod(ScrStr *path, double mode) {
|
|
|
1653
1693
|
}
|
|
1654
1694
|
|
|
1655
1695
|
void scr_fs_chown(ScrStr *path, double uid, double gid) {
|
|
1656
|
-
#
|
|
1696
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
1657
1697
|
/* libuv's uv_fs_chown on Windows is an unconditional no-op success —
|
|
1658
|
-
* Node's chownSync "works" and changes nothing there;
|
|
1698
|
+
* Node's chownSync "works" and changes nothing there; WASI likewise has
|
|
1699
|
+
* no ownership model. */
|
|
1659
1700
|
(void)path; (void)uid; (void)gid;
|
|
1660
1701
|
#else
|
|
1661
1702
|
/* Node passes the ids straight to chown(2); -1 is the POSIX "leave
|
|
@@ -1863,7 +1904,7 @@ static bool scr_fs_write_fd_valid(double fd) {
|
|
|
1863
1904
|
* call, preserving any signal that was already pending. */
|
|
1864
1905
|
static ssize_t scr_fs_write_once(int fd, const void *data, size_t length,
|
|
1865
1906
|
bool positioned, double position) {
|
|
1866
|
-
#
|
|
1907
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
1867
1908
|
return positioned
|
|
1868
1909
|
? scr_fs_pwrite(fd, data, length, position)
|
|
1869
1910
|
: write(fd, data, length);
|
|
@@ -2520,23 +2561,23 @@ void scr_fs_rm_opts_retry(ScrStr *path, bool recursive, bool force, double max_r
|
|
|
2520
2561
|
scr_str_release(f.path);
|
|
2521
2562
|
}
|
|
2522
2563
|
|
|
2523
|
-
#
|
|
2524
|
-
/* No mkdtemp in the CRT: libuv's own fallback shape — six random
|
|
2564
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
2565
|
+
/* No mkdtemp in the Windows CRT or WASI libc: libuv's own fallback shape — six random
|
|
2525
2566
|
* [a-z0-9] name characters from the CSPRNG, retried on EEXIST. */
|
|
2526
|
-
static char *
|
|
2567
|
+
static char *scr_portable_mkdtemp(char *tmpl) {
|
|
2527
2568
|
static const char cs[] = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
2528
2569
|
size_t len = strlen(tmpl);
|
|
2529
2570
|
for (int tries = 0; tries < 32; tries++) {
|
|
2530
2571
|
unsigned char r[6];
|
|
2531
2572
|
arc4random_buf(r, sizeof r);
|
|
2532
2573
|
for (size_t i = 0; i < 6; i++) tmpl[len - 6 + i] = cs[r[i] % 36];
|
|
2533
|
-
if (
|
|
2574
|
+
if (scr_sys_mkdir(tmpl, 0777) == 0) return tmpl;
|
|
2534
2575
|
if (errno != EEXIST) break;
|
|
2535
2576
|
}
|
|
2536
2577
|
memcpy(tmpl + len - 6, "XXXXXX", 6); /* the error message shows the template */
|
|
2537
2578
|
return NULL;
|
|
2538
2579
|
}
|
|
2539
|
-
#define mkdtemp
|
|
2580
|
+
#define mkdtemp scr_portable_mkdtemp
|
|
2540
2581
|
#endif
|
|
2541
2582
|
|
|
2542
2583
|
ScrStr *scr_fs_mkdtemp(ScrStr *prefix) {
|
|
@@ -2688,6 +2729,8 @@ double scr_process_columns(double fd) {
|
|
|
2688
2729
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
2689
2730
|
if (h == INVALID_HANDLE_VALUE || !GetConsoleScreenBufferInfo(h, &info)) return -1;
|
|
2690
2731
|
return (double)(info.srWindow.Right - info.srWindow.Left + 1);
|
|
2732
|
+
#elif defined(__wasi__)
|
|
2733
|
+
return -1;
|
|
2691
2734
|
#else
|
|
2692
2735
|
struct winsize ws;
|
|
2693
2736
|
if (ioctl((int)fd, TIOCGWINSZ, &ws) != 0) return -1;
|
|
@@ -2728,6 +2771,12 @@ void scr_process_stdin_set_raw_mode(bool raw) {
|
|
|
2728
2771
|
(void)SetConsoleMode(h, scr_stdin_cooked);
|
|
2729
2772
|
}
|
|
2730
2773
|
}
|
|
2774
|
+
#elif defined(__wasi__)
|
|
2775
|
+
void scr_process_stdin_set_raw_mode(bool raw) {
|
|
2776
|
+
(void)raw;
|
|
2777
|
+
const char msg[] = "process.stdin.setRawMode is not a function";
|
|
2778
|
+
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
|
2779
|
+
}
|
|
2731
2780
|
#else
|
|
2732
2781
|
static struct termios scr_stdin_cooked;
|
|
2733
2782
|
static bool scr_stdin_cooked_saved = false;
|
package/src/scr_musl.c
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/* musl libc shim — compiled into linux-musl target builds only (cc.ts adds
|
|
2
|
+
* this TU together with -DSCR_MUSL). musl deliberately provides no libc
|
|
3
|
+
* identification macro, so the target-selected project macro is the guard.
|
|
4
|
+
*
|
|
5
|
+
* The x86_64 ucontext implementation below is derived from libucontext
|
|
6
|
+
* commit 49e671dd52ff6791295d8161ad3b6da7dc5f6f9d:
|
|
7
|
+
* https://github.com/kaniini/libucontext
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2018-2025 Ariadne Conill <ariadne@dereferenced.org>
|
|
10
|
+
*
|
|
11
|
+
* Permission to use, copy, modify, and/or distribute this software for any
|
|
12
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
13
|
+
* copyright notice and this permission notice appear in all copies.
|
|
14
|
+
*
|
|
15
|
+
* This software is provided 'as is' and without any warranty, express or
|
|
16
|
+
* implied. In no event shall the authors be liable for any damages arising
|
|
17
|
+
* from the use of this software. */
|
|
18
|
+
#ifdef SCR_MUSL
|
|
19
|
+
|
|
20
|
+
#include "scr_runtime.h"
|
|
21
|
+
|
|
22
|
+
#include <errno.h>
|
|
23
|
+
#include <stdlib.h>
|
|
24
|
+
#include <sys/random.h>
|
|
25
|
+
#ifndef SCR_LIB
|
|
26
|
+
#include <stdarg.h>
|
|
27
|
+
#include <ucontext.h>
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
#if !defined(__x86_64__)
|
|
31
|
+
#error "scriptc's musl runtime currently supports x86_64 only"
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
/* The scriptc runtime uses arc4random_buf as its infallible CSPRNG contract.
|
|
35
|
+
* Linux getrandom has the same kernel source and needs neither mutable state
|
|
36
|
+
* nor an fd. Retry interrupts and short reads; any other failure enters the
|
|
37
|
+
* runtime trap funnel rather than returning predictable or partially
|
|
38
|
+
* initialized bytes. Executables still print and abort there, while library
|
|
39
|
+
* artifacts deliver the failure to their registered panic sink. */
|
|
40
|
+
void arc4random_buf(void *buf, size_t n) {
|
|
41
|
+
unsigned char *p = buf;
|
|
42
|
+
while (n > 0) {
|
|
43
|
+
ssize_t got = getrandom(p, n, 0);
|
|
44
|
+
if (got > 0) {
|
|
45
|
+
p += (size_t)got;
|
|
46
|
+
n -= (size_t)got;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (got < 0 && errno == EINTR) continue;
|
|
50
|
+
scr_trap("scriptc: getrandom failed\n");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Library artifacts contain no fibers or event loop, so they need only the
|
|
55
|
+
* CSPRNG shim above and must not acquire context-switching definitions. */
|
|
56
|
+
#ifndef SCR_LIB
|
|
57
|
+
|
|
58
|
+
/* musl exposes the POSIX ucontext types but intentionally omits these legacy
|
|
59
|
+
* functions. scriptc's async/generator fibers need only user-space register
|
|
60
|
+
* swaps; they never use a context to mutate the process signal mask. This is
|
|
61
|
+
* libucontext's fast (non-POSIX-signal-mask) x86_64 implementation, emitted
|
|
62
|
+
* under the standard symbol names that musl leaves unresolved. */
|
|
63
|
+
_Static_assert(offsetof(ucontext_t, uc_mcontext.gregs) == 40,
|
|
64
|
+
"unexpected musl x86_64 ucontext layout");
|
|
65
|
+
_Static_assert(offsetof(ucontext_t, uc_mcontext.fpregs) == 224,
|
|
66
|
+
"unexpected musl x86_64 fpregs layout");
|
|
67
|
+
_Static_assert(offsetof(ucontext_t, __fpregs_mem) == 424,
|
|
68
|
+
"unexpected musl x86_64 fpregs storage layout");
|
|
69
|
+
|
|
70
|
+
/* Keep the assembler independent of whether the sysroot exposes glibc's
|
|
71
|
+
* REG_* spellings as enum constants, self-referential macros, or neither. */
|
|
72
|
+
#undef REG_R8
|
|
73
|
+
#undef REG_R9
|
|
74
|
+
#undef REG_R10
|
|
75
|
+
#undef REG_R11
|
|
76
|
+
#undef REG_R12
|
|
77
|
+
#undef REG_R13
|
|
78
|
+
#undef REG_R14
|
|
79
|
+
#undef REG_R15
|
|
80
|
+
#undef REG_RDI
|
|
81
|
+
#undef REG_RSI
|
|
82
|
+
#undef REG_RBP
|
|
83
|
+
#undef REG_RBX
|
|
84
|
+
#undef REG_RDX
|
|
85
|
+
#undef REG_RAX
|
|
86
|
+
#undef REG_RCX
|
|
87
|
+
#undef REG_RSP
|
|
88
|
+
#undef REG_RIP
|
|
89
|
+
#define REG_R8 0
|
|
90
|
+
#define REG_R9 1
|
|
91
|
+
#define REG_R10 2
|
|
92
|
+
#define REG_R11 3
|
|
93
|
+
#define REG_R12 4
|
|
94
|
+
#define REG_R13 5
|
|
95
|
+
#define REG_R14 6
|
|
96
|
+
#define REG_R15 7
|
|
97
|
+
#define REG_RDI 8
|
|
98
|
+
#define REG_RSI 9
|
|
99
|
+
#define REG_RBP 10
|
|
100
|
+
#define REG_RBX 11
|
|
101
|
+
#define REG_RDX 12
|
|
102
|
+
#define REG_RAX 13
|
|
103
|
+
#define REG_RCX 14
|
|
104
|
+
#define REG_RSP 15
|
|
105
|
+
#define REG_RIP 16
|
|
106
|
+
|
|
107
|
+
#define SCR_STR_INNER(x) #x
|
|
108
|
+
#define SCR_STR(x) SCR_STR_INNER(x)
|
|
109
|
+
#define SCR_UC_GREG(reg) (40 + ((reg) * 8))
|
|
110
|
+
|
|
111
|
+
__asm__(
|
|
112
|
+
".text\n"
|
|
113
|
+
".global getcontext\n"
|
|
114
|
+
"getcontext:\n"
|
|
115
|
+
" movq %r8, " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rdi)\n"
|
|
116
|
+
" movq %r9, " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rdi)\n"
|
|
117
|
+
" movq %r10, " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rdi)\n"
|
|
118
|
+
" movq %r11, " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rdi)\n"
|
|
119
|
+
" movq %r12, " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rdi)\n"
|
|
120
|
+
" movq %r13, " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rdi)\n"
|
|
121
|
+
" movq %r14, " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rdi)\n"
|
|
122
|
+
" movq %r15, " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rdi)\n"
|
|
123
|
+
" movq %rdi, " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rdi)\n"
|
|
124
|
+
" movq %rsi, " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rdi)\n"
|
|
125
|
+
" movq %rbp, " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rdi)\n"
|
|
126
|
+
" movq %rbx, " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rdi)\n"
|
|
127
|
+
" movq %rdx, " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rdi)\n"
|
|
128
|
+
" movq %rax, " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rdi)\n"
|
|
129
|
+
" movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rdi)\n"
|
|
130
|
+
" movq (%rsp), %rcx\n"
|
|
131
|
+
" movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rdi)\n"
|
|
132
|
+
" leaq 8(%rsp), %rcx\n"
|
|
133
|
+
" movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rdi)\n"
|
|
134
|
+
" leaq 424(%rdi), %rcx\n"
|
|
135
|
+
" movq %rcx, 224(%rdi)\n"
|
|
136
|
+
" fnstenv (%rcx)\n"
|
|
137
|
+
" fldenv (%rcx)\n"
|
|
138
|
+
" stmxcsr 448(%rdi)\n"
|
|
139
|
+
" xorl %eax, %eax\n"
|
|
140
|
+
" ret\n"
|
|
141
|
+
|
|
142
|
+
".global setcontext\n"
|
|
143
|
+
"setcontext:\n"
|
|
144
|
+
" movq 224(%rdi), %rcx\n"
|
|
145
|
+
" fldenv (%rcx)\n"
|
|
146
|
+
" ldmxcsr 448(%rdi)\n"
|
|
147
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rdi), %r8\n"
|
|
148
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rdi), %r9\n"
|
|
149
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rdi), %r10\n"
|
|
150
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rdi), %r11\n"
|
|
151
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rdi), %r12\n"
|
|
152
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rdi), %r13\n"
|
|
153
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rdi), %r14\n"
|
|
154
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rdi), %r15\n"
|
|
155
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rdi), %rsi\n"
|
|
156
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rdi), %rbp\n"
|
|
157
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rdi), %rbx\n"
|
|
158
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rdi), %rdx\n"
|
|
159
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rdi), %rax\n"
|
|
160
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rdi), %rcx\n"
|
|
161
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rdi), %rsp\n"
|
|
162
|
+
" pushq " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rdi)\n"
|
|
163
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rdi), %rdi\n"
|
|
164
|
+
" xorl %eax, %eax\n"
|
|
165
|
+
" ret\n"
|
|
166
|
+
|
|
167
|
+
".global swapcontext\n"
|
|
168
|
+
"swapcontext:\n"
|
|
169
|
+
" movq %r8, " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rdi)\n"
|
|
170
|
+
" movq %r9, " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rdi)\n"
|
|
171
|
+
" movq %r10, " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rdi)\n"
|
|
172
|
+
" movq %r11, " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rdi)\n"
|
|
173
|
+
" movq %r12, " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rdi)\n"
|
|
174
|
+
" movq %r13, " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rdi)\n"
|
|
175
|
+
" movq %r14, " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rdi)\n"
|
|
176
|
+
" movq %r15, " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rdi)\n"
|
|
177
|
+
" movq %rdi, " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rdi)\n"
|
|
178
|
+
" movq %rsi, " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rdi)\n"
|
|
179
|
+
" movq %rbp, " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rdi)\n"
|
|
180
|
+
" movq %rbx, " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rdi)\n"
|
|
181
|
+
" movq %rdx, " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rdi)\n"
|
|
182
|
+
" movq %rax, " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rdi)\n"
|
|
183
|
+
" movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rdi)\n"
|
|
184
|
+
" movq (%rsp), %rcx\n"
|
|
185
|
+
" movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rdi)\n"
|
|
186
|
+
" leaq 8(%rsp), %rcx\n"
|
|
187
|
+
" movq %rcx, " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rdi)\n"
|
|
188
|
+
" leaq 424(%rdi), %rcx\n"
|
|
189
|
+
" movq %rcx, 224(%rdi)\n"
|
|
190
|
+
" fnstenv (%rcx)\n"
|
|
191
|
+
" fldenv (%rcx)\n"
|
|
192
|
+
" stmxcsr 448(%rdi)\n"
|
|
193
|
+
" movq 224(%rsi), %rcx\n"
|
|
194
|
+
" fldenv (%rcx)\n"
|
|
195
|
+
" ldmxcsr 448(%rsi)\n"
|
|
196
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R8)) "(%rsi), %r8\n"
|
|
197
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R9)) "(%rsi), %r9\n"
|
|
198
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R10)) "(%rsi), %r10\n"
|
|
199
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R11)) "(%rsi), %r11\n"
|
|
200
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R12)) "(%rsi), %r12\n"
|
|
201
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R13)) "(%rsi), %r13\n"
|
|
202
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R14)) "(%rsi), %r14\n"
|
|
203
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_R15)) "(%rsi), %r15\n"
|
|
204
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RDI)) "(%rsi), %rdi\n"
|
|
205
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RBP)) "(%rsi), %rbp\n"
|
|
206
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RBX)) "(%rsi), %rbx\n"
|
|
207
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RDX)) "(%rsi), %rdx\n"
|
|
208
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RAX)) "(%rsi), %rax\n"
|
|
209
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RCX)) "(%rsi), %rcx\n"
|
|
210
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RSP)) "(%rsi), %rsp\n"
|
|
211
|
+
" pushq " SCR_STR(SCR_UC_GREG(REG_RIP)) "(%rsi)\n"
|
|
212
|
+
" movq " SCR_STR(SCR_UC_GREG(REG_RSI)) "(%rsi), %rsi\n"
|
|
213
|
+
" xorl %eax, %eax\n"
|
|
214
|
+
" ret\n"
|
|
215
|
+
|
|
216
|
+
".hidden scr_musl_context_trampoline\n"
|
|
217
|
+
"scr_musl_context_trampoline:\n"
|
|
218
|
+
" movq (%rbx), %rdi\n"
|
|
219
|
+
" testq %rdi, %rdi\n"
|
|
220
|
+
" je 1f\n"
|
|
221
|
+
" call setcontext\n"
|
|
222
|
+
" ud2\n"
|
|
223
|
+
"1:\n"
|
|
224
|
+
" xorl %edi, %edi\n"
|
|
225
|
+
" call exit\n"
|
|
226
|
+
" ud2\n");
|
|
227
|
+
|
|
228
|
+
extern void scr_musl_context_trampoline(void);
|
|
229
|
+
|
|
230
|
+
void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) {
|
|
231
|
+
greg_t *sp;
|
|
232
|
+
va_list va;
|
|
233
|
+
int i;
|
|
234
|
+
unsigned int link_slot = (unsigned int)(argc > 6 ? argc - 6 : 0) + 1;
|
|
235
|
+
|
|
236
|
+
sp = (greg_t *)((uintptr_t)ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size);
|
|
237
|
+
sp -= link_slot;
|
|
238
|
+
sp = (greg_t *)(((uintptr_t)sp & ~(uintptr_t)15) - 8);
|
|
239
|
+
|
|
240
|
+
ucp->uc_mcontext.fpregs = (void *)&ucp->__fpregs_mem;
|
|
241
|
+
ucp->uc_mcontext.gregs[REG_RIP] = (greg_t)(uintptr_t)func;
|
|
242
|
+
ucp->uc_mcontext.gregs[REG_RBX] = (greg_t)(uintptr_t)&sp[link_slot];
|
|
243
|
+
ucp->uc_mcontext.gregs[REG_RSP] = (greg_t)(uintptr_t)sp;
|
|
244
|
+
|
|
245
|
+
sp[0] = (greg_t)(uintptr_t)&scr_musl_context_trampoline;
|
|
246
|
+
sp[link_slot] = (greg_t)(uintptr_t)ucp->uc_link;
|
|
247
|
+
|
|
248
|
+
va_start(va, argc);
|
|
249
|
+
for (i = 0; i < argc; i++) {
|
|
250
|
+
greg_t arg = va_arg(va, greg_t);
|
|
251
|
+
switch (i) {
|
|
252
|
+
case 0: ucp->uc_mcontext.gregs[REG_RDI] = arg; break;
|
|
253
|
+
case 1: ucp->uc_mcontext.gregs[REG_RSI] = arg; break;
|
|
254
|
+
case 2: ucp->uc_mcontext.gregs[REG_RDX] = arg; break;
|
|
255
|
+
case 3: ucp->uc_mcontext.gregs[REG_RCX] = arg; break;
|
|
256
|
+
case 4: ucp->uc_mcontext.gregs[REG_R8] = arg; break;
|
|
257
|
+
case 5: ucp->uc_mcontext.gregs[REG_R9] = arg; break;
|
|
258
|
+
default: sp[i - 5] = arg; break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
va_end(va);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#undef SCR_UC_GREG
|
|
265
|
+
#undef SCR_STR
|
|
266
|
+
#undef SCR_STR_INNER
|
|
267
|
+
|
|
268
|
+
#endif /* !SCR_LIB */
|
|
269
|
+
|
|
270
|
+
#else /* !SCR_MUSL */
|
|
271
|
+
|
|
272
|
+
typedef int scr_musl_unused;
|
|
273
|
+
|
|
274
|
+
#endif /* SCR_MUSL */
|
package/src/scr_net.c
CHANGED
|
@@ -389,6 +389,16 @@ struct ScrNetServer {
|
|
|
389
389
|
bool closing; /* close() called; 'close' fires when conns drain */
|
|
390
390
|
bool close_emitted; /* settled: off the registry, listeners dropped */
|
|
391
391
|
bool emit_listening;
|
|
392
|
+
/* The writable http.Server timeout property values. Kept on the shared
|
|
393
|
+
* server handle because http/https servers retain that handle identity;
|
|
394
|
+
* plain net servers never expose these fields through the static type
|
|
395
|
+
* surface. Indices match lower-server.ts's selector ABI. */
|
|
396
|
+
double http_timeouts[5];
|
|
397
|
+
/* A dynamic write may store any JS value on these ordinary writable
|
|
398
|
+
* properties. Numeric writes stay in http_timeouts; every other value
|
|
399
|
+
* is retained here so dynamic reads preserve kind and identity. */
|
|
400
|
+
ScrDyn *http_timeout_dyn[5];
|
|
401
|
+
bool http_timeout_surface; /* true only for HTTP/1 and HTTPS servers */
|
|
392
402
|
bool defer_conn; /* TLS: 'connection' fires post-handshake, not at accept */
|
|
393
403
|
bool bound_v6; /* the bound family (address()'s 'IPv6'/'IPv4' split) */
|
|
394
404
|
ScrStr *bound_host; /* the explicit bind host (NULL = the host-less any:
|
|
@@ -556,6 +566,7 @@ void scr_net_server_release(ScrNetServer *s) {
|
|
|
556
566
|
scr_net_ls_drop(&s->close_ls);
|
|
557
567
|
scr_net_ls_drop(&s->listening_cbs);
|
|
558
568
|
scr_closure_release(s->close_override);
|
|
569
|
+
for (size_t i = 0; i < 5; i++) scr_dyn_release(s->http_timeout_dyn[i]);
|
|
559
570
|
scr_str_release(s->bound_host);
|
|
560
571
|
scr_str_release(s->pending_err);
|
|
561
572
|
if (s->fd >= 0) scr_net_close_fd_raw(s->fd);
|
|
@@ -1200,6 +1211,11 @@ static ScrNetServer *scr_net_server_new(void) {
|
|
|
1200
1211
|
s->kind = SCR_NET_K_SERVER;
|
|
1201
1212
|
s->rc = 1;
|
|
1202
1213
|
s->fd = -1;
|
|
1214
|
+
s->http_timeouts[0] = 0; /* timeout */
|
|
1215
|
+
s->http_timeouts[1] = 5000; /* keepAliveTimeout */
|
|
1216
|
+
s->http_timeouts[2] = 60000; /* headersTimeout */
|
|
1217
|
+
s->http_timeouts[3] = 300000; /* requestTimeout */
|
|
1218
|
+
s->http_timeouts[4] = 1000; /* keepAliveTimeoutBuffer */
|
|
1203
1219
|
#ifdef SCR_RC_AUDIT
|
|
1204
1220
|
scr_net_live++;
|
|
1205
1221
|
#endif
|
|
@@ -1378,6 +1394,79 @@ void scr_net_listen_opts(ScrNetServer *s, double port, ScrStr *host /*borrowed*/
|
|
|
1378
1394
|
|
|
1379
1395
|
double scr_net_server_port(ScrNetServer *s) { return (double)s->port; }
|
|
1380
1396
|
|
|
1397
|
+
double scr_net_server_timeout_get(ScrNetServer *s, double field) {
|
|
1398
|
+
static const char *const names[] = {
|
|
1399
|
+
"timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
|
|
1400
|
+
"keepAliveTimeoutBuffer",
|
|
1401
|
+
};
|
|
1402
|
+
int i = (int)field;
|
|
1403
|
+
if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
|
|
1404
|
+
if (s->http_timeout_dyn[i] != NULL) {
|
|
1405
|
+
scr_dyn_arg_type_fail(names[i], "of type number", s->http_timeout_dyn[i]);
|
|
1406
|
+
return 0; /* pending exception; typed reads validate an any-written value */
|
|
1407
|
+
}
|
|
1408
|
+
return s->http_timeouts[i];
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
void scr_net_server_timeout_set(ScrNetServer *s, double field, double value) {
|
|
1412
|
+
int i = (int)field;
|
|
1413
|
+
if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
|
|
1414
|
+
scr_dyn_release(s->http_timeout_dyn[i]);
|
|
1415
|
+
s->http_timeout_dyn[i] = NULL;
|
|
1416
|
+
s->http_timeouts[i] = value;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
void scr_net_server_enable_http_timeout_surface(ScrNetServer *s) {
|
|
1420
|
+
s->http_timeout_surface = true;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/* Constructor-option validation is intentionally separate from the plain
|
|
1424
|
+
* writable-property setter above. Node validates these option values as
|
|
1425
|
+
* non-negative safe integers, while later property assignments store the
|
|
1426
|
+
* number directly. */
|
|
1427
|
+
bool scr_net_server_timeout_option_check(double field, double value) {
|
|
1428
|
+
static const char *const names[] = {
|
|
1429
|
+
"timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
|
|
1430
|
+
"keepAliveTimeoutBuffer",
|
|
1431
|
+
};
|
|
1432
|
+
int i = (int)field;
|
|
1433
|
+
if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
|
|
1434
|
+
char recv[48], msg[224];
|
|
1435
|
+
if (!(isfinite(value) && trunc(value) == value)) {
|
|
1436
|
+
scr_num_received(value, recv);
|
|
1437
|
+
int len = snprintf(msg, sizeof msg,
|
|
1438
|
+
"The value of \"%s\" is out of range. It must be an integer. Received %s",
|
|
1439
|
+
names[i], recv);
|
|
1440
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
if (value < 0 || value > 9007199254740991.0) {
|
|
1444
|
+
scr_num_received(value, recv);
|
|
1445
|
+
int len = snprintf(msg, sizeof msg,
|
|
1446
|
+
"The value of \"%s\" is out of range. It must be >= 0 && <= 9007199254740991. Received %s",
|
|
1447
|
+
names[i], recv);
|
|
1448
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
return true;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
void scr_net_server_timeout_option_set(ScrNetServer *s, double field, const ScrDyn *value) {
|
|
1455
|
+
int i = (int)field;
|
|
1456
|
+
if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
|
|
1457
|
+
if (value->kind == SCR_DYN_UNDEF) return; /* optional field is absent */
|
|
1458
|
+
if (value->kind != SCR_DYN_NUM) {
|
|
1459
|
+
static const char *const names[] = {
|
|
1460
|
+
"timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
|
|
1461
|
+
"keepAliveTimeoutBuffer",
|
|
1462
|
+
};
|
|
1463
|
+
scr_dyn_arg_type_fail(names[i], "of type number", value);
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
if (!scr_net_server_timeout_option_check(field, value->v.num)) return;
|
|
1467
|
+
scr_net_server_timeout_set(s, field, value->v.num);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1381
1470
|
/* address()'s other two fields — the bound host ('::'/'0.0.0.0' for the
|
|
1382
1471
|
* host-less any, the normalized explicit host otherwise) and the family
|
|
1383
1472
|
* string. Answer the any-form defaults before listen (Node answers null
|
|
@@ -3299,13 +3388,28 @@ static ScrDyn *scr_net_dynh_srv_invoke(void *h, ScrDyn *self, const char *method
|
|
|
3299
3388
|
return NULL;
|
|
3300
3389
|
}
|
|
3301
3390
|
|
|
3391
|
+
static int scr_net_dynh_srv_timeout_field(const char *key) {
|
|
3392
|
+
if (strcmp(key, "timeout") == 0) return 0;
|
|
3393
|
+
if (strcmp(key, "keepAliveTimeout") == 0) return 1;
|
|
3394
|
+
if (strcmp(key, "headersTimeout") == 0) return 2;
|
|
3395
|
+
if (strcmp(key, "requestTimeout") == 0) return 3;
|
|
3396
|
+
if (strcmp(key, "keepAliveTimeoutBuffer") == 0) return 4;
|
|
3397
|
+
return -1;
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3302
3400
|
static ScrDyn *scr_net_dynh_srv_get(void *h, const char *key, size_t key_len) {
|
|
3303
3401
|
ScrNetServer *s = (ScrNetServer *)h;
|
|
3304
3402
|
(void)key_len;
|
|
3305
3403
|
if (strcmp(key, "listening") == 0) return scr_dyn_new_bool(s->listening);
|
|
3404
|
+
int timeout_field = scr_net_dynh_srv_timeout_field(key);
|
|
3405
|
+
if (timeout_field >= 0 && s->http_timeout_surface) {
|
|
3406
|
+
if (s->http_timeout_dyn[timeout_field] != NULL) {
|
|
3407
|
+
return scr_dyn_retain(s->http_timeout_dyn[timeout_field]);
|
|
3408
|
+
}
|
|
3409
|
+
return scr_dyn_new_num(scr_net_server_timeout_get(s, (double)timeout_field));
|
|
3410
|
+
}
|
|
3306
3411
|
{
|
|
3307
|
-
static const char *const known[] = { "maxConnections", "connections", "maxHeadersCount",
|
|
3308
|
-
"timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout", NULL };
|
|
3412
|
+
static const char *const known[] = { "maxConnections", "connections", "maxHeadersCount", NULL };
|
|
3309
3413
|
for (size_t i = 0; known[i]; i++) {
|
|
3310
3414
|
if (strcmp(key, known[i]) == 0) {
|
|
3311
3415
|
scr_net_dynh_srv_unsupported(key, NULL);
|
|
@@ -3317,7 +3421,18 @@ static ScrDyn *scr_net_dynh_srv_get(void *h, const char *key, size_t key_len) {
|
|
|
3317
3421
|
}
|
|
3318
3422
|
|
|
3319
3423
|
static bool scr_net_dynh_srv_set(void *h, const char *key, size_t key_len, const ScrDyn *value) {
|
|
3320
|
-
|
|
3424
|
+
ScrNetServer *s = (ScrNetServer *)h;
|
|
3425
|
+
(void)key_len;
|
|
3426
|
+
int timeout_field = scr_net_dynh_srv_timeout_field(key);
|
|
3427
|
+
if (timeout_field >= 0 && s->http_timeout_surface) {
|
|
3428
|
+
if (value->kind == SCR_DYN_NUM) {
|
|
3429
|
+
scr_net_server_timeout_set(s, (double)timeout_field, value->v.num);
|
|
3430
|
+
} else {
|
|
3431
|
+
scr_dyn_release(s->http_timeout_dyn[timeout_field]);
|
|
3432
|
+
s->http_timeout_dyn[timeout_field] = scr_dyn_retain((ScrDyn *)value);
|
|
3433
|
+
}
|
|
3434
|
+
return true;
|
|
3435
|
+
}
|
|
3321
3436
|
return false;
|
|
3322
3437
|
}
|
|
3323
3438
|
|