@dxos/network-manager 0.1.0
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/LICENSE +21 -0
- package/dist/src/connection-log.d.ts +23 -0
- package/dist/src/connection-log.d.ts.map +1 -0
- package/dist/src/connection-log.js +98 -0
- package/dist/src/connection-log.js.map +1 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +28 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/network-manager.d.ts +80 -0
- package/dist/src/network-manager.d.ts.map +1 -0
- package/dist/src/network-manager.js +123 -0
- package/dist/src/network-manager.js.map +1 -0
- package/dist/src/network-manager.test.d.ts +2 -0
- package/dist/src/network-manager.test.d.ts.map +1 -0
- package/dist/src/network-manager.test.js +621 -0
- package/dist/src/network-manager.test.js.map +1 -0
- package/dist/src/presence.test.d.ts +2 -0
- package/dist/src/presence.test.d.ts.map +1 -0
- package/dist/src/presence.test.js +60 -0
- package/dist/src/presence.test.js.map +1 -0
- package/dist/src/protocol-factory.d.ts +30 -0
- package/dist/src/protocol-factory.d.ts.map +1 -0
- package/dist/src/protocol-factory.js +64 -0
- package/dist/src/protocol-factory.js.map +1 -0
- package/dist/src/signal/index.d.ts +4 -0
- package/dist/src/signal/index.d.ts.map +1 -0
- package/dist/src/signal/index.js +23 -0
- package/dist/src/signal/index.js.map +1 -0
- 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 +105 -0
- package/dist/src/signal/integration.test.js.map +1 -0
- package/dist/src/signal/message-router.d.ts +39 -0
- package/dist/src/signal/message-router.d.ts.map +1 -0
- package/dist/src/signal/message-router.js +135 -0
- package/dist/src/signal/message-router.js.map +1 -0
- package/dist/src/signal/message-router.test.d.ts +2 -0
- package/dist/src/signal/message-router.test.d.ts.map +1 -0
- package/dist/src/signal/message-router.test.js +211 -0
- package/dist/src/signal/message-router.test.js.map +1 -0
- package/dist/src/signal/signal-connection.d.ts +21 -0
- package/dist/src/signal/signal-connection.d.ts.map +1 -0
- package/dist/src/signal/signal-connection.js +6 -0
- package/dist/src/signal/signal-connection.js.map +1 -0
- package/dist/src/signal/signal-messaging.d.ts +34 -0
- package/dist/src/signal/signal-messaging.d.ts.map +1 -0
- package/dist/src/signal/signal-messaging.js +6 -0
- package/dist/src/signal/signal-messaging.js.map +1 -0
- package/dist/src/swarm/connection.d.ts +69 -0
- package/dist/src/swarm/connection.d.ts.map +1 -0
- package/dist/src/swarm/connection.js +187 -0
- package/dist/src/swarm/connection.js.map +1 -0
- package/dist/src/swarm/index.d.ts +4 -0
- package/dist/src/swarm/index.d.ts.map +1 -0
- package/dist/src/swarm/index.js +23 -0
- package/dist/src/swarm/index.js.map +1 -0
- package/dist/src/swarm/swarm-mapper.d.ts +30 -0
- package/dist/src/swarm/swarm-mapper.d.ts.map +1 -0
- package/dist/src/swarm/swarm-mapper.js +90 -0
- package/dist/src/swarm/swarm-mapper.js.map +1 -0
- package/dist/src/swarm/swarm.d.ts +64 -0
- package/dist/src/swarm/swarm.d.ts.map +1 -0
- package/dist/src/swarm/swarm.js +276 -0
- package/dist/src/swarm/swarm.js.map +1 -0
- package/dist/src/swarm/swarm.test.d.ts +2 -0
- package/dist/src/swarm/swarm.test.d.ts.map +1 -0
- package/dist/src/swarm/swarm.test.js +145 -0
- package/dist/src/swarm/swarm.test.js.map +1 -0
- package/dist/src/testing/index.d.ts +2 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +21 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/test-protocol.d.ts +59 -0
- package/dist/src/testing/test-protocol.d.ts.map +1 -0
- package/dist/src/testing/test-protocol.js +140 -0
- package/dist/src/testing/test-protocol.js.map +1 -0
- package/dist/src/topology/fully-connected-topology.d.ts +11 -0
- package/dist/src/topology/fully-connected-topology.d.ts.map +1 -0
- package/dist/src/topology/fully-connected-topology.js +34 -0
- package/dist/src/topology/fully-connected-topology.js.map +1 -0
- package/dist/src/topology/index.d.ts +5 -0
- package/dist/src/topology/index.d.ts.map +1 -0
- package/dist/src/topology/index.js +24 -0
- package/dist/src/topology/index.js.map +1 -0
- package/dist/src/topology/mmst-topology.d.ts +31 -0
- package/dist/src/topology/mmst-topology.d.ts.map +1 -0
- package/dist/src/topology/mmst-topology.js +73 -0
- package/dist/src/topology/mmst-topology.js.map +1 -0
- package/dist/src/topology/star-topology.d.ts +13 -0
- package/dist/src/topology/star-topology.d.ts.map +1 -0
- package/dist/src/topology/star-topology.js +60 -0
- package/dist/src/topology/star-topology.js.map +1 -0
- package/dist/src/topology/topology.d.ts +56 -0
- package/dist/src/topology/topology.d.ts.map +1 -0
- package/dist/src/topology/topology.js +6 -0
- package/dist/src/topology/topology.js.map +1 -0
- package/dist/src/transport/index.d.ts +6 -0
- package/dist/src/transport/index.d.ts.map +1 -0
- package/dist/src/transport/index.js +25 -0
- package/dist/src/transport/index.js.map +1 -0
- package/dist/src/transport/memory-transport.d.ts +32 -0
- package/dist/src/transport/memory-transport.d.ts.map +1 -0
- package/dist/src/transport/memory-transport.js +98 -0
- package/dist/src/transport/memory-transport.js.map +1 -0
- package/dist/src/transport/memory-transport.test.d.ts +2 -0
- package/dist/src/transport/memory-transport.test.d.ts.map +1 -0
- package/dist/src/transport/memory-transport.test.js +84 -0
- package/dist/src/transport/memory-transport.test.js.map +1 -0
- package/dist/src/transport/transport.d.ts +38 -0
- package/dist/src/transport/transport.d.ts.map +1 -0
- package/dist/src/transport/transport.js +6 -0
- package/dist/src/transport/transport.js.map +1 -0
- package/dist/src/transport/webrtc-transport-proxy.d.ts +48 -0
- package/dist/src/transport/webrtc-transport-proxy.d.ts.map +1 -0
- package/dist/src/transport/webrtc-transport-proxy.js +139 -0
- package/dist/src/transport/webrtc-transport-proxy.js.map +1 -0
- package/dist/src/transport/webrtc-transport-proxy.test.d.ts +2 -0
- package/dist/src/transport/webrtc-transport-proxy.test.d.ts.map +1 -0
- package/dist/src/transport/webrtc-transport-proxy.test.js +245 -0
- package/dist/src/transport/webrtc-transport-proxy.test.js.map +1 -0
- package/dist/src/transport/webrtc-transport-service.d.ts +15 -0
- package/dist/src/transport/webrtc-transport-service.d.ts.map +1 -0
- package/dist/src/transport/webrtc-transport-service.js +97 -0
- package/dist/src/transport/webrtc-transport-service.js.map +1 -0
- package/dist/src/transport/webrtc-transport.d.ts +34 -0
- package/dist/src/transport/webrtc-transport.d.ts.map +1 -0
- package/dist/src/transport/webrtc-transport.js +100 -0
- package/dist/src/transport/webrtc-transport.js.map +1 -0
- package/dist/src/transport/webrtc-transport.test.d.ts +2 -0
- package/dist/src/transport/webrtc-transport.test.d.ts.map +1 -0
- package/dist/src/transport/webrtc-transport.test.js +80 -0
- package/dist/src/transport/webrtc-transport.test.js.map +1 -0
- package/dist/src/transport/webrtc.d.ts +6 -0
- package/dist/src/transport/webrtc.d.ts.map +1 -0
- package/dist/src/transport/webrtc.js +17 -0
- package/dist/src/transport/webrtc.js.map +1 -0
- package/dist/src/transport/wrtc-simple-peer.test.d.ts +2 -0
- package/dist/src/transport/wrtc-simple-peer.test.d.ts.map +1 -0
- package/dist/src/transport/wrtc-simple-peer.test.js +24 -0
- package/dist/src/transport/wrtc-simple-peer.test.js.map +1 -0
- package/dist/src/types.d.ts +6 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +6 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +58 -0
- package/src/connection-log.ts +102 -0
- package/src/globals.d.ts +7 -0
- package/src/index.ts +12 -0
- package/src/network-manager.test.ts +764 -0
- package/src/network-manager.ts +204 -0
- package/src/presence.test.ts +78 -0
- package/src/protocol-factory.ts +83 -0
- package/src/signal/index.ts +7 -0
- package/src/signal/integration.test.ts +129 -0
- package/src/signal/message-router.test.ts +274 -0
- package/src/signal/message-router.ts +200 -0
- package/src/signal/signal-connection.ts +20 -0
- package/src/signal/signal-messaging.ts +38 -0
- package/src/swarm/connection.ts +206 -0
- package/src/swarm/index.ts +7 -0
- package/src/swarm/swarm-mapper.ts +120 -0
- package/src/swarm/swarm.test.ts +192 -0
- package/src/swarm/swarm.ts +328 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/test-protocol.ts +160 -0
- package/src/topology/fully-connected-topology.ts +38 -0
- package/src/topology/index.ts +8 -0
- package/src/topology/mmst-topology.ts +105 -0
- package/src/topology/star-topology.ts +67 -0
- package/src/topology/topology.ts +69 -0
- package/src/transport/index.ts +9 -0
- package/src/transport/memory-transport.test.ts +110 -0
- package/src/transport/memory-transport.ts +128 -0
- package/src/transport/transport.ts +47 -0
- package/src/transport/webrtc-transport-proxy.test.ts +290 -0
- package/src/transport/webrtc-transport-proxy.ts +167 -0
- package/src/transport/webrtc-transport-service.ts +115 -0
- package/src/transport/webrtc-transport.test.ts +112 -0
- package/src/transport/webrtc-transport.ts +122 -0
- package/src/transport/webrtc.ts +15 -0
- package/src/transport/wrtc-simple-peer.test.ts +24 -0
- package/src/types.ts +10 -0
- package/src/typings.d.ts +7 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @dxos/mocha platform=nodejs
|
|
6
|
+
|
|
7
|
+
import { expect } from 'earljs';
|
|
8
|
+
import { it, describe } from 'mocha';
|
|
9
|
+
import waitForExpect from 'wait-for-expect';
|
|
10
|
+
|
|
11
|
+
import { Awaited } from '@dxos/async';
|
|
12
|
+
import { PublicKey } from '@dxos/keys';
|
|
13
|
+
import { Messenger, WebsocketSignalManager } from '@dxos/messaging';
|
|
14
|
+
import { Answer } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
15
|
+
import { createTestBroker } from '@dxos/signal';
|
|
16
|
+
import { afterTest } from '@dxos/testutils';
|
|
17
|
+
|
|
18
|
+
import { MessageRouter } from './message-router';
|
|
19
|
+
import { OfferMessage, SignalMessage } from './signal-messaging';
|
|
20
|
+
|
|
21
|
+
describe('MessageRouter', function () {
|
|
22
|
+
let topic: PublicKey;
|
|
23
|
+
|
|
24
|
+
let broker1: Awaited<ReturnType<typeof createTestBroker>>;
|
|
25
|
+
|
|
26
|
+
before(async function () {
|
|
27
|
+
broker1 = await createTestBroker();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
beforeEach(function () {
|
|
31
|
+
topic = PublicKey.random();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
after(function () {
|
|
35
|
+
broker1.stop();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const createSignalClientAndMessageRouter = async ({
|
|
39
|
+
signalApiUrl,
|
|
40
|
+
onSignal = (async () => {}) as any,
|
|
41
|
+
onOffer = async () => ({ accept: true }),
|
|
42
|
+
topic
|
|
43
|
+
}: {
|
|
44
|
+
signalApiUrl: string;
|
|
45
|
+
onSignal?: (msg: SignalMessage) => Promise<void>;
|
|
46
|
+
onOffer?: (msg: OfferMessage) => Promise<Answer>;
|
|
47
|
+
topic: PublicKey;
|
|
48
|
+
}) => {
|
|
49
|
+
const peerId = PublicKey.random();
|
|
50
|
+
const signalManager = new WebsocketSignalManager([signalApiUrl]);
|
|
51
|
+
afterTest(() => signalManager.destroy());
|
|
52
|
+
|
|
53
|
+
const messenger = new Messenger({ signalManager });
|
|
54
|
+
await messenger.listen({
|
|
55
|
+
peerId,
|
|
56
|
+
payloadType: 'dxos.mesh.swarm.SwarmMessage',
|
|
57
|
+
onMessage: async (message) => await router.receiveMessage(message)
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const router: MessageRouter = new MessageRouter({
|
|
61
|
+
// todo(mykola): added catch to avoid not finished request.
|
|
62
|
+
sendMessage: async (message) => await messenger.sendMessage(message),
|
|
63
|
+
onSignal,
|
|
64
|
+
onOffer,
|
|
65
|
+
topic
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
peerId,
|
|
70
|
+
signalManager,
|
|
71
|
+
router
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
it('signaling between 2 clients', async () => {
|
|
76
|
+
const received: SignalMessage[] = [];
|
|
77
|
+
const signalMock1 = async (msg: SignalMessage) => {
|
|
78
|
+
received.push(msg);
|
|
79
|
+
};
|
|
80
|
+
const { signalManager: signalManager1, peerId: peer1 } = await createSignalClientAndMessageRouter({
|
|
81
|
+
signalApiUrl: broker1.url(),
|
|
82
|
+
onSignal: signalMock1,
|
|
83
|
+
topic
|
|
84
|
+
});
|
|
85
|
+
const {
|
|
86
|
+
signalManager: signalManager2,
|
|
87
|
+
router: router2,
|
|
88
|
+
peerId: peer2
|
|
89
|
+
} = await createSignalClientAndMessageRouter({
|
|
90
|
+
signalApiUrl: broker1.url(),
|
|
91
|
+
topic
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await signalManager1.join({ topic, peerId: peer1 });
|
|
95
|
+
await signalManager2.join({ topic, peerId: peer2 });
|
|
96
|
+
|
|
97
|
+
const msg: SignalMessage = {
|
|
98
|
+
author: peer2,
|
|
99
|
+
recipient: peer1,
|
|
100
|
+
sessionId: PublicKey.random(),
|
|
101
|
+
topic,
|
|
102
|
+
data: { signal: { json: JSON.stringify({ asd: 'asd' }) } }
|
|
103
|
+
};
|
|
104
|
+
await router2.signal(msg);
|
|
105
|
+
|
|
106
|
+
await waitForExpect(() => {
|
|
107
|
+
expect(received[0]).toBeAnObjectWith(msg);
|
|
108
|
+
}, 4_000);
|
|
109
|
+
}).timeout(5_000);
|
|
110
|
+
|
|
111
|
+
it('offer/answer', async () => {
|
|
112
|
+
const {
|
|
113
|
+
signalManager: signalManager1,
|
|
114
|
+
router: router1,
|
|
115
|
+
peerId: peer1
|
|
116
|
+
} = await createSignalClientAndMessageRouter({
|
|
117
|
+
signalApiUrl: broker1.url(),
|
|
118
|
+
onSignal: (async () => {}) as any,
|
|
119
|
+
onOffer: async () => ({ accept: true }),
|
|
120
|
+
topic
|
|
121
|
+
});
|
|
122
|
+
const { signalManager: signalManager2, peerId: peer2 } = await createSignalClientAndMessageRouter({
|
|
123
|
+
signalApiUrl: broker1.url(),
|
|
124
|
+
onSignal: (async () => {}) as any,
|
|
125
|
+
onOffer: async () => ({ accept: true }),
|
|
126
|
+
topic
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await signalManager1.join({ topic, peerId: peer1 });
|
|
130
|
+
await signalManager2.join({ topic, peerId: peer2 });
|
|
131
|
+
const answer = await router1.offer({
|
|
132
|
+
author: peer1,
|
|
133
|
+
recipient: peer2,
|
|
134
|
+
sessionId: PublicKey.random(),
|
|
135
|
+
topic,
|
|
136
|
+
data: { offer: {} }
|
|
137
|
+
});
|
|
138
|
+
expect(answer.accept).toEqual(true);
|
|
139
|
+
}).timeout(5_000);
|
|
140
|
+
|
|
141
|
+
it('signaling between 3 clients', async () => {
|
|
142
|
+
const received1: SignalMessage[] = [];
|
|
143
|
+
const signalMock1 = async (msg: SignalMessage) => {
|
|
144
|
+
received1.push(msg);
|
|
145
|
+
};
|
|
146
|
+
const {
|
|
147
|
+
signalManager: signalManager1,
|
|
148
|
+
router: router1,
|
|
149
|
+
peerId: peer1
|
|
150
|
+
} = await createSignalClientAndMessageRouter({
|
|
151
|
+
signalApiUrl: broker1.url(),
|
|
152
|
+
onSignal: signalMock1,
|
|
153
|
+
onOffer: async () => ({ accept: true }),
|
|
154
|
+
topic
|
|
155
|
+
});
|
|
156
|
+
const received2: SignalMessage[] = [];
|
|
157
|
+
const signalMock2 = async (msg: SignalMessage) => {
|
|
158
|
+
received2.push(msg);
|
|
159
|
+
};
|
|
160
|
+
const {
|
|
161
|
+
signalManager: signalManager2,
|
|
162
|
+
router: router2,
|
|
163
|
+
peerId: peer2
|
|
164
|
+
} = await createSignalClientAndMessageRouter({
|
|
165
|
+
signalApiUrl: broker1.url(),
|
|
166
|
+
onSignal: signalMock2,
|
|
167
|
+
onOffer: async () => ({ accept: true }),
|
|
168
|
+
topic
|
|
169
|
+
});
|
|
170
|
+
const received3: SignalMessage[] = [];
|
|
171
|
+
const signalMock3 = async (msg: SignalMessage) => {
|
|
172
|
+
received3.push(msg);
|
|
173
|
+
};
|
|
174
|
+
const {
|
|
175
|
+
signalManager: signalManager3,
|
|
176
|
+
router: router3,
|
|
177
|
+
peerId: peer3
|
|
178
|
+
} = await createSignalClientAndMessageRouter({
|
|
179
|
+
signalApiUrl: broker1.url(),
|
|
180
|
+
onSignal: signalMock3,
|
|
181
|
+
onOffer: async () => ({ accept: true }),
|
|
182
|
+
topic
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await signalManager1.join({ topic, peerId: peer1 });
|
|
186
|
+
await signalManager2.join({ topic, peerId: peer2 });
|
|
187
|
+
await signalManager3.join({ topic, peerId: peer3 });
|
|
188
|
+
|
|
189
|
+
// sending signal from peer1 to peer3.
|
|
190
|
+
const msg1to3: SignalMessage = {
|
|
191
|
+
author: peer1,
|
|
192
|
+
recipient: peer3,
|
|
193
|
+
sessionId: PublicKey.random(),
|
|
194
|
+
topic,
|
|
195
|
+
data: { signal: { json: '1to3' } }
|
|
196
|
+
};
|
|
197
|
+
await router1.signal(msg1to3);
|
|
198
|
+
await waitForExpect(() => {
|
|
199
|
+
expect(received3[0]).toBeAnObjectWith(msg1to3);
|
|
200
|
+
}, 4_000);
|
|
201
|
+
|
|
202
|
+
// sending signal from peer2 to peer3.
|
|
203
|
+
const msg2to3: SignalMessage = {
|
|
204
|
+
author: peer2,
|
|
205
|
+
recipient: peer3,
|
|
206
|
+
sessionId: PublicKey.random(),
|
|
207
|
+
topic,
|
|
208
|
+
data: { signal: { json: '2to3' } }
|
|
209
|
+
};
|
|
210
|
+
await router2.signal(msg2to3);
|
|
211
|
+
await waitForExpect(() => {
|
|
212
|
+
expect(received3[1]).toBeAnObjectWith(msg2to3);
|
|
213
|
+
}, 4_000);
|
|
214
|
+
|
|
215
|
+
// sending signal from peer3 to peer1.
|
|
216
|
+
const msg3to1: SignalMessage = {
|
|
217
|
+
author: peer3,
|
|
218
|
+
recipient: peer1,
|
|
219
|
+
sessionId: PublicKey.random(),
|
|
220
|
+
topic,
|
|
221
|
+
data: { signal: { json: '3to1' } }
|
|
222
|
+
};
|
|
223
|
+
await router3.signal(msg3to1);
|
|
224
|
+
await waitForExpect(() => {
|
|
225
|
+
expect(received1[0]).toBeAnObjectWith(msg3to1);
|
|
226
|
+
}, 4_000);
|
|
227
|
+
}).timeout(5_000);
|
|
228
|
+
|
|
229
|
+
it('two offers', async () => {
|
|
230
|
+
const {
|
|
231
|
+
signalManager: signalManager1,
|
|
232
|
+
router: router1,
|
|
233
|
+
peerId: peer1
|
|
234
|
+
} = await createSignalClientAndMessageRouter({
|
|
235
|
+
signalApiUrl: broker1.url(),
|
|
236
|
+
onSignal: (async () => {}) as any,
|
|
237
|
+
onOffer: async () => ({ accept: true }),
|
|
238
|
+
topic
|
|
239
|
+
});
|
|
240
|
+
const {
|
|
241
|
+
signalManager: signalManager2,
|
|
242
|
+
router: router2,
|
|
243
|
+
peerId: peer2
|
|
244
|
+
} = await createSignalClientAndMessageRouter({
|
|
245
|
+
signalApiUrl: broker1.url(),
|
|
246
|
+
onSignal: (async () => {}) as any,
|
|
247
|
+
onOffer: async () => ({ accept: true }),
|
|
248
|
+
topic
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
await signalManager1.join({ topic, peerId: peer1 });
|
|
252
|
+
await signalManager2.join({ topic, peerId: peer2 });
|
|
253
|
+
|
|
254
|
+
// sending offer from peer1 to peer2.
|
|
255
|
+
const answer1 = await router1.offer({
|
|
256
|
+
author: peer1,
|
|
257
|
+
recipient: peer2,
|
|
258
|
+
sessionId: PublicKey.random(),
|
|
259
|
+
topic,
|
|
260
|
+
data: { offer: {} }
|
|
261
|
+
});
|
|
262
|
+
expect(answer1.accept).toEqual(true);
|
|
263
|
+
|
|
264
|
+
// sending offer from peer2 to peer1.
|
|
265
|
+
const answer2 = await router2.offer({
|
|
266
|
+
author: peer2,
|
|
267
|
+
recipient: peer1,
|
|
268
|
+
sessionId: PublicKey.random(),
|
|
269
|
+
topic,
|
|
270
|
+
data: { offer: {} }
|
|
271
|
+
});
|
|
272
|
+
expect(answer2.accept).toEqual(true);
|
|
273
|
+
}).timeout(5_000);
|
|
274
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'assert';
|
|
6
|
+
|
|
7
|
+
import { Any } from '@dxos/codec-protobuf';
|
|
8
|
+
import { PublicKey } from '@dxos/keys';
|
|
9
|
+
import { log } from '@dxos/log';
|
|
10
|
+
import { schema } from '@dxos/protocols';
|
|
11
|
+
import { Answer, SwarmMessage } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
12
|
+
import { ComplexMap, MakeOptional } from '@dxos/util';
|
|
13
|
+
|
|
14
|
+
import { OfferMessage, SignalMessage, SignalMessaging } from './signal-messaging';
|
|
15
|
+
|
|
16
|
+
interface OfferRecord {
|
|
17
|
+
resolve: (answer: Answer) => void;
|
|
18
|
+
reject: (error?: Error) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface MessageRouterOptions {
|
|
22
|
+
sendMessage: (params: { author: PublicKey; recipient: PublicKey; payload: Any }) => Promise<void>;
|
|
23
|
+
onOffer: (message: OfferMessage) => Promise<Answer>;
|
|
24
|
+
onSignal: (message: SignalMessage) => Promise<void>;
|
|
25
|
+
topic: PublicKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Adds offer/answer and signal interfaces.
|
|
30
|
+
*/
|
|
31
|
+
export class MessageRouter implements SignalMessaging {
|
|
32
|
+
private readonly _onSignal: (message: SignalMessage) => Promise<void>;
|
|
33
|
+
private readonly _sendMessage: (msg: { author: PublicKey; recipient: PublicKey; payload: Any }) => Promise<void>;
|
|
34
|
+
|
|
35
|
+
private readonly _onOffer: (message: OfferMessage) => Promise<Answer>;
|
|
36
|
+
private readonly _topic: PublicKey;
|
|
37
|
+
|
|
38
|
+
private readonly _offerRecords: ComplexMap<PublicKey, OfferRecord> = new ComplexMap((key) => key.toHex());
|
|
39
|
+
|
|
40
|
+
constructor({ sendMessage, onSignal, onOffer, topic }: MessageRouterOptions) {
|
|
41
|
+
this._sendMessage = sendMessage;
|
|
42
|
+
this._onSignal = onSignal;
|
|
43
|
+
this._onOffer = onOffer;
|
|
44
|
+
this._topic = topic;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async receiveMessage({
|
|
48
|
+
author,
|
|
49
|
+
recipient,
|
|
50
|
+
payload
|
|
51
|
+
}: {
|
|
52
|
+
author: PublicKey;
|
|
53
|
+
recipient: PublicKey;
|
|
54
|
+
payload: Any;
|
|
55
|
+
}): Promise<void> {
|
|
56
|
+
if (payload.type_url !== 'dxos.mesh.swarm.SwarmMessage') {
|
|
57
|
+
// Ignore not swarm messages.
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const message: SwarmMessage = schema.getCodecForType('dxos.mesh.swarm.SwarmMessage').decode(payload.value);
|
|
61
|
+
|
|
62
|
+
if (!this._topic.equals(message.topic)) {
|
|
63
|
+
// Ignore messages from wrong topics.
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
log('received', { from: author, to: recipient, msg: message });
|
|
68
|
+
|
|
69
|
+
if (message.data?.offer) {
|
|
70
|
+
await this._handleOffer({ author, recipient, message });
|
|
71
|
+
} else if (message.data?.answer) {
|
|
72
|
+
await this._resolveAnswers(message);
|
|
73
|
+
} else if (message.data?.signal) {
|
|
74
|
+
await this._handleSignal({ author, recipient, message });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async signal(message: SignalMessage): Promise<void> {
|
|
79
|
+
assert(message.data?.signal);
|
|
80
|
+
await this._sendReliableMessage({
|
|
81
|
+
author: message.author,
|
|
82
|
+
recipient: message.recipient,
|
|
83
|
+
message
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async offer(message: OfferMessage): Promise<Answer> {
|
|
88
|
+
const networkMessage: SwarmMessage = {
|
|
89
|
+
...message,
|
|
90
|
+
messageId: PublicKey.random()
|
|
91
|
+
};
|
|
92
|
+
return new Promise<Answer>((resolve, reject) => {
|
|
93
|
+
this._offerRecords.set(networkMessage.messageId!, { resolve, reject });
|
|
94
|
+
return this._sendReliableMessage({
|
|
95
|
+
author: message.author,
|
|
96
|
+
recipient: message.recipient,
|
|
97
|
+
message: networkMessage
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private async _sendReliableMessage({
|
|
103
|
+
author,
|
|
104
|
+
recipient,
|
|
105
|
+
message
|
|
106
|
+
}: {
|
|
107
|
+
author: PublicKey;
|
|
108
|
+
recipient: PublicKey;
|
|
109
|
+
message: MakeOptional<SwarmMessage, 'messageId'>;
|
|
110
|
+
}): Promise<void> {
|
|
111
|
+
const networkMessage: SwarmMessage = {
|
|
112
|
+
...message,
|
|
113
|
+
// Setting unique message_id if it not specified yet.
|
|
114
|
+
messageId: message.messageId ?? PublicKey.random()
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
log('sending', { from: author, to: recipient, msg: networkMessage });
|
|
118
|
+
await this._encodeAndSend({ author, recipient, message: networkMessage });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private async _encodeAndSend({
|
|
122
|
+
author,
|
|
123
|
+
recipient,
|
|
124
|
+
message
|
|
125
|
+
}: {
|
|
126
|
+
author: PublicKey;
|
|
127
|
+
recipient: PublicKey;
|
|
128
|
+
message: SwarmMessage;
|
|
129
|
+
}) {
|
|
130
|
+
await this._sendMessage({
|
|
131
|
+
author,
|
|
132
|
+
recipient,
|
|
133
|
+
payload: {
|
|
134
|
+
type_url: 'dxos.mesh.swarm.SwarmMessage',
|
|
135
|
+
value: schema.getCodecForType('dxos.mesh.swarm.SwarmMessage').encode(message)
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async _resolveAnswers(message: SwarmMessage): Promise<void> {
|
|
141
|
+
assert(message.data?.answer?.offerMessageId, 'No offerMessageId');
|
|
142
|
+
const offerRecord = this._offerRecords.get(message.data.answer.offerMessageId);
|
|
143
|
+
if (offerRecord) {
|
|
144
|
+
this._offerRecords.delete(message.data.answer.offerMessageId);
|
|
145
|
+
assert(message.data?.answer, 'No answer');
|
|
146
|
+
log('resolving', { answer: message.data.answer });
|
|
147
|
+
offerRecord.resolve(message.data.answer);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private async _handleOffer({
|
|
152
|
+
author,
|
|
153
|
+
recipient,
|
|
154
|
+
message
|
|
155
|
+
}: {
|
|
156
|
+
author: PublicKey;
|
|
157
|
+
recipient: PublicKey;
|
|
158
|
+
message: SwarmMessage;
|
|
159
|
+
}): Promise<void> {
|
|
160
|
+
assert(message.data.offer, 'No offer');
|
|
161
|
+
const offerMessage: OfferMessage = {
|
|
162
|
+
author,
|
|
163
|
+
recipient,
|
|
164
|
+
...message,
|
|
165
|
+
data: { offer: message.data.offer }
|
|
166
|
+
};
|
|
167
|
+
const answer = await this._onOffer(offerMessage);
|
|
168
|
+
answer.offerMessageId = message.messageId;
|
|
169
|
+
await this._sendReliableMessage({
|
|
170
|
+
author: recipient,
|
|
171
|
+
recipient: author,
|
|
172
|
+
message: {
|
|
173
|
+
topic: message.topic,
|
|
174
|
+
sessionId: message.sessionId,
|
|
175
|
+
data: { answer }
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private async _handleSignal({
|
|
181
|
+
author,
|
|
182
|
+
recipient,
|
|
183
|
+
message
|
|
184
|
+
}: {
|
|
185
|
+
author: PublicKey;
|
|
186
|
+
recipient: PublicKey;
|
|
187
|
+
message: SwarmMessage;
|
|
188
|
+
}): Promise<void> {
|
|
189
|
+
assert(message.messageId);
|
|
190
|
+
assert(message.data.signal, 'No Signal');
|
|
191
|
+
const signalMessage: SignalMessage = {
|
|
192
|
+
author,
|
|
193
|
+
recipient,
|
|
194
|
+
...message,
|
|
195
|
+
data: { signal: message.data.signal }
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
await this._onSignal(signalMessage);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { PublicKey } from '@dxos/keys';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Signal peer discovery interface.
|
|
9
|
+
*/
|
|
10
|
+
export interface SignalConnection {
|
|
11
|
+
/**
|
|
12
|
+
* Join topic on signal network, to be discoverable by other peers.
|
|
13
|
+
*/
|
|
14
|
+
join(params: { topic: PublicKey; peerId: PublicKey }): Promise<void>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Leave topic on signal network, to stop being discoverable by other peers.
|
|
18
|
+
*/
|
|
19
|
+
leave(params: { topic: PublicKey; peerId: PublicKey }): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { PublicKey } from '@dxos/keys';
|
|
6
|
+
import { Answer, Offer, Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
7
|
+
|
|
8
|
+
export interface OfferMessage {
|
|
9
|
+
author: PublicKey;
|
|
10
|
+
recipient: PublicKey;
|
|
11
|
+
topic: PublicKey;
|
|
12
|
+
sessionId: PublicKey;
|
|
13
|
+
data: { offer: Offer };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SignalMessage {
|
|
17
|
+
author: PublicKey;
|
|
18
|
+
recipient: PublicKey;
|
|
19
|
+
topic: PublicKey;
|
|
20
|
+
sessionId: PublicKey;
|
|
21
|
+
data: { signal: Signal };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Signal peer messaging interface.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export interface SignalMessaging {
|
|
29
|
+
/**
|
|
30
|
+
* Offer/answer RPC.
|
|
31
|
+
*/
|
|
32
|
+
offer(msg: OfferMessage): Promise<Answer>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Reliably send a signal to a peer.
|
|
36
|
+
*/
|
|
37
|
+
signal(msg: SignalMessage): Promise<void>;
|
|
38
|
+
}
|