feedback7 G-TOOLS: a reconnect token cannot be claimed twice by two tabs started apart (PR #299 by @siruimei07)
Applied as-is. A tab that is still choosing a reconnect token now answers another tab's query for the same candidate hashes, so a tab started during the 150 ms window cannot adopt a token the earlier tab is also considering. The smaller tab id still wins, and a tab that already holds the token keeps it. Tests: node --test test/client-identity-session.test.js test/client-static.test.js — 319 pass, 0 fail.
This commit is contained in:
1 parent
ae22335cfe
commit
7c307d8613
3 files changed
+204
-4
No files matched your search
@@ -790,6 +790,58 @@ describe('identity (reconnect-token selection across tabs)', () => {
|
||||
assert.equal(ty, null);
|
||||
});
|
||||
|
||||
for (const [firstId, secondId, expected] of [
|
||||
['a-tab', 'b-tab', ['tokShared', null]],
|
||||
['b-tab', 'a-tab', [null, 'tokShared']],
|
||||
]) {
|
||||
test(`staggered channels: ${firstId} starts before ${secondId}`, async () => {
|
||||
const { createIdentity, CLAIM_QUERY_MS } = await mod('net.js');
|
||||
const hub = channelHub(), timers = fakeTimers(), local = memStorage();
|
||||
local.setItem('sp.tokens', JSON.stringify(['tokShared']));
|
||||
const firstSession = memStorage(), secondSession = memStorage();
|
||||
const first = createIdentity({ local, session: firstSession, tabId: firstId,
|
||||
channel: hub.create(), setTimeout: timers.setTimeout });
|
||||
const firstPick = first.init();
|
||||
timers.advance(30);
|
||||
// This channel did not exist when the first tab broadcast its query.
|
||||
const second = createIdentity({ local, session: secondSession, tabId: secondId,
|
||||
channel: hub.create(), setTimeout: timers.setTimeout });
|
||||
const secondPick = second.init();
|
||||
await new Promise((r) => setImmediate(r));
|
||||
timers.advance(CLAIM_QUERY_MS - 30);
|
||||
assert.equal(await firstPick, expected[0]);
|
||||
timers.advance(30);
|
||||
assert.equal(await secondPick, expected[1]);
|
||||
assert.deepEqual([first.getToken(), second.getToken()], expected);
|
||||
assert.deepEqual([firstSession.getItem('sp.token'), secondSession.getItem('sp.token')], expected);
|
||||
});
|
||||
}
|
||||
|
||||
test('staggered elections reserve fallback candidates while a live owner keeps its token', async () => {
|
||||
const { createIdentity, CLAIM_QUERY_MS } = await mod('net.js');
|
||||
const hub = channelHub(), timers = fakeTimers(), local = memStorage();
|
||||
const owner = createIdentity({ local, session: memStorage(), tabId: 'z-owner', channel: hub.create() });
|
||||
await owner.init();
|
||||
owner.saveToken('tokOwned');
|
||||
local.setItem('sp.tokens', JSON.stringify(['tokOwned', 'tokFree']));
|
||||
const first = createIdentity({ local, session: memStorage(), tabId: 'a-tab',
|
||||
channel: hub.create(), setTimeout: timers.setTimeout });
|
||||
const firstPick = first.init();
|
||||
timers.advance(30);
|
||||
const duplicateSession = memStorage();
|
||||
duplicateSession.setItem('sp.token', 'tokOwned');
|
||||
const second = createIdentity({ local, session: duplicateSession, tabId: 'b-tab',
|
||||
channel: hub.create(), setTimeout: timers.setTimeout });
|
||||
const secondPick = second.init();
|
||||
await new Promise((r) => setImmediate(r));
|
||||
timers.advance(CLAIM_QUERY_MS - 30);
|
||||
assert.equal(await firstPick, 'tokFree');
|
||||
timers.advance(30);
|
||||
assert.equal(await secondPick, null);
|
||||
assert.equal(duplicateSession.getItem('sp.token'), null, 'the copied live token is discarded');
|
||||
assert.equal(owner.getToken(), 'tokOwned', 'a live owner outranks even a smaller tab id');
|
||||
});
|
||||
|
||||
test('without BroadcastChannel only the own token is used; init is idempotent; welcome during init wins', async () => {
|
||||
const { createIdentity } = await mod('net.js');
|
||||
const local = memStorage();
|
||||
|
||||
Reference in new issue
Block a user