diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-17 14:32:46 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-17 14:32:46 +0200 |
| commit | 28be8579995aef6df3e87b0f293a4fdd4aa4b072 (patch) | |
| tree | a7ce6ea4d5cd8d2a2840496bc8c7017ffc3f7038 /packages/meshbay-hub/tests/harness/reconnect_race_harness.mjs | |
| parent | 621305ab0363a43edc62a69664de7254f03514e7 (diff) | |
| download | meshbay-28be8579995aef6df3e87b0f293a4fdd4aa4b072.tar.gz | |
fix(spa): a film playing waits for the session after a reconnect
Stream credit sent before the new handshake was refused, and the node's
"Handshake required" replaced the film.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests/harness/reconnect_race_harness.mjs')
| -rw-r--r-- | packages/meshbay-hub/tests/harness/reconnect_race_harness.mjs | 29 |
1 files changed, 24 insertions, 5 deletions
diff --git a/packages/meshbay-hub/tests/harness/reconnect_race_harness.mjs b/packages/meshbay-hub/tests/harness/reconnect_race_harness.mjs index 0f1cd979..606598da 100644 --- a/packages/meshbay-hub/tests/harness/reconnect_race_harness.mjs +++ b/packages/meshbay-hub/tests/harness/reconnect_race_harness.mjs @@ -30,6 +30,7 @@ const { rttMs = 300, // one request → its reply, during the handshake lateCloseMs = 0, // >0: the replaced channel's close event fires this late runUntil = 300000, + stream = false, // a film playing instead of a download (see below) } = JSON.parse(process.argv[4] || '{}'); for (const level of ['log', 'warn', 'error', 'info', 'debug']) { @@ -106,6 +107,7 @@ globalThis.fetch = async (url) => ({ const OPENING = new Set(['handshake', 'handshake_response', 'keypair_bundle_fetch', 'gek_bundle_fetch', 'join_request', 'device_add_request', 'device_hello']); let refusedBeforeHandshake = 0; +const refusedTypes = new Set(); let served = 0; const pcs = []; @@ -127,6 +129,7 @@ class NodeSession { const req_id = msg.req_id; if (!this.authed && !OPENING.has(msg.type)) { refusedBeforeHandshake += 1; + refusedTypes.add(msg.type); return this.reply({ type: 'error', detail: 'Handshake required', req_id }, 1); } switch (msg.type) { @@ -210,10 +213,26 @@ main.addReconnectListener(() => { reconnected += 1; }); const written = []; const writable = { write: async (bytes) => { written.push(bytes[0]); } }; -main.connect('node', 'token', 'group', new Uint8Array(32), identity, null, 'u', 'uid', - undefined, undefined, '', 'pk') - .then(() => pipelinedDownload(main, 'gek', 'f'.repeat(64), chunks, null, writable, null, '')) - .then(() => { outcome = 'done'; }, (err) => { outcome = `failed: ${err.message}`; }); +// A film playing (`stream`): the player goes on granting the node room for +// segments as it plays from its buffer, and on telling it what it sees, through +// the connection loss and the reconnect, as video-player.js does. A refusal of +// either is a node error the player shows in place of the film. +const opened = main.connect('node', 'token', 'group', new Uint8Array(32), identity, null, + 'u', 'uid', undefined, undefined, '', 'pk'); +if (stream) { + opened.then(() => { + main.requestStream('f'.repeat(64)); + setInterval(() => { + main.grantStreamCredit(1); + main.sendStreamDiag({ buffered: 1 }); + }, 50); + outcome = 'done'; + }, (err) => { outcome = `failed: ${err.message}`; }); +} else { + opened + .then(() => pipelinedDownload(main, 'gek', 'f'.repeat(64), chunks, null, writable, null, '')) + .then(() => { outcome = 'done'; }, (err) => { outcome = `failed: ${err.message}`; }); +} await run(hangAt); pcs[0].channel.session.hung = true; @@ -223,7 +242,7 @@ pcs[0].onconnectionstatechange(); await run(runUntil); process.stdout.write(JSON.stringify({ - outcome, reconnected, refusedBeforeHandshake, served, + outcome, reconnected, refusedBeforeHandshake, refusedTypes: [...refusedTypes], served, written: written.length, inOrder: written.every((b, i) => b === (i & 255)), connections: pcs.length, |