@dxos/network-manager 0.1.3 → 0.1.4
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/swarm/connection.d.ts.map +1 -1
- package/dist/src/swarm/connection.js +14 -10
- package/dist/src/swarm/connection.js.map +1 -1
- package/dist/src/transport/memory-transport.d.ts +6 -13
- package/dist/src/transport/memory-transport.d.ts.map +1 -1
- package/dist/src/transport/memory-transport.js +41 -35
- package/dist/src/transport/memory-transport.js.map +1 -1
- package/dist/src/transport/memory-transport.test.js +14 -3
- package/dist/src/transport/memory-transport.test.js.map +1 -1
- package/dist/src/transport/transport.d.ts +1 -7
- package/dist/src/transport/transport.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport-proxy.d.ts +3 -9
- package/dist/src/transport/webrtc-transport-proxy.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport-proxy.js +12 -17
- package/dist/src/transport/webrtc-transport-proxy.js.map +1 -1
- package/dist/src/transport/webrtc-transport-proxy.test.js +15 -35
- package/dist/src/transport/webrtc-transport-proxy.test.js.map +1 -1
- package/dist/src/transport/webrtc-transport-service.d.ts +1 -4
- package/dist/src/transport/webrtc-transport-service.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport-service.js +32 -36
- package/dist/src/transport/webrtc-transport-service.js.map +1 -1
- package/dist/src/transport/webrtc-transport.d.ts +8 -13
- package/dist/src/transport/webrtc-transport.d.ts.map +1 -1
- package/dist/src/transport/webrtc-transport.js +18 -44
- package/dist/src/transport/webrtc-transport.js.map +1 -1
- package/dist/src/transport/webrtc-transport.test.js +25 -14
- package/dist/src/transport/webrtc-transport.test.js.map +1 -1
- package/package.json +16 -16
- package/src/swarm/connection.ts +9 -5
- package/src/transport/memory-transport.test.ts +14 -15
- package/src/transport/memory-transport.ts +48 -49
- package/src/transport/transport.ts +1 -9
- package/src/transport/webrtc-transport-proxy.test.ts +17 -46
- package/src/transport/webrtc-transport-proxy.ts +13 -23
- package/src/transport/webrtc-transport-service.ts +34 -39
- package/src/transport/webrtc-transport.test.ts +17 -30
- package/src/transport/webrtc-transport.ts +22 -54
|
@@ -5,16 +5,14 @@
|
|
|
5
5
|
import assert from 'node:assert';
|
|
6
6
|
import { Transform } from 'stream';
|
|
7
7
|
|
|
8
|
-
import { Event } from '@dxos/async';
|
|
8
|
+
import { Event, Trigger } from '@dxos/async';
|
|
9
9
|
import { ErrorStream } from '@dxos/debug';
|
|
10
10
|
import { PublicKey } from '@dxos/keys';
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
12
|
import { Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
13
13
|
import { ComplexMap } from '@dxos/util';
|
|
14
14
|
|
|
15
|
-
import { Transport, TransportFactory } from './transport';
|
|
16
|
-
|
|
17
|
-
type ConnectionKey = [topic: PublicKey, nodeId: PublicKey, remoteId: PublicKey];
|
|
15
|
+
import { Transport, TransportFactory, TransportOptions } from './transport';
|
|
18
16
|
|
|
19
17
|
// Delay (in milliseconds) for data being sent through in-memory connections to simulate network latency.
|
|
20
18
|
const MEMORY_TRANSPORT_DELAY = 1;
|
|
@@ -36,71 +34,72 @@ const createStreamDelay = (delay: number): NodeJS.ReadWriteStream =>
|
|
|
36
34
|
// TODO(burdon): Remove static variables.
|
|
37
35
|
export class MemoryTransport implements Transport {
|
|
38
36
|
// TODO(burdon): Remove global properties.
|
|
39
|
-
private static readonly _connections = new ComplexMap<
|
|
40
|
-
([topic, nodeId, remoteId]) => topic.toHex() + nodeId.toHex() + remoteId.toHex()
|
|
41
|
-
);
|
|
37
|
+
private static readonly _connections = new ComplexMap<PublicKey, MemoryTransport>(PublicKey.hash);
|
|
42
38
|
|
|
43
39
|
public readonly closed = new Event<void>();
|
|
44
40
|
public readonly connected = new Event<void>();
|
|
45
41
|
public readonly errors = new ErrorStream();
|
|
46
42
|
|
|
47
|
-
private readonly
|
|
48
|
-
private
|
|
43
|
+
private readonly _ownId = PublicKey.random();
|
|
44
|
+
private _remoteId!: PublicKey;
|
|
45
|
+
private readonly _remote = new Trigger<PublicKey>();
|
|
49
46
|
|
|
50
47
|
private readonly _outgoingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
|
|
51
48
|
private readonly _incomingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
|
|
52
49
|
|
|
53
50
|
private _remoteConnection?: MemoryTransport;
|
|
54
51
|
|
|
55
|
-
constructor(
|
|
56
|
-
|
|
57
|
-
private readonly _remoteId: PublicKey,
|
|
58
|
-
private readonly _sessionId: PublicKey,
|
|
59
|
-
private readonly _topic: PublicKey,
|
|
60
|
-
private readonly _stream: NodeJS.ReadWriteStream
|
|
61
|
-
) {
|
|
62
|
-
log('creating', { topic: this._topic, peerId: this._ownId, remoteId: this._remoteId });
|
|
63
|
-
|
|
64
|
-
this._ownKey = [this._topic, this._ownId, this._remoteId];
|
|
65
|
-
this._remoteKey = [this._topic, this._remoteId, this._ownId];
|
|
66
|
-
|
|
67
|
-
assert(!MemoryTransport._connections.has(this._ownKey), 'Duplicate memory connection');
|
|
68
|
-
MemoryTransport._connections.set(this._ownKey, this);
|
|
69
|
-
|
|
70
|
-
this._remoteConnection = MemoryTransport._connections.get(this._remoteKey);
|
|
71
|
-
if (this._remoteConnection) {
|
|
72
|
-
this._remoteConnection._remoteConnection = this;
|
|
73
|
-
|
|
74
|
-
log('connected', { topic: this._topic, peerId: this._ownId, remoteId: this._remoteId });
|
|
75
|
-
this._stream
|
|
76
|
-
.pipe(this._outgoingDelay)
|
|
77
|
-
.pipe(this._remoteConnection._stream)
|
|
78
|
-
.pipe(this._incomingDelay)
|
|
79
|
-
.pipe(this._stream);
|
|
52
|
+
constructor(private readonly params: TransportOptions) {
|
|
53
|
+
log('creating', this._ownId);
|
|
80
54
|
|
|
81
|
-
|
|
82
|
-
this.
|
|
55
|
+
if (this.params.initiator) {
|
|
56
|
+
setTimeout(async () => this.params.sendSignal({ json: JSON.stringify({ transportId: this._ownId.toHex() }) }));
|
|
83
57
|
}
|
|
84
|
-
}
|
|
85
58
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
59
|
+
assert(!MemoryTransport._connections.has(this._ownId), 'Duplicate memory connection');
|
|
60
|
+
MemoryTransport._connections.set(this._ownId, this);
|
|
61
|
+
|
|
62
|
+
this._remote.wait().then(
|
|
63
|
+
(remoteId) => {
|
|
64
|
+
this._remoteId = remoteId;
|
|
65
|
+
this._remoteConnection = MemoryTransport._connections.get(this._remoteId);
|
|
66
|
+
if (this._remoteConnection) {
|
|
67
|
+
this._remoteConnection._remoteConnection = this;
|
|
68
|
+
this._remoteConnection._remoteId = this._ownId;
|
|
69
|
+
|
|
70
|
+
log('connected', { ownId: this._ownId, remoteId: this._remoteId });
|
|
71
|
+
this.params.stream
|
|
72
|
+
.pipe(this._outgoingDelay)
|
|
73
|
+
.pipe(this._remoteConnection.params.stream)
|
|
74
|
+
.pipe(this._incomingDelay)
|
|
75
|
+
.pipe(this.params.stream);
|
|
76
|
+
|
|
77
|
+
this.connected.emit();
|
|
78
|
+
this._remoteConnection.connected.emit();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
async (err) => {
|
|
82
|
+
log.catch(err);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
92
85
|
}
|
|
93
86
|
|
|
94
87
|
async signal(signal: Signal) {
|
|
95
|
-
|
|
88
|
+
const { json } = signal;
|
|
89
|
+
if (json) {
|
|
90
|
+
const { transportId } = JSON.parse(json);
|
|
91
|
+
if (transportId) {
|
|
92
|
+
this._remote.wake(PublicKey.from(transportId));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
async close(): Promise<void> {
|
|
99
|
-
log('closing',
|
|
98
|
+
log('closing', this._ownId);
|
|
100
99
|
|
|
101
|
-
MemoryTransport._connections.delete(this.
|
|
100
|
+
MemoryTransport._connections.delete(this._ownId);
|
|
102
101
|
if (this._remoteConnection) {
|
|
103
|
-
MemoryTransport._connections.delete(this.
|
|
102
|
+
MemoryTransport._connections.delete(this._remoteId);
|
|
104
103
|
|
|
105
104
|
// TODO(dmaretskyi): Hypercore streams do not seem to have the unpipe method.
|
|
106
105
|
// NOTE(burdon): Using readable-stream.wrap() might help (see feed-store).
|
|
@@ -119,10 +118,10 @@ export class MemoryTransport implements Transport {
|
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
this.closed.emit();
|
|
122
|
-
log('closed'
|
|
121
|
+
log('closed');
|
|
123
122
|
}
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
export const MemoryTransportFactory: TransportFactory = {
|
|
127
|
-
create: (
|
|
126
|
+
create: (params) => new MemoryTransport(params)
|
|
128
127
|
};
|
|
@@ -4,11 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { Event } from '@dxos/async';
|
|
6
6
|
import { ErrorStream } from '@dxos/debug';
|
|
7
|
-
import { PublicKey } from '@dxos/keys';
|
|
8
7
|
import { Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
9
8
|
|
|
10
|
-
import { SignalMessage } from '../signal';
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* Abstraction over a P2P connection transport. Currently either WebRTC or in-memory.
|
|
14
11
|
*/
|
|
@@ -26,11 +23,6 @@ export type TransportOptions = {
|
|
|
26
23
|
*/
|
|
27
24
|
initiator: boolean;
|
|
28
25
|
|
|
29
|
-
ownId: PublicKey;
|
|
30
|
-
remoteId: PublicKey;
|
|
31
|
-
sessionId: PublicKey;
|
|
32
|
-
topic: PublicKey;
|
|
33
|
-
|
|
34
26
|
/**
|
|
35
27
|
* Wire protocol.
|
|
36
28
|
*/
|
|
@@ -39,7 +31,7 @@ export type TransportOptions = {
|
|
|
39
31
|
/**
|
|
40
32
|
* Send a signal message to remote peer.
|
|
41
33
|
*/
|
|
42
|
-
sendSignal: (
|
|
34
|
+
sendSignal: (signal: Signal) => Promise<void>; // TODO(burdon): Remove async?
|
|
43
35
|
};
|
|
44
36
|
|
|
45
37
|
export interface TransportFactory {
|
|
@@ -14,10 +14,10 @@ import { PublicKey } from '@dxos/keys';
|
|
|
14
14
|
import { Protocol } from '@dxos/mesh-protocol';
|
|
15
15
|
import { schema } from '@dxos/protocols';
|
|
16
16
|
import { BridgeService } from '@dxos/protocols/proto/dxos/mesh/bridge';
|
|
17
|
+
import { Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
17
18
|
import { createLinkedPorts, createProtoRpcPeer, ProtoRpcPeer } from '@dxos/rpc';
|
|
18
19
|
import { afterTest } from '@dxos/testutils';
|
|
19
20
|
|
|
20
|
-
import { SignalMessage } from '../signal';
|
|
21
21
|
import { TestProtocolPlugin, testProtocolProvider } from '../testing';
|
|
22
22
|
import { WebRTCTransportProxy } from './webrtc-transport-proxy';
|
|
23
23
|
import { WebRTCTransportService } from './webrtc-transport-service';
|
|
@@ -25,27 +25,21 @@ import { WebRTCTransportService } from './webrtc-transport-service';
|
|
|
25
25
|
describe('WebRTCTransportProxy', function () {
|
|
26
26
|
const setupProxy = async ({
|
|
27
27
|
initiator = true,
|
|
28
|
-
ownId = PublicKey.random(),
|
|
29
|
-
remoteId = PublicKey.random(),
|
|
30
|
-
sessionId = PublicKey.random(),
|
|
31
|
-
topic = PublicKey.random(),
|
|
32
28
|
stream = new Duplex({ write: () => {}, read: () => {} }),
|
|
33
|
-
sendSignal = () => {}
|
|
29
|
+
sendSignal = async () => {}
|
|
34
30
|
}: {
|
|
35
31
|
initiator?: boolean;
|
|
36
|
-
ownId?: PublicKey;
|
|
37
|
-
remoteId?: PublicKey;
|
|
38
|
-
sessionId?: PublicKey;
|
|
39
|
-
topic?: PublicKey;
|
|
40
32
|
stream?: NodeJS.ReadWriteStream;
|
|
41
|
-
sendSignal?: (msg:
|
|
33
|
+
sendSignal?: (msg: Signal) => Promise<void>;
|
|
42
34
|
} = {}) => {
|
|
43
35
|
const [port1, port2] = createLinkedPorts();
|
|
44
36
|
|
|
37
|
+
// Starting BridgeService
|
|
45
38
|
const webRTCTransportService: BridgeService = new WebRTCTransportService();
|
|
46
39
|
|
|
47
40
|
// Starting BridgeService
|
|
48
|
-
const
|
|
41
|
+
const rpcService = createProtoRpcPeer({
|
|
42
|
+
requested: {},
|
|
49
43
|
exposed: {
|
|
50
44
|
BridgeService: schema.getService('dxos.mesh.bridge.BridgeService')
|
|
51
45
|
},
|
|
@@ -56,8 +50,8 @@ describe('WebRTCTransportProxy', function () {
|
|
|
56
50
|
preserveAny: true
|
|
57
51
|
}
|
|
58
52
|
});
|
|
59
|
-
await
|
|
60
|
-
afterTest(() =>
|
|
53
|
+
await rpcService.open();
|
|
54
|
+
afterTest(() => rpcService.close());
|
|
61
55
|
|
|
62
56
|
// Starting RPC client
|
|
63
57
|
const rpcClient = createProtoRpcPeer({
|
|
@@ -76,16 +70,12 @@ describe('WebRTCTransportProxy', function () {
|
|
|
76
70
|
const webRTCTransportProxy = new WebRTCTransportProxy({
|
|
77
71
|
initiator,
|
|
78
72
|
stream,
|
|
79
|
-
ownId,
|
|
80
|
-
remoteId,
|
|
81
|
-
sessionId,
|
|
82
|
-
topic,
|
|
83
73
|
sendSignal,
|
|
84
74
|
bridgeService: rpcClient.rpc.BridgeService
|
|
85
75
|
});
|
|
86
76
|
afterTest(async () => await webRTCTransportProxy.close());
|
|
87
77
|
|
|
88
|
-
return { webRTCService, webRTCTransportProxy };
|
|
78
|
+
return { webRTCService: rpcService, webRTCTransportProxy };
|
|
89
79
|
};
|
|
90
80
|
|
|
91
81
|
// This doesn't clean up correctly and crashes with SIGSEGV / SIGABRT at the end. Probably an issue with wrtc package.
|
|
@@ -116,7 +106,6 @@ describe('WebRTCTransportProxy', function () {
|
|
|
116
106
|
const topic = PublicKey.random();
|
|
117
107
|
const peer1Id = PublicKey.random();
|
|
118
108
|
const peer2Id = PublicKey.random();
|
|
119
|
-
const sessionId = PublicKey.random();
|
|
120
109
|
|
|
121
110
|
const plugin1 = new TestProtocolPlugin(peer1Id.asBuffer());
|
|
122
111
|
const protocolProvider1 = testProtocolProvider(topic.asBuffer(), peer1Id.asBuffer(), plugin1);
|
|
@@ -126,13 +115,9 @@ describe('WebRTCTransportProxy', function () {
|
|
|
126
115
|
channel: discoveryKey(topic),
|
|
127
116
|
initiator: true
|
|
128
117
|
}).stream,
|
|
129
|
-
|
|
130
|
-
remoteId: peer2Id,
|
|
131
|
-
sessionId,
|
|
132
|
-
topic,
|
|
133
|
-
sendSignal: async (msg) => {
|
|
118
|
+
sendSignal: async (signal) => {
|
|
134
119
|
await sleep(10);
|
|
135
|
-
await connection2.signal(
|
|
120
|
+
await connection2.signal(signal);
|
|
136
121
|
}
|
|
137
122
|
});
|
|
138
123
|
afterTest(() => connection1.errors.assertNoUnhandledErrors());
|
|
@@ -145,13 +130,9 @@ describe('WebRTCTransportProxy', function () {
|
|
|
145
130
|
channel: discoveryKey(topic),
|
|
146
131
|
initiator: false
|
|
147
132
|
}).stream,
|
|
148
|
-
|
|
149
|
-
remoteId: peer1Id,
|
|
150
|
-
sessionId,
|
|
151
|
-
topic,
|
|
152
|
-
sendSignal: async (msg) => {
|
|
133
|
+
sendSignal: async (signal) => {
|
|
153
134
|
await sleep(10);
|
|
154
|
-
await connection1.signal(
|
|
135
|
+
await connection1.signal(signal);
|
|
155
136
|
}
|
|
156
137
|
});
|
|
157
138
|
afterTest(() => connection2.errors.assertNoUnhandledErrors());
|
|
@@ -166,7 +147,6 @@ describe('WebRTCTransportProxy', function () {
|
|
|
166
147
|
plugin2.on('connect', async (protocol) => {
|
|
167
148
|
await plugin2.send(peer1Id.asBuffer(), '{"message": "Hello"}');
|
|
168
149
|
});
|
|
169
|
-
|
|
170
150
|
await waitForExpect(() => {
|
|
171
151
|
expect(received.length).toBe(2);
|
|
172
152
|
expect(received[0]).toBeInstanceOf(Protocol);
|
|
@@ -219,7 +199,6 @@ describe('WebRTCTransportProxy', function () {
|
|
|
219
199
|
const topic = PublicKey.random();
|
|
220
200
|
const peer1Id = PublicKey.random();
|
|
221
201
|
const peer2Id = PublicKey.random();
|
|
222
|
-
const sessionId = PublicKey.random();
|
|
223
202
|
|
|
224
203
|
const plugin1 = new TestProtocolPlugin(peer1Id.asBuffer());
|
|
225
204
|
const protocolProvider1 = testProtocolProvider(topic.asBuffer(), peer1Id.asBuffer(), plugin1);
|
|
@@ -229,13 +208,9 @@ describe('WebRTCTransportProxy', function () {
|
|
|
229
208
|
channel: discoveryKey(topic),
|
|
230
209
|
initiator: true
|
|
231
210
|
}).stream,
|
|
232
|
-
|
|
233
|
-
remoteId: peer2Id,
|
|
234
|
-
sessionId,
|
|
235
|
-
topic,
|
|
236
|
-
sendSignal: async (msg) => {
|
|
211
|
+
sendSignal: async (signal) => {
|
|
237
212
|
await sleep(10);
|
|
238
|
-
await proxy2.signal(
|
|
213
|
+
await proxy2.signal(signal);
|
|
239
214
|
},
|
|
240
215
|
bridgeService: rpcClient.rpc.BridgeService
|
|
241
216
|
});
|
|
@@ -249,13 +224,9 @@ describe('WebRTCTransportProxy', function () {
|
|
|
249
224
|
channel: discoveryKey(topic),
|
|
250
225
|
initiator: false
|
|
251
226
|
}).stream,
|
|
252
|
-
|
|
253
|
-
remoteId: peer1Id,
|
|
254
|
-
sessionId,
|
|
255
|
-
topic,
|
|
256
|
-
sendSignal: async (msg) => {
|
|
227
|
+
sendSignal: async (signal) => {
|
|
257
228
|
await sleep(10);
|
|
258
|
-
await proxy1.signal(
|
|
229
|
+
await proxy1.signal(signal);
|
|
259
230
|
},
|
|
260
231
|
bridgeService: rpcClient.rpc.BridgeService
|
|
261
232
|
});
|
|
@@ -12,24 +12,20 @@ import { log } from '@dxos/log';
|
|
|
12
12
|
import { ConnectionState, BridgeEvent, BridgeService } from '@dxos/protocols/proto/dxos/mesh/bridge';
|
|
13
13
|
import { Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
14
14
|
|
|
15
|
-
import { SignalMessage } from '../signal';
|
|
16
15
|
import { Transport, TransportFactory, TransportOptions } from './transport';
|
|
17
16
|
|
|
18
|
-
export
|
|
17
|
+
export type WebRTCTransportProxyParams = {
|
|
19
18
|
initiator: boolean;
|
|
20
19
|
stream: NodeJS.ReadWriteStream;
|
|
21
|
-
ownId: PublicKey;
|
|
22
|
-
remoteId: PublicKey;
|
|
23
|
-
sessionId: PublicKey;
|
|
24
|
-
topic: PublicKey;
|
|
25
|
-
sendSignal: (msg: SignalMessage) => void;
|
|
26
20
|
bridgeService: BridgeService;
|
|
27
|
-
|
|
21
|
+
sendSignal: (signal: Signal) => Promise<void>;
|
|
22
|
+
};
|
|
28
23
|
|
|
29
24
|
export class WebRTCTransportProxy implements Transport {
|
|
30
25
|
readonly closed = new Event();
|
|
31
26
|
private _closed = false;
|
|
32
27
|
readonly connected = new Event();
|
|
28
|
+
|
|
33
29
|
readonly errors = new ErrorStream();
|
|
34
30
|
|
|
35
31
|
private _serviceStream!: Stream<BridgeEvent>;
|
|
@@ -43,16 +39,16 @@ export class WebRTCTransportProxy implements Transport {
|
|
|
43
39
|
proxyId: this._proxyId,
|
|
44
40
|
initiator: this._params.initiator
|
|
45
41
|
});
|
|
46
|
-
|
|
47
42
|
this._serviceStream.waitUntilReady().then(
|
|
48
43
|
() => {
|
|
49
|
-
this._serviceStream.subscribe(async (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
this._serviceStream.subscribe(async (event: BridgeEvent) => {
|
|
45
|
+
log('WebRTCTransportProxy: event', event);
|
|
46
|
+
if (event.connection) {
|
|
47
|
+
await this._handleConnection(event.connection);
|
|
48
|
+
} else if (event.data) {
|
|
49
|
+
this._handleData(event.data);
|
|
50
|
+
} else if (event.signal) {
|
|
51
|
+
await this._handleSignal(event.signal);
|
|
56
52
|
}
|
|
57
53
|
});
|
|
58
54
|
|
|
@@ -94,13 +90,7 @@ export class WebRTCTransportProxy implements Transport {
|
|
|
94
90
|
}
|
|
95
91
|
|
|
96
92
|
private async _handleSignal(signalEvent: BridgeEvent.SignalEvent) {
|
|
97
|
-
await this._params.sendSignal(
|
|
98
|
-
author: this._params.ownId,
|
|
99
|
-
recipient: this._params.remoteId,
|
|
100
|
-
topic: this._params.topic,
|
|
101
|
-
sessionId: this._params.sessionId,
|
|
102
|
-
data: { signal: signalEvent.payload }
|
|
103
|
-
});
|
|
93
|
+
await this._params.sendSignal(signalEvent.payload);
|
|
104
94
|
}
|
|
105
95
|
|
|
106
96
|
async signal(signal: Signal): Promise<void> {
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import assert from 'assert';
|
|
6
|
-
import
|
|
6
|
+
import { Duplex } from 'stream';
|
|
7
7
|
|
|
8
8
|
import { Stream } from '@dxos/codec-protobuf';
|
|
9
|
-
import { raise } from '@dxos/debug';
|
|
10
9
|
import { PublicKey } from '@dxos/keys';
|
|
11
10
|
import { log } from '@dxos/log';
|
|
12
11
|
import {
|
|
@@ -20,10 +19,12 @@ import {
|
|
|
20
19
|
} from '@dxos/protocols/proto/dxos/mesh/bridge';
|
|
21
20
|
import { ComplexMap } from '@dxos/util';
|
|
22
21
|
|
|
23
|
-
import {
|
|
22
|
+
import { WebRTCTransport } from './webrtc-transport';
|
|
24
23
|
|
|
25
24
|
export class WebRTCTransportService implements BridgeService {
|
|
26
|
-
|
|
25
|
+
private readonly transports = new ComplexMap<PublicKey, { transport: WebRTCTransport; stream: Duplex }>(
|
|
26
|
+
PublicKey.hash
|
|
27
|
+
);
|
|
27
28
|
|
|
28
29
|
// prettier-ignore
|
|
29
30
|
constructor(
|
|
@@ -31,14 +32,23 @@ export class WebRTCTransportService implements BridgeService {
|
|
|
31
32
|
) {}
|
|
32
33
|
|
|
33
34
|
open(request: ConnectionRequest): Stream<BridgeEvent> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const rpcStream: Stream<BridgeEvent> = new Stream(({ ready, next, close }) => {
|
|
36
|
+
const duplex: Duplex = new Duplex({
|
|
37
|
+
read: () => {},
|
|
38
|
+
write: function (chunk, _, callback) {
|
|
39
|
+
next({ data: { payload: chunk } });
|
|
40
|
+
callback();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const transport = new WebRTCTransport({
|
|
39
45
|
initiator: request.initiator,
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
stream: duplex,
|
|
47
|
+
sendSignal: async (signal) => {
|
|
48
|
+
next({
|
|
49
|
+
signal: { payload: signal }
|
|
50
|
+
});
|
|
51
|
+
}
|
|
42
52
|
});
|
|
43
53
|
|
|
44
54
|
next({
|
|
@@ -47,23 +57,7 @@ export class WebRTCTransportService implements BridgeService {
|
|
|
47
57
|
}
|
|
48
58
|
});
|
|
49
59
|
|
|
50
|
-
|
|
51
|
-
next({
|
|
52
|
-
data: {
|
|
53
|
-
payload
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
peer.on('signal', async (data) => {
|
|
59
|
-
next({
|
|
60
|
-
signal: {
|
|
61
|
-
payload: { json: JSON.stringify(data) }
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
peer.on('connect', () => {
|
|
60
|
+
transport.connected.on(() => {
|
|
67
61
|
next({
|
|
68
62
|
connection: {
|
|
69
63
|
state: ConnectionState.CONNECTED
|
|
@@ -71,7 +65,7 @@ export class WebRTCTransportService implements BridgeService {
|
|
|
71
65
|
});
|
|
72
66
|
});
|
|
73
67
|
|
|
74
|
-
|
|
68
|
+
transport.errors.handle((err) => {
|
|
75
69
|
next({
|
|
76
70
|
connection: {
|
|
77
71
|
state: ConnectionState.CLOSED,
|
|
@@ -81,7 +75,7 @@ export class WebRTCTransportService implements BridgeService {
|
|
|
81
75
|
close(err);
|
|
82
76
|
});
|
|
83
77
|
|
|
84
|
-
|
|
78
|
+
transport.closed.on(() => {
|
|
85
79
|
next({
|
|
86
80
|
connection: {
|
|
87
81
|
state: ConnectionState.CLOSED
|
|
@@ -90,26 +84,27 @@ export class WebRTCTransportService implements BridgeService {
|
|
|
90
84
|
close();
|
|
91
85
|
});
|
|
92
86
|
|
|
93
|
-
this.peers.set(request.proxyId, peer);
|
|
94
|
-
|
|
95
87
|
ready();
|
|
88
|
+
this.transports.set(request.proxyId, { transport, stream: duplex });
|
|
96
89
|
});
|
|
90
|
+
|
|
91
|
+
return rpcStream;
|
|
97
92
|
}
|
|
98
93
|
|
|
99
94
|
async sendSignal({ proxyId, signal }: SignalRequest): Promise<void> {
|
|
100
|
-
assert(this.
|
|
101
|
-
|
|
102
|
-
this.peers.get(proxyId)!.signal(JSON.parse(signal.json));
|
|
95
|
+
assert(this.transports.has(proxyId));
|
|
96
|
+
await this.transports.get(proxyId)!.transport.signal(signal);
|
|
103
97
|
}
|
|
104
98
|
|
|
105
99
|
async sendData({ proxyId, payload }: DataRequest): Promise<void> {
|
|
106
|
-
assert(this.
|
|
107
|
-
this.
|
|
100
|
+
assert(this.transports.has(proxyId));
|
|
101
|
+
await this.transports.get(proxyId)!.stream.push(payload);
|
|
108
102
|
}
|
|
109
103
|
|
|
110
104
|
async close({ proxyId }: CloseRequest) {
|
|
111
|
-
this.
|
|
112
|
-
this.
|
|
105
|
+
await this.transports.get(proxyId)?.transport.close();
|
|
106
|
+
await this.transports.get(proxyId)?.stream.end();
|
|
107
|
+
this.transports.delete(proxyId);
|
|
113
108
|
log('Closed.');
|
|
114
109
|
}
|
|
115
110
|
}
|
|
@@ -18,15 +18,11 @@ import { WebRTCTransport } from './webrtc-transport';
|
|
|
18
18
|
describe('WebRTCTransport', function () {
|
|
19
19
|
// This doesn't clean up correctly and crashes with SIGSEGV / SIGABRT at the end. Probably an issue with wrtc package.
|
|
20
20
|
it('open and close', async function () {
|
|
21
|
-
const connection = new WebRTCTransport(
|
|
22
|
-
true,
|
|
23
|
-
new Duplex(),
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
PublicKey.random(),
|
|
27
|
-
PublicKey.random(),
|
|
28
|
-
async (msg) => {}
|
|
29
|
-
);
|
|
21
|
+
const connection = new WebRTCTransport({
|
|
22
|
+
initiator: true,
|
|
23
|
+
stream: new Duplex(),
|
|
24
|
+
sendSignal: async () => {}
|
|
25
|
+
});
|
|
30
26
|
|
|
31
27
|
let callsCounter = 0;
|
|
32
28
|
const closedCb = () => {
|
|
@@ -48,45 +44,36 @@ describe('WebRTCTransport', function () {
|
|
|
48
44
|
const topic = PublicKey.random();
|
|
49
45
|
const peer1Id = PublicKey.random();
|
|
50
46
|
const peer2Id = PublicKey.random();
|
|
51
|
-
const sessionId = PublicKey.random();
|
|
52
47
|
|
|
53
48
|
const plugin1 = new TestProtocolPlugin(peer1Id.asBuffer());
|
|
54
49
|
const protocolProvider1 = testProtocolProvider(topic.asBuffer(), peer1Id.asBuffer(), plugin1);
|
|
55
|
-
const connection1 = new WebRTCTransport(
|
|
56
|
-
true,
|
|
57
|
-
protocolProvider1({
|
|
50
|
+
const connection1 = new WebRTCTransport({
|
|
51
|
+
initiator: true,
|
|
52
|
+
stream: protocolProvider1({
|
|
58
53
|
channel: discoveryKey(topic),
|
|
59
54
|
initiator: true
|
|
60
55
|
}).stream,
|
|
61
|
-
|
|
62
|
-
peer2Id,
|
|
63
|
-
sessionId,
|
|
64
|
-
topic,
|
|
65
|
-
async (msg) => {
|
|
56
|
+
sendSignal: async (signal) => {
|
|
66
57
|
await sleep(10);
|
|
67
|
-
await connection2.signal(
|
|
58
|
+
await connection2.signal(signal);
|
|
68
59
|
}
|
|
69
|
-
);
|
|
60
|
+
});
|
|
70
61
|
afterTest(() => connection1.close());
|
|
71
62
|
afterTest(() => connection1.errors.assertNoUnhandledErrors());
|
|
72
63
|
|
|
73
64
|
const plugin2 = new TestProtocolPlugin(peer2Id.asBuffer());
|
|
74
65
|
const protocolProvider2 = testProtocolProvider(topic.asBuffer(), peer2Id.asBuffer(), plugin2);
|
|
75
|
-
const connection2 = new WebRTCTransport(
|
|
76
|
-
false,
|
|
77
|
-
protocolProvider2({
|
|
66
|
+
const connection2 = new WebRTCTransport({
|
|
67
|
+
initiator: false,
|
|
68
|
+
stream: protocolProvider2({
|
|
78
69
|
channel: discoveryKey(topic),
|
|
79
70
|
initiator: false
|
|
80
71
|
}).stream,
|
|
81
|
-
|
|
82
|
-
peer1Id,
|
|
83
|
-
sessionId,
|
|
84
|
-
topic,
|
|
85
|
-
async (msg) => {
|
|
72
|
+
sendSignal: async (signal) => {
|
|
86
73
|
await sleep(10);
|
|
87
|
-
await connection1.signal(
|
|
74
|
+
await connection1.signal(signal);
|
|
88
75
|
}
|
|
89
|
-
);
|
|
76
|
+
});
|
|
90
77
|
afterTest(() => connection2.close());
|
|
91
78
|
afterTest(() => connection2.errors.assertNoUnhandledErrors());
|
|
92
79
|
|