@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,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
|
|
7
|
+
import { PublicKey } from '@dxos/keys';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
|
|
10
|
+
import { SwarmController, Topology } from './topology';
|
|
11
|
+
|
|
12
|
+
export class StarTopology implements Topology {
|
|
13
|
+
private _controller?: SwarmController;
|
|
14
|
+
|
|
15
|
+
// prettier-ignore
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly _centralPeer: PublicKey
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
toString() {
|
|
21
|
+
return `StarTopology(${this._centralPeer.truncate()})`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
init(controller: SwarmController): void {
|
|
25
|
+
assert(!this._controller, 'Already initialized.');
|
|
26
|
+
this._controller = controller;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
update(): void {
|
|
30
|
+
assert(this._controller, 'Not initialized.');
|
|
31
|
+
const { candidates, connected, ownPeerId } = this._controller.getState();
|
|
32
|
+
if (!ownPeerId.equals(this._centralPeer)) {
|
|
33
|
+
log('leaf peer dropping all connections apart from central peer.');
|
|
34
|
+
|
|
35
|
+
// Drop all connections other than central peer.
|
|
36
|
+
for (const peer of connected) {
|
|
37
|
+
if (!peer.equals(this._centralPeer)) {
|
|
38
|
+
log('dropping connection', { peer });
|
|
39
|
+
this._controller.disconnect(peer);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const peer of candidates) {
|
|
45
|
+
// Connect to central peer.
|
|
46
|
+
if (peer.equals(this._centralPeer) || ownPeerId.equals(this._centralPeer)) {
|
|
47
|
+
log('connecting to peer', { peer });
|
|
48
|
+
this._controller.connect(peer);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async onOffer(peer: PublicKey): Promise<boolean> {
|
|
54
|
+
assert(this._controller, 'Not initialized.');
|
|
55
|
+
const { ownPeerId } = this._controller.getState();
|
|
56
|
+
log('offer', {
|
|
57
|
+
peer,
|
|
58
|
+
isCentral: peer.equals(this._centralPeer),
|
|
59
|
+
isSelfCentral: ownPeerId.equals(this._centralPeer)
|
|
60
|
+
});
|
|
61
|
+
return ownPeerId.equals(this._centralPeer) || peer.equals(this._centralPeer);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async destroy(): Promise<void> {
|
|
65
|
+
// Nothing to do.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { PublicKey } from '@dxos/keys';
|
|
6
|
+
|
|
7
|
+
export interface SwarmController {
|
|
8
|
+
/**
|
|
9
|
+
* Get current state.
|
|
10
|
+
*/
|
|
11
|
+
getState(): SwarmState;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initiate a connection.
|
|
15
|
+
*/
|
|
16
|
+
connect(peer: PublicKey): void;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Disconnect from a peer.
|
|
20
|
+
*/
|
|
21
|
+
disconnect(peer: PublicKey): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SwarmState {
|
|
25
|
+
/**
|
|
26
|
+
* This node's peer Id.
|
|
27
|
+
*/
|
|
28
|
+
ownPeerId: PublicKey;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Peers with established connections.
|
|
32
|
+
*/
|
|
33
|
+
connected: PublicKey[];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Candidates for connection. Does not intersect with a set of already connected peers.
|
|
37
|
+
*/
|
|
38
|
+
candidates: PublicKey[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Topology {
|
|
42
|
+
/**
|
|
43
|
+
* Called when swarm is created.
|
|
44
|
+
*
|
|
45
|
+
* May be used to bind the swarm controller and initialize any asynchronous actions.
|
|
46
|
+
*
|
|
47
|
+
* @param controller
|
|
48
|
+
*/
|
|
49
|
+
init(controller: SwarmController): void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Called when swarm state is updated.
|
|
53
|
+
*/
|
|
54
|
+
update(): void;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Called when remote peer offers a connection.
|
|
58
|
+
*
|
|
59
|
+
* @returns true - to accept the connection, false - to reject.
|
|
60
|
+
*/
|
|
61
|
+
onOffer(peer: PublicKey): Promise<boolean>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Called when swarm is destroyed or topology is changed.
|
|
65
|
+
*
|
|
66
|
+
* Any error thrown here will be a critical error for the swarm.
|
|
67
|
+
*/
|
|
68
|
+
destroy(): Promise<void>;
|
|
69
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2021 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import expect from 'expect';
|
|
6
|
+
import waitForExpect from 'wait-for-expect';
|
|
7
|
+
|
|
8
|
+
import { discoveryKey } from '@dxos/crypto';
|
|
9
|
+
import { PublicKey } from '@dxos/keys';
|
|
10
|
+
import { Protocol } from '@dxos/mesh-protocol';
|
|
11
|
+
import { afterTest } from '@dxos/testutils';
|
|
12
|
+
import { range } from '@dxos/util';
|
|
13
|
+
|
|
14
|
+
import { TestProtocolPlugin, testProtocolProvider } from '../testing';
|
|
15
|
+
import { MemoryTransport } from './memory-transport';
|
|
16
|
+
|
|
17
|
+
// TODO(burdon): Flaky test.
|
|
18
|
+
// Cannot log after tests are done. Did you forget to wait for something async in your test?
|
|
19
|
+
// Attempted to log "Ignoring unsupported ICE candidate.".
|
|
20
|
+
|
|
21
|
+
const createPair = () => {
|
|
22
|
+
const topic = PublicKey.random();
|
|
23
|
+
const peer1Id = PublicKey.random();
|
|
24
|
+
const peer2Id = PublicKey.random();
|
|
25
|
+
const sessionId = PublicKey.random();
|
|
26
|
+
|
|
27
|
+
const plugin1 = new TestProtocolPlugin(peer1Id.asBuffer());
|
|
28
|
+
const protocolProvider1 = testProtocolProvider(topic.asBuffer(), peer1Id.asBuffer(), plugin1);
|
|
29
|
+
const connection1 = new MemoryTransport(
|
|
30
|
+
peer1Id,
|
|
31
|
+
peer2Id,
|
|
32
|
+
sessionId,
|
|
33
|
+
topic,
|
|
34
|
+
protocolProvider1({ channel: discoveryKey(topic), initiator: true }).stream
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
afterTest(() => connection1.close());
|
|
38
|
+
afterTest(() => connection1.errors.assertNoUnhandledErrors());
|
|
39
|
+
|
|
40
|
+
const plugin2 = new TestProtocolPlugin(peer2Id.asBuffer());
|
|
41
|
+
const protocolProvider2 = testProtocolProvider(topic.asBuffer(), peer2Id.asBuffer(), plugin2);
|
|
42
|
+
const connection2 = new MemoryTransport(
|
|
43
|
+
peer2Id,
|
|
44
|
+
peer1Id,
|
|
45
|
+
sessionId,
|
|
46
|
+
topic,
|
|
47
|
+
protocolProvider2({ channel: discoveryKey(topic), initiator: false }).stream
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
afterTest(() => connection2.close());
|
|
51
|
+
afterTest(() => connection2.errors.assertNoUnhandledErrors());
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
connection1,
|
|
55
|
+
connection2,
|
|
56
|
+
plugin1,
|
|
57
|
+
plugin2,
|
|
58
|
+
peer1Id,
|
|
59
|
+
peer2Id,
|
|
60
|
+
topic
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
describe('MemoryTransport', function () {
|
|
65
|
+
it('establish connection and send data through with protocol', async function () {
|
|
66
|
+
const { plugin1, plugin2, peer1Id } = createPair();
|
|
67
|
+
|
|
68
|
+
const received: any[] = [];
|
|
69
|
+
const mockReceive = (p: Protocol, s: string) => {
|
|
70
|
+
received.push(p, s);
|
|
71
|
+
return undefined;
|
|
72
|
+
};
|
|
73
|
+
plugin1.on('receive', mockReceive);
|
|
74
|
+
|
|
75
|
+
plugin2.on('connect', async (protocol) => {
|
|
76
|
+
await plugin2.send(peer1Id.asBuffer(), '{"message": "Hello"}');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await waitForExpect(() => {
|
|
80
|
+
expect(received.length).toBe(2);
|
|
81
|
+
expect(received[0]).toBeInstanceOf(Protocol);
|
|
82
|
+
expect(received[1]).toBe('{"message": "Hello"}');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('10 pairs of peers connecting at the same time', async function () {
|
|
87
|
+
await Promise.all(
|
|
88
|
+
range(10).map(async () => {
|
|
89
|
+
const { plugin1, plugin2, peer1Id } = createPair();
|
|
90
|
+
|
|
91
|
+
const received: any[] = [];
|
|
92
|
+
const mockReceive = (p: Protocol, s: string) => {
|
|
93
|
+
received.push(p, s);
|
|
94
|
+
return undefined;
|
|
95
|
+
};
|
|
96
|
+
plugin1.on('receive', mockReceive);
|
|
97
|
+
|
|
98
|
+
plugin2.on('connect', async (protocol) => {
|
|
99
|
+
await plugin2.send(peer1Id.asBuffer(), '{"message": "Hello"}');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await waitForExpect(() => {
|
|
103
|
+
expect(received.length).toBe(2);
|
|
104
|
+
expect(received[0]).toBeInstanceOf(Protocol);
|
|
105
|
+
expect(received[1]).toBe('{"message": "Hello"}');
|
|
106
|
+
});
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { Transform } from 'stream';
|
|
7
|
+
|
|
8
|
+
import { Event } from '@dxos/async';
|
|
9
|
+
import { ErrorStream } from '@dxos/debug';
|
|
10
|
+
import { PublicKey } from '@dxos/keys';
|
|
11
|
+
import { log } from '@dxos/log';
|
|
12
|
+
import { Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
13
|
+
import { ComplexMap } from '@dxos/util';
|
|
14
|
+
|
|
15
|
+
import { Transport, TransportFactory } from './transport';
|
|
16
|
+
|
|
17
|
+
type ConnectionKey = [topic: PublicKey, nodeId: PublicKey, remoteId: PublicKey];
|
|
18
|
+
|
|
19
|
+
// Delay (in milliseconds) for data being sent through in-memory connections to simulate network latency.
|
|
20
|
+
const MEMORY_TRANSPORT_DELAY = 1;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a binary stream that delays data being sent through the stream by the specified amount of time.
|
|
24
|
+
*/
|
|
25
|
+
const createStreamDelay = (delay: number): NodeJS.ReadWriteStream =>
|
|
26
|
+
new Transform({
|
|
27
|
+
objectMode: true,
|
|
28
|
+
transform: (chunk, _, cb) => {
|
|
29
|
+
setTimeout(() => cb(null, chunk), delay); // TODO(burdon): Randomize.
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Fake transport.
|
|
35
|
+
*/
|
|
36
|
+
// TODO(burdon): Remove static variables.
|
|
37
|
+
export class MemoryTransport implements Transport {
|
|
38
|
+
// TODO(burdon): Remove global properties.
|
|
39
|
+
private static readonly _connections = new ComplexMap<ConnectionKey, MemoryTransport>(
|
|
40
|
+
([topic, nodeId, remoteId]) => topic.toHex() + nodeId.toHex() + remoteId.toHex()
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
public readonly closed = new Event<void>();
|
|
44
|
+
public readonly connected = new Event<void>();
|
|
45
|
+
public readonly errors = new ErrorStream();
|
|
46
|
+
|
|
47
|
+
private readonly _ownKey: ConnectionKey;
|
|
48
|
+
private readonly _remoteKey: ConnectionKey;
|
|
49
|
+
|
|
50
|
+
private readonly _outgoingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
|
|
51
|
+
private readonly _incomingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
|
|
52
|
+
|
|
53
|
+
private _remoteConnection?: MemoryTransport;
|
|
54
|
+
|
|
55
|
+
constructor(
|
|
56
|
+
private readonly _ownId: PublicKey,
|
|
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);
|
|
80
|
+
|
|
81
|
+
this.connected.emit();
|
|
82
|
+
this._remoteConnection.connected.emit();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get remoteId(): PublicKey {
|
|
87
|
+
return this._remoteId;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get sessionId(): PublicKey {
|
|
91
|
+
return this._sessionId;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async signal(signal: Signal) {
|
|
95
|
+
// No-op.
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async close(): Promise<void> {
|
|
99
|
+
log('closing', { topic: this._topic, peerId: this._ownId, remoteId: this._remoteId });
|
|
100
|
+
|
|
101
|
+
MemoryTransport._connections.delete(this._ownKey);
|
|
102
|
+
if (this._remoteConnection) {
|
|
103
|
+
MemoryTransport._connections.delete(this._remoteKey);
|
|
104
|
+
|
|
105
|
+
// TODO(dmaretskyi): Hypercore streams do not seem to have the unpipe method.
|
|
106
|
+
// NOTE(burdon): Using readable-stream.wrap() might help (see feed-store).
|
|
107
|
+
// code this._stream
|
|
108
|
+
// code .unpipe(this._outgoingDelay)
|
|
109
|
+
// code .unpipe(this._remoteConnection._stream)
|
|
110
|
+
// code .unpipe(this._incomingDelay)
|
|
111
|
+
// code .unpipe(this._stream);
|
|
112
|
+
|
|
113
|
+
this._outgoingDelay.unpipe();
|
|
114
|
+
this._incomingDelay.unpipe();
|
|
115
|
+
|
|
116
|
+
this._remoteConnection.closed.emit();
|
|
117
|
+
this._remoteConnection._remoteConnection = undefined;
|
|
118
|
+
this._remoteConnection = undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.closed.emit();
|
|
122
|
+
log('closed', { topic: this._topic });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const MemoryTransportFactory: TransportFactory = {
|
|
127
|
+
create: (opts) => new MemoryTransport(opts.ownId, opts.remoteId, opts.sessionId, opts.topic, opts.stream)
|
|
128
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Event } from '@dxos/async';
|
|
6
|
+
import { ErrorStream } from '@dxos/debug';
|
|
7
|
+
import { PublicKey } from '@dxos/keys';
|
|
8
|
+
import { Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
9
|
+
|
|
10
|
+
import { SignalMessage } from '../signal';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Abstraction over a P2P connection transport. Currently either WebRTC or in-memory.
|
|
14
|
+
*/
|
|
15
|
+
export interface Transport {
|
|
16
|
+
closed: Event;
|
|
17
|
+
connected: Event;
|
|
18
|
+
errors: ErrorStream;
|
|
19
|
+
signal(signal: Signal): Promise<void>; // TODO(burdon): Remove async?
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type TransportOptions = {
|
|
24
|
+
/**
|
|
25
|
+
* Did local node initiate this connection.
|
|
26
|
+
*/
|
|
27
|
+
initiator: boolean;
|
|
28
|
+
|
|
29
|
+
ownId: PublicKey;
|
|
30
|
+
remoteId: PublicKey;
|
|
31
|
+
sessionId: PublicKey;
|
|
32
|
+
topic: PublicKey;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Wire protocol.
|
|
36
|
+
*/
|
|
37
|
+
stream: NodeJS.ReadWriteStream;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Send a signal message to remote peer.
|
|
41
|
+
*/
|
|
42
|
+
sendSignal: (msg: SignalMessage) => Promise<void>; // TODO(burdon): Remove async?
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export interface TransportFactory {
|
|
46
|
+
create(options: TransportOptions): Transport;
|
|
47
|
+
}
|