@dxos/network-manager 2.33.9-dev.7d11f506 → 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 (158) 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 +13 -12
  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 +39 -0
  13. package/dist/src/proto/gen/dxos/credentials.d.ts.map +1 -1
  14. package/dist/src/proto/gen/dxos/halo/keys.d.ts +44 -2
  15. package/dist/src/proto/gen/dxos/halo/keys.d.ts.map +1 -1
  16. package/dist/src/proto/gen/dxos/halo/keys.js +4 -0
  17. package/dist/src/proto/gen/dxos/halo/keys.js.map +1 -1
  18. package/dist/src/proto/gen/dxos/mesh/signal.d.ts +74 -16
  19. package/dist/src/proto/gen/dxos/mesh/signal.d.ts.map +1 -1
  20. package/dist/src/proto/gen/dxos/mesh/signalMessage.d.ts +79 -0
  21. package/dist/src/proto/gen/dxos/mesh/signalMessage.d.ts.map +1 -0
  22. package/dist/src/proto/gen/dxos/mesh/signalMessage.js +3 -0
  23. package/dist/src/proto/gen/dxos/mesh/signalMessage.js.map +1 -0
  24. package/dist/src/proto/gen/google/protobuf.d.ts +8 -2
  25. package/dist/src/proto/gen/google/protobuf.d.ts.map +1 -1
  26. package/dist/src/proto/gen/index.d.ts +17 -4
  27. package/dist/src/proto/gen/index.d.ts.map +1 -1
  28. package/dist/src/proto/gen/index.js +1 -1
  29. package/dist/src/proto/gen/index.js.map +1 -1
  30. package/dist/src/proto/substitutions.d.ts +4 -0
  31. package/dist/src/proto/substitutions.d.ts.map +1 -1
  32. package/dist/src/proto/substitutions.js +3 -1
  33. package/dist/src/proto/substitutions.js.map +1 -1
  34. package/dist/src/protocol-factory.js +3 -3
  35. package/dist/src/protocol-factory.js.map +1 -1
  36. package/dist/src/signal/in-memory-signal-manager.d.ts +7 -7
  37. package/dist/src/signal/in-memory-signal-manager.d.ts.map +1 -1
  38. package/dist/src/signal/in-memory-signal-manager.js +34 -13
  39. package/dist/src/signal/in-memory-signal-manager.js.map +1 -1
  40. package/dist/src/signal/index.d.ts +1 -2
  41. package/dist/src/signal/index.d.ts.map +1 -1
  42. package/dist/src/signal/index.js +1 -2
  43. package/dist/src/signal/index.js.map +1 -1
  44. package/dist/src/signal/integration.test.d.ts +2 -0
  45. package/dist/src/signal/integration.test.d.ts.map +1 -0
  46. package/dist/src/signal/integration.test.js +102 -0
  47. package/dist/src/signal/integration.test.js.map +1 -0
  48. package/dist/src/signal/message-router.d.ts +20 -8
  49. package/dist/src/signal/message-router.d.ts.map +1 -1
  50. package/dist/src/signal/message-router.js +96 -17
  51. package/dist/src/signal/message-router.js.map +1 -1
  52. package/dist/src/signal/message-router.test.js +125 -22
  53. package/dist/src/signal/message-router.test.js.map +1 -1
  54. package/dist/src/signal/signal-client.d.ts +33 -17
  55. package/dist/src/signal/signal-client.d.ts.map +1 -1
  56. package/dist/src/signal/signal-client.js +102 -82
  57. package/dist/src/signal/signal-client.js.map +1 -1
  58. package/dist/src/signal/signal-client.test.js +60 -75
  59. package/dist/src/signal/signal-client.test.js.map +1 -1
  60. package/dist/src/signal/{websocket-signal-manager.d.ts → signal-manager-impl.d.ts} +13 -11
  61. package/dist/src/signal/signal-manager-impl.d.ts.map +1 -0
  62. package/dist/src/signal/signal-manager-impl.js +151 -0
  63. package/dist/src/signal/signal-manager-impl.js.map +1 -0
  64. package/dist/src/signal/signal-manager.d.ts +12 -11
  65. package/dist/src/signal/signal-manager.d.ts.map +1 -1
  66. package/dist/src/signal/signal-rpc-client.d.ts +19 -0
  67. package/dist/src/signal/signal-rpc-client.d.ts.map +1 -0
  68. package/dist/src/signal/signal-rpc-client.js +108 -0
  69. package/dist/src/signal/signal-rpc-client.js.map +1 -0
  70. package/dist/src/signal/signal-rpc-client.test.d.ts +2 -0
  71. package/dist/src/signal/signal-rpc-client.test.d.ts.map +1 -0
  72. package/dist/src/signal/signal-rpc-client.test.js +74 -0
  73. package/dist/src/signal/signal-rpc-client.test.js.map +1 -0
  74. package/dist/src/swarm/connection.d.ts +3 -3
  75. package/dist/src/swarm/connection.d.ts.map +1 -1
  76. package/dist/src/swarm/connection.js +8 -11
  77. package/dist/src/swarm/connection.js.map +1 -1
  78. package/dist/src/swarm/swarm.d.ts +6 -7
  79. package/dist/src/swarm/swarm.d.ts.map +1 -1
  80. package/dist/src/swarm/swarm.js +29 -25
  81. package/dist/src/swarm/swarm.js.map +1 -1
  82. package/dist/src/swarm/swarm.test.js +156 -115
  83. package/dist/src/swarm/swarm.test.js.map +1 -1
  84. package/dist/src/testing/test-protocol.d.ts.map +1 -1
  85. package/dist/src/testing/test-protocol.js +3 -3
  86. package/dist/src/testing/test-protocol.js.map +1 -1
  87. package/dist/src/topology/fully-connected-topology.d.ts +0 -1
  88. package/dist/src/topology/fully-connected-topology.d.ts.map +1 -1
  89. package/dist/src/topology/fully-connected-topology.js +4 -9
  90. package/dist/src/topology/fully-connected-topology.js.map +1 -1
  91. package/dist/src/topology/mmst-topology.d.ts +0 -1
  92. package/dist/src/topology/mmst-topology.d.ts.map +1 -1
  93. package/dist/src/topology/mmst-topology.js +6 -11
  94. package/dist/src/topology/mmst-topology.js.map +1 -1
  95. package/dist/src/topology/star-topology.d.ts +0 -1
  96. package/dist/src/topology/star-topology.d.ts.map +1 -1
  97. package/dist/src/topology/star-topology.js +5 -10
  98. package/dist/src/topology/star-topology.js.map +1 -1
  99. package/dist/src/topology/topology.d.ts +0 -6
  100. package/dist/src/topology/topology.d.ts.map +1 -1
  101. package/dist/src/transport/in-memory-transport.d.ts +2 -2
  102. package/dist/src/transport/in-memory-transport.d.ts.map +1 -1
  103. package/dist/src/transport/in-memory-transport.js +2 -2
  104. package/dist/src/transport/in-memory-transport.js.map +1 -1
  105. package/dist/src/transport/transport.d.ts +3 -3
  106. package/dist/src/transport/transport.d.ts.map +1 -1
  107. package/dist/src/transport/webrtc-transport.d.ts +3 -3
  108. package/dist/src/transport/webrtc-transport.d.ts.map +1 -1
  109. package/dist/src/transport/webrtc-transport.js +3 -3
  110. package/dist/src/transport/webrtc-transport.js.map +1 -1
  111. package/dist/tests-setup.js +1 -1
  112. package/dist/tsconfig.tsbuildinfo +1 -1
  113. package/package.json +17 -12
  114. package/src/network-manager.blueprint-test.ts +57 -22
  115. package/src/network-manager.browser-test.ts +1 -1
  116. package/src/network-manager.test.ts +8 -7
  117. package/src/network-manager.ts +10 -10
  118. package/src/proto/defs/dxos/mesh/signal.proto +54 -23
  119. package/src/proto/defs/dxos/mesh/signalMessage.proto +51 -0
  120. package/src/proto/gen/dxos/credentials.ts +40 -0
  121. package/src/proto/gen/dxos/halo/keys.ts +45 -2
  122. package/src/proto/gen/dxos/mesh/signal.ts +73 -16
  123. package/src/proto/gen/dxos/mesh/signalMessage.ts +83 -0
  124. package/src/proto/gen/google/protobuf.ts +9 -2
  125. package/src/proto/gen/index.ts +18 -5
  126. package/src/proto/substitutions.ts +3 -1
  127. package/src/protocol-factory.ts +1 -1
  128. package/src/signal/in-memory-signal-manager.ts +38 -13
  129. package/src/signal/index.ts +1 -2
  130. package/src/signal/integration.test.ts +117 -0
  131. package/src/signal/message-router.test.ts +169 -58
  132. package/src/signal/message-router.ts +120 -27
  133. package/src/signal/signal-client.test.ts +70 -90
  134. package/src/signal/signal-client.ts +120 -87
  135. package/src/signal/signal-manager-impl.ts +166 -0
  136. package/src/signal/signal-manager.ts +12 -12
  137. package/src/signal/signal-rpc-client.test.ts +86 -0
  138. package/src/signal/signal-rpc-client.ts +121 -0
  139. package/src/swarm/connection.ts +6 -9
  140. package/src/swarm/swarm.test.ts +208 -167
  141. package/src/swarm/swarm.ts +26 -22
  142. package/src/testing/test-protocol.ts +1 -1
  143. package/src/topology/fully-connected-topology.ts +2 -10
  144. package/src/topology/mmst-topology.ts +2 -10
  145. package/src/topology/star-topology.ts +2 -8
  146. package/src/topology/topology.ts +0 -7
  147. package/src/transport/in-memory-transport.ts +3 -3
  148. package/src/transport/transport.ts +3 -3
  149. package/src/transport/webrtc-transport.ts +4 -4
  150. package/dist/src/signal/websocket-rpc.d.ts +0 -30
  151. package/dist/src/signal/websocket-rpc.d.ts.map +0 -1
  152. package/dist/src/signal/websocket-rpc.js +0 -203
  153. package/dist/src/signal/websocket-rpc.js.map +0 -1
  154. package/dist/src/signal/websocket-signal-manager.d.ts.map +0 -1
  155. package/dist/src/signal/websocket-signal-manager.js +0 -134
  156. package/dist/src/signal/websocket-signal-manager.js.map +0 -1
  157. package/src/signal/websocket-rpc.ts +0 -208
  158. package/src/signal/websocket-signal-manager.ts +0 -158
