@dxos/network-manager 0.6.8-main.3be982f → 0.6.8-staging.63bcb81
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/lib/browser/{chunk-ZT4NXID2.mjs → chunk-ZQ4OU7JZ.mjs} +289 -251
- package/dist/lib/browser/chunk-ZQ4OU7JZ.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +5 -2
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-DZJ3BJOK.cjs → chunk-IQBYIEAR.cjs} +294 -256
- package/dist/lib/node/chunk-IQBYIEAR.cjs.map +7 -0
- package/dist/lib/node/index.cjs +28 -28
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +22 -19
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/types/src/network-manager.d.ts +9 -6
- package/dist/types/src/network-manager.d.ts.map +1 -1
- package/dist/types/src/signal/signal-connection.d.ts +3 -2
- package/dist/types/src/signal/signal-connection.d.ts.map +1 -1
- package/dist/types/src/signal/signal-messenger.d.ts +5 -4
- package/dist/types/src/signal/signal-messenger.d.ts.map +1 -1
- package/dist/types/src/signal/swarm-messenger.d.ts +4 -7
- package/dist/types/src/signal/swarm-messenger.d.ts.map +1 -1
- package/dist/types/src/swarm/connection.d.ts +4 -4
- package/dist/types/src/swarm/connection.d.ts.map +1 -1
- package/dist/types/src/swarm/peer.d.ts +5 -4
- package/dist/types/src/swarm/peer.d.ts.map +1 -1
- package/dist/types/src/swarm/swarm-mapper.d.ts.map +1 -1
- package/dist/types/src/swarm/swarm.d.ts +4 -4
- package/dist/types/src/swarm/swarm.d.ts.map +1 -1
- package/dist/types/src/tests/basic-test-suite.d.ts.map +1 -1
- package/dist/types/src/tests/property-test-suite.d.ts.map +1 -1
- package/package.json +19 -18
- package/src/connection-log.ts +1 -1
- package/src/network-manager.ts +27 -13
- package/src/signal/integration.test.ts +25 -19
- package/src/signal/signal-connection.ts +3 -2
- package/src/signal/signal-messenger.ts +5 -4
- package/src/signal/swarm-messenger.test.ts +21 -21
- package/src/signal/swarm-messenger.ts +11 -10
- package/src/swarm/connection.test.ts +19 -17
- package/src/swarm/connection.ts +16 -15
- package/src/swarm/peer.ts +41 -35
- package/src/swarm/swarm-mapper.ts +7 -6
- package/src/swarm/swarm.test.ts +36 -28
- package/src/swarm/swarm.ts +57 -53
- package/src/testing/test-builder.ts +1 -1
- package/src/tests/basic-test-suite.ts +8 -5
- package/src/tests/property-test-suite.ts +4 -1
- package/dist/lib/browser/chunk-ZT4NXID2.mjs.map +0 -7
- package/dist/lib/node/chunk-DZJ3BJOK.cjs.map +0 -7
|
@@ -45,14 +45,14 @@ describe('SwarmMessenger', () => {
|
|
|
45
45
|
onOffer?: (msg: OfferMessage) => Promise<Answer>;
|
|
46
46
|
topic: PublicKey;
|
|
47
47
|
}) => {
|
|
48
|
-
const
|
|
48
|
+
const peer = { peerKey: PublicKey.random().toHex() };
|
|
49
49
|
const signalManager = new WebsocketSignalManager([{ server: signalApiUrl }]);
|
|
50
50
|
await signalManager.open();
|
|
51
51
|
afterTest(() => signalManager.close());
|
|
52
52
|
|
|
53
53
|
const messenger = new Messenger({ signalManager });
|
|
54
54
|
await messenger.listen({
|
|
55
|
-
|
|
55
|
+
peer,
|
|
56
56
|
payloadType: 'dxos.mesh.swarm.SwarmMessage',
|
|
57
57
|
onMessage: async (message) => await router.receiveMessage(message),
|
|
58
58
|
});
|
|
@@ -66,7 +66,7 @@ describe('SwarmMessenger', () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
return {
|
|
69
|
-
|
|
69
|
+
peer,
|
|
70
70
|
signalManager,
|
|
71
71
|
router,
|
|
72
72
|
};
|
|
@@ -77,7 +77,7 @@ describe('SwarmMessenger', () => {
|
|
|
77
77
|
const signalMock1 = async (msg: SignalMessage) => {
|
|
78
78
|
received.push(msg);
|
|
79
79
|
};
|
|
80
|
-
const { signalManager: signalManager1,
|
|
80
|
+
const { signalManager: signalManager1, peer: peer1 } = await createSignalClientAndMessageRouter({
|
|
81
81
|
signalApiUrl: broker1.url(),
|
|
82
82
|
onSignal: signalMock1,
|
|
83
83
|
topic,
|
|
@@ -85,14 +85,14 @@ describe('SwarmMessenger', () => {
|
|
|
85
85
|
const {
|
|
86
86
|
signalManager: signalManager2,
|
|
87
87
|
router: router2,
|
|
88
|
-
|
|
88
|
+
peer: peer2,
|
|
89
89
|
} = await createSignalClientAndMessageRouter({
|
|
90
90
|
signalApiUrl: broker1.url(),
|
|
91
91
|
topic,
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
await signalManager1.join({ topic,
|
|
95
|
-
await signalManager2.join({ topic,
|
|
94
|
+
await signalManager1.join({ topic, peer: peer1 });
|
|
95
|
+
await signalManager2.join({ topic, peer: peer2 });
|
|
96
96
|
|
|
97
97
|
const msg: SignalMessage = {
|
|
98
98
|
author: peer2,
|
|
@@ -115,22 +115,22 @@ describe('SwarmMessenger', () => {
|
|
|
115
115
|
const {
|
|
116
116
|
signalManager: signalManager1,
|
|
117
117
|
router: router1,
|
|
118
|
-
|
|
118
|
+
peer: peer1,
|
|
119
119
|
} = await createSignalClientAndMessageRouter({
|
|
120
120
|
signalApiUrl: broker1.url(),
|
|
121
121
|
onSignal: (async () => {}) as any,
|
|
122
122
|
onOffer: async () => ({ accept: true }),
|
|
123
123
|
topic,
|
|
124
124
|
});
|
|
125
|
-
const { signalManager: signalManager2,
|
|
125
|
+
const { signalManager: signalManager2, peer: peer2 } = await createSignalClientAndMessageRouter({
|
|
126
126
|
signalApiUrl: broker1.url(),
|
|
127
127
|
onSignal: (async () => {}) as any,
|
|
128
128
|
onOffer: async () => ({ accept: true }),
|
|
129
129
|
topic,
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
await signalManager1.join({ topic,
|
|
133
|
-
await signalManager2.join({ topic,
|
|
132
|
+
await signalManager1.join({ topic, peer: peer1 });
|
|
133
|
+
await signalManager2.join({ topic, peer: peer2 });
|
|
134
134
|
const answer = await router1.offer({
|
|
135
135
|
author: peer1,
|
|
136
136
|
recipient: peer2,
|
|
@@ -149,7 +149,7 @@ describe('SwarmMessenger', () => {
|
|
|
149
149
|
const {
|
|
150
150
|
signalManager: signalManager1,
|
|
151
151
|
router: router1,
|
|
152
|
-
|
|
152
|
+
peer: peer1,
|
|
153
153
|
} = await createSignalClientAndMessageRouter({
|
|
154
154
|
signalApiUrl: broker1.url(),
|
|
155
155
|
onSignal: signalMock1,
|
|
@@ -163,7 +163,7 @@ describe('SwarmMessenger', () => {
|
|
|
163
163
|
const {
|
|
164
164
|
signalManager: signalManager2,
|
|
165
165
|
router: router2,
|
|
166
|
-
|
|
166
|
+
peer: peer2,
|
|
167
167
|
} = await createSignalClientAndMessageRouter({
|
|
168
168
|
signalApiUrl: broker1.url(),
|
|
169
169
|
onSignal: signalMock2,
|
|
@@ -177,7 +177,7 @@ describe('SwarmMessenger', () => {
|
|
|
177
177
|
const {
|
|
178
178
|
signalManager: signalManager3,
|
|
179
179
|
router: router3,
|
|
180
|
-
|
|
180
|
+
peer: peer3,
|
|
181
181
|
} = await createSignalClientAndMessageRouter({
|
|
182
182
|
signalApiUrl: broker1.url(),
|
|
183
183
|
onSignal: signalMock3,
|
|
@@ -185,9 +185,9 @@ describe('SwarmMessenger', () => {
|
|
|
185
185
|
topic,
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
-
await signalManager1.join({ topic,
|
|
189
|
-
await signalManager2.join({ topic,
|
|
190
|
-
await signalManager3.join({ topic,
|
|
188
|
+
await signalManager1.join({ topic, peer: peer1 });
|
|
189
|
+
await signalManager2.join({ topic, peer: peer2 });
|
|
190
|
+
await signalManager3.join({ topic, peer: peer3 });
|
|
191
191
|
|
|
192
192
|
// sending signal from peer1 to peer3.
|
|
193
193
|
const msg1to3: SignalMessage = {
|
|
@@ -233,7 +233,7 @@ describe('SwarmMessenger', () => {
|
|
|
233
233
|
const {
|
|
234
234
|
signalManager: signalManager1,
|
|
235
235
|
router: router1,
|
|
236
|
-
|
|
236
|
+
peer: peer1,
|
|
237
237
|
} = await createSignalClientAndMessageRouter({
|
|
238
238
|
signalApiUrl: broker1.url(),
|
|
239
239
|
onSignal: (async () => {}) as any,
|
|
@@ -243,7 +243,7 @@ describe('SwarmMessenger', () => {
|
|
|
243
243
|
const {
|
|
244
244
|
signalManager: signalManager2,
|
|
245
245
|
router: router2,
|
|
246
|
-
|
|
246
|
+
peer: peer2,
|
|
247
247
|
} = await createSignalClientAndMessageRouter({
|
|
248
248
|
signalApiUrl: broker1.url(),
|
|
249
249
|
onSignal: (async () => {}) as any,
|
|
@@ -251,8 +251,8 @@ describe('SwarmMessenger', () => {
|
|
|
251
251
|
topic,
|
|
252
252
|
});
|
|
253
253
|
|
|
254
|
-
await signalManager1.join({ topic,
|
|
255
|
-
await signalManager2.join({ topic,
|
|
254
|
+
await signalManager1.join({ topic, peer: peer1 });
|
|
255
|
+
await signalManager2.join({ topic, peer: peer2 });
|
|
256
256
|
|
|
257
257
|
// sending offer from peer1 to peer2.
|
|
258
258
|
const answer1 = await router1.offer({
|
|
@@ -7,6 +7,7 @@ import { Context } from '@dxos/context';
|
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
8
|
import { PublicKey } from '@dxos/keys';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
+
import { type PeerInfo, type Message } from '@dxos/messaging';
|
|
10
11
|
import { schema, TimeoutError } from '@dxos/protocols';
|
|
11
12
|
import { type Answer, type SwarmMessage } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
12
13
|
import { ComplexMap, type MakeOptional } from '@dxos/util';
|
|
@@ -18,7 +19,7 @@ interface OfferRecord {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export type SwarmMessengerOptions = {
|
|
21
|
-
sendMessage: (params:
|
|
22
|
+
sendMessage: (params: Message) => Promise<void>;
|
|
22
23
|
onOffer: (message: OfferMessage) => Promise<Answer>;
|
|
23
24
|
onSignal: (message: SignalMessage) => Promise<void>;
|
|
24
25
|
topic: PublicKey;
|
|
@@ -32,7 +33,7 @@ const SwarmMessage = schema.getCodecForType('dxos.mesh.swarm.SwarmMessage');
|
|
|
32
33
|
export class SwarmMessenger implements SignalMessenger {
|
|
33
34
|
private readonly _ctx = new Context();
|
|
34
35
|
|
|
35
|
-
private readonly _sendMessage: (msg:
|
|
36
|
+
private readonly _sendMessage: (msg: Message) => Promise<void>;
|
|
36
37
|
private readonly _onSignal: (message: SignalMessage) => Promise<void>;
|
|
37
38
|
private readonly _onOffer: (message: OfferMessage) => Promise<Answer>;
|
|
38
39
|
private readonly _topic: PublicKey;
|
|
@@ -51,8 +52,8 @@ export class SwarmMessenger implements SignalMessenger {
|
|
|
51
52
|
recipient,
|
|
52
53
|
payload,
|
|
53
54
|
}: {
|
|
54
|
-
author:
|
|
55
|
-
recipient:
|
|
55
|
+
author: PeerInfo;
|
|
56
|
+
recipient: PeerInfo;
|
|
56
57
|
payload: Any;
|
|
57
58
|
}): Promise<void> {
|
|
58
59
|
if (payload.type_url !== 'dxos.mesh.swarm.SwarmMessage') {
|
|
@@ -110,8 +111,8 @@ export class SwarmMessenger implements SignalMessenger {
|
|
|
110
111
|
recipient,
|
|
111
112
|
message,
|
|
112
113
|
}: {
|
|
113
|
-
author:
|
|
114
|
-
recipient:
|
|
114
|
+
author: PeerInfo;
|
|
115
|
+
recipient: PeerInfo;
|
|
115
116
|
message: MakeOptional<SwarmMessage, 'messageId'>;
|
|
116
117
|
}): Promise<void> {
|
|
117
118
|
const networkMessage: SwarmMessage = {
|
|
@@ -147,8 +148,8 @@ export class SwarmMessenger implements SignalMessenger {
|
|
|
147
148
|
recipient,
|
|
148
149
|
message,
|
|
149
150
|
}: {
|
|
150
|
-
author:
|
|
151
|
-
recipient:
|
|
151
|
+
author: PeerInfo;
|
|
152
|
+
recipient: PeerInfo;
|
|
152
153
|
message: SwarmMessage;
|
|
153
154
|
}): Promise<void> {
|
|
154
155
|
invariant(message.data.offer, 'No offer');
|
|
@@ -184,8 +185,8 @@ export class SwarmMessenger implements SignalMessenger {
|
|
|
184
185
|
recipient,
|
|
185
186
|
message,
|
|
186
187
|
}: {
|
|
187
|
-
author:
|
|
188
|
-
recipient:
|
|
188
|
+
author: PeerInfo;
|
|
189
|
+
recipient: PeerInfo;
|
|
189
190
|
message: SwarmMessage;
|
|
190
191
|
}): Promise<void> {
|
|
191
192
|
invariant(message.messageId);
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import { sleep } from '@dxos/async';
|
|
6
6
|
import { PublicKey } from '@dxos/keys';
|
|
7
|
-
import { log } from '@dxos/log';
|
|
8
7
|
import { describe, test } from '@dxos/test';
|
|
9
8
|
|
|
10
9
|
import { Connection } from './connection';
|
|
@@ -13,18 +12,18 @@ import { createLibDataChannelTransportFactory, createSimplePeerTransportFactory
|
|
|
13
12
|
|
|
14
13
|
describe('Connection', () => {
|
|
15
14
|
test('initiator opens after responder', async () => {
|
|
16
|
-
const [topic,
|
|
17
|
-
const
|
|
15
|
+
const [topic, sessionId] = PublicKey.randomSequence();
|
|
16
|
+
const peer1 = { peerKey: PublicKey.random().toHex() };
|
|
17
|
+
const peer2 = { peerKey: PublicKey.random().toHex() };
|
|
18
|
+
const protocol1 = new TestWireProtocol(PublicKey.from(peer1.peerKey));
|
|
18
19
|
const connection1 = new Connection(
|
|
19
20
|
topic,
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
peer1,
|
|
22
|
+
peer2,
|
|
22
23
|
sessionId,
|
|
23
24
|
true,
|
|
24
25
|
{
|
|
25
26
|
offer: async (msg) => {
|
|
26
|
-
log('offer', { msg });
|
|
27
|
-
|
|
28
27
|
return { accept: true };
|
|
29
28
|
},
|
|
30
29
|
signal: async (msg) => {
|
|
@@ -33,32 +32,35 @@ describe('Connection', () => {
|
|
|
33
32
|
},
|
|
34
33
|
protocol1.factory({
|
|
35
34
|
initiator: true,
|
|
36
|
-
localPeerId:
|
|
37
|
-
remotePeerId:
|
|
35
|
+
localPeerId: PublicKey.from(peer1.peerKey),
|
|
36
|
+
remotePeerId: PublicKey.from(peer2.peerKey),
|
|
38
37
|
topic,
|
|
39
38
|
}),
|
|
40
39
|
// TODO(nf): configure better
|
|
41
40
|
process.env.MOCHA_ENV === 'nodejs' ? createLibDataChannelTransportFactory() : createSimplePeerTransportFactory(),
|
|
42
41
|
);
|
|
43
42
|
|
|
44
|
-
const protocol2 = new TestWireProtocol(
|
|
43
|
+
const protocol2 = new TestWireProtocol(PublicKey.from(peer2.peerKey));
|
|
45
44
|
const connection2 = new Connection(
|
|
46
45
|
topic,
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
peer2,
|
|
47
|
+
peer1,
|
|
49
48
|
sessionId,
|
|
50
49
|
false,
|
|
51
50
|
{
|
|
52
51
|
offer: async (msg) => {
|
|
53
|
-
log('offer', { msg });
|
|
54
|
-
|
|
55
52
|
return { accept: true };
|
|
56
53
|
},
|
|
57
54
|
signal: async (msg) => {
|
|
58
55
|
await connection1.signal(msg);
|
|
59
56
|
},
|
|
60
57
|
},
|
|
61
|
-
protocol2.factory({
|
|
58
|
+
protocol2.factory({
|
|
59
|
+
initiator: false,
|
|
60
|
+
localPeerId: PublicKey.from(peer2.peerKey),
|
|
61
|
+
remotePeerId: PublicKey.from(peer1.peerKey),
|
|
62
|
+
topic,
|
|
63
|
+
}),
|
|
62
64
|
// TODO(nf): configure better
|
|
63
65
|
process.env.MOCHA_ENV === 'nodejs' ? createLibDataChannelTransportFactory() : createSimplePeerTransportFactory(),
|
|
64
66
|
);
|
|
@@ -70,8 +72,8 @@ describe('Connection', () => {
|
|
|
70
72
|
connection1.initiate();
|
|
71
73
|
await connection1.openConnection();
|
|
72
74
|
await Promise.all([
|
|
73
|
-
protocol1.testConnection(
|
|
74
|
-
protocol2.testConnection(
|
|
75
|
+
protocol1.testConnection(PublicKey.from(peer2.peerKey), 'test message 1'),
|
|
76
|
+
protocol2.testConnection(PublicKey.from(peer1.peerKey), 'test message 2'),
|
|
75
77
|
]);
|
|
76
78
|
});
|
|
77
79
|
});
|
package/src/swarm/connection.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { ErrorStream } from '@dxos/debug';
|
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
9
|
import { PublicKey } from '@dxos/keys';
|
|
10
10
|
import { log, logInfo } from '@dxos/log';
|
|
11
|
+
import { type PeerInfo } from '@dxos/messaging';
|
|
11
12
|
import {
|
|
12
13
|
CancelledError,
|
|
13
14
|
ProtocolError,
|
|
@@ -125,8 +126,8 @@ export class Connection {
|
|
|
125
126
|
|
|
126
127
|
constructor(
|
|
127
128
|
public readonly topic: PublicKey,
|
|
128
|
-
public readonly
|
|
129
|
-
public readonly
|
|
129
|
+
public readonly localInfo: PeerInfo,
|
|
130
|
+
public readonly remoteInfo: PeerInfo,
|
|
130
131
|
public readonly sessionId: PublicKey,
|
|
131
132
|
public readonly initiator: boolean,
|
|
132
133
|
private readonly _signalMessaging: SignalMessenger,
|
|
@@ -137,8 +138,8 @@ export class Connection {
|
|
|
137
138
|
log.trace('dxos.mesh.connection.construct', {
|
|
138
139
|
sessionId: this.sessionId,
|
|
139
140
|
topic: this.topic,
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
localPeer: this.localInfo,
|
|
142
|
+
remotePeer: this.remoteInfo,
|
|
142
143
|
initiator: this.initiator,
|
|
143
144
|
});
|
|
144
145
|
}
|
|
@@ -169,8 +170,8 @@ export class Connection {
|
|
|
169
170
|
log.trace('dxos.mesh.connection.open', {
|
|
170
171
|
sessionId: this.sessionId,
|
|
171
172
|
topic: this.topic,
|
|
172
|
-
localPeerId: this.
|
|
173
|
-
remotePeerId: this.
|
|
173
|
+
localPeerId: this.localInfo,
|
|
174
|
+
remotePeerId: this.remoteInfo,
|
|
174
175
|
initiator: this.initiator,
|
|
175
176
|
});
|
|
176
177
|
|
|
@@ -317,7 +318,7 @@ export class Connection {
|
|
|
317
318
|
await this.connectedTimeoutContext.dispose();
|
|
318
319
|
await this._ctx.dispose();
|
|
319
320
|
|
|
320
|
-
log('closing...', { peerId: this.
|
|
321
|
+
log('closing...', { peerId: this.localInfo });
|
|
321
322
|
|
|
322
323
|
let abortProtocol = false;
|
|
323
324
|
if (lastState !== ConnectionState.CONNECTED) {
|
|
@@ -336,7 +337,7 @@ export class Connection {
|
|
|
336
337
|
log.catch(err);
|
|
337
338
|
}
|
|
338
339
|
|
|
339
|
-
log('closed', { peerId: this.
|
|
340
|
+
log('closed', { peerId: this.localInfo });
|
|
340
341
|
this._changeState(ConnectionState.CLOSED);
|
|
341
342
|
this._callbacks?.onClosed?.(err);
|
|
342
343
|
}
|
|
@@ -373,8 +374,8 @@ export class Connection {
|
|
|
373
374
|
this._outgoingSignalBuffer.length = 0;
|
|
374
375
|
|
|
375
376
|
await this._signalMessaging.signal({
|
|
376
|
-
author: this.
|
|
377
|
-
recipient: this.
|
|
377
|
+
author: this.localInfo,
|
|
378
|
+
recipient: this.remoteInfo,
|
|
378
379
|
sessionId: this.sessionId,
|
|
379
380
|
topic: this.topic,
|
|
380
381
|
data: { signalBatch: { signals } },
|
|
@@ -405,8 +406,8 @@ export class Connection {
|
|
|
405
406
|
return;
|
|
406
407
|
}
|
|
407
408
|
invariant(msg.data.signal || msg.data.signalBatch);
|
|
408
|
-
invariant(msg.author
|
|
409
|
-
invariant(msg.recipient
|
|
409
|
+
invariant(msg.author.peerKey === this.remoteInfo.peerKey);
|
|
410
|
+
invariant(msg.recipient.peerKey === this.localInfo.peerKey);
|
|
410
411
|
|
|
411
412
|
const signals = msg.data.signalBatch ? msg.data.signalBatch.signals ?? [] : [msg.data.signal];
|
|
412
413
|
for (const signal of signals) {
|
|
@@ -415,11 +416,11 @@ export class Connection {
|
|
|
415
416
|
}
|
|
416
417
|
|
|
417
418
|
if ([ConnectionState.CREATED, ConnectionState.INITIAL].includes(this.state)) {
|
|
418
|
-
log('buffered signal', { peerId: this.
|
|
419
|
+
log('buffered signal', { peerId: this.localInfo, remoteId: this.remoteInfo, msg: msg.data });
|
|
419
420
|
this._incomingSignalBuffer.push(signal);
|
|
420
421
|
} else {
|
|
421
422
|
invariant(this._transport, 'Connection not ready to accept signals.');
|
|
422
|
-
log('received signal', { peerId: this.
|
|
423
|
+
log('received signal', { peerId: this.localInfo, remoteId: this.remoteInfo, msg: msg.data });
|
|
423
424
|
await this._transport.onSignal(signal);
|
|
424
425
|
}
|
|
425
426
|
}
|
|
@@ -430,7 +431,7 @@ export class Connection {
|
|
|
430
431
|
}
|
|
431
432
|
|
|
432
433
|
private _changeState(state: ConnectionState): void {
|
|
433
|
-
log('stateChanged', { from: this._state, to: state, peerId: this.
|
|
434
|
+
log('stateChanged', { from: this._state, to: state, peerId: this.localInfo });
|
|
434
435
|
invariant(state !== this._state, 'Already in this state.');
|
|
435
436
|
this._state = state;
|
|
436
437
|
this.stateChanged.emit(state);
|
package/src/swarm/peer.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Context } from '@dxos/context';
|
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
8
|
import { PublicKey } from '@dxos/keys';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
+
import { type PeerInfo } from '@dxos/messaging';
|
|
10
11
|
import { CancelledError, SystemError } from '@dxos/protocols';
|
|
11
12
|
import { type Answer } from '@dxos/protocols/proto/dxos/mesh/swarm';
|
|
12
13
|
|
|
@@ -51,7 +52,7 @@ interface PeerCallbacks {
|
|
|
51
52
|
/**
|
|
52
53
|
* Returns true if the remote peer's offer should be accepted.
|
|
53
54
|
*/
|
|
54
|
-
onOffer: (
|
|
55
|
+
onOffer: (remote: PeerInfo) => Promise<boolean>;
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
58
|
* Peer is available to connect.
|
|
@@ -91,9 +92,9 @@ export class Peer {
|
|
|
91
92
|
public readonly connectionDisplaced = new Event<Connection>();
|
|
92
93
|
|
|
93
94
|
constructor(
|
|
94
|
-
public readonly
|
|
95
|
+
public readonly remoteInfo: PeerInfo,
|
|
95
96
|
public readonly topic: PublicKey,
|
|
96
|
-
public readonly
|
|
97
|
+
public readonly localInfo: PeerInfo,
|
|
97
98
|
private readonly _signalMessaging: SignalMessenger,
|
|
98
99
|
private readonly _protocolProvider: WireProtocolProvider,
|
|
99
100
|
private readonly _transportFactory: TransportFactory,
|
|
@@ -105,7 +106,7 @@ export class Peer {
|
|
|
105
106
|
* Respond to remote offer.
|
|
106
107
|
*/
|
|
107
108
|
async onOffer(message: OfferMessage): Promise<Answer> {
|
|
108
|
-
const
|
|
109
|
+
const remote = message.author;
|
|
109
110
|
|
|
110
111
|
if (
|
|
111
112
|
this.connection &&
|
|
@@ -118,12 +119,12 @@ export class Peer {
|
|
|
118
119
|
if (this.connection || this.initiating) {
|
|
119
120
|
// Determine the "polite" peer (the one that will accept offers).
|
|
120
121
|
// Peer with the highest Id closes its connection, and accepts remote peer's offer.
|
|
121
|
-
if (
|
|
122
|
+
if (remote.peerKey < this.localInfo.peerKey) {
|
|
122
123
|
// TODO(nf): Gets stuck when remote connection is aborted (i.e. closed tab).
|
|
123
124
|
log('close local connection', {
|
|
124
|
-
|
|
125
|
+
localPeer: this.localInfo,
|
|
125
126
|
topic: this.topic,
|
|
126
|
-
|
|
127
|
+
remotePeer: this.remoteInfo,
|
|
127
128
|
sessionId: this.connection?.sessionId,
|
|
128
129
|
});
|
|
129
130
|
|
|
@@ -137,7 +138,7 @@ export class Peer {
|
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
if (await this._callbacks.onOffer(
|
|
141
|
+
if (await this._callbacks.onOffer(remote)) {
|
|
141
142
|
if (!this.connection) {
|
|
142
143
|
// Connection might have been already established.
|
|
143
144
|
invariant(message.sessionId);
|
|
@@ -150,7 +151,7 @@ export class Peer {
|
|
|
150
151
|
await connection.openConnection();
|
|
151
152
|
} catch (err: any) {
|
|
152
153
|
if (!(err instanceof CancelledError)) {
|
|
153
|
-
log.info('connection error', { topic: this.topic, peerId: this.
|
|
154
|
+
log.info('connection error', { topic: this.topic, peerId: this.localInfo, remoteId: this.remoteInfo, err });
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
// Calls `onStateChange` with CLOSED state.
|
|
@@ -170,7 +171,7 @@ export class Peer {
|
|
|
170
171
|
invariant(!this.initiating, 'Initiation in progress.');
|
|
171
172
|
invariant(!this.connection, 'Already connected.');
|
|
172
173
|
const sessionId = PublicKey.random();
|
|
173
|
-
log('initiating...', {
|
|
174
|
+
log('initiating...', { local: this.localInfo, topic: this.topic, remote: this.remoteInfo, sessionId });
|
|
174
175
|
|
|
175
176
|
const connection = this._createConnection(true, sessionId);
|
|
176
177
|
this.initiating = true;
|
|
@@ -181,19 +182,19 @@ export class Peer {
|
|
|
181
182
|
connection.initiate();
|
|
182
183
|
|
|
183
184
|
answer = await this._signalMessaging.offer({
|
|
184
|
-
author: this.
|
|
185
|
-
recipient: this.
|
|
185
|
+
author: this.localInfo,
|
|
186
|
+
recipient: this.remoteInfo,
|
|
186
187
|
sessionId,
|
|
187
188
|
topic: this.topic,
|
|
188
189
|
data: { offer: {} },
|
|
189
190
|
});
|
|
190
|
-
log('received', { answer, topic: this.topic,
|
|
191
|
+
log('received', { answer, topic: this.topic, local: this.localInfo, remote: this.remoteInfo });
|
|
191
192
|
if (connection.state !== ConnectionState.INITIAL) {
|
|
192
193
|
log('ignoring response');
|
|
193
194
|
return;
|
|
194
195
|
}
|
|
195
196
|
} catch (err: any) {
|
|
196
|
-
log('initiation error: send offer', { err, topic: this.topic,
|
|
197
|
+
log('initiation error: send offer', { err, topic: this.topic, local: this.localInfo, remote: this.remoteInfo });
|
|
197
198
|
await connection.abort(err);
|
|
198
199
|
throw err;
|
|
199
200
|
} finally {
|
|
@@ -209,8 +210,8 @@ export class Peer {
|
|
|
209
210
|
log('initiation error: accept answer', {
|
|
210
211
|
err,
|
|
211
212
|
topic: this.topic,
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
local: this.localInfo,
|
|
214
|
+
remote: this.remoteInfo,
|
|
214
215
|
});
|
|
215
216
|
await connection.abort(err);
|
|
216
217
|
throw err;
|
|
@@ -226,8 +227,8 @@ export class Peer {
|
|
|
226
227
|
log('initiation error: open connection', {
|
|
227
228
|
err,
|
|
228
229
|
topic: this.topic,
|
|
229
|
-
|
|
230
|
-
|
|
230
|
+
local: this.localInfo,
|
|
231
|
+
remote: this.remoteInfo,
|
|
231
232
|
});
|
|
232
233
|
// TODO(nf): unsure when this will be called and the connection won't abort itself. but if it does fall through we should probably abort and not close.
|
|
233
234
|
log.warn('closing connection due to unhandled error on openConnection', { err });
|
|
@@ -246,8 +247,8 @@ export class Peer {
|
|
|
246
247
|
private _createConnection(initiator: boolean, sessionId: PublicKey) {
|
|
247
248
|
log('creating connection', {
|
|
248
249
|
topic: this.topic,
|
|
249
|
-
peerId: this.
|
|
250
|
-
remoteId: this.
|
|
250
|
+
peerId: this.localInfo,
|
|
251
|
+
remoteId: this.remoteInfo,
|
|
251
252
|
initiator,
|
|
252
253
|
sessionId,
|
|
253
254
|
});
|
|
@@ -255,13 +256,18 @@ export class Peer {
|
|
|
255
256
|
|
|
256
257
|
const connection = new Connection(
|
|
257
258
|
this.topic,
|
|
258
|
-
this.
|
|
259
|
-
this.
|
|
259
|
+
this.localInfo,
|
|
260
|
+
this.remoteInfo,
|
|
260
261
|
sessionId,
|
|
261
262
|
initiator,
|
|
262
263
|
this._signalMessaging,
|
|
263
264
|
// TODO(dmaretskyi): Init only when connection is established.
|
|
264
|
-
this._protocolProvider({
|
|
265
|
+
this._protocolProvider({
|
|
266
|
+
initiator,
|
|
267
|
+
localPeerId: PublicKey.from(this.localInfo.peerKey),
|
|
268
|
+
remotePeerId: PublicKey.from(this.remoteInfo.peerKey),
|
|
269
|
+
topic: this.topic,
|
|
270
|
+
}),
|
|
265
271
|
this._transportFactory,
|
|
266
272
|
{
|
|
267
273
|
onConnected: () => {
|
|
@@ -272,14 +278,14 @@ export class Peer {
|
|
|
272
278
|
this._connectionLimiter.doneConnecting(sessionId);
|
|
273
279
|
log.trace('dxos.mesh.connection.connected', {
|
|
274
280
|
topic: this.topic,
|
|
275
|
-
localPeerId: this.
|
|
276
|
-
remotePeerId: this.
|
|
281
|
+
localPeerId: this.localInfo,
|
|
282
|
+
remotePeerId: this.remoteInfo,
|
|
277
283
|
sessionId,
|
|
278
284
|
initiator,
|
|
279
285
|
});
|
|
280
286
|
},
|
|
281
287
|
onClosed: (err) => {
|
|
282
|
-
log('connection closed', { topic: this.topic, peerId: this.
|
|
288
|
+
log('connection closed', { topic: this.topic, peerId: this.localInfo, remoteId: this.remoteInfo, initiator });
|
|
283
289
|
|
|
284
290
|
// Make sure none of the connections are stuck in the limiter.
|
|
285
291
|
this._connectionLimiter.doneConnecting(sessionId);
|
|
@@ -288,8 +294,8 @@ export class Peer {
|
|
|
288
294
|
|
|
289
295
|
log.trace('dxos.mesh.connection.closed', {
|
|
290
296
|
topic: this.topic,
|
|
291
|
-
localPeerId: this.
|
|
292
|
-
remotePeerId: this.
|
|
297
|
+
localPeerId: this.localInfo,
|
|
298
|
+
remotePeerId: this.remoteInfo,
|
|
293
299
|
sessionId,
|
|
294
300
|
initiator,
|
|
295
301
|
});
|
|
@@ -328,15 +334,15 @@ export class Peer {
|
|
|
328
334
|
connection.errors.handle((err) => {
|
|
329
335
|
log.info('connection error, closing', {
|
|
330
336
|
topic: this.topic,
|
|
331
|
-
peerId: this.
|
|
332
|
-
remoteId: this.
|
|
337
|
+
peerId: this.localInfo,
|
|
338
|
+
remoteId: this.remoteInfo,
|
|
333
339
|
initiator,
|
|
334
340
|
err,
|
|
335
341
|
});
|
|
336
342
|
log.trace('dxos.mesh.connection.error', {
|
|
337
343
|
topic: this.topic,
|
|
338
|
-
localPeerId: this.
|
|
339
|
-
remotePeerId: this.
|
|
344
|
+
localPeerId: this.localInfo,
|
|
345
|
+
remotePeerId: this.remoteInfo,
|
|
340
346
|
sessionId,
|
|
341
347
|
initiator,
|
|
342
348
|
err,
|
|
@@ -358,13 +364,13 @@ export class Peer {
|
|
|
358
364
|
|
|
359
365
|
const connection = this.connection;
|
|
360
366
|
|
|
361
|
-
log('closing...', { peerId: this.
|
|
367
|
+
log('closing...', { peerId: this.remoteInfo, sessionId: connection.sessionId });
|
|
362
368
|
|
|
363
369
|
// Triggers `onStateChange` callback which will clean up the connection.
|
|
364
370
|
// Won't throw.
|
|
365
371
|
await connection.close(err);
|
|
366
372
|
|
|
367
|
-
log('closed', { peerId: this.
|
|
373
|
+
log('closed', { peerId: this.remoteInfo, sessionId: connection.sessionId });
|
|
368
374
|
}
|
|
369
375
|
|
|
370
376
|
async onSignal(message: SignalMessage) {
|
|
@@ -379,7 +385,7 @@ export class Peer {
|
|
|
379
385
|
@synchronized
|
|
380
386
|
async safeDestroy(reason?: Error) {
|
|
381
387
|
await this._ctx.dispose();
|
|
382
|
-
log('Destroying peer', { peerId: this.
|
|
388
|
+
log('Destroying peer', { peerId: this.remoteInfo, topic: this.topic });
|
|
383
389
|
|
|
384
390
|
// Won't throw.
|
|
385
391
|
await this?.connection?.close(reason);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Event, EventSubscriptions } from '@dxos/async';
|
|
6
6
|
import { PublicKey } from '@dxos/keys';
|
|
7
7
|
import { log } from '@dxos/log';
|
|
8
|
+
import { PeerInfoHash, type PeerInfo as MessagingPeer } from '@dxos/messaging';
|
|
8
9
|
import { ComplexMap } from '@dxos/util';
|
|
9
10
|
|
|
10
11
|
import { type ConnectionState } from './connection';
|
|
@@ -28,8 +29,8 @@ type Unsubscribe = () => void;
|
|
|
28
29
|
|
|
29
30
|
export class SwarmMapper {
|
|
30
31
|
private readonly _subscriptions = new EventSubscriptions();
|
|
31
|
-
private readonly _connectionSubscriptions = new ComplexMap<
|
|
32
|
-
private readonly _peers = new ComplexMap<
|
|
32
|
+
private readonly _connectionSubscriptions = new ComplexMap<MessagingPeer, Unsubscribe>(PeerInfoHash);
|
|
33
|
+
private readonly _peers = new ComplexMap<MessagingPeer, PeerInfo>(PeerInfoHash);
|
|
33
34
|
|
|
34
35
|
readonly mapUpdated = new Event<PeerInfo[]>();
|
|
35
36
|
|
|
@@ -42,7 +43,7 @@ export class SwarmMapper {
|
|
|
42
43
|
_swarm.connectionAdded.on((connection) => {
|
|
43
44
|
this._update();
|
|
44
45
|
this._connectionSubscriptions.set(
|
|
45
|
-
connection.
|
|
46
|
+
connection.remoteInfo,
|
|
46
47
|
connection.stateChanged.on(() => {
|
|
47
48
|
this._update();
|
|
48
49
|
}),
|
|
@@ -72,15 +73,15 @@ export class SwarmMapper {
|
|
|
72
73
|
log('updating swarm');
|
|
73
74
|
|
|
74
75
|
this._peers.clear();
|
|
75
|
-
this._peers.set(this._swarm.
|
|
76
|
+
this._peers.set(this._swarm.ownPeer, {
|
|
76
77
|
id: this._swarm.ownPeerId,
|
|
77
78
|
state: 'ME', // TODO(burdon): Enum (rename "local").
|
|
78
79
|
connections: [],
|
|
79
80
|
});
|
|
80
81
|
|
|
81
82
|
for (const connection of this._swarm.connections) {
|
|
82
|
-
this._peers.set(connection.
|
|
83
|
-
id: connection.
|
|
83
|
+
this._peers.set(connection.remoteInfo, {
|
|
84
|
+
id: PublicKey.from(connection.remoteInfo.peerKey),
|
|
84
85
|
state: connection.state,
|
|
85
86
|
connections: [this._swarm.ownPeerId],
|
|
86
87
|
});
|