@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/kernel-worker.js
CHANGED
|
@@ -2299,8 +2299,8 @@ async function access(fs, cred, path, want, options = {}) {
|
|
|
2299
2299
|
if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
|
|
2300
2300
|
return { ...walked, missing: true };
|
|
2301
2301
|
}
|
|
2302
|
-
function refused(path,
|
|
2303
|
-
return fsError(
|
|
2302
|
+
function refused(path, errno2 = "EACCES") {
|
|
2303
|
+
return fsError(errno2, path);
|
|
2304
2304
|
}
|
|
2305
2305
|
|
|
2306
2306
|
// src/fs/dac.ts
|
|
@@ -2360,9 +2360,9 @@ var Created = class {
|
|
|
2360
2360
|
function linked(g, options) {
|
|
2361
2361
|
return g.links === void 0 ? options : { ...options, links: g.links };
|
|
2362
2362
|
}
|
|
2363
|
-
function deny(g, op, path,
|
|
2363
|
+
function deny(g, op, path, errno2 = "EACCES") {
|
|
2364
2364
|
const tag = g.tag === void 0 ? {} : { tag: g.tag };
|
|
2365
|
-
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno, ...tag });
|
|
2365
|
+
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno: errno2, ...tag });
|
|
2366
2366
|
}
|
|
2367
2367
|
var TTY_MODE = 400;
|
|
2368
2368
|
function ttyOpenable(g, tty) {
|
|
@@ -3029,7 +3029,7 @@ var WASI_SIGNAL_TO_POSIX = Object.fromEntries(
|
|
|
3029
3029
|
|
|
3030
3030
|
// src/process/wasi-errno.ts
|
|
3031
3031
|
var WASI_ERRNO = {
|
|
3032
|
-
...Object.fromEntries(Object.entries(E).map(([name2,
|
|
3032
|
+
...Object.fromEntries(Object.entries(E).map(([name2, errno2]) => [`E${name2}`, errno2])),
|
|
3033
3033
|
EWOULDBLOCK: E.AGAIN,
|
|
3034
3034
|
ENOTSUP: 138,
|
|
3035
3035
|
EOPNOTSUPP: 138,
|
|
@@ -3126,7 +3126,7 @@ function dialError(e, timedOut, closed) {
|
|
|
3126
3126
|
if (timedOut) return new KernelError("ETIMEDOUT");
|
|
3127
3127
|
if (closed) return new KernelError("ECONNABORTED");
|
|
3128
3128
|
const code = e?.code;
|
|
3129
|
-
const known = DIAL_ERRORS.find((
|
|
3129
|
+
const known = DIAL_ERRORS.find((errno2) => errno2 === code);
|
|
3130
3130
|
return new KernelError(known ?? "EHOSTUNREACH");
|
|
3131
3131
|
}
|
|
3132
3132
|
function isLoopback(host) {
|
|
@@ -3793,11 +3793,11 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
|
3793
3793
|
};
|
|
3794
3794
|
const sockets = /* @__PURE__ */ new Map();
|
|
3795
3795
|
const credit = /* @__PURE__ */ new Map();
|
|
3796
|
-
const refill = (nid, n,
|
|
3796
|
+
const refill = (nid, n, closing2 = false) => {
|
|
3797
3797
|
const flow = credit.get(nid);
|
|
3798
3798
|
if (!flow) return;
|
|
3799
3799
|
flow.inflight -= n;
|
|
3800
|
-
if (
|
|
3800
|
+
if (closing2 && !flow.closing) {
|
|
3801
3801
|
flow.closing = true;
|
|
3802
3802
|
flow.deadline();
|
|
3803
3803
|
}
|
|
@@ -4928,6 +4928,33 @@ function toHostLoopback(href) {
|
|
|
4928
4928
|
url.hostname = "127.0.0.1";
|
|
4929
4929
|
return url.href;
|
|
4930
4930
|
}
|
|
4931
|
+
var LOOPBACK_V4 = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
4932
|
+
var LOOPBACK_V6 = /* @__PURE__ */ new Set(["::1", "::", "0:0:0:0:0:0:0:1"]);
|
|
4933
|
+
var MAPPED_V4 = /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/;
|
|
4934
|
+
var KERNEL_PORT = /^(\d{1,5})\.kernel\.localhost$/;
|
|
4935
|
+
var SCHEMES = {
|
|
4936
|
+
"http:": 80,
|
|
4937
|
+
"ws:": 80,
|
|
4938
|
+
"https:": 443,
|
|
4939
|
+
"wss:": 443
|
|
4940
|
+
};
|
|
4941
|
+
function isKernelLoopbackHost(hostname) {
|
|
4942
|
+
const host = hostname.replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "").toLowerCase();
|
|
4943
|
+
return host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0" || LOOPBACK_V4.test(host) || LOOPBACK_V6.has(host) || MAPPED_V4.test(host);
|
|
4944
|
+
}
|
|
4945
|
+
function kernelLoopbackPort(href) {
|
|
4946
|
+
let url;
|
|
4947
|
+
try {
|
|
4948
|
+
url = new URL(href);
|
|
4949
|
+
} catch {
|
|
4950
|
+
return void 0;
|
|
4951
|
+
}
|
|
4952
|
+
const fallback = SCHEMES[url.protocol];
|
|
4953
|
+
if (fallback === void 0 || !isKernelLoopbackHost(url.hostname)) return void 0;
|
|
4954
|
+
const named = KERNEL_PORT.exec(url.hostname.replace(/\.$/, "").toLowerCase())?.[1];
|
|
4955
|
+
const port = Number(named ?? (url.port || fallback));
|
|
4956
|
+
return port >= 1 && port <= 65535 ? port : void 0;
|
|
4957
|
+
}
|
|
4931
4958
|
var DEFAULT_HOSTNAME = "slicc";
|
|
4932
4959
|
var HOSTNAME_ADDRESS = "127.0.1.1";
|
|
4933
4960
|
var LIBC_HOSTNAMES = ["emscripten", "wasmer.sh"];
|
|
@@ -5233,6 +5260,9 @@ async function acceptKey(headers) {
|
|
|
5233
5260
|
}
|
|
5234
5261
|
const key = field(headers, "sec-websocket-key") ?? "";
|
|
5235
5262
|
if (!/^[A-Za-z0-9+/]{22}==$/.test(key)) throw new HttpError(400, "bad Sec-WebSocket-Key");
|
|
5263
|
+
return acceptFor(key);
|
|
5264
|
+
}
|
|
5265
|
+
async function acceptFor(key) {
|
|
5236
5266
|
const digest = await crypto.subtle.digest("SHA-1", new TextEncoder().encode(key + GUID));
|
|
5237
5267
|
return btoa(String.fromCharCode(...new Uint8Array(digest)));
|
|
5238
5268
|
}
|
|
@@ -5256,6 +5286,19 @@ function frame(opcode, payload) {
|
|
|
5256
5286
|
out.set(payload, size);
|
|
5257
5287
|
return out;
|
|
5258
5288
|
}
|
|
5289
|
+
function clientFrame(opcode, payload) {
|
|
5290
|
+
const plain = frame(opcode, payload);
|
|
5291
|
+
const head = plain.length - payload.length;
|
|
5292
|
+
const mask = crypto.getRandomValues(new Uint8Array(4));
|
|
5293
|
+
const out = new Uint8Array(plain.length + 4);
|
|
5294
|
+
out.set(plain.subarray(0, head));
|
|
5295
|
+
out[1] = out[1] | 128;
|
|
5296
|
+
out.set(mask, head);
|
|
5297
|
+
for (let i = 0; i < payload.length; i++) {
|
|
5298
|
+
out[head + 4 + i] = payload[i] ^ mask[i & 3];
|
|
5299
|
+
}
|
|
5300
|
+
return out;
|
|
5301
|
+
}
|
|
5259
5302
|
function closeFrame(code, reason = "") {
|
|
5260
5303
|
let bytes = new TextEncoder().encode(reason);
|
|
5261
5304
|
if (bytes.length > 123) {
|
|
@@ -5295,10 +5338,12 @@ var WsReader = class {
|
|
|
5295
5338
|
continuing = false;
|
|
5296
5339
|
binaryMessage = false;
|
|
5297
5340
|
binary;
|
|
5298
|
-
|
|
5341
|
+
fromServer;
|
|
5342
|
+
constructor(incoming, max = MAX_MESSAGE, binary, fromServer = false) {
|
|
5299
5343
|
this.incoming = incoming;
|
|
5300
5344
|
this.max = max;
|
|
5301
5345
|
this.binary = binary;
|
|
5346
|
+
this.fromServer = fromServer;
|
|
5302
5347
|
}
|
|
5303
5348
|
async next(signal) {
|
|
5304
5349
|
for (; ; ) {
|
|
@@ -5310,7 +5355,7 @@ var WsReader = class {
|
|
|
5310
5355
|
const fin = (head[0] & 128) !== 0;
|
|
5311
5356
|
const opcode = head[0] & 15;
|
|
5312
5357
|
if ((head[0] & 112) !== 0) throw new WsError(CLOSE.protocol, "reserved bits are set");
|
|
5313
|
-
|
|
5358
|
+
const masked = this.masking(head[1]);
|
|
5314
5359
|
const length2 = await this.length(head[1] & 127, signal);
|
|
5315
5360
|
const control = opcode >= 8;
|
|
5316
5361
|
if (control && (length2 > 125 || !fin)) {
|
|
@@ -5319,15 +5364,19 @@ var WsReader = class {
|
|
|
5319
5364
|
if (!control && this.size + length2 > this.max) {
|
|
5320
5365
|
throw new WsError(CLOSE.tooBig, `a message is over ${this.max} bytes`);
|
|
5321
5366
|
}
|
|
5322
|
-
const
|
|
5323
|
-
await fill(this.incoming, mask, signal);
|
|
5324
|
-
const payload = new Uint8Array(length2);
|
|
5325
|
-
await fill(this.incoming, payload, signal);
|
|
5326
|
-
for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
|
|
5367
|
+
const payload = await this.payload(length2, masked, signal);
|
|
5327
5368
|
const event = this.take(opcode, fin, payload);
|
|
5328
5369
|
if (event) return event;
|
|
5329
5370
|
}
|
|
5330
5371
|
}
|
|
5372
|
+
masking(second) {
|
|
5373
|
+
const masked = (second & 128) !== 0;
|
|
5374
|
+
if (masked !== this.fromServer) return masked;
|
|
5375
|
+
throw new WsError(
|
|
5376
|
+
CLOSE.protocol,
|
|
5377
|
+
this.fromServer ? "server frames must not be masked" : "client frames must be masked"
|
|
5378
|
+
);
|
|
5379
|
+
}
|
|
5331
5380
|
data(opcode, fin, payload) {
|
|
5332
5381
|
if (opcode === OP.continuation !== this.continuing) {
|
|
5333
5382
|
throw new WsError(CLOSE.protocol, "unexpected continuation frame");
|
|
@@ -5342,6 +5391,14 @@ var WsReader = class {
|
|
|
5342
5391
|
this.size = 0;
|
|
5343
5392
|
return this.binaryMessage ? { kind: "binary", bytes: whole } : { kind: "text", text: decode(whole) };
|
|
5344
5393
|
}
|
|
5394
|
+
async payload(length2, masked, signal) {
|
|
5395
|
+
const mask = new Uint8Array(4);
|
|
5396
|
+
if (masked) await fill(this.incoming, mask, signal);
|
|
5397
|
+
const payload = new Uint8Array(length2);
|
|
5398
|
+
await fill(this.incoming, payload, signal);
|
|
5399
|
+
if (masked) for (let i = 0; i < length2; i++) payload[i] ^= mask[i & 3];
|
|
5400
|
+
return payload;
|
|
5401
|
+
}
|
|
5345
5402
|
async length(short, signal) {
|
|
5346
5403
|
if (short < 126) return short;
|
|
5347
5404
|
const ext = new Uint8Array(short === 126 ? 2 : 8);
|
|
@@ -5777,10 +5834,10 @@ var RealmProxy = class {
|
|
|
5777
5834
|
encodedBodies: this.options.transport.traits.encodedBodies,
|
|
5778
5835
|
bodiless: bodiless2
|
|
5779
5836
|
});
|
|
5780
|
-
const
|
|
5781
|
-
const keep2 = keepAlive && (bodiless2 ||
|
|
5837
|
+
const chunked2 = !bodiless2 && minor >= 1;
|
|
5838
|
+
const keep2 = keepAlive && (bodiless2 || chunked2);
|
|
5782
5839
|
headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
|
|
5783
|
-
if (
|
|
5840
|
+
if (chunked2) headers.push(["Transfer-Encoding", "chunked"]);
|
|
5784
5841
|
const signal = this.stop.signal;
|
|
5785
5842
|
await conn.write(
|
|
5786
5843
|
responseHead(status, response.statusText || REASON[status] || "", headers),
|
|
@@ -5792,13 +5849,13 @@ var RealmProxy = class {
|
|
|
5792
5849
|
}
|
|
5793
5850
|
try {
|
|
5794
5851
|
for await (const piece of response.body) {
|
|
5795
|
-
if (piece.length > 0) await conn.write(
|
|
5852
|
+
if (piece.length > 0) await conn.write(chunked2 ? chunk(piece) : piece, signal);
|
|
5796
5853
|
}
|
|
5797
5854
|
} catch {
|
|
5798
5855
|
await response.cancel().catch(() => void 0);
|
|
5799
5856
|
return false;
|
|
5800
5857
|
}
|
|
5801
|
-
if (
|
|
5858
|
+
if (chunked2) await conn.write(LAST_CHUNK, signal);
|
|
5802
5859
|
return keep2;
|
|
5803
5860
|
}
|
|
5804
5861
|
async connect(conn, incoming, req, idle) {
|
|
@@ -8225,15 +8282,15 @@ var UnlinkedKeeper = class {
|
|
|
8225
8282
|
wrap(fs = this.fs) {
|
|
8226
8283
|
const keeper = this;
|
|
8227
8284
|
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
8228
|
-
const
|
|
8285
|
+
const counted2 = (st) => {
|
|
8229
8286
|
const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
|
|
8230
8287
|
return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
|
|
8231
8288
|
};
|
|
8232
8289
|
return {
|
|
8233
8290
|
...fs,
|
|
8234
8291
|
links: (path) => mayLink(fs, path),
|
|
8235
|
-
stat: async (path) =>
|
|
8236
|
-
lstat: async (path) =>
|
|
8292
|
+
stat: async (path) => counted2(await fs.stat(path)),
|
|
8293
|
+
lstat: async (path) => counted2(await fs.lstat(path)),
|
|
8237
8294
|
async rm(path, options) {
|
|
8238
8295
|
const target = fs.resolvePath("/", path);
|
|
8239
8296
|
const pinned = await keeper.pin(target);
|
|
@@ -8247,7 +8304,7 @@ var UnlinkedKeeper = class {
|
|
|
8247
8304
|
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
8248
8305
|
},
|
|
8249
8306
|
readdir: async (path) => visible(await fs.readdir(path)),
|
|
8250
|
-
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st &&
|
|
8307
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
|
|
8251
8308
|
};
|
|
8252
8309
|
}
|
|
8253
8310
|
async settle(pinned, op) {
|
|
@@ -10174,6 +10231,7 @@ async function storageUsage() {
|
|
|
10174
10231
|
if (typeof estimate?.quota !== "number") return null;
|
|
10175
10232
|
return { quota: estimate.quota, usage: estimate.usage ?? 0 };
|
|
10176
10233
|
}
|
|
10234
|
+
var PLAIN_DIR_MODE = 493;
|
|
10177
10235
|
var done = { ok: true, kind: "void" };
|
|
10178
10236
|
function json2(value) {
|
|
10179
10237
|
return { ok: true, kind: "json", json: value };
|
|
@@ -10183,6 +10241,11 @@ function ownerFor2(entry, mode) {
|
|
|
10183
10241
|
if (!cred) return void 0;
|
|
10184
10242
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
10185
10243
|
}
|
|
10244
|
+
function plainDirMode(entry, mode) {
|
|
10245
|
+
if (entry.cred || mode === void 0) return void 0;
|
|
10246
|
+
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
10247
|
+
return wanted === PLAIN_DIR_MODE ? void 0 : wanted;
|
|
10248
|
+
}
|
|
10186
10249
|
function mkdirOptions(entry, mode = 511) {
|
|
10187
10250
|
const owner = ownerFor2(entry, mode);
|
|
10188
10251
|
if (!owner) return { recursive: true };
|
|
@@ -10224,9 +10287,13 @@ async function run(entry, path, req) {
|
|
|
10224
10287
|
return json2(await fs.readdir(path));
|
|
10225
10288
|
case "readdir-stat":
|
|
10226
10289
|
return json2(await readdirStat(fs, path));
|
|
10227
|
-
case "mkdir":
|
|
10290
|
+
case "mkdir": {
|
|
10291
|
+
const plain = plainDirMode(entry, req.mode);
|
|
10292
|
+
const fresh2 = plain !== void 0 && !await fs.exists(path);
|
|
10228
10293
|
await (req.exclusive ? mkdirExclusive(entry, path, req.mode) : fs.mkdir(path, mkdirOptions(entry, req.mode)));
|
|
10294
|
+
if (fresh2) await fs.chmod(path, plain);
|
|
10229
10295
|
return done;
|
|
10296
|
+
}
|
|
10230
10297
|
case "rm":
|
|
10231
10298
|
await fs.rm(path, { recursive: true });
|
|
10232
10299
|
return done;
|
|
@@ -11079,7 +11146,7 @@ function mountTable(mounts = []) {
|
|
|
11079
11146
|
}
|
|
11080
11147
|
|
|
11081
11148
|
// src/version.ts
|
|
11082
|
-
var KERNEL_VERSION = true ? "1.
|
|
11149
|
+
var KERNEL_VERSION = true ? "1.92.0" : "0.0.0-dev";
|
|
11083
11150
|
var OS_RELEASE = "6.0.0-slicc";
|
|
11084
11151
|
function procVersion() {
|
|
11085
11152
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -11667,6 +11734,339 @@ async function hostSyscall(req, ctx) {
|
|
|
11667
11734
|
}
|
|
11668
11735
|
}
|
|
11669
11736
|
|
|
11737
|
+
// src/kernel/net/loopback-transport.ts
|
|
11738
|
+
var LOOPBACK_TRAITS = {
|
|
11739
|
+
manualRedirects: true,
|
|
11740
|
+
encodedBodies: true,
|
|
11741
|
+
maxRequestBody: 64 * 1024 * 1024,
|
|
11742
|
+
crossOrigin: "any",
|
|
11743
|
+
websocket: true
|
|
11744
|
+
};
|
|
11745
|
+
var MAX_HEAD2 = 64 * 1024;
|
|
11746
|
+
var OWN_HEADERS = /* @__PURE__ */ new Set([
|
|
11747
|
+
"host",
|
|
11748
|
+
"connection",
|
|
11749
|
+
"content-length",
|
|
11750
|
+
"transfer-encoding",
|
|
11751
|
+
"upgrade",
|
|
11752
|
+
"sec-websocket-key",
|
|
11753
|
+
"sec-websocket-version",
|
|
11754
|
+
"sec-websocket-protocol",
|
|
11755
|
+
"sec-websocket-extensions"
|
|
11756
|
+
]);
|
|
11757
|
+
var OP_CLOSE = 8;
|
|
11758
|
+
var OP_PONG = 10;
|
|
11759
|
+
var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
11760
|
+
var SAFE_VALUE = /^[^\r\n\0]*$/;
|
|
11761
|
+
var CLOSE_WAIT_MS3 = 5e3;
|
|
11762
|
+
var MAX_UNREAD = 16 * 1024 * 1024;
|
|
11763
|
+
function errno(code, message3) {
|
|
11764
|
+
return Object.assign(new Error(`${code}: ${message3}`), { code });
|
|
11765
|
+
}
|
|
11766
|
+
function requestHead(method, url, headers, extra) {
|
|
11767
|
+
if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
|
|
11768
|
+
const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];
|
|
11769
|
+
for (const [name2, value] of headers) {
|
|
11770
|
+
if (OWN_HEADERS.has(name2.toLowerCase())) continue;
|
|
11771
|
+
if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value)) throw errno("EINVAL", `header ${name2}`);
|
|
11772
|
+
lines2.push(`${name2}: ${value}`);
|
|
11773
|
+
}
|
|
11774
|
+
for (const [name2, value] of extra) lines2.push(`${name2}: ${value}`);
|
|
11775
|
+
return latin1Bytes(`${lines2.join("\r\n")}\r
|
|
11776
|
+
\r
|
|
11777
|
+
`);
|
|
11778
|
+
}
|
|
11779
|
+
function parseHead(bytes) {
|
|
11780
|
+
const [statusLine = "", ...lines2] = latin1(bytes).split(/\r?\n/);
|
|
11781
|
+
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
11782
|
+
if (!match) throw errno("EPROTO", `not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);
|
|
11783
|
+
const headers = [];
|
|
11784
|
+
for (const line of lines2) {
|
|
11785
|
+
const colon = line.indexOf(":");
|
|
11786
|
+
if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);
|
|
11787
|
+
}
|
|
11788
|
+
return { status: Number(match[1]), statusText: match[2] ?? "", headers };
|
|
11789
|
+
}
|
|
11790
|
+
async function readHead(incoming, signal) {
|
|
11791
|
+
for (; ; ) {
|
|
11792
|
+
let raw;
|
|
11793
|
+
try {
|
|
11794
|
+
raw = await incoming.head(MAX_HEAD2, signal);
|
|
11795
|
+
} catch (err) {
|
|
11796
|
+
if (err instanceof KernelError) throw err;
|
|
11797
|
+
throw errno("EPROTO", "the response head is malformed or too large");
|
|
11798
|
+
}
|
|
11799
|
+
if (!raw) throw errno("ECONNRESET", "the connection closed before a response");
|
|
11800
|
+
const head = parseHead(raw);
|
|
11801
|
+
if (head.status < 100 || head.status >= 200 || head.status === 101) return head;
|
|
11802
|
+
}
|
|
11803
|
+
}
|
|
11804
|
+
async function* chunked(incoming, signal) {
|
|
11805
|
+
for (; ; ) {
|
|
11806
|
+
const line = await incoming.line(4096, signal);
|
|
11807
|
+
const size = /^([0-9A-Fa-f]{1,12})[ \t]*(;.*)?$/.exec(line);
|
|
11808
|
+
if (!size) throw errno("EPROTO", "a malformed chunk size");
|
|
11809
|
+
const n = Number.parseInt(size[1], 16);
|
|
11810
|
+
if (n === 0) {
|
|
11811
|
+
while (await incoming.line(MAX_HEAD2, signal) !== "") ;
|
|
11812
|
+
return;
|
|
11813
|
+
}
|
|
11814
|
+
yield await incoming.exactly(n, signal);
|
|
11815
|
+
if (await incoming.line(2, signal) !== "") throw errno("EPROTO", "a malformed chunk");
|
|
11816
|
+
}
|
|
11817
|
+
}
|
|
11818
|
+
async function* counted(incoming, length2, signal) {
|
|
11819
|
+
for (let left = length2; left > 0; ) {
|
|
11820
|
+
const data = await incoming.some(Math.min(left, 64 * 1024), signal);
|
|
11821
|
+
if (data.length === 0) throw errno("EPROTO", "the body ended early");
|
|
11822
|
+
left -= data.length;
|
|
11823
|
+
yield data;
|
|
11824
|
+
}
|
|
11825
|
+
}
|
|
11826
|
+
async function* untilClose(incoming, signal) {
|
|
11827
|
+
for (; ; ) {
|
|
11828
|
+
const data = await incoming.some(64 * 1024, signal);
|
|
11829
|
+
if (data.length === 0) return;
|
|
11830
|
+
yield data;
|
|
11831
|
+
}
|
|
11832
|
+
}
|
|
11833
|
+
function bodyOf(incoming, head, method, signal) {
|
|
11834
|
+
if (method === "HEAD" || head.status === 204 || head.status === 304) return [];
|
|
11835
|
+
if (fieldTokens(head.headers, "transfer-encoding").includes("chunked")) {
|
|
11836
|
+
return chunked(incoming, signal);
|
|
11837
|
+
}
|
|
11838
|
+
const lengths = fieldValues(head.headers, "content-length");
|
|
11839
|
+
if (lengths.length === 0) return untilClose(incoming, signal);
|
|
11840
|
+
const length2 = lengths[0]?.trim() ?? "";
|
|
11841
|
+
if (!/^\d{1,15}$/.test(length2) || lengths.some((l) => l.trim() !== length2)) {
|
|
11842
|
+
throw errno("EPROTO", `a malformed Content-Length: ${lengths.join(", ")}`);
|
|
11843
|
+
}
|
|
11844
|
+
return counted(incoming, Number(length2), signal);
|
|
11845
|
+
}
|
|
11846
|
+
var KernelLoopback = class {
|
|
11847
|
+
net;
|
|
11848
|
+
cred;
|
|
11849
|
+
constructor(net, cred) {
|
|
11850
|
+
this.net = net;
|
|
11851
|
+
this.cred = cred;
|
|
11852
|
+
}
|
|
11853
|
+
port(url) {
|
|
11854
|
+
return kernelLoopbackPort(url);
|
|
11855
|
+
}
|
|
11856
|
+
connect(url, port, signal) {
|
|
11857
|
+
if (url.protocol === "https:" || url.protocol === "wss:") {
|
|
11858
|
+
throw errno("ENOTSUP", `TLS to the kernel's loopback is not supported: ${url.origin}`);
|
|
11859
|
+
}
|
|
11860
|
+
signal.throwIfAborted();
|
|
11861
|
+
const socket = this.net.socket("inet");
|
|
11862
|
+
try {
|
|
11863
|
+
void socket.connect({ family: "inet", host: "127.0.0.1", port }, false, this.cred());
|
|
11864
|
+
} catch (err) {
|
|
11865
|
+
socket.close();
|
|
11866
|
+
const refused2 = err instanceof KernelError && err.code === "ECONNREFUSED";
|
|
11867
|
+
throw refused2 ? errno("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
|
|
11868
|
+
}
|
|
11869
|
+
signal.addEventListener("abort", () => socket.close(), { once: true });
|
|
11870
|
+
return socket;
|
|
11871
|
+
}
|
|
11872
|
+
async fetch(req, port) {
|
|
11873
|
+
const url = new URL(req.url);
|
|
11874
|
+
const body = req.body ?? (/^(POST|PUT|PATCH)$/i.test(req.method) ? new Uint8Array(0) : void 0);
|
|
11875
|
+
const length2 = body ? [["Content-Length", String(body.length)]] : [];
|
|
11876
|
+
const head = requestHead(req.method, url, req.headers, [...length2, ["Connection", "close"]]);
|
|
11877
|
+
const socket = this.connect(url, port, req.signal);
|
|
11878
|
+
try {
|
|
11879
|
+
await writeAll2(socket, body ? concat([head, body]) : head, req.signal);
|
|
11880
|
+
const incoming = new Incoming(socket);
|
|
11881
|
+
const response = await readHead(incoming, req.signal);
|
|
11882
|
+
const chunks = bodyOf(incoming, response, req.method, req.signal);
|
|
11883
|
+
return {
|
|
11884
|
+
url: req.url,
|
|
11885
|
+
status: response.status,
|
|
11886
|
+
statusText: response.statusText,
|
|
11887
|
+
headers: response.headers,
|
|
11888
|
+
body: closing(chunks, socket),
|
|
11889
|
+
cancel: async () => socket.close()
|
|
11890
|
+
};
|
|
11891
|
+
} catch (err) {
|
|
11892
|
+
socket.close();
|
|
11893
|
+
throw err;
|
|
11894
|
+
}
|
|
11895
|
+
}
|
|
11896
|
+
async websocket(req, port) {
|
|
11897
|
+
const url = new URL(req.url);
|
|
11898
|
+
const socket = this.connect(url, port, req.signal);
|
|
11899
|
+
try {
|
|
11900
|
+
const key = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
|
|
11901
|
+
const extra = [
|
|
11902
|
+
["Upgrade", "websocket"],
|
|
11903
|
+
["Connection", "Upgrade"],
|
|
11904
|
+
["Sec-WebSocket-Key", key],
|
|
11905
|
+
["Sec-WebSocket-Version", "13"]
|
|
11906
|
+
];
|
|
11907
|
+
if (req.protocols.length > 0)
|
|
11908
|
+
extra.push(["Sec-WebSocket-Protocol", req.protocols.join(", ")]);
|
|
11909
|
+
await writeAll2(socket, requestHead("GET", url, req.headers, extra), req.signal);
|
|
11910
|
+
const incoming = new Incoming(socket);
|
|
11911
|
+
const head = await readHead(incoming, req.signal);
|
|
11912
|
+
if (head.status !== 101) {
|
|
11913
|
+
throw errno(
|
|
11914
|
+
"EPROTO",
|
|
11915
|
+
`the server answered ${head.status} ${head.statusText}, not an upgrade`
|
|
11916
|
+
);
|
|
11917
|
+
}
|
|
11918
|
+
const upgraded = fieldTokens(head.headers, "upgrade").includes("websocket") && fieldTokens(head.headers, "connection").includes("upgrade");
|
|
11919
|
+
const accept2 = fieldValues(head.headers, "sec-websocket-accept")[0];
|
|
11920
|
+
if (!upgraded || accept2 !== await acceptFor(key)) {
|
|
11921
|
+
throw errno("EPROTO", "the server did not accept the WebSocket key");
|
|
11922
|
+
}
|
|
11923
|
+
const protocol = fieldValues(head.headers, "sec-websocket-protocol")[0] ?? "";
|
|
11924
|
+
return new LoopbackWebSocket(socket, incoming, protocol);
|
|
11925
|
+
} catch (err) {
|
|
11926
|
+
socket.close();
|
|
11927
|
+
throw err;
|
|
11928
|
+
}
|
|
11929
|
+
}
|
|
11930
|
+
};
|
|
11931
|
+
async function writeAll2(socket, bytes, signal) {
|
|
11932
|
+
for (let at = 0; at < bytes.length; ) at += await socket.write(bytes.subarray(at), signal);
|
|
11933
|
+
}
|
|
11934
|
+
async function* closing(chunks, socket) {
|
|
11935
|
+
try {
|
|
11936
|
+
yield* chunks;
|
|
11937
|
+
} catch (err) {
|
|
11938
|
+
if (typeof err?.code === "string") throw err;
|
|
11939
|
+
throw errno("EPROTO", err instanceof Error ? err.message : String(err));
|
|
11940
|
+
} finally {
|
|
11941
|
+
socket.close();
|
|
11942
|
+
}
|
|
11943
|
+
}
|
|
11944
|
+
var LoopbackWebSocket = class {
|
|
11945
|
+
protocol;
|
|
11946
|
+
messages;
|
|
11947
|
+
closed;
|
|
11948
|
+
pending = 0;
|
|
11949
|
+
writes = Promise.resolve();
|
|
11950
|
+
sentClose = false;
|
|
11951
|
+
stop = new AbortController();
|
|
11952
|
+
socket;
|
|
11953
|
+
settle;
|
|
11954
|
+
unread = 0;
|
|
11955
|
+
room;
|
|
11956
|
+
constructor(socket, incoming, protocol) {
|
|
11957
|
+
this.socket = socket;
|
|
11958
|
+
this.protocol = protocol;
|
|
11959
|
+
this.closed = new Promise((resolve2) => {
|
|
11960
|
+
this.settle = resolve2;
|
|
11961
|
+
});
|
|
11962
|
+
this.stop.signal.addEventListener("abort", () => this.room?.(), { once: true });
|
|
11963
|
+
const queue = [];
|
|
11964
|
+
let wake;
|
|
11965
|
+
let ended = false;
|
|
11966
|
+
void this.pump(new WsReader(incoming, void 0, true, true), (message3) => {
|
|
11967
|
+
queue.push(message3);
|
|
11968
|
+
this.unread += message3.length;
|
|
11969
|
+
wake?.();
|
|
11970
|
+
}).finally(() => {
|
|
11971
|
+
ended = true;
|
|
11972
|
+
wake?.();
|
|
11973
|
+
});
|
|
11974
|
+
const taken = (message3) => {
|
|
11975
|
+
this.unread -= message3.length;
|
|
11976
|
+
const room = this.room;
|
|
11977
|
+
this.room = void 0;
|
|
11978
|
+
room?.();
|
|
11979
|
+
};
|
|
11980
|
+
this.messages = {
|
|
11981
|
+
[Symbol.asyncIterator]: async function* () {
|
|
11982
|
+
for (; ; ) {
|
|
11983
|
+
const next2 = queue.shift();
|
|
11984
|
+
if (next2 !== void 0) {
|
|
11985
|
+
taken(next2);
|
|
11986
|
+
yield next2;
|
|
11987
|
+
continue;
|
|
11988
|
+
}
|
|
11989
|
+
if (ended) return;
|
|
11990
|
+
await new Promise((resolve2) => {
|
|
11991
|
+
wake = resolve2;
|
|
11992
|
+
});
|
|
11993
|
+
wake = void 0;
|
|
11994
|
+
}
|
|
11995
|
+
}
|
|
11996
|
+
};
|
|
11997
|
+
}
|
|
11998
|
+
async space() {
|
|
11999
|
+
while (this.unread > MAX_UNREAD && !this.stop.signal.aborted) {
|
|
12000
|
+
await new Promise((resolve2) => {
|
|
12001
|
+
this.room = resolve2;
|
|
12002
|
+
});
|
|
12003
|
+
}
|
|
12004
|
+
}
|
|
12005
|
+
get buffered() {
|
|
12006
|
+
return this.pending;
|
|
12007
|
+
}
|
|
12008
|
+
write(bytes) {
|
|
12009
|
+
this.pending += bytes.length;
|
|
12010
|
+
const previous = this.writes;
|
|
12011
|
+
this.writes = (async () => {
|
|
12012
|
+
await previous;
|
|
12013
|
+
try {
|
|
12014
|
+
await writeAll2(this.socket, bytes, this.stop.signal);
|
|
12015
|
+
} catch {
|
|
12016
|
+
this.stop.abort();
|
|
12017
|
+
}
|
|
12018
|
+
this.pending -= bytes.length;
|
|
12019
|
+
})();
|
|
12020
|
+
}
|
|
12021
|
+
async pump(reader, deliver) {
|
|
12022
|
+
let close = { code: 1006, reason: "" };
|
|
12023
|
+
try {
|
|
12024
|
+
for (; ; ) {
|
|
12025
|
+
await this.space();
|
|
12026
|
+
const event = await reader.next(this.stop.signal);
|
|
12027
|
+
if (!event) break;
|
|
12028
|
+
if (event.kind === "text") deliver(event.text);
|
|
12029
|
+
else if (event.kind === "binary") deliver(event.bytes);
|
|
12030
|
+
else if (event.kind === "ping") this.write(clientFrame(OP_PONG, event.payload));
|
|
12031
|
+
else {
|
|
12032
|
+
close = { code: event.code, reason: event.reason };
|
|
12033
|
+
if (!this.sentClose) this.sendClose(event.code === 1005 ? 1e3 : event.code, "");
|
|
12034
|
+
break;
|
|
12035
|
+
}
|
|
12036
|
+
}
|
|
12037
|
+
} catch (err) {
|
|
12038
|
+
if (err instanceof WsError) this.sendClose(err.code, err.message);
|
|
12039
|
+
close = { code: err instanceof WsError ? err.code : 1006, reason: "" };
|
|
12040
|
+
} finally {
|
|
12041
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
12042
|
+
await this.writes;
|
|
12043
|
+
clearTimeout(late);
|
|
12044
|
+
this.socket.close();
|
|
12045
|
+
this.settle(close);
|
|
12046
|
+
}
|
|
12047
|
+
}
|
|
12048
|
+
sendClose(code, reason) {
|
|
12049
|
+
if (this.sentClose) return;
|
|
12050
|
+
this.sentClose = true;
|
|
12051
|
+
const late = setTimeout(() => this.stop.abort(), CLOSE_WAIT_MS3);
|
|
12052
|
+
void this.closed.then(() => clearTimeout(late));
|
|
12053
|
+
const bytes = new TextEncoder().encode(reason).subarray(0, 123);
|
|
12054
|
+
const payload = new Uint8Array(2 + bytes.length);
|
|
12055
|
+
new DataView(payload.buffer).setUint16(0, sendableCode(code) ? code : CLOSE.error);
|
|
12056
|
+
payload.set(bytes, 2);
|
|
12057
|
+
this.write(clientFrame(OP_CLOSE, payload));
|
|
12058
|
+
}
|
|
12059
|
+
send(data) {
|
|
12060
|
+
if (this.sentClose) throw errno("EPIPE", "the websocket is closing");
|
|
12061
|
+
const text = typeof data === "string";
|
|
12062
|
+
const payload = text ? new TextEncoder().encode(data) : data;
|
|
12063
|
+
this.write(clientFrame(text ? OP_TEXT : OP_BINARY, payload));
|
|
12064
|
+
}
|
|
12065
|
+
close(code = 1e3, reason = "") {
|
|
12066
|
+
this.sendClose(code, reason);
|
|
12067
|
+
}
|
|
12068
|
+
};
|
|
12069
|
+
|
|
11670
12070
|
// src/kernel/net/ws-syscalls.ts
|
|
11671
12071
|
var WS_OPS = [
|
|
11672
12072
|
"net-ws-open",
|
|
@@ -11676,7 +12076,7 @@ var WS_OPS = [
|
|
|
11676
12076
|
"net-ws-send",
|
|
11677
12077
|
"net-ws-close"
|
|
11678
12078
|
];
|
|
11679
|
-
var
|
|
12079
|
+
var TOKEN4 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
11680
12080
|
function message(e) {
|
|
11681
12081
|
return e instanceof Error ? e.message : String(e);
|
|
11682
12082
|
}
|
|
@@ -11685,16 +12085,18 @@ function errnoOf(e) {
|
|
|
11685
12085
|
const code = e?.code;
|
|
11686
12086
|
return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
|
|
11687
12087
|
}
|
|
11688
|
-
function fail(
|
|
11689
|
-
return { ok: false, errno, message: text };
|
|
12088
|
+
function fail(errno2, text) {
|
|
12089
|
+
return { ok: false, errno: errno2, message: text };
|
|
11690
12090
|
}
|
|
11691
12091
|
var WsHandles = class {
|
|
11692
12092
|
transport;
|
|
11693
12093
|
open = /* @__PURE__ */ new Map();
|
|
11694
12094
|
next;
|
|
11695
|
-
|
|
12095
|
+
local;
|
|
12096
|
+
constructor(transport, next2, local) {
|
|
11696
12097
|
this.transport = transport;
|
|
11697
12098
|
this.next = next2;
|
|
12099
|
+
this.local = local;
|
|
11698
12100
|
}
|
|
11699
12101
|
owns(handle) {
|
|
11700
12102
|
return this.open.has(handle);
|
|
@@ -11723,16 +12125,18 @@ var WsHandles = class {
|
|
|
11723
12125
|
return { ok: true, kind: "json", json: handle };
|
|
11724
12126
|
}
|
|
11725
12127
|
async connect(handle, req, entry) {
|
|
11726
|
-
const { transport } = this;
|
|
11727
|
-
|
|
12128
|
+
const { transport, local } = this;
|
|
12129
|
+
const port = local?.port(req.url);
|
|
12130
|
+
const open = { protocols: req.protocols, headers: req.headers, signal: entry.abort.signal };
|
|
12131
|
+
if (port === void 0 && (!transport.traits.websocket || !transport.websocket)) {
|
|
12132
|
+
return fail("ENOTSUP", NO_WEBSOCKET2);
|
|
12133
|
+
}
|
|
11728
12134
|
let socket;
|
|
11729
12135
|
try {
|
|
11730
|
-
socket = await transport.websocket({
|
|
12136
|
+
socket = port === void 0 ? await transport.websocket({
|
|
11731
12137
|
url: toHostLoopback(req.url),
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
signal: entry.abort.signal
|
|
11735
|
-
});
|
|
12138
|
+
...open
|
|
12139
|
+
}) : await local.websocket({ url: req.url, ...open }, port);
|
|
11736
12140
|
} catch (e) {
|
|
11737
12141
|
return fail(errnoOf(e), message(e));
|
|
11738
12142
|
}
|
|
@@ -11740,7 +12144,7 @@ var WsHandles = class {
|
|
|
11740
12144
|
socket.close(1001, "the program went away");
|
|
11741
12145
|
return fail("EBADF", `no websocket ${handle}`);
|
|
11742
12146
|
}
|
|
11743
|
-
if (socket.protocol && !(req.protocols.includes(socket.protocol) &&
|
|
12147
|
+
if (socket.protocol && !(req.protocols.includes(socket.protocol) && TOKEN4.test(socket.protocol))) {
|
|
11744
12148
|
socket.close(1002, "subprotocol not offered");
|
|
11745
12149
|
return fail("EPROTO", "the far end chose a subprotocol the program did not offer");
|
|
11746
12150
|
}
|
|
@@ -11803,6 +12207,10 @@ var EMPTY = new Uint8Array(0);
|
|
|
11803
12207
|
function message2(e) {
|
|
11804
12208
|
return e instanceof Error ? e.message : String(e);
|
|
11805
12209
|
}
|
|
12210
|
+
function codeOf2(e) {
|
|
12211
|
+
const code = e?.code;
|
|
12212
|
+
return typeof code === "string" && /^E[A-Z]+$/.test(code) ? code : "EIO";
|
|
12213
|
+
}
|
|
11806
12214
|
async function* once(bytes) {
|
|
11807
12215
|
yield bytes;
|
|
11808
12216
|
}
|
|
@@ -11812,10 +12220,12 @@ var HttpHandles = class {
|
|
|
11812
12220
|
nextHandle = 0;
|
|
11813
12221
|
ws;
|
|
11814
12222
|
owner;
|
|
11815
|
-
|
|
12223
|
+
local;
|
|
12224
|
+
constructor(transport, owner, local) {
|
|
11816
12225
|
this.transport = transport;
|
|
11817
12226
|
this.owner = owner;
|
|
11818
|
-
this.
|
|
12227
|
+
this.local = local;
|
|
12228
|
+
this.ws = new WsHandles(transport, () => ++this.nextHandle, local);
|
|
11819
12229
|
}
|
|
11820
12230
|
syscall(req) {
|
|
11821
12231
|
if (req.op.startsWith("net-ws-")) return this.ws.syscall(req);
|
|
@@ -11823,10 +12233,13 @@ var HttpHandles = class {
|
|
|
11823
12233
|
if (req.op === "net-open") return Promise.resolve(this.opened(req));
|
|
11824
12234
|
if (req.op === "net-head") return this.head(req.handle);
|
|
11825
12235
|
if (req.op === "net-read") return this.read(req.handle, req.max);
|
|
11826
|
-
if (req.op === "net-traits") return Promise.resolve(this.traits());
|
|
12236
|
+
if (req.op === "net-traits") return Promise.resolve(this.traits(req.url));
|
|
11827
12237
|
return this.close(req.handle);
|
|
11828
12238
|
}
|
|
11829
|
-
traits() {
|
|
12239
|
+
traits(url) {
|
|
12240
|
+
if (url !== void 0 && this.local?.port(url) !== void 0) {
|
|
12241
|
+
return { ok: true, kind: "json", json: LOOPBACK_TRAITS };
|
|
12242
|
+
}
|
|
11830
12243
|
const { unavailable, crossOrigin = "any", ...rest2 } = this.transport.traits;
|
|
11831
12244
|
if (unavailable) return { ok: false, errno: "ENETUNREACH", message: NO_TRANSPORT };
|
|
11832
12245
|
return { ok: true, kind: "json", json: { ...rest2, crossOrigin } };
|
|
@@ -11857,28 +12270,30 @@ var HttpHandles = class {
|
|
|
11857
12270
|
return { handle, head: entry.head };
|
|
11858
12271
|
}
|
|
11859
12272
|
async respond(handle, req, entry) {
|
|
12273
|
+
const port = this.local?.port(req.url);
|
|
11860
12274
|
try {
|
|
11861
|
-
const
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
);
|
|
12275
|
+
const target = port === void 0 ? toHostLoopback(req.url) : req.url;
|
|
12276
|
+
const request2 = {
|
|
12277
|
+
url: target,
|
|
12278
|
+
method: req.method,
|
|
12279
|
+
headers: req.headers,
|
|
12280
|
+
...req.body ? { body: req.body } : {},
|
|
12281
|
+
signal: entry.abort.signal
|
|
12282
|
+
};
|
|
12283
|
+
const response = port === void 0 ? await this.transport.fetch(request2, this.owner) : await this.local.fetch(request2, port);
|
|
11871
12284
|
entry.body = response.body[Symbol.asyncIterator]();
|
|
11872
12285
|
entry.cancel = () => response.cancel();
|
|
11873
12286
|
if (!this.open.has(handle)) await entry.cancel().catch(() => void 0);
|
|
11874
|
-
const
|
|
12287
|
+
const shown = response.url && response.url !== target ? response.url : req.url;
|
|
12288
|
+
const head = this.headOf(handle, shown, response);
|
|
11875
12289
|
return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
|
|
11876
12290
|
} catch (e) {
|
|
11877
12291
|
const text = message2(e);
|
|
12292
|
+
if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
|
|
11878
12293
|
const status = e.status;
|
|
11879
12294
|
if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
|
|
11880
|
-
const
|
|
11881
|
-
return { ok: false, errno, message: text };
|
|
12295
|
+
const errno2 = text === NO_TRANSPORT ? "ENETUNREACH" : "ECONNREFUSED";
|
|
12296
|
+
return { ok: false, errno: errno2, message: text };
|
|
11882
12297
|
}
|
|
11883
12298
|
entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
|
|
11884
12299
|
return this.headOf(handle, req.url, {
|
|
@@ -13347,6 +13762,7 @@ function aborted(signal) {
|
|
|
13347
13762
|
signal.addEventListener("abort", () => reject(new KernelError("EINTR")), { once: true });
|
|
13348
13763
|
});
|
|
13349
13764
|
}
|
|
13765
|
+
var PLAIN_FILE_MODE = 420;
|
|
13350
13766
|
var JOB_OPS = /* @__PURE__ */ new Set([
|
|
13351
13767
|
"proc-setpgid",
|
|
13352
13768
|
"proc-getpgid",
|
|
@@ -13801,15 +14217,21 @@ var WasmProcess = class {
|
|
|
13801
14217
|
}
|
|
13802
14218
|
if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
|
|
13803
14219
|
const open = { ...req, path };
|
|
14220
|
+
const plain = this.plainMode(req.mode);
|
|
13804
14221
|
if (req.exclusive) {
|
|
13805
|
-
await nodes.createExclusive(path, this.newOwner());
|
|
14222
|
+
await nodes.createExclusive(path, this.newOwner(req.mode));
|
|
14223
|
+
if (plain !== void 0) await this.options.fs.chmod?.(path, plain);
|
|
13806
14224
|
this.fds.installAt(fd, this.openVfsFile(open));
|
|
13807
14225
|
} else if (req.create) {
|
|
13808
14226
|
await nodes.onEntry(path, async () => {
|
|
13809
|
-
const
|
|
14227
|
+
const marks = this.options.owners || plain !== void 0;
|
|
14228
|
+
const fresh2 = marks && !await present2(this.options.fs, path, true);
|
|
13810
14229
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
13811
14230
|
await this.fds.get(fd).file.stat?.();
|
|
13812
|
-
if (fresh2) await this.options.fs.
|
|
14231
|
+
if (fresh2 && plain !== void 0) await this.options.fs.chmod?.(path, plain);
|
|
14232
|
+
else if (fresh2) {
|
|
14233
|
+
await this.options.fs.chown?.(path, { ...this.newOwner(req.mode), kind: "new" });
|
|
14234
|
+
}
|
|
13813
14235
|
});
|
|
13814
14236
|
} else {
|
|
13815
14237
|
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
@@ -13922,9 +14344,14 @@ var WasmProcess = class {
|
|
|
13922
14344
|
if (!tty) throw new KernelError("ENOENT");
|
|
13923
14345
|
return tty;
|
|
13924
14346
|
}
|
|
13925
|
-
|
|
14347
|
+
plainMode(mode) {
|
|
14348
|
+
if (this.options.owners || mode === void 0) return void 0;
|
|
14349
|
+
const wanted = mode & 4095 & ~this.umask;
|
|
14350
|
+
return wanted === PLAIN_FILE_MODE ? void 0 : wanted;
|
|
14351
|
+
}
|
|
14352
|
+
newOwner(mode = 438) {
|
|
13926
14353
|
if (!this.options.owners) return void 0;
|
|
13927
|
-
return { uid: this.cred.euid, gid: this.cred.egid, mode:
|
|
14354
|
+
return { uid: this.cred.euid, gid: this.cred.egid, mode: mode & 4095 & ~this.umask };
|
|
13928
14355
|
}
|
|
13929
14356
|
async openProc(req, path) {
|
|
13930
14357
|
return this.fds.install(await this.procFile(req, path), 3);
|
|
@@ -14664,7 +15091,7 @@ function inherited(opts) {
|
|
|
14664
15091
|
}
|
|
14665
15092
|
function holdingToken(opts, holds, caller) {
|
|
14666
15093
|
return mintSyncFsToken({
|
|
14667
|
-
...opts.owners ? caller : {},
|
|
15094
|
+
...opts.owners ? caller : { umask: caller.umask },
|
|
14668
15095
|
...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
|
|
14669
15096
|
...opts.links !== void 0 ? { links: opts.links } : {},
|
|
14670
15097
|
...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
|
|
@@ -15614,7 +16041,7 @@ function scrubberOf(secrets) {
|
|
|
15614
16041
|
const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
|
|
15615
16042
|
return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
|
|
15616
16043
|
}
|
|
15617
|
-
async function
|
|
16044
|
+
async function writeAll3(file, bytes) {
|
|
15618
16045
|
const write = file.write;
|
|
15619
16046
|
for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
|
|
15620
16047
|
}
|
|
@@ -15648,7 +16075,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
15648
16075
|
const later = (bytes) => {
|
|
15649
16076
|
if (bytes.length === 0) return;
|
|
15650
16077
|
pending += 1;
|
|
15651
|
-
tail = tail.then(() =>
|
|
16078
|
+
tail = tail.then(() => writeAll3(file, bytes)).catch((err) => {
|
|
15652
16079
|
if (failed3 === void 0) failed3 = err;
|
|
15653
16080
|
}).finally(() => {
|
|
15654
16081
|
pending -= 1;
|
|
@@ -16279,8 +16706,8 @@ var FSA_CAPABILITIES = {
|
|
|
16279
16706
|
ranges: true,
|
|
16280
16707
|
sessions: true
|
|
16281
16708
|
};
|
|
16282
|
-
var
|
|
16283
|
-
var denied = (err) =>
|
|
16709
|
+
var codeOf3 = (err) => err?.code;
|
|
16710
|
+
var denied = (err) => codeOf3(err) === "EACCES" || err?.name === "NotAllowedError";
|
|
16284
16711
|
function attrOf(st) {
|
|
16285
16712
|
return {
|
|
16286
16713
|
kind: st.isDirectory ? "directory" : st.isSymbolicLink ? "symlink" : "file",
|
|
@@ -16302,7 +16729,7 @@ function kernelFsHandlers(fs) {
|
|
|
16302
16729
|
try {
|
|
16303
16730
|
return await fs.stat(path);
|
|
16304
16731
|
} catch (err) {
|
|
16305
|
-
if (
|
|
16732
|
+
if (codeOf3(err) === "ENOENT") return void 0;
|
|
16306
16733
|
throw err;
|
|
16307
16734
|
}
|
|
16308
16735
|
}
|
|
@@ -16495,7 +16922,7 @@ function parseFstab(text) {
|
|
|
16495
16922
|
});
|
|
16496
16923
|
return lines2;
|
|
16497
16924
|
}
|
|
16498
|
-
function
|
|
16925
|
+
function codeOf4(err) {
|
|
16499
16926
|
const code = err?.code;
|
|
16500
16927
|
return typeof code === "string" ? code : "EIO";
|
|
16501
16928
|
}
|
|
@@ -16533,18 +16960,18 @@ async function mountLine({ line, spec, invalid }, mount, signal, retries, discar
|
|
|
16533
16960
|
break;
|
|
16534
16961
|
} catch (err) {
|
|
16535
16962
|
failed3 = err;
|
|
16536
|
-
if (FINAL.has(
|
|
16963
|
+
if (FINAL.has(codeOf4(err)) || attempt === retries.length) break;
|
|
16537
16964
|
await wait(retries[attempt], signal);
|
|
16538
16965
|
}
|
|
16539
16966
|
}
|
|
16540
|
-
const code =
|
|
16967
|
+
const code = codeOf4(failed3);
|
|
16541
16968
|
return { line, target: spec.target, code, error: String(failed3?.message ?? code) };
|
|
16542
16969
|
}
|
|
16543
16970
|
|
|
16544
16971
|
// src/mount/hostfs.ts
|
|
16545
16972
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
16546
|
-
var
|
|
16547
|
-
var unreachable = (err) =>
|
|
16973
|
+
var codeOf5 = (err) => err?.code;
|
|
16974
|
+
var unreachable = (err) => codeOf5(err) === "ENOTCONN";
|
|
16548
16975
|
function rel(path) {
|
|
16549
16976
|
return path.replace(/^\/+/, "");
|
|
16550
16977
|
}
|
|
@@ -16604,9 +17031,9 @@ var HostfsClient = class {
|
|
|
16604
17031
|
throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
|
|
16605
17032
|
return this.send(path, init, false, true);
|
|
16606
17033
|
}
|
|
16607
|
-
const
|
|
17034
|
+
const errno2 = response.headers.get("X-Hostfs-Errno") ?? "EIO";
|
|
16608
17035
|
const body = await response.json().catch(() => ({}));
|
|
16609
|
-
throw fsError2(
|
|
17036
|
+
throw fsError2(errno2, typeof body.message === "string" ? body.message : errno2);
|
|
16610
17037
|
}
|
|
16611
17038
|
async call(op, body = {}, handle = false) {
|
|
16612
17039
|
const response = await this.send(
|
|
@@ -16721,7 +17148,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
16721
17148
|
try {
|
|
16722
17149
|
return { opened: await opening2(), restore: void 0 };
|
|
16723
17150
|
} catch (err) {
|
|
16724
|
-
if (!flags2.write ||
|
|
17151
|
+
if (!flags2.write || codeOf5(err) !== "EACCES") throw err;
|
|
16725
17152
|
const was = await client.call("stat", { path: rel(path) });
|
|
16726
17153
|
const mode = was.mode ?? 0;
|
|
16727
17154
|
if (mode & 128) throw err;
|
|
@@ -17634,8 +18061,8 @@ var RULE = new RegExp(
|
|
|
17634
18061
|
var TERM = /^[A-Za-z0-9._+-]+$/;
|
|
17635
18062
|
var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
17636
18063
|
var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
|
|
17637
|
-
function failed2(
|
|
17638
|
-
return { error, errno };
|
|
18064
|
+
function failed2(errno2, error) {
|
|
18065
|
+
return { error, errno: errno2 };
|
|
17639
18066
|
}
|
|
17640
18067
|
function unescaped(double) {
|
|
17641
18068
|
return double.replace(/\\(["\\$`])/g, "$1");
|
|
@@ -18929,6 +19356,12 @@ var Launcher = class {
|
|
|
18929
19356
|
groupSession(pgid) {
|
|
18930
19357
|
return this.jobs.sessionOf(pgid);
|
|
18931
19358
|
}
|
|
19359
|
+
loopbackFor(pid) {
|
|
19360
|
+
return new KernelLoopback(this.net, () => {
|
|
19361
|
+
const cred = this.credOf(pid);
|
|
19362
|
+
return { pid, uid: cred.euid, gid: cred.egid };
|
|
19363
|
+
});
|
|
19364
|
+
}
|
|
18932
19365
|
treeScrubs(req) {
|
|
18933
19366
|
const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
|
|
18934
19367
|
return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
|
|
@@ -19006,7 +19439,7 @@ var Launcher = class {
|
|
|
19006
19439
|
jobs: this.jobs,
|
|
19007
19440
|
ptys: this.ptys,
|
|
19008
19441
|
net: this.net,
|
|
19009
|
-
http: new HttpHandles(this.transport, pid),
|
|
19442
|
+
http: new HttpHandles(this.transport, pid, this.loopbackFor(pid)),
|
|
19010
19443
|
locks: this.locks,
|
|
19011
19444
|
resolver: this.resolver,
|
|
19012
19445
|
onReap: (child) => this.reaped(child, pid),
|