@dxos/network-manager 0.6.13 → 0.6.14-main.1366248
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-XYSYUN63.mjs → chunk-UEVA7BFW.mjs} +1323 -1102
- package/dist/lib/browser/chunk-UEVA7BFW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +9 -19
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +30 -37
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/transport/tcp/index.mjs +38 -0
- package/dist/lib/browser/transport/tcp/index.mjs.map +7 -0
- package/dist/lib/node/{chunk-4YAYC7WN.cjs → chunk-LK5D44SA.cjs} +1336 -1239
- package/dist/lib/node/chunk-LK5D44SA.cjs.map +7 -0
- package/dist/lib/node/index.cjs +27 -37
- package/dist/lib/node/index.cjs.map +2 -2
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +34 -38
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node/transport/tcp/index.cjs +191 -0
- package/dist/lib/node/transport/tcp/index.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-Y5TD36KR.mjs +4413 -0
- package/dist/lib/node-esm/chunk-Y5TD36KR.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +50 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +285 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/transport/tcp/index.mjs +159 -0
- package/dist/lib/node-esm/transport/tcp/index.mjs.map +7 -0
- package/dist/types/src/network-manager.d.ts +2 -1
- package/dist/types/src/network-manager.d.ts.map +1 -1
- package/dist/types/src/signal/ice.d.ts.map +1 -1
- package/dist/types/src/signal/integration.node.test.d.ts +2 -0
- package/dist/types/src/signal/integration.node.test.d.ts.map +1 -0
- package/dist/types/src/signal/swarm-messenger.node.test.d.ts +2 -0
- package/dist/types/src/signal/swarm-messenger.node.test.d.ts.map +1 -0
- package/dist/types/src/swarm/connection.d.ts.map +1 -1
- package/dist/types/src/swarm/peer.d.ts.map +1 -1
- package/dist/types/src/swarm/swarm.d.ts +2 -1
- package/dist/types/src/swarm/swarm.d.ts.map +1 -1
- package/dist/types/src/testing/test-builder.d.ts +2 -2
- package/dist/types/src/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-wire-protocol.d.ts +1 -2
- package/dist/types/src/testing/test-wire-protocol.d.ts.map +1 -1
- package/dist/types/src/tests/basic-test-suite.d.ts.map +1 -1
- package/dist/types/src/tests/property-test-suite.d.ts.map +1 -1
- package/dist/types/src/tests/tcp-transport.node.test.d.ts +2 -0
- package/dist/types/src/tests/tcp-transport.node.test.d.ts.map +1 -0
- package/dist/types/src/tests/utils.d.ts.map +1 -1
- package/dist/types/src/transport/index.d.ts +1 -5
- package/dist/types/src/transport/index.d.ts.map +1 -1
- package/dist/types/src/transport/memory-transport.d.ts +2 -2
- package/dist/types/src/transport/memory-transport.d.ts.map +1 -1
- package/dist/types/src/transport/tcp/index.d.ts +2 -0
- package/dist/types/src/transport/tcp/index.d.ts.map +1 -0
- package/dist/types/src/transport/{tcp-transport.browser.d.ts → tcp/tcp-transport.browser.d.ts} +3 -3
- package/dist/types/src/transport/tcp/tcp-transport.browser.d.ts.map +1 -0
- package/dist/types/src/transport/{tcp-transport.d.ts → tcp/tcp-transport.d.ts} +3 -3
- package/dist/types/src/transport/tcp/tcp-transport.d.ts.map +1 -0
- package/dist/types/src/transport/transport.d.ts +7 -6
- package/dist/types/src/transport/transport.d.ts.map +1 -1
- package/dist/types/src/transport/webrtc/index.d.ts +4 -0
- package/dist/types/src/transport/webrtc/index.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts +14 -0
- package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts +68 -0
- package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts +33 -0
- package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts +2 -0
- package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts +4 -0
- package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts.map +1 -0
- package/dist/types/src/transport/{simplepeer-transport-proxy.d.ts → webrtc/rtc-transport-proxy.d.ts} +10 -12
- package/dist/types/src/transport/webrtc/rtc-transport-proxy.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts +2 -0
- package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts.map +1 -0
- package/dist/types/src/transport/{simplepeer-transport-service.d.ts → webrtc/rtc-transport-service.d.ts} +9 -7
- package/dist/types/src/transport/webrtc/rtc-transport-service.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts +4 -0
- package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts +2 -0
- package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/test-utils.d.ts +5 -0
- package/dist/types/src/transport/webrtc/test-utils.d.ts.map +1 -0
- package/dist/types/src/transport/webrtc/utils.d.ts +3 -0
- package/dist/types/src/transport/webrtc/utils.d.ts.map +1 -0
- package/package.json +53 -36
- package/src/network-manager.ts +5 -13
- package/src/signal/ice.test.ts +1 -3
- package/src/signal/ice.ts +6 -1
- package/src/signal/{integration.test.ts → integration.node.test.ts} +9 -15
- package/src/signal/{swarm-messenger.test.ts → swarm-messenger.node.test.ts} +13 -23
- package/src/swarm/connection-limiter.test.ts +3 -6
- package/src/swarm/connection.test.ts +63 -38
- package/src/swarm/connection.ts +7 -7
- package/src/swarm/peer.ts +4 -1
- package/src/swarm/swarm.test.ts +10 -12
- package/src/swarm/swarm.ts +16 -3
- package/src/testing/test-builder.ts +14 -29
- package/src/testing/test-wire-protocol.ts +7 -8
- package/src/tests/basic-test-suite.ts +32 -31
- package/src/tests/memory-transport.test.ts +40 -42
- package/src/tests/property-test-suite.ts +21 -22
- package/src/tests/tcp-transport.node.test.ts +65 -0
- package/src/tests/utils.ts +3 -2
- package/src/tests/webrtc-transport.test.ts +10 -10
- package/src/transport/index.ts +1 -5
- package/src/transport/memory-transport.ts +2 -0
- package/src/transport/tcp/index.ts +5 -0
- package/src/transport/{tcp-transport.browser.ts → tcp/tcp-transport.browser.ts} +7 -3
- package/src/transport/{tcp-transport.ts → tcp/tcp-transport.ts} +3 -1
- package/src/transport/transport.ts +8 -7
- package/src/transport/webrtc/index.ts +7 -0
- package/src/transport/webrtc/rtc-connection-factory.ts +82 -0
- package/src/transport/webrtc/rtc-peer-connection.ts +472 -0
- package/src/transport/webrtc/rtc-transport-channel.test.ts +176 -0
- package/src/transport/webrtc/rtc-transport-channel.ts +195 -0
- package/src/transport/webrtc/rtc-transport-factory.ts +28 -0
- package/src/transport/webrtc/rtc-transport-proxy.test.ts +413 -0
- package/src/transport/webrtc/rtc-transport-proxy.ts +264 -0
- package/src/transport/webrtc/rtc-transport-service.ts +192 -0
- package/src/transport/webrtc/rtc-transport-stats.ts +67 -0
- package/src/transport/webrtc/rtc-transport.test.ts +210 -0
- package/src/transport/webrtc/test-utils.ts +22 -0
- package/src/transport/webrtc/utils.ts +36 -0
- package/src/typings.d.ts +8 -2
- package/dist/lib/browser/chunk-XYSYUN63.mjs.map +0 -7
- package/dist/lib/node/chunk-4YAYC7WN.cjs.map +0 -7
- package/dist/types/src/signal/integration.test.d.ts +0 -2
- package/dist/types/src/signal/integration.test.d.ts.map +0 -1
- package/dist/types/src/signal/swarm-messenger.test.d.ts +0 -2
- package/dist/types/src/signal/swarm-messenger.test.d.ts.map +0 -1
- package/dist/types/src/tests/tcp-transport.test.d.ts +0 -2
- package/dist/types/src/tests/tcp-transport.test.d.ts.map +0 -1
- package/dist/types/src/transport/libdatachannel-transport.d.ts +0 -42
- package/dist/types/src/transport/libdatachannel-transport.d.ts.map +0 -1
- package/dist/types/src/transport/libdatachannel-transport.test.d.ts +0 -2
- package/dist/types/src/transport/libdatachannel-transport.test.d.ts.map +0 -1
- package/dist/types/src/transport/memory-transport.test.d.ts +0 -2
- package/dist/types/src/transport/memory-transport.test.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-simple-peer.d.ts +0 -2
- package/dist/types/src/transport/simplepeer-simple-peer.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts +0 -2
- package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-transport-proxy.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-transport-service.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-transport.d.ts +0 -36
- package/dist/types/src/transport/simplepeer-transport.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-transport.test.d.ts +0 -2
- package/dist/types/src/transport/simplepeer-transport.test.d.ts.map +0 -1
- package/dist/types/src/transport/tcp-transport.browser.d.ts.map +0 -1
- package/dist/types/src/transport/tcp-transport.d.ts.map +0 -1
- package/dist/types/src/transport/webrtc.d.ts +0 -6
- package/dist/types/src/transport/webrtc.d.ts.map +0 -1
- package/src/globals.d.ts +0 -7
- package/src/tests/tcp-transport.test.ts +0 -67
- package/src/transport/libdatachannel-transport.test.ts +0 -100
- package/src/transport/libdatachannel-transport.ts +0 -376
- package/src/transport/memory-transport.test.ts +0 -74
- package/src/transport/simplepeer-simple-peer.ts +0 -26
- package/src/transport/simplepeer-transport-proxy-test.ts +0 -181
- package/src/transport/simplepeer-transport-proxy.ts +0 -246
- package/src/transport/simplepeer-transport-service.ts +0 -160
- package/src/transport/simplepeer-transport.test.ts +0 -61
- package/src/transport/simplepeer-transport.ts +0 -250
- package/src/transport/webrtc.ts +0 -15
|
@@ -2,78 +2,103 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
5
7
|
import { sleep } from '@dxos/async';
|
|
6
8
|
import { PublicKey } from '@dxos/keys';
|
|
7
|
-
import { describe, test } from '@dxos/test';
|
|
8
9
|
|
|
9
10
|
import { Connection } from './connection';
|
|
10
11
|
import { TestWireProtocol } from '../testing/test-wire-protocol';
|
|
11
|
-
import {
|
|
12
|
+
import { createRtcTransportFactory } from '../transport';
|
|
13
|
+
import { chooseInitiatorPeer } from '../transport/webrtc/utils';
|
|
12
14
|
|
|
13
15
|
describe('Connection', () => {
|
|
16
|
+
test('responder opens after initiator', async () => {
|
|
17
|
+
const { initiator, responder } = createPeerKeys();
|
|
18
|
+
await connectionTest({
|
|
19
|
+
fastConnectionKey: initiator,
|
|
20
|
+
slowConnectionKey: responder,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
14
24
|
test('initiator opens after responder', async () => {
|
|
25
|
+
const { initiator, responder } = createPeerKeys();
|
|
26
|
+
await connectionTest({
|
|
27
|
+
fastConnectionKey: responder,
|
|
28
|
+
slowConnectionKey: initiator,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const connectionTest = async (setup: { fastConnectionKey: PublicKey; slowConnectionKey: PublicKey }) => {
|
|
15
33
|
const [topic, sessionId] = PublicKey.randomSequence();
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const
|
|
34
|
+
const slowPeer = { peerKey: setup.slowConnectionKey.toHex() };
|
|
35
|
+
const fastPeer = { peerKey: setup.fastConnectionKey.toHex() };
|
|
36
|
+
|
|
37
|
+
const slowPeerProtocol = new TestWireProtocol();
|
|
38
|
+
const slowConnection = new Connection(
|
|
20
39
|
topic,
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
slowPeer,
|
|
41
|
+
fastPeer,
|
|
23
42
|
sessionId,
|
|
24
43
|
true,
|
|
25
44
|
{
|
|
26
|
-
offer: async (msg) => {
|
|
27
|
-
return { accept: true };
|
|
28
|
-
},
|
|
45
|
+
offer: async (msg) => ({ accept: true }),
|
|
29
46
|
signal: async (msg) => {
|
|
30
|
-
await
|
|
47
|
+
await fastConnection.signal(msg);
|
|
31
48
|
},
|
|
32
49
|
},
|
|
33
|
-
|
|
50
|
+
slowPeerProtocol.factory({
|
|
34
51
|
initiator: true,
|
|
35
|
-
localPeerId: PublicKey.from(
|
|
36
|
-
remotePeerId: PublicKey.from(
|
|
52
|
+
localPeerId: PublicKey.from(slowPeer.peerKey),
|
|
53
|
+
remotePeerId: PublicKey.from(fastPeer.peerKey),
|
|
37
54
|
topic,
|
|
38
55
|
}),
|
|
39
|
-
|
|
40
|
-
process.env.MOCHA_ENV === 'nodejs' ? createLibDataChannelTransportFactory() : createSimplePeerTransportFactory(),
|
|
56
|
+
createRtcTransportFactory(),
|
|
41
57
|
);
|
|
42
58
|
|
|
43
|
-
const
|
|
44
|
-
const
|
|
59
|
+
const fastPeerProtocol = new TestWireProtocol();
|
|
60
|
+
const fastConnection = new Connection(
|
|
45
61
|
topic,
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
fastPeer,
|
|
63
|
+
slowPeer,
|
|
48
64
|
sessionId,
|
|
49
65
|
false,
|
|
50
66
|
{
|
|
51
|
-
offer: async (msg) => {
|
|
52
|
-
return { accept: true };
|
|
53
|
-
},
|
|
67
|
+
offer: async (msg) => ({ accept: true }),
|
|
54
68
|
signal: async (msg) => {
|
|
55
|
-
await
|
|
69
|
+
await slowConnection.signal(msg);
|
|
56
70
|
},
|
|
57
71
|
},
|
|
58
|
-
|
|
72
|
+
fastPeerProtocol.factory({
|
|
59
73
|
initiator: false,
|
|
60
|
-
localPeerId: PublicKey.from(
|
|
61
|
-
remotePeerId: PublicKey.from(
|
|
74
|
+
localPeerId: PublicKey.from(fastPeer.peerKey),
|
|
75
|
+
remotePeerId: PublicKey.from(slowPeer.peerKey),
|
|
62
76
|
topic,
|
|
63
77
|
}),
|
|
64
|
-
|
|
65
|
-
process.env.MOCHA_ENV === 'nodejs' ? createLibDataChannelTransportFactory() : createSimplePeerTransportFactory(),
|
|
78
|
+
createRtcTransportFactory(),
|
|
66
79
|
);
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
await
|
|
70
|
-
await sleep(
|
|
81
|
+
fastConnection.initiate();
|
|
82
|
+
await fastConnection.openConnection();
|
|
83
|
+
await sleep(200);
|
|
84
|
+
|
|
85
|
+
slowConnection.initiate();
|
|
86
|
+
await slowConnection.openConnection();
|
|
71
87
|
|
|
72
|
-
connection1.initiate();
|
|
73
|
-
await connection1.openConnection();
|
|
74
88
|
await Promise.all([
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
slowPeerProtocol.testConnection(PublicKey.from(fastPeer.peerKey), 'test message 1'),
|
|
90
|
+
fastPeerProtocol.testConnection(PublicKey.from(slowPeer.peerKey), 'test message 2'),
|
|
77
91
|
]);
|
|
78
|
-
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const createPeerKeys = () => {
|
|
95
|
+
const peer1 = PublicKey.random();
|
|
96
|
+
const peer2 = PublicKey.random();
|
|
97
|
+
const initiator = PublicKey.fromHex(chooseInitiatorPeer(peer1.toHex(), peer2.toHex()));
|
|
98
|
+
if (initiator.equals(peer1)) {
|
|
99
|
+
return { initiator: peer1, responder: peer2 };
|
|
100
|
+
} else {
|
|
101
|
+
return { initiator: peer2, responder: peer1 };
|
|
102
|
+
}
|
|
103
|
+
};
|
|
79
104
|
});
|
package/src/swarm/connection.ts
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
ConnectionResetError,
|
|
16
16
|
ConnectivityError,
|
|
17
17
|
TimeoutError,
|
|
18
|
-
UnknownProtocolError,
|
|
19
18
|
trace,
|
|
20
19
|
} from '@dxos/protocols';
|
|
21
20
|
import { type Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
@@ -202,14 +201,15 @@ export class Connection {
|
|
|
202
201
|
|
|
203
202
|
invariant(!this._transport);
|
|
204
203
|
this._transport = this._transportFactory.createTransport({
|
|
204
|
+
ownPeerKey: this.localInfo.peerKey,
|
|
205
|
+
remotePeerKey: this.remoteInfo.peerKey,
|
|
206
|
+
topic: this.topic.toHex(),
|
|
205
207
|
initiator: this.initiator,
|
|
206
208
|
stream: this._protocol.stream,
|
|
207
209
|
sendSignal: async (signal) => this._sendSignal(signal),
|
|
208
210
|
sessionId: this.sessionId,
|
|
209
211
|
});
|
|
210
212
|
|
|
211
|
-
await this._transport.open();
|
|
212
|
-
|
|
213
213
|
this._transport.connected.once(async () => {
|
|
214
214
|
this._changeState(ConnectionState.CONNECTED);
|
|
215
215
|
await this.connectedTimeoutContext.dispose();
|
|
@@ -234,12 +234,10 @@ export class Connection {
|
|
|
234
234
|
// TODO(nf): fix ErrorStream so instanceof works here
|
|
235
235
|
if (err instanceof ConnectionResetError) {
|
|
236
236
|
log.info('aborting due to transport ConnectionResetError');
|
|
237
|
-
this.abort().catch((err) => this.errors.raise(err));
|
|
237
|
+
this.abort(err).catch((err) => this.errors.raise(err));
|
|
238
238
|
} else if (err instanceof ConnectivityError) {
|
|
239
239
|
log.info('aborting due to transport ConnectivityError');
|
|
240
|
-
this.abort().catch((err) => this.errors.raise(err));
|
|
241
|
-
} else if (err instanceof UnknownProtocolError) {
|
|
242
|
-
log.warn('unsure what to do with UnknownProtocolError, will keep on truckin', { err });
|
|
240
|
+
this.abort(err).catch((err) => this.errors.raise(err));
|
|
243
241
|
}
|
|
244
242
|
|
|
245
243
|
if (this._state !== ConnectionState.CLOSED && this._state !== ConnectionState.CLOSING) {
|
|
@@ -248,6 +246,8 @@ export class Connection {
|
|
|
248
246
|
}
|
|
249
247
|
});
|
|
250
248
|
|
|
249
|
+
await this._transport.open();
|
|
250
|
+
|
|
251
251
|
// Replay signals that were received before transport was created.
|
|
252
252
|
for (const signal of this._incomingSignalBuffer) {
|
|
253
253
|
void this._transport.onSignal(signal); // TODO(burdon): Remove async?
|
package/src/swarm/peer.ts
CHANGED
|
@@ -285,7 +285,8 @@ export class Peer {
|
|
|
285
285
|
});
|
|
286
286
|
},
|
|
287
287
|
onClosed: (err) => {
|
|
288
|
-
|
|
288
|
+
const logMeta = { topic: this.topic, peerId: this.localInfo, remoteId: this.remoteInfo, initiator };
|
|
289
|
+
log('connection closed', logMeta);
|
|
289
290
|
|
|
290
291
|
// Make sure none of the connections are stuck in the limiter.
|
|
291
292
|
this._connectionLimiter.doneConnecting(sessionId);
|
|
@@ -310,11 +311,13 @@ export class Peer {
|
|
|
310
311
|
this.availableToConnect = false;
|
|
311
312
|
this._availableAfter = increaseInterval(this._availableAfter);
|
|
312
313
|
}
|
|
314
|
+
|
|
313
315
|
this._callbacks.onDisconnected();
|
|
314
316
|
|
|
315
317
|
scheduleTask(
|
|
316
318
|
this._connectionCtx!,
|
|
317
319
|
() => {
|
|
320
|
+
log('peer became available', logMeta);
|
|
318
321
|
this.availableToConnect = true;
|
|
319
322
|
this._callbacks.onPeerAvailable();
|
|
320
323
|
},
|
package/src/swarm/swarm.test.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from '
|
|
5
|
+
import { onTestFinished, describe, expect, test } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { asyncTimeout, sleep } from '@dxos/async';
|
|
8
8
|
import { PublicKey } from '@dxos/keys';
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
type PeerInfo,
|
|
14
14
|
type SignalManager,
|
|
15
15
|
} from '@dxos/messaging';
|
|
16
|
-
import { afterTest, describe, test } from '@dxos/test';
|
|
17
16
|
import { ComplexSet } from '@dxos/util';
|
|
18
17
|
|
|
19
18
|
import { ConnectionState } from './connection';
|
|
@@ -21,7 +20,7 @@ import { ConnectionLimiter } from './connection-limiter';
|
|
|
21
20
|
import { Swarm } from './swarm';
|
|
22
21
|
import { TestWireProtocol } from '../testing/test-wire-protocol';
|
|
23
22
|
import { FullyConnectedTopology } from '../topology';
|
|
24
|
-
import {
|
|
23
|
+
import { createRtcTransportFactory } from '../transport';
|
|
25
24
|
|
|
26
25
|
type TestPeer = {
|
|
27
26
|
swarm: Swarm;
|
|
@@ -47,21 +46,20 @@ describe('Swarm', () => {
|
|
|
47
46
|
signalManager?: SignalManager;
|
|
48
47
|
initiationDelay?: number;
|
|
49
48
|
}): Promise<TestPeer> => {
|
|
50
|
-
const protocol = new TestWireProtocol(
|
|
49
|
+
const protocol = new TestWireProtocol();
|
|
51
50
|
const swarm = new Swarm(
|
|
52
51
|
topic,
|
|
53
52
|
peer,
|
|
54
53
|
new FullyConnectedTopology(),
|
|
55
54
|
protocol.factory,
|
|
56
55
|
new Messenger({ signalManager }),
|
|
57
|
-
|
|
58
|
-
process.env.MOCHA_ENV === 'nodejs' ? createLibDataChannelTransportFactory() : createSimplePeerTransportFactory(),
|
|
56
|
+
createRtcTransportFactory(),
|
|
59
57
|
undefined,
|
|
60
58
|
connectionLimiter,
|
|
61
59
|
initiationDelay,
|
|
62
60
|
);
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
onTestFinished(async () => {
|
|
65
63
|
await swarm.destroy();
|
|
66
64
|
await signalManager.close();
|
|
67
65
|
});
|
|
@@ -81,7 +79,7 @@ describe('Swarm', () => {
|
|
|
81
79
|
expect(peer2.swarm.connections.length).to.equal(0);
|
|
82
80
|
|
|
83
81
|
await connectSwarms(peer1, peer2);
|
|
84
|
-
})
|
|
82
|
+
});
|
|
85
83
|
|
|
86
84
|
test('two peers try to originate connections to each other simultaneously', async () => {
|
|
87
85
|
const topic = PublicKey.random();
|
|
@@ -93,7 +91,7 @@ describe('Swarm', () => {
|
|
|
93
91
|
expect(peer2.swarm.connections.length).to.equal(0);
|
|
94
92
|
|
|
95
93
|
await connectSwarms(peer1, peer2);
|
|
96
|
-
})
|
|
94
|
+
});
|
|
97
95
|
|
|
98
96
|
test('with simultaneous connections one of the peers drops initiated connection', async () => {
|
|
99
97
|
const topic = PublicKey.random();
|
|
@@ -111,9 +109,9 @@ describe('Swarm', () => {
|
|
|
111
109
|
|
|
112
110
|
await connectSwarms(peer1, peer2);
|
|
113
111
|
await asyncTimeout(connectionDisplaced!, 1000);
|
|
114
|
-
})
|
|
112
|
+
});
|
|
115
113
|
|
|
116
|
-
test('second peer discovered after delay', async () => {
|
|
114
|
+
test('second peer discovered after delay', { timeout: 10_000 }, async () => {
|
|
117
115
|
const topic = PublicKey.random();
|
|
118
116
|
|
|
119
117
|
const peer1 = await setupSwarm({ topic });
|
|
@@ -126,7 +124,7 @@ describe('Swarm', () => {
|
|
|
126
124
|
expect(peer2.swarm.connections.length).to.equal(0);
|
|
127
125
|
|
|
128
126
|
await connectSwarms(peer1, peer2, () => sleep(15));
|
|
129
|
-
})
|
|
127
|
+
});
|
|
130
128
|
|
|
131
129
|
test('connection limiter', async () => {
|
|
132
130
|
// remotePeer1 <--> peer (connectionLimiter: max = 1) <--> remotePeer2
|
package/src/swarm/swarm.ts
CHANGED
|
@@ -29,7 +29,7 @@ const getClassName = (obj: any) => Object.getPrototypeOf(obj).constructor.name;
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* A single peer's view of the swarm.
|
|
32
|
-
* Manages a set of
|
|
32
|
+
* Manages a set of peers subscribed on the same topic.
|
|
33
33
|
* Routes signal events and maintains swarm topology.
|
|
34
34
|
*/
|
|
35
35
|
export class Swarm {
|
|
@@ -189,8 +189,10 @@ export class Swarm {
|
|
|
189
189
|
const peer = this._peers.get(swarmEvent.peerLeft.peer);
|
|
190
190
|
if (peer) {
|
|
191
191
|
peer.advertizing = false;
|
|
192
|
-
if
|
|
193
|
-
|
|
192
|
+
// Destroy peer only if there is no p2p-connection established. Otherwise, let peers go through
|
|
193
|
+
// the graceful shutdown protocol.
|
|
194
|
+
if (this._isConnectionEstablishmentInProgress(peer)) {
|
|
195
|
+
log(`destroying peer, state: ${peer.connection?.state}`);
|
|
194
196
|
void this._destroyPeer(swarmEvent.peerLeft.peer, 'peer left').catch((err) => log.catch(err));
|
|
195
197
|
}
|
|
196
198
|
} else {
|
|
@@ -277,6 +279,7 @@ export class Swarm {
|
|
|
277
279
|
},
|
|
278
280
|
onDisconnected: async () => {
|
|
279
281
|
if (this._isUnregistered(peer)) {
|
|
282
|
+
log.verbose('ignored onDisconnected for unregistered peer');
|
|
280
283
|
return;
|
|
281
284
|
}
|
|
282
285
|
if (!peer!.advertizing) {
|
|
@@ -312,6 +315,7 @@ export class Swarm {
|
|
|
312
315
|
}
|
|
313
316
|
|
|
314
317
|
private async _destroyPeer(peerInfo: PeerInfo, reason?: string) {
|
|
318
|
+
log('destroy peer', { peerKey: peerInfo.peerKey, reason });
|
|
315
319
|
const peer = this._peers.get(peerInfo);
|
|
316
320
|
invariant(peer);
|
|
317
321
|
this._peers.delete(peerInfo);
|
|
@@ -399,6 +403,15 @@ export class Swarm {
|
|
|
399
403
|
await peer.closeConnection();
|
|
400
404
|
}
|
|
401
405
|
|
|
406
|
+
private _isConnectionEstablishmentInProgress(peer: Peer) {
|
|
407
|
+
if (!peer.connection) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
return [ConnectionState.INITIAL, ConnectionState.CREATED, ConnectionState.CONNECTING].includes(
|
|
411
|
+
peer.connection.state,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
402
415
|
private _isUnregistered(peer?: Peer): boolean {
|
|
403
416
|
return !peer || this._peers.get(peer.remoteInfo) !== peer;
|
|
404
417
|
}
|
|
@@ -16,19 +16,12 @@ import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
|
16
16
|
import { createLinkedPorts, createProtoRpcPeer, type ProtoRpcPeer } from '@dxos/rpc';
|
|
17
17
|
import { ComplexMap } from '@dxos/util';
|
|
18
18
|
|
|
19
|
+
import { TcpTransportFactory } from '#tcp-transport';
|
|
19
20
|
import { type TestTeleportExtensionFactory, TestWireProtocol } from './test-wire-protocol';
|
|
20
21
|
import { SwarmNetworkManager } from '../network-manager';
|
|
21
22
|
import { FullyConnectedTopology } from '../topology';
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
createSimplePeerTransportFactory,
|
|
25
|
-
MemoryTransportFactory,
|
|
26
|
-
SimplePeerTransportProxyFactory,
|
|
27
|
-
SimplePeerTransportService,
|
|
28
|
-
type TransportFactory,
|
|
29
|
-
TransportKind,
|
|
30
|
-
} from '../transport';
|
|
31
|
-
import { TcpTransportFactory } from '../transport/tcp-transport';
|
|
23
|
+
import { MemoryTransportFactory, type TransportFactory, TransportKind } from '../transport';
|
|
24
|
+
import { createRtcTransportFactory, RtcTransportProxyFactory, RtcTransportService } from '../transport';
|
|
32
25
|
|
|
33
26
|
// Signal server will be started by the setup script.
|
|
34
27
|
const port = process.env.SIGNAL_PORT ?? 4000;
|
|
@@ -85,18 +78,13 @@ export class TestPeer {
|
|
|
85
78
|
constructor(
|
|
86
79
|
private readonly testBuilder: TestBuilder,
|
|
87
80
|
public readonly peerId: PublicKey,
|
|
88
|
-
public readonly transport
|
|
81
|
+
public readonly transport: TransportKind = testBuilder.options.signalHosts
|
|
82
|
+
? TransportKind.WEB_RTC
|
|
83
|
+
: TransportKind.MEMORY,
|
|
89
84
|
) {
|
|
90
85
|
this._signalManager = this.testBuilder.createSignalManager();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// TODO(nf): configure better
|
|
94
|
-
transport = process.env.MOCHA_ENV === 'nodejs' ? TransportKind.LIBDATACHANNEL : TransportKind.SIMPLE_PEER;
|
|
95
|
-
} else {
|
|
96
|
-
transport = TransportKind.MEMORY;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
this._networkManager = this.createNetworkManager(transport);
|
|
86
|
+
this._networkManager = this.createNetworkManager(this.transport);
|
|
87
|
+
this._networkManager.setPeerInfo({ identityKey: peerId.toHex(), peerKey: peerId.toHex() });
|
|
100
88
|
}
|
|
101
89
|
|
|
102
90
|
// TODO(burdon): Move to TestBuilder.
|
|
@@ -110,13 +98,10 @@ export class TestPeer {
|
|
|
110
98
|
case TransportKind.TCP:
|
|
111
99
|
transportFactory = TcpTransportFactory;
|
|
112
100
|
break;
|
|
113
|
-
case TransportKind.
|
|
114
|
-
transportFactory =
|
|
115
|
-
break;
|
|
116
|
-
case TransportKind.LIBDATACHANNEL:
|
|
117
|
-
transportFactory = createLibDataChannelTransportFactory();
|
|
101
|
+
case TransportKind.WEB_RTC:
|
|
102
|
+
transportFactory = createRtcTransportFactory();
|
|
118
103
|
break;
|
|
119
|
-
case TransportKind.
|
|
104
|
+
case TransportKind.WEB_RTC_PROXY:
|
|
120
105
|
{
|
|
121
106
|
// Simulates bridge to shared worker.
|
|
122
107
|
const [proxyPort, servicePort] = createLinkedPorts();
|
|
@@ -137,14 +122,14 @@ export class TestPeer {
|
|
|
137
122
|
exposed: {
|
|
138
123
|
BridgeService: schema.getService('dxos.mesh.bridge.BridgeService'),
|
|
139
124
|
},
|
|
140
|
-
handlers: { BridgeService: new
|
|
125
|
+
handlers: { BridgeService: new RtcTransportService() },
|
|
141
126
|
noHandshake: true,
|
|
142
127
|
encodingOptions: {
|
|
143
128
|
preserveAny: true,
|
|
144
129
|
},
|
|
145
130
|
});
|
|
146
131
|
|
|
147
|
-
transportFactory = new
|
|
132
|
+
transportFactory = new RtcTransportProxyFactory().setBridgeService(this._proxy.rpc.BridgeService);
|
|
148
133
|
}
|
|
149
134
|
break;
|
|
150
135
|
default:
|
|
@@ -219,7 +204,7 @@ export class TestSwarmConnection {
|
|
|
219
204
|
) {
|
|
220
205
|
// TODO(burdon): Configure plugins.
|
|
221
206
|
// TODO(burdon): Prevent reuse?
|
|
222
|
-
this.protocol = new TestWireProtocol(this.
|
|
207
|
+
this.protocol = new TestWireProtocol(this.extensionFactory);
|
|
223
208
|
}
|
|
224
209
|
|
|
225
210
|
// TODO(burdon): Need to create new plugin instance per swarm?
|
|
@@ -29,18 +29,17 @@ export class TestWireProtocol {
|
|
|
29
29
|
({ remotePeerId, extension }) => remotePeerId.toHex() + extension,
|
|
30
30
|
);
|
|
31
31
|
|
|
32
|
-
constructor(
|
|
33
|
-
public readonly peerId: PublicKey,
|
|
34
|
-
private readonly _extensionFactory: TestTeleportExtensionFactory = () => [],
|
|
35
|
-
) {}
|
|
32
|
+
constructor(private readonly _extensionFactory: TestTeleportExtensionFactory = () => []) {}
|
|
36
33
|
|
|
37
34
|
readonly factory = createTeleportProtocolFactory(async (teleport) => {
|
|
38
35
|
log('create', { remotePeerId: teleport.remotePeerId });
|
|
36
|
+
const handleDisconnect = () => {
|
|
37
|
+
this.connections.delete(teleport.remotePeerId);
|
|
38
|
+
this.disconnected.emit(teleport.remotePeerId);
|
|
39
|
+
};
|
|
39
40
|
const extension = new TestExtension({
|
|
40
|
-
onClose: async () =>
|
|
41
|
-
|
|
42
|
-
this.disconnected.emit(teleport.remotePeerId);
|
|
43
|
-
},
|
|
41
|
+
onClose: async () => handleDisconnect(),
|
|
42
|
+
onAbort: async () => handleDisconnect(),
|
|
44
43
|
});
|
|
45
44
|
this.connections.set(teleport.remotePeerId, extension);
|
|
46
45
|
teleport.addExtension('test', extension);
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from '
|
|
6
|
-
import waitForExpect from 'wait-for-expect';
|
|
5
|
+
import { onTestFinished, expect, test } from 'vitest';
|
|
7
6
|
|
|
8
7
|
import { asyncTimeout } from '@dxos/async';
|
|
9
8
|
import { PublicKey } from '@dxos/keys';
|
|
10
9
|
import { log } from '@dxos/log';
|
|
11
|
-
import { afterTest, test } from '@dxos/test';
|
|
12
10
|
import { range } from '@dxos/util';
|
|
13
11
|
|
|
14
12
|
import { exchangeMessages, joinSwarm, leaveSwarm, openAndCloseAfterTest } from './utils';
|
|
@@ -34,21 +32,21 @@ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
|
|
|
34
32
|
const [swarm1, swarm2] = await joinSwarm([peer1, peer2], topic, () => new FullyConnectedTopology());
|
|
35
33
|
await exchangeMessages(swarm1, swarm2);
|
|
36
34
|
await leaveSwarm([peer1, peer2], topic);
|
|
37
|
-
})
|
|
35
|
+
});
|
|
38
36
|
|
|
39
37
|
// TODO(burdon): Test with more peers (configure and test messaging).
|
|
40
38
|
test('joins swarm with star topology', async () => {
|
|
41
39
|
const peer1 = testBuilder.createPeer();
|
|
42
|
-
|
|
40
|
+
onTestFinished(() => peer1.close());
|
|
43
41
|
const peer2 = testBuilder.createPeer();
|
|
44
|
-
|
|
42
|
+
onTestFinished(() => peer2.close());
|
|
45
43
|
await openAndCloseAfterTest([peer1, peer2]);
|
|
46
44
|
|
|
47
45
|
const topic = PublicKey.random();
|
|
48
46
|
const [swarm1, swarm2] = await joinSwarm([peer1, peer2], topic, () => new StarTopology(peer1.peerId)); // NOTE: Same peer.
|
|
49
47
|
await exchangeMessages(swarm1, swarm2);
|
|
50
48
|
await leaveSwarm([peer1, peer2], topic);
|
|
51
|
-
})
|
|
49
|
+
});
|
|
52
50
|
|
|
53
51
|
// TODO(burdon): Fails when trying to reconnect to same topic.
|
|
54
52
|
test('joins swarm multiple times', async () => {
|
|
@@ -75,7 +73,7 @@ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
|
|
|
75
73
|
await exchangeMessages(swarm1, swarm2);
|
|
76
74
|
await leaveSwarm([peer1, peer2], topic2);
|
|
77
75
|
}
|
|
78
|
-
})
|
|
76
|
+
});
|
|
79
77
|
|
|
80
78
|
test('joins multiple swarms', async () => {
|
|
81
79
|
// TODO(burdon): N peers.
|
|
@@ -87,7 +85,7 @@ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
|
|
|
87
85
|
const numSwarms = 5;
|
|
88
86
|
const topics = Array.from(Array(numSwarms)).map(() => PublicKey.random());
|
|
89
87
|
expect(topics).to.have.length(numSwarms);
|
|
90
|
-
})
|
|
88
|
+
});
|
|
91
89
|
|
|
92
90
|
test('joins multiple swarms concurrently', async () => {
|
|
93
91
|
const createSwarm = async () => {
|
|
@@ -109,7 +107,7 @@ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
|
|
|
109
107
|
test1.swarm1a.protocol.testConnection(test1.peer2a.peerId),
|
|
110
108
|
test2.swarm1a.protocol.testConnection(test2.peer2a.peerId),
|
|
111
109
|
]);
|
|
112
|
-
})
|
|
110
|
+
});
|
|
113
111
|
|
|
114
112
|
test('peers reconnect after and error in connection', async () => {
|
|
115
113
|
const peer1 = testBuilder.createPeer();
|
|
@@ -120,20 +118,20 @@ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
|
|
|
120
118
|
const [swarm1, swarm2] = await joinSwarm([peer1, peer2], topic, () => new FullyConnectedTopology());
|
|
121
119
|
await exchangeMessages(swarm1, swarm2);
|
|
122
120
|
|
|
123
|
-
|
|
121
|
+
const disconnectedKeys = new Set();
|
|
122
|
+
swarm1.protocol.disconnected.on((peerInfo) => disconnectedKeys.add(peerInfo.toHex()));
|
|
123
|
+
swarm2.protocol.disconnected.on((peerInfo) => disconnectedKeys.add(peerInfo.toHex()));
|
|
124
124
|
|
|
125
|
+
void swarm1.protocol.connections.get(swarm2.peer.peerId)!.closeConnection(new Error('test error'));
|
|
125
126
|
// Wait until both peers are disconnected.
|
|
126
|
-
await
|
|
127
|
-
swarm1.protocol.disconnected.waitForCondition(() => swarm1.protocol.connections.size === 0),
|
|
128
|
-
swarm2.protocol.disconnected.waitForCondition(() => swarm2.protocol.connections.size === 0),
|
|
129
|
-
]);
|
|
127
|
+
await expect.poll(() => disconnectedKeys.size).toEqual(2);
|
|
130
128
|
|
|
131
129
|
await exchangeMessages(swarm1, swarm2);
|
|
132
130
|
|
|
133
131
|
await leaveSwarm([peer1, peer2], topic);
|
|
134
|
-
})
|
|
132
|
+
});
|
|
135
133
|
|
|
136
|
-
test('going offline and back online', async () => {
|
|
134
|
+
test('going offline and back online', { timeout: 2_000 }, async () => {
|
|
137
135
|
const peer1 = testBuilder.createPeer();
|
|
138
136
|
const peer2 = testBuilder.createPeer();
|
|
139
137
|
await openAndCloseAfterTest([peer1, peer2]);
|
|
@@ -158,25 +156,28 @@ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
|
|
|
158
156
|
await peerLeft;
|
|
159
157
|
|
|
160
158
|
// Wait for peer to be removed from the swarm.
|
|
161
|
-
await
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
159
|
+
await expect
|
|
160
|
+
.poll(() => !!peer2._networkManager.getSwarm(topic)!._peers.get({ peerKey: peer1.peerId.toHex() })?.advertizing, {
|
|
161
|
+
timeout: 1_000,
|
|
162
|
+
})
|
|
163
|
+
.toBe(false);
|
|
165
164
|
|
|
166
165
|
await peer1.goOnline();
|
|
167
166
|
|
|
168
|
-
await
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
167
|
+
await expect
|
|
168
|
+
.poll(() => peer1._networkManager.getSwarm(topic)?._peers.get({ peerKey: peer2.peerId.toHex() })?.advertizing, {
|
|
169
|
+
timeout: 2_000,
|
|
170
|
+
})
|
|
171
|
+
.toBe(true);
|
|
172
|
+
await expect
|
|
173
|
+
.poll(() => peer2._networkManager.getSwarm(topic)?._peers.get({ peerKey: peer1.peerId.toHex() })?.advertizing, {
|
|
174
|
+
timeout: 2_000,
|
|
175
|
+
})
|
|
176
|
+
.toBe(true);
|
|
174
177
|
|
|
175
178
|
await exchangeMessages(swarm1, swarm2);
|
|
176
179
|
await leaveSwarm([peer1, peer2], topic);
|
|
177
|
-
})
|
|
178
|
-
.tag('flaky')
|
|
179
|
-
.timeout(2_000);
|
|
180
|
+
});
|
|
180
181
|
|
|
181
182
|
// TODO(mykola): Fails with large amount of peers ~10.
|
|
182
183
|
test('many peers and connections', async () => {
|
|
@@ -207,5 +208,5 @@ export const basicTestSuite = (testBuilder: TestBuilder, runTests = true) => {
|
|
|
207
208
|
);
|
|
208
209
|
|
|
209
210
|
await asyncTimeout(Promise.all(swarmsAllPeersConnected), 2_000);
|
|
210
|
-
})
|
|
211
|
+
});
|
|
211
212
|
};
|