Files
nym/wasm/smolmix/internal-dev/worker.js
T
mfahampshire 43a1bd38e8 Max/smolmix wasm (#6784)
* Mod gitignore + license trimming + comment trimming

* Big rewrite

* SURB inputs + DNS button in internal-dev

* Make ipr addr optional

* Accidentatly omitted files from rewrite commit

* Makefile + readme

* Comment rewrite

* Optimisation comment

* Replace manual waker map with
      smoltcp built-ins + adaptive poll

* Comments

* Extract socket creation helpers into stream.rs

* Cleanup comments

* Comment

* Comment notes and restrict ciphersuites wrt rustls-rustcrypto

* Dep. hack fix for demo + add clearnet fetch() for contrast

* Stripped down devtester

* Fix Clippy arg (fatfingered deletion)

* CodeRabbit catches

* Cargofmt

* Review nits: bridge logs, fetch early-return, static port counter, copyright years, README + Cargo + headless.js tidying

* PHONY + taskset override, switch internal-dev/tests to pnpm, fix wasm-pack out-dir

* Gate codec tests behind the codec feature for no-default-features builds

* IPv6 addr/route on smoltcp iface + configurable DNS resolvers via TunnelOpts

* DNS GUI inputs, close stale WS on reconnect, worker init guards + ws-send warning, Playwright listener cleanup, pnpm-lock in internal-dev

* Fix lp -> lp-data after rebase

* Revert nym-lp/nym-lp-data feature-gating left over from rebase

* Lift getrandom wasm_js cfg to workspace .cargo/config.toml so cargo check -p smolmix-wasm works from any CWD

* temp will amend git message

* Auto-discover IPR when none specified + 'Use random IPR' checkbox in internal-dev

* smolmix_tracker + State machine + ready_tunnel gate + getTunnelState JS surface

* Mirror red display() entries to console.error

* Add left out package-lock

* Reactor clock + yield_now + atomic seq + gateway-storage errors

* setupMixTunnel gate + MTU 1980 + http::Uri cleanup

* Review pass + fix test + clippy

* restore axum 0.8 bump from borked earlier merge

* Feature gating (dns/fetch/socket) + TunnelOptsBuilder + pnpm bypass

* Cont. with review comments

* tokio Nofity reactor wakes + cancellation + setup polishing

* Notify wakes + inner pattern + close_notify + util

* Tunable tunnelopts

* Fix tired commit

* CI prep

* Lint + Clippy

* coderabbit u32 fix

* nits + runtime debugging + expose in internal-dev

* remove redudant default-features

* Remove more redundant default-features
2026-05-28 15:57:10 +00:00

157 lines
4.0 KiB
JavaScript

// smolmix-wasm Web Worker
//
// Loads the WASM module in a dedicated thread. All mixnet I/O
// (smoltcp polling, TLS crypto, DNS resolution, mixnet messaging)
// runs here, keeping the main thread responsive for UI rendering.
//
// Two communication channels:
// 1. Comlink — request/response for fetch (setupMixTunnel, mixFetch, disconnect)
// 2. Raw postMessage — bidirectional events for WebSocket (ws-connect, ws-send, ws-close)
//
// Comlink ignores messages without its internal UUID markers,
// so both channels coexist on the same Worker without conflicts.
import initWasm, {
setupMixTunnel as wasmSetup,
mixFetch as wasmFetch,
mixResolve as wasmResolve,
disconnectMixTunnel as wasmDisconnect,
mixSocket as wasmMixSocket,
wsSend as wasmWsSend,
wsClose as wasmWsClose,
setDebugLogging as wasmSetDebugLogging,
} from "smolmix-wasm";
import * as Comlink from "comlink";
let wasmReady = false;
// Comlink API (fetch)
const api = {
async setupMixTunnel(opts) {
if (!wasmReady) {
await initWasm();
wasmReady = true;
}
await wasmSetup(opts);
},
async mixFetch(url, init) {
if (!wasmReady) {
throw new Error("WASM not initialised; call setupMixTunnel first");
}
return await wasmFetch(url, init || {});
},
async mixResolve(hostname) {
if (!wasmReady) {
throw new Error("WASM not initialised; call setupMixTunnel first");
}
return await wasmResolve(hostname);
},
async disconnectMixTunnel() {
if (!wasmReady) {
throw new Error("WASM not initialised; call setupMixTunnel first");
}
await wasmDisconnect();
},
async setDebugLogging(enabled) {
if (!wasmReady) {
await initWasm();
wasmReady = true;
}
wasmSetDebugLogging(enabled);
},
};
Comlink.expose(api);
// Raw postMessage API (WebSocket)
// Maps main-thread connId → WASM handleId
const wsConnMap = new Map();
self.addEventListener("message", async (event) => {
const msg = event.data;
if (!msg || typeof msg.kind !== "string") return;
switch (msg.kind) {
case "ws-connect": {
if (!wasmReady) {
self.postMessage({
kind: "ws-event",
connId: msg.connId,
type: "error",
data: "WASM not initialised — call setupMixTunnel first",
});
return;
}
// WASM callback: fires for open/text/binary/close/error events.
// Captures connId so all events route to the correct MixSocket instance.
const onEvent = (handleId, type, data) => {
if (!wsConnMap.has(msg.connId)) {
wsConnMap.set(msg.connId, handleId);
}
self.postMessage({ kind: "ws-event", connId: msg.connId, type, data });
};
try {
await wasmMixSocket(msg.url, msg.protocols, onEvent);
} catch (e) {
console.error("[ws] connect failed:", e);
self.postMessage({
kind: "ws-event",
connId: msg.connId,
type: "error",
data: String(e),
});
}
break;
}
case "ws-send": {
const handleId = wsConnMap.get(msg.connId);
if (handleId == null) {
console.warn(`[ws] send for connId ${msg.connId} before connection ready`);
return;
}
try {
wasmWsSend(handleId, msg.payload);
} catch (e) {
self.postMessage({
kind: "ws-event",
connId: msg.connId,
type: "error",
data: String(e),
});
}
break;
}
case "ws-close": {
const handleId = wsConnMap.get(msg.connId);
if (handleId == null) {
console.warn(`[ws] close for connId ${msg.connId} before connection ready`);
return;
}
try {
wasmWsClose(handleId, msg.code || 1000, msg.reason || "");
} catch (e) {
self.postMessage({
kind: "ws-event",
connId: msg.connId,
type: "error",
data: String(e),
});
}
wsConnMap.delete(msg.connId);
break;
}
}
});
self.postMessage({ kind: "Loaded" });