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