Files

137 lines
3.9 KiB
JavaScript

import assert from "node:assert/strict";
import path from "node:path";
import { rollup } from "rollup";
import typescript from "@rollup/plugin-typescript";
const root = path.resolve(import.meta.dirname, "..");
const entry = "\0wire-test-entry";
const bundle = await rollup({
input: "wire-test-entry",
plugins: [
{
name: "wire-test-entry",
resolveId(id) {
return id === "wire-test-entry" ? entry : null;
},
load(id) {
if (id !== entry) return null;
return `export { PortEndpoint } from ${JSON.stringify(path.join(root, "src/wire/endpoint.ts"))};
export { makeDispatcher } from ${JSON.stringify(path.join(root, "src/wire/router.ts"))};`;
},
},
typescript({
tsconfig: path.join(root, "tsconfig.worker.json"),
declaration: false,
}),
],
});
const { output } = await bundle.generate({ format: "es" });
await bundle.close();
const { PortEndpoint, makeDispatcher } = await import(
`data:text/javascript;base64,${Buffer.from(output[0].code).toString("base64")}`
);
const left = new PortEndpoint();
const right = new PortEndpoint();
const channel = new MessageChannel();
left.attach(channel.port1);
right.attach(channel.port2);
right.router.register(
7,
makeDispatcher(7, async (request) => ({
value: { echoed: request.value },
}))
);
// Node loads its own MessageEvent implementation on the first port message.
// Warm that platform path before mutating prototypes; the test targets our wire.
await left.call(7, { op: "warmup", value: 0 });
const targets = [
[JSON, "stringify"],
[JSON, "parse"],
[Map.prototype, "get"],
[Map.prototype, "set"],
[Map.prototype, "has"],
[Map.prototype, "delete"],
[Map.prototype, "clear"],
[Map.prototype, "forEach"],
[MessagePort.prototype, "postMessage"],
[MessagePort.prototype, "start"],
[DataView.prototype, "getUint16"],
[DataView.prototype, "getUint32"],
[DataView.prototype, "setUint16"],
[DataView.prototype, "setUint32"],
[TextEncoder.prototype, "encode"],
[TextDecoder.prototype, "decode"],
[Uint8Array.prototype, "set"],
[Uint8Array.prototype, "subarray"],
[Array.prototype, "push"],
[Array.prototype, "map"],
[Array.prototype, "slice"],
[Promise.prototype, "then"],
[Promise.prototype, "catch"],
];
const originals = targets.map(([object, name]) => [object, name, object[name]]);
const intercepted = () => {
throw new Error("internal call intercepted");
};
let result;
const deadline = setTimeout(
() => left.close(new Error("wire timed out")),
2000
);
try {
for (const [object, name] of targets) object[name] = intercepted;
result = await left.call(7, { op: "probe", value: 42 });
} finally {
clearTimeout(deadline);
for (const [object, name, original] of originals) object[name] = original;
left.close();
right.close();
}
assert.deepEqual(result.decoded.header.result, {
ok: true,
value: { echoed: 42 },
});
const chunkSizes = [131_072, 262_144, 524_288];
const stream = new ReadableStream({
start(controller) {
for (const size of chunkSizes) controller.enqueue(new Uint8Array(size));
controller.close();
},
});
// Reattach for the stream test after the poisoned-prototype test closed both ends.
const streamChannel = new MessageChannel();
const streamSender = new PortEndpoint();
const streamReceiver = new PortEndpoint();
streamSender.attach(streamChannel.port1);
streamReceiver.attach(streamChannel.port2);
streamReceiver.router.register(
8,
makeDispatcher(8, async (_request, _parts, attachments) => ({
transfer: [attachments[0]],
}))
);
const transferred = await streamSender.call(
8,
{ op: "stream" },
{ transfer: [stream] }
);
const reader = transferred.attachments[0].getReader();
let bytes = 0;
for (;;) {
const next = await reader.read();
if (next.done) break;
bytes += next.value.byteLength;
}
assert.equal(
bytes,
chunkSizes.reduce((a, b) => a + b, 0)
);
streamSender.close();
streamReceiver.close();
console.log("wire survives builtin and prototype replacement");
console.log(
"stream attachment transfers 917504 bytes without framing or buffering limits"
);