@secure-exec/core 0.2.0-rc.1 → 0.2.0-rc.2
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/dist/generated/isolate-runtime.d.ts +2 -2
- package/dist/generated/isolate-runtime.js +2 -2
- package/dist/isolate-runtime/require-setup.js +1351 -239
- package/dist/isolate-runtime/setup-dynamic-import.js +31 -0
- package/dist/kernel/file-lock.js +2 -3
- package/dist/kernel/kernel.js +7 -10
- package/dist/kernel/socket-table.d.ts +7 -0
- package/dist/kernel/socket-table.js +99 -35
- package/dist/shared/bridge-contract.d.ts +21 -3
- package/dist/shared/bridge-contract.js +2 -0
- package/dist/shared/global-exposure.js +95 -0
- package/dist/shared/in-memory-fs.d.ts +3 -0
- package/dist/shared/in-memory-fs.js +74 -39
- package/package.json +1 -1
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
var __dynamicImportConfig = globalThis.__runtimeDynamicImportConfig ?? {};
|
|
32
32
|
var __fallbackReferrer = typeof __dynamicImportConfig.referrerPath === "string" && __dynamicImportConfig.referrerPath.length > 0 ? __dynamicImportConfig.referrerPath : "/";
|
|
33
33
|
var __dynamicImportCache = /* @__PURE__ */ new Map();
|
|
34
|
+
var __pathToFileURL = typeof globalThis.require === "function" ? globalThis.require("node:url").pathToFileURL ?? null : null;
|
|
34
35
|
var __resolveDynamicImportPath = function(request, referrer) {
|
|
35
36
|
if (!request.startsWith("./") && !request.startsWith("../") && !request.startsWith("/")) {
|
|
36
37
|
return request;
|
|
@@ -88,5 +89,35 @@
|
|
|
88
89
|
__dynamicImportCache.set(cacheKey, namespaceFallback);
|
|
89
90
|
return Promise.resolve(namespaceFallback);
|
|
90
91
|
};
|
|
92
|
+
var __importMetaResolveHandler = function(specifier, fromPath) {
|
|
93
|
+
const request = String(specifier);
|
|
94
|
+
const referrer = typeof fromPath === "string" && fromPath.length > 0 ? fromPath : __fallbackReferrer;
|
|
95
|
+
let resolved = null;
|
|
96
|
+
if (typeof globalThis._resolveModuleSync !== "undefined") {
|
|
97
|
+
resolved = globalThis._resolveModuleSync.applySync(
|
|
98
|
+
void 0,
|
|
99
|
+
[request, referrer, "import"]
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (resolved === null || resolved === void 0) {
|
|
103
|
+
resolved = globalThis._resolveModule.applySyncPromise(
|
|
104
|
+
void 0,
|
|
105
|
+
[request, referrer, "import"]
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (resolved === null) {
|
|
109
|
+
const err = new Error("Cannot find module '" + request + "'");
|
|
110
|
+
err.code = "MODULE_NOT_FOUND";
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
if (resolved.startsWith("node:")) {
|
|
114
|
+
return resolved;
|
|
115
|
+
}
|
|
116
|
+
if (__pathToFileURL && resolved.startsWith("/")) {
|
|
117
|
+
return __pathToFileURL(resolved).href;
|
|
118
|
+
}
|
|
119
|
+
return resolved;
|
|
120
|
+
};
|
|
91
121
|
__runtimeExposeCustomGlobal("__dynamicImport", __dynamicImportHandler);
|
|
122
|
+
__runtimeExposeCustomGlobal("__importMetaResolve", __importMetaResolveHandler);
|
|
92
123
|
})();
|
package/dist/kernel/file-lock.js
CHANGED
|
@@ -12,7 +12,6 @@ export const LOCK_SH = 1;
|
|
|
12
12
|
export const LOCK_EX = 2;
|
|
13
13
|
export const LOCK_UN = 8;
|
|
14
14
|
export const LOCK_NB = 4;
|
|
15
|
-
const FLOCK_WAIT_TIMEOUT_MS = 30_000;
|
|
16
15
|
export class FileLockManager {
|
|
17
16
|
/** path -> lock state */
|
|
18
17
|
locks = new Map();
|
|
@@ -40,8 +39,8 @@ export class FileLockManager {
|
|
|
40
39
|
if (nonBlocking) {
|
|
41
40
|
throw new KernelError("EAGAIN", "resource temporarily unavailable");
|
|
42
41
|
}
|
|
43
|
-
//
|
|
44
|
-
const handle = state.waiters.enqueue(
|
|
42
|
+
// Wait indefinitely until an unlock wakes this waiter.
|
|
43
|
+
const handle = state.waiters.enqueue();
|
|
45
44
|
try {
|
|
46
45
|
await handle.wait();
|
|
47
46
|
}
|
package/dist/kernel/kernel.js
CHANGED
|
@@ -80,8 +80,10 @@ class KernelImpl {
|
|
|
80
80
|
this.userManager = new UserManager();
|
|
81
81
|
this.socketTable = new SocketTable({
|
|
82
82
|
vfs: this.vfs,
|
|
83
|
+
networkCheck: options.permissions?.network,
|
|
83
84
|
hostAdapter: options.hostNetworkAdapter,
|
|
84
85
|
getSignalState: (pid) => this.processTable.getSignalState(pid),
|
|
86
|
+
processExists: (pid) => this.processTable.get(pid) !== undefined,
|
|
85
87
|
});
|
|
86
88
|
this.timerTable = new TimerTable();
|
|
87
89
|
// Clean up FD table and sockets when a process exits
|
|
@@ -387,16 +389,11 @@ class KernelImpl {
|
|
|
387
389
|
const outputHandler = options?.onData
|
|
388
390
|
?? ((data) => { stdout.write(data); });
|
|
389
391
|
shell.onData = outputHandler;
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
stdout.on("resize", onResize);
|
|
396
|
-
// Set initial terminal size
|
|
397
|
-
if (stdout.isTTY) {
|
|
398
|
-
shell.resize(stdout.columns || 80, stdout.rows || 24);
|
|
399
|
-
}
|
|
392
|
+
// PTY resize forwarding is currently unsafe for Wasm shell sessions:
|
|
393
|
+
// an early resize can terminate the shell before the first prompt.
|
|
394
|
+
// Keep interactive stdin/stdout working and leave resize disabled
|
|
395
|
+
// until the PTY/SIGWINCH path is fixed end-to-end.
|
|
396
|
+
onResize = undefined;
|
|
400
397
|
return await shell.wait();
|
|
401
398
|
}
|
|
402
399
|
finally {
|
|
@@ -93,6 +93,7 @@ export declare class SocketTable {
|
|
|
93
93
|
private readonly hostAdapter?;
|
|
94
94
|
private readonly vfs?;
|
|
95
95
|
private readonly getSignalState?;
|
|
96
|
+
private readonly processExists?;
|
|
96
97
|
/** Bound/listening address → socket ID. Used for EADDRINUSE and TCP routing. */
|
|
97
98
|
private listeners;
|
|
98
99
|
/** Bound UDP address → socket ID. Separate from TCP listeners. */
|
|
@@ -103,7 +104,9 @@ export declare class SocketTable {
|
|
|
103
104
|
hostAdapter?: HostNetworkAdapter;
|
|
104
105
|
vfs?: VirtualFileSystem;
|
|
105
106
|
getSignalState?: (pid: number) => ProcessSignalState;
|
|
107
|
+
processExists?: (pid: number) => boolean;
|
|
106
108
|
});
|
|
109
|
+
hasHostNetworkAdapter(): boolean;
|
|
107
110
|
/**
|
|
108
111
|
* Create a new socket owned by the given process.
|
|
109
112
|
* Returns the kernel socket ID.
|
|
@@ -279,6 +282,10 @@ export declare class SocketTable {
|
|
|
279
282
|
private startAcceptPump;
|
|
280
283
|
/** Look up a listening socket by exact address key. */
|
|
281
284
|
private getListeningSocket;
|
|
285
|
+
/** Replay stored socket options onto a host-backed connection. */
|
|
286
|
+
private applySocketOptionsToHostSocket;
|
|
287
|
+
/** Best-effort option forwarding for host-backed sockets. */
|
|
288
|
+
private applySocketOptionToHostSocket;
|
|
282
289
|
/** Peek up to maxBytes from a socket's readBuffer without consuming. */
|
|
283
290
|
private peekFromBuffer;
|
|
284
291
|
/** Consume up to maxBytes from a socket's readBuffer. */
|
|
@@ -68,6 +68,7 @@ export class SocketTable {
|
|
|
68
68
|
hostAdapter;
|
|
69
69
|
vfs;
|
|
70
70
|
getSignalState;
|
|
71
|
+
processExists;
|
|
71
72
|
/** Bound/listening address → socket ID. Used for EADDRINUSE and TCP routing. */
|
|
72
73
|
listeners = new Map();
|
|
73
74
|
/** Bound UDP address → socket ID. Separate from TCP listeners. */
|
|
@@ -78,6 +79,10 @@ export class SocketTable {
|
|
|
78
79
|
this.hostAdapter = options?.hostAdapter;
|
|
79
80
|
this.vfs = options?.vfs;
|
|
80
81
|
this.getSignalState = options?.getSignalState;
|
|
82
|
+
this.processExists = options?.processExists;
|
|
83
|
+
}
|
|
84
|
+
hasHostNetworkAdapter() {
|
|
85
|
+
return this.hostAdapter !== undefined;
|
|
81
86
|
}
|
|
82
87
|
/**
|
|
83
88
|
* Create a new socket owned by the given process.
|
|
@@ -87,6 +92,9 @@ export class SocketTable {
|
|
|
87
92
|
if (this.sockets.size >= this.maxSockets) {
|
|
88
93
|
throw new KernelError("EMFILE", "too many open sockets");
|
|
89
94
|
}
|
|
95
|
+
if (this.processExists && !this.processExists(pid)) {
|
|
96
|
+
throw new KernelError("ESRCH", `cannot create socket for unknown pid ${pid}`);
|
|
97
|
+
}
|
|
90
98
|
const id = this.nextSocketId++;
|
|
91
99
|
const socket = {
|
|
92
100
|
id,
|
|
@@ -196,19 +204,26 @@ export class SocketTable {
|
|
|
196
204
|
throw new KernelError("EINVAL", "socket must be bound before listen");
|
|
197
205
|
}
|
|
198
206
|
socket.backlogLimit = Math.max(0, backlogSize);
|
|
199
|
-
//
|
|
200
|
-
|
|
207
|
+
// AF_UNIX listeners stay entirely in-kernel, so host-network policy
|
|
208
|
+
// only applies to inet listeners.
|
|
209
|
+
if (socket.localAddr && isInetAddr(socket.localAddr)) {
|
|
201
210
|
this.checkNetworkPermission("listen", socket.localAddr);
|
|
202
211
|
}
|
|
203
212
|
// External listen — delegate to host adapter
|
|
204
|
-
if (options?.external &&
|
|
213
|
+
if (options?.external &&
|
|
214
|
+
this.hostAdapter &&
|
|
215
|
+
socket.localAddr &&
|
|
216
|
+
isInetAddr(socket.localAddr)) {
|
|
205
217
|
const hostListener = await this.hostAdapter.tcpListen(socket.localAddr.host, socket.requestedEphemeralPort ? 0 : socket.localAddr.port);
|
|
206
218
|
socket.hostListener = hostListener;
|
|
207
219
|
socket.external = true;
|
|
208
220
|
// Update port for ephemeral (port 0) bindings
|
|
209
221
|
if (socket.requestedEphemeralPort || socket.localAddr.port === 0) {
|
|
210
222
|
const oldKey = addrKey(socket.localAddr);
|
|
211
|
-
socket.localAddr = {
|
|
223
|
+
socket.localAddr = {
|
|
224
|
+
host: socket.localAddr.host,
|
|
225
|
+
port: hostListener.port,
|
|
226
|
+
};
|
|
212
227
|
// Re-register in listeners map with actual port
|
|
213
228
|
this.listeners.delete(oldKey);
|
|
214
229
|
this.listeners.set(addrKey(socket.localAddr), socketId);
|
|
@@ -266,7 +281,9 @@ export class SocketTable {
|
|
|
266
281
|
*/
|
|
267
282
|
shutdown(socketId, how) {
|
|
268
283
|
const socket = this.requireSocket(socketId);
|
|
269
|
-
if (socket.state !== "connected" &&
|
|
284
|
+
if (socket.state !== "connected" &&
|
|
285
|
+
socket.state !== "write-closed" &&
|
|
286
|
+
socket.state !== "read-closed") {
|
|
270
287
|
throw new KernelError("ENOTCONN", "socket is not connected");
|
|
271
288
|
}
|
|
272
289
|
// Propagate half-close/full-close semantics to real host sockets so
|
|
@@ -341,6 +358,7 @@ export class SocketTable {
|
|
|
341
358
|
setsockopt(socketId, level, optname, optval) {
|
|
342
359
|
const socket = this.requireSocket(socketId);
|
|
343
360
|
socket.options.set(optKey(level, optname), optval);
|
|
361
|
+
this.applySocketOptionToHostSocket(socket, level, optname, optval);
|
|
344
362
|
}
|
|
345
363
|
/** Toggle non-blocking behavior for an existing socket. */
|
|
346
364
|
setNonBlocking(socketId, nonBlocking) {
|
|
@@ -395,16 +413,17 @@ export class SocketTable {
|
|
|
395
413
|
}
|
|
396
414
|
// Unix domain sockets: check VFS for socket file existence
|
|
397
415
|
if (isUnixAddr(addr) && this.vfs) {
|
|
398
|
-
if (!await this.vfs.exists(addr.path)) {
|
|
416
|
+
if (!(await this.vfs.exists(addr.path))) {
|
|
399
417
|
throw new KernelError("ECONNREFUSED", `connection refused: ${addr.path}`);
|
|
400
418
|
}
|
|
401
419
|
}
|
|
402
420
|
const listener = this.findListener(addr);
|
|
403
421
|
if (!listener) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
this.checkNetworkPermission("connect", addr);
|
|
422
|
+
if (isUnixAddr(addr)) {
|
|
423
|
+
throw new KernelError("ECONNREFUSED", `connection refused: ${addr.path}`);
|
|
407
424
|
}
|
|
425
|
+
// Check external connections through the deny-by-default network policy.
|
|
426
|
+
this.checkNetworkPermission("connect", addr);
|
|
408
427
|
// Route through host adapter if available
|
|
409
428
|
if (this.hostAdapter && isInetAddr(addr)) {
|
|
410
429
|
if (socket.nonBlocking) {
|
|
@@ -418,6 +437,7 @@ export class SocketTable {
|
|
|
418
437
|
socket.external = true;
|
|
419
438
|
socket.remoteAddr = addr;
|
|
420
439
|
socket.hostSocket = hostSocket;
|
|
440
|
+
this.applySocketOptionsToHostSocket(socket);
|
|
421
441
|
this.startReadPump(socket);
|
|
422
442
|
return;
|
|
423
443
|
}
|
|
@@ -467,8 +487,9 @@ export class SocketTable {
|
|
|
467
487
|
if (socket.state !== "connected" && socket.state !== "read-closed") {
|
|
468
488
|
throw new KernelError("ENOTCONN", "socket is not connected");
|
|
469
489
|
}
|
|
470
|
-
//
|
|
471
|
-
|
|
490
|
+
// Re-check outbound external writes so pre-existing host sockets still
|
|
491
|
+
// honor deny-by-default network policy.
|
|
492
|
+
if (socket.external) {
|
|
472
493
|
this.checkNetworkPermission("connect", socket.remoteAddr);
|
|
473
494
|
}
|
|
474
495
|
// External socket: write to host socket
|
|
@@ -480,16 +501,12 @@ export class SocketTable {
|
|
|
480
501
|
return data.length;
|
|
481
502
|
}
|
|
482
503
|
if (socket.peerId === undefined) {
|
|
483
|
-
throw new KernelError("EPIPE", nosignal
|
|
484
|
-
? "broken pipe (MSG_NOSIGNAL)"
|
|
485
|
-
: "broken pipe: peer closed");
|
|
504
|
+
throw new KernelError("EPIPE", nosignal ? "broken pipe (MSG_NOSIGNAL)" : "broken pipe: peer closed");
|
|
486
505
|
}
|
|
487
506
|
const peer = this.sockets.get(socket.peerId);
|
|
488
507
|
if (!peer) {
|
|
489
508
|
socket.peerId = undefined;
|
|
490
|
-
throw new KernelError("EPIPE", nosignal
|
|
491
|
-
? "broken pipe (MSG_NOSIGNAL)"
|
|
492
|
-
: "broken pipe: peer closed");
|
|
509
|
+
throw new KernelError("EPIPE", nosignal ? "broken pipe (MSG_NOSIGNAL)" : "broken pipe: peer closed");
|
|
493
510
|
}
|
|
494
511
|
// Enforce SO_RCVBUF on the peer's receive buffer
|
|
495
512
|
const rcvBuf = peer.options.get(optKey(SOL_SOCKET, SO_RCVBUF));
|
|
@@ -524,7 +541,9 @@ export class SocketTable {
|
|
|
524
541
|
return this.consumeFromBuffer(socket, maxBytes);
|
|
525
542
|
}
|
|
526
543
|
// Buffer empty — check for EOF (peer gone or peer shut down write)
|
|
527
|
-
if (socket.peerId === undefined ||
|
|
544
|
+
if (socket.peerId === undefined ||
|
|
545
|
+
!this.sockets.has(socket.peerId) ||
|
|
546
|
+
socket.peerWriteClosed) {
|
|
528
547
|
return null;
|
|
529
548
|
}
|
|
530
549
|
// No data available
|
|
@@ -570,10 +589,10 @@ export class SocketTable {
|
|
|
570
589
|
}
|
|
571
590
|
// External routing via host adapter
|
|
572
591
|
if (socket.hostUdpSocket && this.hostAdapter && isInetAddr(destAddr)) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
592
|
+
this.checkNetworkPermission("connect", destAddr);
|
|
593
|
+
this.hostAdapter
|
|
594
|
+
.udpSend(socket.hostUdpSocket, new Uint8Array(data), destAddr.host, destAddr.port)
|
|
595
|
+
.catch(() => { });
|
|
577
596
|
return data.length;
|
|
578
597
|
}
|
|
579
598
|
// No loopback target, no host adapter — silently drop (UDP semantics)
|
|
@@ -647,12 +666,12 @@ export class SocketTable {
|
|
|
647
666
|
if (socket.state !== "bound") {
|
|
648
667
|
throw new KernelError("EINVAL", "socket must be bound before external UDP bind");
|
|
649
668
|
}
|
|
650
|
-
if (!this.hostAdapter ||
|
|
669
|
+
if (!this.hostAdapter ||
|
|
670
|
+
!socket.localAddr ||
|
|
671
|
+
!isInetAddr(socket.localAddr)) {
|
|
651
672
|
throw new KernelError("EINVAL", "host adapter and inet address required");
|
|
652
673
|
}
|
|
653
|
-
|
|
654
|
-
this.checkNetworkPermission("listen", socket.localAddr);
|
|
655
|
-
}
|
|
674
|
+
this.checkNetworkPermission("listen", socket.localAddr);
|
|
656
675
|
const hostUdpSocket = await this.hostAdapter.udpBind(socket.localAddr.host, socket.localAddr.port);
|
|
657
676
|
socket.hostUdpSocket = hostUdpSocket;
|
|
658
677
|
socket.external = true;
|
|
@@ -680,10 +699,11 @@ export class SocketTable {
|
|
|
680
699
|
const closed = socket.state === "closed";
|
|
681
700
|
const readClosed = socket.state === "read-closed";
|
|
682
701
|
const writeClosed = socket.state === "write-closed";
|
|
702
|
+
const pendingAccept = socket.state === "listening" && socket.backlog.length > 0;
|
|
683
703
|
// UDP: readable when datagramQueue has data
|
|
684
704
|
const readable = socket.type === SOCK_DGRAM
|
|
685
705
|
? socket.datagramQueue.length > 0 || closed
|
|
686
|
-
: socket.readBuffer.length > 0 || closed || readClosed;
|
|
706
|
+
: socket.readBuffer.length > 0 || pendingAccept || closed || readClosed;
|
|
687
707
|
const writable = socket.state === "connected" ||
|
|
688
708
|
socket.state === "created" ||
|
|
689
709
|
socket.state === "read-closed" ||
|
|
@@ -756,6 +776,15 @@ export class SocketTable {
|
|
|
756
776
|
}
|
|
757
777
|
}
|
|
758
778
|
destroySocket(socket) {
|
|
779
|
+
// Tear down queued-but-unaccepted connections with the listener so they
|
|
780
|
+
// cannot leak detached server-side sockets after the listening endpoint closes.
|
|
781
|
+
for (const pendingId of [...socket.backlog]) {
|
|
782
|
+
const pending = this.sockets.get(pendingId);
|
|
783
|
+
if (pending) {
|
|
784
|
+
this.destroySocket(pending);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
socket.backlog.length = 0;
|
|
759
788
|
// Propagate EOF to peer: clear peer link and wake readers
|
|
760
789
|
if (socket.peerId !== undefined) {
|
|
761
790
|
const peer = this.sockets.get(socket.peerId);
|
|
@@ -829,7 +858,9 @@ export class SocketTable {
|
|
|
829
858
|
startExternalConnect(socket, addr) {
|
|
830
859
|
if (!this.hostAdapter)
|
|
831
860
|
return;
|
|
832
|
-
this.hostAdapter
|
|
861
|
+
this.hostAdapter
|
|
862
|
+
.tcpConnect(addr.host, addr.port)
|
|
863
|
+
.then((hostSocket) => {
|
|
833
864
|
const current = this.sockets.get(socket.id);
|
|
834
865
|
if (!current || current !== socket || current.state === "closed") {
|
|
835
866
|
hostSocket.close().catch(() => { });
|
|
@@ -839,8 +870,10 @@ export class SocketTable {
|
|
|
839
870
|
current.external = true;
|
|
840
871
|
current.remoteAddr = addr;
|
|
841
872
|
current.hostSocket = hostSocket;
|
|
873
|
+
this.applySocketOptionsToHostSocket(current);
|
|
842
874
|
this.startReadPump(current);
|
|
843
|
-
})
|
|
875
|
+
})
|
|
876
|
+
.catch(() => {
|
|
844
877
|
const current = this.sockets.get(socket.id);
|
|
845
878
|
if (!current || current !== socket || current.state === "closed") {
|
|
846
879
|
return;
|
|
@@ -859,7 +892,8 @@ export class SocketTable {
|
|
|
859
892
|
const hostListener = socket.hostListener;
|
|
860
893
|
const pump = async () => {
|
|
861
894
|
try {
|
|
862
|
-
while (socket.state === "listening" &&
|
|
895
|
+
while (socket.state === "listening" &&
|
|
896
|
+
socket.hostListener === hostListener) {
|
|
863
897
|
const hostSocket = await hostListener.accept();
|
|
864
898
|
if (socket.backlog.length >= socket.backlogLimit) {
|
|
865
899
|
hostSocket.close().catch(() => { });
|
|
@@ -872,6 +906,7 @@ export class SocketTable {
|
|
|
872
906
|
connSock.external = true;
|
|
873
907
|
connSock.hostSocket = hostSocket;
|
|
874
908
|
connSock.localAddr = socket.localAddr;
|
|
909
|
+
this.applySocketOptionsToHostSocket(connSock);
|
|
875
910
|
// Start read pump for the accepted socket
|
|
876
911
|
this.startReadPump(connSock);
|
|
877
912
|
// Queue in listener's backlog
|
|
@@ -895,6 +930,27 @@ export class SocketTable {
|
|
|
895
930
|
return null;
|
|
896
931
|
return sock;
|
|
897
932
|
}
|
|
933
|
+
/** Replay stored socket options onto a host-backed connection. */
|
|
934
|
+
applySocketOptionsToHostSocket(socket) {
|
|
935
|
+
for (const [key, value] of socket.options.entries()) {
|
|
936
|
+
const [level, optname] = key.split(":").map(Number);
|
|
937
|
+
if (Number.isNaN(level) || Number.isNaN(optname))
|
|
938
|
+
continue;
|
|
939
|
+
this.applySocketOptionToHostSocket(socket, level, optname, value);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
/** Best-effort option forwarding for host-backed sockets. */
|
|
943
|
+
applySocketOptionToHostSocket(socket, level, optname, optval) {
|
|
944
|
+
if (!socket.external || !socket.hostSocket) {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
socket.hostSocket.setOption(level, optname, optval);
|
|
949
|
+
}
|
|
950
|
+
catch {
|
|
951
|
+
// Host adapters may not support every kernel-tracked option.
|
|
952
|
+
}
|
|
953
|
+
}
|
|
898
954
|
/** Peek up to maxBytes from a socket's readBuffer without consuming. */
|
|
899
955
|
peekFromBuffer(socket, maxBytes) {
|
|
900
956
|
const chunks = [];
|
|
@@ -972,7 +1028,9 @@ export class SocketTable {
|
|
|
972
1028
|
if (socket.external) {
|
|
973
1029
|
return !socket.peerWriteClosed;
|
|
974
1030
|
}
|
|
975
|
-
return socket.peerId !== undefined &&
|
|
1031
|
+
return (socket.peerId !== undefined &&
|
|
1032
|
+
this.sockets.has(socket.peerId) &&
|
|
1033
|
+
!socket.peerWriteClosed);
|
|
976
1034
|
}
|
|
977
1035
|
/** Wait for socket readiness or an interrupting signal. */
|
|
978
1036
|
async waitForSocketWake(waiters, pid, op) {
|
|
@@ -1048,7 +1106,8 @@ export class SocketTable {
|
|
|
1048
1106
|
continue;
|
|
1049
1107
|
if (existing.localAddr.port !== addr.port)
|
|
1050
1108
|
continue;
|
|
1051
|
-
const existingIsWildcard = existing.localAddr.host === "0.0.0.0" ||
|
|
1109
|
+
const existingIsWildcard = existing.localAddr.host === "0.0.0.0" ||
|
|
1110
|
+
existing.localAddr.host === "::";
|
|
1052
1111
|
if (isWildcard || existingIsWildcard)
|
|
1053
1112
|
return true;
|
|
1054
1113
|
}
|
|
@@ -1061,12 +1120,16 @@ export class SocketTable {
|
|
|
1061
1120
|
const hostUdpSocket = socket.hostUdpSocket;
|
|
1062
1121
|
const pump = async () => {
|
|
1063
1122
|
try {
|
|
1064
|
-
while (socket.state !== "closed" &&
|
|
1123
|
+
while (socket.state !== "closed" &&
|
|
1124
|
+
socket.hostUdpSocket === hostUdpSocket) {
|
|
1065
1125
|
const result = await hostUdpSocket.recv();
|
|
1066
1126
|
if (socket.datagramQueue.length < MAX_UDP_QUEUE_DEPTH) {
|
|
1067
1127
|
socket.datagramQueue.push({
|
|
1068
1128
|
data: result.data,
|
|
1069
|
-
srcAddr: {
|
|
1129
|
+
srcAddr: {
|
|
1130
|
+
host: result.remoteAddr.host,
|
|
1131
|
+
port: result.remoteAddr.port,
|
|
1132
|
+
},
|
|
1070
1133
|
});
|
|
1071
1134
|
socket.readWaiters.wakeOne();
|
|
1072
1135
|
}
|
|
@@ -1097,7 +1160,8 @@ export class SocketTable {
|
|
|
1097
1160
|
continue;
|
|
1098
1161
|
if (existing.localAddr.port !== addr.port)
|
|
1099
1162
|
continue;
|
|
1100
|
-
const existingIsWildcard = existing.localAddr.host === "0.0.0.0" ||
|
|
1163
|
+
const existingIsWildcard = existing.localAddr.host === "0.0.0.0" ||
|
|
1164
|
+
existing.localAddr.host === "::";
|
|
1101
1165
|
if (isWildcard || existingIsWildcard)
|
|
1102
1166
|
return true;
|
|
1103
1167
|
}
|
|
@@ -91,6 +91,7 @@ export declare const HOST_BRIDGE_GLOBAL_KEYS: {
|
|
|
91
91
|
readonly networkHttp2StreamPushStreamRaw: "_networkHttp2StreamPushStreamRaw";
|
|
92
92
|
readonly networkHttp2StreamWriteRaw: "_networkHttp2StreamWriteRaw";
|
|
93
93
|
readonly networkHttp2StreamEndRaw: "_networkHttp2StreamEndRaw";
|
|
94
|
+
readonly networkHttp2StreamCloseRaw: "_networkHttp2StreamCloseRaw";
|
|
94
95
|
readonly networkHttp2StreamPauseRaw: "_networkHttp2StreamPauseRaw";
|
|
95
96
|
readonly networkHttp2StreamResumeRaw: "_networkHttp2StreamResumeRaw";
|
|
96
97
|
readonly networkHttp2StreamRespondWithFileRaw: "_networkHttp2StreamRespondWithFileRaw";
|
|
@@ -124,6 +125,7 @@ export declare const HOST_BRIDGE_GLOBAL_KEYS: {
|
|
|
124
125
|
readonly resolveModuleSync: "_resolveModuleSync";
|
|
125
126
|
readonly loadFileSync: "_loadFileSync";
|
|
126
127
|
readonly ptySetRawMode: "_ptySetRawMode";
|
|
128
|
+
readonly kernelStdinRead: "_kernelStdinRead";
|
|
127
129
|
readonly processConfig: "_processConfig";
|
|
128
130
|
readonly osConfig: "_osConfig";
|
|
129
131
|
readonly log: "_log";
|
|
@@ -237,6 +239,7 @@ export declare const HOST_BRIDGE_GLOBAL_KEY_LIST: ValueOf<{
|
|
|
237
239
|
readonly networkHttp2StreamPushStreamRaw: "_networkHttp2StreamPushStreamRaw";
|
|
238
240
|
readonly networkHttp2StreamWriteRaw: "_networkHttp2StreamWriteRaw";
|
|
239
241
|
readonly networkHttp2StreamEndRaw: "_networkHttp2StreamEndRaw";
|
|
242
|
+
readonly networkHttp2StreamCloseRaw: "_networkHttp2StreamCloseRaw";
|
|
240
243
|
readonly networkHttp2StreamPauseRaw: "_networkHttp2StreamPauseRaw";
|
|
241
244
|
readonly networkHttp2StreamResumeRaw: "_networkHttp2StreamResumeRaw";
|
|
242
245
|
readonly networkHttp2StreamRespondWithFileRaw: "_networkHttp2StreamRespondWithFileRaw";
|
|
@@ -270,6 +273,7 @@ export declare const HOST_BRIDGE_GLOBAL_KEY_LIST: ValueOf<{
|
|
|
270
273
|
readonly resolveModuleSync: "_resolveModuleSync";
|
|
271
274
|
readonly loadFileSync: "_loadFileSync";
|
|
272
275
|
readonly ptySetRawMode: "_ptySetRawMode";
|
|
276
|
+
readonly kernelStdinRead: "_kernelStdinRead";
|
|
273
277
|
readonly processConfig: "_processConfig";
|
|
274
278
|
readonly osConfig: "_osConfig";
|
|
275
279
|
readonly log: "_log";
|
|
@@ -379,6 +383,7 @@ export declare const BRIDGE_GLOBAL_KEY_LIST: readonly (ValueOf<{
|
|
|
379
383
|
readonly networkHttp2StreamPushStreamRaw: "_networkHttp2StreamPushStreamRaw";
|
|
380
384
|
readonly networkHttp2StreamWriteRaw: "_networkHttp2StreamWriteRaw";
|
|
381
385
|
readonly networkHttp2StreamEndRaw: "_networkHttp2StreamEndRaw";
|
|
386
|
+
readonly networkHttp2StreamCloseRaw: "_networkHttp2StreamCloseRaw";
|
|
382
387
|
readonly networkHttp2StreamPauseRaw: "_networkHttp2StreamPauseRaw";
|
|
383
388
|
readonly networkHttp2StreamResumeRaw: "_networkHttp2StreamResumeRaw";
|
|
384
389
|
readonly networkHttp2StreamRespondWithFileRaw: "_networkHttp2StreamRespondWithFileRaw";
|
|
@@ -412,6 +417,7 @@ export declare const BRIDGE_GLOBAL_KEY_LIST: readonly (ValueOf<{
|
|
|
412
417
|
readonly resolveModuleSync: "_resolveModuleSync";
|
|
413
418
|
readonly loadFileSync: "_loadFileSync";
|
|
414
419
|
readonly ptySetRawMode: "_ptySetRawMode";
|
|
420
|
+
readonly kernelStdinRead: "_kernelStdinRead";
|
|
415
421
|
readonly processConfig: "_processConfig";
|
|
416
422
|
readonly osConfig: "_osConfig";
|
|
417
423
|
readonly log: "_log";
|
|
@@ -463,6 +469,7 @@ export interface BridgeApplySyncRef<TArgs extends unknown[], TResult> {
|
|
|
463
469
|
export interface BridgeApplySyncPromiseRef<TArgs extends unknown[], TResult> {
|
|
464
470
|
applySyncPromise(ctx: undefined, args: TArgs): TResult;
|
|
465
471
|
}
|
|
472
|
+
export type ModuleLoadMode = "require" | "import";
|
|
466
473
|
export type DynamicImportBridgeRef = BridgeApplyRef<[
|
|
467
474
|
string,
|
|
468
475
|
string
|
|
@@ -471,13 +478,20 @@ export type LoadPolyfillBridgeRef = BridgeApplyRef<[string], string | null>;
|
|
|
471
478
|
export type ResolveModuleBridgeRef = BridgeApplySyncPromiseRef<[
|
|
472
479
|
string,
|
|
473
480
|
string
|
|
474
|
-
], string | null>;
|
|
475
|
-
export type LoadFileBridgeRef = BridgeApplySyncPromiseRef<[
|
|
481
|
+
] | [string, string, ModuleLoadMode], string | null>;
|
|
482
|
+
export type LoadFileBridgeRef = BridgeApplySyncPromiseRef<[
|
|
483
|
+
string
|
|
484
|
+
] | [string, ModuleLoadMode], string | null>;
|
|
476
485
|
export type RequireFromBridgeFn = (request: string, dirname: string) => unknown;
|
|
477
486
|
export type ModuleCacheBridgeRecord = Record<string, unknown>;
|
|
478
487
|
export type ProcessLogBridgeRef = BridgeApplySyncRef<[string], void>;
|
|
479
488
|
export type ProcessErrorBridgeRef = BridgeApplySyncRef<[string], void>;
|
|
480
489
|
export type ScheduleTimerBridgeRef = BridgeApplyRef<[number], void>;
|
|
490
|
+
export type KernelStdinReadBridgeRef = BridgeApplyRef<[
|
|
491
|
+
], {
|
|
492
|
+
done: boolean;
|
|
493
|
+
dataBase64?: string;
|
|
494
|
+
}>;
|
|
481
495
|
export type CryptoRandomFillBridgeRef = BridgeApplySyncRef<[number], string>;
|
|
482
496
|
export type CryptoRandomUuidBridgeRef = BridgeApplySyncRef<[], string>;
|
|
483
497
|
export type CryptoHashDigestBridgeRef = BridgeApplySyncRef<[string, string], string>;
|
|
@@ -678,7 +692,7 @@ export type NetworkHttp2StreamRespondRawBridgeRef = BridgeApplySyncRef<[
|
|
|
678
692
|
number,
|
|
679
693
|
string
|
|
680
694
|
], void>;
|
|
681
|
-
export type NetworkHttp2StreamPushStreamRawBridgeRef =
|
|
695
|
+
export type NetworkHttp2StreamPushStreamRawBridgeRef = BridgeApplySyncRef<[
|
|
682
696
|
number,
|
|
683
697
|
string,
|
|
684
698
|
string
|
|
@@ -691,6 +705,10 @@ export type NetworkHttp2StreamEndRawBridgeRef = BridgeApplySyncRef<[
|
|
|
691
705
|
number,
|
|
692
706
|
string | null
|
|
693
707
|
], void>;
|
|
708
|
+
export type NetworkHttp2StreamCloseRawBridgeRef = BridgeApplySyncRef<[
|
|
709
|
+
number,
|
|
710
|
+
number | null
|
|
711
|
+
], void>;
|
|
694
712
|
export type NetworkHttp2StreamPauseRawBridgeRef = BridgeApplySyncRef<[number], void>;
|
|
695
713
|
export type NetworkHttp2StreamResumeRawBridgeRef = BridgeApplySyncRef<[number], void>;
|
|
696
714
|
export type NetworkHttp2StreamRespondWithFileRawBridgeRef = BridgeApplySyncRef<[
|
|
@@ -93,6 +93,7 @@ export const HOST_BRIDGE_GLOBAL_KEYS = {
|
|
|
93
93
|
networkHttp2StreamPushStreamRaw: "_networkHttp2StreamPushStreamRaw",
|
|
94
94
|
networkHttp2StreamWriteRaw: "_networkHttp2StreamWriteRaw",
|
|
95
95
|
networkHttp2StreamEndRaw: "_networkHttp2StreamEndRaw",
|
|
96
|
+
networkHttp2StreamCloseRaw: "_networkHttp2StreamCloseRaw",
|
|
96
97
|
networkHttp2StreamPauseRaw: "_networkHttp2StreamPauseRaw",
|
|
97
98
|
networkHttp2StreamResumeRaw: "_networkHttp2StreamResumeRaw",
|
|
98
99
|
networkHttp2StreamRespondWithFileRaw: "_networkHttp2StreamRespondWithFileRaw",
|
|
@@ -126,6 +127,7 @@ export const HOST_BRIDGE_GLOBAL_KEYS = {
|
|
|
126
127
|
resolveModuleSync: "_resolveModuleSync",
|
|
127
128
|
loadFileSync: "_loadFileSync",
|
|
128
129
|
ptySetRawMode: "_ptySetRawMode",
|
|
130
|
+
kernelStdinRead: "_kernelStdinRead",
|
|
129
131
|
processConfig: "_processConfig",
|
|
130
132
|
osConfig: "_osConfig",
|
|
131
133
|
log: "_log",
|