@@ -10,9 +10,8 @@ import { Awaited } from '@dxos/async';
10
10
  import { PublicKey } from '@dxos/protocols';
11
11
  import { createTestBroker } from '@dxos/signal';
12
12
  import { afterTest } from '@dxos/testutils';
13
- import { randomInt } from '@dxos/util';
14
13
 
15
- import { Answer, Message } from '../proto/gen/dxos/mesh/signal';
14
+ import { Answer, SignalMessage } from '../proto/gen/dxos/mesh/signalMessage';
16
15
  import { MessageRouter } from './message-router';
17
16
  import { SignalClient } from './signal-client';
18
17
 
@@ -22,11 +21,9 @@ describe('MessageRouter', () => {
22
21
  let peer2: PublicKey;
23
22
 
24
23
  let broker1: Awaited<ReturnType<typeof createTestBroker>>;
25
- const signalApiPort1 = randomInt(10000, 50000);
26
- const signalApiUrl1 = 'http://0.0.0.0:' + signalApiPort1;
27
24
 
28
25
  before(async () => {
29
- broker1 = await createTestBroker(signalApiPort1);
26
+ broker1 = await createTestBroker();
30
27
  });
31
28
 
32
29
  beforeEach(() => {
@@ -35,30 +32,33 @@ describe('MessageRouter', () => {
35
32
  peer2 = PublicKey.random();
36
33
  });
37
34
 
38
- after(async function () {
39
- this.timeout(0);
40
- await broker1.stop();
35
+ after(() => {
36
+ broker1.stop();
41
37
  });
42
38
 
43
- const createSignalClientAndMessageRouter = async (
44
- signalApiUrl: string,
45
- onSignal: (msg: Message) => Promise<void> = (async () => {}) as any,
46
- onOffer: (msg: Message) => Promise<Answer> = async () => ({ accept: true })
47
- ) => {
39
+ const createSignalClientAndMessageRouter = async ({
40
+ signalApiUrl,
41
+ onSignal = (async () => { }) as any,
42
+ onOffer = async () => ({ accept: true })
43
+ }: {
44
+ signalApiUrl: string;
45
+ onSignal?: (msg: SignalMessage) => Promise<void>;
46
+ onOffer?: (msg: SignalMessage) => Promise<Answer>;
47
+ }) => {
48
48
 
49
49
  // eslint-disable-next-line prefer-const
50
50
  let api: SignalClient;
51
51
  const router: MessageRouter = new MessageRouter({
52
52
  // todo(mykola): added catch to avoid not finished request.
53
- sendMessage: (msg: Message) => api.signal(msg).catch((_) => { }),
53
+ sendMessage: (msg: SignalMessage) => api.signal(msg).catch((_) => { }),
54
54
  onSignal: onSignal,
55
55
  onOffer: onOffer
56
56
  });
57
+ afterTest(() => router.destroy());
57
58
 
58
59
  api = new SignalClient(
59
60
  signalApiUrl,
60
- (async () => {}) as any,
61
- async (msg: Message) => router.receiveMessage(msg)
61
+ async (msg: SignalMessage) => router.receiveMessage(msg)
62
62
  );
63
63
 
64
64
  afterTest(() => api.close());
@@ -69,19 +69,19 @@ describe('MessageRouter', () => {
69
69
  };
70
70
 
71
71
  test('signaling between 2 clients', async () => {
72
- const signalMock1 = mockFn<(msg: Message) => Promise<void>>().resolvesTo();
73
- const { api: api1 } = await createSignalClientAndMessageRouter(signalApiUrl1, signalMock1);
74
- const { api: api2, router: router2 } = await createSignalClientAndMessageRouter(signalApiUrl1);
72
+ const signalMock1 = mockFn<(msg: SignalMessage) => Promise<void>>().resolvesTo();
73
+ const { api: api1 } = await createSignalClientAndMessageRouter({ signalApiUrl: broker1.url(), onSignal: signalMock1 });
74
+ const { api: api2, router: router2 } = await createSignalClientAndMessageRouter({ signalApiUrl: broker1.url() });
75
75
 
76
76
  await api1.join(topic, peer1);
77
77
  await api2.join(topic, peer2);
78
78
 
79
- const msg: Message = {
79
+ const msg: SignalMessage = {
80
80
  id: peer2,
81
81
  remoteId: peer1,
82
82
  sessionId: PublicKey.random(),
83
83
  topic,
84
- data: { signal: { json: '{"asd": "asd"}' } }
84
+ data: { signal: { json: JSON.stringify({ 'asd': 'asd' }) } }
85
85
  };
86
86
  await router2.signal(msg);
87
87
 
@@ -92,15 +92,19 @@ describe('MessageRouter', () => {
92
92
 
93
93
  test('offer/answer', async () => {
94
94
  const { api: api1, router: router1 } = await createSignalClientAndMessageRouter(
95
- signalApiUrl1,
96
- (async () => {}) as any,
97
- async () => ({ accept: true })
98
- );
95
+ {
96
+ signalApiUrl: broker1.url(),
97
+ onSignal: (async () => { }) as any,
98
+ onOffer:
99
+ async () => ({ accept: true })
100
+ });
99
101
  const { api: api2 } = await createSignalClientAndMessageRouter(
100
- signalApiUrl1,
101
- (async () => {}) as any,
102
- async () => ({ accept: true })
103
- );
102
+ {
103
+ signalApiUrl: broker1.url(),
104
+ onSignal: (async () => { }) as any,
105
+ onOffer:
106
+ async () => ({ accept: true })
107
+ });
104
108
 
105
109
  await api1.join(topic, peer1);
106
110
  await api2.join(topic, peer2);
@@ -112,28 +116,34 @@ describe('MessageRouter', () => {
112
116
  topic,
113
117
  data: { offer: { } }
114
118
  });
115
- expect(answer).toEqual({ accept: true });
119
+ expect(answer.accept).toEqual(true);
116
120
  }).timeout(5_000);
117
121
 
118
122
  test('signaling between 3 clients', async () => {
119
- const signalMock1 = mockFn<(msg: Message) => Promise<void>>().resolvesTo();
123
+ const signalMock1 = mockFn<(msg: SignalMessage) => Promise<void>>().resolvesTo();
120
124
  const { api: api1, router: router1 } = await createSignalClientAndMessageRouter(
121
- signalApiUrl1,
122
- signalMock1,
123
- async () => ({ accept: true })
124
- );
125
- const signalMock2 = mockFn<(msg: Message) => Promise<void>>().resolvesTo();
125
+ {
126
+ signalApiUrl: broker1.url(),
127
+ onSignal: signalMock1,
128
+ onOffer:
129
+ async () => ({ accept: true })
130
+ });
131
+ const signalMock2 = mockFn<(msg: SignalMessage) => Promise<void>>().resolvesTo();
126
132
  const { api: api2, router: router2 } = await createSignalClientAndMessageRouter(
127
- signalApiUrl1,
128
- signalMock2,
129
- async () => ({ accept: true })
130
- );
131
- const signalMock3 = mockFn<(msg: Message) => Promise<void>>().resolvesTo();
133
+ {
134
+ signalApiUrl: broker1.url(),
135
+ onSignal: signalMock2,
136
+ onOffer:
137
+ async () => ({ accept: true })
138
+ });
139
+ const signalMock3 = mockFn<(msg: SignalMessage) => Promise<void>>().resolvesTo();
132
140
  const { api: api3, router: router3 } = await createSignalClientAndMessageRouter(
133
- signalApiUrl1,
134
- signalMock3,
135
- async () => ({ accept: true })
136
- );
141
+ {
142
+ signalApiUrl: broker1.url(),
143
+ onSignal: signalMock3,
144
+ onOffer:
145
+ async () => ({ accept: true })
146
+ });
137
147
 
138
148
  await api1.join(topic, peer1);
139
149
  await api2.join(topic, peer2);
@@ -141,7 +151,7 @@ describe('MessageRouter', () => {
141
151
  await api3.join(topic, peer3);
142
152
 
143
153
  // sending signal from peer1 to peer3.
144
- const msg1to3: Message = {
154
+ const msg1to3: SignalMessage = {
145
155
  id: peer1,
146
156
  remoteId: peer3,
147
157
  sessionId: PublicKey.random(),
@@ -154,7 +164,7 @@ describe('MessageRouter', () => {
154
164
  }, 4_000);
155
165
 
156
166
  // sending signal from peer2 to peer3.
157
- const msg2to3: Message = {
167
+ const msg2to3: SignalMessage = {
158
168
  id: peer2,
159
169
  remoteId: peer3,
160
170
  sessionId: PublicKey.random(),
@@ -167,7 +177,7 @@ describe('MessageRouter', () => {
167
177
  }, 4_000);
168
178
 
169
179
  // sending signal from peer3 to peer1.
170
- const msg3to1: Message = {
180
+ const msg3to1: SignalMessage = {
171
181
  id: peer3,
172
182
  remoteId: peer1,
173
183
  sessionId: PublicKey.random(),
@@ -182,15 +192,19 @@ describe('MessageRouter', () => {
182
192
 
183
193
  test('two offers', async () => {
184
194
  const { api: api1, router: router1 } = await createSignalClientAndMessageRouter(
185
- signalApiUrl1,
186
- (async () => {}) as any,
187
- async () => ({ accept: true })
188
- );
195
+ {
196
+ signalApiUrl: broker1.url(),
197
+ onSignal: (async () => { }) as any,
198
+ onOffer:
199
+ async () => ({ accept: true })
200
+ });
189
201
  const { api: api2, router: router2 } = await createSignalClientAndMessageRouter(
190
- signalApiUrl1,
191
- (async () => {}) as any,
192
- async () => ({ accept: true })
193
- );
202
+ {
203
+ signalApiUrl: broker1.url(),
204
+ onSignal: (async () => { }) as any,
205
+ onOffer:
206
+ async () => ({ accept: true })
207
+ });
194
208
 
195
209
  await api1.join(topic, peer1);
196
210
  await api2.join(topic, peer2);
@@ -203,7 +217,7 @@ describe('MessageRouter', () => {
203
217
  topic,
204
218
  data: { offer: {} }
205
219
  });
206
- expect(answer1).toEqual({ accept: true });
220
+ expect(answer1.accept).toEqual(true);
207
221
 
208
222
  // sending offer from peer2 to peer1.
209
223
  const answer2 = await router2.offer({
@@ -213,6 +227,103 @@ describe('MessageRouter', () => {
213
227
  topic,
214
228
  data: { offer: {} }
215
229
  });
216
- expect(answer2).toEqual({ accept: true });
230
+ expect(answer2.accept).toEqual(true);
217
231
  }).timeout(5_000);
232
+
233
+ describe('Reliability', () => {
234
+ const setup = ({
235
+ onSignal1 = async () => { },
236
+ onSignal2 = async () => { },
237
+ // Imitates signal network disruptions (e. g. message doubling, ).
238
+ messageDisruption = msg => [msg]
239
+ }: {
240
+ onSignal1?: (msg: SignalMessage) => Promise<void>;
241
+ onSignal2?: (msg: SignalMessage) => Promise<void>;
242
+ messageDisruption?: (msg: SignalMessage) => SignalMessage[];
243
+ }): {mr1: MessageRouter; mr2: MessageRouter} => {
244
+
245
+ const mr1: MessageRouter = new MessageRouter({
246
+ sendMessage: async msg => messageDisruption(msg).forEach(msg => mr2.receiveMessage(msg)),
247
+ onOffer: async () => ({ accept: true }),
248
+ onSignal: onSignal1
249
+ });
250
+ afterTest(() => mr1.destroy());
251
+
252
+ const mr2: MessageRouter = new MessageRouter({
253
+ sendMessage: async msg => messageDisruption(msg).forEach(msg => mr1.receiveMessage(msg)),
254
+ onOffer: async () => ({ accept: true }),
255
+ onSignal: onSignal2
256
+ });
257
+ afterTest(() => mr1.destroy());
258
+
259
+ return { mr1, mr2 };
260
+ };
261
+
262
+ test('signaling with non reliable connection', async () => {
263
+ // Simulate unreliable connection.
264
+ // Only each 3rd message is sent.
265
+ let i = 0;
266
+ const unreliableConnection = (msg: SignalMessage): SignalMessage[] => {
267
+ i++;
268
+ if (i % 3 !== 0) {
269
+ return [msg];
270
+ }
271
+ return [];
272
+ };
273
+
274
+ const received: SignalMessage[] = [];
275
+ const signalMock1 = async (msg: SignalMessage) => {
276
+ received.push(msg);
277
+ };
278
+
279
+ const { mr2 } = await setup({
280
+ onSignal1: signalMock1,
281
+ messageDisruption: unreliableConnection
282
+ });
283
+
284
+ // Sending 3 messages.
285
+ // Setup sends messages directly to between. So we don`t need to specify any ids.
286
+ Array(3).fill(0).forEach(async () => {
287
+ await mr2.signal({
288
+ id: PublicKey.random(),
289
+ remoteId: PublicKey.random(),
290
+ sessionId: PublicKey.random(),
291
+ topic: PublicKey.random(),
292
+ data: { signal: { json: JSON.stringify({ 'asd': 'asd' }) } }
293
+ });
294
+ });
295
+ // expect to receive 3 messages.
296
+ await waitForExpect(() => {
297
+ expect(received.length).toEqual(3);
298
+ }, 4_000);
299
+ }).timeout(5_000);
300
+
301
+ test('ignoring doubled messages', async () => {
302
+ // Message got doubled going through signal network.
303
+ const doublingMessage = (msg: SignalMessage) => [msg, msg];
304
+
305
+ const received: SignalMessage[] = [];
306
+ const signalMock1 = async (msg: SignalMessage) => {
307
+ received.push(msg);
308
+ };
309
+
310
+ const { mr2 } = setup({
311
+ onSignal1: signalMock1,
312
+ messageDisruption: doublingMessage
313
+ });
314
+
315
+ // sending message.
316
+ await mr2.signal({
317
+ id: PublicKey.random(),
318
+ remoteId: PublicKey.random(),
319
+ sessionId: PublicKey.random(),
320
+ topic: PublicKey.random(),
321
+ data: { signal: { json: 'asd' } }
322
+ });
323
+ // expect to receive 1 message.
324
+ await waitForExpect(() => {
325
+ expect(received.length).toEqual(1);
326
+ }, 4_000);
327
+ }).timeout(5_000);
328
+ });
218
329
  });
@@ -3,11 +3,12 @@
3
3
  //
4
4
 
5
5
  import assert from 'assert';
6
+ import debug from 'debug';
6
7
 
7
8
  import { PublicKey } from '@dxos/protocols';
8
- import { ComplexMap } from '@dxos/util';
9
+ import { ComplexMap, ComplexSet, exponentialBackoffInterval, SubscriptionGroup } from '@dxos/util';
9
10
 
10
- import { Answer, Message } from '../proto/gen/dxos/mesh/signal';
11
+ import { Answer, SignalMessage } from '../proto/gen/dxos/mesh/signalMessage';
11
12
  import { SignalMessaging } from './signal-manager';
12
13
 
13
14
  interface OfferRecord {
@@ -16,74 +17,166 @@ interface OfferRecord {
16
17
  }
17
18
 
18
19
  interface MessageRouterOptions {
19
- onSignal: (message: Message) => Promise<void>;
20
- sendMessage: (message: Message) => Promise<void>;
21
- onOffer: (message: Message) => Promise<Answer>;
20
+ sendMessage?: (message: SignalMessage) => Promise<void>;
21
+ onOffer?: (message: SignalMessage) => Promise<Answer>;
22
+ onSignal?: (message: SignalMessage) => Promise<void>;
23
+ retryDelay?: number;
24
+ timeout?: number;
22
25
  }
23
26
 
27
+ const log = debug('dxos:network-manager:message-router');
24
28
  /**
25
29
  * Adds offer/answer RPC and reliable messaging.
26
30
  */
27
31
  // TODO(mykola): https://github.com/dxos/protocols/issues/1316
28
32
  export class MessageRouter implements SignalMessaging {
29
33
  private readonly _offerRecords: ComplexMap<PublicKey, OfferRecord> = new ComplexMap(key => key.toHex());
30
- private readonly _onSignal: (message: Message) => Promise<void>;
31
- private readonly _sendMessage: (message: Message) => Promise<void>;
32
- private readonly _onOffer: (message: Message) => Promise<Answer>;
34
+ private readonly _onSignal: (message: SignalMessage) => Promise<void>;
35
+ private readonly _sendMessage: (message: SignalMessage) => Promise<void>;
36
+ private readonly _onOffer: (message: SignalMessage) => Promise<Answer>;
37
+
38
+ private readonly _onAckCallbacks = new ComplexMap<PublicKey, () => void>(key => key.toHex());
39
+ private readonly _receivedMessages = new ComplexSet<PublicKey>(key => key.toHex());
40
+ private readonly _retryDelay: number;
41
+ private readonly _timeout: number;
42
+
43
+ private readonly _subscriptions = new SubscriptionGroup();
33
44
 
34
45
  constructor ({
35
46
  sendMessage,
36
47
  onSignal,
37
- onOffer
38
- }: MessageRouterOptions) {
48
+ onOffer,
49
+ retryDelay = 100,
50
+ timeout = 3000
51
+ }: MessageRouterOptions = {}) {
52
+ assert(sendMessage);
39
53
  this._sendMessage = sendMessage;
54
+ assert(onSignal);
40
55
  this._onSignal = onSignal;
56
+ assert(onOffer);
41
57
  this._onOffer = onOffer;
58
+ this._retryDelay = retryDelay;
59
+ this._timeout = timeout;
42
60
  }
43
61
 
44
- async receiveMessage (message: Message): Promise<void> {
62
+ async receiveMessage (message: SignalMessage): Promise<void> {
63
+ log(`receive message: ${JSON.stringify(message)}`);
64
+ if (!message.data?.ack) {
65
+ if (this._receivedMessages.has(message.messageId!)) {
66
+ return;
67
+ }
68
+
69
+ this._receivedMessages.add(message.messageId!);
70
+ await this._sendAcknowledgement(message);
71
+ }
72
+
45
73
  if (message.data?.offer) {
46
74
  await this._handleOffer(message);
47
75
  } else if (message.data?.answer) {
48
76
  await this._resolveAnswers(message);
49
77
  } else if (message.data?.signal) {
50
- await this._onSignal(message);
78
+ await this._handleSignal(message);
79
+ } else if (message.data?.ack) {
80
+ await this._handleAcknowledgement(message);
51
81
  }
52
82
  }
53
83
 
54
- async signal (message: Message): Promise<void> {
84
+ async signal (message: SignalMessage): Promise<void> {
55
85
  assert(message.data?.signal);
56
- await this._sendMessage(message);
86
+ await this._sendReliableMessage(message);
57
87
  }
58
88
 
59
- async offer (message: Message): Promise<Answer> {
89
+ async offer (message: SignalMessage): Promise<Answer> {
90
+ message.messageId = PublicKey.random();
60
91
  const promise = new Promise<Answer>((resolve, reject) => {
61
- assert(message.sessionId);
62
- this._offerRecords.set(message.sessionId, { resolve, reject });
92
+ this._offerRecords.set(message.messageId!, { resolve, reject });
93
+ return this._sendReliableMessage(message);
63
94
  });
64
- await this._sendMessage(message);
65
95
  return promise;
66
96
  }
67
97
 
68
- private async _resolveAnswers (message: Message): Promise<void> {
69
- assert(message.sessionId);
70
- const offerRecord = this._offerRecords.get(message.sessionId);
98
+ private async _sendReliableMessage (message: SignalMessage): Promise<PublicKey> {
99
+ // Setting unique messageId if it not specified yet.
100
+ message.messageId = message.messageId ?? PublicKey.random();
101
+ log(`sent message: ${JSON.stringify(message)}`);
102
+
103
+ // Setting retry interval if signal was not acknowledged.
104
+ const cancelRetry = exponentialBackoffInterval(async () => {
105
+ log(`retrying message: ${JSON.stringify(message)}`);
106
+ try {
107
+ await this._sendMessage(message);
108
+ } catch (error) {
109
+ log(`ERROR failed to send message: ${error}`);
110
+ }
111
+ }, this._retryDelay);
112
+
113
+ const timeout = setTimeout(() => {
114
+ log('Signal was not delivered!');
115
+ this._onAckCallbacks.delete(message.messageId!);
116
+ cancelRetry();
117
+ }, this._timeout);
118
+
119
+ assert(!this._onAckCallbacks.has(message.messageId!));
120
+ this._onAckCallbacks.set(message.messageId, () => {
121
+ this._onAckCallbacks.delete(message.messageId!);
122
+ cancelRetry();
123
+ clearTimeout(timeout);
124
+ });
125
+ this._subscriptions.push(cancelRetry);
126
+ this._subscriptions.push(() => clearTimeout(timeout));
127
+
128
+ await this._sendMessage(message);
129
+ return message.messageId;
130
+ }
131
+
132
+ private async _resolveAnswers (message: SignalMessage): Promise<void> {
133
+ assert(message.data?.answer?.offerMessageId, 'No offerMessageId');
134
+ const offerRecord = this._offerRecords.get(message.data.answer.offerMessageId);
71
135
  if (offerRecord) {
72
- this._offerRecords.delete(message.sessionId);
73
- assert(message.data?.answer);
136
+ this._offerRecords.delete(message.data.answer.offerMessageId);
137
+ assert(message.data?.answer, 'No Answer');
138
+ log(`resolving answer with ${message.data.answer}`);
74
139
  offerRecord.resolve(message.data.answer);
75
140
  }
76
141
  }
77
142
 
78
- private async _handleOffer (message: Message): Promise<void> {
143
+ private async _handleOffer (message: SignalMessage): Promise<void> {
79
144
  const answer = await this._onOffer(message);
80
- const answerMessage = {
145
+ answer.offerMessageId = message.messageId;
146
+ const answerMessage: SignalMessage = {
147
+ id: message.remoteId,
148
+ remoteId: message.id,
149
+ topic: message.topic,
150
+ sessionId: message.sessionId,
151
+ data: { answer }
152
+ };
153
+ await this._sendReliableMessage(answerMessage);
154
+ }
155
+
156
+ private async _handleSignal (message: SignalMessage): Promise<void> {
157
+ assert(message.messageId);
158
+ await this._onSignal(message);
159
+ }
160
+
161
+ private async _handleAcknowledgement (message: SignalMessage): Promise<void> {
162
+ assert(message.data?.ack?.messageId);
163
+ this._onAckCallbacks.get(message.data.ack.messageId)?.();
164
+ }
165
+
166
+ private async _sendAcknowledgement (message: SignalMessage): Promise<void> {
167
+ assert(message.messageId);
168
+ const ackMessage = {
81
169
  id: message.remoteId,
82
170
  remoteId: message.id,
83
171
  topic: message.topic,
84
172
  sessionId: message.sessionId,
85
- data: { answer: answer }
173
+ data: { ack: { messageId: message.messageId } }
86
174
  };
87
- await this._sendMessage(answerMessage);
175
+ log(`sent ack: ${JSON.stringify(ackMessage)}`);
176
+ await this._sendMessage(ackMessage);
177
+ }
178
+
179
+ destroy (): void {
180
+ this._subscriptions.unsubscribe();
88
181
  }
89
182
  }