@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
|
@@ -6,164 +6,144 @@ import { expect, mockFn } from 'earljs';
|
|
|
6
6
|
import { it as test, describe } from 'mocha';
|
|
7
7
|
import waitForExpect from 'wait-for-expect';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { sleep } from '@dxos/async';
|
|
10
10
|
import { PublicKey } from '@dxos/protocols';
|
|
11
|
-
import { createTestBroker } from '@dxos/signal';
|
|
12
|
-
import {
|
|
11
|
+
import { createTestBroker, TestBroker } from '@dxos/signal';
|
|
12
|
+
import { afterTest } from '@dxos/testutils';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { SignalMessage } from '../proto/gen/dxos/mesh/signalMessage';
|
|
15
15
|
import { SignalClient } from './signal-client';
|
|
16
16
|
|
|
17
|
-
describe('
|
|
18
|
-
let
|
|
19
|
-
let peer1: PublicKey;
|
|
20
|
-
let peer2: PublicKey;
|
|
21
|
-
let api1: SignalClient;
|
|
22
|
-
let api2: SignalClient;
|
|
17
|
+
describe('SignalClient', () => {
|
|
18
|
+
let broker1: TestBroker;
|
|
23
19
|
|
|
24
|
-
let
|
|
25
|
-
const signalApiPort1 = randomInt(10000, 50000);
|
|
26
|
-
const signalApiUrl1 = 'http://0.0.0.0:' + signalApiPort1;
|
|
27
|
-
|
|
28
|
-
// code let broker2: ReturnType<typeof createBroker>;
|
|
29
|
-
const signalApiPort2 = randomInt(10000, 50000);
|
|
30
|
-
const signalApiUrl2 = 'http://0.0.0.0:' + signalApiPort2;
|
|
20
|
+
let broker2: TestBroker;
|
|
31
21
|
|
|
32
22
|
before(async () => {
|
|
33
|
-
broker1 = await createTestBroker(
|
|
34
|
-
// broker2 = await createTestBroker(signalApiPort2);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
topic = PublicKey.random();
|
|
39
|
-
peer1 = PublicKey.random();
|
|
40
|
-
peer2 = PublicKey.random();
|
|
23
|
+
broker1 = await createTestBroker();
|
|
24
|
+
// broker2 = await await createTestBroker(signalApiPort2);
|
|
41
25
|
});
|
|
42
26
|
|
|
43
|
-
after(
|
|
44
|
-
|
|
45
|
-
await api1.close();
|
|
46
|
-
await broker1.stop();
|
|
27
|
+
after(() => {
|
|
28
|
+
broker1.stop();
|
|
47
29
|
// code await broker2.stop();
|
|
48
30
|
});
|
|
49
31
|
|
|
50
32
|
test('message between 2 clients', async () => {
|
|
51
|
-
const
|
|
33
|
+
const topic = PublicKey.random();
|
|
34
|
+
const peer1 = PublicKey.random();
|
|
35
|
+
const peer2 = PublicKey.random();
|
|
36
|
+
const signalMock1 = mockFn<(msg: SignalMessage) => Promise<void>>()
|
|
52
37
|
.resolvesTo();
|
|
53
|
-
api1 = new SignalClient(
|
|
54
|
-
|
|
38
|
+
const api1 = new SignalClient(broker1.url(), signalMock1);
|
|
39
|
+
afterTest(() => api1.close());
|
|
40
|
+
const api2 = new SignalClient(broker1.url(), (async () => {}) as any);
|
|
41
|
+
afterTest(() => api2.close());
|
|
55
42
|
|
|
56
43
|
await api1.join(topic, peer1);
|
|
57
44
|
await api2.join(topic, peer2);
|
|
58
45
|
|
|
59
|
-
const msg:
|
|
46
|
+
const msg: SignalMessage = {
|
|
60
47
|
id: peer2,
|
|
61
48
|
remoteId: peer1,
|
|
62
49
|
sessionId: PublicKey.random(),
|
|
63
50
|
topic,
|
|
64
|
-
data: { signal: { json:
|
|
51
|
+
data: { signal: { json: JSON.stringify({ 'asd': 'asd' }) } }
|
|
65
52
|
};
|
|
66
53
|
await api2.signal(msg);
|
|
67
|
-
|
|
68
54
|
await waitForExpect(() => {
|
|
69
55
|
expect(signalMock1).toHaveBeenCalledWith([msg]);
|
|
70
56
|
}, 4_000);
|
|
71
|
-
}).timeout(
|
|
57
|
+
}).timeout(500);
|
|
72
58
|
|
|
73
59
|
test('join', async () => {
|
|
74
|
-
|
|
60
|
+
const topic = PublicKey.random();
|
|
61
|
+
const peer1 = PublicKey.random();
|
|
62
|
+
const peer2 = PublicKey.random();
|
|
63
|
+
const api1 = new SignalClient(broker1.url(), async () => {});
|
|
64
|
+
afterTest(() => api1.close());
|
|
65
|
+
const api2 = new SignalClient(broker1.url(), async () => {});
|
|
66
|
+
afterTest(() => api2.close());
|
|
75
67
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const join2 = await api1.join(topic, peer2);
|
|
80
|
-
expect(join2).toEqual([peer1, peer2]);
|
|
81
|
-
}).timeout(1_000);
|
|
82
|
-
|
|
83
|
-
test('offer', async () => {
|
|
84
|
-
const offerMock = mockFn<(msg: Message) => Promise<Answer>>()
|
|
85
|
-
.resolvesTo({ accept: true });
|
|
86
|
-
api1 = new SignalClient(signalApiUrl1, offerMock, async () => {});
|
|
68
|
+
const promise1 = api1.swarmEvent.waitFor(([, swarmEvent]) => !!swarmEvent.peerAvailable && peer2.equals(swarmEvent.peerAvailable.peer));
|
|
69
|
+
const promise2 = api2.swarmEvent.waitFor(([, swarmEvent]) => !!swarmEvent.peerAvailable && peer1.equals(swarmEvent.peerAvailable.peer));
|
|
87
70
|
|
|
88
71
|
await api1.join(topic, peer1);
|
|
72
|
+
await api2.join(topic, peer2);
|
|
89
73
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
remoteId: peer1,
|
|
94
|
-
sessionId: PublicKey.random(),
|
|
95
|
-
topic
|
|
96
|
-
};
|
|
97
|
-
const offerResult = await api1.offer(offer);
|
|
98
|
-
expect(offerResult).toEqual({ accept: true });
|
|
99
|
-
expect(offerMock).toHaveBeenCalledWith([offer]);
|
|
100
|
-
}).timeout(5_000);
|
|
74
|
+
await promise1;
|
|
75
|
+
await promise2;
|
|
76
|
+
}).timeout(500);
|
|
101
77
|
|
|
102
|
-
test('signal', async () => {
|
|
103
|
-
const
|
|
78
|
+
test('signal to self', async () => {
|
|
79
|
+
const topic = PublicKey.random();
|
|
80
|
+
const peer1 = PublicKey.random();
|
|
81
|
+
const peer2 = PublicKey.random();
|
|
82
|
+
const signalMock = mockFn<(msg: SignalMessage) => Promise<void>>()
|
|
104
83
|
.resolvesTo();
|
|
105
|
-
api1 = new SignalClient(
|
|
84
|
+
const api1 = new SignalClient(broker1.url(), signalMock);
|
|
85
|
+
afterTest(() => api1.close());
|
|
106
86
|
|
|
107
87
|
await api1.join(topic, peer1);
|
|
108
88
|
|
|
109
|
-
const msg:
|
|
89
|
+
const msg: SignalMessage = {
|
|
110
90
|
id: peer2,
|
|
111
91
|
remoteId: peer1,
|
|
112
92
|
sessionId: PublicKey.random(),
|
|
113
93
|
topic,
|
|
114
|
-
data: { signal: { json: '
|
|
94
|
+
data: { signal: { json: JSON.stringify({ 'asd': 'asd' }) } }
|
|
115
95
|
};
|
|
116
96
|
await api1.signal(msg);
|
|
117
97
|
|
|
118
98
|
await waitForExpect(() => {
|
|
119
99
|
expect(signalMock).toHaveBeenCalledWith([msg]);
|
|
120
100
|
}, 4_000);
|
|
121
|
-
}).timeout(
|
|
101
|
+
}).timeout(500);
|
|
122
102
|
|
|
123
103
|
test.skip('join across multiple signal servers', async () => {
|
|
104
|
+
const topic = PublicKey.random();
|
|
105
|
+
const peer1 = PublicKey.random();
|
|
106
|
+
const peer2 = PublicKey.random();
|
|
124
107
|
// This feature is not implemented yet.
|
|
125
|
-
api1 = new SignalClient(
|
|
126
|
-
|
|
108
|
+
const api1 = new SignalClient(broker1.url(), async () => {});
|
|
109
|
+
afterTest(() => api1.close());
|
|
110
|
+
const api2 = new SignalClient(broker2.url(), async () => {});
|
|
111
|
+
afterTest(() => api2.close());
|
|
127
112
|
|
|
128
113
|
await api1.join(topic, peer1);
|
|
129
114
|
await api2.join(topic, peer2);
|
|
130
115
|
|
|
131
|
-
await waitForExpect(async () => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}, 4_000);
|
|
116
|
+
// await waitForExpect(async () => {
|
|
117
|
+
// const peers = await api2.lookup(topic);
|
|
118
|
+
// expect(peers.length).toEqual(2);
|
|
119
|
+
// }, 4_000);
|
|
135
120
|
|
|
136
|
-
await waitForExpect(async () => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}, 4_000);
|
|
121
|
+
// await waitForExpect(async () => {
|
|
122
|
+
// const peers = await api1.lookup(topic);
|
|
123
|
+
// expect(peers.length).toEqual(2);
|
|
124
|
+
// }, 4_000);
|
|
140
125
|
}).timeout(5_000);
|
|
141
126
|
|
|
142
127
|
// Skip because communication between signal servers is not yet implemented.
|
|
143
128
|
test.skip('newly joined peer can receive signals from other signal servers', async () => {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
const
|
|
129
|
+
const topic = PublicKey.random();
|
|
130
|
+
const peer1 = PublicKey.random();
|
|
131
|
+
const peer2 = PublicKey.random();
|
|
132
|
+
const signalMock = mockFn<(msg: SignalMessage) => Promise<void>>()
|
|
147
133
|
.resolvesTo();
|
|
148
134
|
|
|
149
|
-
api1 = new SignalClient(
|
|
150
|
-
|
|
135
|
+
const api1 = new SignalClient(broker1.url(), async () => {});
|
|
136
|
+
afterTest(() => api1.close());
|
|
137
|
+
const api2 = new SignalClient(broker2.url(), signalMock);
|
|
138
|
+
afterTest(() => api2.close());
|
|
151
139
|
|
|
152
140
|
await api1.join(topic, peer1);
|
|
153
141
|
await sleep(3000);
|
|
154
142
|
await api2.join(topic, peer2);
|
|
155
143
|
|
|
156
144
|
const sessionId = PublicKey.random();
|
|
157
|
-
const answer = await api2.offer({
|
|
158
|
-
remoteId: peer1,
|
|
159
|
-
id: peer2,
|
|
160
|
-
topic,
|
|
161
|
-
sessionId,
|
|
162
|
-
data: { offer: {} }
|
|
163
|
-
});
|
|
164
|
-
expect(answer).toEqual({ accept: true });
|
|
165
145
|
|
|
166
|
-
const msg:
|
|
146
|
+
const msg: SignalMessage = {
|
|
167
147
|
id: peer2,
|
|
168
148
|
remoteId: peer1,
|
|
169
149
|
sessionId,
|
|
@@ -176,4 +156,4 @@ describe('SignalApi', () => {
|
|
|
176
156
|
expect(signalMock).toHaveBeenCalledWith([msg]);
|
|
177
157
|
}, 4_000);
|
|
178
158
|
}).timeout(5_000);
|
|
179
|
-
})
|
|
159
|
+
});
|
|
@@ -2,24 +2,55 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import assert from 'assert';
|
|
5
6
|
import debug from 'debug';
|
|
6
7
|
|
|
7
|
-
import { Event } from '@dxos/async';
|
|
8
|
+
import { Event, synchronized } from '@dxos/async';
|
|
9
|
+
import { Any, Stream } from '@dxos/codec-protobuf';
|
|
8
10
|
import { PublicKey } from '@dxos/protocols';
|
|
11
|
+
import { ComplexMap, SubscriptionGroup } from '@dxos/util';
|
|
9
12
|
|
|
10
|
-
import {
|
|
13
|
+
import { schema } from '../proto/gen';
|
|
14
|
+
import { Message, SwarmEvent } from '../proto/gen/dxos/mesh/signal';
|
|
15
|
+
import { SignalMessage } from '../proto/gen/dxos/mesh/signalMessage';
|
|
11
16
|
import { SignalApi } from './signal-api';
|
|
12
|
-
import {
|
|
17
|
+
import { SignalRPCClient } from './signal-rpc-client';
|
|
13
18
|
|
|
14
19
|
const log = debug('dxos:network-manager:signal-client');
|
|
15
20
|
|
|
16
21
|
const DEFAULT_RECONNECT_TIMEOUT = 1000;
|
|
17
22
|
|
|
23
|
+
enum State {
|
|
24
|
+
/** Connection is being established. */
|
|
25
|
+
CONNECTING = 'CONNECTING',
|
|
26
|
+
|
|
27
|
+
/** Connection is being re-established. */
|
|
28
|
+
RE_CONNECTING = 'RE_CONNECTING',
|
|
29
|
+
|
|
30
|
+
/** Connected. */
|
|
31
|
+
CONNECTED = 'CONNECTED',
|
|
32
|
+
|
|
33
|
+
/** Server terminated the connection. Socket will be reconnected. */
|
|
34
|
+
DISCONNECTED = 'DISCONNECTED',
|
|
35
|
+
|
|
36
|
+
/** Socket was closed. */
|
|
37
|
+
CLOSED = 'CLOSED'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type Status = {
|
|
41
|
+
host: string
|
|
42
|
+
state: State
|
|
43
|
+
error?: string
|
|
44
|
+
reconnectIn: number
|
|
45
|
+
connectionStarted: number
|
|
46
|
+
lastStateChange: number
|
|
47
|
+
}
|
|
48
|
+
|
|
18
49
|
/**
|
|
19
50
|
* Establishes a websocket connection to signal server and provides RPC methods.
|
|
20
51
|
*/
|
|
21
52
|
export class SignalClient {
|
|
22
|
-
private _state =
|
|
53
|
+
private _state = State.CONNECTING;
|
|
23
54
|
|
|
24
55
|
private _lastError?: Error;
|
|
25
56
|
|
|
@@ -40,28 +71,29 @@ export class SignalClient {
|
|
|
40
71
|
|
|
41
72
|
private _reconnectIntervalId?: NodeJS.Timeout;
|
|
42
73
|
|
|
43
|
-
private _client!:
|
|
74
|
+
private _client!: SignalRPCClient;
|
|
44
75
|
|
|
45
|
-
private
|
|
76
|
+
private _cleanupSubscriptions = new SubscriptionGroup();
|
|
77
|
+
readonly statusChanged = new Event<Status>();
|
|
46
78
|
|
|
47
|
-
readonly statusChanged = new Event<SignalApi.Status>();
|
|
48
79
|
readonly commandTrace = new Event<SignalApi.CommandTrace>();
|
|
80
|
+
readonly swarmEvent = new Event<[topic: PublicKey, swarmEvent: SwarmEvent]>();
|
|
49
81
|
|
|
82
|
+
private readonly _swarmStreams = new ComplexMap<PublicKey, Stream<SwarmEvent>>(key => key.toHex());
|
|
83
|
+
private readonly _messageStreams = new ComplexMap<PublicKey, Stream<Message>>(key => key.toHex());
|
|
50
84
|
/**
|
|
51
85
|
* @param _host Signal server websocket URL.
|
|
52
|
-
* @param _onOffer See `SignalApi.offer`.
|
|
53
86
|
* @param _onSignal See `SignalApi.signal`.
|
|
54
87
|
*/
|
|
55
88
|
constructor (
|
|
56
89
|
private readonly _host: string,
|
|
57
|
-
private readonly
|
|
58
|
-
private readonly _onSignal: (message: Message) => Promise<void>
|
|
90
|
+
private readonly _onSignal: (message: SignalMessage) => Promise<void>
|
|
59
91
|
) {
|
|
60
|
-
this._setState(
|
|
92
|
+
this._setState(State.CONNECTING);
|
|
61
93
|
this._createClient();
|
|
62
94
|
}
|
|
63
95
|
|
|
64
|
-
private _setState (newState:
|
|
96
|
+
private _setState (newState: State) {
|
|
65
97
|
this._state = newState;
|
|
66
98
|
this._lastStateChange = Date.now();
|
|
67
99
|
log(`Signal state changed ${JSON.stringify(this.getStatus())}`);
|
|
@@ -71,107 +103,88 @@ export class SignalClient {
|
|
|
71
103
|
private _createClient () {
|
|
72
104
|
this._connectionStarted = Date.now();
|
|
73
105
|
try {
|
|
74
|
-
this._client = new
|
|
106
|
+
this._client = new SignalRPCClient(this._host);
|
|
75
107
|
} catch (error: any) {
|
|
76
|
-
if (this._state ===
|
|
108
|
+
if (this._state === State.RE_CONNECTING) {
|
|
77
109
|
this._reconnectAfter *= 2;
|
|
78
110
|
}
|
|
79
111
|
|
|
80
112
|
this._lastError = error;
|
|
81
|
-
this._setState(
|
|
113
|
+
this._setState(State.DISCONNECTED);
|
|
82
114
|
this._reconnect();
|
|
83
115
|
}
|
|
84
116
|
|
|
85
|
-
this._client.
|
|
86
|
-
id: PublicKey.from(message.id),
|
|
87
|
-
remoteId: PublicKey.from(message.remoteId),
|
|
88
|
-
topic: PublicKey.from(message.topic),
|
|
89
|
-
sessionId: PublicKey.from(message.sessionId),
|
|
90
|
-
data: message.data
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
this._client.subscribe('signal', (msg: Message) => this._onSignal({
|
|
94
|
-
id: PublicKey.from(msg.id!),
|
|
95
|
-
remoteId: PublicKey.from(msg.remoteId!),
|
|
96
|
-
topic: PublicKey.from(msg.topic!),
|
|
97
|
-
sessionId: PublicKey.from(msg.sessionId!),
|
|
98
|
-
data: msg.data
|
|
99
|
-
}));
|
|
100
|
-
|
|
101
|
-
this._clientCleanup.push(this._client.connected.on(() => {
|
|
102
|
-
log('Socket connected');
|
|
117
|
+
this._cleanupSubscriptions.push(this._client.connected.on(() => {
|
|
103
118
|
this._lastError = undefined;
|
|
104
119
|
this._reconnectAfter = DEFAULT_RECONNECT_TIMEOUT;
|
|
105
|
-
this._setState(
|
|
120
|
+
this._setState(State.CONNECTED);
|
|
106
121
|
}));
|
|
107
122
|
|
|
108
|
-
this.
|
|
123
|
+
this._cleanupSubscriptions.push(this._client.error.on(error => {
|
|
109
124
|
log(`Socket error: ${error.message}`);
|
|
110
|
-
if (this._state ===
|
|
125
|
+
if (this._state === State.CLOSED) {
|
|
111
126
|
return;
|
|
112
127
|
}
|
|
113
128
|
|
|
114
|
-
if (this._state ===
|
|
129
|
+
if (this._state === State.RE_CONNECTING) {
|
|
115
130
|
this._reconnectAfter *= 2;
|
|
116
131
|
}
|
|
117
132
|
|
|
118
133
|
this._lastError = error;
|
|
119
|
-
this._setState(
|
|
134
|
+
this._setState(State.DISCONNECTED);
|
|
120
135
|
|
|
121
136
|
this._reconnect();
|
|
122
137
|
}));
|
|
123
138
|
|
|
124
|
-
this.
|
|
139
|
+
this._cleanupSubscriptions.push(this._client.disconnected.on(() => {
|
|
125
140
|
log('Socket disconnected');
|
|
126
141
|
// This is also called in case of error, but we already have disconnected the socket on error, so no need to do anything here.
|
|
127
|
-
if (this._state !==
|
|
142
|
+
if (this._state !== State.CONNECTING && this._state !== State.RE_CONNECTING) {
|
|
128
143
|
return;
|
|
129
144
|
}
|
|
130
145
|
|
|
131
|
-
if (this._state ===
|
|
146
|
+
if (this._state === State.RE_CONNECTING) {
|
|
132
147
|
this._reconnectAfter *= 2;
|
|
133
148
|
}
|
|
134
149
|
|
|
135
|
-
this._setState(
|
|
150
|
+
this._setState(State.DISCONNECTED);
|
|
136
151
|
this._reconnect();
|
|
137
152
|
}));
|
|
138
|
-
|
|
139
|
-
this._clientCleanup.push(this._client.commandTrace.on(trace => this.commandTrace.emit(trace)));
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
private _reconnect () {
|
|
156
|
+
log(`Reconnecting in ${this._reconnectAfter}ms`);
|
|
143
157
|
if (this._reconnectIntervalId !== undefined) {
|
|
144
158
|
console.error('Signal api already reconnecting.');
|
|
145
159
|
return;
|
|
146
160
|
}
|
|
147
|
-
if (this._state ===
|
|
161
|
+
if (this._state === State.CLOSED) {
|
|
148
162
|
return;
|
|
149
163
|
}
|
|
150
164
|
|
|
151
165
|
this._reconnectIntervalId = setTimeout(() => {
|
|
152
166
|
this._reconnectIntervalId = undefined;
|
|
153
167
|
|
|
154
|
-
this.
|
|
155
|
-
this._clientCleanup = [];
|
|
168
|
+
this._cleanupSubscriptions.unsubscribe();
|
|
156
169
|
|
|
157
170
|
// Close client if it wasn't already closed.
|
|
158
171
|
this._client.close().catch(() => {});
|
|
159
172
|
|
|
160
|
-
this._setState(
|
|
173
|
+
this._setState(State.RE_CONNECTING);
|
|
161
174
|
this._createClient();
|
|
162
175
|
}, this._reconnectAfter);
|
|
163
176
|
}
|
|
164
177
|
|
|
165
178
|
async close () {
|
|
166
|
-
this.
|
|
167
|
-
this._clientCleanup = [];
|
|
179
|
+
this._cleanupSubscriptions.unsubscribe();
|
|
168
180
|
|
|
169
181
|
if (this._reconnectIntervalId !== undefined) {
|
|
170
182
|
clearTimeout(this._reconnectIntervalId);
|
|
171
183
|
}
|
|
172
184
|
|
|
173
185
|
await this._client.close();
|
|
174
|
-
this._setState(
|
|
186
|
+
this._setState(State.CLOSED);
|
|
187
|
+
log('Closed.');
|
|
175
188
|
}
|
|
176
189
|
|
|
177
190
|
getStatus (): SignalApi.Status {
|
|
@@ -185,52 +198,72 @@ export class SignalClient {
|
|
|
185
198
|
};
|
|
186
199
|
}
|
|
187
200
|
|
|
188
|
-
async join (topic: PublicKey, peerId: PublicKey): Promise<
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
});
|
|
193
|
-
return peers.map(id => PublicKey.from(id));
|
|
201
|
+
async join (topic: PublicKey, peerId: PublicKey): Promise<void> {
|
|
202
|
+
log(`Join: topic=${topic} peerId=${peerId}`);
|
|
203
|
+
await this._subscribeMessages(peerId);
|
|
204
|
+
await this._subscribeSwarmEvents(topic, peerId);
|
|
194
205
|
}
|
|
195
206
|
|
|
196
207
|
async leave (topic: PublicKey, peerId: PublicKey): Promise<void> {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
208
|
+
log(`Leave: topic=${topic} peerId=${peerId}`);
|
|
209
|
+
|
|
210
|
+
this._swarmStreams.get(topic)?.close();
|
|
211
|
+
this._swarmStreams.delete(topic);
|
|
212
|
+
|
|
213
|
+
this._messageStreams.get(topic)?.close();
|
|
214
|
+
this._messageStreams.delete(topic);
|
|
201
215
|
}
|
|
202
216
|
|
|
203
|
-
async
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
217
|
+
async signal (message: SignalMessage): Promise<void> {
|
|
218
|
+
const payload: Any = {
|
|
219
|
+
type_url: 'dxos.mesh.signalMessage.SignalMessage',
|
|
220
|
+
value: schema.getCodecForType('dxos.mesh.signalMessage.SignalMessage').encode(message)
|
|
221
|
+
};
|
|
222
|
+
return this._client.sendMessage(message.id, message.remoteId, payload);
|
|
208
223
|
}
|
|
209
224
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
225
|
+
@synchronized
|
|
226
|
+
private async _subscribeSwarmEvents (topic: PublicKey, peerId: PublicKey): Promise<void> {
|
|
227
|
+
assert(!this._swarmStreams.has(topic));
|
|
228
|
+
const swarmStream = await this._client.join(topic, peerId);
|
|
229
|
+
// Subscribing to swarm events.
|
|
230
|
+
// TODO(mykola): What happens when the swarm stream is closed? Maybe send leave event for each peer?
|
|
231
|
+
swarmStream.subscribe((swarmEvent: SwarmEvent) => {
|
|
232
|
+
this.swarmEvent.emit([topic, swarmEvent]);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Saving swarm stream.
|
|
236
|
+
this._swarmStreams.set(topic, swarmStream);
|
|
237
|
+
|
|
238
|
+
this._cleanupSubscriptions.push(() => {
|
|
239
|
+
swarmStream.close();
|
|
240
|
+
this._swarmStreams.delete(topic);
|
|
221
241
|
});
|
|
222
242
|
}
|
|
223
243
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
244
|
+
private async _subscribeMessages (peerId: PublicKey) {
|
|
245
|
+
// Subscribing to messages.
|
|
246
|
+
const messageStream = await this._client.receiveMessages(peerId);
|
|
247
|
+
messageStream.subscribe(async (message: Message) => {
|
|
248
|
+
if (message.payload.type_url === 'dxos.mesh.signalMessage.SignalMessage') {
|
|
249
|
+
const signalMessage = schema.getCodecForType('dxos.mesh.signalMessage.SignalMessage').decode(message.payload.value);
|
|
250
|
+
log('Message received: ' + JSON.stringify(signalMessage));
|
|
251
|
+
assert(signalMessage.remoteId.equals(peerId));
|
|
252
|
+
await this._onSignal(signalMessage);
|
|
253
|
+
} else {
|
|
254
|
+
log('Unknown message type: ' + message.payload.type_url);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Saving message stream.
|
|
259
|
+
if (!this._messageStreams.has(peerId)) {
|
|
260
|
+
this._messageStreams.set(peerId, messageStream);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this._cleanupSubscriptions.push(() => {
|
|
264
|
+
messageStream.close();
|
|
265
|
+
this._messageStreams.delete(peerId);
|
|
234
266
|
});
|
|
235
267
|
}
|
|
268
|
+
|
|
236
269
|
}
|