@dxos/network-manager 0.1.56-main.c787da3 → 0.1.56-main.e47dfd1

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.
Files changed (37) hide show
  1. package/dist/lib/browser/{chunk-MIYEP22D.mjs → chunk-5R7TDXUU.mjs} +323 -229
  2. package/dist/lib/browser/chunk-5R7TDXUU.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +5 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +1 -1
  6. package/dist/lib/node/index.cjs +318 -222
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +314 -222
  10. package/dist/lib/node/testing/index.cjs.map +4 -4
  11. package/dist/types/src/network-manager.d.ts +1 -1
  12. package/dist/types/src/network-manager.d.ts.map +1 -1
  13. package/dist/types/src/swarm/connection-limiter.d.ts +28 -0
  14. package/dist/types/src/swarm/connection-limiter.d.ts.map +1 -0
  15. package/dist/types/src/swarm/connection-limiter.test.d.ts +2 -0
  16. package/dist/types/src/swarm/connection-limiter.test.d.ts.map +1 -0
  17. package/dist/types/src/swarm/connection.d.ts +1 -1
  18. package/dist/types/src/swarm/connection.d.ts.map +1 -1
  19. package/dist/types/src/swarm/connection.test.d.ts +2 -0
  20. package/dist/types/src/swarm/connection.test.d.ts.map +1 -0
  21. package/dist/types/src/swarm/index.d.ts +1 -0
  22. package/dist/types/src/swarm/index.d.ts.map +1 -1
  23. package/dist/types/src/swarm/peer.d.ts +3 -1
  24. package/dist/types/src/swarm/peer.d.ts.map +1 -1
  25. package/dist/types/src/swarm/swarm.d.ts +3 -1
  26. package/dist/types/src/swarm/swarm.d.ts.map +1 -1
  27. package/package.json +17 -17
  28. package/src/network-manager.ts +18 -4
  29. package/src/swarm/connection-limiter.test.ts +44 -0
  30. package/src/swarm/connection-limiter.ts +71 -0
  31. package/src/swarm/connection.test.ts +72 -0
  32. package/src/swarm/connection.ts +1 -2
  33. package/src/swarm/index.ts +1 -0
  34. package/src/swarm/peer.ts +22 -7
  35. package/src/swarm/swarm.test.ts +140 -90
  36. package/src/swarm/swarm.ts +3 -1
  37. package/dist/lib/browser/chunk-MIYEP22D.mjs.map +0 -7
@@ -2,31 +2,43 @@
2
2
  // Copyright 2020 DXOS.org
3
3
  //
4
4
 
5
- // @dxos/test platform=nodejs
6
-
7
- import { expect } from 'earljs';
5
+ import { expect } from 'chai';
8
6
 
9
7
  import { asyncTimeout, sleep } from '@dxos/async';
10
8
  import { PublicKey } from '@dxos/keys';
11
- import { log } from '@dxos/log';
12
- import { MemorySignalManager, MemorySignalManagerContext, Messenger } from '@dxos/messaging';
13
- import { afterTest, beforeEach, describe, test } from '@dxos/test';
9
+ import { MemorySignalManager, MemorySignalManagerContext, Messenger, SignalManager } from '@dxos/messaging';
10
+ import { afterTest, describe, test } from '@dxos/test';
11
+ import { ComplexSet } from '@dxos/util';
14
12
 
15
13
  import { TestWireProtocol } from '../testing/test-wire-protocol';
16
14
  import { FullyConnectedTopology } from '../topology';
17
15
  import { createWebRTCTransportFactory } from '../transport';
16
+ import { ConnectionState } from './connection';
17
+ import { ConnectionLimiter } from './connection-limiter';
18
18
  import { Swarm } from './swarm';
19
19
 
