diff --git a/wasm/mix-fetch/internal-dev/index.js b/wasm/mix-fetch/internal-dev/index.js
index 097ab041f4..988ce8966d 100644
--- a/wasm/mix-fetch/internal-dev/index.js
+++ b/wasm/mix-fetch/internal-dev/index.js
@@ -15,226 +15,226 @@
// ─── Worker client ──────────────────────────────────────────────────────────
class WebWorkerClient {
- worker = null;
+ worker = null;
- constructor() {
- this.worker = new Worker("./worker.js");
+ constructor() {
+ this.worker = new Worker('./worker.js');
- this.worker.onmessage = (ev) => {
- if (!ev.data || !ev.data.kind) return;
- switch (ev.data.kind) {
- case "DisplayString":
- appendFetchLog(ev.data.args.rawString);
- console.log("[mixfetch response]", ev.data.args.rawString);
- break;
- case "Log": {
- const { message, level } = ev.data.args;
- const fn = level === "error" ? console.error
- : level === "warn" ? console.warn
- : console.log;
- fn(`[worker/${level}]`, message);
- break;
- }
- case "MixFetchReady":
- onMixFetchReady();
- break;
- case "MixFetchError":
- onMixFetchError(ev.data.args.error);
- break;
- case "StressTestFetchResult":
- onStressTestFetchResult(ev.data.args);
- break;
- }
- };
- }
-
- startMixFetch = (preferredGateway, setupOpts) => {
- this.worker.postMessage({ kind: "StartMixFetch", args: { preferredGateway, setupOpts } });
- };
-
- doFetch = (target) => {
- this.worker.postMessage({ kind: "FetchPayload", args: { target } });
- };
-
- setGoTimeout = (timeoutMs) => {
- this.worker.postMessage({ kind: "SetGoTimeout", args: { timeoutMs } });
- };
-
- doStressTest = (requests) => {
- for (const req of requests) {
- this.worker.postMessage({
- kind: "StressTestFetch",
- args: { id: req.id, url: req.url, label: req.label },
- });
+ this.worker.onmessage = (ev) => {
+ if (!ev.data || !ev.data.kind) return;
+ switch (ev.data.kind) {
+ case 'DisplayString':
+ appendFetchLog(ev.data.args.rawString);
+ console.log('[mixfetch response]', ev.data.args.rawString);
+ break;
+ case 'Log': {
+ const { message, level } = ev.data.args;
+ const fn = level === 'error' ? console.error
+ : level === 'warn' ? console.warn
+ : console.log;
+ fn(`[worker/${level}]`, message);
+ break;
+ }
+ case 'MixFetchReady':
+ onMixFetchReady();
+ break;
+ case 'MixFetchError':
+ onMixFetchError(ev.data.args.error);
+ break;
+ case 'StressTestFetchResult':
+ onStressTestFetchResult(ev.data.args);
+ break;
+ }
+ };
}
- };
+
+ startMixFetch = (preferredGateway, setupOpts) => {
+ this.worker.postMessage({ kind: 'StartMixFetch', args: { preferredGateway, setupOpts } });
+ };
+
+ doFetch = (target) => {
+ this.worker.postMessage({ kind: 'FetchPayload', args: { target } });
+ };
+
+ setGoTimeout = (timeoutMs) => {
+ this.worker.postMessage({ kind: 'SetGoTimeout', args: { timeoutMs } });
+ };
+
+ doStressTest = (requests) => {
+ for (const req of requests) {
+ this.worker.postMessage({
+ kind: 'StressTestFetch',
+ args: { id: req.id, url: req.url, label: req.label },
+ });
+ }
+ };
}
// ─── Startup ────────────────────────────────────────────────────────────────
let client = null;
-const DEFAULT_GATEWAY = "q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1";
+const DEFAULT_GATEWAY = 'q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1';
async function main() {
- client = new WebWorkerClient();
+ client = new WebWorkerClient();
- document.querySelector("#start-mixfetch").onclick = () => {
- const gatewayMode = document.querySelector('input[name="gateway-mode"]:checked').value;
- const preferredGateway = gatewayMode === "default" ? DEFAULT_GATEWAY : undefined;
+ document.querySelector('#start-mixfetch').onclick = () => {
+ const gatewayMode = document.querySelector('input[name="gateway-mode"]:checked').value;
+ const preferredGateway = gatewayMode === 'default' ? DEFAULT_GATEWAY : undefined;
- const setupOpts = {
- forceTls: document.getElementById("opt-force-tls").checked,
- clientId: document.getElementById("opt-client-id").value,
- disablePoisson: document.getElementById("opt-disable-poisson").checked,
- disableCover: document.getElementById("opt-disable-cover").checked,
- requestTimeoutMs: parseInt(document.getElementById("opt-request-timeout").value, 10),
+ const setupOpts = {
+ forceTls: document.getElementById('opt-force-tls').checked,
+ clientId: document.getElementById('opt-client-id').value,
+ disablePoisson: document.getElementById('opt-disable-poisson').checked,
+ disableCover: document.getElementById('opt-disable-cover').checked,
+ requestTimeoutMs: parseInt(document.getElementById('opt-request-timeout').value, 10),
+ };
+
+ document.querySelector('#start-mixfetch').disabled = true;
+ document.querySelectorAll('input[name="gateway-mode"]').forEach((r) => (r.disabled = true));
+ updateStatus('mixfetch-status', 'Starting...', 'orange');
+
+ // Sync the stress-test Go timeout to match the configured request timeout
+ document.getElementById('stress-go-timeout').value = setupOpts.requestTimeoutMs;
+
+ console.log(`Starting MixFetch (${gatewayMode} gateway${preferredGateway ? `: ${preferredGateway}` : ''})...`);
+ console.log('Setup options:', setupOpts);
+ client.startMixFetch(preferredGateway, setupOpts);
};
- document.querySelector("#start-mixfetch").disabled = true;
- document.querySelectorAll('input[name="gateway-mode"]').forEach((r) => (r.disabled = true));
- updateStatus("mixfetch-status", "Starting...", "orange");
+ document.querySelector('#fetch-button-1').onclick = () => doFetch(1);
+ document.querySelector('#fetch-button-2').onclick = () => doFetch(2);
- // Sync the stress-test Go timeout to match the configured request timeout
- document.getElementById("stress-go-timeout").value = setupOpts.requestTimeoutMs;
-
- console.log(`Starting MixFetch (${gatewayMode} gateway${preferredGateway ? `: ${preferredGateway}` : ""})...`);
- console.log("Setup options:", setupOpts);
- client.startMixFetch(preferredGateway, setupOpts);
- };
-
- document.querySelector("#fetch-button-1").onclick = () => doFetch(1);
- document.querySelector("#fetch-button-2").onclick = () => doFetch(2);
-
- const stressModeSelect = document.getElementById("stress-test-mode");
- stressModeSelect.onchange = function () {
- document.getElementById("stress-uniform-opts").style.display = this.value === "uniform" ? "block" : "none";
- document.getElementById("stress-mixed-opts").style.display = this.value === "mixed" ? "block" : "none";
- document.getElementById("stress-drip-opts").style.display = this.value === "drip" ? "block" : "none";
- };
-
- document.querySelector("#stress-test-button").onclick = () => {
- const count = parseInt(document.getElementById("stress-test-count").value, 10);
- const mode = document.getElementById("stress-test-mode").value;
- const goTimeoutMs = parseInt(document.getElementById("stress-go-timeout").value, 10);
-
- document.querySelector("#stress-test-button").disabled = true;
- updateStatus("stress-test-status", "Running...", "orange");
- client.setGoTimeout(goTimeoutMs);
-
- const requests = generateStressRequests(count, mode, goTimeoutMs);
- stressTest = {
- count,
- mode,
- startTime: performance.now(),
- results: [],
- completionOrder: [],
- profiles: {},
+ const stressModeSelect = document.getElementById('stress-test-mode');
+ stressModeSelect.onchange = function () {
+ document.getElementById('stress-uniform-opts').style.display = this.value === 'uniform' ? 'block' : 'none';
+ document.getElementById('stress-mixed-opts').style.display = this.value === 'mixed' ? 'block' : 'none';
+ document.getElementById('stress-drip-opts').style.display = this.value === 'drip' ? 'block' : 'none';
};
- for (const req of requests) {
- stressTest.profiles[req.id] = { label: req.label, bytes: req.bytes };
- }
- initStressTracker(requests);
+ document.querySelector('#stress-test-button').onclick = () => {
+ const count = parseInt(document.getElementById('stress-test-count').value, 10);
+ const mode = document.getElementById('stress-test-mode').value;
+ const goTimeoutMs = parseInt(document.getElementById('stress-go-timeout').value, 10);
- console.log(`%c=== STRESS TEST: ${count} requests, ${mode} mode, timeout=${goTimeoutMs}ms ===`, "font-weight: bold");
+ document.querySelector('#stress-test-button').disabled = true;
+ updateStatus('stress-test-status', 'Running...', 'orange');
+ client.setGoTimeout(goTimeoutMs);
- if (mode === "mixed" || mode === "drip") {
- const breakdown = {};
- for (const req of requests) breakdown[req.label] = (breakdown[req.label] || 0) + 1;
- console.log("Profiles:", breakdown);
- }
+ const requests = generateStressRequests(count, mode, goTimeoutMs);
+ stressTest = {
+ count,
+ mode,
+ startTime: performance.now(),
+ results: [],
+ completionOrder: [],
+ profiles: {},
+ };
+ for (const req of requests) {
+ stressTest.profiles[req.id] = { label: req.label, bytes: req.bytes };
+ }
- client.doStressTest(requests);
- };
+ initStressTracker(requests);
+
+ console.log(`%c=== STRESS TEST: ${count} requests, ${mode} mode, timeout=${goTimeoutMs}ms ===`, 'font-weight: bold');
+
+ if (mode === 'mixed' || mode === 'drip') {
+ const breakdown = {};
+ for (const req of requests) breakdown[req.label] = (breakdown[req.label] || 0) + 1;
+ console.log('Profiles:', breakdown);
+ }
+
+ client.doStressTest(requests);
+ };
}
// ─── UI helpers ─────────────────────────────────────────────────────────────
function updateStatus(elementId, text, color) {
- const el = document.getElementById(elementId);
- el.textContent = text;
- el.style.color = color;
+ const el = document.getElementById(elementId);
+ el.textContent = text;
+ el.style.color = color;
}
function onMixFetchReady() {
- updateStatus("mixfetch-status", "Ready", "green");
- document.getElementById("fetch-controls").disabled = false;
- console.log("%cMixFetch ready!", "color: green; font-weight: bold");
+ updateStatus('mixfetch-status', 'Ready', 'green');
+ document.getElementById('fetch-controls').disabled = false;
+ console.log('%cMixFetch ready!', 'color: green; font-weight: bold');
}
function onMixFetchError(error) {
- updateStatus("mixfetch-status", "Error: " + error, "red");
- document.querySelector("#start-mixfetch").disabled = false;
- document.querySelectorAll('input[name="gateway-mode"]').forEach((r) => (r.disabled = false));
- console.error("MixFetch error:", error);
+ updateStatus('mixfetch-status', 'Error: ' + error, 'red');
+ document.querySelector('#start-mixfetch').disabled = false;
+ document.querySelectorAll('input[name="gateway-mode"]').forEach((r) => (r.disabled = false));
+ console.error('MixFetch error:', error);
}
// ─── Quick fetch ────────────────────────────────────────────────────────────
function appendFetchLog(text) {
- const log = document.getElementById("fetch-log");
- log.style.display = "block";
- const ts = new Date().toISOString().substr(11, 12);
- log.textContent += `${ts} ${text}\n`;
- log.scrollTop = log.scrollHeight;
+ const log = document.getElementById('fetch-log');
+ log.style.display = 'block';
+ const ts = new Date().toISOString().substr(11, 12);
+ log.textContent += `${ts} ${text}\n`;
+ log.scrollTop = log.scrollHeight;
}
async function doFetch(id) {
- const url = document.getElementById(`fetch_payload_${id}`).value;
- appendFetchLog(`GET ${url}`);
- console.log(`GET ${url}`);
- await client.doFetch(url);
+ const url = document.getElementById(`fetch_payload_${id}`).value;
+ appendFetchLog(`GET ${url}`);
+ console.log(`GET ${url}`);
+ await client.doFetch(url);
}
// ─── Stress test ────────────────────────────────────────────────────────────
const STRESS_PROFILES = [
- { label: "tiny", bytes: 128 },
- { label: "small", bytes: 1024 },
- { label: "medium", bytes: 10240 },
- { label: "large", bytes: 102400 },
- { label: "xlarge", bytes: 1048576 },
+ { label: 'tiny', bytes: 128 },
+ { label: 'small', bytes: 1024 },
+ { label: 'medium', bytes: 10240 },
+ { label: 'large', bytes: 102400 },
+ { label: 'xlarge', bytes: 1048576 },
];
function buildDripProfiles(timeoutSec) {
- return [
- { label: "safe", dripDuration: Math.round(timeoutSec * 0.50), dripDelay: 0, dripBytes: 100 },
- { label: "boundary", dripDuration: Math.round(timeoutSec * 0.92), dripDelay: 0, dripBytes: 100 },
- { label: "over", dripDuration: Math.round(timeoutSec * 1.08), dripDelay: 0, dripBytes: 100 },
- { label: "slow-start", dripDuration: Math.round(timeoutSec * 0.83), dripDelay: Math.round(timeoutSec * 0.17), dripBytes: 100 },
- ];
+ return [
+ { label: 'safe', dripDuration: Math.round(timeoutSec * 0.50), dripDelay: 0, dripBytes: 100 },
+ { label: 'boundary', dripDuration: Math.round(timeoutSec * 0.92), dripDelay: 0, dripBytes: 100 },
+ { label: 'over', dripDuration: Math.round(timeoutSec * 1.08), dripDelay: 0, dripBytes: 100 },
+ { label: 'slow-start', dripDuration: Math.round(timeoutSec * 0.83), dripDelay: Math.round(timeoutSec * 0.17), dripBytes: 100 },
+ ];
}
function generateStressRequests(count, mode, timeoutMs) {
- const requests = [];
- if (mode === "uniform") {
- const baseUrl = document.getElementById("stress-test-url").value;
- for (let i = 1; i <= count; i++) {
- requests.push({ id: i, url: `${baseUrl}${i}`, label: "uniform", bytes: null });
+ const requests = [];
+ if (mode === 'uniform') {
+ const baseUrl = document.getElementById('stress-test-url').value;
+ for (let i = 1; i <= count; i++) {
+ requests.push({ id: i, url: `${baseUrl}${i}`, label: 'uniform', bytes: null });
+ }
+ } else if (mode === 'drip') {
+ const dripProfiles = buildDripProfiles(timeoutMs / 1000);
+ for (let i = 1; i <= count; i++) {
+ const p = dripProfiles[Math.floor(Math.random() * dripProfiles.length)];
+ requests.push({
+ id: i,
+ url: `https://httpbin.org/drip?duration=${p.dripDuration}&numbytes=${p.dripBytes}&delay=${p.dripDelay}&code=200`,
+ label: p.label,
+ bytes: p.dripBytes,
+ });
+ }
+ } else {
+ for (let i = 1; i <= count; i++) {
+ const p = STRESS_PROFILES[Math.floor(Math.random() * STRESS_PROFILES.length)];
+ requests.push({
+ id: i,
+ url: `https://httpbin.org/bytes/${p.bytes}`,
+ label: p.label,
+ bytes: p.bytes,
+ });
+ }
}
- } else if (mode === "drip") {
- const dripProfiles = buildDripProfiles(timeoutMs / 1000);
- for (let i = 1; i <= count; i++) {
- const p = dripProfiles[Math.floor(Math.random() * dripProfiles.length)];
- requests.push({
- id: i,
- url: `https://httpbin.org/drip?duration=${p.dripDuration}&numbytes=${p.dripBytes}&delay=${p.dripDelay}&code=200`,
- label: p.label,
- bytes: p.dripBytes,
- });
- }
- } else {
- for (let i = 1; i <= count; i++) {
- const p = STRESS_PROFILES[Math.floor(Math.random() * STRESS_PROFILES.length)];
- requests.push({
- id: i,
- url: `https://httpbin.org/bytes/${p.bytes}`,
- label: p.label,
- bytes: p.bytes,
- });
- }
- }
- return requests;
+ return requests;
}
let stressTest = null;
@@ -243,125 +243,125 @@ let stressTest = null;
let trackerData = {};
function initStressTracker(requests) {
- const tracker = document.getElementById("stress-tracker");
- tracker.style.display = "block";
- trackerData = {};
+ const tracker = document.getElementById('stress-tracker');
+ tracker.style.display = 'block';
+ trackerData = {};
- // Count how many of each profile were sent
- for (const req of requests) {
- if (!trackerData[req.label]) {
- trackerData[req.label] = { sent: 0, ok: 0, fail: 0, times: [] };
+ // Count how many of each profile were sent
+ for (const req of requests) {
+ if (!trackerData[req.label]) {
+ trackerData[req.label] = { sent: 0, ok: 0, fail: 0, times: [] };
+ }
+ trackerData[req.label].sent++;
}
- trackerData[req.label].sent++;
- }
- renderTracker();
+ renderTracker();
}
function renderTracker() {
- const tracker = document.getElementById("stress-tracker");
- const rows = Object.entries(trackerData).map(([label, d]) => {
- const pending = d.sent - d.ok - d.fail;
- const avg = d.times.length > 0
- ? (d.times.reduce((a, b) => a + b, 0) / d.times.length).toFixed(1) + "s"
- : "-";
- const max = d.times.length > 0 ? Math.max(...d.times).toFixed(1) + "s" : "-";
- const min = d.times.length > 0 ? Math.min(...d.times).toFixed(1) + "s" : "-";
+ const tracker = document.getElementById('stress-tracker');
+ const rows = Object.entries(trackerData).map(([label, d]) => {
+ const pending = d.sent - d.ok - d.fail;
+ const avg = d.times.length > 0
+ ? (d.times.reduce((a, b) => a + b, 0) / d.times.length).toFixed(1) + 's'
+ : '-';
+ const max = d.times.length > 0 ? Math.max(...d.times).toFixed(1) + 's' : '-';
+ const min = d.times.length > 0 ? Math.min(...d.times).toFixed(1) + 's' : '-';
- let status = "";
- if (d.ok > 0) status += `${d.ok} ok`;
- if (d.fail > 0) status += `${status ? " " : ""}${d.fail} fail`;
- if (pending > 0) status += `${status ? " " : ""}${pending} pending`;
+ let status = '';
+ if (d.ok > 0) status += `${d.ok} ok`;
+ if (d.fail > 0) status += `${status ? ' ' : ''}${d.fail} fail`;
+ if (pending > 0) status += `${status ? ' ' : ''}${pending} pending`;
- let timing = "";
- if (d.times.length > 0) {
- timing = `avg ${avg} / min ${min} / max ${max}`;
- }
+ let timing = '';
+ if (d.times.length > 0) {
+ timing = `avg ${avg} / min ${min} / max ${max}`;
+ }
- return `
- | ${label} |
- ${d.sent} |
- ${status} |
- ${timing} |
-
`;
- });
+ return `
+ | ${label} |
+ ${d.sent} |
+ ${status} |
+ ${timing} |
+
`;
+ });
- tracker.innerHTML = `
-
- | profile |
- sent |
- status |
- timing |
-
- ${rows.join("")}
-
`;
+ tracker.innerHTML = `
+
+ | profile |
+ sent |
+ status |
+ timing |
+
+ ${rows.join('')}
+
`;
}
function onStressTestFetchResult(result) {
- if (!stressTest) return;
+ if (!stressTest) return;
- const profile = stressTest.profiles[result.id];
- result.label = profile ? profile.label : "?";
+ const profile = stressTest.profiles[result.id];
+ result.label = profile ? profile.label : '?';
- stressTest.results.push(result);
- stressTest.completionOrder.push(result.id);
+ stressTest.results.push(result);
+ stressTest.completionOrder.push(result.id);
+
+ // Update tracker
+ const td = trackerData[result.label];
+ if (td) {
+ if (result.ok) {
+ td.ok++;
+ td.times.push(parseFloat(result.elapsed));
+ } else {
+ td.fail++;
+ }
+ renderTracker();
+ }
+
+ const progress = `${stressTest.results.length}/${stressTest.count}`;
+ const tag = `#${result.id} ${result.label}`;
- // Update tracker
- const td = trackerData[result.label];
- if (td) {
if (result.ok) {
- td.ok++;
- td.times.push(parseFloat(result.elapsed));
+ console.log(`%c[${tag}] ${result.status} OK ${result.elapsed}s ${result.textLength}B (${progress})`, 'color: green');
} else {
- td.fail++;
- }
- renderTracker();
- }
-
- const progress = `${stressTest.results.length}/${stressTest.count}`;
- const tag = `#${result.id} ${result.label}`;
-
- if (result.ok) {
- console.log(`%c[${tag}] ${result.status} OK ${result.elapsed}s ${result.textLength}B (${progress})`, "color: green");
- } else {
- console.error(`[${tag}] FAIL ${result.elapsed}s ${result.error} (${progress})`);
- }
-
- updateStatus("stress-test-status", progress, "orange");
-
- // Summary on completion
- if (stressTest.results.length === stressTest.count) {
- const totalElapsed = ((performance.now() - stressTest.startTime) / 1000).toFixed(2);
- const succeeded = stressTest.results.filter((r) => r.ok).length;
- const failed = stressTest.results.filter((r) => !r.ok).length;
-
- console.log(`%c=== COMPLETE: ${totalElapsed}s | OK ${succeeded}/${stressTest.count} | Failed ${failed}/${stressTest.count} ===`, "font-weight: bold");
- console.log("Completion order:", stressTest.completionOrder);
-
- // Per-profile breakdown in console
- const profileLabels = Object.keys(trackerData);
- const profileList = profileLabels.map((label) => ({ label }));
- if (stressTest.mode === "mixed" || stressTest.mode === "drip") {
- const table = [];
- for (const profile of profileList) {
- const matching = stressTest.results.filter((r) => r.label === profile.label);
- if (matching.length === 0) continue;
- const ok = matching.filter((r) => r.ok).length;
- const times = matching.filter((r) => r.ok).map((r) => parseFloat(r.elapsed));
- const avg = times.length > 0 ? (times.reduce((a, b) => a + b, 0) / times.length).toFixed(2) : "-";
- const max = times.length > 0 ? Math.max(...times).toFixed(2) : "-";
- table.push({ profile: profile.label, ok: `${ok}/${matching.length}`, avg: `${avg}s`, max: `${max}s` });
- }
- console.table(table);
+ console.error(`[${tag}] FAIL ${result.elapsed}s ${result.error} (${progress})`);
}
- updateStatus("stress-test-status",
- `Done: ${succeeded}/${stressTest.count} OK, ${failed} failed (${totalElapsed}s)`,
- failed > 0 ? "red" : "green"
- );
- document.querySelector("#stress-test-button").disabled = false;
- stressTest = null;
- }
+ updateStatus('stress-test-status', progress, 'orange');
+
+ // Summary on completion
+ if (stressTest.results.length === stressTest.count) {
+ const totalElapsed = ((performance.now() - stressTest.startTime) / 1000).toFixed(2);
+ const succeeded = stressTest.results.filter((r) => r.ok).length;
+ const failed = stressTest.results.filter((r) => !r.ok).length;
+
+ console.log(`%c=== COMPLETE: ${totalElapsed}s | OK ${succeeded}/${stressTest.count} | Failed ${failed}/${stressTest.count} ===`, 'font-weight: bold');
+ console.log('Completion order:', stressTest.completionOrder);
+
+ // Per-profile breakdown in console
+ const profileLabels = Object.keys(trackerData);
+ const profileList = profileLabels.map((label) => ({ label }));
+ if (stressTest.mode === 'mixed' || stressTest.mode === 'drip') {
+ const table = [];
+ for (const profile of profileList) {
+ const matching = stressTest.results.filter((r) => r.label === profile.label);
+ if (matching.length === 0) continue;
+ const ok = matching.filter((r) => r.ok).length;
+ const times = matching.filter((r) => r.ok).map((r) => parseFloat(r.elapsed));
+ const avg = times.length > 0 ? (times.reduce((a, b) => a + b, 0) / times.length).toFixed(2) : '-';
+ const max = times.length > 0 ? Math.max(...times).toFixed(2) : '-';
+ table.push({ profile: profile.label, ok: `${ok}/${matching.length}`, avg: `${avg}s`, max: `${max}s` });
+ }
+ console.table(table);
+ }
+
+ updateStatus('stress-test-status',
+ `Done: ${succeeded}/${stressTest.count} OK, ${failed} failed (${totalElapsed}s)`,
+ failed > 0 ? 'red' : 'green'
+ );
+ document.querySelector('#stress-test-button').disabled = false;
+ stressTest = null;
+ }
}
main();
diff --git a/wasm/mix-fetch/internal-dev/worker.js b/wasm/mix-fetch/internal-dev/worker.js
index 01868a178a..c747299f54 100644
--- a/wasm/mix-fetch/internal-dev/worker.js
+++ b/wasm/mix-fetch/internal-dev/worker.js
@@ -15,28 +15,28 @@
const RUST_WASM_URL = "mix_fetch_wasm_bg.wasm";
const GO_WASM_URL = "go_conn.wasm";
-importScripts("mix_fetch_wasm.js");
-importScripts("wasm_exec.js");
+importScripts('mix_fetch_wasm.js');
+importScripts('wasm_exec.js');
-console.log("Initializing worker");
+console.log('Initializing worker');
// wasm_bindgen creates a global variable (with the exports attached) that is in scope after `importScripts`
const {
- default_debug,
- no_cover_debug,
- NymClient,
- set_panic_hook,
- Config,
- GatewayEndpointConfig,
- ClientStorage,
- MixFetchConfig,
- send_client_data,
- start_new_mixnet_connection,
- setupMixFetch,
- disconnectMixFetch,
- setupMixFetchWithConfig,
- mix_fetch_initialised,
- finish_mixnet_connection,
+ default_debug,
+ no_cover_debug,
+ NymClient,
+ set_panic_hook,
+ Config,
+ GatewayEndpointConfig,
+ ClientStorage,
+ MixFetchConfig,
+ send_client_data,
+ start_new_mixnet_connection,
+ setupMixFetch,
+ disconnectMixFetch,
+ setupMixFetchWithConfig,
+ mix_fetch_initialised,
+ finish_mixnet_connection,
} = wasm_bindgen;
let client = null;
@@ -45,43 +45,43 @@ const go = new Go(); // Defined in wasm_exec.js
var goWasm;
let mixFetchReady = false;
-function sendLog(message, level = "info") {
- self.postMessage({
- kind: "Log",
- args: { message, level },
- });
+function sendLog(message, level = 'info') {
+ self.postMessage({
+ kind: 'Log',
+ args: { message, level },
+ });
}
function sendReady() {
- self.postMessage({ kind: "MixFetchReady" });
+ self.postMessage({ kind: 'MixFetchReady' });
}
function sendError(error) {
- self.postMessage({
- kind: "MixFetchError",
- args: { error: String(error) },
- });
+ self.postMessage({
+ kind: 'MixFetchError',
+ args: { error: String(error) },
+ });
}
async function logFetchResult(res) {
- console.log(res);
- let text = await res.text();
- console.log("HEADERS: ", ...res.headers);
- console.log("STATUS: ", res.status);
- console.log("STATUS TEXT: ", res.statusText);
- console.log("OK: ", res.ok);
- console.log("TYPE: ", res.type);
- console.log("URL: ", res.url);
- console.log("BODYUSED: ", res.bodyUsed);
- console.log("REDIRECTED: ", res.redirected);
- console.log("TEXT: ", text);
+ console.log(res);
+ let text = await res.text();
+ console.log("HEADERS: ", ...res.headers);
+ console.log("STATUS: ", res.status);
+ console.log("STATUS TEXT: ", res.statusText);
+ console.log("OK: ", res.ok);
+ console.log("TYPE: ", res.type);
+ console.log("URL: ", res.url);
+ console.log("BODYUSED: ", res.bodyUsed);
+ console.log("REDIRECTED: ", res.redirected);
+ console.log("TEXT: ", text);
- self.postMessage({
- kind: "DisplayString",
- args: {
- rawString: text,
- },
- });
+ self.postMessage({
+ kind: 'DisplayString',
+ args: {
+ rawString: text,
+ },
+ });
}
/*
@@ -110,205 +110,201 @@ async function logFetchResult(res) {
*/
async function nativeSetup(preferredGateway, setupOpts = {}) {
- sendLog("Setting up MixFetch...");
- if (preferredGateway) {
- sendLog(`Using preferred gateway: ${preferredGateway}`);
- } else {
- sendLog("Using random gateway selection");
- }
+ sendLog('Setting up MixFetch...');
+ if (preferredGateway) {
+ sendLog(`Using preferred gateway: ${preferredGateway}`);
+ } else {
+ sendLog('Using random gateway selection');
+ }
- const {
- forceTls = true,
- clientId = "my-client",
- disablePoisson = true,
- disableCover = true,
- requestTimeoutMs = 60000,
- } = setupOpts;
+ const {
+ forceTls = true,
+ clientId = 'my-client',
+ disablePoisson = true,
+ disableCover = true,
+ requestTimeoutMs = 60000,
+ } = setupOpts;
- const noCoverTrafficOverride = {
- traffic: { disableMainPoissonPacketDistribution: disablePoisson },
- coverTraffic: { disableLoopCoverTrafficStream: disableCover },
- };
- const mixFetchOverride = {
- requestTimeoutMs,
- };
+ const noCoverTrafficOverride = {
+ traffic: { disableMainPoissonPacketDistribution: disablePoisson },
+ coverTraffic: { disableLoopCoverTrafficStream: disableCover },
+ };
+ const mixFetchOverride = {
+ requestTimeoutMs,
+ };
- const opts = {
- forceTls,
- clientId,
- clientOverride: noCoverTrafficOverride,
- mixFetchOverride,
- };
+ const opts = {
+ forceTls,
+ clientId,
+ clientOverride: noCoverTrafficOverride,
+ mixFetchOverride,
+ };
- if (preferredGateway) {
- opts.preferredGateway = preferredGateway;
- }
+ if (preferredGateway) {
+ opts.preferredGateway = preferredGateway;
+ }
- sendLog(
- `Setup config: forceTls=${forceTls}, clientId=${clientId}, disablePoisson=${disablePoisson}, disableCover=${disableCover}, timeout=${requestTimeoutMs}ms`
- );
- sendLog("Calling setupMixFetch...");
- await setupMixFetch(opts);
- sendLog("setupMixFetch completed");
+ sendLog(
+ `Setup config: forceTls=${forceTls}, clientId=${clientId}, disablePoisson=${disablePoisson}, disableCover=${disableCover}, timeout=${requestTimeoutMs}ms`
+ );
+ sendLog('Calling setupMixFetch...');
+ await setupMixFetch(opts);
+ sendLog('setupMixFetch completed');
}
async function startMixFetch(preferredGateway, setupOpts) {
- sendLog("Instantiating MixFetch...");
+ sendLog('Instantiating MixFetch...');
- try {
- await nativeSetup(preferredGateway, setupOpts);
- mixFetchReady = true;
- sendLog("MixFetch client running!");
- sendReady();
- } catch (e) {
- sendLog("Failed to start MixFetch: " + e, "error");
- sendError(e);
- }
+ try {
+ await nativeSetup(preferredGateway, setupOpts);
+ mixFetchReady = true;
+ sendLog('MixFetch client running!');
+ sendReady();
+ } catch (e) {
+ sendLog('Failed to start MixFetch: ' + e, 'error');
+ sendError(e);
+ }
}
async function handleFetchPayload(target) {
- if (!mixFetchReady) {
- sendLog("MixFetch not ready yet", "error");
- return;
- }
+ if (!mixFetchReady) {
+ sendLog('MixFetch not ready yet', 'error');
+ return;
+ }
- const url = target;
- const args = { mode: "unsafe-ignore-cors" };
+ const url = target;
+ const args = { mode: "unsafe-ignore-cors" };
- try {
- sendLog(`Fetching: ${url}`);
- const mixFetchRes = await mixFetch(url, args);
- sendLog("Fetch completed");
- await logFetchResult(mixFetchRes);
- } catch (e) {
- sendLog("Fetch request failure: " + e, "error");
- console.error("mix fetch request failure: ", e);
- }
+ try {
+ sendLog(`Fetching: ${url}`);
+ const mixFetchRes = await mixFetch(url, args);
+ sendLog('Fetch completed');
+ await logFetchResult(mixFetchRes);
+ } catch (e) {
+ sendLog('Fetch request failure: ' + e, 'error');
+ console.error("mix fetch request failure: ", e);
+ }
}
async function handleStressTestFetch(id, url, label) {
- if (!mixFetchReady) {
- sendLog("MixFetch not ready yet", "error");
- return;
- }
+ if (!mixFetchReady) {
+ sendLog('MixFetch not ready yet', 'error');
+ return;
+ }
- const tag = `[stress #${id} ${label}]`;
- const start = performance.now();
- const args = { mode: "unsafe-ignore-cors" };
+ const tag = `[stress #${id} ${label}]`;
+ const start = performance.now();
+ const args = { mode: "unsafe-ignore-cors" };
- try {
- sendLog(`${tag} Fetching: ${url}`);
- const res = await mixFetch(url, args);
- const text = await res.text();
- const elapsed = ((performance.now() - start) / 1000).toFixed(2);
- sendLog(`${tag} ${res.status} OK in ${elapsed}s (${text.length} bytes)`);
- self.postMessage({
- kind: "StressTestFetchResult",
- args: {
- id,
- ok: true,
- status: res.status,
- elapsed,
- textLength: text.length,
- body: text,
- },
- });
- } catch (e) {
- const elapsed = ((performance.now() - start) / 1000).toFixed(2);
- sendLog(`${tag} FAILED in ${elapsed}s: ${e}`, "error");
- self.postMessage({
- kind: "StressTestFetchResult",
- args: { id, ok: false, elapsed, error: String(e) },
- });
- }
+ try {
+ sendLog(`${tag} Fetching: ${url}`);
+ const res = await mixFetch(url, args);
+ const text = await res.text();
+ const elapsed = ((performance.now() - start) / 1000).toFixed(2);
+ sendLog(`${tag} ${res.status} OK in ${elapsed}s (${text.length} bytes)`);
+ self.postMessage({
+ kind: 'StressTestFetchResult',
+ args: {
+ id,
+ ok: true,
+ status: res.status,
+ elapsed,
+ textLength: text.length,
+ body: text,
+ },
+ });
+ } catch (e) {
+ const elapsed = ((performance.now() - start) / 1000).toFixed(2);
+ sendLog(`${tag} FAILED in ${elapsed}s: ${e}`, 'error');
+ self.postMessage({
+ kind: 'StressTestFetchResult',
+ args: { id, ok: false, elapsed, error: String(e) },
+ });
+ }
}
function setupMessageHandler() {
- self.onmessage = async (event) => {
- if (event.data && event.data.kind) {
- switch (event.data.kind) {
- case "StartMixFetch": {
- const { preferredGateway, setupOpts } = event.data.args;
- await startMixFetch(preferredGateway, setupOpts);
- break;
+ self.onmessage = async (event) => {
+ if (event.data && event.data.kind) {
+ switch (event.data.kind) {
+ case 'StartMixFetch': {
+ const { preferredGateway, setupOpts } = event.data.args;
+ await startMixFetch(preferredGateway, setupOpts);
+ break;
+ }
+ case 'FetchPayload': {
+ const { target } = event.data.args;
+ await handleFetchPayload(target);
+ break;
+ }
+ case 'SetGoTimeout': {
+ const { timeoutMs } = event.data.args;
+ sendLog(`Setting Go-side request timeout to ${timeoutMs}ms`);
+ self.__go_rs_bridge__.goWasmSetMixFetchRequestTimeout(timeoutMs);
+ break;
+ }
+ case 'StressTestFetch': {
+ const { id, url, label } = event.data.args;
+ // NOT awaited — each request runs independently,
+ // just like separate callers in a real app
+ handleStressTestFetch(id, url, label);
+ break;
+ }
+ }
}
- case "FetchPayload": {
- const { target } = event.data.args;
- await handleFetchPayload(target);
- break;
- }
- case "SetGoTimeout": {
- const { timeoutMs } = event.data.args;
- sendLog(`Setting Go-side request timeout to ${timeoutMs}ms`);
- self.__go_rs_bridge__.goWasmSetMixFetchRequestTimeout(timeoutMs);
- break;
- }
- case "StressTestFetch": {
- const { id, url, label } = event.data.args;
- // NOT awaited — each request runs independently,
- // just like separate callers in a real app
- handleStressTestFetch(id, url, label);
- break;
- }
- }
- }
- };
+ };
}
// TODO: look into https://www.aaron-powell.com/posts/2019-02-08-golang-wasm-5-compiling-with-webpack/
async function loadGoWasm() {
- const resp = await fetch(GO_WASM_URL);
+ const resp = await fetch(GO_WASM_URL);
- if ("instantiateStreaming" in WebAssembly) {
- const wasmObj = await WebAssembly.instantiateStreaming(
- resp,
- go.importObject
- );
- goWasm = wasmObj.instance;
- go.run(goWasm);
- } else {
- const bytes = await resp.arrayBuffer();
- const wasmObj = await WebAssembly.instantiate(bytes, go.importObject);
- goWasm = wasmObj.instance;
- go.run(goWasm);
- }
+ if ('instantiateStreaming' in WebAssembly) {
+ const wasmObj = await WebAssembly.instantiateStreaming(resp, go.importObject);
+ goWasm = wasmObj.instance;
+ go.run(goWasm);
+ } else {
+ const bytes = await resp.arrayBuffer();
+ const wasmObj = await WebAssembly.instantiate(bytes, go.importObject);
+ goWasm = wasmObj.instance;
+ go.run(goWasm);
+ }
}
function setupRsGoBridge() {
- // (note: reason for intermediate `__rs_go_bridge__` object is to decrease global scope bloat
- // and to discourage users from trying to call those methods directly)
- self.__rs_go_bridge__ = {};
- self.__rs_go_bridge__.send_client_data = send_client_data;
- self.__rs_go_bridge__.start_new_mixnet_connection =
- start_new_mixnet_connection;
- self.__rs_go_bridge__.mix_fetch_initialised = mix_fetch_initialised;
- self.__rs_go_bridge__.finish_mixnet_connection = finish_mixnet_connection;
+ // (note: reason for intermediate `__rs_go_bridge__` object is to decrease global scope bloat
+ // and to discourage users from trying to call those methods directly)
+ self.__rs_go_bridge__ = {};
+ self.__rs_go_bridge__.send_client_data = send_client_data;
+ self.__rs_go_bridge__.start_new_mixnet_connection = start_new_mixnet_connection;
+ self.__rs_go_bridge__.mix_fetch_initialised = mix_fetch_initialised;
+ self.__rs_go_bridge__.finish_mixnet_connection = finish_mixnet_connection;
}
async function main() {
- sendLog("Worker starting...");
+ sendLog('Worker starting...');
- // load rust WASM package
- sendLog("Loading Rust WASM...");
- await wasm_bindgen(RUST_WASM_URL);
- sendLog("Loaded Rust WASM");
+ // load rust WASM package
+ sendLog('Loading Rust WASM...');
+ await wasm_bindgen(RUST_WASM_URL);
+ sendLog('Loaded Rust WASM');
- // load go WASM package
- sendLog("Loading Go WASM...");
- await loadGoWasm();
- sendLog("Loaded Go WASM");
+ // load go WASM package
+ sendLog('Loading Go WASM...');
+ await loadGoWasm();
+ sendLog('Loaded Go WASM');
- // sets up better stack traces in case of in-rust panics
- set_panic_hook();
+ // sets up better stack traces in case of in-rust panics
+ set_panic_hook();
- setupRsGoBridge();
+ setupRsGoBridge();
- goWasmSetLogging("trace");
+ goWasmSetLogging("trace");
- // Set up message handler (MixFetch will be started on demand)
- setupMessageHandler();
+ // Set up message handler (MixFetch will be started on demand)
+ setupMessageHandler();
- sendLog("Worker ready - click Start MixFetch to begin");
+ sendLog('Worker ready - click Start MixFetch to begin');
}
// Let's get started!