@ai-ecoverse/slicc-kernel 1.89.0 → 1.91.0
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 +33 -5
- package/dist/driver-worker.js +89 -1
- package/dist/driver-worker.js.map +3 -3
- package/dist/index.js +135 -5
- package/dist/index.js.map +3 -3
- package/dist/kernel-worker.js +767 -95
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +89 -1
- package/dist/node-driver-worker.js.map +3 -3
- package/dist/node-process-worker.js +192 -87
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +1801 -1056
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +154 -49
- package/dist/process-worker.js.map +3 -3
- package/dist/testing.js.map +1 -1
- package/package.json +3 -3
package/dist/kernel-worker.js
CHANGED
|
@@ -2052,7 +2052,7 @@ function portCdpHook(ask, gone) {
|
|
|
2052
2052
|
}
|
|
2053
2053
|
|
|
2054
2054
|
// src/client/protocol.ts
|
|
2055
|
-
var PROTOCOL = [1,
|
|
2055
|
+
var PROTOCOL = [1, 11];
|
|
2056
2056
|
var FS_METHODS = [
|
|
2057
2057
|
"readFile",
|
|
2058
2058
|
"writeFile",
|
|
@@ -2083,6 +2083,52 @@ function fresh(bytes, start2 = 0, end = bytes.length) {
|
|
|
2083
2083
|
return new Uint8Array(bytes.subarray(start2, end));
|
|
2084
2084
|
}
|
|
2085
2085
|
|
|
2086
|
+
// src/facade/port.ts
|
|
2087
|
+
var FACADE_GONE = "the facade host is gone";
|
|
2088
|
+
function portFacadeHook(ask, gone) {
|
|
2089
|
+
return (stamp, { name: name2 }) => new Promise((resolve2, reject) => {
|
|
2090
|
+
const { port1, port2 } = new MessageChannel();
|
|
2091
|
+
let closed = false;
|
|
2092
|
+
const shut = () => {
|
|
2093
|
+
closed = true;
|
|
2094
|
+
port1.close();
|
|
2095
|
+
};
|
|
2096
|
+
const conn = {
|
|
2097
|
+
send: (bytes) => {
|
|
2098
|
+
if (closed) return;
|
|
2099
|
+
const copy = bytes.slice();
|
|
2100
|
+
port1.postMessage({ b: copy }, [copy.buffer]);
|
|
2101
|
+
},
|
|
2102
|
+
onmessage: null,
|
|
2103
|
+
onclose: null,
|
|
2104
|
+
close: (reason) => {
|
|
2105
|
+
if (closed) return;
|
|
2106
|
+
port1.postMessage({ close: reason ?? true });
|
|
2107
|
+
shut();
|
|
2108
|
+
}
|
|
2109
|
+
};
|
|
2110
|
+
port1.addEventListener("message", ({ data }) => {
|
|
2111
|
+
if ("b" in data) conn.onmessage?.(data.b);
|
|
2112
|
+
else if ("open" in data) resolve2(conn);
|
|
2113
|
+
else if ("error" in data) {
|
|
2114
|
+
shut();
|
|
2115
|
+
reject(new Error(data.error));
|
|
2116
|
+
} else if (!closed) {
|
|
2117
|
+
shut();
|
|
2118
|
+
conn.onclose?.();
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
port1.start();
|
|
2122
|
+
void gone?.then(() => {
|
|
2123
|
+
if (closed) return;
|
|
2124
|
+
shut();
|
|
2125
|
+
reject(new Error(FACADE_GONE));
|
|
2126
|
+
conn.onclose?.(FACADE_GONE);
|
|
2127
|
+
});
|
|
2128
|
+
ask({ name: name2, stamp }, port2);
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2086
2132
|
// src/fs/walk.ts
|
|
2087
2133
|
var MAX_LINKS3 = 40;
|
|
2088
2134
|
function parentOf4(path) {
|
|
@@ -2253,8 +2299,8 @@ async function access(fs, cred, path, want, options = {}) {
|
|
|
2253
2299
|
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
2254
2300
|
return { ...walked, missing: true };
|
|
2255
2301
|
}
|
|
2256
|
-
function refused(path,
|
|
2257
|
-
return fsError(
|
|
2302
|
+
function refused(path, errno2 = "EACCES") {
|
|
2303
|
+
return fsError(errno2, path);
|
|
2258
2304
|
}
|
|
2259
2305
|
|
|
2260
2306
|
// src/fs/dac.ts
|
|
@@ -2314,9 +2360,9 @@ var Created = class {
|
|
|
2314
2360
|
function linked(g, options) {
|
|
2315
2361
|
return g.links === void 0 ? options : { ...options, links: g.links };
|
|
2316
2362
|
}
|
|
2317
|
-
function deny(g, op, path,
|
|
2363
|
+
function deny(g, op, path, errno2 = "EACCES") {
|
|
2318
2364
|
const tag = g.tag === void 0 ? {} : { tag: g.tag };
|
|
2319
|
-
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno, ...tag });
|
|
2365
|
+
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno: errno2, ...tag });
|
|
2320
2366
|
}
|
|
2321
2367
|
var TTY_MODE = 400;
|
|
2322
2368
|
function ttyOpenable(g, tty) {
|
|
@@ -2983,7 +3029,7 @@ var WASI_SIGNAL_TO_POSIX = Object.fromEntries(
|
|
|
2983
3029
|
|
|
2984
3030
|
// src/process/wasi-errno.ts
|
|
2985
3031
|
var WASI_ERRNO = {
|
|
2986
|
-
...Object.fromEntries(Object.entries(E).map(([name2,
|
|
3032
|
+
...Object.fromEntries(Object.entries(E).map(([name2, errno2]) => [`E${name2}`, errno2])),
|
|
2987
3033
|
EWOULDBLOCK: E.AGAIN,
|
|
2988
3034
|
ENOTSUP: 138,
|
|
2989
3035
|
EOPNOTSUPP: 138,
|
|
@@ -3080,7 +3126,7 @@ function dialError(e, timedOut, closed) {
|
|
|
3080
3126
|
if (timedOut) return new KernelError("ETIMEDOUT");
|
|
3081
3127
|
if (closed) return new KernelError("ECONNABORTED");
|
|
3082
3128
|
const code = e?.code;
|
|
3083
|
-
const known = DIAL_ERRORS.find((
|
|
3129
|
+
const known = DIAL_ERRORS.find((errno2) => errno2 === code);
|
|
3084
3130
|
return new KernelError(known ?? "EHOSTUNREACH");
|
|
3085
3131
|
}
|
|
3086
3132
|
function isLoopback(host) {
|
|
@@ -3137,6 +3183,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
3137
3183
|
local;
|
|
3138
3184
|
peer;
|
|
3139
3185
|
peerCred;
|
|
3186
|
+
peerStamp;
|
|
3140
3187
|
listenerCred;
|
|
3141
3188
|
link;
|
|
3142
3189
|
readShut = false;
|
|
@@ -3207,10 +3254,10 @@ var KernelSocket = class _KernelSocket {
|
|
|
3207
3254
|
for (const wait2 of waits) wait2.catch(() => void 0);
|
|
3208
3255
|
return pipeCall(() => Promise.race(waits));
|
|
3209
3256
|
}
|
|
3210
|
-
bind(addr) {
|
|
3257
|
+
bind(addr, kernel = false) {
|
|
3211
3258
|
if (this.state !== "open" || this.local) throw new KernelError("EINVAL");
|
|
3212
3259
|
if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
|
|
3213
|
-
this.local = this.net.bind(this, addr);
|
|
3260
|
+
this.local = this.net.bind(this, addr, kernel);
|
|
3214
3261
|
this.boundAt = this.local;
|
|
3215
3262
|
}
|
|
3216
3263
|
listen(backlog, cred) {
|
|
@@ -3239,7 +3286,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
3239
3286
|
this.changes.wake();
|
|
3240
3287
|
return true;
|
|
3241
3288
|
}
|
|
3242
|
-
connect(addr, viaUplink = true, cred) {
|
|
3289
|
+
connect(addr, viaUplink = true, cred, stamp) {
|
|
3243
3290
|
if (this.state === "connected") throw new KernelError("EISCONN");
|
|
3244
3291
|
if (this.state === "connecting") throw new KernelError("EALREADY");
|
|
3245
3292
|
if (this.state !== "open") throw new KernelError("EINVAL");
|
|
@@ -3258,6 +3305,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
3258
3305
|
server.local = addr.family === "inet" ? { ...addr, host: canonicalHost(addr.host) } : addr;
|
|
3259
3306
|
server.peer = this.local;
|
|
3260
3307
|
server.peerCred = cred;
|
|
3308
|
+
server.peerStamp = stamp;
|
|
3261
3309
|
this.peer = server.local;
|
|
3262
3310
|
this.peerCred = target.listenerCred;
|
|
3263
3311
|
link(this, server);
|
|
@@ -3398,6 +3446,8 @@ var LoopbackNet = class {
|
|
|
3398
3446
|
bound = /* @__PURE__ */ new Map();
|
|
3399
3447
|
activators = /* @__PURE__ */ new Map();
|
|
3400
3448
|
internal = /* @__PURE__ */ new Set();
|
|
3449
|
+
reserved = /* @__PURE__ */ new Set();
|
|
3450
|
+
facades = /* @__PURE__ */ new Map();
|
|
3401
3451
|
nextEphemeral = EPHEMERAL_FIRST;
|
|
3402
3452
|
route;
|
|
3403
3453
|
useUplink(route) {
|
|
@@ -3412,6 +3462,18 @@ var LoopbackNet = class {
|
|
|
3412
3462
|
if (reserve) this.internal.add(port);
|
|
3413
3463
|
return this.internal.has(port);
|
|
3414
3464
|
}
|
|
3465
|
+
reserve(port) {
|
|
3466
|
+
this.reserved.add(port);
|
|
3467
|
+
}
|
|
3468
|
+
facade(port, name2) {
|
|
3469
|
+
this.reserve(port);
|
|
3470
|
+
this.kernelOnly(port, true);
|
|
3471
|
+
this.facades.set(port, name2);
|
|
3472
|
+
}
|
|
3473
|
+
facadeAt(addr) {
|
|
3474
|
+
if (addr.family !== "inet" || !isLoopback(canonicalHost(addr.host))) return void 0;
|
|
3475
|
+
return this.facades.get(addr.port);
|
|
3476
|
+
}
|
|
3415
3477
|
activate(addr, start2) {
|
|
3416
3478
|
this.activators.set(keyOf(addr), start2);
|
|
3417
3479
|
}
|
|
@@ -3420,7 +3482,7 @@ var LoopbackNet = class {
|
|
|
3420
3482
|
}
|
|
3421
3483
|
listen(addr, backlog = 128) {
|
|
3422
3484
|
const socket = this.socket(addr.family);
|
|
3423
|
-
socket.bind(addr);
|
|
3485
|
+
socket.bind(addr, true);
|
|
3424
3486
|
socket.listen(backlog);
|
|
3425
3487
|
return socket;
|
|
3426
3488
|
}
|
|
@@ -3429,11 +3491,13 @@ var LoopbackNet = class {
|
|
|
3429
3491
|
void socket.connect(addr, false);
|
|
3430
3492
|
return socket;
|
|
3431
3493
|
}
|
|
3432
|
-
bind(socket, addr) {
|
|
3494
|
+
bind(socket, addr, kernel = false) {
|
|
3433
3495
|
if (addr.family === "unix") {
|
|
3434
3496
|
if (addr.path === "") throw new KernelError("EINVAL");
|
|
3435
3497
|
} else if (addr.host !== "0.0.0.0" && !isLoopback(addr.host)) {
|
|
3436
3498
|
throw new KernelError("EADDRNOTAVAIL");
|
|
3499
|
+
} else if (!kernel && this.reserved.has(addr.port)) {
|
|
3500
|
+
throw new KernelError("EADDRINUSE");
|
|
3437
3501
|
}
|
|
3438
3502
|
const named = addr.family === "inet" && addr.port === 0 ? { ...addr, port: this.ephemeral() } : addr;
|
|
3439
3503
|
const key = keyOf(named);
|
|
@@ -3460,7 +3524,7 @@ var LoopbackNet = class {
|
|
|
3460
3524
|
for (let i = 0; i < span; i++) {
|
|
3461
3525
|
const port = this.nextEphemeral;
|
|
3462
3526
|
this.nextEphemeral = port === EPHEMERAL_LAST ? EPHEMERAL_FIRST : port + 1;
|
|
3463
|
-
if (!this.bound.has(`inet:${port}`)) return port;
|
|
3527
|
+
if (!this.bound.has(`inet:${port}`) && !this.reserved.has(port)) return port;
|
|
3464
3528
|
}
|
|
3465
3529
|
throw new KernelError("EADDRINUSE");
|
|
3466
3530
|
}
|
|
@@ -3729,11 +3793,11 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
3729
3793
|
};
|
|
3730
3794
|
const sockets = /* @__PURE__ */ new Map();
|
|
3731
3795
|
const credit = /* @__PURE__ */ new Map();
|
|
3732
|
-
const refill = (nid, n,
|
|
3796
|
+
const refill = (nid, n, closing2 = false) => {
|
|
3733
3797
|
const flow = credit.get(nid);
|
|
3734
3798
|
if (!flow) return;
|
|
3735
3799
|
flow.inflight -= n;
|
|
3736
|
-
if (
|
|
3800
|
+
if (closing2 && !flow.closing) {
|
|
3737
3801
|
flow.closing = true;
|
|
3738
3802
|
flow.deadline();
|
|
3739
3803
|
}
|
|
@@ -3756,12 +3820,12 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
3756
3820
|
}
|
|
3757
3821
|
sockets.set(call.nid, socket);
|
|
3758
3822
|
peer.postMessage({ net: "ws-opened", nid: call.nid, protocol: socket.protocol });
|
|
3759
|
-
void
|
|
3823
|
+
void relay2(call.nid, socket);
|
|
3760
3824
|
} catch (e) {
|
|
3761
3825
|
fail2(call.nid, e);
|
|
3762
3826
|
}
|
|
3763
3827
|
};
|
|
3764
|
-
const
|
|
3828
|
+
const relay2 = async (nid, socket) => {
|
|
3765
3829
|
let ending;
|
|
3766
3830
|
let late;
|
|
3767
3831
|
const unanswered = new Promise((resolve2) => {
|
|
@@ -4395,6 +4459,45 @@ function cdpRegistry(send, gone) {
|
|
|
4395
4459
|
}
|
|
4396
4460
|
};
|
|
4397
4461
|
}
|
|
4462
|
+
function facadeRegistry(send, gone) {
|
|
4463
|
+
const registered = /* @__PURE__ */ new Map();
|
|
4464
|
+
const drop = (id2) => {
|
|
4465
|
+
registered.get(id2)?.();
|
|
4466
|
+
return registered.delete(id2);
|
|
4467
|
+
};
|
|
4468
|
+
return {
|
|
4469
|
+
serve(l, req) {
|
|
4470
|
+
const hook = portFacadeHook(
|
|
4471
|
+
(request2, facade) => send({ facadeOpen: { ...request2, registration: req.id }, port: facade }, [
|
|
4472
|
+
facade
|
|
4473
|
+
]),
|
|
4474
|
+
gone
|
|
4475
|
+
);
|
|
4476
|
+
registered.set(req.id, l.facades.register(req.name, hook));
|
|
4477
|
+
return req.id;
|
|
4478
|
+
},
|
|
4479
|
+
drop,
|
|
4480
|
+
close: () => {
|
|
4481
|
+
for (const id2 of [...registered.keys()]) drop(id2);
|
|
4482
|
+
}
|
|
4483
|
+
};
|
|
4484
|
+
}
|
|
4485
|
+
function hostRegistries(send, gone) {
|
|
4486
|
+
const cdps = cdpRegistry(send, gone);
|
|
4487
|
+
const facades = facadeRegistry(send, gone);
|
|
4488
|
+
return {
|
|
4489
|
+
answer(l, req) {
|
|
4490
|
+
if (req.op === "serve-cdp") return cdps.serve(l, req);
|
|
4491
|
+
if (req.op === "unserve-cdp") return cdps.drop(req.registration);
|
|
4492
|
+
if (req.op === "serve-facade") return facades.serve(l, req);
|
|
4493
|
+
return facades.drop(req.registration);
|
|
4494
|
+
},
|
|
4495
|
+
close() {
|
|
4496
|
+
cdps.close();
|
|
4497
|
+
facades.close();
|
|
4498
|
+
}
|
|
4499
|
+
};
|
|
4500
|
+
}
|
|
4398
4501
|
function visibleTo(g, notify) {
|
|
4399
4502
|
if (!g?.dac.enforcing) return notify;
|
|
4400
4503
|
let queue = Promise.resolve();
|
|
@@ -4438,7 +4541,7 @@ function serveClient(port, host) {
|
|
|
4438
4541
|
if (state !== "closed") port.postMessage(message3, transfer);
|
|
4439
4542
|
};
|
|
4440
4543
|
const reply = (id2, body, transfer) => send({ id: id2, ...body }, transfer);
|
|
4441
|
-
const
|
|
4544
|
+
const hosts = hostRegistries(send, ended.promise);
|
|
4442
4545
|
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
4443
4546
|
if (state === "closed") return;
|
|
4444
4547
|
send({ bye });
|
|
@@ -4446,7 +4549,7 @@ function serveClient(port, host) {
|
|
|
4446
4549
|
net?.close();
|
|
4447
4550
|
for (const unwatch of watches.values()) unwatch();
|
|
4448
4551
|
watches.clear();
|
|
4449
|
-
|
|
4552
|
+
hosts.close();
|
|
4450
4553
|
for (const session of terminals.values()) session.close();
|
|
4451
4554
|
terminals.clear();
|
|
4452
4555
|
if (kill) killGroups(host, [...groups]);
|
|
@@ -4543,16 +4646,16 @@ function serveClient(port, host) {
|
|
|
4543
4646
|
case "mount":
|
|
4544
4647
|
return { result: await l.mountAs(clientCred(self), req.spec) };
|
|
4545
4648
|
case "umount":
|
|
4546
|
-
await l.umountAs(clientCred(self), req.target);
|
|
4547
|
-
return { result: true };
|
|
4649
|
+
return { result: await l.umountAs(clientCred(self), req.target).then(() => true) };
|
|
4548
4650
|
case "mounts":
|
|
4549
4651
|
return { result: l.mounts.list() };
|
|
4550
4652
|
case "dial":
|
|
4551
4653
|
return dialFor(l, req, watches);
|
|
4552
4654
|
case "serve-cdp":
|
|
4553
|
-
return { result: cdps.serve(l, req) };
|
|
4554
4655
|
case "unserve-cdp":
|
|
4555
|
-
|
|
4656
|
+
case "serve-facade":
|
|
4657
|
+
case "unserve-facade":
|
|
4658
|
+
return { result: hosts.answer(l, req) };
|
|
4556
4659
|
case "detach":
|
|
4557
4660
|
return { result: true };
|
|
4558
4661
|
default:
|
|
@@ -4825,6 +4928,33 @@ function toHostLoopback(href) {
|
|
|
4825
4928
|
url.hostname = "127.0.0.1";
|
|
4826
4929
|
return url.href;
|
|
4827
4930
|
}
|
|
4931
|
+
var LOOPBACK_V4 = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
4932
|
+
var LOOPBACK_V6 = /* @__PURE__ */ new Set(["::1", "::", "0:0:0:0:0:0:0:1"]);
|
|
4933
|
+
var MAPPED_V4 = /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/;
|
|
4934
|
+
var KERNEL_PORT = /^(\d{1,5})\.kernel\.localhost$/;
|
|
4935
|
+
var SCHEMES = {
|
|
4936
|
+
"http:": 80,
|
|
4937
|
+
"ws:": 80,
|
|
4938
|
+
"https:": 443,
|
|
4939
|
+
"wss:": 443
|
|
4940
|
+
};
|
|
4941
|
+
function isKernelLoopbackHost(hostname) {
|
|
4942
|
+
const host = hostname.replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "").toLowerCase();
|
|
4943
|
+
return host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0" || LOOPBACK_V4.test(host) || LOOPBACK_V6.has(host) || MAPPED_V4.test(host);
|
|
4944
|
+
}
|
|
4945
|
+
function kernelLoopbackPort(href) {
|
|
4946
|
+
let url;
|
|
4947
|
+
try {
|
|
4948
|
+
url = new URL(href);
|
|
4949
|
+
} catch {
|
|
4950
|
+
return void 0;
|
|
4951
|
+
}
|
|
4952
|
+
const fallback = SCHEMES[url.protocol];
|
|
4953
|
+
if (fallback === void 0 || !isKernelLoopbackHost(url.hostname)) return void 0;
|
|
4954
|
+
const named = KERNEL_PORT.exec(url.hostname.replace(/\.$/, "").toLowerCase())?.[1];
|
|
4955
|
+
const port = Number(named ?? (url.port || fallback));
|
|
4956
|
+
return port >= 1 && port <= 65535 ? port : void 0;
|
|
4957
|
+
}
|
|
4828
4958
|
var DEFAULT_HOSTNAME = "slicc";
|
|
4829
4959
|
var HOSTNAME_ADDRESS = "127.0.1.1";
|
|
4830
4960
|
var LIBC_HOSTNAMES = ["emscripten", "wasmer.sh"];
|
|
@@ -5130,6 +5260,9 @@ async function acceptKey(headers) {
|
|
|
5130
5260
|
}
|
|
5131
5261
|
const key = field(headers, "sec-websocket-key") ?? "";
|
|
5132
5262
|
if (!/^[A-Za-z0-9+/]{22}==$/.test(key)) throw new HttpError(400, "bad Sec-WebSocket-Key");
|
|
5263
|
+
return acceptFor(key);
|
|
5264
|
+
}
|
|
5265
|
+
async function acceptFor(key) {
|
|
5133
5266
|
const digest = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(key + GUID));
|
|
5134
5267
|
return btoa(String.fromCharCode(...new Uint8Array(digest)));
|
|
5135
5268
|
}
|
|
@@ -5153,6 +5286,19 @@ function frame(opcode, payload) {
|
|
|
5153
5286
|
out.set(payload, size);
|
|
5154
5287
|
return out;
|
|
5155
5288
|
}
|
|
5289
|
+
function clientFrame(opcode, payload) {
|
|
5290
|
+
const plain = frame(opcode, payload);
|
|
5291
|
+
const head = plain.length - payload.length;
|
|
5292
|
+
const mask = crypto.getRandomValues(new Uint8Array(4));
|
|
5293
|
+
const out = new Uint8Array(plain.length + 4);
|
|
5294
|
+
out.set(plain.subarray(0, head));
|
|
5295
|
+
out[1] = out[1] | 128;
|
|
5296
|
+
out.set(mask, head);
|
|
5297
|
+
for (let i = 0; i < payload.length; i++) {
|
|
5298
|
+
out[head + 4 + i] = payload[i] ^ mask[i & 3];
|
|
5299
|
+
}
|
|
5300
|
+
return out;
|
|
5301
|
+
}
|
|
5156
5302
|
function closeFrame(code, reason = "") {
|
|
5157
5303
|
let bytes = new TextEncoder().encode(reason);
|
|
5158
5304
|
if (bytes.length > 123) {
|
|
@@ -5192,10 +5338,12 @@ var WsReader = class {
|
|
|
5192
5338
|
continuing = false;
|
|
5193
5339
|
binaryMessage = false;
|
|
5194
5340
|
binary;
|
|
5195
|
-
|
|
5341
|
+
fromServer;
|
|
5342
|
+
constructor(incoming, max = MAX_MESSAGE, binary, fromServer = false) {
|
|
5196
5343
|
this.incoming = incoming;
|
|
5197
5344
|
this.max = max;
|
|
5198
5345
|
this.binary = binary;
|
|
5346
|
+
this.fromServer = fromServer;
|
|
5199
5347
|
}
|
|
5200
5348
|
async next(signal) {
|
|
5201
5349
|
for (; ; ) {
|
|
@@ -5207,7 +5355,7 @@ var WsReader = class {
|
|
|
5207
5355
|
const fin = (head[0] & 128) !== 0;
|
|
5208
5356
|
const opcode = head[0] & 15;
|
|
5209
5357
|
if ((head[0] & 112) !== 0) throw new WsError(CLOSE.protocol, "reserved bits are set");
|
|
5210
|
-
|
|
5358
|
+
const masked = this.masking(head[1]);
|
|
5211
5359
|
const length2 = await this.length(head[1] & 127, signal);
|
|
5212
5360
|
const control = opcode >= 8;
|
|
5213
5361
|
if (control && (length2 > 125 || !fin)) {
|
|
@@ -5216,15 +5364,19 @@ var WsReader = class {
|
|
|
5216
5364
|
if (!control && this.size + length2 > this.max) {
|
|
5217
5365
|
throw new WsError(CLOSE.tooBig, `a message is over ${this.max} bytes`);
|
|
5218
5366
|
}
|
|
5219
|
-
const
|
|
5220
|
-
await fill(this.incoming, mask, signal);
|
|
5221
|
-
const payload = new Uint8Array(length2);
|
|
5222
|
-
await fill(this.incoming, payload, signal);
|
|
5223
|
-
for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
|
|
5367
|
+
const payload = await this.payload(length2, masked, signal);
|
|
5224
5368
|
const event = this.take(opcode, fin, payload);
|
|
5225
5369
|
if (event) return event;
|
|
5226
5370
|
}
|
|
5227
5371
|
}
|
|
5372
|
+
masking(second) {
|
|
5373
|
+
const masked = (second & 128) !== 0;
|
|
5374
|
+
if (masked !== this.fromServer) return masked;
|
|
5375
|
+
throw new WsError(
|
|
5376
|
+
CLOSE.protocol,
|
|
5377
|
+
this.fromServer ? "server frames must not be masked" : "client frames must be masked"
|
|
5378
|
+
);
|
|
5379
|
+
}
|
|
5228
5380
|
data(opcode, fin, payload) {
|
|
5229
5381
|
if (opcode === OP.continuation !== this.continuing) {
|
|
5230
5382
|
throw new WsError(CLOSE.protocol, "unexpected continuation frame");
|
|
@@ -5239,6 +5391,14 @@ var WsReader = class {
|
|
|
5239
5391
|
this.size = 0;
|
|
5240
5392
|
return this.binaryMessage ? { kind: "binary", bytes: whole } : { kind: "text", text: decode(whole) };
|
|
5241
5393
|
}
|
|
5394
|
+
async payload(length2, masked, signal) {
|
|
5395
|
+
const mask = new Uint8Array(4);
|
|
5396
|
+
if (masked) await fill(this.incoming, mask, signal);
|
|
5397
|
+
const payload = new Uint8Array(length2);
|
|
5398
|
+
await fill(this.incoming, payload, signal);
|
|
5399
|
+
if (masked) for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
|
|
5400
|
+
return payload;
|
|
5401
|
+
}
|
|
5242
5402
|
async length(short, signal) {
|
|
5243
5403
|
if (short < 126) return short;
|
|
5244
5404
|
const ext = new Uint8Array(short === 126 ? 2 : 8);
|
|
@@ -5674,10 +5834,10 @@ var RealmProxy = class {
|
|
|
5674
5834
|
encodedBodies: this.options.transport.traits.encodedBodies,
|
|
5675
5835
|
bodiless: bodiless2
|
|
5676
5836
|
});
|
|
5677
|
-
const
|
|
5678
|
-
const keep2 = keepAlive && (bodiless2 ||
|
|
5837
|
+
const chunked2 = !bodiless2 && minor >= 1;
|
|
5838
|
+
const keep2 = keepAlive && (bodiless2 || chunked2);
|
|
5679
5839
|
headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
|
|
5680
|
-
if (
|
|
5840
|
+
if (chunked2) headers.push(["Transfer-Encoding", "chunked"]);
|
|
5681
5841
|
const signal = this.stop.signal;
|
|
5682
5842
|
await conn.write(
|
|
5683
5843
|
responseHead(status, response.statusText || REASON[status] || "", headers),
|
|
@@ -5689,13 +5849,13 @@ var RealmProxy = class {
|
|
|
5689
5849
|
}
|
|
5690
5850
|
try {
|
|
5691
5851
|
for await (const piece of response.body) {
|
|
5692
|
-
if (piece.length > 0) await conn.write(
|
|
5852
|
+
if (piece.length > 0) await conn.write(chunked2 ? chunk(piece) : piece, signal);
|
|
5693
5853
|
}
|
|
5694
5854
|
} catch {
|
|
5695
5855
|
await response.cancel().catch(() => void 0);
|
|
5696
5856
|
return false;
|
|
5697
5857
|
}
|
|
5698
|
-
if (
|
|
5858
|
+
if (chunked2) await conn.write(LAST_CHUNK, signal);
|
|
5699
5859
|
return keep2;
|
|
5700
5860
|
}
|
|
5701
5861
|
async connect(conn, incoming, req, idle) {
|
|
@@ -7127,6 +7287,7 @@ var Relay = class {
|
|
|
7127
7287
|
function enableCdp(net, hosts) {
|
|
7128
7288
|
let facade;
|
|
7129
7289
|
net.kernelOnly(CDP_PORT, true);
|
|
7290
|
+
net.reserve(CDP_PORT);
|
|
7130
7291
|
net.activate({ family: "inet", host: "127.0.0.1", port: CDP_PORT }, () => {
|
|
7131
7292
|
const started = new CdpFacade({ net, hosts });
|
|
7132
7293
|
facade = started;
|
|
@@ -7441,9 +7602,9 @@ async function scanFilesystems(fs, modules) {
|
|
|
7441
7602
|
} catch {
|
|
7442
7603
|
continue;
|
|
7443
7604
|
}
|
|
7444
|
-
const
|
|
7445
|
-
if (!
|
|
7446
|
-
for (const [type, entry] of Object.entries(
|
|
7605
|
+
const declared2 = manifest.slicc?.filesystems;
|
|
7606
|
+
if (!declared2 || typeof declared2 !== "object") continue;
|
|
7607
|
+
for (const [type, entry] of Object.entries(declared2)) {
|
|
7447
7608
|
const module = inside(pkg, entry?.module);
|
|
7448
7609
|
if (NAME2.test(type) && module && !found.has(type)) found.set(type, module);
|
|
7449
7610
|
}
|
|
@@ -7461,9 +7622,9 @@ async function scanBinfmts(fs, modules, warn = () => {
|
|
|
7461
7622
|
} catch {
|
|
7462
7623
|
continue;
|
|
7463
7624
|
}
|
|
7464
|
-
const
|
|
7465
|
-
if (!
|
|
7466
|
-
for (const [suffix, command] of Object.entries(
|
|
7625
|
+
const declared2 = manifest.slicc?.binfmt;
|
|
7626
|
+
if (!declared2 || typeof declared2 !== "object") continue;
|
|
7627
|
+
for (const [suffix, command] of Object.entries(declared2)) {
|
|
7467
7628
|
if (!SUFFIX.test(suffix) || typeof command !== "string" || !NAME2.test(command)) continue;
|
|
7468
7629
|
const owner = owners.get(suffix);
|
|
7469
7630
|
if (owner === void 0) {
|
|
@@ -7493,6 +7654,132 @@ function binfmtOf(binfmts, path) {
|
|
|
7493
7654
|
return best === void 0 ? void 0 : binfmts.get(best);
|
|
7494
7655
|
}
|
|
7495
7656
|
|
|
7657
|
+
// src/facade/types.ts
|
|
7658
|
+
var FACADE_NAME = /^[a-z][a-z0-9-]*$/;
|
|
7659
|
+
function facadeEnvName(name2) {
|
|
7660
|
+
return `SLICC_${name2.toUpperCase().replaceAll("-", "_")}_URL`;
|
|
7661
|
+
}
|
|
7662
|
+
function facadeUrl(port) {
|
|
7663
|
+
return `tcp://127.0.0.1:${port}`;
|
|
7664
|
+
}
|
|
7665
|
+
|
|
7666
|
+
// src/facade/hosts.ts
|
|
7667
|
+
var TAKEN_PORTS = /* @__PURE__ */ new Set([CDP_PORT, REALM_PROXY_PORT]);
|
|
7668
|
+
var READ_CHUNK = 64 * 1024;
|
|
7669
|
+
var FACADE_MAX_PENDING = 16 * 1024 * 1024;
|
|
7670
|
+
var FACADE_FLOODED = `slicc-kernel: more than ${FACADE_MAX_PENDING} bytes are waiting for the program to read`;
|
|
7671
|
+
function declared(specs) {
|
|
7672
|
+
const out = /* @__PURE__ */ new Map();
|
|
7673
|
+
const ports = /* @__PURE__ */ new Set();
|
|
7674
|
+
for (const [name2, spec] of Object.entries(specs ?? {})) {
|
|
7675
|
+
if (!FACADE_NAME.test(name2)) throw new TypeError(`facades: not a facade name: ${name2}`);
|
|
7676
|
+
const port = spec?.port;
|
|
7677
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535 || TAKEN_PORTS.has(port)) {
|
|
7678
|
+
throw new TypeError(`facades.${name2}: not a free port: ${String(port)}`);
|
|
7679
|
+
}
|
|
7680
|
+
if (ports.has(port)) throw new TypeError(`facades.${name2}: port ${port} is taken twice`);
|
|
7681
|
+
if (spec.hook !== void 0 && typeof spec.hook !== "function") {
|
|
7682
|
+
throw new TypeError(`facades.${name2}: hook must be a function`);
|
|
7683
|
+
}
|
|
7684
|
+
ports.add(port);
|
|
7685
|
+
out.set(name2, { port, embedder: spec.hook, clients: [] });
|
|
7686
|
+
}
|
|
7687
|
+
return out;
|
|
7688
|
+
}
|
|
7689
|
+
var FacadeHosts = class {
|
|
7690
|
+
facades;
|
|
7691
|
+
constructor(specs) {
|
|
7692
|
+
this.facades = declared(specs);
|
|
7693
|
+
}
|
|
7694
|
+
env() {
|
|
7695
|
+
const env = {};
|
|
7696
|
+
for (const [name2, { port }] of this.facades) env[facadeEnvName(name2)] = facadeUrl(port);
|
|
7697
|
+
return env;
|
|
7698
|
+
}
|
|
7699
|
+
enable(net) {
|
|
7700
|
+
for (const [name2, { port }] of this.facades) {
|
|
7701
|
+
net.facade(port, name2);
|
|
7702
|
+
net.activate({ family: "inet", host: "127.0.0.1", port }, () => {
|
|
7703
|
+
const listener = net.listen({ family: "inet", host: "127.0.0.1", port });
|
|
7704
|
+
void acceptAll(listener, (socket) => this.serve(name2, socket));
|
|
7705
|
+
});
|
|
7706
|
+
}
|
|
7707
|
+
}
|
|
7708
|
+
register(name2, hook) {
|
|
7709
|
+
const entry = this.facades.get(name2);
|
|
7710
|
+
if (!entry || typeof hook !== "function") throw new KernelError("EINVAL");
|
|
7711
|
+
entry.clients.push(hook);
|
|
7712
|
+
return () => {
|
|
7713
|
+
const at = entry.clients.indexOf(hook);
|
|
7714
|
+
if (at >= 0) entry.clients.splice(at, 1);
|
|
7715
|
+
};
|
|
7716
|
+
}
|
|
7717
|
+
async serve(name2, socket) {
|
|
7718
|
+
const entry = this.facades.get(name2);
|
|
7719
|
+
const hook = entry.clients.at(-1) ?? entry.embedder;
|
|
7720
|
+
const stamp = socket.peerStamp;
|
|
7721
|
+
if (!hook || !stamp) return socket.close();
|
|
7722
|
+
let conn;
|
|
7723
|
+
try {
|
|
7724
|
+
conn = await hook(structuredClone(stamp), { name: name2 });
|
|
7725
|
+
} catch {
|
|
7726
|
+
return socket.close();
|
|
7727
|
+
}
|
|
7728
|
+
await relay(socket, conn);
|
|
7729
|
+
}
|
|
7730
|
+
};
|
|
7731
|
+
async function acceptAll(listener, serve) {
|
|
7732
|
+
for (; ; ) {
|
|
7733
|
+
const socket = await listener.accept();
|
|
7734
|
+
void serve(socket);
|
|
7735
|
+
}
|
|
7736
|
+
}
|
|
7737
|
+
async function relay(socket, conn) {
|
|
7738
|
+
let ended = false;
|
|
7739
|
+
let pending = 0;
|
|
7740
|
+
let writes = Promise.resolve();
|
|
7741
|
+
const end = () => {
|
|
7742
|
+
if (ended) return;
|
|
7743
|
+
ended = true;
|
|
7744
|
+
socket.close();
|
|
7745
|
+
};
|
|
7746
|
+
conn.onmessage = (bytes) => {
|
|
7747
|
+
if (ended) return;
|
|
7748
|
+
const chunk2 = bytes.slice();
|
|
7749
|
+
pending += chunk2.length;
|
|
7750
|
+
if (pending > FACADE_MAX_PENDING) {
|
|
7751
|
+
end();
|
|
7752
|
+
conn.close(FACADE_FLOODED);
|
|
7753
|
+
return;
|
|
7754
|
+
}
|
|
7755
|
+
writes = writes.then(async () => {
|
|
7756
|
+
if (ended) return;
|
|
7757
|
+
let at = 0;
|
|
7758
|
+
while (at < chunk2.length) at += await socket.write(chunk2.subarray(at));
|
|
7759
|
+
pending -= chunk2.length;
|
|
7760
|
+
});
|
|
7761
|
+
writes.catch(() => {
|
|
7762
|
+
end();
|
|
7763
|
+
conn.close();
|
|
7764
|
+
});
|
|
7765
|
+
};
|
|
7766
|
+
conn.onclose = () => {
|
|
7767
|
+
writes.then(end, end);
|
|
7768
|
+
};
|
|
7769
|
+
try {
|
|
7770
|
+
for (; ; ) {
|
|
7771
|
+
const bytes = await socket.read(READ_CHUNK);
|
|
7772
|
+
if (bytes.length === 0 || ended) break;
|
|
7773
|
+
conn.send(bytes);
|
|
7774
|
+
}
|
|
7775
|
+
} catch {
|
|
7776
|
+
}
|
|
7777
|
+
if (!ended) {
|
|
7778
|
+
end();
|
|
7779
|
+
conn.close();
|
|
7780
|
+
}
|
|
7781
|
+
}
|
|
7782
|
+
|
|
7496
7783
|
// src/fs/attest.ts
|
|
7497
7784
|
var ATTEST_MAX = 1024 * 1024;
|
|
7498
7785
|
var UNOWNED = 1e3;
|
|
@@ -7995,15 +8282,15 @@ var UnlinkedKeeper = class {
|
|
|
7995
8282
|
wrap(fs = this.fs) {
|
|
7996
8283
|
const keeper = this;
|
|
7997
8284
|
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
7998
|
-
const
|
|
8285
|
+
const counted2 = (st) => {
|
|
7999
8286
|
const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
|
|
8000
8287
|
return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
|
|
8001
8288
|
};
|
|
8002
8289
|
return {
|
|
8003
8290
|
...fs,
|
|
8004
8291
|
links: (path) => mayLink(fs, path),
|
|
8005
|
-
stat: async (path) =>
|
|
8006
|
-
lstat: async (path) =>
|
|
8292
|
+
stat: async (path) => counted2(await fs.stat(path)),
|
|
8293
|
+
lstat: async (path) => counted2(await fs.lstat(path)),
|
|
8007
8294
|
async rm(path, options) {
|
|
8008
8295
|
const target = fs.resolvePath("/", path);
|
|
8009
8296
|
const pinned = await keeper.pin(target);
|
|
@@ -8017,7 +8304,7 @@ var UnlinkedKeeper = class {
|
|
|
8017
8304
|
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
8018
8305
|
},
|
|
8019
8306
|
readdir: async (path) => visible(await fs.readdir(path)),
|
|
8020
|
-
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st &&
|
|
8307
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
|
|
8021
8308
|
};
|
|
8022
8309
|
}
|
|
8023
8310
|
async settle(pinned, op) {
|
|
@@ -10849,7 +11136,7 @@ function mountTable(mounts = []) {
|
|
|
10849
11136
|
}
|
|
10850
11137
|
|
|
10851
11138
|
// src/version.ts
|
|
10852
|
-
var KERNEL_VERSION = true ? "1.
|
|
11139
|
+
var KERNEL_VERSION = true ? "1.91.0" : "0.0.0-dev";
|
|
10853
11140
|
var OS_RELEASE = "6.0.0-slicc";
|
|
10854
11141
|
function procVersion() {
|
|
10855
11142
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -11437,6 +11724,339 @@ async function hostSyscall(req, ctx) {
|
|
|
11437
11724
|
}
|
|
11438
11725
|
}
|
|
11439
11726
|
|
|
11727
|
+
// src/kernel/net/loopback-transport.ts
|
|
11728
|
+
var LOOPBACK_TRAITS = {
|
|
11729
|
+
manualRedirects: true,
|
|
11730
|
+
encodedBodies: true,
|
|
11731
|
+
maxRequestBody: 64 * 1024 * 1024,
|
|
11732
|
+
crossOrigin: "any",
|
|
11733
|
+
websocket: true
|
|
11734
|
+
};
|
|
11735
|
+
var MAX_HEAD2 = 64 * 1024;
|
|
11736
|
+
var OWN_HEADERS = /* @__PURE__ */ new Set([
|
|
11737
|
+
"host",
|
|
11738
|
+
"connection",
|
|
11739
|
+
"content-length",
|
|
11740
|
+
"transfer-encoding",
|
|
11741
|
+
"upgrade",
|
|
11742
|
+
"sec-websocket-key",
|
|
11743
|
+
"sec-websocket-version",
|
|
11744
|
+
"sec-websocket-protocol",
|
|
11745
|
+
"sec-websocket-extensions"
|
|
11746
|
+
]);
|
|
11747
|
+
var OP_CLOSE = 8;
|
|
11748
|
+
var OP_PONG = 10;
|
|
11749
|
+
var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
11750
|
+
var SAFE_VALUE = /^[^\r\n\0]*$/;
|
|
11751
|
+
var CLOSE_WAIT_MS3 = 5e3;
|
|
11752
|
+
var MAX_UNREAD = 16 * 1024 * 1024;
|
|
11753
|
+
function errno(code, message3) {
|
|
11754
|
+
return Object.assign(new Error(`${code}: ${message3}`), { code });
|
|
11755
|
+
}
|
|
11756
|
+
function requestHead(method, url, headers, extra) {
|
|
11757
|
+
if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
|
|
11758
|
+
const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
11759
|
+
for (const [name2, value] of headers) {
|
|
11760
|
+
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
11761
|
+
if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value)) throw errno("EINVAL", `header ${name2}`);
|
|
11762
|
+
lines2.push(`${name2}: ${value}`);
|
|
11763
|
+
}
|
|
11764
|
+
for (const [name2, value] of extra) lines2.push(`${name2}: ${value}`);
|
|
11765
|
+
return latin1Bytes(`${lines2.join("\r\n")}\r
|
|
11766
|
+
\r
|
|
11767
|
+
`);
|
|
11768
|
+
}
|
|
11769
|
+
function parseHead(bytes) {
|
|
11770
|
+
const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
|
|
11771
|
+
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
11772
|
+
if (!match) throw errno("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
11773
|
+
const headers = [];
|
|
11774
|
+
for (const line of lines2) {
|
|
11775
|
+
const colon = line.indexOf(":");
|
|
11776
|
+
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
11777
|
+
}
|
|
11778
|
+
return { status: Number(match[1]), statusText: match[2] ?? "", headers };
|
|
11779
|
+
}
|
|
11780
|
+
async function readHead(incoming, signal) {
|
|
11781
|
+
for (; ; ) {
|
|
11782
|
+
let raw;
|
|
11783
|
+
try {
|
|
11784
|
+
raw = await incoming.head(MAX_HEAD2, signal);
|
|
11785
|
+
} catch (err) {
|
|
11786
|
+
if (err instanceof KernelError) throw err;
|
|
11787
|
+
throw errno("EPROTO", "the response head is malformed or too large");
|
|
11788
|
+
}
|
|
11789
|
+
if (!raw) throw errno("ECONNRESET", "the connection closed before a response");
|
|
11790
|
+
const head = parseHead(raw);
|
|
11791
|
+
if (head.status < 100 || head.status >= 200 || head.status === 101) return head;
|
|
11792
|
+
}
|
|
11793
|
+
}
|
|
11794
|
+
async function* chunked(incoming, signal) {
|
|
11795
|
+
for (; ; ) {
|
|
11796
|
+
const line = await incoming.line(4096, signal);
|
|
11797
|
+
const size = /^([0-9A-Fa-f]{1,12})[ \t]*(;.*)?$/.exec(line);
|
|
11798
|
+
if (!size) throw errno("EPROTO", "a malformed chunk size");
|
|
11799
|
+
const n = Number.parseInt(size[1], 16);
|
|
11800
|
+
if (n === 0) {
|
|
11801
|
+
while (await incoming.line(MAX_HEAD2, signal) !== "") ;
|
|
11802
|
+
return;
|
|
11803
|
+
}
|
|
11804
|
+
yield await incoming.exactly(n, signal);
|
|
11805
|
+
if (await incoming.line(2, signal) !== "") throw errno("EPROTO", "a malformed chunk");
|
|
11806
|
+
}
|
|
11807
|
+
}
|
|
11808
|
+
async function* counted(incoming, length2, signal) {
|
|
11809
|
+
for (let left = length2; left > 0; ) {
|
|
11810
|
+
const data = await incoming.some(Math.min(left, 64 * 1024), signal);
|
|
11811
|
+
if (data.length === 0) throw errno("EPROTO", "the body ended early");
|
|
11812
|
+
left -= data.length;
|
|
11813
|
+
yield data;
|
|
11814
|
+
}
|
|
11815
|
+
}
|
|
11816
|
+
async function* untilClose(incoming, signal) {
|
|
11817
|
+
for (; ; ) {
|
|
11818
|
+
const data = await incoming.some(64 * 1024, signal);
|
|
11819
|
+
if (data.length === 0) return;
|
|
11820
|
+
yield data;
|
|
11821
|
+
}
|
|
11822
|
+
}
|
|
11823
|
+
function bodyOf(incoming, head, method, signal) {
|
|
11824
|
+
if (method === "HEAD" || head.status === 204 || head.status === 304) return [];
|
|
11825
|
+
if (fieldTokens(head.headers, "transfer-encoding").includes("chunked")) {
|
|
11826
|
+
return chunked(incoming, signal);
|
|
11827
|
+
}
|
|
11828
|
+
const lengths = fieldValues(head.headers, "content-length");
|
|
11829
|
+
if (lengths.length === 0) return untilClose(incoming, signal);
|
|
11830
|
+
const length2 = lengths[0]?.trim() ?? "";
|
|
11831
|
+
if (!/^\d{1,15}$/.test(length2) || lengths.some((l) => l.trim() !== length2)) {
|
|
11832
|
+
throw errno("EPROTO", `a malformed Content-Length: ${lengths.join(", ")}`);
|
|
11833
|
+
}
|
|
11834
|
+
return counted(incoming, Number(length2), signal);
|
|
11835
|
+
}
|
|
11836
|
+
var KernelLoopback = class {
|
|
11837
|
+
net;
|
|
11838
|
+
cred;
|
|
11839
|
+
constructor(net, cred) {
|
|
11840
|
+
this.net = net;
|
|
11841
|
+
this.cred = cred;
|
|
11842
|
+
}
|
|
11843
|
+
port(url) {
|
|
11844
|
+
return kernelLoopbackPort(url);
|
|
11845
|
+
}
|
|
11846
|
+
connect(url, port, signal) {
|
|
11847
|
+
if (url.protocol === "https:" || url.protocol === "wss:") {
|
|
11848
|
+
throw errno("ENOTSUP", `TLS to the kernel's loopback is not supported: ${url.origin}`);
|
|
11849
|
+
}
|
|
11850
|
+
signal.throwIfAborted();
|
|
11851
|
+
const socket = this.net.socket("inet");
|
|
11852
|
+
try {
|
|
11853
|
+
void socket.connect({ family: "inet", host: "127.0.0.1", port }, false, this.cred());
|
|
11854
|
+
} catch (err) {
|
|
11855
|
+
socket.close();
|
|
11856
|
+
const refused2 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
11857
|
+
throw refused2 ? errno("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
11858
|
+
}
|
|
11859
|
+
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
11860
|
+
return socket;
|
|
11861
|
+
}
|
|
11862
|
+
async fetch(req, port) {
|
|
11863
|
+
const url = new URL(req.url);
|
|
11864
|
+
const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
|
|
11865
|
+
const length2 = body ? [["Content-Length", String(body.length)]] : [];
|
|
11866
|
+
const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
|
|
11867
|
+
const socket = this.connect(url, port, req.signal);
|
|
11868
|
+
try {
|
|
11869
|
+
await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
|
|
11870
|
+
const incoming = new Incoming(socket);
|
|
11871
|
+
const response = await readHead(incoming, req.signal);
|
|
11872
|
+
const chunks = bodyOf(incoming, response, req.method, req.signal);
|
|
11873
|
+
return {
|
|
11874
|
+
url: req.url,
|
|
11875
|
+
status: response.status,
|
|
11876
|
+
statusText: response.statusText,
|
|
11877
|
+
headers: response.headers,
|
|
11878
|
+
body: closing(chunks, socket),
|
|
11879
|
+
cancel: async () => socket.close()
|
|
11880
|
+
};
|
|
11881
|
+
} catch (err) {
|
|
11882
|
+
socket.close();
|
|
11883
|
+
throw err;
|
|
11884
|
+
}
|
|
11885
|
+
}
|
|
11886
|
+
async websocket(req, port) {
|
|
11887
|
+
const url = new URL(req.url);
|
|
11888
|
+
const socket = this.connect(url, port, req.signal);
|
|
11889
|
+
try {
|
|
11890
|
+
const key = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
|
|
11891
|
+
const extra = [
|
|
11892
|
+
["Upgrade", "websocket"],
|
|
11893
|
+
["Connection", "Upgrade"],
|
|
11894
|
+
["Sec-WebSocket-Key", key],
|
|
11895
|
+
["Sec-WebSocket-Version", "13"]
|
|
11896
|
+
];
|
|
11897
|
+
if (req.protocols.length > 0)
|
|
11898
|
+
extra.push(["Sec-WebSocket-Protocol", req.protocols.join(", ")]);
|
|
11899
|
+
await writeAll2(socket, requestHead("GET", url, req.headers, extra), req.signal);
|
|
11900
|
+
const incoming = new Incoming(socket);
|
|
11901
|
+
const head = await readHead(incoming, req.signal);
|
|
11902
|
+
if (head.status !== 101) {
|
|
11903
|
+
throw errno(
|
|
11904
|
+
"EPROTO",
|
|
11905
|
+
`the server answered ${head.status} ${head.statusText}, not an upgrade`
|
|
11906
|
+
);
|
|
11907
|
+
}
|
|
11908
|
+
const upgraded = fieldTokens(head.headers, "upgrade").includes("websocket") && fieldTokens(head.headers, "connection").includes("upgrade");
|
|
11909
|
+
const accept2 = fieldValues(head.headers, "sec-websocket-accept")[0];
|
|
11910
|
+
if (!upgraded || accept2 !== await acceptFor(key)) {
|
|
11911
|
+
throw errno("EPROTO", "the server did not accept the WebSocket key");
|
|
11912
|
+
}
|
|
11913
|
+
const protocol = fieldValues(head.headers, "sec-websocket-protocol")[0] ?? "";
|
|
11914
|
+
return new LoopbackWebSocket(socket, incoming, protocol);
|
|
11915
|
+
} catch (err) {
|
|
11916
|
+
socket.close();
|
|
11917
|
+
throw err;
|
|
11918
|
+
}
|
|
11919
|
+
}
|
|
11920
|
+
};
|
|
11921
|
+
async function writeAll2(socket, bytes, signal) {
|
|
11922
|
+
for (let at = 0; at < bytes.length; ) at += await socket.write(bytes.subarray(at), signal);
|
|
11923
|
+
}
|
|
11924
|
+
async function* closing(chunks, socket) {
|
|
11925
|
+
try {
|
|
11926
|
+
yield* chunks;
|
|
11927
|
+
} catch (err) {
|
|
11928
|
+
if (typeof err?.code === "string") throw err;
|
|
11929
|
+
throw errno("EPROTO", err instanceof Error ? err.message : String(err));
|
|
11930
|
+
} finally {
|
|
11931
|
+
socket.close();
|
|
11932
|
+
}
|
|
11933
|
+
}
|
|
11934
|
+
var LoopbackWebSocket = class {
|
|
11935
|
+
protocol;
|
|
11936
|
+
messages;
|
|
11937
|
+
closed;
|
|
11938
|
+
pending = 0;
|
|
11939
|
+
writes = Promise.resolve();
|
|
11940
|
+
sentClose = false;
|
|
11941
|
+
stop = new AbortController();
|
|
11942
|
+
socket;
|
|
11943
|
+
settle;
|
|
11944
|
+
unread = 0;
|
|
11945
|
+
room;
|
|
11946
|
+
constructor(socket, incoming, protocol) {
|
|
11947
|
+
this.socket = socket;
|
|
11948
|
+
this.protocol = protocol;
|
|
11949
|
+
this.closed = new Promise((resolve2) => {
|
|
11950
|
+
this.settle = resolve2;
|
|
11951
|
+
});
|
|
11952
|
+
this.stop.signal.addEventListener("abort", () => this.room?.(), { once: true });
|
|
11953
|
+
const queue = [];
|
|
11954
|
+
let wake;
|
|
11955
|
+
let ended = false;
|
|
11956
|
+
void this.pump(new WsReader(incoming, void 0, true, true), (message3) => {
|
|
11957
|
+
queue.push(message3);
|
|
11958
|
+
this.unread += message3.length;
|
|
11959
|
+
wake?.();
|
|
11960
|
+
}).finally(() => {
|
|
11961
|
+
ended = true;
|
|
11962
|
+
wake?.();
|
|
11963
|
+
});
|
|
11964
|
+
const taken = (message3) => {
|
|
11965
|
+
this.unread -= message3.length;
|
|
11966
|
+
const room = this.room;
|
|
11967
|
+
this.room = void 0;
|
|
11968
|
+
room?.();
|
|
11969
|
+
};
|
|
11970
|
+
this.messages = {
|
|
11971
|
+
[Symbol.asyncIterator]: async function* () {
|
|
11972
|
+
for (; ; ) {
|
|
11973
|
+
const next2 = queue.shift();
|
|
11974
|
+
if (next2 !== void 0) {
|
|
11975
|
+
taken(next2);
|
|
11976
|
+
yield next2;
|
|
11977
|
+
continue;
|
|
11978
|
+
}
|
|
11979
|
+
if (ended) return;
|
|
11980
|
+
await new Promise((resolve2) => {
|
|
11981
|
+
wake = resolve2;
|
|
11982
|
+
});
|
|
11983
|
+
wake = void 0;
|
|
11984
|
+
}
|
|
11985
|
+
}
|
|
11986
|
+
};
|
|
11987
|
+
}
|
|
11988
|
+
async space() {
|
|
11989
|
+
while (this.unread > MAX_UNREAD && !this.stop.signal.aborted) {
|
|
11990
|
+
await new Promise((resolve2) => {
|
|
11991
|
+
this.room = resolve2;
|
|
11992
|
+
});
|
|
11993
|
+
}
|
|
11994
|
+
}
|
|
11995
|
+
get buffered() {
|
|
11996
|
+
return this.pending;
|
|
11997
|
+
}
|
|
11998
|
+
write(bytes) {
|
|
11999
|
+
this.pending += bytes.length;
|
|
12000
|
+
const previous = this.writes;
|
|
12001
|
+
this.writes = (async () => {
|
|
12002
|
+
await previous;
|
|
12003
|
+
try {
|
|
12004
|
+
await writeAll2(this.socket, bytes, this.stop.signal);
|
|
12005
|
+
} catch {
|
|
12006
|
+
this.stop.abort();
|
|
12007
|
+
}
|
|
12008
|
+
this.pending -= bytes.length;
|
|
12009
|
+
})();
|
|
12010
|
+
}
|
|
12011
|
+
async pump(reader, deliver) {
|
|
12012
|
+
let close = { code: 1006, reason: "" };
|
|
12013
|
+
try {
|
|
12014
|
+
for (; ; ) {
|
|
12015
|
+
await this.space();
|
|
12016
|
+
const event = await reader.next(this.stop.signal);
|
|
12017
|
+
if (!event) break;
|
|
12018
|
+
if (event.kind === "text") deliver(event.text);
|
|
12019
|
+
else if (event.kind === "binary") deliver(event.bytes);
|
|
12020
|
+
else if (event.kind === "ping") this.write(clientFrame(OP_PONG, event.payload));
|
|
12021
|
+
else {
|
|
12022
|
+
close = { code: event.code, reason: event.reason };
|
|
12023
|
+
if (!this.sentClose) this.sendClose(event.code === 1005 ? 1e3 : event.code, "");
|
|
12024
|
+
break;
|
|
12025
|
+
}
|
|
12026
|
+
}
|
|
12027
|
+
} catch (err) {
|
|
12028
|
+
if (err instanceof WsError) this.sendClose(err.code, err.message);
|
|
12029
|
+
close = { code: err instanceof WsError ? err.code : 1006, reason: "" };
|
|
12030
|
+
} finally {
|
|
12031
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
12032
|
+
await this.writes;
|
|
12033
|
+
clearTimeout(late);
|
|
12034
|
+
this.socket.close();
|
|
12035
|
+
this.settle(close);
|
|
12036
|
+
}
|
|
12037
|
+
}
|
|
12038
|
+
sendClose(code, reason) {
|
|
12039
|
+
if (this.sentClose) return;
|
|
12040
|
+
this.sentClose = true;
|
|
12041
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
12042
|
+
void this.closed.then(() => clearTimeout(late));
|
|
12043
|
+
const bytes = new TextEncoder().encode(reason).subarray(0, 123);
|
|
12044
|
+
const payload = new Uint8Array(2 + bytes.length);
|
|
12045
|
+
new DataView(payload.buffer).setUint16(0, sendableCode(code) ? code : CLOSE.error);
|
|
12046
|
+
payload.set(bytes, 2);
|
|
12047
|
+
this.write(clientFrame(OP_CLOSE, payload));
|
|
12048
|
+
}
|
|
12049
|
+
send(data) {
|
|
12050
|
+
if (this.sentClose) throw errno("EPIPE", "the websocket is closing");
|
|
12051
|
+
const text = typeof data === "string";
|
|
12052
|
+
const payload = text ? new TextEncoder().encode(data) : data;
|
|
12053
|
+
this.write(clientFrame(text ? OP_TEXT : OP_BINARY, payload));
|
|
12054
|
+
}
|
|
12055
|
+
close(code = 1e3, reason = "") {
|
|
12056
|
+
this.sendClose(code, reason);
|
|
12057
|
+
}
|
|
12058
|
+
};
|
|
12059
|
+
|
|
11440
12060
|
// src/kernel/net/ws-syscalls.ts
|
|
11441
12061
|
var WS_OPS = [
|
|
11442
12062
|
"net-ws-open",
|
|
@@ -11446,7 +12066,7 @@ var WS_OPS = [
|
|
|
11446
12066
|
"net-ws-send",
|
|
11447
12067
|
"net-ws-close"
|
|
11448
12068
|
];
|
|
11449
|
-
var
|
|
12069
|
+
var TOKEN4 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
11450
12070
|
function message(e) {
|
|
11451
12071
|
return e instanceof Error ? e.message : String(e);
|
|
11452
12072
|
}
|
|
@@ -11455,16 +12075,18 @@ function errnoOf(e) {
|
|
|
11455
12075
|
const code = e?.code;
|
|
11456
12076
|
return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
|
|
11457
12077
|
}
|
|
11458
|
-
function fail(
|
|
11459
|
-
return { ok: false, errno, message: text };
|
|
12078
|
+
function fail(errno2, text) {
|
|
12079
|
+
return { ok: false, errno: errno2, message: text };
|
|
11460
12080
|
}
|
|
11461
12081
|
var WsHandles = class {
|
|
11462
12082
|
transport;
|
|
11463
12083
|
open = /* @__PURE__ */ new Map();
|
|
11464
12084
|
next;
|
|
11465
|
-
|
|
12085
|
+
local;
|
|
12086
|
+
constructor(transport, next2, local) {
|
|
11466
12087
|
this.transport = transport;
|
|
11467
12088
|
this.next = next2;
|
|
12089
|
+
this.local = local;
|
|
11468
12090
|
}
|
|
11469
12091
|
owns(handle) {
|
|
11470
12092
|
return this.open.has(handle);
|
|
@@ -11493,16 +12115,18 @@ var WsHandles = class {
|
|
|
11493
12115
|
return { ok: true, kind: "json", json: handle };
|
|
11494
12116
|
}
|
|
11495
12117
|
async connect(handle, req, entry) {
|
|
11496
|
-
const { transport } = this;
|
|
11497
|
-
|
|
12118
|
+
const { transport, local } = this;
|
|
12119
|
+
const port = local?.port(req.url);
|
|
12120
|
+
const open = { protocols: req.protocols, headers: req.headers, signal: entry.abort.signal };
|
|
12121
|
+
if (port === void 0 && (!transport.traits.websocket || !transport.websocket)) {
|
|
12122
|
+
return fail("ENOTSUP", NO_WEBSOCKET2);
|
|
12123
|
+
}
|
|
11498
12124
|
let socket;
|
|
11499
12125
|
try {
|
|
11500
|
-
socket = await transport.websocket({
|
|
12126
|
+
socket = port === void 0 ? await transport.websocket({
|
|
11501
12127
|
url: toHostLoopback(req.url),
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
signal: entry.abort.signal
|
|
11505
|
-
});
|
|
12128
|
+
...open
|
|
12129
|
+
}) : await local.websocket({ url: req.url, ...open }, port);
|
|
11506
12130
|
} catch (e) {
|
|
11507
12131
|
return fail(errnoOf(e), message(e));
|
|
11508
12132
|
}
|
|
@@ -11510,7 +12134,7 @@ var WsHandles = class {
|
|
|
11510
12134
|
socket.close(1001, "the program went away");
|
|
11511
12135
|
return fail("EBADF", `no websocket ${handle}`);
|
|
11512
12136
|
}
|
|
11513
|
-
if (socket.protocol && !(req.protocols.includes(socket.protocol) &&
|
|
12137
|
+
if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN4.test(socket.protocol))) {
|
|
11514
12138
|
socket.close(1002, "subprotocol not offered");
|
|
11515
12139
|
return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
|
|
11516
12140
|
}
|
|
@@ -11573,6 +12197,10 @@ var EMPTY = new Uint8Array(0);
|
|
|
11573
12197
|
function message2(e) {
|
|
11574
12198
|
return e instanceof Error ? e.message : String(e);
|
|
11575
12199
|
}
|
|
12200
|
+
function codeOf2(e) {
|
|
12201
|
+
const code = e?.code;
|
|
12202
|
+
return typeof code === "string" && /^E[A-Z]+$/.test(code) ? code : "EIO";
|
|
12203
|
+
}
|
|
11576
12204
|
async function* once(bytes) {
|
|
11577
12205
|
yield bytes;
|
|
11578
12206
|
}
|
|
@@ -11582,10 +12210,12 @@ var HttpHandles = class {
|
|
|
11582
12210
|
nextHandle = 0;
|
|
11583
12211
|
ws;
|
|
11584
12212
|
owner;
|
|
11585
|
-
|
|
12213
|
+
local;
|
|
12214
|
+
constructor(transport, owner, local) {
|
|
11586
12215
|
this.transport = transport;
|
|
11587
12216
|
this.owner = owner;
|
|
11588
|
-
this.
|
|
12217
|
+
this.local = local;
|
|
12218
|
+
this.ws = new WsHandles(transport, () => ++this.nextHandle, local);
|
|
11589
12219
|
}
|
|
11590
12220
|
syscall(req) {
|
|
11591
12221
|
if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
|
|
@@ -11593,10 +12223,13 @@ var HttpHandles = class {
|
|
|
11593
12223
|
if (req.op === "net-open") return Promise.resolve(this.opened(req));
|
|
11594
12224
|
if (req.op === "net-head") return this.head(req.handle);
|
|
11595
12225
|
if (req.op === "net-read") return this.read(req.handle, req.max);
|
|
11596
|
-
if (req.op === "net-traits") return Promise.resolve(this.traits());
|
|
12226
|
+
if (req.op === "net-traits") return Promise.resolve(this.traits(req.url));
|
|
11597
12227
|
return this.close(req.handle);
|
|
11598
12228
|
}
|
|
11599
|
-
traits() {
|
|
12229
|
+
traits(url) {
|
|
12230
|
+
if (url !== void 0 && this.local?.port(url) !== void 0) {
|
|
12231
|
+
return { ok: true, kind: "json", json: LOOPBACK_TRAITS };
|
|
12232
|
+
}
|
|
11600
12233
|
const { unavailable, crossOrigin = "any", ...rest2 } = this.transport.traits;
|
|
11601
12234
|
if (unavailable) return { ok: false, errno: "ENETUNREACH", message: NO_TRANSPORT };
|
|
11602
12235
|
return { ok: true, kind: "json", json: { ...rest2, crossOrigin } };
|
|
@@ -11627,28 +12260,30 @@ var HttpHandles = class {
|
|
|
11627
12260
|
return { handle, head: entry.head };
|
|
11628
12261
|
}
|
|
11629
12262
|
async respond(handle, req, entry) {
|
|
12263
|
+
const port = this.local?.port(req.url);
|
|
11630
12264
|
try {
|
|
11631
|
-
const
|
|
11632
|
-
|
|
11633
|
-
|
|
11634
|
-
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
);
|
|
12265
|
+
const target = port === void 0 ? toHostLoopback(req.url) : req.url;
|
|
12266
|
+
const request2 = {
|
|
12267
|
+
url: target,
|
|
12268
|
+
method: req.method,
|
|
12269
|
+
headers: req.headers,
|
|
12270
|
+
...req.body ? { body: req.body } : {},
|
|
12271
|
+
signal: entry.abort.signal
|
|
12272
|
+
};
|
|
12273
|
+
const response = port === void 0 ? await this.transport.fetch(request2, this.owner) : await this.local.fetch(request2, port);
|
|
11641
12274
|
entry.body = response.body[Symbol.asyncIterator]();
|
|
11642
12275
|
entry.cancel = () => response.cancel();
|
|
11643
12276
|
if (!this.open.has(handle)) await entry.cancel().catch(() => void 0);
|
|
11644
|
-
const
|
|
12277
|
+
const shown = response.url && response.url !== target ? response.url : req.url;
|
|
12278
|
+
const head = this.headOf(handle, shown, response);
|
|
11645
12279
|
return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
|
|
11646
12280
|
} catch (e) {
|
|
11647
12281
|
const text = message2(e);
|
|
12282
|
+
if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
|
|
11648
12283
|
const status = e.status;
|
|
11649
12284
|
if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
|
|
11650
|
-
const
|
|
11651
|
-
return { ok: false, errno, message: text };
|
|
12285
|
+
const errno2 = text === NO_TRANSPORT ? "ENETUNREACH" : "ECONNREFUSED";
|
|
12286
|
+
return { ok: false, errno: errno2, message: text };
|
|
11652
12287
|
}
|
|
11653
12288
|
entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
|
|
11654
12289
|
return this.headOf(handle, req.url, {
|
|
@@ -12489,7 +13124,9 @@ async function socketSyscall(req, proc) {
|
|
|
12489
13124
|
case "sock-accept":
|
|
12490
13125
|
return ok(await accept(proc, req.fd, req.nonblock));
|
|
12491
13126
|
case "sock-connect": {
|
|
12492
|
-
const
|
|
13127
|
+
const socket = socketAt(fds, req.fd);
|
|
13128
|
+
const stamp = net.facadeAt(req.addr) && await proc.stamp?.();
|
|
13129
|
+
const dialing = socket.connect(req.addr, true, proc.cred?.(), stamp || void 0);
|
|
12493
13130
|
if (req.nonblock) {
|
|
12494
13131
|
dialing?.catch(() => void 0);
|
|
12495
13132
|
throw new KernelError("EINPROGRESS");
|
|
@@ -13668,6 +14305,9 @@ var WasmProcess = class {
|
|
|
13668
14305
|
if (channel) channel.stamp = stamp;
|
|
13669
14306
|
}
|
|
13670
14307
|
}
|
|
14308
|
+
connectorStamp() {
|
|
14309
|
+
return this.sealedCaller();
|
|
14310
|
+
}
|
|
13671
14311
|
async sealedCaller() {
|
|
13672
14312
|
const shown = await this.options.identity?.();
|
|
13673
14313
|
const broker = this.options.sealed;
|
|
@@ -14006,7 +14646,8 @@ var WasmProcess = class {
|
|
|
14006
14646
|
net: this.net,
|
|
14007
14647
|
blocking: () => this.blockingSignal(cancelled),
|
|
14008
14648
|
resolve: (name2, family, blocking) => resolver.resolve(name2, family, blocking),
|
|
14009
|
-
cred: () => ({ pid: this.pid, uid: this.cred.euid, gid: this.cred.egid })
|
|
14649
|
+
cred: () => ({ pid: this.pid, uid: this.cred.euid, gid: this.cred.egid }),
|
|
14650
|
+
stamp: () => this.connectorStamp()
|
|
14010
14651
|
});
|
|
14011
14652
|
}
|
|
14012
14653
|
tty(fd) {
|
|
@@ -15378,7 +16019,7 @@ function scrubberOf(secrets) {
|
|
|
15378
16019
|
const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
|
|
15379
16020
|
return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
|
|
15380
16021
|
}
|
|
15381
|
-
async function
|
|
16022
|
+
async function writeAll3(file, bytes) {
|
|
15382
16023
|
const write = file.write;
|
|
15383
16024
|
for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
|
|
15384
16025
|
}
|
|
@@ -15412,7 +16053,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
15412
16053
|
const later = (bytes) => {
|
|
15413
16054
|
if (bytes.length === 0) return;
|
|
15414
16055
|
pending += 1;
|
|
15415
|
-
tail = tail.then(() =>
|
|
16056
|
+
tail = tail.then(() => writeAll3(file, bytes)).catch((err) => {
|
|
15416
16057
|
if (failed3 === void 0) failed3 = err;
|
|
15417
16058
|
}).finally(() => {
|
|
15418
16059
|
pending -= 1;
|
|
@@ -16043,8 +16684,8 @@ var FSA_CAPABILITIES = {
|
|
|
16043
16684
|
ranges: true,
|
|
16044
16685
|
sessions: true
|
|
16045
16686
|
};
|
|
16046
|
-
var
|
|
16047
|
-
var denied = (err) =>
|
|
16687
|
+
var codeOf3 = (err) => err?.code;
|
|
16688
|
+
var denied = (err) => codeOf3(err) === "EACCES" || err?.name === "NotAllowedError";
|
|
16048
16689
|
function attrOf(st) {
|
|
16049
16690
|
return {
|
|
16050
16691
|
kind: st.isDirectory ? "directory" : st.isSymbolicLink ? "symlink" : "file",
|
|
@@ -16066,7 +16707,7 @@ function kernelFsHandlers(fs) {
|
|
|
16066
16707
|
try {
|
|
16067
16708
|
return await fs.stat(path);
|
|
16068
16709
|
} catch (err) {
|
|
16069
|
-
if (
|
|
16710
|
+
if (codeOf3(err) === "ENOENT") return void 0;
|
|
16070
16711
|
throw err;
|
|
16071
16712
|
}
|
|
16072
16713
|
}
|
|
@@ -16259,7 +16900,7 @@ function parseFstab(text) {
|
|
|
16259
16900
|
});
|
|
16260
16901
|
return lines2;
|
|
16261
16902
|
}
|
|
16262
|
-
function
|
|
16903
|
+
function codeOf4(err) {
|
|
16263
16904
|
const code = err?.code;
|
|
16264
16905
|
return typeof code === "string" ? code : "EIO";
|
|
16265
16906
|
}
|
|
@@ -16297,18 +16938,18 @@ async function mountLine({ line, spec, invalid }, mount, signal, retries, discar
|
|
|
16297
16938
|
break;
|
|
16298
16939
|
} catch (err) {
|
|
16299
16940
|
failed3 = err;
|
|
16300
|
-
if (FINAL.has(
|
|
16941
|
+
if (FINAL.has(codeOf4(err)) || attempt === retries.length) break;
|
|
16301
16942
|
await wait(retries[attempt], signal);
|
|
16302
16943
|
}
|
|
16303
16944
|
}
|
|
16304
|
-
const code =
|
|
16945
|
+
const code = codeOf4(failed3);
|
|
16305
16946
|
return { line, target: spec.target, code, error: String(failed3?.message ?? code) };
|
|
16306
16947
|
}
|
|
16307
16948
|
|
|
16308
16949
|
// src/mount/hostfs.ts
|
|
16309
16950
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
16310
|
-
var
|
|
16311
|
-
var unreachable = (err) =>
|
|
16951
|
+
var codeOf5 = (err) => err?.code;
|
|
16952
|
+
var unreachable = (err) => codeOf5(err) === "ENOTCONN";
|
|
16312
16953
|
function rel(path) {
|
|
16313
16954
|
return path.replace(/^\/+/, "");
|
|
16314
16955
|
}
|
|
@@ -16368,9 +17009,9 @@ var HostfsClient = class {
|
|
|
16368
17009
|
throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
|
|
16369
17010
|
return this.send(path, init, false, true);
|
|
16370
17011
|
}
|
|
16371
|
-
const
|
|
17012
|
+
const errno2 = response.headers.get("X-Hostfs-Errno") ?? "EIO";
|
|
16372
17013
|
const body = await response.json().catch(() => ({}));
|
|
16373
|
-
throw fsError2(
|
|
17014
|
+
throw fsError2(errno2, typeof body.message === "string" ? body.message : errno2);
|
|
16374
17015
|
}
|
|
16375
17016
|
async call(op, body = {}, handle = false) {
|
|
16376
17017
|
const response = await this.send(
|
|
@@ -16485,7 +17126,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
16485
17126
|
try {
|
|
16486
17127
|
return { opened: await opening2(), restore: void 0 };
|
|
16487
17128
|
} catch (err) {
|
|
16488
|
-
if (!flags2.write ||
|
|
17129
|
+
if (!flags2.write || codeOf5(err) !== "EACCES") throw err;
|
|
16489
17130
|
const was = await client.call("stat", { path: rel(path) });
|
|
16490
17131
|
const mode = was.mode ?? 0;
|
|
16491
17132
|
if (mode & 128) throw err;
|
|
@@ -17398,8 +18039,8 @@ var RULE = new RegExp(
|
|
|
17398
18039
|
var TERM = /^[A-Za-z0-9._+-]+$/;
|
|
17399
18040
|
var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
17400
18041
|
var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
|
|
17401
|
-
function failed2(
|
|
17402
|
-
return { error, errno };
|
|
18042
|
+
function failed2(errno2, error) {
|
|
18043
|
+
return { error, errno: errno2 };
|
|
17403
18044
|
}
|
|
17404
18045
|
function unescaped(double) {
|
|
17405
18046
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
@@ -18010,6 +18651,7 @@ var Launcher = class {
|
|
|
18010
18651
|
);
|
|
18011
18652
|
net = new LoopbackNet();
|
|
18012
18653
|
cdp;
|
|
18654
|
+
facades;
|
|
18013
18655
|
ca;
|
|
18014
18656
|
transport;
|
|
18015
18657
|
uplink;
|
|
@@ -18084,10 +18726,16 @@ var Launcher = class {
|
|
|
18084
18726
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
18085
18727
|
this.appended = appendedRoots(options.commands);
|
|
18086
18728
|
this.cdp = new CdpHosts(options.cdp);
|
|
18729
|
+
this.facades = new FacadeHosts(options.facades);
|
|
18087
18730
|
const hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
18088
18731
|
if (!isHostname(hostname)) throw new Error(`not a host name: ${String(hostname)}`);
|
|
18089
18732
|
this.hostname = hostname;
|
|
18090
|
-
this.env = {
|
|
18733
|
+
this.env = {
|
|
18734
|
+
...networkEnv(hostname),
|
|
18735
|
+
SLICC_CDP_URL: this.cdp.url,
|
|
18736
|
+
...this.facades.env(),
|
|
18737
|
+
...options.env
|
|
18738
|
+
};
|
|
18091
18739
|
this.ca = kernelCa(options.caStore ?? memoryCaStore());
|
|
18092
18740
|
this.transport = replayingHops(options.transport ?? missingTransport());
|
|
18093
18741
|
this.announcedTraits = JSON.stringify(this.transport.traits);
|
|
@@ -18102,6 +18750,7 @@ var Launcher = class {
|
|
|
18102
18750
|
ca: this.ca
|
|
18103
18751
|
});
|
|
18104
18752
|
enableCdp(this.net, this.cdp);
|
|
18753
|
+
this.facades.enable(this.net);
|
|
18105
18754
|
const fs = withCommandDirs(
|
|
18106
18755
|
this.base,
|
|
18107
18756
|
async () => new Set((await this.commands()).keys()),
|
|
@@ -18685,6 +19334,12 @@ var Launcher = class {
|
|
|
18685
19334
|
groupSession(pgid) {
|
|
18686
19335
|
return this.jobs.sessionOf(pgid);
|
|
18687
19336
|
}
|
|
19337
|
+
loopbackFor(pid) {
|
|
19338
|
+
return new KernelLoopback(this.net, () => {
|
|
19339
|
+
const cred = this.credOf(pid);
|
|
19340
|
+
return { pid, uid: cred.euid, gid: cred.egid };
|
|
19341
|
+
});
|
|
19342
|
+
}
|
|
18688
19343
|
treeScrubs(req) {
|
|
18689
19344
|
const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
|
|
18690
19345
|
return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
|
|
@@ -18762,7 +19417,7 @@ var Launcher = class {
|
|
|
18762
19417
|
jobs: this.jobs,
|
|
18763
19418
|
ptys: this.ptys,
|
|
18764
19419
|
net: this.net,
|
|
18765
|
-
http: new HttpHandles(this.transport, pid),
|
|
19420
|
+
http: new HttpHandles(this.transport, pid, this.loopbackFor(pid)),
|
|
18766
19421
|
locks: this.locks,
|
|
18767
19422
|
resolver: this.resolver,
|
|
18768
19423
|
onReap: (child) => this.reaped(child, pid),
|
|
@@ -19035,6 +19690,9 @@ var Launcher = class {
|
|
|
19035
19690
|
}
|
|
19036
19691
|
};
|
|
19037
19692
|
}
|
|
19693
|
+
connectorStamp(pid) {
|
|
19694
|
+
return this.sealed.stamp(this.identityOf(pid), this.credOf(pid), this.tagOf(pid));
|
|
19695
|
+
}
|
|
19038
19696
|
identityOf(pid) {
|
|
19039
19697
|
const parent = this.jobs.shownParent(pid);
|
|
19040
19698
|
const alive = parent !== void 0 && this.running(parent);
|
|
@@ -19706,6 +20364,20 @@ function cdpOption(req, port) {
|
|
|
19706
20364
|
)
|
|
19707
20365
|
};
|
|
19708
20366
|
}
|
|
20367
|
+
function pageHooks(req, port) {
|
|
20368
|
+
return { ...cdpOption(req, port), ...facadesOption(req, port) };
|
|
20369
|
+
}
|
|
20370
|
+
function facadesOption(req, port) {
|
|
20371
|
+
if (!req.facades) return {};
|
|
20372
|
+
const hook = portFacadeHook(
|
|
20373
|
+
(request2, facade) => port.postMessage({ facadeOpen: request2, port: facade }, [facade])
|
|
20374
|
+
);
|
|
20375
|
+
const facades = {};
|
|
20376
|
+
for (const [name2, spec] of Object.entries(req.facades)) {
|
|
20377
|
+
facades[name2] = { port: spec.port, ...spec.hook ? { hook } : {} };
|
|
20378
|
+
}
|
|
20379
|
+
return { facades };
|
|
20380
|
+
}
|
|
19709
20381
|
function remotes(port, req) {
|
|
19710
20382
|
return {
|
|
19711
20383
|
...req.transport ? { transport: new RemoteTransport(port, req.transport) } : {},
|
|
@@ -19919,7 +20591,7 @@ function serveKernel(port, deps) {
|
|
|
19919
20591
|
...media ? { media } : {},
|
|
19920
20592
|
onMountPending: (medium) => port.postMessage({ medium }),
|
|
19921
20593
|
...req.hostfs ? { hostfs: askGrant } : {},
|
|
19922
|
-
...
|
|
20594
|
+
...pageHooks(req, port),
|
|
19923
20595
|
...mountPolicy(req.processMounts),
|
|
19924
20596
|
caStore: caStore(deps.metadata ? caDb(req) : false)
|
|
19925
20597
|
});
|