@dxos/network-manager 2.33.9-dev.9246a07b → 2.33.9-dev.9bbef4e2

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 (148) hide show
  1. package/dist/src/network-manager.blueprint-test.d.ts +3 -1
  2. package/dist/src/network-manager.blueprint-test.d.ts.map +1 -1
  3. package/dist/src/network-manager.blueprint-test.js +46 -17
  4. package/dist/src/network-manager.blueprint-test.js.map +1 -1
  5. package/dist/src/network-manager.browser-test.js +1 -1
  6. package/dist/src/network-manager.browser-test.js.map +1 -1
  7. package/dist/src/network-manager.d.ts.map +1 -1
  8. package/dist/src/network-manager.js +6 -6
  9. package/dist/src/network-manager.js.map +1 -1
  10. package/dist/src/network-manager.test.js +5 -4
  11. package/dist/src/network-manager.test.js.map +1 -1
  12. package/dist/src/proto/gen/dxos/credentials.d.ts.map +1 -1
  13. package/dist/src/proto/gen/dxos/halo/keys.d.ts.map +1 -1
  14. package/dist/src/proto/gen/dxos/halo/keys.js.map +1 -1
  15. package/dist/src/proto/gen/dxos/mesh/signal.d.ts +52 -45
  16. package/dist/src/proto/gen/dxos/mesh/signal.d.ts.map +1 -1
  17. package/dist/src/proto/gen/dxos/mesh/signalMessage.d.ts +79 -0
  18. package/dist/src/proto/gen/dxos/mesh/signalMessage.d.ts.map +1 -0
  19. package/dist/src/proto/gen/dxos/mesh/signalMessage.js +3 -0
  20. package/dist/src/proto/gen/dxos/mesh/signalMessage.js.map +1 -0
  21. package/dist/src/proto/gen/google/protobuf.d.ts +6 -0
  22. package/dist/src/proto/gen/google/protobuf.d.ts.map +1 -1
  23. package/dist/src/proto/gen/index.d.ts +17 -5
  24. package/dist/src/proto/gen/index.d.ts.map +1 -1
  25. package/dist/src/proto/gen/index.js +1 -1
  26. package/dist/src/proto/gen/index.js.map +1 -1
  27. package/dist/src/proto/substitutions.d.ts +4 -0
  28. package/dist/src/proto/substitutions.d.ts.map +1 -1
  29. package/dist/src/proto/substitutions.js +3 -1
  30. package/dist/src/proto/substitutions.js.map +1 -1
  31. package/dist/src/signal/in-memory-signal-manager.d.ts +7 -7
  32. package/dist/src/signal/in-memory-signal-manager.d.ts.map +1 -1
  33. package/dist/src/signal/in-memory-signal-manager.js +29 -8
  34. package/dist/src/signal/in-memory-signal-manager.js.map +1 -1
  35. package/dist/src/signal/index.d.ts +1 -2
  36. package/dist/src/signal/index.d.ts.map +1 -1
  37. package/dist/src/signal/index.js +1 -2
  38. package/dist/src/signal/index.js.map +1 -1
  39. package/dist/src/signal/integration.test.d.ts +2 -0
  40. package/dist/src/signal/integration.test.d.ts.map +1 -0
  41. package/dist/src/signal/integration.test.js +102 -0
  42. package/dist/src/signal/integration.test.js.map +1 -0
  43. package/dist/src/signal/message-router.d.ts +7 -7
  44. package/dist/src/signal/message-router.d.ts.map +1 -1
  45. package/dist/src/signal/message-router.js +6 -1
  46. package/dist/src/signal/message-router.js.map +1 -1
  47. package/dist/src/signal/message-router.test.js +15 -19
  48. package/dist/src/signal/message-router.test.js.map +1 -1
  49. package/dist/src/signal/signal-client.d.ts +33 -18
  50. package/dist/src/signal/signal-client.d.ts.map +1 -1
  51. package/dist/src/signal/signal-client.js +102 -92
  52. package/dist/src/signal/signal-client.js.map +1 -1
  53. package/dist/src/signal/signal-client.test.js +60 -77
  54. package/dist/src/signal/signal-client.test.js.map +1 -1
  55. package/dist/src/signal/{websocket-signal-manager.d.ts → signal-manager-impl.d.ts} +13 -11
  56. package/dist/src/signal/signal-manager-impl.d.ts.map +1 -0
  57. package/dist/src/signal/signal-manager-impl.js +151 -0
  58. package/dist/src/signal/signal-manager-impl.js.map +1 -0
  59. package/dist/src/signal/signal-manager.d.ts +12 -11
  60. package/dist/src/signal/signal-manager.d.ts.map +1 -1
  61. package/dist/src/signal/signal-rpc-client.d.ts +19 -0
  62. package/dist/src/signal/signal-rpc-client.d.ts.map +1 -0
  63. package/dist/src/signal/signal-rpc-client.js +108 -0
  64. package/dist/src/signal/signal-rpc-client.js.map +1 -0
  65. package/dist/src/signal/signal-rpc-client.test.d.ts +2 -0
  66. package/dist/src/signal/signal-rpc-client.test.d.ts.map +1 -0
  67. package/dist/src/signal/signal-rpc-client.test.js +74 -0
  68. package/dist/src/signal/signal-rpc-client.test.js.map +1 -0
  69. package/dist/src/swarm/connection.d.ts +3 -3
  70. package/dist/src/swarm/connection.d.ts.map +1 -1
  71. package/dist/src/swarm/connection.js +1 -4
  72. package/dist/src/swarm/connection.js.map +1 -1
  73. package/dist/src/swarm/swarm.d.ts +6 -7
  74. package/dist/src/swarm/swarm.d.ts.map +1 -1
  75. package/dist/src/swarm/swarm.js +21 -17
  76. package/dist/src/swarm/swarm.js.map +1 -1
  77. package/dist/src/swarm/swarm.test.js +156 -117
  78. package/dist/src/swarm/swarm.test.js.map +1 -1
  79. package/dist/src/topology/fully-connected-topology.d.ts +0 -1
  80. package/dist/src/topology/fully-connected-topology.d.ts.map +1 -1
  81. package/dist/src/topology/fully-connected-topology.js +1 -6
  82. package/dist/src/topology/fully-connected-topology.js.map +1 -1
  83. package/dist/src/topology/mmst-topology.d.ts +0 -1
  84. package/dist/src/topology/mmst-topology.d.ts.map +1 -1
  85. package/dist/src/topology/mmst-topology.js +1 -6
  86. package/dist/src/topology/mmst-topology.js.map +1 -1
  87. package/dist/src/topology/star-topology.d.ts +0 -1
  88. package/dist/src/topology/star-topology.d.ts.map +1 -1
  89. package/dist/src/topology/star-topology.js +1 -6
  90. package/dist/src/topology/star-topology.js.map +1 -1
  91. package/dist/src/topology/topology.d.ts +0 -6
  92. package/dist/src/topology/topology.d.ts.map +1 -1
  93. package/dist/src/transport/in-memory-transport.d.ts +2 -2
  94. package/dist/src/transport/in-memory-transport.d.ts.map +1 -1
  95. package/dist/src/transport/in-memory-transport.js.map +1 -1
  96. package/dist/src/transport/transport.d.ts +3 -3
  97. package/dist/src/transport/transport.d.ts.map +1 -1
  98. package/dist/src/transport/webrtc-transport.d.ts +3 -3
  99. package/dist/src/transport/webrtc-transport.d.ts.map +1 -1
  100. package/dist/src/transport/webrtc-transport.js.map +1 -1
  101. package/dist/tests-setup.js +1 -1
  102. package/dist/tsconfig.tsbuildinfo +1 -1
  103. package/package.json +14 -12
  104. package/src/network-manager.blueprint-test.ts +57 -22
  105. package/src/network-manager.browser-test.ts +1 -1
  106. package/src/network-manager.test.ts +8 -7
  107. package/src/network-manager.ts +8 -9
  108. package/src/proto/defs/dxos/mesh/signal.proto +53 -35
  109. package/src/proto/defs/dxos/mesh/signalMessage.proto +51 -0
  110. package/src/proto/gen/dxos/credentials.ts +1 -0
  111. package/src/proto/gen/dxos/halo/keys.ts +1 -0
  112. package/src/proto/gen/dxos/mesh/signal.ts +51 -45
  113. package/src/proto/gen/dxos/mesh/signalMessage.ts +83 -0
  114. package/src/proto/gen/google/protobuf.ts +7 -0
  115. package/src/proto/gen/index.ts +18 -6
  116. package/src/proto/substitutions.ts +3 -1
  117. package/src/signal/in-memory-signal-manager.ts +37 -12
  118. package/src/signal/index.ts +1 -2
  119. package/src/signal/integration.test.ts +117 -0
  120. package/src/signal/message-router.test.ts +36 -41
  121. package/src/signal/message-router.ts +22 -18
  122. package/src/signal/signal-client.test.ts +70 -92
  123. package/src/signal/signal-client.ts +119 -113
  124. package/src/signal/signal-manager-impl.ts +166 -0
  125. package/src/signal/signal-manager.ts +12 -12
  126. package/src/signal/signal-rpc-client.test.ts +86 -0
  127. package/src/signal/signal-rpc-client.ts +121 -0
  128. package/src/swarm/connection.ts +5 -8
  129. package/src/swarm/swarm.test.ts +208 -169
  130. package/src/swarm/swarm.ts +24 -20
  131. package/src/topology/fully-connected-topology.ts +1 -9
  132. package/src/topology/mmst-topology.ts +1 -9
  133. package/src/topology/star-topology.ts +1 -7
  134. package/src/topology/topology.ts +0 -7
  135. package/src/transport/in-memory-transport.ts +2 -2
  136. package/src/transport/transport.ts +3 -3
  137. package/src/transport/webrtc-transport.ts +3 -3
  138. package/dist/browser-mocha/bundle.js +0 -119346
  139. package/dist/browser-mocha/main.js +0 -27
  140. package/dist/src/signal/websocket-rpc.d.ts +0 -30
  141. package/dist/src/signal/websocket-rpc.d.ts.map +0 -1
  142. package/dist/src/signal/websocket-rpc.js +0 -203
  143. package/dist/src/signal/websocket-rpc.js.map +0 -1
  144. package/dist/src/signal/websocket-signal-manager.d.ts.map +0 -1
  145. package/dist/src/signal/websocket-signal-manager.js +0 -134
  146. package/dist/src/signal/websocket-signal-manager.js.map +0 -1
  147. package/src/signal/websocket-rpc.ts +0 -208
  148. package/src/signal/websocket-signal-manager.ts +0 -158
