Skip to content

Commit 20cbc51

Browse files
committed
fix: allow reconnecting with the same peer id
This handles the case when one of the peers was disconnected without others knowing it, ex. iframe was reloaded from the inside. If another party continues to listen -> it will accept the handshakes with the same id by default. one.listen('two'); two.connect('one'); two.connect('one'); <-- this will re-establish connection
1 parent c60072d commit 20cbc51

2 files changed

Lines changed: 175 additions & 9 deletions

File tree

packages/core/src/peer.spec.ts

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,171 @@ describe('Peer', () => {
20072007
]);
20082008
});
20092009

2010+
test(`should disconnect exising when reconnecting with the same id`, () => {
2011+
const [one, two, oldThree] = createPeerTree();
2012+
clearMessages();
2013+
2014+
// 1-2, 1-3
2015+
// simulating reconnection 1-3, different peer instance, same id
2016+
const newThree = createPeer('three');
2017+
newThree.connect('one');
2018+
2019+
// should have received 'disconnect' message and become detached
2020+
expect(oldThree.peerConnections).toEqual(new Map());
2021+
expect(oldThree.knownPeers).toEqual(new Map([['three', knownMessages]]));
2022+
2023+
// network should stay the same
2024+
expect(one.peerConnections).toEqual(
2025+
new Map([
2026+
['two', new Set(['two'])],
2027+
['three', new Set(['three'])],
2028+
]),
2029+
);
2030+
expect(two.peerConnections).toEqual(new Map([['one', new Set(['one', 'three'])]]));
2031+
expect(newThree.peerConnections).toEqual(new Map([['one', new Set(['one', 'two'])]]));
2032+
2033+
// network should stay the same
2034+
for (const peer of [one, two, newThree]) {
2035+
expect(peer.knownPeers).toEqual(
2036+
new Map([
2037+
['one', knownMessages],
2038+
['two', knownMessages],
2039+
['three', knownMessages],
2040+
]),
2041+
);
2042+
}
2043+
2044+
expectMessages(onMessage, [
2045+
// on 3.connect(1) existing 'three' should get disconnected
2046+
{
2047+
three: {
2048+
from: 'one',
2049+
to: [],
2050+
payload: {
2051+
type: 'disconnect',
2052+
version: '1.0',
2053+
disconnected: 'one',
2054+
unreachable: ['one', 'two'],
2055+
},
2056+
},
2057+
},
2058+
{
2059+
two: {
2060+
from: 'one',
2061+
to: [],
2062+
payload: {
2063+
type: 'disconnect',
2064+
version: '1.0',
2065+
disconnected: 'three',
2066+
unreachable: ['three'],
2067+
},
2068+
},
2069+
},
2070+
// Handle handshake as in normal connection:
2071+
// 3 -> 1 handshake
2072+
// 1 -> 2 connect(3)
2073+
// 1 -> 3 handshake
2074+
// 3 -> 1 connect(3)
2075+
// 1 -> 3 connect(1,2)
2076+
{
2077+
one: {
2078+
from: 'three',
2079+
to: ['one'],
2080+
payload: {
2081+
type: 'handshake',
2082+
endpointId: 'one',
2083+
remoteId: 'three',
2084+
version: '1.0',
2085+
knownPeers: new Map([['three', [{ type: 'known', version: '1.0' }]]]),
2086+
},
2087+
},
2088+
},
2089+
{
2090+
two: {
2091+
from: 'one',
2092+
to: [],
2093+
payload: {
2094+
type: 'connect',
2095+
version: '1.0',
2096+
knownPeers: new Map([['three', [{ type: 'known', version: '1.0' }]]]),
2097+
connected: ['three'],
2098+
},
2099+
},
2100+
},
2101+
{
2102+
three: {
2103+
from: 'one',
2104+
to: ['three'],
2105+
payload: {
2106+
type: 'handshake',
2107+
endpointId: 'three',
2108+
remoteId: 'one',
2109+
version: '1.0',
2110+
knownPeers: new Map([
2111+
['one', [{ type: 'known', version: '1.0' }]],
2112+
['two', [{ type: 'known', version: '1.0' }]],
2113+
]),
2114+
},
2115+
},
2116+
},
2117+
{
2118+
one: {
2119+
from: 'three',
2120+
to: ['one'],
2121+
payload: {
2122+
type: 'connect',
2123+
version: '1.0',
2124+
knownPeers: new Map([['three', [{ type: 'known', version: '1.0' }]]]),
2125+
connected: ['three'],
2126+
},
2127+
},
2128+
},
2129+
{
2130+
three: {
2131+
from: 'one',
2132+
to: ['three'],
2133+
payload: {
2134+
type: 'connect',
2135+
version: '1.0',
2136+
knownPeers: new Map([
2137+
['one', [{ type: 'known', version: '1.0' }]],
2138+
['two', [{ type: 'known', version: '1.0' }]],
2139+
]),
2140+
connected: ['one', 'two'],
2141+
},
2142+
},
2143+
},
2144+
]);
2145+
expectErrors(onError, []);
2146+
});
2147+
2148+
test(`should not disconnect exising when reconnecting with the same id and using filter`, () => {
2149+
const one = createPeer('one');
2150+
const two = createPeer('two');
2151+
2152+
// 1-2
2153+
one.listen(({ from }) => !one.knownPeers.has(from));
2154+
two.connect('one');
2155+
2156+
for (const peer of [one, two]) {
2157+
expect(peer.knownPeers).toEqual(
2158+
new Map([
2159+
['one', knownMessages],
2160+
['two', knownMessages],
2161+
]),
2162+
);
2163+
}
2164+
2165+
clearMessages();
2166+
2167+
// simulating reconnection 1-2, different peer instance, same id
2168+
const newTwo = createPeer('two');
2169+
newTwo.connect('one');
2170+
2171+
// no messages should be sent, handshake should be blocked by filter
2172+
expectMessages(onMessage, []);
2173+
});
2174+
20102175
test(`should register new messages`, () => {
20112176
const one = createPeer('one', { knownMessages: [] });
20122177
const two = createPeer('two', { knownMessages: [] });

packages/core/src/peer.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -461,14 +461,7 @@ export class MessagePeer<M extends Message> implements MessagePeerType<M> {
461461
const { remoteId } = payload;
462462

463463
// -> (structure ok; 'handshake' for us)
464-
// 1. Do we already have a connection to this peer?
465-
if (this.#endpoints.has(remoteId)) {
466-
logger(`PEER(${this.id}): HS declined: already connected to '${remoteId}'`);
467-
return;
468-
}
469-
470-
// -> (structure ok; 'handshake' for us; no existing; accepting)
471-
// 2. Does the peer match our filters?
464+
// 1. Does the peer match our filters?
472465
if (!eventMatchesFilters(event, this.#connectionFilters)) {
473466
logger(
474467
`PEER(${this.id}): HS declined: connection from '${remoteId}' does not match any of the filters:`,
@@ -477,7 +470,15 @@ export class MessagePeer<M extends Message> implements MessagePeerType<M> {
477470
return;
478471
}
479472

480-
// -> (structure ok; 'handshake' for us; no existing; accepting; matches filters)
473+
// -> (structure ok; 'handshake' for us; matches filters)
474+
// 2. Do we already have a connection to this peer?
475+
const exisingEndpoint = this.#endpoints.get(remoteId);
476+
if (exisingEndpoint) {
477+
logger(`PEER(${this.id}): already connected to '${remoteId}' -> disconnecting`);
478+
this.#disconnectEndpoint(exisingEndpoint);
479+
}
480+
481+
// -> (structure ok; 'handshake' for us; matches filters; handled exising)
481482
// 3. Create a new endpoint for the peer, configure, and register it
482483
const [port] = ports;
483484

0 commit comments

Comments
 (0)