@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.
- package/dist/src/network-manager.blueprint-test.d.ts +3 -1
- package/dist/src/network-manager.blueprint-test.d.ts.map +1 -1
- package/dist/src/network-manager.blueprint-test.js +46 -17
- package/dist/src/network-manager.blueprint-test.js.map +1 -1
- package/dist/src/network-manager.browser-test.js +1 -1
- package/dist/src/network-manager.browser-test.js.map +1 -1
- package/dist/src/network-manager.d.ts.map +1 -1
- package/dist/src/network-manager.js +13 -12
- package/dist/src/network-manager.js.map +1 -1
- package/dist/src/network-manager.test.js +5 -4
- package/dist/src/network-manager.test.js.map +1 -1
- package/dist/src/proto/gen/dxos/credentials.d.ts +39 -0
- package/dist/src/proto/gen/dxos/credentials.d.ts.map +1 -1
- package/dist/src/proto/gen/dxos/halo/keys.d.ts +44 -2
- package/dist/src/proto/gen/dxos/halo/keys.d.ts.map +1 -1
- package/dist/src/proto/gen/dxos/halo/keys.js +4 -0
- package/dist/src/proto/gen/dxos/halo/keys.js.map +1 -1
- package/dist/src/proto/gen/dxos/mesh/signal.d.ts +74 -16
- package/dist/src/proto/gen/dxos/mesh/signal.d.ts.map +1 -1
- package/dist/src/proto/gen/dxos/mesh/signalMessage.d.ts +79 -0
- package/dist/src/proto/gen/dxos/mesh/signalMessage.d.ts.map +1 -0
- package/dist/src/proto/gen/dxos/mesh/signalMessage.js +3 -0
- package/dist/src/proto/gen/dxos/mesh/signalMessage.js.map +1 -0
- package/dist/src/proto/gen/google/protobuf.d.ts +8 -2
- package/dist/src/proto/gen/google/protobuf.d.ts.map +1 -1
- package/dist/src/proto/gen/index.d.ts +17 -4
- package/dist/src/proto/gen/index.d.ts.map +1 -1
- package/dist/src/proto/gen/index.js +1 -1
- package/dist/src/proto/gen/index.js.map +1 -1
- package/dist/src/proto/substitutions.d.ts +4 -0
- package/dist/src/proto/substitutions.d.ts.map +1 -1
- package/dist/src/proto/substitutions.js +3 -1
- package/dist/src/proto/substitutions.js.map +1 -1
- package/dist/src/protocol-factory.js +3 -3
- package/dist/src/protocol-factory.js.map +1 -1
- package/dist/src/signal/in-memory-signal-manager.d.ts +7 -7
- package/dist/src/signal/in-memory-signal-manager.d.ts.map +1 -1
- package/dist/src/signal/in-memory-signal-manager.js +34 -13
- package/dist/src/signal/in-memory-signal-manager.js.map +1 -1
- package/dist/src/signal/index.d.ts +1 -2
- package/dist/src/signal/index.d.ts.map +1 -1
- package/dist/src/signal/index.js +1 -2
- package/dist/src/signal/index.js.map +1 -1
- package/dist/src/signal/integration.test.d.ts +2 -0
- package/dist/src/signal/integration.test.d.ts.map +1 -0
- package/dist/src/signal/integration.test.js +102 -0
- package/dist/src/signal/integration.test.js.map +1 -0
- package/dist/src/signal/message-router.d.ts +20 -8
- package/dist/src/signal/message-router.d.ts.map +1 -1
- package/dist/src/signal/message-router.js +96 -17
- package/dist/src/signal/message-router.js.map +1 -1
- package/dist/src/signal/message-router.test.js +125 -22
- package/dist/src/signal/message-router.test.js.map +1 -1
- package/dist/src/signal/signal-client.d.ts +33 -17
- package/dist/src/signal/signal-client.d.ts.map +1 -1
- package/dist/src/signal/signal-client.js +102 -82
- package/dist/src/signal/signal-client.js.map +1 -1
- package/dist/src/signal/signal-client.test.js +60 -75
- package/dist/src/signal/signal-client.test.js.map +1 -1
- package/dist/src/signal/{websocket-signal-manager.d.ts → signal-manager-impl.d.ts} +13 -11
- package/dist/src/signal/signal-manager-impl.d.ts.map +1 -0
- package/dist/src/signal/signal-manager-impl.js +151 -0
- package/dist/src/signal/signal-manager-impl.js.map +1 -0
- package/dist/src/signal/signal-manager.d.ts +12 -11
- package/dist/src/signal/signal-manager.d.ts.map +1 -1
- package/dist/src/signal/signal-rpc-client.d.ts +19 -0
- package/dist/src/signal/signal-rpc-client.d.ts.map +1 -0
- package/dist/src/signal/signal-rpc-client.js +108 -0
- package/dist/src/signal/signal-rpc-client.js.map +1 -0
- package/dist/src/signal/signal-rpc-client.test.d.ts +2 -0
- package/dist/src/signal/signal-rpc-client.test.d.ts.map +1 -0
- package/dist/src/signal/signal-rpc-client.test.js +74 -0
- package/dist/src/signal/signal-rpc-client.test.js.map +1 -0
- package/dist/src/swarm/connection.d.ts +3 -3
- package/dist/src/swarm/connection.d.ts.map +1 -1
- package/dist/src/swarm/connection.js +8 -11
- package/dist/src/swarm/connection.js.map +1 -1
- package/dist/src/swarm/swarm.d.ts +6 -7
- package/dist/src/swarm/swarm.d.ts.map +1 -1
- package/dist/src/swarm/swarm.js +29 -25
- package/dist/src/swarm/swarm.js.map +1 -1
- package/dist/src/swarm/swarm.test.js +156 -115
- package/dist/src/swarm/swarm.test.js.map +1 -1
- package/dist/src/testing/test-protocol.d.ts.map +1 -1
- package/dist/src/testing/test-protocol.js +3 -3
- package/dist/src/testing/test-protocol.js.map +1 -1
- package/dist/src/topology/fully-connected-topology.d.ts +0 -1
- package/dist/src/topology/fully-connected-topology.d.ts.map +1 -1
- package/dist/src/topology/fully-connected-topology.js +4 -9
- package/dist/src/topology/fully-connected-topology.js.map +1 -1
- package/dist/src/topology/mmst-topology.d.ts +0 -1
- package/dist/src/topology/mmst-topology.d.ts.map +1 -1
- package/dist/src/topology/mmst-topology.js +6 -11
- package/dist/src/topology/mmst-topology.js.map +1 -1
- package/dist/src/topology/star-topology.d.ts +0 -1
- package/dist/src/topology/star-topology.d.ts.map +1 -1
- package/dist/src/topology/star-topology.js +5 -10
- package/dist/src/topology/star-topology.js.map +1 -1
- package/dist/src/topology/topology.d.ts +0 -6
- package/dist/src/topology/topology.d.ts.map +1 -1
- package/dist/src/transport/in-memory-transport.d.ts +2 -2
- package/dist/src/transport/in-memory-transport.d.ts.map +1 -1
- package/dist/src/transport/in-memory-transport.js +2 -2
- package/dist/src/transport/in-memory-transport.js.map +1 -1
- package/dist/src/transport/transport.d.ts +3 -3
- package/dist/src/transport/transport.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport.d.ts +3 -3
- package/dist/src/transport/webrtc-transport.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport.js +3 -3
- package/dist/src/transport/webrtc-transport.js.map +1 -1
- package/dist/tests-setup.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -12
- package/src/network-manager.blueprint-test.ts +57 -22
- package/src/network-manager.browser-test.ts +1 -1
- package/src/network-manager.test.ts +8 -7
- package/src/network-manager.ts +10 -10
- package/src/proto/defs/dxos/mesh/signal.proto +54 -23
- package/src/proto/defs/dxos/mesh/signalMessage.proto +51 -0
- package/src/proto/gen/dxos/credentials.ts +40 -0
- package/src/proto/gen/dxos/halo/keys.ts +45 -2
- package/src/proto/gen/dxos/mesh/signal.ts +73 -16
- package/src/proto/gen/dxos/mesh/signalMessage.ts +83 -0
- package/src/proto/gen/google/protobuf.ts +9 -2
- package/src/proto/gen/index.ts +18 -5
- package/src/proto/substitutions.ts +3 -1
- package/src/protocol-factory.ts +1 -1
- package/src/signal/in-memory-signal-manager.ts +38 -13
- package/src/signal/index.ts +1 -2
- package/src/signal/integration.test.ts +117 -0
- package/src/signal/message-router.test.ts +169 -58
- package/src/signal/message-router.ts +120 -27
- package/src/signal/signal-client.test.ts +70 -90
- package/src/signal/signal-client.ts +120 -87
- package/src/signal/signal-manager-impl.ts +166 -0
- package/src/signal/signal-manager.ts +12 -12
- package/src/signal/signal-rpc-client.test.ts +86 -0
- package/src/signal/signal-rpc-client.ts +121 -0
- package/src/swarm/connection.ts +6 -9
- package/src/swarm/swarm.test.ts +208 -167
- package/src/swarm/swarm.ts +26 -22
- package/src/testing/test-protocol.ts +1 -1
- package/src/topology/fully-connected-topology.ts +2 -10
- package/src/topology/mmst-topology.ts +2 -10
- package/src/topology/star-topology.ts +2 -8
- package/src/topology/topology.ts +0 -7
- package/src/transport/in-memory-transport.ts +3 -3
- package/src/transport/transport.ts +3 -3
- package/src/transport/webrtc-transport.ts +4 -4
- package/dist/src/signal/websocket-rpc.d.ts +0 -30
- package/dist/src/signal/websocket-rpc.d.ts.map +0 -1
- package/dist/src/signal/websocket-rpc.js +0 -203
- package/dist/src/signal/websocket-rpc.js.map +0 -1
- package/dist/src/signal/websocket-signal-manager.d.ts.map +0 -1
- package/dist/src/signal/websocket-signal-manager.js +0 -134
- package/dist/src/signal/websocket-signal-manager.js.map +0 -1
- package/src/signal/websocket-rpc.ts +0 -208
- 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,
|
|
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(
|
|
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(
|
|
39
|
-
|
|
40
|
-
await broker1.stop();
|
|
35
|
+
after(() => {
|
|
36
|
+
broker1.stop();
|
|
41
37
|
});
|
|
42
38
|
|
|
43
|
-
const createSignalClientAndMessageRouter = async (
|
|
44
|
-
signalApiUrl
|
|
45
|
-
onSignal
|
|
46
|
-
onOffer
|
|
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:
|
|
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
|
-
|
|
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:
|
|
73
|
-
const { api: api1 } = await createSignalClientAndMessageRouter(
|
|
74
|
-
const { api: api2, router: router2 } = await createSignalClientAndMessageRouter(
|
|
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:
|
|
79
|
+
const msg: SignalMessage = {
|
|
80
80
|
id: peer2,
|
|
81
81
|
remoteId: peer1,
|
|
82
82
|
sessionId: PublicKey.random(),
|
|
83
83
|
topic,
|
|
84
|
-
data: { signal: { json: '
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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(
|
|
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:
|
|
123
|
+
const signalMock1 = mockFn<(msg: SignalMessage) => Promise<void>>().resolvesTo();
|
|
120
124
|
const { api: api1, router: router1 } = await createSignalClientAndMessageRouter(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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:
|
|
31
|
-
private readonly _sendMessage: (message:
|
|
32
|
-
private readonly _onOffer: (message:
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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:
|
|
84
|
+
async signal (message: SignalMessage): Promise<void> {
|
|
55
85
|
assert(message.data?.signal);
|
|
56
|
-
await this.
|
|
86
|
+
await this._sendReliableMessage(message);
|
|
57
87
|
}
|
|
58
88
|
|
|
59
|
-
async offer (message:
|
|
89
|
+
async offer (message: SignalMessage): Promise<Answer> {
|
|
90
|
+
message.messageId = PublicKey.random();
|
|
60
91
|
const promise = new Promise<Answer>((resolve, reject) => {
|
|
61
|
-
|
|
62
|
-
this.
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
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.
|
|
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:
|
|
143
|
+
private async _handleOffer (message: SignalMessage): Promise<void> {
|
|
79
144
|
const answer = await this._onOffer(message);
|
|
80
|
-
|
|
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: {
|
|
173
|
+
data: { ack: { messageId: message.messageId } }
|
|
86
174
|
};
|
|
87
|
-
|
|
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
|
}
|