@@ -0,0 +1,121 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ import debug from 'debug';
6
+ import WebSocket from 'isomorphic-ws';
7
+
8
+ import { Trigger, Event } from '@dxos/async';
9
+ import { Any, Stream } from '@dxos/codec-protobuf';
10
+ import { PublicKey } from '@dxos/protocols';
11
+ import { createBundledRpcClient, ProtoRpcClient } from '@dxos/rpc';
12
+
13
+ import { schema } from '../proto/gen';
14
+ import { Message, Signal } from '../proto/gen/dxos/mesh/signal';
15
+ interface Services {
16
+ Signal: Signal
17
+ }
18
+
19
+ const log = debug('dxos:network-manager:signal-rpc-client');
20
+
21
+ export class SignalRPCClient {
22
+ private readonly _socket: WebSocket
23
+ private readonly _rpc: ProtoRpcClient<Services>
24
+ private readonly _connectTrigger = new Trigger();
25
+
26
+ readonly connected = new Event();
27
+ readonly disconnected = new Event();
28
+ readonly error = new Event<Error>();
29
+
30
+ constructor (
31
+ private readonly _url: string
32
+ ) {
33
+ this._socket = new WebSocket(this._url);
34
+ this._socket.onopen = async () => {
35
+ try {
36
+ await this._rpc.open();
37
+ log(`RPC open ${this._url}`);
38
+ this.connected.emit();
39
+ this._connectTrigger.wake();
40
+ } catch (err: any) {
41
+ this.error.emit(err);
42
+ }
43
+ };
44
+
45
+ this._socket.onclose = async () => {
46
+ log(`Disconnected ${this._url}`);
47
+ this.disconnected.emit();
48
+ try {
49
+ await this._rpc.close();
50
+ } catch (err: any) {
51
+ this.error.emit(err);
52
+ }
53
+ };
54
+
55
+ this._socket.onerror = (e: WebSocket.ErrorEvent) => {
56
+ log(`Signal socket error ${this._url} ${e.message}`);
57
+ this.error.emit(e.error ?? new Error(e.message));
58
+ };
59
+
60
+ this._rpc = createBundledRpcClient(
61
+ {
62
+ Signal: schema.getService('dxos.mesh.signal.Signal')
63
+ },
64
+ {
65
+ noHandshake: true,
66
+ port: {
67
+ send: msg => {
68
+ this._socket.send(msg);
69
+ },
70
+ subscribe: cb => {
71
+ this._socket.onmessage = async (msg: WebSocket.MessageEvent) => {
72
+ if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {
73
+ cb(Buffer.from(await msg.data.arrayBuffer()));
74
+ } else {
75
+ cb(msg.data as any);
76
+ }
77
+ };
78
+ }
79
+ }
80
+ }
81
+ );
82
+ }
83
+
84
+ async join (topic: PublicKey, peerId: PublicKey) {
85
+ log('join', topic, peerId);
86
+ await this._connectTrigger.wait();
87
+ const swarmStream = this._rpc.rpc.Signal.join({
88
+ swarm: topic.asUint8Array(),
89
+ peer: peerId.asUint8Array()
90
+ });
91
+ await swarmStream.waitUntilReady();
92
+ return swarmStream;
93
+ }
94
+
95
+ async receiveMessages (peerId: PublicKey): Promise<Stream<Message>> {
96
+ await this._connectTrigger.wait();
97
+ const messageStream = this._rpc.rpc.Signal.receiveMessages({
98
+ peer: peerId.asUint8Array()
99
+ });
100
+ await messageStream.waitUntilReady();
101
+ return messageStream;
102
+ }
103
+
104
+ async sendMessage (author: PublicKey, recipient: PublicKey, message: Any) {
105
+ log('sendMessage', author, recipient, message);
106
+ await this._connectTrigger.wait();
107
+ await this._rpc.rpc.Signal.sendMessage({
108
+ author: author.asUint8Array(),
109
+ recipient: recipient.asUint8Array(),
110
+ payload: message
111
+ });
112
+ }
113
+
114
+ async close () {
115
+ try {
116
+ await this._rpc.close();
117
+ } finally {
118
+ this._socket.close();
119
+ }
120
+ }
121
+ }
@@ -10,7 +10,7 @@ import { ErrorStream } from '@dxos/debug';
10
10
  import { Protocol } from '@dxos/mesh-protocol';
