@ai-ecoverse/slicc-kernel 1.90.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 +2 -1
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +481 -70
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +117 -86
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +498 -87
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +79 -48
- package/dist/process-worker.js.map +3 -3
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -470,8 +470,8 @@ async function access(fs, cred, path, want, options = {}) {
|
|
|
470
470
|
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
471
471
|
return { ...walked, missing: true };
|
|
472
472
|
}
|
|
473
|
-
function refused(path,
|
|
474
|
-
return fsError(
|
|
473
|
+
function refused(path, errno2 = "EACCES") {
|
|
474
|
+
return fsError(errno2, path);
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
// src/fs/dac.ts
|
|
@@ -531,9 +531,9 @@ var Created = class {
|
|
|
531
531
|
function linked(g, options) {
|
|
532
532
|
return g.links === void 0 ? options : { ...options, links: g.links };
|
|
533
533
|
}
|
|
534
|
-
function deny(g, op, path,
|
|
534
|
+
function deny(g, op, path, errno2 = "EACCES") {
|
|
535
535
|
const tag = g.tag === void 0 ? {} : { tag: g.tag };
|
|
536
|
-
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno, ...tag });
|
|
536
|
+
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno: errno2, ...tag });
|
|
537
537
|
}
|
|
538
538
|
var TTY_MODE = 400;
|
|
539
539
|
function ttyOpenable(g, tty) {
|
|
@@ -1200,7 +1200,7 @@ var WASI_SIGNAL_TO_POSIX = Object.fromEntries(
|
|
|
1200
1200
|
|
|
1201
1201
|
// src/process/wasi-errno.ts
|
|
1202
1202
|
var WASI_ERRNO = {
|
|
1203
|
-
...Object.fromEntries(Object.entries(E).map(([name2,
|
|
1203
|
+
...Object.fromEntries(Object.entries(E).map(([name2, errno2]) => [`E${name2}`, errno2])),
|
|
1204
1204
|
EWOULDBLOCK: E.AGAIN,
|
|
1205
1205
|
ENOTSUP: 138,
|
|
1206
1206
|
EOPNOTSUPP: 138,
|
|
@@ -1297,7 +1297,7 @@ function dialError(e, timedOut, closed) {
|
|
|
1297
1297
|
if (timedOut) return new KernelError("ETIMEDOUT");
|
|
1298
1298
|
if (closed) return new KernelError("ECONNABORTED");
|
|
1299
1299
|
const code = e?.code;
|
|
1300
|
-
const known = DIAL_ERRORS.find((
|
|
1300
|
+
const known = DIAL_ERRORS.find((errno2) => errno2 === code);
|
|
1301
1301
|
return new KernelError(known ?? "EHOSTUNREACH");
|
|
1302
1302
|
}
|
|
1303
1303
|
function isLoopback(host) {
|
|
@@ -2153,11 +2153,11 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
2153
2153
|
};
|
|
2154
2154
|
const sockets = /* @__PURE__ */ new Map();
|
|
2155
2155
|
const credit = /* @__PURE__ */ new Map();
|
|
2156
|
-
const refill = (nid, n,
|
|
2156
|
+
const refill = (nid, n, closing2 = false) => {
|
|
2157
2157
|
const flow = credit.get(nid);
|
|
2158
2158
|
if (!flow) return;
|
|
2159
2159
|
flow.inflight -= n;
|
|
2160
|
-
if (
|
|
2160
|
+
if (closing2 && !flow.closing) {
|
|
2161
2161
|
flow.closing = true;
|
|
2162
2162
|
flow.deadline();
|
|
2163
2163
|
}
|
|
@@ -4797,15 +4797,15 @@ var UnlinkedKeeper = class {
|
|
|
4797
4797
|
wrap(fs = this.fs) {
|
|
4798
4798
|
const keeper = this;
|
|
4799
4799
|
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
4800
|
-
const
|
|
4800
|
+
const counted3 = (st) => {
|
|
4801
4801
|
const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
|
|
4802
4802
|
return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
|
|
4803
4803
|
};
|
|
4804
4804
|
return {
|
|
4805
4805
|
...fs,
|
|
4806
4806
|
links: (path) => mayLink(fs, path),
|
|
4807
|
-
stat: async (path) =>
|
|
4808
|
-
lstat: async (path) =>
|
|
4807
|
+
stat: async (path) => counted3(await fs.stat(path)),
|
|
4808
|
+
lstat: async (path) => counted3(await fs.lstat(path)),
|
|
4809
4809
|
async rm(path, options) {
|
|
4810
4810
|
const target = fs.resolvePath("/", path);
|
|
4811
4811
|
const pinned = await keeper.pin(target);
|
|
@@ -4819,7 +4819,7 @@ var UnlinkedKeeper = class {
|
|
|
4819
4819
|
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
4820
4820
|
},
|
|
4821
4821
|
readdir: async (path) => visible(await fs.readdir(path)),
|
|
4822
|
-
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st &&
|
|
4822
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted3(st)])
|
|
4823
4823
|
};
|
|
4824
4824
|
}
|
|
4825
4825
|
async settle(pinned, op) {
|
|
@@ -5246,6 +5246,9 @@ async function acceptKey(headers) {
|
|
|
5246
5246
|
}
|
|
5247
5247
|
const key = field(headers, "sec-websocket-key") ?? "";
|
|
5248
5248
|
if (!/^[A-Za-z0-9+/]{22}==$/.test(key)) throw new HttpError(400, "bad Sec-WebSocket-Key");
|
|
5249
|
+
return acceptFor(key);
|
|
5250
|
+
}
|
|
5251
|
+
async function acceptFor(key) {
|
|
5249
5252
|
const digest = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(key + GUID));
|
|
5250
5253
|
return btoa(String.fromCharCode(...new Uint8Array(digest)));
|
|
5251
5254
|
}
|
|
@@ -5269,6 +5272,19 @@ function frame(opcode, payload) {
|
|
|
5269
5272
|
out.set(payload, size);
|
|
5270
5273
|
return out;
|
|
5271
5274
|
}
|
|
5275
|
+
function clientFrame(opcode, payload) {
|
|
5276
|
+
const plain = frame(opcode, payload);
|
|
5277
|
+
const head = plain.length - payload.length;
|
|
5278
|
+
const mask = crypto.getRandomValues(new Uint8Array(4));
|
|
5279
|
+
const out = new Uint8Array(plain.length + 4);
|
|
5280
|
+
out.set(plain.subarray(0, head));
|
|
5281
|
+
out[1] = out[1] | 128;
|
|
5282
|
+
out.set(mask, head);
|
|
5283
|
+
for (let i = 0; i < payload.length; i++) {
|
|
5284
|
+
out[head + 4 + i] = payload[i] ^ mask[i & 3];
|
|
5285
|
+
}
|
|
5286
|
+
return out;
|
|
5287
|
+
}
|
|
5272
5288
|
function closeFrame(code, reason = "") {
|
|
5273
5289
|
let bytes = new TextEncoder().encode(reason);
|
|
5274
5290
|
if (bytes.length > 123) {
|
|
@@ -5308,10 +5324,12 @@ var WsReader = class {
|
|
|
5308
5324
|
continuing = false;
|
|
5309
5325
|
binaryMessage = false;
|
|
5310
5326
|
binary;
|
|
5311
|
-
|
|
5327
|
+
fromServer;
|
|
5328
|
+
constructor(incoming, max = MAX_MESSAGE, binary, fromServer = false) {
|
|
5312
5329
|
this.incoming = incoming;
|
|
5313
5330
|
this.max = max;
|
|
5314
5331
|
this.binary = binary;
|
|
5332
|
+
this.fromServer = fromServer;
|
|
5315
5333
|
}
|
|
5316
5334
|
async next(signal) {
|
|
5317
5335
|
for (; ; ) {
|
|
@@ -5323,7 +5341,7 @@ var WsReader = class {
|
|
|
5323
5341
|
const fin = (head[0] & 128) !== 0;
|
|
5324
5342
|
const opcode = head[0] & 15;
|
|
5325
5343
|
if ((head[0] & 112) !== 0) throw new WsError(CLOSE.protocol, "reserved bits are set");
|
|
5326
|
-
|
|
5344
|
+
const masked = this.masking(head[1]);
|
|
5327
5345
|
const length2 = await this.length(head[1] & 127, signal);
|
|
5328
5346
|
const control = opcode >= 8;
|
|
5329
5347
|
if (control && (length2 > 125 || !fin)) {
|
|
@@ -5332,15 +5350,19 @@ var WsReader = class {
|
|
|
5332
5350
|
if (!control && this.size + length2 > this.max) {
|
|
5333
5351
|
throw new WsError(CLOSE.tooBig, `a message is over ${this.max} bytes`);
|
|
5334
5352
|
}
|
|
5335
|
-
const
|
|
5336
|
-
await fill(this.incoming, mask, signal);
|
|
5337
|
-
const payload = new Uint8Array(length2);
|
|
5338
|
-
await fill(this.incoming, payload, signal);
|
|
5339
|
-
for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
|
|
5353
|
+
const payload = await this.payload(length2, masked, signal);
|
|
5340
5354
|
const event = this.take(opcode, fin, payload);
|
|
5341
5355
|
if (event) return event;
|
|
5342
5356
|
}
|
|
5343
5357
|
}
|
|
5358
|
+
masking(second) {
|
|
5359
|
+
const masked = (second & 128) !== 0;
|
|
5360
|
+
if (masked !== this.fromServer) return masked;
|
|
5361
|
+
throw new WsError(
|
|
5362
|
+
CLOSE.protocol,
|
|
5363
|
+
this.fromServer ? "server frames must not be masked" : "client frames must be masked"
|
|
5364
|
+
);
|
|
5365
|
+
}
|
|
5344
5366
|
data(opcode, fin, payload) {
|
|
5345
5367
|
if (opcode === OP.continuation !== this.continuing) {
|
|
5346
5368
|
throw new WsError(CLOSE.protocol, "unexpected continuation frame");
|
|
@@ -5355,6 +5377,14 @@ var WsReader = class {
|
|
|
5355
5377
|
this.size = 0;
|
|
5356
5378
|
return this.binaryMessage ? { kind: "binary", bytes: whole } : { kind: "text", text: decode(whole) };
|
|
5357
5379
|
}
|
|
5380
|
+
async payload(length2, masked, signal) {
|
|
5381
|
+
const mask = new Uint8Array(4);
|
|
5382
|
+
if (masked) await fill(this.incoming, mask, signal);
|
|
5383
|
+
const payload = new Uint8Array(length2);
|
|
5384
|
+
await fill(this.incoming, payload, signal);
|
|
5385
|
+
if (masked) for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
|
|
5386
|
+
return payload;
|
|
5387
|
+
}
|
|
5358
5388
|
async length(short, signal) {
|
|
5359
5389
|
if (short < 126) return short;
|
|
5360
5390
|
const ext = new Uint8Array(short === 126 ? 2 : 8);
|
|
@@ -6007,6 +6037,33 @@ function toHostLoopback(href) {
|
|
|
6007
6037
|
url.hostname = "127.0.0.1";
|
|
6008
6038
|
return url.href;
|
|
6009
6039
|
}
|
|
6040
|
+
var LOOPBACK_V4 = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
6041
|
+
var LOOPBACK_V6 = /* @__PURE__ */ new Set(["::1", "::", "0:0:0:0:0:0:0:1"]);
|
|
6042
|
+
var MAPPED_V4 = /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/;
|
|
6043
|
+
var KERNEL_PORT = /^(\d{1,5})\.kernel\.localhost$/;
|
|
6044
|
+
var SCHEMES = {
|
|
6045
|
+
"http:": 80,
|
|
6046
|
+
"ws:": 80,
|
|
6047
|
+
"https:": 443,
|
|
6048
|
+
"wss:": 443
|
|
6049
|
+
};
|
|
6050
|
+
function isKernelLoopbackHost(hostname) {
|
|
6051
|
+
const host = hostname.replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "").toLowerCase();
|
|
6052
|
+
return host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0" || LOOPBACK_V4.test(host) || LOOPBACK_V6.has(host) || MAPPED_V4.test(host);
|
|
6053
|
+
}
|
|
6054
|
+
function kernelLoopbackPort(href) {
|
|
6055
|
+
let url;
|
|
6056
|
+
try {
|
|
6057
|
+
url = new URL(href);
|
|
6058
|
+
} catch {
|
|
6059
|
+
return void 0;
|
|
6060
|
+
}
|
|
6061
|
+
const fallback = SCHEMES[url.protocol];
|
|
6062
|
+
if (fallback === void 0 || !isKernelLoopbackHost(url.hostname)) return void 0;
|
|
6063
|
+
const named = KERNEL_PORT.exec(url.hostname.replace(/\.$/, "").toLowerCase())?.[1];
|
|
6064
|
+
const port = Number(named ?? (url.port || fallback));
|
|
6065
|
+
return port >= 1 && port <= 65535 ? port : void 0;
|
|
6066
|
+
}
|
|
6010
6067
|
var DEFAULT_HOSTNAME = "slicc";
|
|
6011
6068
|
var HOSTNAME_ADDRESS = "127.0.1.1";
|
|
6012
6069
|
var LIBC_HOSTNAMES = ["emscripten", "wasmer.sh"];
|
|
@@ -6411,10 +6468,10 @@ var RealmProxy = class {
|
|
|
6411
6468
|
encodedBodies: this.options.transport.traits.encodedBodies,
|
|
6412
6469
|
bodiless: bodiless2
|
|
6413
6470
|
});
|
|
6414
|
-
const
|
|
6415
|
-
const keep2 = keepAlive && (bodiless2 ||
|
|
6471
|
+
const chunked3 = !bodiless2 && minor >= 1;
|
|
6472
|
+
const keep2 = keepAlive && (bodiless2 || chunked3);
|
|
6416
6473
|
headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
|
|
6417
|
-
if (
|
|
6474
|
+
if (chunked3) headers.push(["Transfer-Encoding", "chunked"]);
|
|
6418
6475
|
const signal = this.stop.signal;
|
|
6419
6476
|
await conn.write(
|
|
6420
6477
|
responseHead(status, response.statusText || REASON[status] || "", headers),
|
|
@@ -6426,13 +6483,13 @@ var RealmProxy = class {
|
|
|
6426
6483
|
}
|
|
6427
6484
|
try {
|
|
6428
6485
|
for await (const piece of response.body) {
|
|
6429
|
-
if (piece.length > 0) await conn.write(
|
|
6486
|
+
if (piece.length > 0) await conn.write(chunked3 ? chunk(piece) : piece, signal);
|
|
6430
6487
|
}
|
|
6431
6488
|
} catch {
|
|
6432
6489
|
await response.cancel().catch(() => void 0);
|
|
6433
6490
|
return false;
|
|
6434
6491
|
}
|
|
6435
|
-
if (
|
|
6492
|
+
if (chunked3) await conn.write(LAST_CHUNK, signal);
|
|
6436
6493
|
return keep2;
|
|
6437
6494
|
}
|
|
6438
6495
|
async connect(conn, incoming, req, idle) {
|
|
@@ -10007,7 +10064,7 @@ function mountTable(mounts = []) {
|
|
|
10007
10064
|
}
|
|
10008
10065
|
|
|
10009
10066
|
// src/version.ts
|
|
10010
|
-
var KERNEL_VERSION = true ? "1.
|
|
10067
|
+
var KERNEL_VERSION = true ? "1.91.0" : "0.0.0-dev";
|
|
10011
10068
|
var OS_RELEASE = "6.0.0-slicc";
|
|
10012
10069
|
function procVersion() {
|
|
10013
10070
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10595,6 +10652,339 @@ async function hostSyscall(req, ctx) {
|
|
|
10595
10652
|
}
|
|
10596
10653
|
}
|
|
10597
10654
|
|
|
10655
|
+
// src/kernel/net/loopback-transport.ts
|
|
10656
|
+
var LOOPBACK_TRAITS = {
|
|
10657
|
+
manualRedirects: true,
|
|
10658
|
+
encodedBodies: true,
|
|
10659
|
+
maxRequestBody: 64 * 1024 * 1024,
|
|
10660
|
+
crossOrigin: "any",
|
|
10661
|
+
websocket: true
|
|
10662
|
+
};
|
|
10663
|
+
var MAX_HEAD2 = 64 * 1024;
|
|
10664
|
+
var OWN_HEADERS = /* @__PURE__ */ new Set([
|
|
10665
|
+
"host",
|
|
10666
|
+
"connection",
|
|
10667
|
+
"content-length",
|
|
10668
|
+
"transfer-encoding",
|
|
10669
|
+
"upgrade",
|
|
10670
|
+
"sec-websocket-key",
|
|
10671
|
+
"sec-websocket-version",
|
|
10672
|
+
"sec-websocket-protocol",
|
|
10673
|
+
"sec-websocket-extensions"
|
|
10674
|
+
]);
|
|
10675
|
+
var OP_CLOSE = 8;
|
|
10676
|
+
var OP_PONG = 10;
|
|
10677
|
+
var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
10678
|
+
var SAFE_VALUE = /^[^\r\n\0]*$/;
|
|
10679
|
+
var CLOSE_WAIT_MS3 = 5e3;
|
|
10680
|
+
var MAX_UNREAD = 16 * 1024 * 1024;
|
|
10681
|
+
function errno(code, message3) {
|
|
10682
|
+
return Object.assign(new Error(`${code}: ${message3}`), { code });
|
|
10683
|
+
}
|
|
10684
|
+
function requestHead(method, url, headers, extra) {
|
|
10685
|
+
if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
|
|
10686
|
+
const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
10687
|
+
for (const [name2, value] of headers) {
|
|
10688
|
+
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
10689
|
+
if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value)) throw errno("EINVAL", `header ${name2}`);
|
|
10690
|
+
lines2.push(`${name2}: ${value}`);
|
|
10691
|
+
}
|
|
10692
|
+
for (const [name2, value] of extra) lines2.push(`${name2}: ${value}`);
|
|
10693
|
+
return latin1Bytes(`${lines2.join("\r\n")}\r
|
|
10694
|
+
\r
|
|
10695
|
+
`);
|
|
10696
|
+
}
|
|
10697
|
+
function parseHead(bytes) {
|
|
10698
|
+
const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
|
|
10699
|
+
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
10700
|
+
if (!match) throw errno("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
10701
|
+
const headers = [];
|
|
10702
|
+
for (const line of lines2) {
|
|
10703
|
+
const colon = line.indexOf(":");
|
|
10704
|
+
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
10705
|
+
}
|
|
10706
|
+
return { status: Number(match[1]), statusText: match[2] ?? "", headers };
|
|
10707
|
+
}
|
|
10708
|
+
async function readHead(incoming, signal) {
|
|
10709
|
+
for (; ; ) {
|
|
10710
|
+
let raw;
|
|
10711
|
+
try {
|
|
10712
|
+
raw = await incoming.head(MAX_HEAD2, signal);
|
|
10713
|
+
} catch (err) {
|
|
10714
|
+
if (err instanceof KernelError) throw err;
|
|
10715
|
+
throw errno("EPROTO", "the response head is malformed or too large");
|
|
10716
|
+
}
|
|
10717
|
+
if (!raw) throw errno("ECONNRESET", "the connection closed before a response");
|
|
10718
|
+
const head = parseHead(raw);
|
|
10719
|
+
if (head.status < 100 || head.status >= 200 || head.status === 101) return head;
|
|
10720
|
+
}
|
|
10721
|
+
}
|
|
10722
|
+
async function* chunked(incoming, signal) {
|
|
10723
|
+
for (; ; ) {
|
|
10724
|
+
const line = await incoming.line(4096, signal);
|
|
10725
|
+
const size = /^([0-9A-Fa-f]{1,12})[ \t]*(;.*)?$/.exec(line);
|
|
10726
|
+
if (!size) throw errno("EPROTO", "a malformed chunk size");
|
|
10727
|
+
const n = Number.parseInt(size[1], 16);
|
|
10728
|
+
if (n === 0) {
|
|
10729
|
+
while (await incoming.line(MAX_HEAD2, signal) !== "") ;
|
|
10730
|
+
return;
|
|
10731
|
+
}
|
|
10732
|
+
yield await incoming.exactly(n, signal);
|
|
10733
|
+
if (await incoming.line(2, signal) !== "") throw errno("EPROTO", "a malformed chunk");
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10736
|
+
async function* counted(incoming, length2, signal) {
|
|
10737
|
+
for (let left = length2; left > 0; ) {
|
|
10738
|
+
const data = await incoming.some(Math.min(left, 64 * 1024), signal);
|
|
10739
|
+
if (data.length === 0) throw errno("EPROTO", "the body ended early");
|
|
10740
|
+
left -= data.length;
|
|
10741
|
+
yield data;
|
|
10742
|
+
}
|
|
10743
|
+
}
|
|
10744
|
+
async function* untilClose(incoming, signal) {
|
|
10745
|
+
for (; ; ) {
|
|
10746
|
+
const data = await incoming.some(64 * 1024, signal);
|
|
10747
|
+
if (data.length === 0) return;
|
|
10748
|
+
yield data;
|
|
10749
|
+
}
|
|
10750
|
+
}
|
|
10751
|
+
function bodyOf(incoming, head, method, signal) {
|
|
10752
|
+
if (method === "HEAD" || head.status === 204 || head.status === 304) return [];
|
|
10753
|
+
if (fieldTokens(head.headers, "transfer-encoding").includes("chunked")) {
|
|
10754
|
+
return chunked(incoming, signal);
|
|
10755
|
+
}
|
|
10756
|
+
const lengths = fieldValues(head.headers, "content-length");
|
|
10757
|
+
if (lengths.length === 0) return untilClose(incoming, signal);
|
|
10758
|
+
const length2 = lengths[0]?.trim() ?? "";
|
|
10759
|
+
if (!/^\d{1,15}$/.test(length2) || lengths.some((l) => l.trim() !== length2)) {
|
|
10760
|
+
throw errno("EPROTO", `a malformed Content-Length: ${lengths.join(", ")}`);
|
|
10761
|
+
}
|
|
10762
|
+
return counted(incoming, Number(length2), signal);
|
|
10763
|
+
}
|
|
10764
|
+
var KernelLoopback = class {
|
|
10765
|
+
net;
|
|
10766
|
+
cred;
|
|
10767
|
+
constructor(net, cred) {
|
|
10768
|
+
this.net = net;
|
|
10769
|
+
this.cred = cred;
|
|
10770
|
+
}
|
|
10771
|
+
port(url) {
|
|
10772
|
+
return kernelLoopbackPort(url);
|
|
10773
|
+
}
|
|
10774
|
+
connect(url, port, signal) {
|
|
10775
|
+
if (url.protocol === "https:" || url.protocol === "wss:") {
|
|
10776
|
+
throw errno("ENOTSUP", `TLS to the kernel's loopback is not supported: ${url.origin}`);
|
|
10777
|
+
}
|
|
10778
|
+
signal.throwIfAborted();
|
|
10779
|
+
const socket = this.net.socket("inet");
|
|
10780
|
+
try {
|
|
10781
|
+
void socket.connect({ family: "inet", host: "127.0.0.1", port }, false, this.cred());
|
|
10782
|
+
} catch (err) {
|
|
10783
|
+
socket.close();
|
|
10784
|
+
const refused2 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
10785
|
+
throw refused2 ? errno("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
10786
|
+
}
|
|
10787
|
+
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
10788
|
+
return socket;
|
|
10789
|
+
}
|
|
10790
|
+
async fetch(req, port) {
|
|
10791
|
+
const url = new URL(req.url);
|
|
10792
|
+
const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
|
|
10793
|
+
const length2 = body ? [["Content-Length", String(body.length)]] : [];
|
|
10794
|
+
const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
|
|
10795
|
+
const socket = this.connect(url, port, req.signal);
|
|
10796
|
+
try {
|
|
10797
|
+
await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
|
|
10798
|
+
const incoming = new Incoming(socket);
|
|
10799
|
+
const response = await readHead(incoming, req.signal);
|
|
10800
|
+
const chunks2 = bodyOf(incoming, response, req.method, req.signal);
|
|
10801
|
+
return {
|
|
10802
|
+
url: req.url,
|
|
10803
|
+
status: response.status,
|
|
10804
|
+
statusText: response.statusText,
|
|
10805
|
+
headers: response.headers,
|
|
10806
|
+
body: closing(chunks2, socket),
|
|
10807
|
+
cancel: async () => socket.close()
|
|
10808
|
+
};
|
|
10809
|
+
} catch (err) {
|
|
10810
|
+
socket.close();
|
|
10811
|
+
throw err;
|
|
10812
|
+
}
|
|
10813
|
+
}
|
|
10814
|
+
async websocket(req, port) {
|
|
10815
|
+
const url = new URL(req.url);
|
|
10816
|
+
const socket = this.connect(url, port, req.signal);
|
|
10817
|
+
try {
|
|
10818
|
+
const key = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
|
|
10819
|
+
const extra = [
|
|
10820
|
+
["Upgrade", "websocket"],
|
|
10821
|
+
["Connection", "Upgrade"],
|
|
10822
|
+
["Sec-WebSocket-Key", key],
|
|
10823
|
+
["Sec-WebSocket-Version", "13"]
|
|
10824
|
+
];
|
|
10825
|
+
if (req.protocols.length > 0)
|
|
10826
|
+
extra.push(["Sec-WebSocket-Protocol", req.protocols.join(", ")]);
|
|
10827
|
+
await writeAll2(socket, requestHead("GET", url, req.headers, extra), req.signal);
|
|
10828
|
+
const incoming = new Incoming(socket);
|
|
10829
|
+
const head = await readHead(incoming, req.signal);
|
|
10830
|
+
if (head.status !== 101) {
|
|
10831
|
+
throw errno(
|
|
10832
|
+
"EPROTO",
|
|
10833
|
+
`the server answered ${head.status} ${head.statusText}, not an upgrade`
|
|
10834
|
+
);
|
|
10835
|
+
}
|
|
10836
|
+
const upgraded = fieldTokens(head.headers, "upgrade").includes("websocket") && fieldTokens(head.headers, "connection").includes("upgrade");
|
|
10837
|
+
const accept2 = fieldValues(head.headers, "sec-websocket-accept")[0];
|
|
10838
|
+
if (!upgraded || accept2 !== await acceptFor(key)) {
|
|
10839
|
+
throw errno("EPROTO", "the server did not accept the WebSocket key");
|
|
10840
|
+
}
|
|
10841
|
+
const protocol = fieldValues(head.headers, "sec-websocket-protocol")[0] ?? "";
|
|
10842
|
+
return new LoopbackWebSocket(socket, incoming, protocol);
|
|
10843
|
+
} catch (err) {
|
|
10844
|
+
socket.close();
|
|
10845
|
+
throw err;
|
|
10846
|
+
}
|
|
10847
|
+
}
|
|
10848
|
+
};
|
|
10849
|
+
async function writeAll2(socket, bytes, signal) {
|
|
10850
|
+
for (let at = 0; at < bytes.length; ) at += await socket.write(bytes.subarray(at), signal);
|
|
10851
|
+
}
|
|
10852
|
+
async function* closing(chunks2, socket) {
|
|
10853
|
+
try {
|
|
10854
|
+
yield* chunks2;
|
|
10855
|
+
} catch (err) {
|
|
10856
|
+
if (typeof err?.code === "string") throw err;
|
|
10857
|
+
throw errno("EPROTO", err instanceof Error ? err.message : String(err));
|
|
10858
|
+
} finally {
|
|
10859
|
+
socket.close();
|
|
10860
|
+
}
|
|
10861
|
+
}
|
|
10862
|
+
var LoopbackWebSocket = class {
|
|
10863
|
+
protocol;
|
|
10864
|
+
messages;
|
|
10865
|
+
closed;
|
|
10866
|
+
pending = 0;
|
|
10867
|
+
writes = Promise.resolve();
|
|
10868
|
+
sentClose = false;
|
|
10869
|
+
stop = new AbortController();
|
|
10870
|
+
socket;
|
|
10871
|
+
settle;
|
|
10872
|
+
unread = 0;
|
|
10873
|
+
room;
|
|
10874
|
+
constructor(socket, incoming, protocol) {
|
|
10875
|
+
this.socket = socket;
|
|
10876
|
+
this.protocol = protocol;
|
|
10877
|
+
this.closed = new Promise((resolve2) => {
|
|
10878
|
+
this.settle = resolve2;
|
|
10879
|
+
});
|
|
10880
|
+
this.stop.signal.addEventListener("abort", () => this.room?.(), { once: true });
|
|
10881
|
+
const queue = [];
|
|
10882
|
+
let wake;
|
|
10883
|
+
let ended = false;
|
|
10884
|
+
void this.pump(new WsReader(incoming, void 0, true, true), (message3) => {
|
|
10885
|
+
queue.push(message3);
|
|
10886
|
+
this.unread += message3.length;
|
|
10887
|
+
wake?.();
|
|
10888
|
+
}).finally(() => {
|
|
10889
|
+
ended = true;
|
|
10890
|
+
wake?.();
|
|
10891
|
+
});
|
|
10892
|
+
const taken = (message3) => {
|
|
10893
|
+
this.unread -= message3.length;
|
|
10894
|
+
const room = this.room;
|
|
10895
|
+
this.room = void 0;
|
|
10896
|
+
room?.();
|
|
10897
|
+
};
|
|
10898
|
+
this.messages = {
|
|
10899
|
+
[Symbol.asyncIterator]: async function* () {
|
|
10900
|
+
for (; ; ) {
|
|
10901
|
+
const next2 = queue.shift();
|
|
10902
|
+
if (next2 !== void 0) {
|
|
10903
|
+
taken(next2);
|
|
10904
|
+
yield next2;
|
|
10905
|
+
continue;
|
|
10906
|
+
}
|
|
10907
|
+
if (ended) return;
|
|
10908
|
+
await new Promise((resolve2) => {
|
|
10909
|
+
wake = resolve2;
|
|
10910
|
+
});
|
|
10911
|
+
wake = void 0;
|
|
10912
|
+
}
|
|
10913
|
+
}
|
|
10914
|
+
};
|
|
10915
|
+
}
|
|
10916
|
+
async space() {
|
|
10917
|
+
while (this.unread > MAX_UNREAD && !this.stop.signal.aborted) {
|
|
10918
|
+
await new Promise((resolve2) => {
|
|
10919
|
+
this.room = resolve2;
|
|
10920
|
+
});
|
|
10921
|
+
}
|
|
10922
|
+
}
|
|
10923
|
+
get buffered() {
|
|
10924
|
+
return this.pending;
|
|
10925
|
+
}
|
|
10926
|
+
write(bytes) {
|
|
10927
|
+
this.pending += bytes.length;
|
|
10928
|
+
const previous = this.writes;
|
|
10929
|
+
this.writes = (async () => {
|
|
10930
|
+
await previous;
|
|
10931
|
+
try {
|
|
10932
|
+
await writeAll2(this.socket, bytes, this.stop.signal);
|
|
10933
|
+
} catch {
|
|
10934
|
+
this.stop.abort();
|
|
10935
|
+
}
|
|
10936
|
+
this.pending -= bytes.length;
|
|
10937
|
+
})();
|
|
10938
|
+
}
|
|
10939
|
+
async pump(reader, deliver) {
|
|
10940
|
+
let close = { code: 1006, reason: "" };
|
|
10941
|
+
try {
|
|
10942
|
+
for (; ; ) {
|
|
10943
|
+
await this.space();
|
|
10944
|
+
const event = await reader.next(this.stop.signal);
|
|
10945
|
+
if (!event) break;
|
|
10946
|
+
if (event.kind === "text") deliver(event.text);
|
|
10947
|
+
else if (event.kind === "binary") deliver(event.bytes);
|
|
10948
|
+
else if (event.kind === "ping") this.write(clientFrame(OP_PONG, event.payload));
|
|
10949
|
+
else {
|
|
10950
|
+
close = { code: event.code, reason: event.reason };
|
|
10951
|
+
if (!this.sentClose) this.sendClose(event.code === 1005 ? 1e3 : event.code, "");
|
|
10952
|
+
break;
|
|
10953
|
+
}
|
|
10954
|
+
}
|
|
10955
|
+
} catch (err) {
|
|
10956
|
+
if (err instanceof WsError) this.sendClose(err.code, err.message);
|
|
10957
|
+
close = { code: err instanceof WsError ? err.code : 1006, reason: "" };
|
|
10958
|
+
} finally {
|
|
10959
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
10960
|
+
await this.writes;
|
|
10961
|
+
clearTimeout(late);
|
|
10962
|
+
this.socket.close();
|
|
10963
|
+
this.settle(close);
|
|
10964
|
+
}
|
|
10965
|
+
}
|
|
10966
|
+
sendClose(code, reason) {
|
|
10967
|
+
if (this.sentClose) return;
|
|
10968
|
+
this.sentClose = true;
|
|
10969
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
10970
|
+
void this.closed.then(() => clearTimeout(late));
|
|
10971
|
+
const bytes = new TextEncoder().encode(reason).subarray(0, 123);
|
|
10972
|
+
const payload = new Uint8Array(2 + bytes.length);
|
|
10973
|
+
new DataView(payload.buffer).setUint16(0, sendableCode(code) ? code : CLOSE.error);
|
|
10974
|
+
payload.set(bytes, 2);
|
|
10975
|
+
this.write(clientFrame(OP_CLOSE, payload));
|
|
10976
|
+
}
|
|
10977
|
+
send(data) {
|
|
10978
|
+
if (this.sentClose) throw errno("EPIPE", "the websocket is closing");
|
|
10979
|
+
const text = typeof data === "string";
|
|
10980
|
+
const payload = text ? new TextEncoder().encode(data) : data;
|
|
10981
|
+
this.write(clientFrame(text ? OP_TEXT : OP_BINARY, payload));
|
|
10982
|
+
}
|
|
10983
|
+
close(code = 1e3, reason = "") {
|
|
10984
|
+
this.sendClose(code, reason);
|
|
10985
|
+
}
|
|
10986
|
+
};
|
|
10987
|
+
|
|
10598
10988
|
// src/kernel/net/x509.ts
|
|
10599
10989
|
function concat4(parts2) {
|
|
10600
10990
|
const out = new Uint8Array(parts2.reduce((n, p) => n + p.length, 0));
|
|
@@ -11268,7 +11658,7 @@ var WS_OPS = [
|
|
|
11268
11658
|
"net-ws-send",
|
|
11269
11659
|
"net-ws-close"
|
|
11270
11660
|
];
|
|
11271
|
-
var
|
|
11661
|
+
var TOKEN4 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
11272
11662
|
function message(e) {
|
|
11273
11663
|
return e instanceof Error ? e.message : String(e);
|
|
11274
11664
|
}
|
|
@@ -11277,16 +11667,18 @@ function errnoOf(e) {
|
|
|
11277
11667
|
const code = e?.code;
|
|
11278
11668
|
return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
|
|
11279
11669
|
}
|
|
11280
|
-
function fail(
|
|
11281
|
-
return { ok: false, errno, message: text };
|
|
11670
|
+
function fail(errno2, text) {
|
|
11671
|
+
return { ok: false, errno: errno2, message: text };
|
|
11282
11672
|
}
|
|
11283
11673
|
var WsHandles = class {
|
|
11284
11674
|
transport;
|
|
11285
11675
|
open = /* @__PURE__ */ new Map();
|
|
11286
11676
|
next;
|
|
11287
|
-
|
|
11677
|
+
local;
|
|
11678
|
+
constructor(transport, next2, local) {
|
|
11288
11679
|
this.transport = transport;
|
|
11289
11680
|
this.next = next2;
|
|
11681
|
+
this.local = local;
|
|
11290
11682
|
}
|
|
11291
11683
|
owns(handle) {
|
|
11292
11684
|
return this.open.has(handle);
|
|
@@ -11315,16 +11707,18 @@ var WsHandles = class {
|
|
|
11315
11707
|
return { ok: true, kind: "json", json: handle };
|
|
11316
11708
|
}
|
|
11317
11709
|
async connect(handle, req, entry) {
|
|
11318
|
-
const { transport } = this;
|
|
11319
|
-
|
|
11710
|
+
const { transport, local } = this;
|
|
11711
|
+
const port = local?.port(req.url);
|
|
11712
|
+
const open = { protocols: req.protocols, headers: req.headers, signal: entry.abort.signal };
|
|
11713
|
+
if (port === void 0 && (!transport.traits.websocket || !transport.websocket)) {
|
|
11714
|
+
return fail("ENOTSUP", NO_WEBSOCKET2);
|
|
11715
|
+
}
|
|
11320
11716
|
let socket;
|
|
11321
11717
|
try {
|
|
11322
|
-
socket = await transport.websocket({
|
|
11718
|
+
socket = port === void 0 ? await transport.websocket({
|
|
11323
11719
|
url: toHostLoopback(req.url),
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
signal: entry.abort.signal
|
|
11327
|
-
});
|
|
11720
|
+
...open
|
|
11721
|
+
}) : await local.websocket({ url: req.url, ...open }, port);
|
|
11328
11722
|
} catch (e) {
|
|
11329
11723
|
return fail(errnoOf(e), message(e));
|
|
11330
11724
|
}
|
|
@@ -11332,7 +11726,7 @@ var WsHandles = class {
|
|
|
11332
11726
|
socket.close(1001, "the program went away");
|
|
11333
11727
|
return fail("EBADF", `no websocket ${handle}`);
|
|
11334
11728
|
}
|
|
11335
|
-
if (socket.protocol && !(req.protocols.includes(socket.protocol) &&
|
|
11729
|
+
if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN4.test(socket.protocol))) {
|
|
11336
11730
|
socket.close(1002, "subprotocol not offered");
|
|
11337
11731
|
return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
|
|
11338
11732
|
}
|
|
@@ -11395,6 +11789,10 @@ var EMPTY = new Uint8Array(0);
|
|
|
11395
11789
|
function message2(e) {
|
|
11396
11790
|
return e instanceof Error ? e.message : String(e);
|
|
11397
11791
|
}
|
|
11792
|
+
function codeOf2(e) {
|
|
11793
|
+
const code = e?.code;
|
|
11794
|
+
return typeof code === "string" && /^E[A-Z]+$/.test(code) ? code : "EIO";
|
|
11795
|
+
}
|
|
11398
11796
|
async function* once(bytes) {
|
|
11399
11797
|
yield bytes;
|
|
11400
11798
|
}
|
|
@@ -11404,10 +11802,12 @@ var HttpHandles = class {
|
|
|
11404
11802
|
nextHandle = 0;
|
|
11405
11803
|
ws;
|
|
11406
11804
|
owner;
|
|
11407
|
-
|
|
11805
|
+
local;
|
|
11806
|
+
constructor(transport, owner, local) {
|
|
11408
11807
|
this.transport = transport;
|
|
11409
11808
|
this.owner = owner;
|
|
11410
|
-
this.
|
|
11809
|
+
this.local = local;
|
|
11810
|
+
this.ws = new WsHandles(transport, () => ++this.nextHandle, local);
|
|
11411
11811
|
}
|
|
11412
11812
|
syscall(req) {
|
|
11413
11813
|
if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
|
|
@@ -11415,10 +11815,13 @@ var HttpHandles = class {
|
|
|
11415
11815
|
if (req.op === "net-open") return Promise.resolve(this.opened(req));
|
|
11416
11816
|
if (req.op === "net-head") return this.head(req.handle);
|
|
11417
11817
|
if (req.op === "net-read") return this.read(req.handle, req.max);
|
|
11418
|
-
if (req.op === "net-traits") return Promise.resolve(this.traits());
|
|
11818
|
+
if (req.op === "net-traits") return Promise.resolve(this.traits(req.url));
|
|
11419
11819
|
return this.close(req.handle);
|
|
11420
11820
|
}
|
|
11421
|
-
traits() {
|
|
11821
|
+
traits(url) {
|
|
11822
|
+
if (url !== void 0 && this.local?.port(url) !== void 0) {
|
|
11823
|
+
return { ok: true, kind: "json", json: LOOPBACK_TRAITS };
|
|
11824
|
+
}
|
|
11422
11825
|
const { unavailable, crossOrigin = "any", ...rest2 } = this.transport.traits;
|
|
11423
11826
|
if (unavailable) return { ok: false, errno: "ENETUNREACH", message: NO_TRANSPORT };
|
|
11424
11827
|
return { ok: true, kind: "json", json: { ...rest2, crossOrigin } };
|
|
@@ -11449,28 +11852,30 @@ var HttpHandles = class {
|
|
|
11449
11852
|
return { handle, head: entry.head };
|
|
11450
11853
|
}
|
|
11451
11854
|
async respond(handle, req, entry) {
|
|
11855
|
+
const port = this.local?.port(req.url);
|
|
11452
11856
|
try {
|
|
11453
|
-
const
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
);
|
|
11857
|
+
const target = port === void 0 ? toHostLoopback(req.url) : req.url;
|
|
11858
|
+
const request = {
|
|
11859
|
+
url: target,
|
|
11860
|
+
method: req.method,
|
|
11861
|
+
headers: req.headers,
|
|
11862
|
+
...req.body ? { body: req.body } : {},
|
|
11863
|
+
signal: entry.abort.signal
|
|
11864
|
+
};
|
|
11865
|
+
const response = port === void 0 ? await this.transport.fetch(request, this.owner) : await this.local.fetch(request, port);
|
|
11463
11866
|
entry.body = response.body[Symbol.asyncIterator]();
|
|
11464
11867
|
entry.cancel = () => response.cancel();
|
|
11465
11868
|
if (!this.open.has(handle)) await entry.cancel().catch(() => void 0);
|
|
11466
|
-
const
|
|
11869
|
+
const shown = response.url && response.url !== target ? response.url : req.url;
|
|
11870
|
+
const head = this.headOf(handle, shown, response);
|
|
11467
11871
|
return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
|
|
11468
11872
|
} catch (e) {
|
|
11469
11873
|
const text = message2(e);
|
|
11874
|
+
if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
|
|
11470
11875
|
const status = e.status;
|
|
11471
11876
|
if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
|
|
11472
|
-
const
|
|
11473
|
-
return { ok: false, errno, message: text };
|
|
11877
|
+
const errno2 = text === NO_TRANSPORT ? "ENETUNREACH" : "ECONNREFUSED";
|
|
11878
|
+
return { ok: false, errno: errno2, message: text };
|
|
11474
11879
|
}
|
|
11475
11880
|
entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
|
|
11476
11881
|
return this.headOf(handle, req.url, {
|
|
@@ -15306,7 +15711,7 @@ function scrubberOf(secrets) {
|
|
|
15306
15711
|
const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
|
|
15307
15712
|
return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
|
|
15308
15713
|
}
|
|
15309
|
-
async function
|
|
15714
|
+
async function writeAll3(file, bytes) {
|
|
15310
15715
|
const write = file.write;
|
|
15311
15716
|
for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
|
|
15312
15717
|
}
|
|
@@ -15340,7 +15745,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
15340
15745
|
const later = (bytes) => {
|
|
15341
15746
|
if (bytes.length === 0) return;
|
|
15342
15747
|
pending += 1;
|
|
15343
|
-
tail = tail.then(() =>
|
|
15748
|
+
tail = tail.then(() => writeAll3(file, bytes)).catch((err) => {
|
|
15344
15749
|
if (failed3 === void 0) failed3 = err;
|
|
15345
15750
|
}).finally(() => {
|
|
15346
15751
|
pending -= 1;
|
|
@@ -15971,8 +16376,8 @@ var FSA_CAPABILITIES = {
|
|
|
15971
16376
|
ranges: true,
|
|
15972
16377
|
sessions: true
|
|
15973
16378
|
};
|
|
15974
|
-
var
|
|
15975
|
-
var denied = (err) =>
|
|
16379
|
+
var codeOf3 = (err) => err?.code;
|
|
16380
|
+
var denied = (err) => codeOf3(err) === "EACCES" || err?.name === "NotAllowedError";
|
|
15976
16381
|
function attrOf(st) {
|
|
15977
16382
|
return {
|
|
15978
16383
|
kind: st.isDirectory ? "directory" : st.isSymbolicLink ? "symlink" : "file",
|
|
@@ -15994,7 +16399,7 @@ function kernelFsHandlers(fs) {
|
|
|
15994
16399
|
try {
|
|
15995
16400
|
return await fs.stat(path);
|
|
15996
16401
|
} catch (err) {
|
|
15997
|
-
if (
|
|
16402
|
+
if (codeOf3(err) === "ENOENT") return void 0;
|
|
15998
16403
|
throw err;
|
|
15999
16404
|
}
|
|
16000
16405
|
}
|
|
@@ -16187,7 +16592,7 @@ function parseFstab(text) {
|
|
|
16187
16592
|
});
|
|
16188
16593
|
return lines2;
|
|
16189
16594
|
}
|
|
16190
|
-
function
|
|
16595
|
+
function codeOf4(err) {
|
|
16191
16596
|
const code = err?.code;
|
|
16192
16597
|
return typeof code === "string" ? code : "EIO";
|
|
16193
16598
|
}
|
|
@@ -16225,18 +16630,18 @@ async function mountLine({ line, spec, invalid }, mount, signal, retries, discar
|
|
|
16225
16630
|
break;
|
|
16226
16631
|
} catch (err) {
|
|
16227
16632
|
failed3 = err;
|
|
16228
|
-
if (FINAL.has(
|
|
16633
|
+
if (FINAL.has(codeOf4(err)) || attempt === retries.length) break;
|
|
16229
16634
|
await wait(retries[attempt], signal);
|
|
16230
16635
|
}
|
|
16231
16636
|
}
|
|
16232
|
-
const code =
|
|
16637
|
+
const code = codeOf4(failed3);
|
|
16233
16638
|
return { line, target: spec.target, code, error: String(failed3?.message ?? code) };
|
|
16234
16639
|
}
|
|
16235
16640
|
|
|
16236
16641
|
// src/mount/hostfs.ts
|
|
16237
16642
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
16238
|
-
var
|
|
16239
|
-
var unreachable = (err) =>
|
|
16643
|
+
var codeOf5 = (err) => err?.code;
|
|
16644
|
+
var unreachable = (err) => codeOf5(err) === "ENOTCONN";
|
|
16240
16645
|
function rel(path) {
|
|
16241
16646
|
return path.replace(/^\/+/, "");
|
|
16242
16647
|
}
|
|
@@ -16296,9 +16701,9 @@ var HostfsClient = class {
|
|
|
16296
16701
|
throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
|
|
16297
16702
|
return this.send(path, init, false, true);
|
|
16298
16703
|
}
|
|
16299
|
-
const
|
|
16704
|
+
const errno2 = response.headers.get("X-Hostfs-Errno") ?? "EIO";
|
|
16300
16705
|
const body = await response.json().catch(() => ({}));
|
|
16301
|
-
throw fsError2(
|
|
16706
|
+
throw fsError2(errno2, typeof body.message === "string" ? body.message : errno2);
|
|
16302
16707
|
}
|
|
16303
16708
|
async call(op, body = {}, handle = false) {
|
|
16304
16709
|
const response = await this.send(
|
|
@@ -16413,7 +16818,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
16413
16818
|
try {
|
|
16414
16819
|
return { opened: await opening2(), restore: void 0 };
|
|
16415
16820
|
} catch (err) {
|
|
16416
|
-
if (!flags2.write ||
|
|
16821
|
+
if (!flags2.write || codeOf5(err) !== "EACCES") throw err;
|
|
16417
16822
|
const was = await client.call("stat", { path: rel(path) });
|
|
16418
16823
|
const mode = was.mode ?? 0;
|
|
16419
16824
|
if (mode & 128) throw err;
|
|
@@ -16838,7 +17243,7 @@ var PortTransport = class {
|
|
|
16838
17243
|
}
|
|
16839
17244
|
};
|
|
16840
17245
|
var errorOf = (err) => ({
|
|
16841
|
-
code: typeof
|
|
17246
|
+
code: typeof codeOf5(err) === "string" ? codeOf5(err) : "EIO",
|
|
16842
17247
|
message: err?.message ?? String(err)
|
|
16843
17248
|
});
|
|
16844
17249
|
function serveHostfsPort(port, transport) {
|
|
@@ -17429,8 +17834,8 @@ var RULE = new RegExp(
|
|
|
17429
17834
|
var TERM = /^[A-Za-z0-9._+-]+$/;
|
|
17430
17835
|
var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
17431
17836
|
var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
|
|
17432
|
-
function failed2(
|
|
17433
|
-
return { error, errno };
|
|
17837
|
+
function failed2(errno2, error) {
|
|
17838
|
+
return { error, errno: errno2 };
|
|
17434
17839
|
}
|
|
17435
17840
|
function unescaped(double) {
|
|
17436
17841
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
@@ -18724,6 +19129,12 @@ var Launcher = class {
|
|
|
18724
19129
|
groupSession(pgid) {
|
|
18725
19130
|
return this.jobs.sessionOf(pgid);
|
|
18726
19131
|
}
|
|
19132
|
+
loopbackFor(pid) {
|
|
19133
|
+
return new KernelLoopback(this.net, () => {
|
|
19134
|
+
const cred = this.credOf(pid);
|
|
19135
|
+
return { pid, uid: cred.euid, gid: cred.egid };
|
|
19136
|
+
});
|
|
19137
|
+
}
|
|
18727
19138
|
treeScrubs(req) {
|
|
18728
19139
|
const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
|
|
18729
19140
|
return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
|
|
@@ -18801,7 +19212,7 @@ var Launcher = class {
|
|
|
18801
19212
|
jobs: this.jobs,
|
|
18802
19213
|
ptys: this.ptys,
|
|
18803
19214
|
net: this.net,
|
|
18804
|
-
http: new HttpHandles(this.transport, pid),
|
|
19215
|
+
http: new HttpHandles(this.transport, pid, this.loopbackFor(pid)),
|
|
18805
19216
|
locks: this.locks,
|
|
18806
19217
|
resolver: this.resolver,
|
|
18807
19218
|
onReap: (child) => this.reaped(child, pid),
|
|
@@ -19983,7 +20394,7 @@ import { connect as tls } from "node:tls";
|
|
|
19983
20394
|
var PROBE_MS = 3e3;
|
|
19984
20395
|
var PASSED = /* @__PURE__ */ new Set(["ECONNREFUSED", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT", "ECONNRESET"]);
|
|
19985
20396
|
var NO_NAME = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "ENODATA"]);
|
|
19986
|
-
function
|
|
20397
|
+
function codeOf6(e) {
|
|
19987
20398
|
const code = e?.code;
|
|
19988
20399
|
return typeof code === "string" ? code : void 0;
|
|
19989
20400
|
}
|
|
@@ -19998,7 +20409,7 @@ function probe(secure, host, port, ms) {
|
|
|
19998
20409
|
const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
|
|
19999
20410
|
socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
|
|
20000
20411
|
socket.once("error", (e) => {
|
|
20001
|
-
const code =
|
|
20412
|
+
const code = codeOf6(e) ?? "";
|
|
20002
20413
|
done4(PASSED.has(code) ? code : "EPROTO");
|
|
20003
20414
|
});
|
|
20004
20415
|
});
|
|
@@ -20011,7 +20422,7 @@ async function diagnose(url, ms = PROBE_MS) {
|
|
|
20011
20422
|
try {
|
|
20012
20423
|
await lookup2(host);
|
|
20013
20424
|
} catch (e) {
|
|
20014
|
-
return NO_NAME.has(
|
|
20425
|
+
return NO_NAME.has(codeOf6(e) ?? "") ? "EAI_NONAME" : void 0;
|
|
20015
20426
|
}
|
|
20016
20427
|
return probe(secure, host, port, ms);
|
|
20017
20428
|
}
|
|
@@ -20027,7 +20438,7 @@ function diagnosing(transport, check = diagnose) {
|
|
|
20027
20438
|
try {
|
|
20028
20439
|
return await open(request);
|
|
20029
20440
|
} catch (e) {
|
|
20030
|
-
if (request.signal.aborted || !(e instanceof Error) ||
|
|
20441
|
+
if (request.signal.aborted || !(e instanceof Error) || codeOf6(e)) throw e;
|
|
20031
20442
|
const code = await check(request.url).catch(() => void 0);
|
|
20032
20443
|
throw code ? Object.assign(e, { code }) : e;
|
|
20033
20444
|
}
|
|
@@ -20285,7 +20696,7 @@ function dialStream(port, onClose) {
|
|
|
20285
20696
|
}
|
|
20286
20697
|
|
|
20287
20698
|
// src/client/loopback-fetch.ts
|
|
20288
|
-
var
|
|
20699
|
+
var OWN_HEADERS2 = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
|
|
20289
20700
|
var encoder8 = new TextEncoder();
|
|
20290
20701
|
var latin12 = new TextDecoder("latin1");
|
|
20291
20702
|
var ByteSource = class {
|
|
@@ -20332,7 +20743,7 @@ function indexOf(haystack, needle) {
|
|
|
20332
20743
|
}
|
|
20333
20744
|
return -1;
|
|
20334
20745
|
}
|
|
20335
|
-
function
|
|
20746
|
+
function parseHead2(bytes) {
|
|
20336
20747
|
const [statusLine = "", ...lines2] = latin12.decode(bytes).split("\r\n");
|
|
20337
20748
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
20338
20749
|
if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
@@ -20346,7 +20757,7 @@ function parseHead(bytes) {
|
|
|
20346
20757
|
function header3(head, name2) {
|
|
20347
20758
|
return head.headers.find(([key]) => key.toLowerCase() === name2)?.[1];
|
|
20348
20759
|
}
|
|
20349
|
-
async function*
|
|
20760
|
+
async function* chunked2(source) {
|
|
20350
20761
|
for (; ; ) {
|
|
20351
20762
|
const line = await source.until("\r\n");
|
|
20352
20763
|
if (!line) throw errorWithCode("EPROTO: the chunked body ended early");
|
|
@@ -20358,7 +20769,7 @@ async function* chunked(source) {
|
|
|
20358
20769
|
yield data;
|
|
20359
20770
|
}
|
|
20360
20771
|
}
|
|
20361
|
-
async function*
|
|
20772
|
+
async function* counted2(source, length2) {
|
|
20362
20773
|
let left = length2;
|
|
20363
20774
|
while (left > 0) {
|
|
20364
20775
|
const data = await source.some(left);
|
|
@@ -20367,19 +20778,19 @@ async function* counted(source, length2) {
|
|
|
20367
20778
|
yield data;
|
|
20368
20779
|
}
|
|
20369
20780
|
}
|
|
20370
|
-
async function*
|
|
20781
|
+
async function* untilClose2(source) {
|
|
20371
20782
|
for (let data = await source.some(65536); data; data = await source.some(65536)) yield data;
|
|
20372
20783
|
}
|
|
20373
|
-
function
|
|
20784
|
+
function bodyOf2(source, head, method) {
|
|
20374
20785
|
if (method === "HEAD" || head.status === 204 || head.status === 304) return void 0;
|
|
20375
|
-
if (header3(head, "transfer-encoding")?.toLowerCase().includes("chunked")) return
|
|
20786
|
+
if (header3(head, "transfer-encoding")?.toLowerCase().includes("chunked")) return chunked2(source);
|
|
20376
20787
|
const length2 = header3(head, "content-length");
|
|
20377
|
-
if (length2 === void 0) return
|
|
20788
|
+
if (length2 === void 0) return untilClose2(source);
|
|
20378
20789
|
const size = Number(length2);
|
|
20379
20790
|
if (!/^\d+$/.test(length2.trim()) || !Number.isSafeInteger(size)) {
|
|
20380
20791
|
throw errorWithCode(`EPROTO: a malformed Content-Length: ${length2}`);
|
|
20381
20792
|
}
|
|
20382
|
-
return
|
|
20793
|
+
return counted2(source, size);
|
|
20383
20794
|
}
|
|
20384
20795
|
function stream(chunks2, socket, signal) {
|
|
20385
20796
|
return new ReadableStream({
|
|
@@ -20408,7 +20819,7 @@ async function requestBytes(request) {
|
|
|
20408
20819
|
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
|
|
20409
20820
|
const lines2 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
20410
20821
|
request.headers.forEach((value, name2) => {
|
|
20411
|
-
if (!
|
|
20822
|
+
if (!OWN_HEADERS2.has(name2)) lines2.push(`${name2}: ${value}`);
|
|
20412
20823
|
});
|
|
20413
20824
|
if (body) lines2.push(`Content-Length: ${body.length}`);
|
|
20414
20825
|
lines2.push("Connection: close", "", "");
|
|
@@ -20436,9 +20847,9 @@ async function loopbackFetch(dial, input, options) {
|
|
|
20436
20847
|
do {
|
|
20437
20848
|
const raw = await source.until("\r\n\r\n");
|
|
20438
20849
|
if (!raw) throw errorWithCode("ECONNRESET: the connection closed before a response");
|
|
20439
|
-
head =
|
|
20850
|
+
head = parseHead2(raw);
|
|
20440
20851
|
} while (head.status >= 100 && head.status < 200);
|
|
20441
|
-
const chunks2 =
|
|
20852
|
+
const chunks2 = bodyOf2(source, head, request.method);
|
|
20442
20853
|
if (!chunks2) socket.close();
|
|
20443
20854
|
return new Response(chunks2 ? stream(chunks2, socket, options.signal) : null, {
|
|
20444
20855
|
status: head.status,
|