@ai-ecoverse/slicc-kernel 1.90.0 → 1.92.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 +6 -3
- package/dist/driver-worker.js +9 -7
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +510 -77
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +195 -109
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +527 -94
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +157 -71
- package/dist/process-worker.js.map +3 -3
- package/dist/testing.js.map +1 -1
- package/package.json +3 -3
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) {
|
|
@@ -9102,6 +9159,7 @@ async function storageUsage() {
|
|
|
9102
9159
|
if (typeof estimate?.quota !== "number") return null;
|
|
9103
9160
|
return { quota: estimate.quota, usage: estimate.usage ?? 0 };
|
|
9104
9161
|
}
|
|
9162
|
+
var PLAIN_DIR_MODE = 493;
|
|
9105
9163
|
var done = { ok: true, kind: "void" };
|
|
9106
9164
|
function json2(value) {
|
|
9107
9165
|
return { ok: true, kind: "json", json: value };
|
|
@@ -9111,6 +9169,11 @@ function ownerFor2(entry, mode) {
|
|
|
9111
9169
|
if (!cred) return void 0;
|
|
9112
9170
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
9113
9171
|
}
|
|
9172
|
+
function plainDirMode(entry, mode) {
|
|
9173
|
+
if (entry.cred || mode === void 0) return void 0;
|
|
9174
|
+
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
9175
|
+
return wanted === PLAIN_DIR_MODE ? void 0 : wanted;
|
|
9176
|
+
}
|
|
9114
9177
|
function mkdirOptions(entry, mode = 511) {
|
|
9115
9178
|
const owner = ownerFor2(entry, mode);
|
|
9116
9179
|
if (!owner) return { recursive: true };
|
|
@@ -9152,9 +9215,13 @@ async function run(entry, path, req) {
|
|
|
9152
9215
|
return json2(await fs.readdir(path));
|
|
9153
9216
|
case "readdir-stat":
|
|
9154
9217
|
return json2(await readdirStat(fs, path));
|
|
9155
|
-
case "mkdir":
|
|
9218
|
+
case "mkdir": {
|
|
9219
|
+
const plain = plainDirMode(entry, req.mode);
|
|
9220
|
+
const fresh2 = plain !== void 0 && !await fs.exists(path);
|
|
9156
9221
|
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
9222
|
+
if (fresh2) await fs.chmod(path, plain);
|
|
9157
9223
|
return done;
|
|
9224
|
+
}
|
|
9158
9225
|
case "rm":
|
|
9159
9226
|
await fs.rm(path, { recursive: true });
|
|
9160
9227
|
return done;
|
|
@@ -10007,7 +10074,7 @@ function mountTable(mounts = []) {
|
|
|
10007
10074
|
}
|
|
10008
10075
|
|
|
10009
10076
|
// src/version.ts
|
|
10010
|
-
var KERNEL_VERSION = true ? "1.
|
|
10077
|
+
var KERNEL_VERSION = true ? "1.92.0" : "0.0.0-dev";
|
|
10011
10078
|
var OS_RELEASE = "6.0.0-slicc";
|
|
10012
10079
|
function procVersion() {
|
|
10013
10080
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10595,6 +10662,339 @@ async function hostSyscall(req, ctx) {
|
|
|
10595
10662
|
}
|
|
10596
10663
|
}
|
|
10597
10664
|
|
|
10665
|
+
// src/kernel/net/loopback-transport.ts
|
|
10666
|
+
var LOOPBACK_TRAITS = {
|
|
10667
|
+
manualRedirects: true,
|
|
10668
|
+
encodedBodies: true,
|
|
10669
|
+
maxRequestBody: 64 * 1024 * 1024,
|
|
10670
|
+
crossOrigin: "any",
|
|
10671
|
+
websocket: true
|
|
10672
|
+
};
|
|
10673
|
+
var MAX_HEAD2 = 64 * 1024;
|
|
10674
|
+
var OWN_HEADERS = /* @__PURE__ */ new Set([
|
|
10675
|
+
"host",
|
|
10676
|
+
"connection",
|
|
10677
|
+
"content-length",
|
|
10678
|
+
"transfer-encoding",
|
|
10679
|
+
"upgrade",
|
|
10680
|
+
"sec-websocket-key",
|
|
10681
|
+
"sec-websocket-version",
|
|
10682
|
+
"sec-websocket-protocol",
|
|
10683
|
+
"sec-websocket-extensions"
|
|
10684
|
+
]);
|
|
10685
|
+
var OP_CLOSE = 8;
|
|
10686
|
+
var OP_PONG = 10;
|
|
10687
|
+
var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
10688
|
+
var SAFE_VALUE = /^[^\r\n\0]*$/;
|
|
10689
|
+
var CLOSE_WAIT_MS3 = 5e3;
|
|
10690
|
+
var MAX_UNREAD = 16 * 1024 * 1024;
|
|
10691
|
+
function errno(code, message3) {
|
|
10692
|
+
return Object.assign(new Error(`${code}: ${message3}`), { code });
|
|
10693
|
+
}
|
|
10694
|
+
function requestHead(method, url, headers, extra) {
|
|
10695
|
+
if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
|
|
10696
|
+
const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
10697
|
+
for (const [name2, value] of headers) {
|
|
10698
|
+
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
10699
|
+
if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value)) throw errno("EINVAL", `header ${name2}`);
|
|
10700
|
+
lines2.push(`${name2}: ${value}`);
|
|
10701
|
+
}
|
|
10702
|
+
for (const [name2, value] of extra) lines2.push(`${name2}: ${value}`);
|
|
10703
|
+
return latin1Bytes(`${lines2.join("\r\n")}\r
|
|
10704
|
+
\r
|
|
10705
|
+
`);
|
|
10706
|
+
}
|
|
10707
|
+
function parseHead(bytes) {
|
|
10708
|
+
const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
|
|
10709
|
+
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
10710
|
+
if (!match) throw errno("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
10711
|
+
const headers = [];
|
|
10712
|
+
for (const line of lines2) {
|
|
10713
|
+
const colon = line.indexOf(":");
|
|
10714
|
+
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
10715
|
+
}
|
|
10716
|
+
return { status: Number(match[1]), statusText: match[2] ?? "", headers };
|
|
10717
|
+
}
|
|
10718
|
+
async function readHead(incoming, signal) {
|
|
10719
|
+
for (; ; ) {
|
|
10720
|
+
let raw;
|
|
10721
|
+
try {
|
|
10722
|
+
raw = await incoming.head(MAX_HEAD2, signal);
|
|
10723
|
+
} catch (err) {
|
|
10724
|
+
if (err instanceof KernelError) throw err;
|
|
10725
|
+
throw errno("EPROTO", "the response head is malformed or too large");
|
|
10726
|
+
}
|
|
10727
|
+
if (!raw) throw errno("ECONNRESET", "the connection closed before a response");
|
|
10728
|
+
const head = parseHead(raw);
|
|
10729
|
+
if (head.status < 100 || head.status >= 200 || head.status === 101) return head;
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
async function* chunked(incoming, signal) {
|
|
10733
|
+
for (; ; ) {
|
|
10734
|
+
const line = await incoming.line(4096, signal);
|
|
10735
|
+
const size = /^([0-9A-Fa-f]{1,12})[ \t]*(;.*)?$/.exec(line);
|
|
10736
|
+
if (!size) throw errno("EPROTO", "a malformed chunk size");
|
|
10737
|
+
const n = Number.parseInt(size[1], 16);
|
|
10738
|
+
if (n === 0) {
|
|
10739
|
+
while (await incoming.line(MAX_HEAD2, signal) !== "") ;
|
|
10740
|
+
return;
|
|
10741
|
+
}
|
|
10742
|
+
yield await incoming.exactly(n, signal);
|
|
10743
|
+
if (await incoming.line(2, signal) !== "") throw errno("EPROTO", "a malformed chunk");
|
|
10744
|
+
}
|
|
10745
|
+
}
|
|
10746
|
+
async function* counted(incoming, length2, signal) {
|
|
10747
|
+
for (let left = length2; left > 0; ) {
|
|
10748
|
+
const data = await incoming.some(Math.min(left, 64 * 1024), signal);
|
|
10749
|
+
if (data.length === 0) throw errno("EPROTO", "the body ended early");
|
|
10750
|
+
left -= data.length;
|
|
10751
|
+
yield data;
|
|
10752
|
+
}
|
|
10753
|
+
}
|
|
10754
|
+
async function* untilClose(incoming, signal) {
|
|
10755
|
+
for (; ; ) {
|
|
10756
|
+
const data = await incoming.some(64 * 1024, signal);
|
|
10757
|
+
if (data.length === 0) return;
|
|
10758
|
+
yield data;
|
|
10759
|
+
}
|
|
10760
|
+
}
|
|
10761
|
+
function bodyOf(incoming, head, method, signal) {
|
|
10762
|
+
if (method === "HEAD" || head.status === 204 || head.status === 304) return [];
|
|
10763
|
+
if (fieldTokens(head.headers, "transfer-encoding").includes("chunked")) {
|
|
10764
|
+
return chunked(incoming, signal);
|
|
10765
|
+
}
|
|
10766
|
+
const lengths = fieldValues(head.headers, "content-length");
|
|
10767
|
+
if (lengths.length === 0) return untilClose(incoming, signal);
|
|
10768
|
+
const length2 = lengths[0]?.trim() ?? "";
|
|
10769
|
+
if (!/^\d{1,15}$/.test(length2) || lengths.some((l) => l.trim() !== length2)) {
|
|
10770
|
+
throw errno("EPROTO", `a malformed Content-Length: ${lengths.join(", ")}`);
|
|
10771
|
+
}
|
|
10772
|
+
return counted(incoming, Number(length2), signal);
|
|
10773
|
+
}
|
|
10774
|
+
var KernelLoopback = class {
|
|
10775
|
+
net;
|
|
10776
|
+
cred;
|
|
10777
|
+
constructor(net, cred) {
|
|
10778
|
+
this.net = net;
|
|
10779
|
+
this.cred = cred;
|
|
10780
|
+
}
|
|
10781
|
+
port(url) {
|
|
10782
|
+
return kernelLoopbackPort(url);
|
|
10783
|
+
}
|
|
10784
|
+
connect(url, port, signal) {
|
|
10785
|
+
if (url.protocol === "https:" || url.protocol === "wss:") {
|
|
10786
|
+
throw errno("ENOTSUP", `TLS to the kernel's loopback is not supported: ${url.origin}`);
|
|
10787
|
+
}
|
|
10788
|
+
signal.throwIfAborted();
|
|
10789
|
+
const socket = this.net.socket("inet");
|
|
10790
|
+
try {
|
|
10791
|
+
void socket.connect({ family: "inet", host: "127.0.0.1", port }, false, this.cred());
|
|
10792
|
+
} catch (err) {
|
|
10793
|
+
socket.close();
|
|
10794
|
+
const refused2 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
10795
|
+
throw refused2 ? errno("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
10796
|
+
}
|
|
10797
|
+
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
10798
|
+
return socket;
|
|
10799
|
+
}
|
|
10800
|
+
async fetch(req, port) {
|
|
10801
|
+
const url = new URL(req.url);
|
|
10802
|
+
const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
|
|
10803
|
+
const length2 = body ? [["Content-Length", String(body.length)]] : [];
|
|
10804
|
+
const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
|
|
10805
|
+
const socket = this.connect(url, port, req.signal);
|
|
10806
|
+
try {
|
|
10807
|
+
await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
|
|
10808
|
+
const incoming = new Incoming(socket);
|
|
10809
|
+
const response = await readHead(incoming, req.signal);
|
|
10810
|
+
const chunks2 = bodyOf(incoming, response, req.method, req.signal);
|
|
10811
|
+
return {
|
|
10812
|
+
url: req.url,
|
|
10813
|
+
status: response.status,
|
|
10814
|
+
statusText: response.statusText,
|
|
10815
|
+
headers: response.headers,
|
|
10816
|
+
body: closing(chunks2, socket),
|
|
10817
|
+
cancel: async () => socket.close()
|
|
10818
|
+
};
|
|
10819
|
+
} catch (err) {
|
|
10820
|
+
socket.close();
|
|
10821
|
+
throw err;
|
|
10822
|
+
}
|
|
10823
|
+
}
|
|
10824
|
+
async websocket(req, port) {
|
|
10825
|
+
const url = new URL(req.url);
|
|
10826
|
+
const socket = this.connect(url, port, req.signal);
|
|
10827
|
+
try {
|
|
10828
|
+
const key = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
|
|
10829
|
+
const extra = [
|
|
10830
|
+
["Upgrade", "websocket"],
|
|
10831
|
+
["Connection", "Upgrade"],
|
|
10832
|
+
["Sec-WebSocket-Key", key],
|
|
10833
|
+
["Sec-WebSocket-Version", "13"]
|
|
10834
|
+
];
|
|
10835
|
+
if (req.protocols.length > 0)
|
|
10836
|
+
extra.push(["Sec-WebSocket-Protocol", req.protocols.join(", ")]);
|
|
10837
|
+
await writeAll2(socket, requestHead("GET", url, req.headers, extra), req.signal);
|
|
10838
|
+
const incoming = new Incoming(socket);
|
|
10839
|
+
const head = await readHead(incoming, req.signal);
|
|
10840
|
+
if (head.status !== 101) {
|
|
10841
|
+
throw errno(
|
|
10842
|
+
"EPROTO",
|
|
10843
|
+
`the server answered ${head.status} ${head.statusText}, not an upgrade`
|
|
10844
|
+
);
|
|
10845
|
+
}
|
|
10846
|
+
const upgraded = fieldTokens(head.headers, "upgrade").includes("websocket") && fieldTokens(head.headers, "connection").includes("upgrade");
|
|
10847
|
+
const accept2 = fieldValues(head.headers, "sec-websocket-accept")[0];
|
|
10848
|
+
if (!upgraded || accept2 !== await acceptFor(key)) {
|
|
10849
|
+
throw errno("EPROTO", "the server did not accept the WebSocket key");
|
|
10850
|
+
}
|
|
10851
|
+
const protocol = fieldValues(head.headers, "sec-websocket-protocol")[0] ?? "";
|
|
10852
|
+
return new LoopbackWebSocket(socket, incoming, protocol);
|
|
10853
|
+
} catch (err) {
|
|
10854
|
+
socket.close();
|
|
10855
|
+
throw err;
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
};
|
|
10859
|
+
async function writeAll2(socket, bytes, signal) {
|
|
10860
|
+
for (let at = 0; at < bytes.length; ) at += await socket.write(bytes.subarray(at), signal);
|
|
10861
|
+
}
|
|
10862
|
+
async function* closing(chunks2, socket) {
|
|
10863
|
+
try {
|
|
10864
|
+
yield* chunks2;
|
|
10865
|
+
} catch (err) {
|
|
10866
|
+
if (typeof err?.code === "string") throw err;
|
|
10867
|
+
throw errno("EPROTO", err instanceof Error ? err.message : String(err));
|
|
10868
|
+
} finally {
|
|
10869
|
+
socket.close();
|
|
10870
|
+
}
|
|
10871
|
+
}
|
|
10872
|
+
var LoopbackWebSocket = class {
|
|
10873
|
+
protocol;
|
|
10874
|
+
messages;
|
|
10875
|
+
closed;
|
|
10876
|
+
pending = 0;
|
|
10877
|
+
writes = Promise.resolve();
|
|
10878
|
+
sentClose = false;
|
|
10879
|
+
stop = new AbortController();
|
|
10880
|
+
socket;
|
|
10881
|
+
settle;
|
|
10882
|
+
unread = 0;
|
|
10883
|
+
room;
|
|
10884
|
+
constructor(socket, incoming, protocol) {
|
|
10885
|
+
this.socket = socket;
|
|
10886
|
+
this.protocol = protocol;
|
|
10887
|
+
this.closed = new Promise((resolve2) => {
|
|
10888
|
+
this.settle = resolve2;
|
|
10889
|
+
});
|
|
10890
|
+
this.stop.signal.addEventListener("abort", () => this.room?.(), { once: true });
|
|
10891
|
+
const queue = [];
|
|
10892
|
+
let wake;
|
|
10893
|
+
let ended = false;
|
|
10894
|
+
void this.pump(new WsReader(incoming, void 0, true, true), (message3) => {
|
|
10895
|
+
queue.push(message3);
|
|
10896
|
+
this.unread += message3.length;
|
|
10897
|
+
wake?.();
|
|
10898
|
+
}).finally(() => {
|
|
10899
|
+
ended = true;
|
|
10900
|
+
wake?.();
|
|
10901
|
+
});
|
|
10902
|
+
const taken = (message3) => {
|
|
10903
|
+
this.unread -= message3.length;
|
|
10904
|
+
const room = this.room;
|
|
10905
|
+
this.room = void 0;
|
|
10906
|
+
room?.();
|
|
10907
|
+
};
|
|
10908
|
+
this.messages = {
|
|
10909
|
+
[Symbol.asyncIterator]: async function* () {
|
|
10910
|
+
for (; ; ) {
|
|
10911
|
+
const next2 = queue.shift();
|
|
10912
|
+
if (next2 !== void 0) {
|
|
10913
|
+
taken(next2);
|
|
10914
|
+
yield next2;
|
|
10915
|
+
continue;
|
|
10916
|
+
}
|
|
10917
|
+
if (ended) return;
|
|
10918
|
+
await new Promise((resolve2) => {
|
|
10919
|
+
wake = resolve2;
|
|
10920
|
+
});
|
|
10921
|
+
wake = void 0;
|
|
10922
|
+
}
|
|
10923
|
+
}
|
|
10924
|
+
};
|
|
10925
|
+
}
|
|
10926
|
+
async space() {
|
|
10927
|
+
while (this.unread > MAX_UNREAD && !this.stop.signal.aborted) {
|
|
10928
|
+
await new Promise((resolve2) => {
|
|
10929
|
+
this.room = resolve2;
|
|
10930
|
+
});
|
|
10931
|
+
}
|
|
10932
|
+
}
|
|
10933
|
+
get buffered() {
|
|
10934
|
+
return this.pending;
|
|
10935
|
+
}
|
|
10936
|
+
write(bytes) {
|
|
10937
|
+
this.pending += bytes.length;
|
|
10938
|
+
const previous = this.writes;
|
|
10939
|
+
this.writes = (async () => {
|
|
10940
|
+
await previous;
|
|
10941
|
+
try {
|
|
10942
|
+
await writeAll2(this.socket, bytes, this.stop.signal);
|
|
10943
|
+
} catch {
|
|
10944
|
+
this.stop.abort();
|
|
10945
|
+
}
|
|
10946
|
+
this.pending -= bytes.length;
|
|
10947
|
+
})();
|
|
10948
|
+
}
|
|
10949
|
+
async pump(reader, deliver) {
|
|
10950
|
+
let close = { code: 1006, reason: "" };
|
|
10951
|
+
try {
|
|
10952
|
+
for (; ; ) {
|
|
10953
|
+
await this.space();
|
|
10954
|
+
const event = await reader.next(this.stop.signal);
|
|
10955
|
+
if (!event) break;
|
|
10956
|
+
if (event.kind === "text") deliver(event.text);
|
|
10957
|
+
else if (event.kind === "binary") deliver(event.bytes);
|
|
10958
|
+
else if (event.kind === "ping") this.write(clientFrame(OP_PONG, event.payload));
|
|
10959
|
+
else {
|
|
10960
|
+
close = { code: event.code, reason: event.reason };
|
|
10961
|
+
if (!this.sentClose) this.sendClose(event.code === 1005 ? 1e3 : event.code, "");
|
|
10962
|
+
break;
|
|
10963
|
+
}
|
|
10964
|
+
}
|
|
10965
|
+
} catch (err) {
|
|
10966
|
+
if (err instanceof WsError) this.sendClose(err.code, err.message);
|
|
10967
|
+
close = { code: err instanceof WsError ? err.code : 1006, reason: "" };
|
|
10968
|
+
} finally {
|
|
10969
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
10970
|
+
await this.writes;
|
|
10971
|
+
clearTimeout(late);
|
|
10972
|
+
this.socket.close();
|
|
10973
|
+
this.settle(close);
|
|
10974
|
+
}
|
|
10975
|
+
}
|
|
10976
|
+
sendClose(code, reason) {
|
|
10977
|
+
if (this.sentClose) return;
|
|
10978
|
+
this.sentClose = true;
|
|
10979
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
10980
|
+
void this.closed.then(() => clearTimeout(late));
|
|
10981
|
+
const bytes = new TextEncoder().encode(reason).subarray(0, 123);
|
|
10982
|
+
const payload = new Uint8Array(2 + bytes.length);
|
|
10983
|
+
new DataView(payload.buffer).setUint16(0, sendableCode(code) ? code : CLOSE.error);
|
|
10984
|
+
payload.set(bytes, 2);
|
|
10985
|
+
this.write(clientFrame(OP_CLOSE, payload));
|
|
10986
|
+
}
|
|
10987
|
+
send(data) {
|
|
10988
|
+
if (this.sentClose) throw errno("EPIPE", "the websocket is closing");
|
|
10989
|
+
const text = typeof data === "string";
|
|
10990
|
+
const payload = text ? new TextEncoder().encode(data) : data;
|
|
10991
|
+
this.write(clientFrame(text ? OP_TEXT : OP_BINARY, payload));
|
|
10992
|
+
}
|
|
10993
|
+
close(code = 1e3, reason = "") {
|
|
10994
|
+
this.sendClose(code, reason);
|
|
10995
|
+
}
|
|
10996
|
+
};
|
|
10997
|
+
|
|
10598
10998
|
// src/kernel/net/x509.ts
|
|
10599
10999
|
function concat4(parts2) {
|
|
10600
11000
|
const out = new Uint8Array(parts2.reduce((n, p) => n + p.length, 0));
|
|
@@ -11268,7 +11668,7 @@ var WS_OPS = [
|
|
|
11268
11668
|
"net-ws-send",
|
|
11269
11669
|
"net-ws-close"
|
|
11270
11670
|
];
|
|
11271
|
-
var
|
|
11671
|
+
var TOKEN4 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
11272
11672
|
function message(e) {
|
|
11273
11673
|
return e instanceof Error ? e.message : String(e);
|
|
11274
11674
|
}
|
|
@@ -11277,16 +11677,18 @@ function errnoOf(e) {
|
|
|
11277
11677
|
const code = e?.code;
|
|
11278
11678
|
return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
|
|
11279
11679
|
}
|
|
11280
|
-
function fail(
|
|
11281
|
-
return { ok: false, errno, message: text };
|
|
11680
|
+
function fail(errno2, text) {
|
|
11681
|
+
return { ok: false, errno: errno2, message: text };
|
|
11282
11682
|
}
|
|
11283
11683
|
var WsHandles = class {
|
|
11284
11684
|
transport;
|
|
11285
11685
|
open = /* @__PURE__ */ new Map();
|
|
11286
11686
|
next;
|
|
11287
|
-
|
|
11687
|
+
local;
|
|
11688
|
+
constructor(transport, next2, local) {
|
|
11288
11689
|
this.transport = transport;
|
|
11289
11690
|
this.next = next2;
|
|
11691
|
+
this.local = local;
|
|
11290
11692
|
}
|
|
11291
11693
|
owns(handle) {
|
|
11292
11694
|
return this.open.has(handle);
|
|
@@ -11315,16 +11717,18 @@ var WsHandles = class {
|
|
|
11315
11717
|
return { ok: true, kind: "json", json: handle };
|
|
11316
11718
|
}
|
|
11317
11719
|
async connect(handle, req, entry) {
|
|
11318
|
-
const { transport } = this;
|
|
11319
|
-
|
|
11720
|
+
const { transport, local } = this;
|
|
11721
|
+
const port = local?.port(req.url);
|
|
11722
|
+
const open = { protocols: req.protocols, headers: req.headers, signal: entry.abort.signal };
|
|
11723
|
+
if (port === void 0 && (!transport.traits.websocket || !transport.websocket)) {
|
|
11724
|
+
return fail("ENOTSUP", NO_WEBSOCKET2);
|
|
11725
|
+
}
|
|
11320
11726
|
let socket;
|
|
11321
11727
|
try {
|
|
11322
|
-
socket = await transport.websocket({
|
|
11728
|
+
socket = port === void 0 ? await transport.websocket({
|
|
11323
11729
|
url: toHostLoopback(req.url),
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
signal: entry.abort.signal
|
|
11327
|
-
});
|
|
11730
|
+
...open
|
|
11731
|
+
}) : await local.websocket({ url: req.url, ...open }, port);
|
|
11328
11732
|
} catch (e) {
|
|
11329
11733
|
return fail(errnoOf(e), message(e));
|
|
11330
11734
|
}
|
|
@@ -11332,7 +11736,7 @@ var WsHandles = class {
|
|
|
11332
11736
|
socket.close(1001, "the program went away");
|
|
11333
11737
|
return fail("EBADF", `no websocket ${handle}`);
|
|
11334
11738
|
}
|
|
11335
|
-
if (socket.protocol && !(req.protocols.includes(socket.protocol) &&
|
|
11739
|
+
if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN4.test(socket.protocol))) {
|
|
11336
11740
|
socket.close(1002, "subprotocol not offered");
|
|
11337
11741
|
return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
|
|
11338
11742
|
}
|
|
@@ -11395,6 +11799,10 @@ var EMPTY = new Uint8Array(0);
|
|
|
11395
11799
|
function message2(e) {
|
|
11396
11800
|
return e instanceof Error ? e.message : String(e);
|
|
11397
11801
|
}
|
|
11802
|
+
function codeOf2(e) {
|
|
11803
|
+
const code = e?.code;
|
|
11804
|
+
return typeof code === "string" && /^E[A-Z]+$/.test(code) ? code : "EIO";
|
|
11805
|
+
}
|
|
11398
11806
|
async function* once(bytes) {
|
|
11399
11807
|
yield bytes;
|
|
11400
11808
|
}
|
|
@@ -11404,10 +11812,12 @@ var HttpHandles = class {
|
|
|
11404
11812
|
nextHandle = 0;
|
|
11405
11813
|
ws;
|
|
11406
11814
|
owner;
|
|
11407
|
-
|
|
11815
|
+
local;
|
|
11816
|
+
constructor(transport, owner, local) {
|
|
11408
11817
|
this.transport = transport;
|
|
11409
11818
|
this.owner = owner;
|
|
11410
|
-
this.
|
|
11819
|
+
this.local = local;
|
|
11820
|
+
this.ws = new WsHandles(transport, () => ++this.nextHandle, local);
|
|
11411
11821
|
}
|
|
11412
11822
|
syscall(req) {
|
|
11413
11823
|
if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
|
|
@@ -11415,10 +11825,13 @@ var HttpHandles = class {
|
|
|
11415
11825
|
if (req.op === "net-open") return Promise.resolve(this.opened(req));
|
|
11416
11826
|
if (req.op === "net-head") return this.head(req.handle);
|
|
11417
11827
|
if (req.op === "net-read") return this.read(req.handle, req.max);
|
|
11418
|
-
if (req.op === "net-traits") return Promise.resolve(this.traits());
|
|
11828
|
+
if (req.op === "net-traits") return Promise.resolve(this.traits(req.url));
|
|
11419
11829
|
return this.close(req.handle);
|
|
11420
11830
|
}
|
|
11421
|
-
traits() {
|
|
11831
|
+
traits(url) {
|
|
11832
|
+
if (url !== void 0 && this.local?.port(url) !== void 0) {
|
|
11833
|
+
return { ok: true, kind: "json", json: LOOPBACK_TRAITS };
|
|
11834
|
+
}
|
|
11422
11835
|
const { unavailable, crossOrigin = "any", ...rest2 } = this.transport.traits;
|
|
11423
11836
|
if (unavailable) return { ok: false, errno: "ENETUNREACH", message: NO_TRANSPORT };
|
|
11424
11837
|
return { ok: true, kind: "json", json: { ...rest2, crossOrigin } };
|
|
@@ -11449,28 +11862,30 @@ var HttpHandles = class {
|
|
|
11449
11862
|
return { handle, head: entry.head };
|
|
11450
11863
|
}
|
|
11451
11864
|
async respond(handle, req, entry) {
|
|
11865
|
+
const port = this.local?.port(req.url);
|
|
11452
11866
|
try {
|
|
11453
|
-
const
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
);
|
|
11867
|
+
const target = port === void 0 ? toHostLoopback(req.url) : req.url;
|
|
11868
|
+
const request = {
|
|
11869
|
+
url: target,
|
|
11870
|
+
method: req.method,
|
|
11871
|
+
headers: req.headers,
|
|
11872
|
+
...req.body ? { body: req.body } : {},
|
|
11873
|
+
signal: entry.abort.signal
|
|
11874
|
+
};
|
|
11875
|
+
const response = port === void 0 ? await this.transport.fetch(request, this.owner) : await this.local.fetch(request, port);
|
|
11463
11876
|
entry.body = response.body[Symbol.asyncIterator]();
|
|
11464
11877
|
entry.cancel = () => response.cancel();
|
|
11465
11878
|
if (!this.open.has(handle)) await entry.cancel().catch(() => void 0);
|
|
11466
|
-
const
|
|
11879
|
+
const shown = response.url && response.url !== target ? response.url : req.url;
|
|
11880
|
+
const head = this.headOf(handle, shown, response);
|
|
11467
11881
|
return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
|
|
11468
11882
|
} catch (e) {
|
|
11469
11883
|
const text = message2(e);
|
|
11884
|
+
if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
|
|
11470
11885
|
const status = e.status;
|
|
11471
11886
|
if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
|
|
11472
|
-
const
|
|
11473
|
-
return { ok: false, errno, message: text };
|
|
11887
|
+
const errno2 = text === NO_TRANSPORT ? "ENETUNREACH" : "ECONNREFUSED";
|
|
11888
|
+
return { ok: false, errno: errno2, message: text };
|
|
11474
11889
|
}
|
|
11475
11890
|
entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
|
|
11476
11891
|
return this.headOf(handle, req.url, {
|
|
@@ -13039,6 +13454,7 @@ function aborted(signal) {
|
|
|
13039
13454
|
signal.addEventListener("abort", () => reject(new KernelError("EINTR")), { once: true });
|
|
13040
13455
|
});
|
|
13041
13456
|
}
|
|
13457
|
+
var PLAIN_FILE_MODE = 420;
|
|
13042
13458
|
var JOB_OPS = /* @__PURE__ */ new Set([
|
|
13043
13459
|
"proc-setpgid",
|
|
13044
13460
|
"proc-getpgid",
|
|
@@ -13493,15 +13909,21 @@ var WasmProcess = class {
|
|
|
13493
13909
|
}
|
|
13494
13910
|
if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
|
|
13495
13911
|
const open = { ...req, path };
|
|
13912
|
+
const plain = this.plainMode(req.mode);
|
|
13496
13913
|
if (req.exclusive) {
|
|
13497
|
-
await nodes.createExclusive(path, this.newOwner());
|
|
13914
|
+
await nodes.createExclusive(path, this.newOwner(req.mode));
|
|
13915
|
+
if (plain !== void 0) await this.options.fs.chmod?.(path, plain);
|
|
13498
13916
|
this.fds.installAt(fd, this.openVfsFile(open));
|
|
13499
13917
|
} else if (req.create) {
|
|
13500
13918
|
await nodes.onEntry(path, async () => {
|
|
13501
|
-
const
|
|
13919
|
+
const marks = this.options.owners || plain !== void 0;
|
|
13920
|
+
const fresh2 = marks && !await present2(this.options.fs, path, true);
|
|
13502
13921
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
13503
13922
|
await this.fds.get(fd).file.stat?.();
|
|
13504
|
-
if (fresh2) await this.options.fs.
|
|
13923
|
+
if (fresh2 && plain !== void 0) await this.options.fs.chmod?.(path, plain);
|
|
13924
|
+
else if (fresh2) {
|
|
13925
|
+
await this.options.fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
13926
|
+
}
|
|
13505
13927
|
});
|
|
13506
13928
|
} else {
|
|
13507
13929
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
@@ -13614,9 +14036,14 @@ var WasmProcess = class {
|
|
|
13614
14036
|
if (!tty) throw new KernelError("ENOENT");
|
|
13615
14037
|
return tty;
|
|
13616
14038
|
}
|
|
13617
|
-
|
|
14039
|
+
plainMode(mode) {
|
|
14040
|
+
if (this.options.owners || mode === void 0) return void 0;
|
|
14041
|
+
const wanted = mode & 4095 & ~this.umask;
|
|
14042
|
+
return wanted === PLAIN_FILE_MODE ? void 0 : wanted;
|
|
14043
|
+
}
|
|
14044
|
+
newOwner(mode = 438) {
|
|
13618
14045
|
if (!this.options.owners) return void 0;
|
|
13619
|
-
return { uid: this.cred.euid, gid: this.cred.egid, mode:
|
|
14046
|
+
return { uid: this.cred.euid, gid: this.cred.egid, mode: mode & 4095 & ~this.umask };
|
|
13620
14047
|
}
|
|
13621
14048
|
async openProc(req, path) {
|
|
13622
14049
|
return this.fds.install(await this.procFile(req, path), 3);
|
|
@@ -14356,7 +14783,7 @@ function inherited(opts) {
|
|
|
14356
14783
|
}
|
|
14357
14784
|
function holdingToken(opts, holds, caller) {
|
|
14358
14785
|
return mintSyncFsToken({
|
|
14359
|
-
...opts.owners ? caller : {},
|
|
14786
|
+
...opts.owners ? caller : { umask: caller.umask },
|
|
14360
14787
|
...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
|
|
14361
14788
|
...opts.links !== void 0 ? { links: opts.links } : {},
|
|
14362
14789
|
...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
|
|
@@ -15306,7 +15733,7 @@ function scrubberOf(secrets) {
|
|
|
15306
15733
|
const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
|
|
15307
15734
|
return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
|
|
15308
15735
|
}
|
|
15309
|
-
async function
|
|
15736
|
+
async function writeAll3(file, bytes) {
|
|
15310
15737
|
const write = file.write;
|
|
15311
15738
|
for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
|
|
15312
15739
|
}
|
|
@@ -15340,7 +15767,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
15340
15767
|
const later = (bytes) => {
|
|
15341
15768
|
if (bytes.length === 0) return;
|
|
15342
15769
|
pending += 1;
|
|
15343
|
-
tail = tail.then(() =>
|
|
15770
|
+
tail = tail.then(() => writeAll3(file, bytes)).catch((err) => {
|
|
15344
15771
|
if (failed3 === void 0) failed3 = err;
|
|
15345
15772
|
}).finally(() => {
|
|
15346
15773
|
pending -= 1;
|
|
@@ -15971,8 +16398,8 @@ var FSA_CAPABILITIES = {
|
|
|
15971
16398
|
ranges: true,
|
|
15972
16399
|
sessions: true
|
|
15973
16400
|
};
|
|
15974
|
-
var
|
|
15975
|
-
var denied = (err) =>
|
|
16401
|
+
var codeOf3 = (err) => err?.code;
|
|
16402
|
+
var denied = (err) => codeOf3(err) === "EACCES" || err?.name === "NotAllowedError";
|
|
15976
16403
|
function attrOf(st) {
|
|
15977
16404
|
return {
|
|
15978
16405
|
kind: st.isDirectory ? "directory" : st.isSymbolicLink ? "symlink" : "file",
|
|
@@ -15994,7 +16421,7 @@ function kernelFsHandlers(fs) {
|
|
|
15994
16421
|
try {
|
|
15995
16422
|
return await fs.stat(path);
|
|
15996
16423
|
} catch (err) {
|
|
15997
|
-
if (
|
|
16424
|
+
if (codeOf3(err) === "ENOENT") return void 0;
|
|
15998
16425
|
throw err;
|
|
15999
16426
|
}
|
|
16000
16427
|
}
|
|
@@ -16187,7 +16614,7 @@ function parseFstab(text) {
|
|
|
16187
16614
|
});
|
|
16188
16615
|
return lines2;
|
|
16189
16616
|
}
|
|
16190
|
-
function
|
|
16617
|
+
function codeOf4(err) {
|
|
16191
16618
|
const code = err?.code;
|
|
16192
16619
|
return typeof code === "string" ? code : "EIO";
|
|
16193
16620
|
}
|
|
@@ -16225,18 +16652,18 @@ async function mountLine({ line, spec, invalid }, mount, signal, retries, discar
|
|
|
16225
16652
|
break;
|
|
16226
16653
|
} catch (err) {
|
|
16227
16654
|
failed3 = err;
|
|
16228
|
-
if (FINAL.has(
|
|
16655
|
+
if (FINAL.has(codeOf4(err)) || attempt === retries.length) break;
|
|
16229
16656
|
await wait(retries[attempt], signal);
|
|
16230
16657
|
}
|
|
16231
16658
|
}
|
|
16232
|
-
const code =
|
|
16659
|
+
const code = codeOf4(failed3);
|
|
16233
16660
|
return { line, target: spec.target, code, error: String(failed3?.message ?? code) };
|
|
16234
16661
|
}
|
|
16235
16662
|
|
|
16236
16663
|
// src/mount/hostfs.ts
|
|
16237
16664
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
16238
|
-
var
|
|
16239
|
-
var unreachable = (err) =>
|
|
16665
|
+
var codeOf5 = (err) => err?.code;
|
|
16666
|
+
var unreachable = (err) => codeOf5(err) === "ENOTCONN";
|
|
16240
16667
|
function rel(path) {
|
|
16241
16668
|
return path.replace(/^\/+/, "");
|
|
16242
16669
|
}
|
|
@@ -16296,9 +16723,9 @@ var HostfsClient = class {
|
|
|
16296
16723
|
throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
|
|
16297
16724
|
return this.send(path, init, false, true);
|
|
16298
16725
|
}
|
|
16299
|
-
const
|
|
16726
|
+
const errno2 = response.headers.get("X-Hostfs-Errno") ?? "EIO";
|
|
16300
16727
|
const body = await response.json().catch(() => ({}));
|
|
16301
|
-
throw fsError2(
|
|
16728
|
+
throw fsError2(errno2, typeof body.message === "string" ? body.message : errno2);
|
|
16302
16729
|
}
|
|
16303
16730
|
async call(op, body = {}, handle = false) {
|
|
16304
16731
|
const response = await this.send(
|
|
@@ -16413,7 +16840,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
16413
16840
|
try {
|
|
16414
16841
|
return { opened: await opening2(), restore: void 0 };
|
|
16415
16842
|
} catch (err) {
|
|
16416
|
-
if (!flags2.write ||
|
|
16843
|
+
if (!flags2.write || codeOf5(err) !== "EACCES") throw err;
|
|
16417
16844
|
const was = await client.call("stat", { path: rel(path) });
|
|
16418
16845
|
const mode = was.mode ?? 0;
|
|
16419
16846
|
if (mode & 128) throw err;
|
|
@@ -16838,7 +17265,7 @@ var PortTransport = class {
|
|
|
16838
17265
|
}
|
|
16839
17266
|
};
|
|
16840
17267
|
var errorOf = (err) => ({
|
|
16841
|
-
code: typeof
|
|
17268
|
+
code: typeof codeOf5(err) === "string" ? codeOf5(err) : "EIO",
|
|
16842
17269
|
message: err?.message ?? String(err)
|
|
16843
17270
|
});
|
|
16844
17271
|
function serveHostfsPort(port, transport) {
|
|
@@ -17429,8 +17856,8 @@ var RULE = new RegExp(
|
|
|
17429
17856
|
var TERM = /^[A-Za-z0-9._+-]+$/;
|
|
17430
17857
|
var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
17431
17858
|
var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
|
|
17432
|
-
function failed2(
|
|
17433
|
-
return { error, errno };
|
|
17859
|
+
function failed2(errno2, error) {
|
|
17860
|
+
return { error, errno: errno2 };
|
|
17434
17861
|
}
|
|
17435
17862
|
function unescaped(double) {
|
|
17436
17863
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
@@ -18724,6 +19151,12 @@ var Launcher = class {
|
|
|
18724
19151
|
groupSession(pgid) {
|
|
18725
19152
|
return this.jobs.sessionOf(pgid);
|
|
18726
19153
|
}
|
|
19154
|
+
loopbackFor(pid) {
|
|
19155
|
+
return new KernelLoopback(this.net, () => {
|
|
19156
|
+
const cred = this.credOf(pid);
|
|
19157
|
+
return { pid, uid: cred.euid, gid: cred.egid };
|
|
19158
|
+
});
|
|
19159
|
+
}
|
|
18727
19160
|
treeScrubs(req) {
|
|
18728
19161
|
const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
|
|
18729
19162
|
return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
|
|
@@ -18801,7 +19234,7 @@ var Launcher = class {
|
|
|
18801
19234
|
jobs: this.jobs,
|
|
18802
19235
|
ptys: this.ptys,
|
|
18803
19236
|
net: this.net,
|
|
18804
|
-
http: new HttpHandles(this.transport, pid),
|
|
19237
|
+
http: new HttpHandles(this.transport, pid, this.loopbackFor(pid)),
|
|
18805
19238
|
locks: this.locks,
|
|
18806
19239
|
resolver: this.resolver,
|
|
18807
19240
|
onReap: (child) => this.reaped(child, pid),
|
|
@@ -19983,7 +20416,7 @@ import { connect as tls } from "node:tls";
|
|
|
19983
20416
|
var PROBE_MS = 3e3;
|
|
19984
20417
|
var PASSED = /* @__PURE__ */ new Set(["ECONNREFUSED", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT", "ECONNRESET"]);
|
|
19985
20418
|
var NO_NAME = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "ENODATA"]);
|
|
19986
|
-
function
|
|
20419
|
+
function codeOf6(e) {
|
|
19987
20420
|
const code = e?.code;
|
|
19988
20421
|
return typeof code === "string" ? code : void 0;
|
|
19989
20422
|
}
|
|
@@ -19998,7 +20431,7 @@ function probe(secure, host, port, ms) {
|
|
|
19998
20431
|
const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
|
|
19999
20432
|
socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
|
|
20000
20433
|
socket.once("error", (e) => {
|
|
20001
|
-
const code =
|
|
20434
|
+
const code = codeOf6(e) ?? "";
|
|
20002
20435
|
done4(PASSED.has(code) ? code : "EPROTO");
|
|
20003
20436
|
});
|
|
20004
20437
|
});
|
|
@@ -20011,7 +20444,7 @@ async function diagnose(url, ms = PROBE_MS) {
|
|
|
20011
20444
|
try {
|
|
20012
20445
|
await lookup2(host);
|
|
20013
20446
|
} catch (e) {
|
|
20014
|
-
return NO_NAME.has(
|
|
20447
|
+
return NO_NAME.has(codeOf6(e) ?? "") ? "EAI_NONAME" : void 0;
|
|
20015
20448
|
}
|
|
20016
20449
|
return probe(secure, host, port, ms);
|
|
20017
20450
|
}
|
|
@@ -20027,7 +20460,7 @@ function diagnosing(transport, check = diagnose) {
|
|
|
20027
20460
|
try {
|
|
20028
20461
|
return await open(request);
|
|
20029
20462
|
} catch (e) {
|
|
20030
|
-
if (request.signal.aborted || !(e instanceof Error) ||
|
|
20463
|
+
if (request.signal.aborted || !(e instanceof Error) || codeOf6(e)) throw e;
|
|
20031
20464
|
const code = await check(request.url).catch(() => void 0);
|
|
20032
20465
|
throw code ? Object.assign(e, { code }) : e;
|
|
20033
20466
|
}
|
|
@@ -20285,7 +20718,7 @@ function dialStream(port, onClose) {
|
|
|
20285
20718
|
}
|
|
20286
20719
|
|
|
20287
20720
|
// src/client/loopback-fetch.ts
|
|
20288
|
-
var
|
|
20721
|
+
var OWN_HEADERS2 = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
|
|
20289
20722
|
var encoder8 = new TextEncoder();
|
|
20290
20723
|
var latin12 = new TextDecoder("latin1");
|
|
20291
20724
|
var ByteSource = class {
|
|
@@ -20332,7 +20765,7 @@ function indexOf(haystack, needle) {
|
|
|
20332
20765
|
}
|
|
20333
20766
|
return -1;
|
|
20334
20767
|
}
|
|
20335
|
-
function
|
|
20768
|
+
function parseHead2(bytes) {
|
|
20336
20769
|
const [statusLine = "", ...lines2] = latin12.decode(bytes).split("\r\n");
|
|
20337
20770
|
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
20338
20771
|
if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
@@ -20346,7 +20779,7 @@ function parseHead(bytes) {
|
|
|
20346
20779
|
function header3(head, name2) {
|
|
20347
20780
|
return head.headers.find(([key]) => key.toLowerCase() === name2)?.[1];
|
|
20348
20781
|
}
|
|
20349
|
-
async function*
|
|
20782
|
+
async function* chunked2(source) {
|
|
20350
20783
|
for (; ; ) {
|
|
20351
20784
|
const line = await source.until("\r\n");
|
|
20352
20785
|
if (!line) throw errorWithCode("EPROTO: the chunked body ended early");
|
|
@@ -20358,7 +20791,7 @@ async function* chunked(source) {
|
|
|
20358
20791
|
yield data;
|
|
20359
20792
|
}
|
|
20360
20793
|
}
|
|
20361
|
-
async function*
|
|
20794
|
+
async function* counted2(source, length2) {
|
|
20362
20795
|
let left = length2;
|
|
20363
20796
|
while (left > 0) {
|
|
20364
20797
|
const data = await source.some(left);
|
|
@@ -20367,19 +20800,19 @@ async function* counted(source, length2) {
|
|
|
20367
20800
|
yield data;
|
|
20368
20801
|
}
|
|
20369
20802
|
}
|
|
20370
|
-
async function*
|
|
20803
|
+
async function* untilClose2(source) {
|
|
20371
20804
|
for (let data = await source.some(65536); data; data = await source.some(65536)) yield data;
|
|
20372
20805
|
}
|
|
20373
|
-
function
|
|
20806
|
+
function bodyOf2(source, head, method) {
|
|
20374
20807
|
if (method === "HEAD" || head.status === 204 || head.status === 304) return void 0;
|
|
20375
|
-
if (header3(head, "transfer-encoding")?.toLowerCase().includes("chunked")) return
|
|
20808
|
+
if (header3(head, "transfer-encoding")?.toLowerCase().includes("chunked")) return chunked2(source);
|
|
20376
20809
|
const length2 = header3(head, "content-length");
|
|
20377
|
-
if (length2 === void 0) return
|
|
20810
|
+
if (length2 === void 0) return untilClose2(source);
|
|
20378
20811
|
const size = Number(length2);
|
|
20379
20812
|
if (!/^\d+$/.test(length2.trim()) || !Number.isSafeInteger(size)) {
|
|
20380
20813
|
throw errorWithCode(`EPROTO: a malformed Content-Length: ${length2}`);
|
|
20381
20814
|
}
|
|
20382
|
-
return
|
|
20815
|
+
return counted2(source, size);
|
|
20383
20816
|
}
|
|
20384
20817
|
function stream(chunks2, socket, signal) {
|
|
20385
20818
|
return new ReadableStream({
|
|
@@ -20408,7 +20841,7 @@ async function requestBytes(request) {
|
|
|
20408
20841
|
const body = request.body ? new Uint8Array(await request.arrayBuffer()) : void 0;
|
|
20409
20842
|
const lines2 = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
20410
20843
|
request.headers.forEach((value, name2) => {
|
|
20411
|
-
if (!
|
|
20844
|
+
if (!OWN_HEADERS2.has(name2)) lines2.push(`${name2}: ${value}`);
|
|
20412
20845
|
});
|
|
20413
20846
|
if (body) lines2.push(`Content-Length: ${body.length}`);
|
|
20414
20847
|
lines2.push("Connection: close", "", "");
|
|
@@ -20436,9 +20869,9 @@ async function loopbackFetch(dial, input, options) {
|
|
|
20436
20869
|
do {
|
|
20437
20870
|
const raw = await source.until("\r\n\r\n");
|
|
20438
20871
|
if (!raw) throw errorWithCode("ECONNRESET: the connection closed before a response");
|
|
20439
|
-
head =
|
|
20872
|
+
head = parseHead2(raw);
|
|
20440
20873
|
} while (head.status >= 100 && head.status < 200);
|
|
20441
|
-
const chunks2 =
|
|
20874
|
+
const chunks2 = bodyOf2(source, head, request.method);
|
|
20442
20875
|
if (!chunks2) socket.close();
|
|
20443
20876
|
return new Response(chunks2 ? stream(chunks2, socket, options.signal) : null, {
|
|
20444
20877
|
status: head.status,
|