20
+ type TestPeer = {
21
+ swarm: Swarm;
22
+ peerId: PublicKey;
23
+ protocol: TestWireProtocol;
24
+ topic: PublicKey;
25
+ signalManager: SignalManager;
26
+ };
27
+
20
28
  describe('Swarm', () => {
21
29
  const context = new MemorySignalManagerContext();
22
- let signalManager: MemorySignalManager;
23
-
24
- beforeEach(() => {
25
- signalManager = new MemorySignalManager(context);
26
- afterTest(() => signalManager.close());
27
- });
28
30
 
29
- const setupSwarm = async ({ topic, peerId }: { topic: PublicKey; peerId: PublicKey }) => {
31
+ const setupSwarm = async ({
32
+ topic = PublicKey.random(),
33
+ peerId = PublicKey.random(),
34
+ connectionLimiter = new ConnectionLimiter(),
35
+ signalManager = new MemorySignalManager(context),
36
+ }: {
37
+ topic?: PublicKey;
38
+ peerId?: PublicKey;
39
+ connectionLimiter?: ConnectionLimiter;
40
+ signalManager?: SignalManager;
41
+ }): Promise<TestPeer> => {
30
42
  const protocol = new TestWireProtocol(peerId);
31
43
  const swarm = new Swarm(
32
44
  topic,
@@ -36,116 +48,154 @@ describe('Swarm', () => {
36
48
  new Messenger({ signalManager }),
37
49
  createWebRTCTransportFactory(),
38
50
  undefined,
51
+ connectionLimiter,
39
52
  );
40
53
 
41
54
  afterTest(async () => {
42
55
  await swarm.destroy();
56
+ await signalManager.close();
43
57
  });
44
58
 
45
59
  await swarm.open();
46
60
 
47
- return { swarm, protocol };
61
+ return { swarm, protocol, topic, peerId, signalManager };
48
62
  };
49
63
 
50
64
  test('connects two peers in a swarm', async () => {
51
65
  const topic = PublicKey.random();
52
- const peerId1 = PublicKey.random();
53
- const peerId2 = PublicKey.random();
54
66
 
55
- const { swarm: swarm1, protocol: protocol1 } = await setupSwarm({ topic, peerId: peerId1 });
67
+ const peer1 = await setupSwarm({ topic });
68
+ const peer2 = await setupSwarm({ topic });
56
69
 
57
- const { swarm: swarm2 } = await setupSwarm({ topic, peerId: peerId2 });
70
+ expect(peer1.swarm.connections.length).to.equal(0);
71
+ expect(peer2.swarm.connections.length).to.equal(0);
58
72
 
59
- expect(swarm1.connections.length).toEqual(0);
60
- expect(swarm2.connections.length).toEqual(0);
61
-
62
- const promise = Promise.all([
63
- asyncTimeout(swarm1.connected.waitForCount(1), 3000, new Error('Swarm1 connect timeout.')),
64
- asyncTimeout(swarm2.connected.waitForCount(1), 3000, new Error('Swarm2 connect timeout.')),
65
- ]);
73
+ await connectSwarms(peer1, peer2);
74
+ }).timeout(5_000);
66
75
 
67
- // Behavior of the Signal Server.
68
- swarm1.onSwarmEvent({
69
- peerAvailable: {
70
- peer: peerId1.asUint8Array(),
71
- since: new Date(),
72
- },
73
- });
76
+ test('two peers try to originate connections to each other simultaneously', async () => {
77
+ const topic = PublicKey.random();
74
78
 
75
- swarm1.onSwarmEvent({
76
- peerAvailable: {
77
- peer: peerId2.asUint8Array(),
78
- since: new Date(),
79
- },
80
- });
79
+ const peer1 = await setupSwarm({ topic });
80
+ const peer2 = await setupSwarm({ topic });
81
81
 
82
- log('Candidates changed');
83
- await promise;
84
- log('Swarms connected');
82
+ expect(peer1.swarm.connections.length).to.equal(0);
83
+ expect(peer2.swarm.connections.length).to.equal(0);
85
84
 
86
- await protocol1.testConnection(peerId2);
85
+ await connectSwarms(peer1, peer2);
87
86
  }).timeout(5_000);
88
87
 
89
- test('two peers try to originate connections to each other simultaneously', async () => {
88
+ test('second peer discovered after delay', async () => {
90
89
  const topic = PublicKey.random();
91
- const peerId1 = PublicKey.random();
92
- const peerId2 = PublicKey.random();
93
90
 
94
- const { swarm: swarm1 } = await setupSwarm({ topic, peerId: peerId1 });
95
- const { swarm: swarm2 } = await setupSwarm({ topic, peerId: peerId2 });
91
+ const peer1 = await setupSwarm({ topic });
92
+ const peer2 = await setupSwarm({ topic });
96
93
 
97
- expect(swarm1.connections.length).toEqual(0);
98
- expect(swarm2.connections.length).toEqual(0);
94
+ expect(peer1.swarm.connections.length).to.equal(0);
95
+ expect(peer2.swarm.connections.length).to.equal(0);
99
96
 
100
- const connectPromises = Promise.all([swarm1.connected.waitForCount(1), swarm2.connected.waitForCount(1)]);
97
+ expect(peer1.swarm.connections.length).to.equal(0);
98
+ expect(peer2.swarm.connections.length).to.equal(0);
101
99
 
102
- swarm1.onSwarmEvent({
103
- peerAvailable: {
104
- peer: peerId2.asUint8Array(),
105
- since: new Date(),
106
- },
107
- });
100
+ await connectSwarms(peer1, peer2, () => sleep(15));
101
+ }).timeout(10_000);
108
102
 
109
- swarm2.onSwarmEvent({
110
- peerAvailable: {
111
- peer: peerId1.asUint8Array(),
112
- since: new Date(),
113
- },
114
- });
103
+ test('connection limiter', async () => {
104
+ // remotePeer1 <--> peer (connectionLimiter: max = 1) <--> remotePeer2
115
105
 
116
- await connectPromises;
117
- }).timeout(5_000);
106
+ const peerId = PublicKey.fromHex('7701dc2d');
107
+ const remotePeerId1 = PublicKey.fromHex('7d2bc6aa');
108
+ const remotePeerId2 = PublicKey.fromHex('39ba0e41');
118
109
 
119
- test('second peer discovered after delay', async () => {
120
110
  const topic = PublicKey.random();
121
- const peerId1 = PublicKey.random();
122
- const peerId2 = PublicKey.random();
111
+ const connectionLimiter = new ConnectionLimiter({ maxConcurrentInitConnections: 1 });
123
112
 
124
- const { swarm: swarm1, protocol: protocol1 } = await setupSwarm({ topic, peerId: peerId1 });
125
-
126
- const { swarm: swarm2 } = await setupSwarm({ topic, peerId: peerId2 });
113
+ const signalManager = new MemorySignalManager(context);
114
+ const sendOriginal = signalManager.sendMessage.bind(signalManager);
115
+ const messages = new ComplexSet<{ author: PublicKey; recipient: PublicKey }>(
116
+ ({ author, recipient }) => author.toHex() + recipient.toHex(),
117
+ );
118
+ signalManager.sendMessage = async (message) => {
119
+ messages.add({ author: message.author, recipient: message.recipient });
120
+ return sendOriginal(message);
121
+ };
122
+ // Stop signaling to stop connection in initiation state.
123
+ signalManager.freeze();
124
+
125
+ const peer = await setupSwarm({
126
+ topic,
127
+ peerId,
128
+ connectionLimiter,
129
+ signalManager,
130
+ });
131
+ const remotePeer1 = await setupSwarm({ peerId: remotePeerId1, topic });
132
+ const remotePeer2 = await setupSwarm({ peerId: remotePeerId2, topic });
133
+
134
+ let connected1: Promise<void>;
135
+ {
136
+ // Connection limiter allow only one connection to be started.
137
+ const connectionInit = peer.swarm.connectionAdded.waitForCount(1);
138
+ connected1 = connectSwarms(peer, remotePeer1);
139
+ await asyncTimeout(connectionInit, 1000);
140
+ }
141
+
142
+ let connected2: Promise<void>;
143
+ {
144
+ // Connection limiter should prevent second connection from being started (only created).
145
+ const connectionInit = peer.swarm.connectionAdded.waitForCount(1);
146
+ connected2 = connectSwarms(peer, remotePeer2);
147
+ await asyncTimeout(connectionInit, 1000);
148
+ }
149
+
150
+ {
151
+ // Peer sent messages only to first remote peer.
152
+ expect(messages.has({ author: peer.peerId, recipient: remotePeer1.peerId })).to.equal(true);
153
+ expect(messages.has({ author: peer.peerId, recipient: remotePeer2.peerId })).to.equal(false);
154
+ }
155
+
156
+ signalManager.unfreeze();
157
+
158
+ await Promise.all([connected1, connected2]);
159
+ });
160
+ });
127
161
 
128
- expect(swarm1.connections.length).toEqual(0);
129
- expect(swarm2.connections.length).toEqual(0);
162
+ const connectSwarms = async (peer1: TestPeer, peer2: TestPeer, delay = async () => {}) => {
163
+ const connect1 = peer1.swarm.connected.waitForCount(1);
164
+ const connect2 = peer2.swarm.connected.waitForCount(1);
130
165
 
131
- const connectPromises = Promise.all([swarm1.connected.waitForCount(1), swarm2.connected.waitForCount(1)]);
166
+ peer1.swarm.onSwarmEvent({
167
+ peerAvailable: {
168
+ peer: peer2.peerId.asUint8Array(),
169
+ since: new Date(),
170
+ },
171
+ });
132
172
 
133
- swarm1.onSwarmEvent({
134
- peerAvailable: {
135
- peer: peerId2.asUint8Array(),
136
- since: new Date(),
137
- },
138
- });
139
- await sleep(15);
140
- swarm2.onSwarmEvent({
141
- peerAvailable: {
142
- peer: peerId1.asUint8Array(),
143
- since: new Date(),
144
- },
145
- });
173
+ await delay();
146
174
 
147
- await connectPromises;
175
+ peer2.swarm.onSwarmEvent({
176
+ peerAvailable: {
177
+ peer: peer1.peerId.asUint8Array(),
178
+ since: new Date(),
179
+ },
180
+ });
148
181
 
149
- await protocol1.testConnection(peerId2);
150
- }).timeout(10_000);
151
- });
182
+ if (
183
+ !(
184
+ peer1.swarm.connections.find((connection) => connection.remoteId.equals(peer2.peerId))?.state ===
185
+ ConnectionState.CONNECTED
186
+ )
187
+ ) {
188
+ await asyncTimeout(connect1, 3000);
189
+ }
190
+ if (
191
+ !(
192
+ peer2.swarm.connections.find((connection) => connection.remoteId.equals(peer1.peerId))?.state ===
193
+ ConnectionState.CONNECTED
194
+ )
195
+ ) {
196
+ await asyncTimeout(connect2, 3000);
197
+ }
198
+
199
+ await peer1.protocol.testConnection(peer2.peerId, 'test message 1');
200
+ await peer2.protocol.testConnection(peer1.peerId, 'test message 2');
201
+ };
@@ -21,6 +21,7 @@ import { TransportFactory } from '../transport';
21
21
  import { Topic } from '../types';
22
22
  import { WireProtocolProvider } from '../wire-protocol';
23
23
  import { Connection, ConnectionState } from './connection';
24
+ import { ConnectionLimiter } from './connection-limiter';
24
25
  import { Peer } from './peer';
25
26
 
26
27
  const INITIATION_DELAY = 100;
@@ -82,6 +83,7 @@ export class Swarm {
82
83
  private readonly _messenger: Messenger,
83
84
  private readonly _transportFactory: TransportFactory,
84
85
  private readonly _label: string | undefined,
86
+ private readonly _connectionLimiter: ConnectionLimiter,
85
87
  ) {
86
88
  log.trace(
87
89
  'dxos.mesh.swarm.constructor',
@@ -217,7 +219,6 @@ export class Swarm {
217
219
  return answer;
218
220
  }
219
221
 
220
- @synchronized
221
222
  async onSignal(message: SignalMessage): Promise<void> {
222
223
  log('signal', { message });
223
224
  if (this._ctx.disposed) {
@@ -258,6 +259,7 @@ export class Swarm {
258
259
  this._swarmMessenger,
259
260
  this._protocolProvider,
260
261
  this._transportFactory,
262
+ this._connectionLimiter,
261
263
  {
262
264
  onInitiated: (connection) => {
263
265
  this.connectionAdded.emit(connection);