Make internal-dev less debug specific
- Evergreen the drip test - Add setup config to MixFetch instance
This commit is contained in:
@@ -20,6 +20,27 @@
|
||||
<label><input type="radio" name="gateway-mode" value="random" />
|
||||
Random Gateway</label>
|
||||
</div>
|
||||
<details style="margin-top: 8px">
|
||||
<summary style="cursor: pointer; font-size: 0.9em; color: #555">Advanced Options</summary>
|
||||
<div style="margin-top: 6px; padding: 8px; background: #f9f9f9; font-size: 0.9em">
|
||||
<div>
|
||||
<label><input type="checkbox" id="opt-force-tls" checked /> Force TLS</label>
|
||||
</div>
|
||||
<div style="margin-top: 4px">
|
||||
<label>Client ID: <input type="text" id="opt-client-id" value="my-client" size="20" /></label>
|
||||
</div>
|
||||
<div style="margin-top: 4px">
|
||||
<label><input type="checkbox" id="opt-disable-poisson" checked /> Disable Poisson packet distribution</label>
|
||||
</div>
|
||||
<div style="margin-top: 4px">
|
||||
<label><input type="checkbox" id="opt-disable-cover" checked /> Disable cover traffic</label>
|
||||
</div>
|
||||
<div style="margin-top: 4px">
|
||||
<label>Request timeout (ms): <input type="number" id="opt-request-timeout" value="60000" min="1000" max="300000" style="width: 80px" /></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div style="margin-top: 8px">
|
||||
<button id="start-mixfetch">Start MixFetch</button>
|
||||
<span id="mixfetch-status" style="margin-left: 10px; color: gray">Not started</span>
|
||||
@@ -79,30 +100,29 @@
|
||||
<div id="stress-drip-opts" style="display: none; margin-top: 8px; padding: 8px; background: #f9f9f9">
|
||||
<p style="margin: 0 0 4px; font-size: 0.85em; color: #666">
|
||||
Uses <code>httpbin.org/drip</code> to slowly drip bytes over a set duration,
|
||||
keeping connections alive for a controlled time. Profiles are designed to
|
||||
straddle the Go timeout boundary — some will finish just in time,
|
||||
others will be mid-transfer when the timeout fires. This is how we
|
||||
reproduce the Go runtime panic (see DEBUGGING_NOTES.md).
|
||||
keeping connections alive for a controlled time. Profiles are computed from
|
||||
the Go timeout value below, so they always straddle the configured boundary
|
||||
— some finish well before the timeout, others are mid-transfer when it fires.
|
||||
</p>
|
||||
<table style="font-size: 0.9em; border-collapse: collapse">
|
||||
<tr>
|
||||
<td style="padding: 1px 10px 1px 0"><b>safe</b></td>
|
||||
<td>30s</td>
|
||||
<td style="padding-left: 10px; color: #666">well under 60s timeout</td>
|
||||
<td>~50% of timeout</td>
|
||||
<td style="padding-left: 10px; color: #666">well under the limit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 1px 10px 1px 0"><b>boundary</b></td>
|
||||
<td>55s</td>
|
||||
<td style="padding-left: 10px; color: #666">55s server + ~10s mixnet latency = over</td>
|
||||
<td>~92% of timeout</td>
|
||||
<td style="padding-left: 10px; color: #666">server-side OK, but mixnet latency pushes it over</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 1px 10px 1px 0"><b>over</b></td>
|
||||
<td>65s</td>
|
||||
<td style="padding-left: 10px; color: #666">exceeds 60s timeout on its own</td>
|
||||
<td>~108% of timeout</td>
|
||||
<td style="padding-left: 10px; color: #666">exceeds the timeout on its own</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 1px 10px 1px 0"><b>slow-start</b></td>
|
||||
<td>10s delay + 50s</td>
|
||||
<td>~17% delay + ~83% drip</td>
|
||||
<td style="padding-left: 10px; color: #666">late first byte, then long transfer</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -48,8 +48,8 @@ class WebWorkerClient {
|
||||
};
|
||||
}
|
||||
|
||||
startMixFetch = (preferredGateway) => {
|
||||
this.worker.postMessage({ kind: "StartMixFetch", args: { preferredGateway } });
|
||||
startMixFetch = (preferredGateway, setupOpts) => {
|
||||
this.worker.postMessage({ kind: "StartMixFetch", args: { preferredGateway, setupOpts } });
|
||||
};
|
||||
|
||||
doFetch = (target) => {
|
||||
@@ -82,12 +82,24 @@ async function main() {
|
||||
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),
|
||||
};
|
||||
|
||||
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}` : ""})...`);
|
||||
client.startMixFetch(preferredGateway);
|
||||
console.log("Setup options:", setupOpts);
|
||||
client.startMixFetch(preferredGateway, setupOpts);
|
||||
};
|
||||
|
||||
document.querySelector("#fetch-button-1").onclick = () => doFetch(1);
|
||||
@@ -109,7 +121,7 @@ async function main() {
|
||||
updateStatus("stress-test-status", "Running...", "orange");
|
||||
client.setGoTimeout(goTimeoutMs);
|
||||
|
||||
const requests = generateStressRequests(count, mode);
|
||||
const requests = generateStressRequests(count, mode, goTimeoutMs);
|
||||
stressTest = {
|
||||
count,
|
||||
mode,
|
||||
@@ -184,14 +196,16 @@ const STRESS_PROFILES = [
|
||||
{ label: "xlarge", bytes: 1048576 },
|
||||
];
|
||||
|
||||
const DRIP_PROFILES = [
|
||||
{ label: "safe", dripDuration: 30, dripDelay: 0, dripBytes: 100 },
|
||||
{ label: "boundary", dripDuration: 55, dripDelay: 0, dripBytes: 100 },
|
||||
{ label: "over", dripDuration: 65, dripDelay: 0, dripBytes: 100 },
|
||||
{ label: "slow-start", dripDuration: 50, dripDelay: 10, dripBytes: 100 },
|
||||
];
|
||||
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 },
|
||||
];
|
||||
}
|
||||
|
||||
function generateStressRequests(count, mode) {
|
||||
function generateStressRequests(count, mode, timeoutMs) {
|
||||
const requests = [];
|
||||
if (mode === "uniform") {
|
||||
const baseUrl = document.getElementById("stress-test-url").value;
|
||||
@@ -199,8 +213,9 @@ function generateStressRequests(count, mode) {
|
||||
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 = DRIP_PROFILES[Math.floor(Math.random() * DRIP_PROFILES.length)];
|
||||
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`,
|
||||
@@ -324,7 +339,8 @@ function onStressTestFetchResult(result) {
|
||||
console.log("Completion order:", stressTest.completionOrder);
|
||||
|
||||
// Per-profile breakdown in console
|
||||
const profileList = stressTest.mode === "drip" ? DRIP_PROFILES : STRESS_PROFILES;
|
||||
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) {
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "create-wasm-app",
|
||||
"name": "mix-fetch-internal-dev",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "create-wasm-app",
|
||||
"name": "mix-fetch-internal-dev",
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "create-wasm-app",
|
||||
"name": "mix-fetch-internal-dev",
|
||||
"version": "0.1.0",
|
||||
"description": "create an app to fetch data through the mixnet",
|
||||
"description": "Internal dev/stress-test harness for mix-fetch WASM",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"create-wasm-app": ".bin/create-wasm-app.js"
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
|
||||
"url": "git+https://github.com/nymtech/nym.git"
|
||||
},
|
||||
"keywords": [
|
||||
"webassembly",
|
||||
|
||||
@@ -12,31 +12,31 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
const RUST_WASM_URL = "mix_fetch_wasm_bg.wasm"
|
||||
const GO_WASM_URL = "go_conn.wasm"
|
||||
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,281 +45,271 @@ 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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function wasm_bindgenSetup() {
|
||||
const preferredGateway = "6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
const validator = 'https://qa-nym-api.qa.nymte.ch/api';
|
||||
/*
|
||||
* ── Alternative MixFetch setup ──────────────────────────────
|
||||
*
|
||||
* Shows how to pass custom MixFetchConfig options: specific network requester
|
||||
* address, validator URL, or debug overrides. Uncomment and adapt if you
|
||||
* need non-default setup for local testing.
|
||||
*
|
||||
* async function wasm_bindgenSetup() {
|
||||
* const preferredGateway = "6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
* const validator = 'https://qa-nym-api.qa.nymte.ch/api';
|
||||
*
|
||||
* const mixFetchNetworkRequesterAddress = "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi...";
|
||||
*
|
||||
* // MixFetchConfigOpts: { id?, nymApi?, nyxd?, debug? }
|
||||
* const differentDebug = default_debug()
|
||||
* differentDebug.traffic.use_extended_packet_size = true
|
||||
* differentDebug.traffic.average_packet_delay_ms = 666
|
||||
*
|
||||
* const config = new MixFetchConfig(mixFetchNetworkRequesterAddress, {debug: differentDebug});
|
||||
*
|
||||
* // MixFetchOptsSimple: { preferredGateway?, storagePassphrase? }
|
||||
* await setupMixFetchWithConfig(config)
|
||||
* }
|
||||
*/
|
||||
|
||||
// local
|
||||
const mixFetchNetworkRequesterAddress = "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi.6uQGnCqSczpXwh86NdbsCoDDXuqZQM9Uwko8GE7uC9g8@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
// const mixFetchNetworkRequesterAddress= "GqiGWmKRCbGQFSqH88BzLKijvZgipnqhmbNFsmkZw84t.4L8sXFuAUyUYyHZYgMdM3AtiusKnYUft6Pd8e41rrCHA@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
|
||||
async function nativeSetup(preferredGateway, setupOpts = {}) {
|
||||
sendLog("Setting up MixFetch...");
|
||||
if (preferredGateway) {
|
||||
sendLog(`Using preferred gateway: ${preferredGateway}`);
|
||||
} else {
|
||||
sendLog("Using random gateway selection");
|
||||
}
|
||||
|
||||
// STEP 1. construct config
|
||||
// those are just some examples, there are obviously more permutations;
|
||||
// note, the extra optional argument is of the following type:
|
||||
// /*
|
||||
// export interface MixFetchConfigOpts {
|
||||
// id?: string;
|
||||
// nymApi?: string;
|
||||
// nyxd?: string;
|
||||
// debug?: DebugWasm;
|
||||
// }
|
||||
// */
|
||||
//
|
||||
// const debug = no_cover_debug()
|
||||
//
|
||||
// #1
|
||||
// const config = new MixFetchConfig(mixFetchNetworkRequesterAddress, { id: 'my-awesome-mix-fetch-client', nymApi: validator, debug: debug} );
|
||||
// #2
|
||||
// const config = new MixFetchConfig(mixFetchNetworkRequesterAddress, { nymApi: validator, debug: debug} );
|
||||
// #3
|
||||
// const config = new MixFetchConfig(mixFetchNetworkRequesterAddress, { id: 'my-awesome-mix-fetch-client' } );
|
||||
//
|
||||
// #4
|
||||
const differentDebug = default_debug()
|
||||
const updatedTraffic = differentDebug.traffic;
|
||||
updatedTraffic.use_extended_packet_size = true
|
||||
updatedTraffic.average_packet_delay_ms = 666;
|
||||
differentDebug.traffic = updatedTraffic;
|
||||
const {
|
||||
forceTls = true,
|
||||
clientId = "my-client",
|
||||
disablePoisson = true,
|
||||
disableCover = true,
|
||||
requestTimeoutMs = 60000,
|
||||
} = setupOpts;
|
||||
|
||||
const config = new MixFetchConfig(mixFetchNetworkRequesterAddress, {debug: differentDebug});
|
||||
//
|
||||
// // STEP 2. setup the client
|
||||
// // note, the extra optional argument is of the following type:
|
||||
// /*
|
||||
// export interface MixFetchOptsSimple {
|
||||
// preferredGateway?: string;
|
||||
// storagePassphrase?: string;
|
||||
// }
|
||||
// */
|
||||
// #1
|
||||
await setupMixFetchWithConfig(config)
|
||||
//
|
||||
// #2
|
||||
// await setupMixFetchWithConfig(config, { storagePassphrase: "foomp" })
|
||||
//
|
||||
// #3
|
||||
// await setupMixFetchWithConfig(config, { storagePassphrase: "foomp", preferredGateway })
|
||||
const noCoverTrafficOverride = {
|
||||
traffic: { disableMainPoissonPacketDistribution: disablePoisson },
|
||||
coverTraffic: { disableLoopCoverTrafficStream: disableCover },
|
||||
};
|
||||
const mixFetchOverride = {
|
||||
requestTimeoutMs,
|
||||
};
|
||||
|
||||
const opts = {
|
||||
forceTls,
|
||||
clientId,
|
||||
clientOverride: noCoverTrafficOverride,
|
||||
mixFetchOverride,
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
async function nativeSetup(preferredGateway) {
|
||||
sendLog('Setting up MixFetch...');
|
||||
if (preferredGateway) {
|
||||
sendLog(`Using preferred gateway: ${preferredGateway}`);
|
||||
} else {
|
||||
sendLog('Using random gateway selection');
|
||||
}
|
||||
async function startMixFetch(preferredGateway, setupOpts) {
|
||||
sendLog("Instantiating MixFetch...");
|
||||
|
||||
const noCoverTrafficOverride = {
|
||||
traffic: {disableMainPoissonPacketDistribution: true},
|
||||
coverTraffic: {disableLoopCoverTrafficStream: true},
|
||||
}
|
||||
const mixFetchOverride = {
|
||||
requestTimeoutMs: 60000
|
||||
}
|
||||
|
||||
const opts = {
|
||||
forceTls: true,
|
||||
clientId: "my-client",
|
||||
clientOverride: noCoverTrafficOverride,
|
||||
mixFetchOverride,
|
||||
};
|
||||
|
||||
if (preferredGateway) {
|
||||
opts.preferredGateway = preferredGateway;
|
||||
}
|
||||
|
||||
sendLog('Calling setupMixFetch...');
|
||||
await setupMixFetch(opts);
|
||||
sendLog('setupMixFetch completed');
|
||||
}
|
||||
|
||||
async function startMixFetch(preferredGateway) {
|
||||
sendLog('Instantiating MixFetch...');
|
||||
|
||||
try {
|
||||
await nativeSetup(preferredGateway);
|
||||
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 } = event.data.args;
|
||||
await startMixFetch(preferredGateway);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 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!
|
||||
main();
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user