11
11
  import { PublicKey } from '@dxos/protocols';
12
12
 
13
- import { Message } from '../proto/gen/dxos/mesh/signal';
13
+ import { SignalMessage } from '../proto/gen/dxos/mesh/signalMessage';
14
14
  import { Transport, TransportFactory } from '../transport';
15
15
 
16
16
  const log = debug('dxos:network-manager:swarm:connection');
@@ -51,7 +51,7 @@ export enum ConnectionState {
51
51
  export class Connection {
52
52
  private _state: ConnectionState = ConnectionState.INITIAL;
53
53
  private _transport: Transport | undefined;
54
- private _bufferedSignals: Message[] = [];
54
+ private _bufferedSignals: SignalMessage[] = [];
55
55
 
56
56
  readonly stateChanged = new Event<ConnectionState>();
57
57
  readonly errors = new ErrorStream();
@@ -62,7 +62,7 @@ export class Connection {
62
62
  public readonly remoteId: PublicKey,
63
63
  public readonly sessionId: PublicKey,
64
64
  public readonly initiator: boolean,
65
- private readonly _sendSignal: (msg: Message) => Promise<void>,
65
+ private readonly _sendSignal: (msg: SignalMessage) => Promise<void>,
66
66
  private readonly _protocol: Protocol,
67
67
  private readonly _transportFactory: TransportFactory
68
68
  ) {}
@@ -116,16 +116,13 @@ export class Connection {
116
116
  this._bufferedSignals = [];
117
117
  }
118
118
 
119
- async signal (msg: Message) {
119
+ async signal (msg: SignalMessage) {
120
120
  assert(msg.sessionId);
121
121
  if (!msg.sessionId.equals(this.sessionId)) {
122
122
  log('Dropping signal for incorrect session id.');
123
123
  return;
124
124
  }
125
- assert(msg.data);
126
- if (msg.data.offer && this._state === ConnectionState.INITIATING_CONNECTION) {
127
- throw new Error('Invalid state: Cannot send offer to an initiating peer.');
128
- }
125
+ assert(msg.data.signal);
129
126
  assert(msg.id?.equals(this.remoteId));
130
127
  assert(msg.remoteId?.equals(this.ownId));
131
128
 
@@ -12,7 +12,7 @@ import { Protocol } from '@dxos/mesh-protocol';
12
12
  import { PublicKey } from '@dxos/protocols';
13
13
  import { afterTest } from '@dxos/testutils';
14
14
 
15
- import { Message } from '../proto/gen/dxos/mesh/signal';
15
+ import { SignalMessage } from '../proto/gen/dxos/mesh/signalMessage';
16
16
  import { SignalMessaging } from '../signal';
17
17
  import { MessageRouter } from '../signal/message-router';
18
18
  import { FullyConnectedTopology } from '../topology';
@@ -21,175 +21,214 @@ import { Swarm } from './swarm';
21
21
 
22
22
  const log = debug('dxos:network-manager:swarm:test');
23
23
 
24
- class MockSignalConnection implements SignalMessaging {
25
- constructor (
26
- readonly _swarm: () => Swarm,
27
- readonly _delay = 10
28
- ) {}
29
-
30
- async offer (msg: Message) {
31
- await sleep(this._delay);
32
- return this._swarm().onOffer(msg);
24
+ describe('Swarm', () => {
25
+ class MockSignalConnection implements SignalMessaging {
26
+ constructor (
27
+ readonly _swarm: () => Swarm,
28
+ readonly _delay = 10
29
+ ) {}
30
+
31
+ async offer (msg: SignalMessage) {
32
+ await sleep(this._delay);
33
+ return this._swarm().onOffer(msg);
34
+ }
35
+
36
+ async signal (msg: SignalMessage) {
37
+ await sleep(this._delay);
38
+ await this._swarm().onSignal(msg);
39
+ }
33
40
  }
34
41
 
35
- async signal (msg: Message) {
36
- await sleep(this._delay);
37
- await this._swarm().onSignal(msg);
38
- }
39
- }
40
-
41
- const setup = ({ router = false } = {}) => {
42
- const topic = PublicKey.random();
43
- const peerId1 = PublicKey.random();
44
- const peerId2 = PublicKey.random();
45
- // eslint-disable-next-line prefer-const
46
- let swarm1: Swarm;
47
- // eslint-disable-next-line prefer-const
48
- let swarm2: Swarm;
49
-
50
- const mr1: MessageRouter = new MessageRouter({
51
- sendMessage: msg => mr2.receiveMessage(msg),
52
- onSignal: msg => swarm1.onSignal(msg),
53
- onOffer: msg => swarm1.onOffer(msg)
54
- });
55
- afterTest(() => mr1.destroy());
56
-
57
- const mr2: MessageRouter = new MessageRouter({
58
- sendMessage: msg => mr1.receiveMessage(msg),
59
- onSignal: msg => swarm2.onSignal(msg),
60
- onOffer: msg => swarm2.onOffer(msg)
61
- });
62
- afterTest(() => mr2.destroy());
63
-
64
- const sm1: SignalMessaging = router ? mr1 : new MockSignalConnection(() => swarm2);
65
-
66
- const sm2: SignalMessaging = router ? mr2 : new MockSignalConnection(() => swarm1);
67
-
68
- swarm1 = new Swarm(
69
- topic,
70
- peerId1,
71
- new FullyConnectedTopology(),
72
- () => new Protocol(),
73
- sm1,
74
- () => {},
75
- createWebRTCTransportFactory(),
76
- undefined
77
- );
78
-
79
- swarm2 = new Swarm(
80
- topic,
81
- peerId2,
82
- new FullyConnectedTopology(),
83
- () => new Protocol(),
84
- sm2,
85
- () => {},
86
- createWebRTCTransportFactory(),
87
- undefined
88
- );
89
-
90
- afterTest(async () => {
42
+ const setup = ({ router = false } = {}) => {
43
+ const topic = PublicKey.random();
44
+ const peerId1 = PublicKey.random();
45
+ const peerId2 = PublicKey.random();
46
+ // eslint-disable-next-line prefer-const
47
+ let swarm1: Swarm;
48
+ // eslint-disable-next-line prefer-const
49
+ let swarm2: Swarm;
50
+
51
+ const mr1: MessageRouter = new MessageRouter({
52
+ sendMessage: msg => mr2.receiveMessage(msg),
53
+ onSignal: msg => swarm1.onSignal(msg),
54
+ onOffer: msg => swarm1.onOffer(msg)
55
+ });
56
+ afterTest(() => mr1.destroy());
57
+
58
+ const mr2: MessageRouter = new MessageRouter({
59
+ sendMessage: msg => mr1.receiveMessage(msg),
60
+ onSignal: msg => swarm2.onSignal(msg),
61
+ onOffer: msg => swarm2.onOffer(msg)
62
+ });
63
+ afterTest(() => mr2.destroy());
64
+
65
+ const sm1: SignalMessaging = router ? mr1 : new MockSignalConnection(() => swarm2);
66
+
67
+ const sm2: SignalMessaging = router ? mr2 : new MockSignalConnection(() => swarm1);
68
+
69
+ swarm1 = new Swarm(
70
+ topic,
71
+ peerId1,
72
+ new FullyConnectedTopology(),
73
+ () => new Protocol(),
74
+ sm1,
75
+ createWebRTCTransportFactory(),
76
+ undefined
77
+ );
78
+
79
+ swarm2 = new Swarm(
80
+ topic,
81
+ peerId2,
82
+ new FullyConnectedTopology(),
83
+ () => new Protocol(),
84
+ sm2,
85
+ createWebRTCTransportFactory(),
86
+ undefined
87
+ );
88
+
89
+ afterTest(async () => {
90
+ await swarm1.destroy();
91
+ await swarm2.destroy();
92
+ });
93
+
94
+ return { swarm1, swarm2, peerId1, peerId2 };
95
+ };
96
+
97
+ test('connects two peers in a swarm', async () => {
98
+ const { swarm1, swarm2, peerId1, peerId2 } = setup();
99
+
100
+ expect(swarm1.connections.length).toEqual(0);
101
+ expect(swarm2.connections.length).toEqual(0);
102
+
103
+ const promise = Promise.all([
104
+ promiseTimeout(swarm1.connected.waitForCount(1), 3000, new Error('Swarm1 connect timeout.')),
105
+ promiseTimeout(swarm2.connected.waitForCount(1), 3000, new Error('Swarm2 connect timeout.'))
106
+ ]);
107
+
108
+ // Behavior of the Signal Server.
109
+ swarm1.onSwarmEvent({
110
+ peerAvailable: {
111
+ peer: peerId1.asUint8Array(),
112
+ since: new Date()
113
+ }
114
+ });
115
+
116
+ swarm1.onSwarmEvent({
117
+ peerAvailable: {
118
+ peer: peerId2.asUint8Array(),
119
+ since: new Date()
120
+ }
121
+ });
122
+
123
+ log('Candidates changed');
124
+ await promise;
125
+ log('Swarms connected');
126
+
127
+ const swarm1Connection = swarm1.connections[0];
128
+ const swarm2Connection = swarm2.connections[0];
129
+ const onData = mockFn<(data: Buffer) => void>().returns(undefined);
130
+ (swarm2Connection.transport as WebRTCTransport).peer!.on('data', onData);
131
+
132
+ const data = Buffer.from('1234');
133
+ (swarm1Connection.transport as WebRTCTransport).peer!.send(data);
134
+ await waitForExpect(() => {
135
+ expect(onData).toHaveBeenCalledWith([data]);
136
+ });
137
+ }).timeout(5_000);
138
+
139
+ test('two peers try to originate connections to each other simultaneously', async () => {
140
+ const { swarm1, swarm2, peerId1, peerId2 } = setup();
141
+
142
+ expect(swarm1.connections.length).toEqual(0);
143
+ expect(swarm2.connections.length).toEqual(0);
144
+
145
+ swarm1.onSwarmEvent({
146
+ peerAvailable: {
147
+ peer: peerId2.asUint8Array(),
148
+ since: new Date()
149
+ }
150
+ });
151
+
152
+ swarm2.onSwarmEvent({
153
+ peerAvailable: {
154
+ peer: peerId1.asUint8Array(),
155
+ since: new Date()
156
+ }
157
+ });
158
+
159
+ await Promise.all([
160
+ swarm1.connected.waitForCount(1),
161
+ swarm2.connected.waitForCount(1)
162
+ ]);
163
+ }).timeout(5_000);
164
+
165
+ test('second peer discovered after delay', async () => {
166
+ const { swarm1, swarm2, peerId1, peerId2 } = setup();
167
+
168
+ expect(swarm1.connections.length).toEqual(0);
169
+ expect(swarm2.connections.length).toEqual(0);
170
+
171
+ swarm1.onSwarmEvent({
172
+ peerAvailable: {
173
+ peer: peerId2.asUint8Array(),
174
+ since: new Date()
175
+ }
176
+ });
177
+ await sleep(15);
178
+ swarm2.onSwarmEvent({
179
+ peerAvailable: {
180
+ peer: peerId1.asUint8Array(),
181
+ since: new Date()
182
+ }
183
+ });
184
+
185
+ await Promise.all([
186
+ swarm1.connected.waitForCount(1),
187
+ swarm1.connected.waitForCount(1)
188
+ ]);
189
+
190
+ const swarm1Connection = swarm1.connections[0];
191
+ const swarm2Connection = swarm2.connections[0];
192
+ const onData = mockFn<(data: Buffer) => void>().returns(undefined);
193
+ (swarm2Connection.transport as WebRTCTransport).peer!.on('data', onData);
194
+
195
+ const data = Buffer.from('1234');
196
+ (swarm1Connection.transport as WebRTCTransport).peer!.send(data);
197
+ await waitForExpect(() => {
198
+ expect(onData).toHaveBeenCalledWith([data]);
199
+ });
200
+ }).timeout(5_000);
201
+
202
+ test('swarming with message router', async () => {
203
+ const { swarm1, swarm2, peerId2 } = setup({ router: true });
204
+
205
+ const promise = Promise.all([
206
+ promiseTimeout(swarm1.connected.waitForCount(1), 3000, new Error('Swarm1 connect timeout.')),
207
+ promiseTimeout(swarm2.connected.waitForCount(1), 3000, new Error('Swarm2 connect timeout.'))
208
+ ]);
209
+
210
+ swarm1.onSwarmEvent({
211
+ peerAvailable: {
212
+ peer: peerId2.asUint8Array(),
213
+ since: new Date()
214
+ }
215
+ });
216
+
217
+ log('Candidates changed');
218
+ await promise;
219
+ log('Swarms connected');
220
+
221
+ const swarm1Connection = swarm1.connections[0];
222
+ const swarm2Connection = swarm2.connections[0];
223
+ const onData = mockFn<(data: Buffer) => void>().returns(undefined);
224
+ (swarm2Connection.transport as WebRTCTransport).peer!.on('data', onData);
225
+
226
+ const data = Buffer.from('1234');
227
+ (swarm1Connection.transport as WebRTCTransport).peer!.send(data);
228
+ await waitForExpect(() => {
229
+ expect(onData).toHaveBeenCalledWith([data]);
230
+ });
91
231
  await swarm1.destroy();
92
232
  await swarm2.destroy();
93
- });
94
-
95
- return { swarm1, swarm2, peerId1, peerId2 };
96
- };
97
-
98
- test('connects two peers in a swarm', async () => {
99
- const { swarm1, swarm2, peerId2 } = setup();
100
-
101
- expect(swarm1.connections.length).toEqual(0);
102
- expect(swarm2.connections.length).toEqual(0);
103
-
104
- const promise = Promise.all([
105
- promiseTimeout(swarm1.connected.waitForCount(1), 3000, new Error('Swarm1 connect timeout.')),
106
- promiseTimeout(swarm2.connected.waitForCount(1), 3000, new Error('Swarm2 connect timeout.'))
107
- ]);
108
-
109
- swarm1.onPeerCandidatesChanged([peerId2]);
110
-
111
- log('Candidates changed');
112
- await promise;
113
- log('Swarms connected');
114
-
115
- const swarm1Connection = swarm1.connections[0];
116
- const swarm2Connection = swarm2.connections[0];
117
- const onData = mockFn<(data: Buffer) => void>().returns(undefined);
118
- (swarm2Connection.transport as WebRTCTransport).peer!.on('data', onData);
119
-
120
- const data = Buffer.from('1234');
121
- (swarm1Connection.transport as WebRTCTransport).peer!.send(data);
122
- await waitForExpect(() => {
123
- expect(onData).toHaveBeenCalledWith([data]);
124
- });
125
- }).timeout(5_000);
126
-
127
- test('two peers try to originate connections to each other simultaneously', async () => {
128
- const { swarm1, swarm2, peerId1, peerId2 } = setup();
129
-
130
- expect(swarm1.connections.length).toEqual(0);
131
- expect(swarm2.connections.length).toEqual(0);
132
-
133
- swarm1.onPeerCandidatesChanged([peerId2]);
134
- swarm2.onPeerCandidatesChanged([peerId1]);
135
-
136
- await Promise.all([
137
- swarm1.connected.waitForCount(1),
138
- swarm2.connected.waitForCount(1)
139
- ]);
140
- }).timeout(5_000);
141
-
142
- test('second peer discovered after delay', async () => {
143
- const { swarm1, swarm2, peerId1, peerId2 } = setup();
144
-
145
- expect(swarm1.connections.length).toEqual(0);
146
- expect(swarm2.connections.length).toEqual(0);
147
-
148
- swarm1.onPeerCandidatesChanged([peerId2]);
149
- await sleep(15);
150
- swarm2.onPeerCandidatesChanged([peerId1]);
151
-
152
- await Promise.all([
153
- swarm1.connected.waitForCount(1),
154
- swarm1.connected.waitForCount(1)
155
- ]);
156
-
157
- const swarm1Connection = swarm1.connections[0];
158
- const swarm2Connection = swarm2.connections[0];
159
- const onData = mockFn<(data: Buffer) => void>().returns(undefined);
160
- (swarm2Connection.transport as WebRTCTransport).peer!.on('data', onData);
161
-
162
- const data = Buffer.from('1234');
163
- (swarm1Connection.transport as WebRTCTransport).peer!.send(data);
164
- await waitForExpect(() => {
165
- expect(onData).toHaveBeenCalledWith([data]);
166
- });
167
- }).timeout(5_000);
168
-
169
- test('swarming with message router', async () => {
170
- const { swarm1, swarm2, peerId2 } = setup({ router: true });
171
-
172
- const promise = Promise.all([
173
- promiseTimeout(swarm1.connected.waitForCount(1), 3000, new Error('Swarm1 connect timeout.')),
174
- promiseTimeout(swarm2.connected.waitForCount(1), 3000, new Error('Swarm2 connect timeout.'))
175
- ]);
176
-
177
- swarm1.onPeerCandidatesChanged([peerId2]);
178
-
179
- log('Candidates changed');
180
- await promise;
181
- log('Swarms connected');
182
-
183
- const swarm1Connection = swarm1.connections[0];
184
- const swarm2Connection = swarm2.connections[0];
185
- const onData = mockFn<(data: Buffer) => void>().returns(undefined);
186
- (swarm2Connection.transport as WebRTCTransport).peer!.on('data', onData);
187
-
188
- const data = Buffer.from('1234');
189
- (swarm1Connection.transport as WebRTCTransport).peer!.send(data);
190
- await waitForExpect(() => {
191
- expect(onData).toHaveBeenCalledWith([data]);
192
- });
193
- await swarm1.destroy();
194
- await swarm2.destroy();
195
- }).timeout(5_000);
233
+ }).timeout(5_000);
234
+ });
@@ -12,7 +12,8 @@ import { PublicKey } from '@dxos/protocols';
12
12
  import { ComplexMap, ComplexSet } from '@dxos/util';
13
13
 
14
14
  import { ProtocolProvider } from '../network-manager';
15
- import { Answer, Message } from '../proto/gen/dxos/mesh/signal';
15
+ import { SwarmEvent } from '../proto/gen/dxos/mesh/signal';
16
+ import { Answer, SignalMessage } from '../proto/gen/dxos/mesh/signalMessage';
16
17
  import { SignalMessaging } from '../signal';
17
18
  import { SwarmController, Topology } from '../topology';
18
19
  import { TransportFactory } from '../transport';
@@ -34,7 +35,6 @@ export class Swarm {
34
35
 
35
36
  private readonly _connections = new ComplexMap<PublicKey, Connection>(x => x.toHex());
36
37
  private readonly _discoveredPeers = new ComplexSet<PublicKey>(x => x.toHex());
37
- private readonly _peerCandidatesUpdated = new Event();
38
38
 
39
39
  get connections () {
40
40
  return Array.from(this._connections.values());
@@ -64,7 +64,6 @@ export class Swarm {
64
64
  private _topology: Topology,
65
65
  private readonly _protocolProvider: ProtocolProvider,
66
66
  private readonly _signalMessaging: SignalMessaging,
67
- private readonly _lookupPeers: (topic: PublicKey) => void,
68
67
  private readonly _transportFactory: TransportFactory,
69
68
  private readonly _label: string | undefined
70
69
  ) {
@@ -87,26 +86,33 @@ export class Swarm {
87
86
  return this._topic;
88
87
  }
89
88
 
90
- onPeerCandidatesChanged (candidates: PublicKey[]) {
91
- log(`New peers for ${this._topic} ${candidates}`);
92
- this._discoveredPeers.clear();
93
- for (const candidate of candidates) {
94
- if (candidate.equals(this._ownPeerId)) {
95
- continue;
89
+ onSwarmEvent (swarmEvent: SwarmEvent) {
90
+ log(`Swarm event ${JSON.stringify(swarmEvent)}`);
91
+ if (swarmEvent.peerAvailable) {
92
+ const peerId = PublicKey.from(swarmEvent.peerAvailable.peer);
93
+ log(`New peer for ${this._topic} ${peerId}`);
94
+ if (!peerId.equals(this._ownPeerId)) {
95
+ this._discoveredPeers.add(peerId);
96
96
  }
97
- this._discoveredPeers.add(candidate);
97
+ } else if (swarmEvent.peerLeft) {
98
+ this._discoveredPeers.delete(PublicKey.from(swarmEvent.peerLeft.peer));
98
99
  }
99
- this._peerCandidatesUpdated.emit();
100
100
  this._topology.update();
101
101
  }
102
102
 
103
- async onOffer (message: Message): Promise<Answer> {
103
+ async onOffer (message: SignalMessage): Promise<Answer> {
104
104
  log(`Offer from ${JSON.stringify(message)}`);
105
105
  // Id of the peer offering us the connection.
106
106
  assert(message.id);
107
107
  const remoteId = message.id;
108
- assert(message.remoteId?.equals(this._ownPeerId));
109
- assert(message.topic?.equals(this._topic));
108
+ if (!message.remoteId?.equals(this._ownPeerId)) {
109
+ log(`Rejecting offer with incorrect peerId: ${message.remoteId}`);
110
+ return { accept: false };
111
+ }
112
+ if (!message.topic?.equals(this._topic)) {
113
+ log(`Rejecting offer with incorrect topic: ${message.topic}`);
114
+ return { accept: false };
115
+ }
110
116
 
111
117
  // Check if we are already trying to connect to that peer.
112
118
  if (this._connections.has(remoteId)) {
@@ -140,7 +146,7 @@ export class Swarm {
140
146
  return { accept };
141
147
  }
142
148
 
143
- async onSignal (message: Message): Promise<void> {
149
+ async onSignal (message: SignalMessage): Promise<void> {
144
150
  log(`Signal ${this._topic} ${JSON.stringify(message)}`);
145
151
  assert(message.remoteId?.equals(this._ownPeerId), `Invalid signal peer id expected=${this.ownPeerId}, actual=${message.remoteId}`);
146
152
  assert(message.topic?.equals(this._topic));
@@ -186,9 +192,6 @@ export class Swarm {
186
192
  this.errors.raise(err);
187
193
  }
188
194
  this._topology.update();
189
- },
190
- lookup: () => {
191
- this._lookupPeers(this._topic);
192
195
  }
193
196
  };
194
197
  }
@@ -201,6 +204,7 @@ export class Swarm {
201
204
 
202
205
  const sessionId = PublicKey.random();
203
206
 
207
+ log(`Initiate connection: topic=${this._topic} peerId=${remoteId} sessionId=${sessionId}`);
204
208
  const connection = this._createConnection(true, remoteId, sessionId);
205
209
  this._signalMessaging.offer({
206
210
  id: this._ownPeerId,
@@ -236,7 +240,7 @@ export class Swarm {
236
240
  }
237
241
 
238
242
  private _createConnection (initiator: boolean, remoteId: PublicKey, sessionId: PublicKey) {
239
- log(`Create connection topic=${this._topic} remoteId=${remoteId} initiator=${initiator}`);
243
+ log(`Create connection topic=${this._topic} ownId=${this._ownPeerId} remoteId=${remoteId} initiator=${initiator}`);
240
244
  assert(!this._connections.has(remoteId), 'Peer already connected.');
241
245
 
242
246
  const connection = new Connection(
@@ -245,7 +249,7 @@ export class Swarm {
245
249
  remoteId,
246
250
  sessionId,
247
251
  initiator,
248
- (msg: Message) => this._signalMessaging.signal(msg),
252
+ (msg: SignalMessage) => this._signalMessaging.signal(msg),
249
253
  this._protocolProvider({ channel: discoveryKey(this._topic), initiator }),
250
254
  this._transportFactory
251
255
  );