@dxos/network-manager 0.6.11 → 0.6.12-main.5cc132e
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-XYSYUN63.mjs → chunk-NMDGRINN.mjs} +41 -26
- package/dist/lib/browser/{chunk-XYSYUN63.mjs.map → chunk-NMDGRINN.mjs.map} +3 -3
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/node/{chunk-4YAYC7WN.cjs → chunk-4K3JQNY3.cjs} +55 -40
- package/dist/lib/node/chunk-4K3JQNY3.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 +18 -18
- package/dist/lib/node-esm/chunk-X2RY5LSM.mjs +4319 -0
- package/dist/lib/node-esm/chunk-X2RY5LSM.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +59 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +290 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/signal/integration.node.test.d.ts +2 -0
- package/dist/types/src/signal/integration.node.test.d.ts.map +1 -0
- package/dist/types/src/signal/swarm-messenger.node.test.d.ts +2 -0
- package/dist/types/src/signal/swarm-messenger.node.test.d.ts.map +1 -0
- 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/dist/types/src/tests/tcp-transport.node.test.d.ts +2 -0
- package/dist/types/src/tests/tcp-transport.node.test.d.ts.map +1 -0
- package/dist/types/src/tests/utils.d.ts.map +1 -1
- package/dist/types/src/transport/libdatachannel-transport.d.ts.map +1 -1
- package/dist/types/src/transport/libdatachannel-transport.node.test.d.ts +2 -0
- package/dist/types/src/transport/libdatachannel-transport.node.test.d.ts.map +1 -0
- package/dist/types/src/transport/simplepeer-simple-peer.node.test.d.ts +2 -0
- package/dist/types/src/transport/simplepeer-simple-peer.node.test.d.ts.map +1 -0
- package/dist/types/src/transport/simplepeer-transport-proxy.node.test.d.ts +2 -0
- package/dist/types/src/transport/simplepeer-transport-proxy.node.test.d.ts.map +1 -0
- package/dist/types/src/transport/simplepeer-transport.d.ts.map +1 -1
- package/package.json +33 -34
- package/src/signal/ice.test.ts +1 -3
- package/src/signal/{integration.test.ts → integration.node.test.ts} +9 -15
- package/src/signal/{swarm-messenger.test.ts → swarm-messenger.node.test.ts} +12 -22
- package/src/swarm/connection-limiter.test.ts +3 -6
- package/src/swarm/connection.test.ts +2 -1
- package/src/swarm/swarm.test.ts +7 -8
- package/src/tests/basic-test-suite.ts +34 -33
- package/src/tests/memory-transport.test.ts +40 -42
- package/src/tests/property-test-suite.ts +21 -22
- package/src/tests/tcp-transport.node.test.ts +65 -0
- package/src/tests/utils.ts +3 -2
- package/src/tests/webrtc-transport.test.ts +9 -9
- package/src/transport/{libdatachannel-transport.test.ts → libdatachannel-transport.node.test.ts} +16 -25
- package/src/transport/libdatachannel-transport.ts +2 -6
- package/src/transport/memory-transport.test.ts +6 -5
- package/src/transport/simplepeer-simple-peer.node.test.ts +22 -0
- package/src/transport/{simplepeer-transport-proxy-test.ts → simplepeer-transport-proxy.node.test.ts} +14 -15
- package/src/transport/simplepeer-transport.test.ts +10 -14
- package/src/transport/simplepeer-transport.ts +1 -1
- package/src/typings.d.ts +8 -2
- package/dist/lib/node/chunk-4YAYC7WN.cjs.map +0 -7
- package/dist/types/src/signal/integration.test.d.ts +0 -2
- package/dist/types/src/signal/integration.test.d.ts.map +0 -1
- package/dist/types/src/signal/swarm-messenger.test.d.ts +0 -2
- package/dist/types/src/signal/swarm-messenger.test.d.ts.map +0 -1
- package/dist/types/src/tests/tcp-transport.test.d.ts +0 -2
- package/dist/types/src/tests/tcp-transport.test.d.ts.map +0 -1
- package/dist/types/src/transport/libdatachannel-transport.test.d.ts +0 -2
- package/dist/types/src/transport/libdatachannel-transport.test.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-simple-peer.d.ts +0 -2
- package/dist/types/src/transport/simplepeer-simple-peer.d.ts.map +0 -1
- package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts +0 -2
- package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts.map +0 -1
- package/src/globals.d.ts +0 -7
- package/src/tests/tcp-transport.test.ts +0 -67
- package/src/transport/simplepeer-simple-peer.ts +0 -26
|
@@ -0,0 +1,4319 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// packages/core/mesh/network-manager/src/swarm/connection.ts
|
|
9
|
+
import { DeferredTask, Event, sleep, scheduleTask, scheduleTaskInterval, synchronized, Trigger } from "@dxos/async";
|
|
10
|
+
import { Context, cancelWithContext, ContextDisposedError } from "@dxos/context";
|
|
11
|
+
import { ErrorStream } from "@dxos/debug";
|
|
12
|
+
import { invariant } from "@dxos/invariant";
|
|
13
|
+
import { PublicKey } from "@dxos/keys";
|
|
14
|
+
import { log, logInfo } from "@dxos/log";
|
|
15
|
+
import { CancelledError, ProtocolError, ConnectionResetError, ConnectivityError, TimeoutError, UnknownProtocolError, trace } from "@dxos/protocols";
|
|
16
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
17
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
18
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
19
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
20
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
21
|
+
}
|
|
22
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/connection.ts";
|
|
23
|
+
var STARTING_SIGNALLING_DELAY = 10;
|
|
24
|
+
var TRANSPORT_CONNECTION_TIMEOUT = 1e4;
|
|
25
|
+
var TRANSPORT_STATS_INTERVAL = 5e3;
|
|
26
|
+
var MAX_SIGNALLING_DELAY = 300;
|
|
27
|
+
var ConnectionState;
|
|
28
|
+
(function(ConnectionState5) {
|
|
29
|
+
ConnectionState5["CREATED"] = "CREATED";
|
|
30
|
+
ConnectionState5["INITIAL"] = "INITIAL";
|
|
31
|
+
ConnectionState5["CONNECTING"] = "CONNECTING";
|
|
32
|
+
ConnectionState5["CONNECTED"] = "CONNECTED";
|
|
33
|
+
ConnectionState5["CLOSING"] = "CLOSING";
|
|
34
|
+
ConnectionState5["CLOSED"] = "CLOSED";
|
|
35
|
+
ConnectionState5["ABORTING"] = "ABORTING";
|
|
36
|
+
ConnectionState5["ABORTED"] = "ABORTED";
|
|
37
|
+
})(ConnectionState || (ConnectionState = {}));
|
|
38
|
+
var Connection = class {
|
|
39
|
+
constructor(topic, localInfo, remoteInfo, sessionId, initiator, _signalMessaging, _protocol, _transportFactory, _callbacks) {
|
|
40
|
+
this.topic = topic;
|
|
41
|
+
this.localInfo = localInfo;
|
|
42
|
+
this.remoteInfo = remoteInfo;
|
|
43
|
+
this.sessionId = sessionId;
|
|
44
|
+
this.initiator = initiator;
|
|
45
|
+
this._signalMessaging = _signalMessaging;
|
|
46
|
+
this._protocol = _protocol;
|
|
47
|
+
this._transportFactory = _transportFactory;
|
|
48
|
+
this._callbacks = _callbacks;
|
|
49
|
+
this._ctx = new Context(void 0, {
|
|
50
|
+
F: __dxlog_file,
|
|
51
|
+
L: 101
|
|
52
|
+
});
|
|
53
|
+
this.connectedTimeoutContext = new Context(void 0, {
|
|
54
|
+
F: __dxlog_file,
|
|
55
|
+
L: 102
|
|
56
|
+
});
|
|
57
|
+
this._protocolClosed = new Trigger();
|
|
58
|
+
this._transportClosed = new Trigger();
|
|
59
|
+
this._state = "CREATED";
|
|
60
|
+
this._incomingSignalBuffer = [];
|
|
61
|
+
this._outgoingSignalBuffer = [];
|
|
62
|
+
this.stateChanged = new Event();
|
|
63
|
+
this.errors = new ErrorStream();
|
|
64
|
+
this._instanceId = PublicKey.random().toHex();
|
|
65
|
+
this.transportStats = new Event();
|
|
66
|
+
this._signalSendTask = new DeferredTask(this._ctx, async () => {
|
|
67
|
+
await this._flushSignalBuffer();
|
|
68
|
+
});
|
|
69
|
+
this._signallingDelay = STARTING_SIGNALLING_DELAY;
|
|
70
|
+
log.trace("dxos.mesh.connection.construct", {
|
|
71
|
+
sessionId: this.sessionId,
|
|
72
|
+
topic: this.topic,
|
|
73
|
+
localPeer: this.localInfo,
|
|
74
|
+
remotePeer: this.remoteInfo,
|
|
75
|
+
initiator: this.initiator
|
|
76
|
+
}, {
|
|
77
|
+
F: __dxlog_file,
|
|
78
|
+
L: 138,
|
|
79
|
+
S: this,
|
|
80
|
+
C: (f, a) => f(...a)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
get sessionIdString() {
|
|
84
|
+
return this.sessionId.truncate();
|
|
85
|
+
}
|
|
86
|
+
get state() {
|
|
87
|
+
return this._state;
|
|
88
|
+
}
|
|
89
|
+
get transport() {
|
|
90
|
+
return this._transport;
|
|
91
|
+
}
|
|
92
|
+
get protocol() {
|
|
93
|
+
return this._protocol;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create an underlying transport and prepares it for the connection.
|
|
97
|
+
*/
|
|
98
|
+
async openConnection() {
|
|
99
|
+
invariant(this._state === "INITIAL", "Invalid state.", {
|
|
100
|
+
F: __dxlog_file,
|
|
101
|
+
L: 168,
|
|
102
|
+
S: this,
|
|
103
|
+
A: [
|
|
104
|
+
"this._state === ConnectionState.INITIAL",
|
|
105
|
+
"'Invalid state.'"
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
log.trace("dxos.mesh.connection.open-connection", trace.begin({
|
|
109
|
+
id: this._instanceId
|
|
110
|
+
}), {
|
|
111
|
+
F: __dxlog_file,
|
|
112
|
+
L: 169,
|
|
113
|
+
S: this,
|
|
114
|
+
C: (f, a) => f(...a)
|
|
115
|
+
});
|
|
116
|
+
log.trace("dxos.mesh.connection.open", {
|
|
117
|
+
sessionId: this.sessionId,
|
|
118
|
+
topic: this.topic,
|
|
119
|
+
localPeerId: this.localInfo,
|
|
120
|
+
remotePeerId: this.remoteInfo,
|
|
121
|
+
initiator: this.initiator
|
|
122
|
+
}, {
|
|
123
|
+
F: __dxlog_file,
|
|
124
|
+
L: 170,
|
|
125
|
+
S: this,
|
|
126
|
+
C: (f, a) => f(...a)
|
|
127
|
+
});
|
|
128
|
+
this._changeState("CONNECTING");
|
|
129
|
+
this._protocol.open(this.sessionId).catch((err) => {
|
|
130
|
+
this.errors.raise(err);
|
|
131
|
+
});
|
|
132
|
+
this._protocol.stream.on("close", () => {
|
|
133
|
+
log("protocol stream closed", void 0, {
|
|
134
|
+
F: __dxlog_file,
|
|
135
|
+
L: 187,
|
|
136
|
+
S: this,
|
|
137
|
+
C: (f, a) => f(...a)
|
|
138
|
+
});
|
|
139
|
+
this._protocolClosed.wake();
|
|
140
|
+
this.close(new ProtocolError("protocol stream closed")).catch((err) => this.errors.raise(err));
|
|
141
|
+
});
|
|
142
|
+
scheduleTask(this.connectedTimeoutContext, async () => {
|
|
143
|
+
log.info(`timeout waiting ${TRANSPORT_CONNECTION_TIMEOUT / 1e3}s for transport to connect, aborting`, void 0, {
|
|
144
|
+
F: __dxlog_file,
|
|
145
|
+
L: 195,
|
|
146
|
+
S: this,
|
|
147
|
+
C: (f, a) => f(...a)
|
|
148
|
+
});
|
|
149
|
+
await this.abort(new TimeoutError(`${TRANSPORT_CONNECTION_TIMEOUT / 1e3}s for transport to connect`)).catch((err) => this.errors.raise(err));
|
|
150
|
+
}, TRANSPORT_CONNECTION_TIMEOUT);
|
|
151
|
+
invariant(!this._transport, void 0, {
|
|
152
|
+
F: __dxlog_file,
|
|
153
|
+
L: 203,
|
|
154
|
+
S: this,
|
|
155
|
+
A: [
|
|
156
|
+
"!this._transport",
|
|
157
|
+
""
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
this._transport = this._transportFactory.createTransport({
|
|
161
|
+
initiator: this.initiator,
|
|
162
|
+
stream: this._protocol.stream,
|
|
163
|
+
sendSignal: async (signal) => this._sendSignal(signal),
|
|
164
|
+
sessionId: this.sessionId
|
|
165
|
+
});
|
|
166
|
+
await this._transport.open();
|
|
167
|
+
this._transport.connected.once(async () => {
|
|
168
|
+
this._changeState("CONNECTED");
|
|
169
|
+
await this.connectedTimeoutContext.dispose();
|
|
170
|
+
this._callbacks?.onConnected?.();
|
|
171
|
+
scheduleTaskInterval(this._ctx, async () => this._emitTransportStats(), TRANSPORT_STATS_INTERVAL);
|
|
172
|
+
});
|
|
173
|
+
this._transport.closed.once(() => {
|
|
174
|
+
this._transport = void 0;
|
|
175
|
+
this._transportClosed.wake();
|
|
176
|
+
log("abort triggered by transport close", void 0, {
|
|
177
|
+
F: __dxlog_file,
|
|
178
|
+
L: 224,
|
|
179
|
+
S: this,
|
|
180
|
+
C: (f, a) => f(...a)
|
|
181
|
+
});
|
|
182
|
+
this.abort().catch((err) => this.errors.raise(err));
|
|
183
|
+
});
|
|
184
|
+
this._transport.errors.handle(async (err) => {
|
|
185
|
+
log("transport error:", {
|
|
186
|
+
err
|
|
187
|
+
}, {
|
|
188
|
+
F: __dxlog_file,
|
|
189
|
+
L: 229,
|
|
190
|
+
S: this,
|
|
191
|
+
C: (f, a) => f(...a)
|
|
192
|
+
});
|
|
193
|
+
if (!this.closeReason) {
|
|
194
|
+
this.closeReason = err?.message;
|
|
195
|
+
}
|
|
196
|
+
if (err instanceof ConnectionResetError) {
|
|
197
|
+
log.info("aborting due to transport ConnectionResetError", void 0, {
|
|
198
|
+
F: __dxlog_file,
|
|
199
|
+
L: 236,
|
|
200
|
+
S: this,
|
|
201
|
+
C: (f, a) => f(...a)
|
|
202
|
+
});
|
|
203
|
+
this.abort().catch((err2) => this.errors.raise(err2));
|
|
204
|
+
} else if (err instanceof ConnectivityError) {
|
|
205
|
+
log.info("aborting due to transport ConnectivityError", void 0, {
|
|
206
|
+
F: __dxlog_file,
|
|
207
|
+
L: 239,
|
|
208
|
+
S: this,
|
|
209
|
+
C: (f, a) => f(...a)
|
|
210
|
+
});
|
|
211
|
+
this.abort().catch((err2) => this.errors.raise(err2));
|
|
212
|
+
} else if (err instanceof UnknownProtocolError) {
|
|
213
|
+
log.warn("unsure what to do with UnknownProtocolError, will keep on truckin", {
|
|
214
|
+
err
|
|
215
|
+
}, {
|
|
216
|
+
F: __dxlog_file,
|
|
217
|
+
L: 242,
|
|
218
|
+
S: this,
|
|
219
|
+
C: (f, a) => f(...a)
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (this._state !== "CLOSED" && this._state !== "CLOSING") {
|
|
223
|
+
await this.connectedTimeoutContext.dispose();
|
|
224
|
+
this.errors.raise(err);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
for (const signal of this._incomingSignalBuffer) {
|
|
228
|
+
void this._transport.onSignal(signal);
|
|
229
|
+
}
|
|
230
|
+
this._incomingSignalBuffer = [];
|
|
231
|
+
log.trace("dxos.mesh.connection.open-connection", trace.end({
|
|
232
|
+
id: this._instanceId
|
|
233
|
+
}), {
|
|
234
|
+
F: __dxlog_file,
|
|
235
|
+
L: 258,
|
|
236
|
+
S: this,
|
|
237
|
+
C: (f, a) => f(...a)
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async abort(err) {
|
|
241
|
+
log("aborting...", {
|
|
242
|
+
err
|
|
243
|
+
}, {
|
|
244
|
+
F: __dxlog_file,
|
|
245
|
+
L: 265,
|
|
246
|
+
S: this,
|
|
247
|
+
C: (f, a) => f(...a)
|
|
248
|
+
});
|
|
249
|
+
if (this._state === "CLOSED" || this._state === "ABORTED") {
|
|
250
|
+
log(`abort ignored: already ${this._state}`, this.closeReason, {
|
|
251
|
+
F: __dxlog_file,
|
|
252
|
+
L: 267,
|
|
253
|
+
S: this,
|
|
254
|
+
C: (f, a) => f(...a)
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
await this.connectedTimeoutContext.dispose();
|
|
259
|
+
this._changeState("ABORTING");
|
|
260
|
+
if (!this.closeReason) {
|
|
261
|
+
this.closeReason = err?.message;
|
|
262
|
+
}
|
|
263
|
+
await this._ctx.dispose();
|
|
264
|
+
try {
|
|
265
|
+
await this._closeProtocol({
|
|
266
|
+
abort: true
|
|
267
|
+
});
|
|
268
|
+
} catch (err2) {
|
|
269
|
+
log.catch(err2, void 0, {
|
|
270
|
+
F: __dxlog_file,
|
|
271
|
+
L: 283,
|
|
272
|
+
S: this,
|
|
273
|
+
C: (f, a) => f(...a)
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
await this._closeTransport();
|
|
278
|
+
} catch (err2) {
|
|
279
|
+
log.catch(err2, void 0, {
|
|
280
|
+
F: __dxlog_file,
|
|
281
|
+
L: 290,
|
|
282
|
+
S: this,
|
|
283
|
+
C: (f, a) => f(...a)
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
this._callbacks?.onClosed?.(err);
|
|
288
|
+
} catch (err2) {
|
|
289
|
+
log.catch(err2, void 0, {
|
|
290
|
+
F: __dxlog_file,
|
|
291
|
+
L: 296,
|
|
292
|
+
S: this,
|
|
293
|
+
C: (f, a) => f(...a)
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
this._changeState("ABORTED");
|
|
297
|
+
}
|
|
298
|
+
async close(err) {
|
|
299
|
+
if (!this.closeReason) {
|
|
300
|
+
this.closeReason = err?.message;
|
|
301
|
+
} else {
|
|
302
|
+
this.closeReason += `; ${err?.message}`;
|
|
303
|
+
}
|
|
304
|
+
if (this._state === "CLOSED" || this._state === "ABORTING" || this._state === "ABORTED") {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const lastState = this._state;
|
|
308
|
+
this._changeState("CLOSING");
|
|
309
|
+
await this.connectedTimeoutContext.dispose();
|
|
310
|
+
await this._ctx.dispose();
|
|
311
|
+
log("closing...", {
|
|
312
|
+
peerId: this.localInfo
|
|
313
|
+
}, {
|
|
314
|
+
F: __dxlog_file,
|
|
315
|
+
L: 321,
|
|
316
|
+
S: this,
|
|
317
|
+
C: (f, a) => f(...a)
|
|
318
|
+
});
|
|
319
|
+
let abortProtocol = false;
|
|
320
|
+
if (lastState !== "CONNECTED") {
|
|
321
|
+
log(`graceful close requested when we were in ${lastState} state? aborting`, void 0, {
|
|
322
|
+
F: __dxlog_file,
|
|
323
|
+
L: 325,
|
|
324
|
+
S: this,
|
|
325
|
+
C: (f, a) => f(...a)
|
|
326
|
+
});
|
|
327
|
+
abortProtocol = true;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
await this._closeProtocol({
|
|
331
|
+
abort: abortProtocol
|
|
332
|
+
});
|
|
333
|
+
} catch (err2) {
|
|
334
|
+
log.catch(err2, void 0, {
|
|
335
|
+
F: __dxlog_file,
|
|
336
|
+
L: 331,
|
|
337
|
+
S: this,
|
|
338
|
+
C: (f, a) => f(...a)
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
await this._closeTransport();
|
|
343
|
+
} catch (err2) {
|
|
344
|
+
log.catch(err2, void 0, {
|
|
345
|
+
F: __dxlog_file,
|
|
346
|
+
L: 337,
|
|
347
|
+
S: this,
|
|
348
|
+
C: (f, a) => f(...a)
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
log("closed", {
|
|
352
|
+
peerId: this.localInfo
|
|
353
|
+
}, {
|
|
354
|
+
F: __dxlog_file,
|
|
355
|
+
L: 340,
|
|
356
|
+
S: this,
|
|
357
|
+
C: (f, a) => f(...a)
|
|
358
|
+
});
|
|
359
|
+
this._changeState("CLOSED");
|
|
360
|
+
this._callbacks?.onClosed?.(err);
|
|
361
|
+
}
|
|
362
|
+
async _closeProtocol(options) {
|
|
363
|
+
log("closing protocol", options, {
|
|
364
|
+
F: __dxlog_file,
|
|
365
|
+
L: 346,
|
|
366
|
+
S: this,
|
|
367
|
+
C: (f, a) => f(...a)
|
|
368
|
+
});
|
|
369
|
+
await Promise.race([
|
|
370
|
+
options?.abort ? this._protocol.abort() : this._protocol.close(),
|
|
371
|
+
this._protocolClosed.wait()
|
|
372
|
+
]);
|
|
373
|
+
log("protocol closed", options, {
|
|
374
|
+
F: __dxlog_file,
|
|
375
|
+
L: 348,
|
|
376
|
+
S: this,
|
|
377
|
+
C: (f, a) => f(...a)
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
async _closeTransport() {
|
|
381
|
+
log("closing transport", void 0, {
|
|
382
|
+
F: __dxlog_file,
|
|
383
|
+
L: 352,
|
|
384
|
+
S: this,
|
|
385
|
+
C: (f, a) => f(...a)
|
|
386
|
+
});
|
|
387
|
+
await Promise.race([
|
|
388
|
+
this._transport?.close(),
|
|
389
|
+
this._transportClosed.wait()
|
|
390
|
+
]);
|
|
391
|
+
log("transport closed", void 0, {
|
|
392
|
+
F: __dxlog_file,
|
|
393
|
+
L: 354,
|
|
394
|
+
S: this,
|
|
395
|
+
C: (f, a) => f(...a)
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
_sendSignal(signal) {
|
|
399
|
+
this._outgoingSignalBuffer.push(signal);
|
|
400
|
+
this._signalSendTask.schedule();
|
|
401
|
+
}
|
|
402
|
+
async _flushSignalBuffer() {
|
|
403
|
+
if (this._outgoingSignalBuffer.length === 0) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
if (process.env.NODE_ENV !== "test") {
|
|
408
|
+
await cancelWithContext(this._ctx, sleep(this._signallingDelay));
|
|
409
|
+
this._signallingDelay = Math.min(this._signallingDelay * 2, MAX_SIGNALLING_DELAY);
|
|
410
|
+
}
|
|
411
|
+
const signals = [
|
|
412
|
+
...this._outgoingSignalBuffer
|
|
413
|
+
];
|
|
414
|
+
this._outgoingSignalBuffer.length = 0;
|
|
415
|
+
await this._signalMessaging.signal({
|
|
416
|
+
author: this.localInfo,
|
|
417
|
+
recipient: this.remoteInfo,
|
|
418
|
+
sessionId: this.sessionId,
|
|
419
|
+
topic: this.topic,
|
|
420
|
+
data: {
|
|
421
|
+
signalBatch: {
|
|
422
|
+
signals
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
} catch (err) {
|
|
427
|
+
if (err instanceof CancelledError || err instanceof ContextDisposedError || err instanceof Error && err.message?.includes("CANCELLED")) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
log.info("signal message failed to deliver", {
|
|
431
|
+
err
|
|
432
|
+
}, {
|
|
433
|
+
F: __dxlog_file,
|
|
434
|
+
L: 394,
|
|
435
|
+
S: this,
|
|
436
|
+
C: (f, a) => f(...a)
|
|
437
|
+
});
|
|
438
|
+
await this.close(new ConnectivityError("signal message failed to deliver", err));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Receive a signal from the remote peer.
|
|
443
|
+
*/
|
|
444
|
+
async signal(msg) {
|
|
445
|
+
invariant(msg.sessionId, void 0, {
|
|
446
|
+
F: __dxlog_file,
|
|
447
|
+
L: 403,
|
|
448
|
+
S: this,
|
|
449
|
+
A: [
|
|
450
|
+
"msg.sessionId",
|
|
451
|
+
""
|
|
452
|
+
]
|
|
453
|
+
});
|
|
454
|
+
if (!msg.sessionId.equals(this.sessionId)) {
|
|
455
|
+
log("dropping signal for incorrect session id", void 0, {
|
|
456
|
+
F: __dxlog_file,
|
|
457
|
+
L: 405,
|
|
458
|
+
S: this,
|
|
459
|
+
C: (f, a) => f(...a)
|
|
460
|
+
});
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
invariant(msg.data.signal || msg.data.signalBatch, void 0, {
|
|
464
|
+
F: __dxlog_file,
|
|
465
|
+
L: 408,
|
|
466
|
+
S: this,
|
|
467
|
+
A: [
|
|
468
|
+
"msg.data.signal || msg.data.signalBatch",
|
|
469
|
+
""
|
|
470
|
+
]
|
|
471
|
+
});
|
|
472
|
+
invariant(msg.author.peerKey === this.remoteInfo.peerKey, void 0, {
|
|
473
|
+
F: __dxlog_file,
|
|
474
|
+
L: 409,
|
|
475
|
+
S: this,
|
|
476
|
+
A: [
|
|
477
|
+
"msg.author.peerKey === this.remoteInfo.peerKey",
|
|
478
|
+
""
|
|
479
|
+
]
|
|
480
|
+
});
|
|
481
|
+
invariant(msg.recipient.peerKey === this.localInfo.peerKey, void 0, {
|
|
482
|
+
F: __dxlog_file,
|
|
483
|
+
L: 410,
|
|
484
|
+
S: this,
|
|
485
|
+
A: [
|
|
486
|
+
"msg.recipient.peerKey === this.localInfo.peerKey",
|
|
487
|
+
""
|
|
488
|
+
]
|
|
489
|
+
});
|
|
490
|
+
const signals = msg.data.signalBatch ? msg.data.signalBatch.signals ?? [] : [
|
|
491
|
+
msg.data.signal
|
|
492
|
+
];
|
|
493
|
+
for (const signal of signals) {
|
|
494
|
+
if (!signal) {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if ([
|
|
498
|
+
"CREATED",
|
|
499
|
+
"INITIAL"
|
|
500
|
+
].includes(this.state)) {
|
|
501
|
+
log("buffered signal", {
|
|
502
|
+
peerId: this.localInfo,
|
|
503
|
+
remoteId: this.remoteInfo,
|
|
504
|
+
msg: msg.data
|
|
505
|
+
}, {
|
|
506
|
+
F: __dxlog_file,
|
|
507
|
+
L: 419,
|
|
508
|
+
S: this,
|
|
509
|
+
C: (f, a) => f(...a)
|
|
510
|
+
});
|
|
511
|
+
this._incomingSignalBuffer.push(signal);
|
|
512
|
+
} else {
|
|
513
|
+
invariant(this._transport, "Connection not ready to accept signals.", {
|
|
514
|
+
F: __dxlog_file,
|
|
515
|
+
L: 422,
|
|
516
|
+
S: this,
|
|
517
|
+
A: [
|
|
518
|
+
"this._transport",
|
|
519
|
+
"'Connection not ready to accept signals.'"
|
|
520
|
+
]
|
|
521
|
+
});
|
|
522
|
+
log("received signal", {
|
|
523
|
+
peerId: this.localInfo,
|
|
524
|
+
remoteId: this.remoteInfo,
|
|
525
|
+
msg: msg.data
|
|
526
|
+
}, {
|
|
527
|
+
F: __dxlog_file,
|
|
528
|
+
L: 423,
|
|
529
|
+
S: this,
|
|
530
|
+
C: (f, a) => f(...a)
|
|
531
|
+
});
|
|
532
|
+
await this._transport.onSignal(signal);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
initiate() {
|
|
537
|
+
this._changeState("INITIAL");
|
|
538
|
+
}
|
|
539
|
+
_changeState(state) {
|
|
540
|
+
log("stateChanged", {
|
|
541
|
+
from: this._state,
|
|
542
|
+
to: state,
|
|
543
|
+
peerId: this.localInfo
|
|
544
|
+
}, {
|
|
545
|
+
F: __dxlog_file,
|
|
546
|
+
L: 434,
|
|
547
|
+
S: this,
|
|
548
|
+
C: (f, a) => f(...a)
|
|
549
|
+
});
|
|
550
|
+
invariant(state !== this._state, "Already in this state.", {
|
|
551
|
+
F: __dxlog_file,
|
|
552
|
+
L: 435,
|
|
553
|
+
S: this,
|
|
554
|
+
A: [
|
|
555
|
+
"state !== this._state",
|
|
556
|
+
"'Already in this state.'"
|
|
557
|
+
]
|
|
558
|
+
});
|
|
559
|
+
this._state = state;
|
|
560
|
+
this.stateChanged.emit(state);
|
|
561
|
+
}
|
|
562
|
+
async _emitTransportStats() {
|
|
563
|
+
const stats = await this.transport?.getStats();
|
|
564
|
+
if (stats) {
|
|
565
|
+
this.transportStats.emit(stats);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
_ts_decorate([
|
|
570
|
+
logInfo
|
|
571
|
+
], Connection.prototype, "sessionIdString", null);
|
|
572
|
+
_ts_decorate([
|
|
573
|
+
synchronized
|
|
574
|
+
], Connection.prototype, "abort", null);
|
|
575
|
+
_ts_decorate([
|
|
576
|
+
synchronized
|
|
577
|
+
], Connection.prototype, "close", null);
|
|
578
|
+
|
|
579
|
+
// packages/core/mesh/network-manager/src/signal/ice.ts
|
|
580
|
+
import { asyncTimeout } from "@dxos/async";
|
|
581
|
+
import { log as log2 } from "@dxos/log";
|
|
582
|
+
import { isNotNullOrUndefined } from "@dxos/util";
|
|
583
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/signal/ice.ts";
|
|
584
|
+
var createIceProvider = (iceProviders) => {
|
|
585
|
+
let cachedIceServers;
|
|
586
|
+
return {
|
|
587
|
+
getIceServers: async () => {
|
|
588
|
+
if (cachedIceServers) {
|
|
589
|
+
return cachedIceServers;
|
|
590
|
+
}
|
|
591
|
+
cachedIceServers = (await Promise.all(iceProviders.map(({ urls }) => asyncTimeout(fetch(urls, {
|
|
592
|
+
method: "GET"
|
|
593
|
+
}), 1e4).then((response) => response.json()).catch((err) => log2.error("Failed to fetch ICE servers from provider", {
|
|
594
|
+
urls,
|
|
595
|
+
err
|
|
596
|
+
}, {
|
|
597
|
+
F: __dxlog_file2,
|
|
598
|
+
L: 27,
|
|
599
|
+
S: void 0,
|
|
600
|
+
C: (f, a) => f(...a)
|
|
601
|
+
}))))).filter(isNotNullOrUndefined).map(({ iceServers }) => iceServers).flat();
|
|
602
|
+
return cachedIceServers;
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// packages/core/mesh/network-manager/src/signal/swarm-messenger.ts
|
|
608
|
+
import { Context as Context2 } from "@dxos/context";
|
|
609
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
610
|
+
import { PublicKey as PublicKey2 } from "@dxos/keys";
|
|
611
|
+
import { log as log3 } from "@dxos/log";
|
|
612
|
+
import { TimeoutError as TimeoutError2 } from "@dxos/protocols";
|
|
613
|
+
import { schema } from "@dxos/protocols/proto";
|
|
614
|
+
import { ComplexMap } from "@dxos/util";
|
|
615
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/signal/swarm-messenger.ts";
|
|
616
|
+
var SwarmMessage = schema.getCodecForType("dxos.mesh.swarm.SwarmMessage");
|
|
617
|
+
var SwarmMessenger = class {
|
|
618
|
+
constructor({ sendMessage, onSignal, onOffer, topic }) {
|
|
619
|
+
this._ctx = new Context2(void 0, {
|
|
620
|
+
F: __dxlog_file3,
|
|
621
|
+
L: 35
|
|
622
|
+
});
|
|
623
|
+
this._offerRecords = new ComplexMap((key) => key.toHex());
|
|
624
|
+
this._sendMessage = sendMessage;
|
|
625
|
+
this._onSignal = onSignal;
|
|
626
|
+
this._onOffer = onOffer;
|
|
627
|
+
this._topic = topic;
|
|
628
|
+
}
|
|
629
|
+
async receiveMessage({ author, recipient, payload }) {
|
|
630
|
+
if (payload.type_url !== "dxos.mesh.swarm.SwarmMessage") {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const message = SwarmMessage.decode(payload.value);
|
|
634
|
+
if (!this._topic.equals(message.topic)) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
log3("received", {
|
|
638
|
+
from: author,
|
|
639
|
+
to: recipient,
|
|
640
|
+
msg: message
|
|
641
|
+
}, {
|
|
642
|
+
F: __dxlog_file3,
|
|
643
|
+
L: 71,
|
|
644
|
+
S: this,
|
|
645
|
+
C: (f, a) => f(...a)
|
|
646
|
+
});
|
|
647
|
+
if (message.data?.offer) {
|
|
648
|
+
await this._handleOffer({
|
|
649
|
+
author,
|
|
650
|
+
recipient,
|
|
651
|
+
message
|
|
652
|
+
});
|
|
653
|
+
} else if (message.data?.answer) {
|
|
654
|
+
await this._resolveAnswers(message);
|
|
655
|
+
} else if (message.data?.signal) {
|
|
656
|
+
await this._handleSignal({
|
|
657
|
+
author,
|
|
658
|
+
recipient,
|
|
659
|
+
message
|
|
660
|
+
});
|
|
661
|
+
} else if (message.data?.signalBatch) {
|
|
662
|
+
await this._handleSignal({
|
|
663
|
+
author,
|
|
664
|
+
recipient,
|
|
665
|
+
message
|
|
666
|
+
});
|
|
667
|
+
} else {
|
|
668
|
+
log3.warn("unknown message", {
|
|
669
|
+
message
|
|
670
|
+
}, {
|
|
671
|
+
F: __dxlog_file3,
|
|
672
|
+
L: 82,
|
|
673
|
+
S: this,
|
|
674
|
+
C: (f, a) => f(...a)
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async signal(message) {
|
|
679
|
+
invariant2(message.data?.signal || message.data?.signalBatch, "Invalid message", {
|
|
680
|
+
F: __dxlog_file3,
|
|
681
|
+
L: 87,
|
|
682
|
+
S: this,
|
|
683
|
+
A: [
|
|
684
|
+
"message.data?.signal || message.data?.signalBatch",
|
|
685
|
+
"'Invalid message'"
|
|
686
|
+
]
|
|
687
|
+
});
|
|
688
|
+
await this._sendReliableMessage({
|
|
689
|
+
author: message.author,
|
|
690
|
+
recipient: message.recipient,
|
|
691
|
+
message
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
async offer(message) {
|
|
695
|
+
const networkMessage = {
|
|
696
|
+
...message,
|
|
697
|
+
messageId: PublicKey2.random()
|
|
698
|
+
};
|
|
699
|
+
return new Promise((resolve, reject) => {
|
|
700
|
+
this._offerRecords.set(networkMessage.messageId, {
|
|
701
|
+
resolve
|
|
702
|
+
});
|
|
703
|
+
this._sendReliableMessage({
|
|
704
|
+
author: message.author,
|
|
705
|
+
recipient: message.recipient,
|
|
706
|
+
message: networkMessage
|
|
707
|
+
}).catch((err) => reject(err));
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
async _sendReliableMessage({ author, recipient, message }) {
|
|
711
|
+
const networkMessage = {
|
|
712
|
+
...message,
|
|
713
|
+
// Setting unique message_id if it not specified yet.
|
|
714
|
+
messageId: message.messageId ?? PublicKey2.random()
|
|
715
|
+
};
|
|
716
|
+
log3("sending", {
|
|
717
|
+
from: author,
|
|
718
|
+
to: recipient,
|
|
719
|
+
msg: networkMessage
|
|
720
|
+
}, {
|
|
721
|
+
F: __dxlog_file3,
|
|
722
|
+
L: 125,
|
|
723
|
+
S: this,
|
|
724
|
+
C: (f, a) => f(...a)
|
|
725
|
+
});
|
|
726
|
+
await this._sendMessage({
|
|
727
|
+
author,
|
|
728
|
+
recipient,
|
|
729
|
+
payload: {
|
|
730
|
+
type_url: "dxos.mesh.swarm.SwarmMessage",
|
|
731
|
+
value: SwarmMessage.encode(networkMessage)
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
async _resolveAnswers(message) {
|
|
736
|
+
invariant2(message.data?.answer?.offerMessageId, "No offerMessageId", {
|
|
737
|
+
F: __dxlog_file3,
|
|
738
|
+
L: 137,
|
|
739
|
+
S: this,
|
|
740
|
+
A: [
|
|
741
|
+
"message.data?.answer?.offerMessageId",
|
|
742
|
+
"'No offerMessageId'"
|
|
743
|
+
]
|
|
744
|
+
});
|
|
745
|
+
const offerRecord = this._offerRecords.get(message.data.answer.offerMessageId);
|
|
746
|
+
if (offerRecord) {
|
|
747
|
+
this._offerRecords.delete(message.data.answer.offerMessageId);
|
|
748
|
+
invariant2(message.data?.answer, "No answer", {
|
|
749
|
+
F: __dxlog_file3,
|
|
750
|
+
L: 141,
|
|
751
|
+
S: this,
|
|
752
|
+
A: [
|
|
753
|
+
"message.data?.answer",
|
|
754
|
+
"'No answer'"
|
|
755
|
+
]
|
|
756
|
+
});
|
|
757
|
+
log3("resolving", {
|
|
758
|
+
answer: message.data.answer
|
|
759
|
+
}, {
|
|
760
|
+
F: __dxlog_file3,
|
|
761
|
+
L: 142,
|
|
762
|
+
S: this,
|
|
763
|
+
C: (f, a) => f(...a)
|
|
764
|
+
});
|
|
765
|
+
offerRecord.resolve(message.data.answer);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
async _handleOffer({ author, recipient, message }) {
|
|
769
|
+
invariant2(message.data.offer, "No offer", {
|
|
770
|
+
F: __dxlog_file3,
|
|
771
|
+
L: 156,
|
|
772
|
+
S: this,
|
|
773
|
+
A: [
|
|
774
|
+
"message.data.offer",
|
|
775
|
+
"'No offer'"
|
|
776
|
+
]
|
|
777
|
+
});
|
|
778
|
+
const offerMessage = {
|
|
779
|
+
author,
|
|
780
|
+
recipient,
|
|
781
|
+
...message,
|
|
782
|
+
data: {
|
|
783
|
+
offer: message.data.offer
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
const answer = await this._onOffer(offerMessage);
|
|
787
|
+
answer.offerMessageId = message.messageId;
|
|
788
|
+
try {
|
|
789
|
+
await this._sendReliableMessage({
|
|
790
|
+
author: recipient,
|
|
791
|
+
recipient: author,
|
|
792
|
+
message: {
|
|
793
|
+
topic: message.topic,
|
|
794
|
+
sessionId: message.sessionId,
|
|
795
|
+
data: {
|
|
796
|
+
answer
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
} catch (err) {
|
|
801
|
+
if (err instanceof TimeoutError2) {
|
|
802
|
+
log3.info("timeout sending answer to offer", {
|
|
803
|
+
err
|
|
804
|
+
}, {
|
|
805
|
+
F: __dxlog_file3,
|
|
806
|
+
L: 177,
|
|
807
|
+
S: this,
|
|
808
|
+
C: (f, a) => f(...a)
|
|
809
|
+
});
|
|
810
|
+
} else {
|
|
811
|
+
log3.info("error sending answer to offer", {
|
|
812
|
+
err
|
|
813
|
+
}, {
|
|
814
|
+
F: __dxlog_file3,
|
|
815
|
+
L: 179,
|
|
816
|
+
S: this,
|
|
817
|
+
C: (f, a) => f(...a)
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
async _handleSignal({ author, recipient, message }) {
|
|
823
|
+
invariant2(message.messageId, void 0, {
|
|
824
|
+
F: __dxlog_file3,
|
|
825
|
+
L: 193,
|
|
826
|
+
S: this,
|
|
827
|
+
A: [
|
|
828
|
+
"message.messageId",
|
|
829
|
+
""
|
|
830
|
+
]
|
|
831
|
+
});
|
|
832
|
+
invariant2(message.data.signal || message.data.signalBatch, "Invalid message", {
|
|
833
|
+
F: __dxlog_file3,
|
|
834
|
+
L: 194,
|
|
835
|
+
S: this,
|
|
836
|
+
A: [
|
|
837
|
+
"message.data.signal || message.data.signalBatch",
|
|
838
|
+
"'Invalid message'"
|
|
839
|
+
]
|
|
840
|
+
});
|
|
841
|
+
const signalMessage = {
|
|
842
|
+
author,
|
|
843
|
+
recipient,
|
|
844
|
+
...message,
|
|
845
|
+
data: {
|
|
846
|
+
signal: message.data.signal,
|
|
847
|
+
signalBatch: message.data.signalBatch
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
await this._onSignal(signalMessage);
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
// packages/core/mesh/network-manager/src/swarm/swarm.ts
|
|
855
|
+
import { Event as Event3, scheduleTask as scheduleTask3, sleep as sleep2, synchronized as synchronized3 } from "@dxos/async";
|
|
856
|
+
import { Context as Context4 } from "@dxos/context";
|
|
857
|
+
import { ErrorStream as ErrorStream2 } from "@dxos/debug";
|
|
858
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
859
|
+
import { PublicKey as PublicKey4 } from "@dxos/keys";
|
|
860
|
+
import { log as log5, logInfo as logInfo2 } from "@dxos/log";
|
|
861
|
+
import { PeerInfoHash } from "@dxos/messaging";
|
|
862
|
+
import { trace as trace2 } from "@dxos/protocols";
|
|
863
|
+
import { ComplexMap as ComplexMap2, isNotNullOrUndefined as isNotNullOrUndefined2 } from "@dxos/util";
|
|
864
|
+
|
|
865
|
+
// packages/core/mesh/network-manager/src/swarm/peer.ts
|
|
866
|
+
import { Event as Event2, scheduleTask as scheduleTask2, synchronized as synchronized2 } from "@dxos/async";
|
|
867
|
+
import { Context as Context3 } from "@dxos/context";
|
|
868
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
869
|
+
import { PublicKey as PublicKey3 } from "@dxos/keys";
|
|
870
|
+
import { log as log4 } from "@dxos/log";
|
|
871
|
+
import { CancelledError as CancelledError2, SystemError } from "@dxos/protocols";
|
|
872
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
873
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
874
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
875
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
876
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
877
|
+
}
|
|
878
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/peer.ts";
|
|
879
|
+
var ConnectionDisplacedError = class extends SystemError {
|
|
880
|
+
constructor() {
|
|
881
|
+
super("Connection displaced by remote initiator.");
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
var CONNECTION_COUNTS_STABLE_AFTER = 5e3;
|
|
885
|
+
var Peer = class {
|
|
886
|
+
constructor(remoteInfo, topic, localInfo, _signalMessaging, _protocolProvider, _transportFactory, _connectionLimiter, _callbacks) {
|
|
887
|
+
this.remoteInfo = remoteInfo;
|
|
888
|
+
this.topic = topic;
|
|
889
|
+
this.localInfo = localInfo;
|
|
890
|
+
this._signalMessaging = _signalMessaging;
|
|
891
|
+
this._protocolProvider = _protocolProvider;
|
|
892
|
+
this._transportFactory = _transportFactory;
|
|
893
|
+
this._connectionLimiter = _connectionLimiter;
|
|
894
|
+
this._callbacks = _callbacks;
|
|
895
|
+
this._availableAfter = 0;
|
|
896
|
+
this.availableToConnect = true;
|
|
897
|
+
this._ctx = new Context3(void 0, {
|
|
898
|
+
F: __dxlog_file4,
|
|
899
|
+
L: 80
|
|
900
|
+
});
|
|
901
|
+
this.advertizing = false;
|
|
902
|
+
this.initiating = false;
|
|
903
|
+
this.connectionDisplaced = new Event2();
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Respond to remote offer.
|
|
907
|
+
*/
|
|
908
|
+
async onOffer(message) {
|
|
909
|
+
const remote = message.author;
|
|
910
|
+
if (this.connection && ![
|
|
911
|
+
ConnectionState.CREATED,
|
|
912
|
+
ConnectionState.INITIAL,
|
|
913
|
+
ConnectionState.CONNECTING
|
|
914
|
+
].includes(this.connection.state)) {
|
|
915
|
+
log4.info(`received offer when connection already in ${this.connection.state} state`, void 0, {
|
|
916
|
+
F: __dxlog_file4,
|
|
917
|
+
L: 115,
|
|
918
|
+
S: this,
|
|
919
|
+
C: (f, a) => f(...a)
|
|
920
|
+
});
|
|
921
|
+
return {
|
|
922
|
+
accept: false
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
if (this.connection || this.initiating) {
|
|
926
|
+
if (remote.peerKey < this.localInfo.peerKey) {
|
|
927
|
+
log4("close local connection", {
|
|
928
|
+
localPeer: this.localInfo,
|
|
929
|
+
topic: this.topic,
|
|
930
|
+
remotePeer: this.remoteInfo,
|
|
931
|
+
sessionId: this.connection?.sessionId
|
|
932
|
+
}, {
|
|
933
|
+
F: __dxlog_file4,
|
|
934
|
+
L: 124,
|
|
935
|
+
S: this,
|
|
936
|
+
C: (f, a) => f(...a)
|
|
937
|
+
});
|
|
938
|
+
if (this.connection) {
|
|
939
|
+
await this.closeConnection(new ConnectionDisplacedError());
|
|
940
|
+
}
|
|
941
|
+
} else {
|
|
942
|
+
return {
|
|
943
|
+
accept: false
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (await this._callbacks.onOffer(remote)) {
|
|
948
|
+
if (!this.connection) {
|
|
949
|
+
invariant3(message.sessionId, void 0, {
|
|
950
|
+
F: __dxlog_file4,
|
|
951
|
+
L: 144,
|
|
952
|
+
S: this,
|
|
953
|
+
A: [
|
|
954
|
+
"message.sessionId",
|
|
955
|
+
""
|
|
956
|
+
]
|
|
957
|
+
});
|
|
958
|
+
const connection = this._createConnection(false, message.sessionId);
|
|
959
|
+
try {
|
|
960
|
+
await this._connectionLimiter.connecting(message.sessionId);
|
|
961
|
+
connection.initiate();
|
|
962
|
+
await connection.openConnection();
|
|
963
|
+
} catch (err) {
|
|
964
|
+
if (!(err instanceof CancelledError2)) {
|
|
965
|
+
log4.info("connection error", {
|
|
966
|
+
topic: this.topic,
|
|
967
|
+
peerId: this.localInfo,
|
|
968
|
+
remoteId: this.remoteInfo,
|
|
969
|
+
err
|
|
970
|
+
}, {
|
|
971
|
+
F: __dxlog_file4,
|
|
972
|
+
L: 154,
|
|
973
|
+
S: this,
|
|
974
|
+
C: (f, a) => f(...a)
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
await this.closeConnection(err);
|
|
978
|
+
}
|
|
979
|
+
return {
|
|
980
|
+
accept: true
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return {
|
|
985
|
+
accept: false
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Initiate a connection to the remote peer.
|
|
990
|
+
*/
|
|
991
|
+
async initiateConnection() {
|
|
992
|
+
invariant3(!this.initiating, "Initiation in progress.", {
|
|
993
|
+
F: __dxlog_file4,
|
|
994
|
+
L: 171,
|
|
995
|
+
S: this,
|
|
996
|
+
A: [
|
|
997
|
+
"!this.initiating",
|
|
998
|
+
"'Initiation in progress.'"
|
|
999
|
+
]
|
|
1000
|
+
});
|
|
1001
|
+
invariant3(!this.connection, "Already connected.", {
|
|
1002
|
+
F: __dxlog_file4,
|
|
1003
|
+
L: 172,
|
|
1004
|
+
S: this,
|
|
1005
|
+
A: [
|
|
1006
|
+
"!this.connection",
|
|
1007
|
+
"'Already connected.'"
|
|
1008
|
+
]
|
|
1009
|
+
});
|
|
1010
|
+
const sessionId = PublicKey3.random();
|
|
1011
|
+
log4("initiating...", {
|
|
1012
|
+
local: this.localInfo,
|
|
1013
|
+
topic: this.topic,
|
|
1014
|
+
remote: this.remoteInfo,
|
|
1015
|
+
sessionId
|
|
1016
|
+
}, {
|
|
1017
|
+
F: __dxlog_file4,
|
|
1018
|
+
L: 174,
|
|
1019
|
+
S: this,
|
|
1020
|
+
C: (f, a) => f(...a)
|
|
1021
|
+
});
|
|
1022
|
+
const connection = this._createConnection(true, sessionId);
|
|
1023
|
+
this.initiating = true;
|
|
1024
|
+
let answer;
|
|
1025
|
+
try {
|
|
1026
|
+
await this._connectionLimiter.connecting(sessionId);
|
|
1027
|
+
connection.initiate();
|
|
1028
|
+
answer = await this._signalMessaging.offer({
|
|
1029
|
+
author: this.localInfo,
|
|
1030
|
+
recipient: this.remoteInfo,
|
|
1031
|
+
sessionId,
|
|
1032
|
+
topic: this.topic,
|
|
1033
|
+
data: {
|
|
1034
|
+
offer: {}
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
log4("received", {
|
|
1038
|
+
answer,
|
|
1039
|
+
topic: this.topic,
|
|
1040
|
+
local: this.localInfo,
|
|
1041
|
+
remote: this.remoteInfo
|
|
1042
|
+
}, {
|
|
1043
|
+
F: __dxlog_file4,
|
|
1044
|
+
L: 191,
|
|
1045
|
+
S: this,
|
|
1046
|
+
C: (f, a) => f(...a)
|
|
1047
|
+
});
|
|
1048
|
+
if (connection.state !== ConnectionState.INITIAL) {
|
|
1049
|
+
log4("ignoring response", void 0, {
|
|
1050
|
+
F: __dxlog_file4,
|
|
1051
|
+
L: 193,
|
|
1052
|
+
S: this,
|
|
1053
|
+
C: (f, a) => f(...a)
|
|
1054
|
+
});
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
} catch (err) {
|
|
1058
|
+
log4("initiation error: send offer", {
|
|
1059
|
+
err,
|
|
1060
|
+
topic: this.topic,
|
|
1061
|
+
local: this.localInfo,
|
|
1062
|
+
remote: this.remoteInfo
|
|
1063
|
+
}, {
|
|
1064
|
+
F: __dxlog_file4,
|
|
1065
|
+
L: 197,
|
|
1066
|
+
S: this,
|
|
1067
|
+
C: (f, a) => f(...a)
|
|
1068
|
+
});
|
|
1069
|
+
await connection.abort(err);
|
|
1070
|
+
throw err;
|
|
1071
|
+
} finally {
|
|
1072
|
+
this.initiating = false;
|
|
1073
|
+
}
|
|
1074
|
+
try {
|
|
1075
|
+
if (!answer.accept) {
|
|
1076
|
+
this._callbacks.onRejected();
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
} catch (err) {
|
|
1080
|
+
log4("initiation error: accept answer", {
|
|
1081
|
+
err,
|
|
1082
|
+
topic: this.topic,
|
|
1083
|
+
local: this.localInfo,
|
|
1084
|
+
remote: this.remoteInfo
|
|
1085
|
+
}, {
|
|
1086
|
+
F: __dxlog_file4,
|
|
1087
|
+
L: 210,
|
|
1088
|
+
S: this,
|
|
1089
|
+
C: (f, a) => f(...a)
|
|
1090
|
+
});
|
|
1091
|
+
await connection.abort(err);
|
|
1092
|
+
throw err;
|
|
1093
|
+
} finally {
|
|
1094
|
+
this.initiating = false;
|
|
1095
|
+
}
|
|
1096
|
+
try {
|
|
1097
|
+
log4("opening connection as initiator", void 0, {
|
|
1098
|
+
F: __dxlog_file4,
|
|
1099
|
+
L: 223,
|
|
1100
|
+
S: this,
|
|
1101
|
+
C: (f, a) => f(...a)
|
|
1102
|
+
});
|
|
1103
|
+
await connection.openConnection();
|
|
1104
|
+
this._callbacks.onAccepted();
|
|
1105
|
+
} catch (err) {
|
|
1106
|
+
log4("initiation error: open connection", {
|
|
1107
|
+
err,
|
|
1108
|
+
topic: this.topic,
|
|
1109
|
+
local: this.localInfo,
|
|
1110
|
+
remote: this.remoteInfo
|
|
1111
|
+
}, {
|
|
1112
|
+
F: __dxlog_file4,
|
|
1113
|
+
L: 227,
|
|
1114
|
+
S: this,
|
|
1115
|
+
C: (f, a) => f(...a)
|
|
1116
|
+
});
|
|
1117
|
+
log4.warn("closing connection due to unhandled error on openConnection", {
|
|
1118
|
+
err
|
|
1119
|
+
}, {
|
|
1120
|
+
F: __dxlog_file4,
|
|
1121
|
+
L: 234,
|
|
1122
|
+
S: this,
|
|
1123
|
+
C: (f, a) => f(...a)
|
|
1124
|
+
});
|
|
1125
|
+
await this.closeConnection(err);
|
|
1126
|
+
throw err;
|
|
1127
|
+
} finally {
|
|
1128
|
+
this.initiating = false;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Create new connection.
|
|
1133
|
+
* Either we're initiating a connection or creating one in response to an offer from the other peer.
|
|
1134
|
+
*/
|
|
1135
|
+
_createConnection(initiator, sessionId) {
|
|
1136
|
+
log4("creating connection", {
|
|
1137
|
+
topic: this.topic,
|
|
1138
|
+
peerId: this.localInfo,
|
|
1139
|
+
remoteId: this.remoteInfo,
|
|
1140
|
+
initiator,
|
|
1141
|
+
sessionId
|
|
1142
|
+
}, {
|
|
1143
|
+
F: __dxlog_file4,
|
|
1144
|
+
L: 248,
|
|
1145
|
+
S: this,
|
|
1146
|
+
C: (f, a) => f(...a)
|
|
1147
|
+
});
|
|
1148
|
+
invariant3(!this.connection, "Already connected.", {
|
|
1149
|
+
F: __dxlog_file4,
|
|
1150
|
+
L: 255,
|
|
1151
|
+
S: this,
|
|
1152
|
+
A: [
|
|
1153
|
+
"!this.connection",
|
|
1154
|
+
"'Already connected.'"
|
|
1155
|
+
]
|
|
1156
|
+
});
|
|
1157
|
+
const connection = new Connection(
|
|
1158
|
+
this.topic,
|
|
1159
|
+
this.localInfo,
|
|
1160
|
+
this.remoteInfo,
|
|
1161
|
+
sessionId,
|
|
1162
|
+
initiator,
|
|
1163
|
+
this._signalMessaging,
|
|
1164
|
+
// TODO(dmaretskyi): Init only when connection is established.
|
|
1165
|
+
this._protocolProvider({
|
|
1166
|
+
initiator,
|
|
1167
|
+
localPeerId: PublicKey3.from(this.localInfo.peerKey),
|
|
1168
|
+
remotePeerId: PublicKey3.from(this.remoteInfo.peerKey),
|
|
1169
|
+
topic: this.topic
|
|
1170
|
+
}),
|
|
1171
|
+
this._transportFactory,
|
|
1172
|
+
{
|
|
1173
|
+
onConnected: () => {
|
|
1174
|
+
this.availableToConnect = true;
|
|
1175
|
+
this._lastConnectionTime = Date.now();
|
|
1176
|
+
this._callbacks.onConnected();
|
|
1177
|
+
this._connectionLimiter.doneConnecting(sessionId);
|
|
1178
|
+
log4.trace("dxos.mesh.connection.connected", {
|
|
1179
|
+
topic: this.topic,
|
|
1180
|
+
localPeerId: this.localInfo,
|
|
1181
|
+
remotePeerId: this.remoteInfo,
|
|
1182
|
+
sessionId,
|
|
1183
|
+
initiator
|
|
1184
|
+
}, {
|
|
1185
|
+
F: __dxlog_file4,
|
|
1186
|
+
L: 279,
|
|
1187
|
+
S: this,
|
|
1188
|
+
C: (f, a) => f(...a)
|
|
1189
|
+
});
|
|
1190
|
+
},
|
|
1191
|
+
onClosed: (err) => {
|
|
1192
|
+
log4("connection closed", {
|
|
1193
|
+
topic: this.topic,
|
|
1194
|
+
peerId: this.localInfo,
|
|
1195
|
+
remoteId: this.remoteInfo,
|
|
1196
|
+
initiator
|
|
1197
|
+
}, {
|
|
1198
|
+
F: __dxlog_file4,
|
|
1199
|
+
L: 288,
|
|
1200
|
+
S: this,
|
|
1201
|
+
C: (f, a) => f(...a)
|
|
1202
|
+
});
|
|
1203
|
+
this._connectionLimiter.doneConnecting(sessionId);
|
|
1204
|
+
invariant3(this.connection === connection, "Connection mismatch (race condition).", {
|
|
1205
|
+
F: __dxlog_file4,
|
|
1206
|
+
L: 293,
|
|
1207
|
+
S: this,
|
|
1208
|
+
A: [
|
|
1209
|
+
"this.connection === connection",
|
|
1210
|
+
"'Connection mismatch (race condition).'"
|
|
1211
|
+
]
|
|
1212
|
+
});
|
|
1213
|
+
log4.trace("dxos.mesh.connection.closed", {
|
|
1214
|
+
topic: this.topic,
|
|
1215
|
+
localPeerId: this.localInfo,
|
|
1216
|
+
remotePeerId: this.remoteInfo,
|
|
1217
|
+
sessionId,
|
|
1218
|
+
initiator
|
|
1219
|
+
}, {
|
|
1220
|
+
F: __dxlog_file4,
|
|
1221
|
+
L: 295,
|
|
1222
|
+
S: this,
|
|
1223
|
+
C: (f, a) => f(...a)
|
|
1224
|
+
});
|
|
1225
|
+
if (err instanceof ConnectionDisplacedError) {
|
|
1226
|
+
this.connectionDisplaced.emit(this.connection);
|
|
1227
|
+
} else {
|
|
1228
|
+
if (this._lastConnectionTime && this._lastConnectionTime + CONNECTION_COUNTS_STABLE_AFTER < Date.now()) {
|
|
1229
|
+
this._availableAfter = 0;
|
|
1230
|
+
} else {
|
|
1231
|
+
this.availableToConnect = false;
|
|
1232
|
+
this._availableAfter = increaseInterval(this._availableAfter);
|
|
1233
|
+
}
|
|
1234
|
+
this._callbacks.onDisconnected();
|
|
1235
|
+
scheduleTask2(this._connectionCtx, () => {
|
|
1236
|
+
this.availableToConnect = true;
|
|
1237
|
+
this._callbacks.onPeerAvailable();
|
|
1238
|
+
}, this._availableAfter);
|
|
1239
|
+
}
|
|
1240
|
+
this.connection = void 0;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
);
|
|
1244
|
+
this._callbacks.onInitiated(connection);
|
|
1245
|
+
void this._connectionCtx?.dispose();
|
|
1246
|
+
this._connectionCtx = this._ctx.derive();
|
|
1247
|
+
connection.errors.handle((err) => {
|
|
1248
|
+
log4.info("connection error, closing", {
|
|
1249
|
+
topic: this.topic,
|
|
1250
|
+
peerId: this.localInfo,
|
|
1251
|
+
remoteId: this.remoteInfo,
|
|
1252
|
+
initiator,
|
|
1253
|
+
err
|
|
1254
|
+
}, {
|
|
1255
|
+
F: __dxlog_file4,
|
|
1256
|
+
L: 335,
|
|
1257
|
+
S: this,
|
|
1258
|
+
C: (f, a) => f(...a)
|
|
1259
|
+
});
|
|
1260
|
+
log4.trace("dxos.mesh.connection.error", {
|
|
1261
|
+
topic: this.topic,
|
|
1262
|
+
localPeerId: this.localInfo,
|
|
1263
|
+
remotePeerId: this.remoteInfo,
|
|
1264
|
+
sessionId,
|
|
1265
|
+
initiator,
|
|
1266
|
+
err
|
|
1267
|
+
}, {
|
|
1268
|
+
F: __dxlog_file4,
|
|
1269
|
+
L: 342,
|
|
1270
|
+
S: this,
|
|
1271
|
+
C: (f, a) => f(...a)
|
|
1272
|
+
});
|
|
1273
|
+
void this.closeConnection(err);
|
|
1274
|
+
});
|
|
1275
|
+
this.connection = connection;
|
|
1276
|
+
return connection;
|
|
1277
|
+
}
|
|
1278
|
+
async closeConnection(err) {
|
|
1279
|
+
if (!this.connection) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
const connection = this.connection;
|
|
1283
|
+
log4("closing...", {
|
|
1284
|
+
peerId: this.remoteInfo,
|
|
1285
|
+
sessionId: connection.sessionId
|
|
1286
|
+
}, {
|
|
1287
|
+
F: __dxlog_file4,
|
|
1288
|
+
L: 367,
|
|
1289
|
+
S: this,
|
|
1290
|
+
C: (f, a) => f(...a)
|
|
1291
|
+
});
|
|
1292
|
+
await connection.close(err);
|
|
1293
|
+
log4("closed", {
|
|
1294
|
+
peerId: this.remoteInfo,
|
|
1295
|
+
sessionId: connection.sessionId
|
|
1296
|
+
}, {
|
|
1297
|
+
F: __dxlog_file4,
|
|
1298
|
+
L: 373,
|
|
1299
|
+
S: this,
|
|
1300
|
+
C: (f, a) => f(...a)
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
async onSignal(message) {
|
|
1304
|
+
if (!this.connection) {
|
|
1305
|
+
log4("dropping signal message for non-existent connection", {
|
|
1306
|
+
message
|
|
1307
|
+
}, {
|
|
1308
|
+
F: __dxlog_file4,
|
|
1309
|
+
L: 378,
|
|
1310
|
+
S: this,
|
|
1311
|
+
C: (f, a) => f(...a)
|
|
1312
|
+
});
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
await this.connection.signal(message);
|
|
1316
|
+
}
|
|
1317
|
+
async safeDestroy(reason) {
|
|
1318
|
+
await this._ctx.dispose();
|
|
1319
|
+
log4("Destroying peer", {
|
|
1320
|
+
peerId: this.remoteInfo,
|
|
1321
|
+
topic: this.topic
|
|
1322
|
+
}, {
|
|
1323
|
+
F: __dxlog_file4,
|
|
1324
|
+
L: 388,
|
|
1325
|
+
S: this,
|
|
1326
|
+
C: (f, a) => f(...a)
|
|
1327
|
+
});
|
|
1328
|
+
await this?.connection?.close(reason);
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
_ts_decorate2([
|
|
1332
|
+
synchronized2
|
|
1333
|
+
], Peer.prototype, "safeDestroy", null);
|
|
1334
|
+
var increaseInterval = (interval) => {
|
|
1335
|
+
if (interval === 0) {
|
|
1336
|
+
return 50;
|
|
1337
|
+
} else if (interval < 500) {
|
|
1338
|
+
return 500;
|
|
1339
|
+
} else if (interval < 1e3) {
|
|
1340
|
+
return 1e3;
|
|
1341
|
+
} else if (interval < 5e3) {
|
|
1342
|
+
return 5e3;
|
|
1343
|
+
}
|
|
1344
|
+
return 1e4;
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1347
|
+
// packages/core/mesh/network-manager/src/swarm/swarm.ts
|
|
1348
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
1349
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1350
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1351
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1352
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1353
|
+
}
|
|
1354
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/swarm.ts";
|
|
1355
|
+
var INITIATION_DELAY = 100;
|
|
1356
|
+
var getClassName = (obj) => Object.getPrototypeOf(obj).constructor.name;
|
|
1357
|
+
var Swarm = class {
|
|
1358
|
+
// TODO(burdon): Swarm => Peer.create/destroy =< Connection.open/close
|
|
1359
|
+
// TODO(burdon): Pass in object.
|
|
1360
|
+
constructor(_topic, _ownPeer, _topology, _protocolProvider, _messenger, _transportFactory, _label, _connectionLimiter, _initiationDelay = INITIATION_DELAY) {
|
|
1361
|
+
this._topic = _topic;
|
|
1362
|
+
this._ownPeer = _ownPeer;
|
|
1363
|
+
this._topology = _topology;
|
|
1364
|
+
this._protocolProvider = _protocolProvider;
|
|
1365
|
+
this._messenger = _messenger;
|
|
1366
|
+
this._transportFactory = _transportFactory;
|
|
1367
|
+
this._label = _label;
|
|
1368
|
+
this._connectionLimiter = _connectionLimiter;
|
|
1369
|
+
this._initiationDelay = _initiationDelay;
|
|
1370
|
+
this._ctx = new Context4(void 0, {
|
|
1371
|
+
F: __dxlog_file5,
|
|
1372
|
+
L: 38
|
|
1373
|
+
});
|
|
1374
|
+
this._listeningHandle = void 0;
|
|
1375
|
+
this._peers = new ComplexMap2(PeerInfoHash);
|
|
1376
|
+
this._instanceId = PublicKey4.random().toHex();
|
|
1377
|
+
this.connectionAdded = new Event3();
|
|
1378
|
+
this.disconnected = new Event3();
|
|
1379
|
+
this.connected = new Event3();
|
|
1380
|
+
this.errors = new ErrorStream2();
|
|
1381
|
+
log5.trace("dxos.mesh.swarm.constructor", trace2.begin({
|
|
1382
|
+
id: this._instanceId,
|
|
1383
|
+
data: {
|
|
1384
|
+
topic: this._topic.toHex(),
|
|
1385
|
+
peer: this._ownPeer
|
|
1386
|
+
}
|
|
1387
|
+
}), {
|
|
1388
|
+
F: __dxlog_file5,
|
|
1389
|
+
L: 88,
|
|
1390
|
+
S: this,
|
|
1391
|
+
C: (f, a) => f(...a)
|
|
1392
|
+
});
|
|
1393
|
+
log5("creating swarm", {
|
|
1394
|
+
peerId: _ownPeer
|
|
1395
|
+
}, {
|
|
1396
|
+
F: __dxlog_file5,
|
|
1397
|
+
L: 92,
|
|
1398
|
+
S: this,
|
|
1399
|
+
C: (f, a) => f(...a)
|
|
1400
|
+
});
|
|
1401
|
+
_topology.init(this._getSwarmController());
|
|
1402
|
+
this._swarmMessenger = new SwarmMessenger({
|
|
1403
|
+
sendMessage: async (msg) => await this._messenger.sendMessage(msg),
|
|
1404
|
+
onSignal: async (msg) => await this.onSignal(msg),
|
|
1405
|
+
onOffer: async (msg) => await this.onOffer(msg),
|
|
1406
|
+
topic: this._topic
|
|
1407
|
+
});
|
|
1408
|
+
log5.trace("dxos.mesh.swarm.constructor", trace2.end({
|
|
1409
|
+
id: this._instanceId
|
|
1410
|
+
}), {
|
|
1411
|
+
F: __dxlog_file5,
|
|
1412
|
+
L: 101,
|
|
1413
|
+
S: this,
|
|
1414
|
+
C: (f, a) => f(...a)
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
get connections() {
|
|
1418
|
+
return Array.from(this._peers.values()).map((peer) => peer.connection).filter(isNotNullOrUndefined2);
|
|
1419
|
+
}
|
|
1420
|
+
get ownPeerId() {
|
|
1421
|
+
return PublicKey4.from(this._ownPeer.peerKey);
|
|
1422
|
+
}
|
|
1423
|
+
get ownPeer() {
|
|
1424
|
+
return this._ownPeer;
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Custom label assigned to this swarm. Used in devtools to display human-readable names for swarms.
|
|
1428
|
+
*/
|
|
1429
|
+
get label() {
|
|
1430
|
+
return this._label;
|
|
1431
|
+
}
|
|
1432
|
+
get topic() {
|
|
1433
|
+
return this._topic;
|
|
1434
|
+
}
|
|
1435
|
+
async open() {
|
|
1436
|
+
invariant4(!this._listeningHandle, void 0, {
|
|
1437
|
+
F: __dxlog_file5,
|
|
1438
|
+
L: 132,
|
|
1439
|
+
S: this,
|
|
1440
|
+
A: [
|
|
1441
|
+
"!this._listeningHandle",
|
|
1442
|
+
""
|
|
1443
|
+
]
|
|
1444
|
+
});
|
|
1445
|
+
this._listeningHandle = await this._messenger.listen({
|
|
1446
|
+
peer: this._ownPeer,
|
|
1447
|
+
payloadType: "dxos.mesh.swarm.SwarmMessage",
|
|
1448
|
+
onMessage: async (message) => {
|
|
1449
|
+
await this._swarmMessenger.receiveMessage(message).catch((err) => log5.info("Error while receiving message", {
|
|
1450
|
+
err
|
|
1451
|
+
}, {
|
|
1452
|
+
F: __dxlog_file5,
|
|
1453
|
+
L: 140,
|
|
1454
|
+
S: this,
|
|
1455
|
+
C: (f, a) => f(...a)
|
|
1456
|
+
}));
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
async destroy() {
|
|
1461
|
+
log5("destroying...", void 0, {
|
|
1462
|
+
F: __dxlog_file5,
|
|
1463
|
+
L: 146,
|
|
1464
|
+
S: this,
|
|
1465
|
+
C: (f, a) => f(...a)
|
|
1466
|
+
});
|
|
1467
|
+
await this._listeningHandle?.unsubscribe();
|
|
1468
|
+
this._listeningHandle = void 0;
|
|
1469
|
+
await this._ctx.dispose();
|
|
1470
|
+
await this._topology.destroy();
|
|
1471
|
+
await Promise.all(Array.from(this._peers.keys()).map((key) => this._destroyPeer(key, "swarm destroyed")));
|
|
1472
|
+
log5("destroyed", void 0, {
|
|
1473
|
+
F: __dxlog_file5,
|
|
1474
|
+
L: 153,
|
|
1475
|
+
S: this,
|
|
1476
|
+
C: (f, a) => f(...a)
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
async setTopology(topology) {
|
|
1480
|
+
invariant4(!this._ctx.disposed, "Swarm is offline", {
|
|
1481
|
+
F: __dxlog_file5,
|
|
1482
|
+
L: 157,
|
|
1483
|
+
S: this,
|
|
1484
|
+
A: [
|
|
1485
|
+
"!this._ctx.disposed",
|
|
1486
|
+
"'Swarm is offline'"
|
|
1487
|
+
]
|
|
1488
|
+
});
|
|
1489
|
+
if (topology === this._topology) {
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
log5("setting topology", {
|
|
1493
|
+
previous: getClassName(this._topology),
|
|
1494
|
+
topology: getClassName(topology)
|
|
1495
|
+
}, {
|
|
1496
|
+
F: __dxlog_file5,
|
|
1497
|
+
L: 161,
|
|
1498
|
+
S: this,
|
|
1499
|
+
C: (f, a) => f(...a)
|
|
1500
|
+
});
|
|
1501
|
+
await this._topology.destroy();
|
|
1502
|
+
this._topology = topology;
|
|
1503
|
+
this._topology.init(this._getSwarmController());
|
|
1504
|
+
this._topology.update();
|
|
1505
|
+
}
|
|
1506
|
+
onSwarmEvent(swarmEvent) {
|
|
1507
|
+
log5("swarm event", {
|
|
1508
|
+
swarmEvent
|
|
1509
|
+
}, {
|
|
1510
|
+
F: __dxlog_file5,
|
|
1511
|
+
L: 174,
|
|
1512
|
+
S: this,
|
|
1513
|
+
C: (f, a) => f(...a)
|
|
1514
|
+
});
|
|
1515
|
+
if (this._ctx.disposed) {
|
|
1516
|
+
log5("swarm event ignored for disposed swarm", void 0, {
|
|
1517
|
+
F: __dxlog_file5,
|
|
1518
|
+
L: 177,
|
|
1519
|
+
S: this,
|
|
1520
|
+
C: (f, a) => f(...a)
|
|
1521
|
+
});
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
if (swarmEvent.peerAvailable) {
|
|
1525
|
+
const peerId = swarmEvent.peerAvailable.peer.peerKey;
|
|
1526
|
+
if (peerId !== this._ownPeer.peerKey) {
|
|
1527
|
+
log5("new peer", {
|
|
1528
|
+
peerId
|
|
1529
|
+
}, {
|
|
1530
|
+
F: __dxlog_file5,
|
|
1531
|
+
L: 184,
|
|
1532
|
+
S: this,
|
|
1533
|
+
C: (f, a) => f(...a)
|
|
1534
|
+
});
|
|
1535
|
+
const peer = this._getOrCreatePeer(swarmEvent.peerAvailable.peer);
|
|
1536
|
+
peer.advertizing = true;
|
|
1537
|
+
}
|
|
1538
|
+
} else if (swarmEvent.peerLeft) {
|
|
1539
|
+
const peer = this._peers.get(swarmEvent.peerLeft.peer);
|
|
1540
|
+
if (peer) {
|
|
1541
|
+
peer.advertizing = false;
|
|
1542
|
+
if (peer.connection?.state !== ConnectionState.CONNECTED) {
|
|
1543
|
+
void this._destroyPeer(swarmEvent.peerLeft.peer, "peer left").catch((err) => log5.catch(err, void 0, {
|
|
1544
|
+
F: __dxlog_file5,
|
|
1545
|
+
L: 194,
|
|
1546
|
+
S: this,
|
|
1547
|
+
C: (f, a) => f(...a)
|
|
1548
|
+
}));
|
|
1549
|
+
}
|
|
1550
|
+
} else {
|
|
1551
|
+
log5("received peerLeft but no peer found", {
|
|
1552
|
+
peer: swarmEvent.peerLeft.peer.peerKey
|
|
1553
|
+
}, {
|
|
1554
|
+
F: __dxlog_file5,
|
|
1555
|
+
L: 197,
|
|
1556
|
+
S: this,
|
|
1557
|
+
C: (f, a) => f(...a)
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
this._topology.update();
|
|
1562
|
+
}
|
|
1563
|
+
async onOffer(message) {
|
|
1564
|
+
log5("offer", {
|
|
1565
|
+
message
|
|
1566
|
+
}, {
|
|
1567
|
+
F: __dxlog_file5,
|
|
1568
|
+
L: 206,
|
|
1569
|
+
S: this,
|
|
1570
|
+
C: (f, a) => f(...a)
|
|
1571
|
+
});
|
|
1572
|
+
if (this._ctx.disposed) {
|
|
1573
|
+
log5("ignored for disposed swarm", void 0, {
|
|
1574
|
+
F: __dxlog_file5,
|
|
1575
|
+
L: 208,
|
|
1576
|
+
S: this,
|
|
1577
|
+
C: (f, a) => f(...a)
|
|
1578
|
+
});
|
|
1579
|
+
return {
|
|
1580
|
+
accept: false
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
invariant4(message.author, void 0, {
|
|
1584
|
+
F: __dxlog_file5,
|
|
1585
|
+
L: 213,
|
|
1586
|
+
S: this,
|
|
1587
|
+
A: [
|
|
1588
|
+
"message.author",
|
|
1589
|
+
""
|
|
1590
|
+
]
|
|
1591
|
+
});
|
|
1592
|
+
if (message.recipient.peerKey !== this._ownPeer.peerKey) {
|
|
1593
|
+
log5("rejecting offer with incorrect peerId", {
|
|
1594
|
+
message
|
|
1595
|
+
}, {
|
|
1596
|
+
F: __dxlog_file5,
|
|
1597
|
+
L: 215,
|
|
1598
|
+
S: this,
|
|
1599
|
+
C: (f, a) => f(...a)
|
|
1600
|
+
});
|
|
1601
|
+
return {
|
|
1602
|
+
accept: false
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
if (!message.topic?.equals(this._topic)) {
|
|
1606
|
+
log5("rejecting offer with incorrect topic", {
|
|
1607
|
+
message
|
|
1608
|
+
}, {
|
|
1609
|
+
F: __dxlog_file5,
|
|
1610
|
+
L: 219,
|
|
1611
|
+
S: this,
|
|
1612
|
+
C: (f, a) => f(...a)
|
|
1613
|
+
});
|
|
1614
|
+
return {
|
|
1615
|
+
accept: false
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
const peer = this._getOrCreatePeer(message.author);
|
|
1619
|
+
const answer = await peer.onOffer(message);
|
|
1620
|
+
this._topology.update();
|
|
1621
|
+
return answer;
|
|
1622
|
+
}
|
|
1623
|
+
async onSignal(message) {
|
|
1624
|
+
log5("signal", {
|
|
1625
|
+
message
|
|
1626
|
+
}, {
|
|
1627
|
+
F: __dxlog_file5,
|
|
1628
|
+
L: 230,
|
|
1629
|
+
S: this,
|
|
1630
|
+
C: (f, a) => f(...a)
|
|
1631
|
+
});
|
|
1632
|
+
if (this._ctx.disposed) {
|
|
1633
|
+
log5.info("ignored for offline swarm", void 0, {
|
|
1634
|
+
F: __dxlog_file5,
|
|
1635
|
+
L: 232,
|
|
1636
|
+
S: this,
|
|
1637
|
+
C: (f, a) => f(...a)
|
|
1638
|
+
});
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
invariant4(message.recipient.peerKey === this._ownPeer.peerKey, `Invalid signal peer id expected=${this.ownPeerId}, actual=${message.recipient}`, {
|
|
1642
|
+
F: __dxlog_file5,
|
|
1643
|
+
L: 235,
|
|
1644
|
+
S: this,
|
|
1645
|
+
A: [
|
|
1646
|
+
"message.recipient.peerKey === this._ownPeer.peerKey",
|
|
1647
|
+
"`Invalid signal peer id expected=${this.ownPeerId}, actual=${message.recipient}`"
|
|
1648
|
+
]
|
|
1649
|
+
});
|
|
1650
|
+
invariant4(message.topic?.equals(this._topic), void 0, {
|
|
1651
|
+
F: __dxlog_file5,
|
|
1652
|
+
L: 239,
|
|
1653
|
+
S: this,
|
|
1654
|
+
A: [
|
|
1655
|
+
"message.topic?.equals(this._topic)",
|
|
1656
|
+
""
|
|
1657
|
+
]
|
|
1658
|
+
});
|
|
1659
|
+
invariant4(message.author, void 0, {
|
|
1660
|
+
F: __dxlog_file5,
|
|
1661
|
+
L: 240,
|
|
1662
|
+
S: this,
|
|
1663
|
+
A: [
|
|
1664
|
+
"message.author",
|
|
1665
|
+
""
|
|
1666
|
+
]
|
|
1667
|
+
});
|
|
1668
|
+
const peer = this._getOrCreatePeer(message.author);
|
|
1669
|
+
await peer.onSignal(message);
|
|
1670
|
+
}
|
|
1671
|
+
// For debug purposes
|
|
1672
|
+
async goOffline() {
|
|
1673
|
+
await this._ctx.dispose();
|
|
1674
|
+
await Promise.all([
|
|
1675
|
+
...this._peers.keys()
|
|
1676
|
+
].map((peerId) => this._destroyPeer(peerId, "goOffline")));
|
|
1677
|
+
}
|
|
1678
|
+
// For debug purposes
|
|
1679
|
+
async goOnline() {
|
|
1680
|
+
this._ctx = new Context4(void 0, {
|
|
1681
|
+
F: __dxlog_file5,
|
|
1682
|
+
L: 256
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
_getOrCreatePeer(peerInfo) {
|
|
1686
|
+
invariant4(peerInfo.peerKey, "PeerInfo.peerKey is required", {
|
|
1687
|
+
F: __dxlog_file5,
|
|
1688
|
+
L: 260,
|
|
1689
|
+
S: this,
|
|
1690
|
+
A: [
|
|
1691
|
+
"peerInfo.peerKey",
|
|
1692
|
+
"'PeerInfo.peerKey is required'"
|
|
1693
|
+
]
|
|
1694
|
+
});
|
|
1695
|
+
let peer = this._peers.get(peerInfo);
|
|
1696
|
+
if (!peer) {
|
|
1697
|
+
peer = new Peer(peerInfo, this._topic, this._ownPeer, this._swarmMessenger, this._protocolProvider, this._transportFactory, this._connectionLimiter, {
|
|
1698
|
+
onInitiated: (connection) => {
|
|
1699
|
+
this.connectionAdded.emit(connection);
|
|
1700
|
+
},
|
|
1701
|
+
onConnected: () => {
|
|
1702
|
+
this.connected.emit(peerInfo);
|
|
1703
|
+
},
|
|
1704
|
+
onDisconnected: async () => {
|
|
1705
|
+
if (this._isUnregistered(peer)) {
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
if (!peer.advertizing) {
|
|
1709
|
+
await this._destroyPeer(peerInfo, "peer disconnected");
|
|
1710
|
+
}
|
|
1711
|
+
this.disconnected.emit(peerInfo);
|
|
1712
|
+
this._topology.update();
|
|
1713
|
+
},
|
|
1714
|
+
onRejected: () => {
|
|
1715
|
+
if (!this._isUnregistered(peer)) {
|
|
1716
|
+
log5("peer rejected connection", {
|
|
1717
|
+
peerInfo
|
|
1718
|
+
}, {
|
|
1719
|
+
F: __dxlog_file5,
|
|
1720
|
+
L: 293,
|
|
1721
|
+
S: this,
|
|
1722
|
+
C: (f, a) => f(...a)
|
|
1723
|
+
});
|
|
1724
|
+
void this._destroyPeer(peerInfo, "peer rejected connection");
|
|
1725
|
+
}
|
|
1726
|
+
},
|
|
1727
|
+
onAccepted: () => {
|
|
1728
|
+
this._topology.update();
|
|
1729
|
+
},
|
|
1730
|
+
onOffer: (remoteId) => {
|
|
1731
|
+
return this._topology.onOffer(PublicKey4.from(remoteId.peerKey));
|
|
1732
|
+
},
|
|
1733
|
+
onPeerAvailable: () => {
|
|
1734
|
+
this._topology.update();
|
|
1735
|
+
}
|
|
1736
|
+
});
|
|
1737
|
+
this._peers.set(peerInfo, peer);
|
|
1738
|
+
}
|
|
1739
|
+
return peer;
|
|
1740
|
+
}
|
|
1741
|
+
async _destroyPeer(peerInfo, reason) {
|
|
1742
|
+
const peer = this._peers.get(peerInfo);
|
|
1743
|
+
invariant4(peer, void 0, {
|
|
1744
|
+
F: __dxlog_file5,
|
|
1745
|
+
L: 316,
|
|
1746
|
+
S: this,
|
|
1747
|
+
A: [
|
|
1748
|
+
"peer",
|
|
1749
|
+
""
|
|
1750
|
+
]
|
|
1751
|
+
});
|
|
1752
|
+
this._peers.delete(peerInfo);
|
|
1753
|
+
await peer.safeDestroy(new Error(reason));
|
|
1754
|
+
}
|
|
1755
|
+
_getSwarmController() {
|
|
1756
|
+
return {
|
|
1757
|
+
getState: () => ({
|
|
1758
|
+
ownPeerId: PublicKey4.from(this._ownPeer.peerKey),
|
|
1759
|
+
connected: Array.from(this._peers.entries()).filter(([_, peer]) => peer.connection).map(([info]) => PublicKey4.from(info.peerKey)),
|
|
1760
|
+
candidates: Array.from(this._peers.entries()).filter(([_, peer]) => !peer.connection && peer.advertizing && peer.availableToConnect).map(([info]) => PublicKey4.from(info.peerKey)),
|
|
1761
|
+
allPeers: Array.from(this._peers.keys()).map((info) => PublicKey4.from(info.peerKey))
|
|
1762
|
+
}),
|
|
1763
|
+
connect: (peer) => {
|
|
1764
|
+
if (this._ctx.disposed) {
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
scheduleTask3(this._ctx, async () => {
|
|
1768
|
+
try {
|
|
1769
|
+
await this._initiateConnection({
|
|
1770
|
+
peerKey: peer.toHex()
|
|
1771
|
+
});
|
|
1772
|
+
} catch (err) {
|
|
1773
|
+
log5("initiation error", err, {
|
|
1774
|
+
F: __dxlog_file5,
|
|
1775
|
+
L: 343,
|
|
1776
|
+
S: this,
|
|
1777
|
+
C: (f, a) => f(...a)
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
},
|
|
1782
|
+
disconnect: async (peer) => {
|
|
1783
|
+
if (this._ctx.disposed) {
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
scheduleTask3(this._ctx, async () => {
|
|
1787
|
+
await this._closeConnection({
|
|
1788
|
+
peerKey: peer.toHex()
|
|
1789
|
+
});
|
|
1790
|
+
this._topology.update();
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Creates a connection then sends message over signal network.
|
|
1797
|
+
*/
|
|
1798
|
+
async _initiateConnection(remotePeer) {
|
|
1799
|
+
const ctx = this._ctx;
|
|
1800
|
+
const peer = this._getOrCreatePeer(remotePeer);
|
|
1801
|
+
if (remotePeer.peerKey < this._ownPeer.peerKey) {
|
|
1802
|
+
log5("initiation delay", {
|
|
1803
|
+
remotePeer
|
|
1804
|
+
}, {
|
|
1805
|
+
F: __dxlog_file5,
|
|
1806
|
+
L: 371,
|
|
1807
|
+
S: this,
|
|
1808
|
+
C: (f, a) => f(...a)
|
|
1809
|
+
});
|
|
1810
|
+
await sleep2(this._initiationDelay);
|
|
1811
|
+
}
|
|
1812
|
+
if (ctx.disposed) {
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
if (this._isUnregistered(peer)) {
|
|
1816
|
+
throw new Error("Peer left during initiation delay");
|
|
1817
|
+
}
|
|
1818
|
+
if (peer.connection) {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
log5("initiating connection...", {
|
|
1822
|
+
remotePeer
|
|
1823
|
+
}, {
|
|
1824
|
+
F: __dxlog_file5,
|
|
1825
|
+
L: 387,
|
|
1826
|
+
S: this,
|
|
1827
|
+
C: (f, a) => f(...a)
|
|
1828
|
+
});
|
|
1829
|
+
await peer.initiateConnection();
|
|
1830
|
+
this._topology.update();
|
|
1831
|
+
log5("initiated", {
|
|
1832
|
+
remotePeer
|
|
1833
|
+
}, {
|
|
1834
|
+
F: __dxlog_file5,
|
|
1835
|
+
L: 390,
|
|
1836
|
+
S: this,
|
|
1837
|
+
C: (f, a) => f(...a)
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
async _closeConnection(peerInfo) {
|
|
1841
|
+
const peer = this._peers.get(peerInfo);
|
|
1842
|
+
if (!peer) {
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
await peer.closeConnection();
|
|
1846
|
+
}
|
|
1847
|
+
_isUnregistered(peer) {
|
|
1848
|
+
return !peer || this._peers.get(peer.remoteInfo) !== peer;
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
_ts_decorate3([
|
|
1852
|
+
logInfo2
|
|
1853
|
+
], Swarm.prototype, "_instanceId", void 0);
|
|
1854
|
+
_ts_decorate3([
|
|
1855
|
+
logInfo2
|
|
1856
|
+
], Swarm.prototype, "ownPeer", null);
|
|
1857
|
+
_ts_decorate3([
|
|
1858
|
+
logInfo2
|
|
1859
|
+
], Swarm.prototype, "topic", null);
|
|
1860
|
+
_ts_decorate3([
|
|
1861
|
+
synchronized3
|
|
1862
|
+
], Swarm.prototype, "onSwarmEvent", null);
|
|
1863
|
+
_ts_decorate3([
|
|
1864
|
+
synchronized3
|
|
1865
|
+
], Swarm.prototype, "onOffer", null);
|
|
1866
|
+
_ts_decorate3([
|
|
1867
|
+
synchronized3
|
|
1868
|
+
], Swarm.prototype, "goOffline", null);
|
|
1869
|
+
_ts_decorate3([
|
|
1870
|
+
synchronized3
|
|
1871
|
+
], Swarm.prototype, "goOnline", null);
|
|
1872
|
+
|
|
1873
|
+
// packages/core/mesh/network-manager/src/swarm/swarm-mapper.ts
|
|
1874
|
+
import { Event as Event4, EventSubscriptions } from "@dxos/async";
|
|
1875
|
+
import { PublicKey as PublicKey5 } from "@dxos/keys";
|
|
1876
|
+
import { log as log6 } from "@dxos/log";
|
|
1877
|
+
import { PeerInfoHash as PeerInfoHash2 } from "@dxos/messaging";
|
|
1878
|
+
import { ComplexMap as ComplexMap3 } from "@dxos/util";
|
|
1879
|
+
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/swarm-mapper.ts";
|
|
1880
|
+
var SwarmMapper = class {
|
|
1881
|
+
get peers() {
|
|
1882
|
+
return Array.from(this._peers.values());
|
|
1883
|
+
}
|
|
1884
|
+
constructor(_swarm) {
|
|
1885
|
+
this._swarm = _swarm;
|
|
1886
|
+
this._subscriptions = new EventSubscriptions();
|
|
1887
|
+
this._connectionSubscriptions = new ComplexMap3(PeerInfoHash2);
|
|
1888
|
+
this._peers = new ComplexMap3(PeerInfoHash2);
|
|
1889
|
+
this.mapUpdated = new Event4();
|
|
1890
|
+
this._subscriptions.add(_swarm.connectionAdded.on((connection) => {
|
|
1891
|
+
this._update();
|
|
1892
|
+
this._connectionSubscriptions.set(connection.remoteInfo, connection.stateChanged.on(() => {
|
|
1893
|
+
this._update();
|
|
1894
|
+
}));
|
|
1895
|
+
}));
|
|
1896
|
+
this._subscriptions.add(_swarm.disconnected.on((peerId) => {
|
|
1897
|
+
this._connectionSubscriptions.get(peerId)?.();
|
|
1898
|
+
this._connectionSubscriptions.delete(peerId);
|
|
1899
|
+
this._update();
|
|
1900
|
+
}));
|
|
1901
|
+
this._update();
|
|
1902
|
+
}
|
|
1903
|
+
_update() {
|
|
1904
|
+
log6("updating swarm", void 0, {
|
|
1905
|
+
F: __dxlog_file6,
|
|
1906
|
+
L: 73,
|
|
1907
|
+
S: this,
|
|
1908
|
+
C: (f, a) => f(...a)
|
|
1909
|
+
});
|
|
1910
|
+
this._peers.clear();
|
|
1911
|
+
this._peers.set(this._swarm.ownPeer, {
|
|
1912
|
+
id: this._swarm.ownPeerId,
|
|
1913
|
+
state: "ME",
|
|
1914
|
+
connections: []
|
|
1915
|
+
});
|
|
1916
|
+
for (const connection of this._swarm.connections) {
|
|
1917
|
+
this._peers.set(connection.remoteInfo, {
|
|
1918
|
+
id: PublicKey5.from(connection.remoteInfo.peerKey),
|
|
1919
|
+
state: connection.state,
|
|
1920
|
+
connections: [
|
|
1921
|
+
this._swarm.ownPeerId
|
|
1922
|
+
]
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
log6("graph changed", {
|
|
1926
|
+
directConnections: this._swarm.connections.length,
|
|
1927
|
+
totalPeersInSwarm: this._peers.size
|
|
1928
|
+
}, {
|
|
1929
|
+
F: __dxlog_file6,
|
|
1930
|
+
L: 114,
|
|
1931
|
+
S: this,
|
|
1932
|
+
C: (f, a) => f(...a)
|
|
1933
|
+
});
|
|
1934
|
+
this.mapUpdated.emit(Array.from(this._peers.values()));
|
|
1935
|
+
}
|
|
1936
|
+
// TODO(burdon): Async open/close.
|
|
1937
|
+
destroy() {
|
|
1938
|
+
Array.from(this._connectionSubscriptions.values()).forEach((cb) => cb());
|
|
1939
|
+
this._subscriptions.clear();
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1943
|
+
// packages/core/mesh/network-manager/src/swarm/connection-limiter.ts
|
|
1944
|
+
import { DeferredTask as DeferredTask2 } from "@dxos/async";
|
|
1945
|
+
import { Context as Context5 } from "@dxos/context";
|
|
1946
|
+
import { invariant as invariant5 } from "@dxos/invariant";
|
|
1947
|
+
import { PublicKey as PublicKey6 } from "@dxos/keys";
|
|
1948
|
+
import { log as log7 } from "@dxos/log";
|
|
1949
|
+
import { CancelledError as CancelledError3 } from "@dxos/protocols";
|
|
1950
|
+
import { ComplexMap as ComplexMap4 } from "@dxos/util";
|
|
1951
|
+
var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/connection-limiter.ts";
|
|
1952
|
+
var MAX_CONCURRENT_INITIATING_CONNECTIONS = 50;
|
|
1953
|
+
var ConnectionLimiter = class {
|
|
1954
|
+
constructor({ maxConcurrentInitConnections = MAX_CONCURRENT_INITIATING_CONNECTIONS } = {}) {
|
|
1955
|
+
this._ctx = new Context5(void 0, {
|
|
1956
|
+
F: __dxlog_file7,
|
|
1957
|
+
L: 23
|
|
1958
|
+
});
|
|
1959
|
+
/**
|
|
1960
|
+
* Queue of promises to resolve when initiating connections amount is below the limit.
|
|
1961
|
+
*/
|
|
1962
|
+
this._waitingPromises = new ComplexMap4(PublicKey6.hash);
|
|
1963
|
+
this.resolveWaitingPromises = new DeferredTask2(this._ctx, async () => {
|
|
1964
|
+
Array.from(this._waitingPromises.values()).slice(0, this._maxConcurrentInitConnections).forEach(({ resolve }) => {
|
|
1965
|
+
resolve();
|
|
1966
|
+
});
|
|
1967
|
+
});
|
|
1968
|
+
this._maxConcurrentInitConnections = maxConcurrentInitConnections;
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* @returns Promise that resolves in queue when connections amount with 'CONNECTING' state is below the limit.
|
|
1972
|
+
*/
|
|
1973
|
+
async connecting(sessionId) {
|
|
1974
|
+
invariant5(!this._waitingPromises.has(sessionId), "Peer is already waiting for connection", {
|
|
1975
|
+
F: __dxlog_file7,
|
|
1976
|
+
L: 48,
|
|
1977
|
+
S: this,
|
|
1978
|
+
A: [
|
|
1979
|
+
"!this._waitingPromises.has(sessionId)",
|
|
1980
|
+
"'Peer is already waiting for connection'"
|
|
1981
|
+
]
|
|
1982
|
+
});
|
|
1983
|
+
log7("waiting", {
|
|
1984
|
+
sessionId
|
|
1985
|
+
}, {
|
|
1986
|
+
F: __dxlog_file7,
|
|
1987
|
+
L: 49,
|
|
1988
|
+
S: this,
|
|
1989
|
+
C: (f, a) => f(...a)
|
|
1990
|
+
});
|
|
1991
|
+
await new Promise((resolve, reject) => {
|
|
1992
|
+
this._waitingPromises.set(sessionId, {
|
|
1993
|
+
resolve,
|
|
1994
|
+
reject
|
|
1995
|
+
});
|
|
1996
|
+
this.resolveWaitingPromises.schedule();
|
|
1997
|
+
});
|
|
1998
|
+
log7("allow", {
|
|
1999
|
+
sessionId
|
|
2000
|
+
}, {
|
|
2001
|
+
F: __dxlog_file7,
|
|
2002
|
+
L: 57,
|
|
2003
|
+
S: this,
|
|
2004
|
+
C: (f, a) => f(...a)
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Rejects promise returned by `connecting` method.
|
|
2009
|
+
*/
|
|
2010
|
+
doneConnecting(sessionId) {
|
|
2011
|
+
log7("done", {
|
|
2012
|
+
sessionId
|
|
2013
|
+
}, {
|
|
2014
|
+
F: __dxlog_file7,
|
|
2015
|
+
L: 64,
|
|
2016
|
+
S: this,
|
|
2017
|
+
C: (f, a) => f(...a)
|
|
2018
|
+
});
|
|
2019
|
+
if (!this._waitingPromises.has(sessionId)) {
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
2022
|
+
this._waitingPromises.get(sessionId).reject(new CancelledError3());
|
|
2023
|
+
this._waitingPromises.delete(sessionId);
|
|
2024
|
+
this.resolveWaitingPromises.schedule();
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
|
|
2028
|
+
// packages/core/mesh/network-manager/src/connection-log.ts
|
|
2029
|
+
import { Event as Event5 } from "@dxos/async";
|
|
2030
|
+
import { raise } from "@dxos/debug";
|
|
2031
|
+
import { PublicKey as PublicKey7 } from "@dxos/keys";
|
|
2032
|
+
import { ComplexMap as ComplexMap5 } from "@dxos/util";
|
|
2033
|
+
var CONNECTION_GC_THRESHOLD = 1e3 * 60 * 15;
|
|
2034
|
+
var EventType;
|
|
2035
|
+
(function(EventType2) {
|
|
2036
|
+
EventType2["CONNECTION_STATE_CHANGED"] = "CONNECTION_STATE_CHANGED";
|
|
2037
|
+
EventType2["PROTOCOL_ERROR"] = "PROTOCOL_ERROR";
|
|
2038
|
+
EventType2["PROTOCOL_EXTENSIONS_INITIALIZED"] = "PROTOCOL_EXTENSIONS_INITIALIZED";
|
|
2039
|
+
EventType2["PROTOCOL_EXTENSIONS_HANDSHAKE"] = "PROTOCOL_EXTENSIONS_HANDSHAKE";
|
|
2040
|
+
EventType2["PROTOCOL_HANDSHAKE"] = "PROTOCOL_HANDSHAKE";
|
|
2041
|
+
})(EventType || (EventType = {}));
|
|
2042
|
+
var ConnectionLog = class {
|
|
2043
|
+
constructor() {
|
|
2044
|
+
/**
|
|
2045
|
+
* SwarmId => info
|
|
2046
|
+
*/
|
|
2047
|
+
this._swarms = new ComplexMap5(PublicKey7.hash);
|
|
2048
|
+
this.update = new Event5();
|
|
2049
|
+
}
|
|
2050
|
+
getSwarmInfo(swarmId) {
|
|
2051
|
+
return this._swarms.get(swarmId) ?? raise(new Error(`Swarm not found: ${swarmId}`));
|
|
2052
|
+
}
|
|
2053
|
+
get swarms() {
|
|
2054
|
+
return Array.from(this._swarms.values());
|
|
2055
|
+
}
|
|
2056
|
+
joinedSwarm(swarm) {
|
|
2057
|
+
const info = {
|
|
2058
|
+
id: PublicKey7.from(swarm._instanceId),
|
|
2059
|
+
topic: swarm.topic,
|
|
2060
|
+
isActive: true,
|
|
2061
|
+
label: swarm.label,
|
|
2062
|
+
connections: []
|
|
2063
|
+
};
|
|
2064
|
+
this._swarms.set(PublicKey7.from(swarm._instanceId), info);
|
|
2065
|
+
this.update.emit();
|
|
2066
|
+
swarm.connectionAdded.on((connection) => {
|
|
2067
|
+
const connectionInfo = {
|
|
2068
|
+
state: ConnectionState.CREATED,
|
|
2069
|
+
closeReason: connection.closeReason,
|
|
2070
|
+
remotePeerId: PublicKey7.from(connection.remoteInfo.peerKey),
|
|
2071
|
+
sessionId: connection.sessionId,
|
|
2072
|
+
transport: connection.transport && Object.getPrototypeOf(connection.transport).constructor.name,
|
|
2073
|
+
protocolExtensions: [],
|
|
2074
|
+
events: [],
|
|
2075
|
+
lastUpdate: /* @__PURE__ */ new Date()
|
|
2076
|
+
};
|
|
2077
|
+
info.connections.push(connectionInfo);
|
|
2078
|
+
this.update.emit();
|
|
2079
|
+
connection.stateChanged.on(async (state) => {
|
|
2080
|
+
connectionInfo.state = state;
|
|
2081
|
+
connectionInfo.closeReason = connection.closeReason;
|
|
2082
|
+
connectionInfo.lastUpdate = /* @__PURE__ */ new Date();
|
|
2083
|
+
connectionInfo.events.push({
|
|
2084
|
+
type: "CONNECTION_STATE_CHANGED",
|
|
2085
|
+
newState: state
|
|
2086
|
+
});
|
|
2087
|
+
if (state === ConnectionState.CONNECTED) {
|
|
2088
|
+
const details = await connection.transport?.getDetails();
|
|
2089
|
+
connectionInfo.transportDetails = details;
|
|
2090
|
+
}
|
|
2091
|
+
this.update.emit();
|
|
2092
|
+
});
|
|
2093
|
+
connection.protocol?.stats?.on((stats) => {
|
|
2094
|
+
connectionInfo.readBufferSize = stats.readBufferSize;
|
|
2095
|
+
connectionInfo.writeBufferSize = stats.writeBufferSize;
|
|
2096
|
+
connectionInfo.streams = stats.channels;
|
|
2097
|
+
connectionInfo.lastUpdate = /* @__PURE__ */ new Date();
|
|
2098
|
+
this.update.emit();
|
|
2099
|
+
});
|
|
2100
|
+
connection.transportStats?.on((stats) => {
|
|
2101
|
+
connectionInfo.transportBytesSent = stats.bytesSent;
|
|
2102
|
+
connectionInfo.transportBytesReceived = stats.bytesReceived;
|
|
2103
|
+
connectionInfo.transportPacketsSent = stats.packetsSent;
|
|
2104
|
+
connectionInfo.transportPacketsReceived = stats.packetsReceived;
|
|
2105
|
+
});
|
|
2106
|
+
gcSwarm(info);
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
leftSwarm(swarm) {
|
|
2110
|
+
this.getSwarmInfo(PublicKey7.from(swarm._instanceId)).isActive = false;
|
|
2111
|
+
this.update.emit();
|
|
2112
|
+
}
|
|
2113
|
+
};
|
|
2114
|
+
var gcSwarm = (swarm) => {
|
|
2115
|
+
swarm.connections = swarm.connections?.filter((connection) => {
|
|
2116
|
+
return connection.lastUpdate ? Date.now() - connection.lastUpdate.getTime() < CONNECTION_GC_THRESHOLD : true;
|
|
2117
|
+
});
|
|
2118
|
+
};
|
|
2119
|
+
|
|
2120
|
+
// packages/core/mesh/network-manager/src/network-manager.ts
|
|
2121
|
+
import { Event as Event6, synchronized as synchronized4 } from "@dxos/async";
|
|
2122
|
+
import { invariant as invariant6 } from "@dxos/invariant";
|
|
2123
|
+
import { PublicKey as PublicKey8 } from "@dxos/keys";
|
|
2124
|
+
import { log as log8 } from "@dxos/log";
|
|
2125
|
+
import { Messenger } from "@dxos/messaging";
|
|
2126
|
+
import { trace as trace3 } from "@dxos/protocols";
|
|
2127
|
+
import { ConnectionState as ConnectionState2 } from "@dxos/protocols/proto/dxos/client/services";
|
|
2128
|
+
import { ComplexMap as ComplexMap6 } from "@dxos/util";
|
|
2129
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
2130
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2131
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2132
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2133
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2134
|
+
}
|
|
2135
|
+
var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/network-manager.ts";
|
|
2136
|
+
var SwarmNetworkManager = class {
|
|
2137
|
+
constructor({ transportFactory, signalManager, enableDevtoolsLogging, peerInfo }) {
|
|
2138
|
+
/**
|
|
2139
|
+
* @internal
|
|
2140
|
+
*/
|
|
2141
|
+
this._swarms = new ComplexMap6(PublicKey8.hash);
|
|
2142
|
+
this._mappers = new ComplexMap6(PublicKey8.hash);
|
|
2143
|
+
this._instanceId = PublicKey8.random().toHex();
|
|
2144
|
+
this._peerInfo = void 0;
|
|
2145
|
+
this._connectionState = ConnectionState2.ONLINE;
|
|
2146
|
+
this.connectionStateChanged = new Event6();
|
|
2147
|
+
this.topicsUpdated = new Event6();
|
|
2148
|
+
this._transportFactory = transportFactory;
|
|
2149
|
+
this._signalManager = signalManager;
|
|
2150
|
+
this._signalManager.swarmEvent.on((event) => this._swarms.get(event.topic)?.onSwarmEvent(event));
|
|
2151
|
+
this._messenger = new Messenger({
|
|
2152
|
+
signalManager: this._signalManager
|
|
2153
|
+
});
|
|
2154
|
+
this._signalConnection = {
|
|
2155
|
+
join: (opts) => this._signalManager.join(opts),
|
|
2156
|
+
leave: (opts) => this._signalManager.leave(opts)
|
|
2157
|
+
};
|
|
2158
|
+
this._peerInfo = peerInfo;
|
|
2159
|
+
this._connectionLimiter = new ConnectionLimiter();
|
|
2160
|
+
if (enableDevtoolsLogging) {
|
|
2161
|
+
this._connectionLog = new ConnectionLog();
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
// TODO(burdon): Remove access (Devtools only).
|
|
2165
|
+
get connectionLog() {
|
|
2166
|
+
return this._connectionLog;
|
|
2167
|
+
}
|
|
2168
|
+
get connectionState() {
|
|
2169
|
+
return this._connectionState;
|
|
2170
|
+
}
|
|
2171
|
+
// TODO(burdon): Reconcile with "discovery_key".
|
|
2172
|
+
get topics() {
|
|
2173
|
+
return Array.from(this._swarms.keys());
|
|
2174
|
+
}
|
|
2175
|
+
getSwarmMap(topic) {
|
|
2176
|
+
return this._mappers.get(topic);
|
|
2177
|
+
}
|
|
2178
|
+
getSwarm(topic) {
|
|
2179
|
+
return this._swarms.get(topic);
|
|
2180
|
+
}
|
|
2181
|
+
setPeerInfo(peerInfo) {
|
|
2182
|
+
this._peerInfo = peerInfo;
|
|
2183
|
+
}
|
|
2184
|
+
async open() {
|
|
2185
|
+
log8.trace("dxos.mesh.network-manager.open", trace3.begin({
|
|
2186
|
+
id: this._instanceId
|
|
2187
|
+
}), {
|
|
2188
|
+
F: __dxlog_file8,
|
|
2189
|
+
L: 133,
|
|
2190
|
+
S: this,
|
|
2191
|
+
C: (f, a) => f(...a)
|
|
2192
|
+
});
|
|
2193
|
+
await this._messenger.open();
|
|
2194
|
+
await this._signalManager.open();
|
|
2195
|
+
log8.trace("dxos.mesh.network-manager.open", trace3.end({
|
|
2196
|
+
id: this._instanceId
|
|
2197
|
+
}), {
|
|
2198
|
+
F: __dxlog_file8,
|
|
2199
|
+
L: 136,
|
|
2200
|
+
S: this,
|
|
2201
|
+
C: (f, a) => f(...a)
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
async close() {
|
|
2205
|
+
for (const topic of this._swarms.keys()) {
|
|
2206
|
+
await this.leaveSwarm(topic).catch((err) => {
|
|
2207
|
+
log8(err, void 0, {
|
|
2208
|
+
F: __dxlog_file8,
|
|
2209
|
+
L: 142,
|
|
2210
|
+
S: this,
|
|
2211
|
+
C: (f, a) => f(...a)
|
|
2212
|
+
});
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
await this._messenger.close();
|
|
2216
|
+
await this._signalManager.close();
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Join the swarm.
|
|
2220
|
+
*/
|
|
2221
|
+
async joinSwarm({ topic, peerInfo, topology, protocolProvider: protocol, label }) {
|
|
2222
|
+
invariant6(PublicKey8.isPublicKey(topic), void 0, {
|
|
2223
|
+
F: __dxlog_file8,
|
|
2224
|
+
L: 161,
|
|
2225
|
+
S: this,
|
|
2226
|
+
A: [
|
|
2227
|
+
"PublicKey.isPublicKey(topic)",
|
|
2228
|
+
""
|
|
2229
|
+
]
|
|
2230
|
+
});
|
|
2231
|
+
if (!peerInfo) {
|
|
2232
|
+
peerInfo = {
|
|
2233
|
+
peerKey: this._peerInfo?.peerKey ?? PublicKey8.random().toHex(),
|
|
2234
|
+
identityKey: this._peerInfo?.identityKey ?? PublicKey8.random().toHex()
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
invariant6(PublicKey8.from(peerInfo.peerKey), void 0, {
|
|
2238
|
+
F: __dxlog_file8,
|
|
2239
|
+
L: 168,
|
|
2240
|
+
S: this,
|
|
2241
|
+
A: [
|
|
2242
|
+
"PublicKey.from(peerInfo.peerKey)",
|
|
2243
|
+
""
|
|
2244
|
+
]
|
|
2245
|
+
});
|
|
2246
|
+
invariant6(PublicKey8.from(peerInfo.identityKey), void 0, {
|
|
2247
|
+
F: __dxlog_file8,
|
|
2248
|
+
L: 169,
|
|
2249
|
+
S: this,
|
|
2250
|
+
A: [
|
|
2251
|
+
"PublicKey.from(peerInfo.identityKey!)",
|
|
2252
|
+
""
|
|
2253
|
+
]
|
|
2254
|
+
});
|
|
2255
|
+
invariant6(topology, void 0, {
|
|
2256
|
+
F: __dxlog_file8,
|
|
2257
|
+
L: 170,
|
|
2258
|
+
S: this,
|
|
2259
|
+
A: [
|
|
2260
|
+
"topology",
|
|
2261
|
+
""
|
|
2262
|
+
]
|
|
2263
|
+
});
|
|
2264
|
+
invariant6(typeof protocol === "function", void 0, {
|
|
2265
|
+
F: __dxlog_file8,
|
|
2266
|
+
L: 171,
|
|
2267
|
+
S: this,
|
|
2268
|
+
A: [
|
|
2269
|
+
"typeof protocol === 'function'",
|
|
2270
|
+
""
|
|
2271
|
+
]
|
|
2272
|
+
});
|
|
2273
|
+
if (this._swarms.has(topic)) {
|
|
2274
|
+
throw new Error(`Already connected to swarm: ${PublicKey8.from(topic)}`);
|
|
2275
|
+
}
|
|
2276
|
+
log8("joining", {
|
|
2277
|
+
topic: PublicKey8.from(topic),
|
|
2278
|
+
peerInfo,
|
|
2279
|
+
topology: topology.toString()
|
|
2280
|
+
}, {
|
|
2281
|
+
F: __dxlog_file8,
|
|
2282
|
+
L: 176,
|
|
2283
|
+
S: this,
|
|
2284
|
+
C: (f, a) => f(...a)
|
|
2285
|
+
});
|
|
2286
|
+
const swarm = new Swarm(topic, peerInfo, topology, protocol, this._messenger, this._transportFactory, label, this._connectionLimiter);
|
|
2287
|
+
swarm.errors.handle((error) => {
|
|
2288
|
+
log8("swarm error", {
|
|
2289
|
+
error
|
|
2290
|
+
}, {
|
|
2291
|
+
F: __dxlog_file8,
|
|
2292
|
+
L: 189,
|
|
2293
|
+
S: this,
|
|
2294
|
+
C: (f, a) => f(...a)
|
|
2295
|
+
});
|
|
2296
|
+
});
|
|
2297
|
+
this._swarms.set(topic, swarm);
|
|
2298
|
+
this._mappers.set(topic, new SwarmMapper(swarm));
|
|
2299
|
+
await swarm.open();
|
|
2300
|
+
this._signalConnection.join({
|
|
2301
|
+
topic,
|
|
2302
|
+
peer: peerInfo
|
|
2303
|
+
}).catch((error) => log8.catch(error, void 0, {
|
|
2304
|
+
F: __dxlog_file8,
|
|
2305
|
+
L: 198,
|
|
2306
|
+
S: this,
|
|
2307
|
+
C: (f, a) => f(...a)
|
|
2308
|
+
}));
|
|
2309
|
+
this.topicsUpdated.emit();
|
|
2310
|
+
this._connectionLog?.joinedSwarm(swarm);
|
|
2311
|
+
log8("joined", {
|
|
2312
|
+
topic: PublicKey8.from(topic),
|
|
2313
|
+
count: this._swarms.size
|
|
2314
|
+
}, {
|
|
2315
|
+
F: __dxlog_file8,
|
|
2316
|
+
L: 202,
|
|
2317
|
+
S: this,
|
|
2318
|
+
C: (f, a) => f(...a)
|
|
2319
|
+
});
|
|
2320
|
+
return {
|
|
2321
|
+
close: () => this.leaveSwarm(topic)
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
/**
|
|
2325
|
+
* Close the connection.
|
|
2326
|
+
*/
|
|
2327
|
+
async leaveSwarm(topic) {
|
|
2328
|
+
if (!this._swarms.has(topic)) {
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
log8("leaving", {
|
|
2332
|
+
topic: PublicKey8.from(topic)
|
|
2333
|
+
}, {
|
|
2334
|
+
F: __dxlog_file8,
|
|
2335
|
+
L: 219,
|
|
2336
|
+
S: this,
|
|
2337
|
+
C: (f, a) => f(...a)
|
|
2338
|
+
});
|
|
2339
|
+
const swarm = this._swarms.get(topic);
|
|
2340
|
+
await this._signalConnection.leave({
|
|
2341
|
+
topic,
|
|
2342
|
+
peer: swarm.ownPeer
|
|
2343
|
+
});
|
|
2344
|
+
const map = this._mappers.get(topic);
|
|
2345
|
+
map.destroy();
|
|
2346
|
+
this._mappers.delete(topic);
|
|
2347
|
+
this._connectionLog?.leftSwarm(swarm);
|
|
2348
|
+
await swarm.destroy();
|
|
2349
|
+
this._swarms.delete(topic);
|
|
2350
|
+
this.topicsUpdated.emit();
|
|
2351
|
+
log8("left", {
|
|
2352
|
+
topic: PublicKey8.from(topic),
|
|
2353
|
+
count: this._swarms.size
|
|
2354
|
+
}, {
|
|
2355
|
+
F: __dxlog_file8,
|
|
2356
|
+
L: 233,
|
|
2357
|
+
S: this,
|
|
2358
|
+
C: (f, a) => f(...a)
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
async setConnectionState(state) {
|
|
2362
|
+
if (state === this._connectionState) {
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2365
|
+
switch (state) {
|
|
2366
|
+
case ConnectionState2.OFFLINE: {
|
|
2367
|
+
this._connectionState = state;
|
|
2368
|
+
await Promise.all([
|
|
2369
|
+
...this._swarms.values()
|
|
2370
|
+
].map((swarm) => swarm.goOffline()));
|
|
2371
|
+
await this._messenger.close();
|
|
2372
|
+
await this._signalManager.close();
|
|
2373
|
+
break;
|
|
2374
|
+
}
|
|
2375
|
+
case ConnectionState2.ONLINE: {
|
|
2376
|
+
this._connectionState = state;
|
|
2377
|
+
this._messenger.open();
|
|
2378
|
+
await Promise.all([
|
|
2379
|
+
...this._swarms.values()
|
|
2380
|
+
].map((swarm) => swarm.goOnline()));
|
|
2381
|
+
await this._signalManager.open();
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
this.connectionStateChanged.emit(this._connectionState);
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2388
|
+
_ts_decorate4([
|
|
2389
|
+
synchronized4
|
|
2390
|
+
], SwarmNetworkManager.prototype, "joinSwarm", null);
|
|
2391
|
+
_ts_decorate4([
|
|
2392
|
+
synchronized4
|
|
2393
|
+
], SwarmNetworkManager.prototype, "leaveSwarm", null);
|
|
2394
|
+
|
|
2395
|
+
// packages/core/mesh/network-manager/src/topology/fully-connected-topology.ts
|
|
2396
|
+
import { invariant as invariant7 } from "@dxos/invariant";
|
|
2397
|
+
var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/topology/fully-connected-topology.ts";
|
|
2398
|
+
var FullyConnectedTopology = class {
|
|
2399
|
+
toString() {
|
|
2400
|
+
return "FullyConnectedTopology";
|
|
2401
|
+
}
|
|
2402
|
+
init(controller) {
|
|
2403
|
+
invariant7(!this._controller, "Already initialized", {
|
|
2404
|
+
F: __dxlog_file9,
|
|
2405
|
+
L: 18,
|
|
2406
|
+
S: this,
|
|
2407
|
+
A: [
|
|
2408
|
+
"!this._controller",
|
|
2409
|
+
"'Already initialized'"
|
|
2410
|
+
]
|
|
2411
|
+
});
|
|
2412
|
+
this._controller = controller;
|
|
2413
|
+
}
|
|
2414
|
+
update() {
|
|
2415
|
+
invariant7(this._controller, "Not initialized", {
|
|
2416
|
+
F: __dxlog_file9,
|
|
2417
|
+
L: 23,
|
|
2418
|
+
S: this,
|
|
2419
|
+
A: [
|
|
2420
|
+
"this._controller",
|
|
2421
|
+
"'Not initialized'"
|
|
2422
|
+
]
|
|
2423
|
+
});
|
|
2424
|
+
const { candidates: discovered } = this._controller.getState();
|
|
2425
|
+
for (const peer of discovered) {
|
|
2426
|
+
this._controller.connect(peer);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
async onOffer(peer) {
|
|
2430
|
+
return true;
|
|
2431
|
+
}
|
|
2432
|
+
async destroy() {
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
|
|
2436
|
+
// packages/core/mesh/network-manager/src/topology/mmst-topology.ts
|
|
2437
|
+
import { invariant as invariant8 } from "@dxos/invariant";
|
|
2438
|
+
import { log as log9 } from "@dxos/log";
|
|
2439
|
+
var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/topology/mmst-topology.ts";
|
|
2440
|
+
var MIN_UPDATE_INTERVAL = 1e3 * 10;
|
|
2441
|
+
var MAX_CHANGES_PER_UPDATE = 1;
|
|
2442
|
+
var MMSTTopology = class {
|
|
2443
|
+
constructor({ originateConnections = 2, maxPeers = 4, sampleSize = 10 } = {}) {
|
|
2444
|
+
this._sampleCollected = false;
|
|
2445
|
+
this._lastAction = /* @__PURE__ */ new Date(0);
|
|
2446
|
+
this._originateConnections = originateConnections;
|
|
2447
|
+
this._maxPeers = maxPeers;
|
|
2448
|
+
this._sampleSize = sampleSize;
|
|
2449
|
+
}
|
|
2450
|
+
init(controller) {
|
|
2451
|
+
invariant8(!this._controller, "Already initialized", {
|
|
2452
|
+
F: __dxlog_file10,
|
|
2453
|
+
L: 49,
|
|
2454
|
+
S: this,
|
|
2455
|
+
A: [
|
|
2456
|
+
"!this._controller",
|
|
2457
|
+
"'Already initialized'"
|
|
2458
|
+
]
|
|
2459
|
+
});
|
|
2460
|
+
this._controller = controller;
|
|
2461
|
+
}
|
|
2462
|
+
update() {
|
|
2463
|
+
invariant8(this._controller, "Not initialized", {
|
|
2464
|
+
F: __dxlog_file10,
|
|
2465
|
+
L: 54,
|
|
2466
|
+
S: this,
|
|
2467
|
+
A: [
|
|
2468
|
+
"this._controller",
|
|
2469
|
+
"'Not initialized'"
|
|
2470
|
+
]
|
|
2471
|
+
});
|
|
2472
|
+
const { connected, candidates } = this._controller.getState();
|
|
2473
|
+
if (this._sampleCollected || connected.length > this._maxPeers || candidates.length > 0) {
|
|
2474
|
+
log9("Running the algorithm.", void 0, {
|
|
2475
|
+
F: __dxlog_file10,
|
|
2476
|
+
L: 58,
|
|
2477
|
+
S: this,
|
|
2478
|
+
C: (f, a) => f(...a)
|
|
2479
|
+
});
|
|
2480
|
+
this._sampleCollected = true;
|
|
2481
|
+
this._runAlgorithm();
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
forceUpdate() {
|
|
2485
|
+
this._lastAction = /* @__PURE__ */ new Date(0);
|
|
2486
|
+
this.update();
|
|
2487
|
+
}
|
|
2488
|
+
async onOffer(peer) {
|
|
2489
|
+
invariant8(this._controller, "Not initialized", {
|
|
2490
|
+
F: __dxlog_file10,
|
|
2491
|
+
L: 70,
|
|
2492
|
+
S: this,
|
|
2493
|
+
A: [
|
|
2494
|
+
"this._controller",
|
|
2495
|
+
"'Not initialized'"
|
|
2496
|
+
]
|
|
2497
|
+
});
|
|
2498
|
+
const { connected } = this._controller.getState();
|
|
2499
|
+
const accept = connected.length < this._maxPeers;
|
|
2500
|
+
log9(`Offer ${peer} accept=${accept}`, void 0, {
|
|
2501
|
+
F: __dxlog_file10,
|
|
2502
|
+
L: 73,
|
|
2503
|
+
S: this,
|
|
2504
|
+
C: (f, a) => f(...a)
|
|
2505
|
+
});
|
|
2506
|
+
return accept;
|
|
2507
|
+
}
|
|
2508
|
+
async destroy() {
|
|
2509
|
+
}
|
|
2510
|
+
_runAlgorithm() {
|
|
2511
|
+
invariant8(this._controller, "Not initialized", {
|
|
2512
|
+
F: __dxlog_file10,
|
|
2513
|
+
L: 82,
|
|
2514
|
+
S: this,
|
|
2515
|
+
A: [
|
|
2516
|
+
"this._controller",
|
|
2517
|
+
"'Not initialized'"
|
|
2518
|
+
]
|
|
2519
|
+
});
|
|
2520
|
+
const { connected, candidates, ownPeerId } = this._controller.getState();
|
|
2521
|
+
if (connected.length > this._maxPeers) {
|
|
2522
|
+
log9(`disconnect ${connected.length - this._maxPeers} peers.`, void 0, {
|
|
2523
|
+
F: __dxlog_file10,
|
|
2524
|
+
L: 88,
|
|
2525
|
+
S: this,
|
|
2526
|
+
C: (f, a) => f(...a)
|
|
2527
|
+
});
|
|
2528
|
+
const sorted = sortByXorDistance(connected, ownPeerId).reverse().slice(0, this._maxPeers - connected.length);
|
|
2529
|
+
invariant8(sorted.length === 0, void 0, {
|
|
2530
|
+
F: __dxlog_file10,
|
|
2531
|
+
L: 92,
|
|
2532
|
+
S: this,
|
|
2533
|
+
A: [
|
|
2534
|
+
"sorted.length === 0",
|
|
2535
|
+
""
|
|
2536
|
+
]
|
|
2537
|
+
});
|
|
2538
|
+
if (sorted.length > MAX_CHANGES_PER_UPDATE) {
|
|
2539
|
+
log9(`want to disconnect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`, void 0, {
|
|
2540
|
+
F: __dxlog_file10,
|
|
2541
|
+
L: 95,
|
|
2542
|
+
S: this,
|
|
2543
|
+
C: (f, a) => f(...a)
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {
|
|
2547
|
+
for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {
|
|
2548
|
+
log9(`Disconnect ${peer}.`, void 0, {
|
|
2549
|
+
F: __dxlog_file10,
|
|
2550
|
+
L: 100,
|
|
2551
|
+
S: this,
|
|
2552
|
+
C: (f, a) => f(...a)
|
|
2553
|
+
});
|
|
2554
|
+
this._controller.disconnect(peer);
|
|
2555
|
+
}
|
|
2556
|
+
this._lastAction = /* @__PURE__ */ new Date();
|
|
2557
|
+
} else {
|
|
2558
|
+
log9("rate limited disconnect", void 0, {
|
|
2559
|
+
F: __dxlog_file10,
|
|
2560
|
+
L: 105,
|
|
2561
|
+
S: this,
|
|
2562
|
+
C: (f, a) => f(...a)
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
} else if (connected.length < this._originateConnections) {
|
|
2566
|
+
log9(`connect ${this._originateConnections - connected.length} peers.`, void 0, {
|
|
2567
|
+
F: __dxlog_file10,
|
|
2568
|
+
L: 109,
|
|
2569
|
+
S: this,
|
|
2570
|
+
C: (f, a) => f(...a)
|
|
2571
|
+
});
|
|
2572
|
+
const sample = candidates.sort(() => Math.random() - 0.5).slice(0, this._sampleSize);
|
|
2573
|
+
const sorted = sortByXorDistance(sample, ownPeerId).slice(0, this._originateConnections - connected.length);
|
|
2574
|
+
if (sorted.length > MAX_CHANGES_PER_UPDATE) {
|
|
2575
|
+
log9(`want to connect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`, void 0, {
|
|
2576
|
+
F: __dxlog_file10,
|
|
2577
|
+
L: 114,
|
|
2578
|
+
S: this,
|
|
2579
|
+
C: (f, a) => f(...a)
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {
|
|
2583
|
+
for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {
|
|
2584
|
+
log9(`Connect ${peer}.`, void 0, {
|
|
2585
|
+
F: __dxlog_file10,
|
|
2586
|
+
L: 118,
|
|
2587
|
+
S: this,
|
|
2588
|
+
C: (f, a) => f(...a)
|
|
2589
|
+
});
|
|
2590
|
+
this._controller.connect(peer);
|
|
2591
|
+
}
|
|
2592
|
+
this._lastAction = /* @__PURE__ */ new Date();
|
|
2593
|
+
} else {
|
|
2594
|
+
log9("rate limited connect", void 0, {
|
|
2595
|
+
F: __dxlog_file10,
|
|
2596
|
+
L: 123,
|
|
2597
|
+
S: this,
|
|
2598
|
+
C: (f, a) => f(...a)
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
toString() {
|
|
2604
|
+
return "MMSTTopology";
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
var sortByXorDistance = (keys, reference) => {
|
|
2608
|
+
const sorted = keys.sort((a, b) => {
|
|
2609
|
+
return compareXor(distXor(a.asBuffer(), reference.asBuffer()), distXor(b.asBuffer(), reference.asBuffer()));
|
|
2610
|
+
});
|
|
2611
|
+
log9("Sorted keys", {
|
|
2612
|
+
keys,
|
|
2613
|
+
reference,
|
|
2614
|
+
sorted
|
|
2615
|
+
}, {
|
|
2616
|
+
F: __dxlog_file10,
|
|
2617
|
+
L: 137,
|
|
2618
|
+
S: void 0,
|
|
2619
|
+
C: (f, a) => f(...a)
|
|
2620
|
+
});
|
|
2621
|
+
return sorted;
|
|
2622
|
+
};
|
|
2623
|
+
var distXor = (a, b) => {
|
|
2624
|
+
const maxLength = Math.max(a.length, b.length);
|
|
2625
|
+
const result = Buffer.allocUnsafe(maxLength);
|
|
2626
|
+
for (let i = 0; i < maxLength; i++) {
|
|
2627
|
+
result[i] = (a[i] || 0) ^ (b[i] || 0);
|
|
2628
|
+
}
|
|
2629
|
+
return result;
|
|
2630
|
+
};
|
|
2631
|
+
var compareXor = (a, b) => {
|
|
2632
|
+
const maxLength = Math.max(a.length, b.length);
|
|
2633
|
+
for (let i = 0; i < maxLength; i++) {
|
|
2634
|
+
if ((a[i] || 0) === (b[i] || 0)) {
|
|
2635
|
+
continue;
|
|
2636
|
+
}
|
|
2637
|
+
return (a[i] || 0) < (b[i] || 0) ? -1 : 1;
|
|
2638
|
+
}
|
|
2639
|
+
return 0;
|
|
2640
|
+
};
|
|
2641
|
+
|
|
2642
|
+
// packages/core/mesh/network-manager/src/topology/star-topology.ts
|
|
2643
|
+
import { invariant as invariant9 } from "@dxos/invariant";
|
|
2644
|
+
import { log as log10 } from "@dxos/log";
|
|
2645
|
+
var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/topology/star-topology.ts";
|
|
2646
|
+
var StarTopology = class {
|
|
2647
|
+
constructor(_centralPeer) {
|
|
2648
|
+
this._centralPeer = _centralPeer;
|
|
2649
|
+
}
|
|
2650
|
+
toString() {
|
|
2651
|
+
return `StarTopology(${this._centralPeer.truncate()})`;
|
|
2652
|
+
}
|
|
2653
|
+
init(controller) {
|
|
2654
|
+
invariant9(!this._controller, "Already initialized.", {
|
|
2655
|
+
F: __dxlog_file11,
|
|
2656
|
+
L: 21,
|
|
2657
|
+
S: this,
|
|
2658
|
+
A: [
|
|
2659
|
+
"!this._controller",
|
|
2660
|
+
"'Already initialized.'"
|
|
2661
|
+
]
|
|
2662
|
+
});
|
|
2663
|
+
this._controller = controller;
|
|
2664
|
+
}
|
|
2665
|
+
update() {
|
|
2666
|
+
invariant9(this._controller, "Not initialized.", {
|
|
2667
|
+
F: __dxlog_file11,
|
|
2668
|
+
L: 26,
|
|
2669
|
+
S: this,
|
|
2670
|
+
A: [
|
|
2671
|
+
"this._controller",
|
|
2672
|
+
"'Not initialized.'"
|
|
2673
|
+
]
|
|
2674
|
+
});
|
|
2675
|
+
const { candidates, connected, ownPeerId } = this._controller.getState();
|
|
2676
|
+
if (!ownPeerId.equals(this._centralPeer)) {
|
|
2677
|
+
log10("leaf peer dropping all connections apart from central peer.", void 0, {
|
|
2678
|
+
F: __dxlog_file11,
|
|
2679
|
+
L: 29,
|
|
2680
|
+
S: this,
|
|
2681
|
+
C: (f, a) => f(...a)
|
|
2682
|
+
});
|
|
2683
|
+
for (const peer of connected) {
|
|
2684
|
+
if (!peer.equals(this._centralPeer)) {
|
|
2685
|
+
log10("dropping connection", {
|
|
2686
|
+
peer
|
|
2687
|
+
}, {
|
|
2688
|
+
F: __dxlog_file11,
|
|
2689
|
+
L: 34,
|
|
2690
|
+
S: this,
|
|
2691
|
+
C: (f, a) => f(...a)
|
|
2692
|
+
});
|
|
2693
|
+
this._controller.disconnect(peer);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
for (const peer of candidates) {
|
|
2698
|
+
if (peer.equals(this._centralPeer) || ownPeerId.equals(this._centralPeer)) {
|
|
2699
|
+
log10("connecting to peer", {
|
|
2700
|
+
peer
|
|
2701
|
+
}, {
|
|
2702
|
+
F: __dxlog_file11,
|
|
2703
|
+
L: 43,
|
|
2704
|
+
S: this,
|
|
2705
|
+
C: (f, a) => f(...a)
|
|
2706
|
+
});
|
|
2707
|
+
this._controller.connect(peer);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
async onOffer(peer) {
|
|
2712
|
+
invariant9(this._controller, "Not initialized.", {
|
|
2713
|
+
F: __dxlog_file11,
|
|
2714
|
+
L: 50,
|
|
2715
|
+
S: this,
|
|
2716
|
+
A: [
|
|
2717
|
+
"this._controller",
|
|
2718
|
+
"'Not initialized.'"
|
|
2719
|
+
]
|
|
2720
|
+
});
|
|
2721
|
+
const { ownPeerId } = this._controller.getState();
|
|
2722
|
+
log10("offer", {
|
|
2723
|
+
peer,
|
|
2724
|
+
isCentral: peer.equals(this._centralPeer),
|
|
2725
|
+
isSelfCentral: ownPeerId.equals(this._centralPeer)
|
|
2726
|
+
}, {
|
|
2727
|
+
F: __dxlog_file11,
|
|
2728
|
+
L: 52,
|
|
2729
|
+
S: this,
|
|
2730
|
+
C: (f, a) => f(...a)
|
|
2731
|
+
});
|
|
2732
|
+
return ownPeerId.equals(this._centralPeer) || peer.equals(this._centralPeer);
|
|
2733
|
+
}
|
|
2734
|
+
async destroy() {
|
|
2735
|
+
}
|
|
2736
|
+
};
|
|
2737
|
+
|
|
2738
|
+
// packages/core/mesh/network-manager/src/transport/memory-transport.ts
|
|
2739
|
+
import { Transform } from "node:stream";
|
|
2740
|
+
import { Event as Event7, Trigger as Trigger2 } from "@dxos/async";
|
|
2741
|
+
import { ErrorStream as ErrorStream3 } from "@dxos/debug";
|
|
2742
|
+
import { invariant as invariant10 } from "@dxos/invariant";
|
|
2743
|
+
import { PublicKey as PublicKey9 } from "@dxos/keys";
|
|
2744
|
+
import { log as log11, logInfo as logInfo3 } from "@dxos/log";
|
|
2745
|
+
import { ComplexMap as ComplexMap7 } from "@dxos/util";
|
|
2746
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
2747
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2748
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2749
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2750
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2751
|
+
}
|
|
2752
|
+
var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/memory-transport.ts";
|
|
2753
|
+
var MEMORY_TRANSPORT_DELAY = 1;
|
|
2754
|
+
var createStreamDelay = (delay) => {
|
|
2755
|
+
return new Transform({
|
|
2756
|
+
objectMode: true,
|
|
2757
|
+
transform: (chunk, _, cb) => {
|
|
2758
|
+
setTimeout(() => cb(null, chunk), delay);
|
|
2759
|
+
}
|
|
2760
|
+
});
|
|
2761
|
+
};
|
|
2762
|
+
var MemoryTransportFactory = {
|
|
2763
|
+
createTransport: (options) => new MemoryTransport(options)
|
|
2764
|
+
};
|
|
2765
|
+
var MemoryTransport = class _MemoryTransport {
|
|
2766
|
+
static {
|
|
2767
|
+
// TODO(burdon): Remove static properties (inject context into constructor).
|
|
2768
|
+
this._connections = new ComplexMap7(PublicKey9.hash);
|
|
2769
|
+
}
|
|
2770
|
+
constructor(_options) {
|
|
2771
|
+
this._options = _options;
|
|
2772
|
+
this._instanceId = PublicKey9.random();
|
|
2773
|
+
this._remote = new Trigger2();
|
|
2774
|
+
this._outgoingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
|
|
2775
|
+
this._incomingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
|
|
2776
|
+
this._closed = false;
|
|
2777
|
+
this.closed = new Event7();
|
|
2778
|
+
this.connected = new Event7();
|
|
2779
|
+
this.errors = new ErrorStream3();
|
|
2780
|
+
invariant10(!_MemoryTransport._connections.has(this._instanceId), "Duplicate memory connection", {
|
|
2781
|
+
F: __dxlog_file12,
|
|
2782
|
+
L: 64,
|
|
2783
|
+
S: this,
|
|
2784
|
+
A: [
|
|
2785
|
+
"!MemoryTransport._connections.has(this._instanceId)",
|
|
2786
|
+
"'Duplicate memory connection'"
|
|
2787
|
+
]
|
|
2788
|
+
});
|
|
2789
|
+
_MemoryTransport._connections.set(this._instanceId, this);
|
|
2790
|
+
}
|
|
2791
|
+
get isOpen() {
|
|
2792
|
+
return !this._closed;
|
|
2793
|
+
}
|
|
2794
|
+
async open() {
|
|
2795
|
+
log11("opening...", void 0, {
|
|
2796
|
+
F: __dxlog_file12,
|
|
2797
|
+
L: 74,
|
|
2798
|
+
S: this,
|
|
2799
|
+
C: (f, a) => f(...a)
|
|
2800
|
+
});
|
|
2801
|
+
if (this._options.initiator) {
|
|
2802
|
+
log11("sending signal", void 0, {
|
|
2803
|
+
F: __dxlog_file12,
|
|
2804
|
+
L: 78,
|
|
2805
|
+
S: this,
|
|
2806
|
+
C: (f, a) => f(...a)
|
|
2807
|
+
});
|
|
2808
|
+
try {
|
|
2809
|
+
await this._options.sendSignal({
|
|
2810
|
+
payload: {
|
|
2811
|
+
transportId: this._instanceId.toHex()
|
|
2812
|
+
}
|
|
2813
|
+
});
|
|
2814
|
+
} catch (err) {
|
|
2815
|
+
if (!this._closed) {
|
|
2816
|
+
this.errors.raise(toError(err));
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
} else {
|
|
2820
|
+
this._remote.wait({
|
|
2821
|
+
timeout: this._options.timeout ?? 1e3
|
|
2822
|
+
}).then((remoteId) => {
|
|
2823
|
+
if (this._closed) {
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
this._remoteInstanceId = remoteId;
|
|
2827
|
+
this._remoteConnection = _MemoryTransport._connections.get(this._remoteInstanceId);
|
|
2828
|
+
if (!this._remoteConnection) {
|
|
2829
|
+
this._closed = true;
|
|
2830
|
+
this.closed.emit();
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
invariant10(!this._remoteConnection._remoteConnection, `Remote already connected: ${this._remoteInstanceId}`, {
|
|
2834
|
+
F: __dxlog_file12,
|
|
2835
|
+
L: 104,
|
|
2836
|
+
S: this,
|
|
2837
|
+
A: [
|
|
2838
|
+
"!this._remoteConnection._remoteConnection",
|
|
2839
|
+
"`Remote already connected: ${this._remoteInstanceId}`"
|
|
2840
|
+
]
|
|
2841
|
+
});
|
|
2842
|
+
this._remoteConnection._remoteConnection = this;
|
|
2843
|
+
this._remoteConnection._remoteInstanceId = this._instanceId;
|
|
2844
|
+
log11("connected", void 0, {
|
|
2845
|
+
F: __dxlog_file12,
|
|
2846
|
+
L: 108,
|
|
2847
|
+
S: this,
|
|
2848
|
+
C: (f, a) => f(...a)
|
|
2849
|
+
});
|
|
2850
|
+
this._options.stream.pipe(this._outgoingDelay).pipe(this._remoteConnection._options.stream).pipe(this._incomingDelay).pipe(this._options.stream);
|
|
2851
|
+
this.connected.emit();
|
|
2852
|
+
this._remoteConnection.connected.emit();
|
|
2853
|
+
}).catch((err) => {
|
|
2854
|
+
if (this._closed) {
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
this.errors.raise(err);
|
|
2858
|
+
});
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
async close() {
|
|
2862
|
+
log11("closing...", void 0, {
|
|
2863
|
+
F: __dxlog_file12,
|
|
2864
|
+
L: 129,
|
|
2865
|
+
S: this,
|
|
2866
|
+
C: (f, a) => f(...a)
|
|
2867
|
+
});
|
|
2868
|
+
this._closed = true;
|
|
2869
|
+
_MemoryTransport._connections.delete(this._instanceId);
|
|
2870
|
+
if (this._remoteConnection) {
|
|
2871
|
+
this._remoteConnection._closed = true;
|
|
2872
|
+
_MemoryTransport._connections.delete(this._remoteInstanceId);
|
|
2873
|
+
this._options.stream.unpipe(this._incomingDelay);
|
|
2874
|
+
this._incomingDelay.unpipe(this._remoteConnection._options.stream);
|
|
2875
|
+
this._remoteConnection._options.stream.unpipe(this._outgoingDelay);
|
|
2876
|
+
this._outgoingDelay.unpipe(this._options.stream);
|
|
2877
|
+
this._options.stream.unpipe(this._outgoingDelay);
|
|
2878
|
+
this._remoteConnection.closed.emit();
|
|
2879
|
+
this._remoteConnection._remoteConnection = void 0;
|
|
2880
|
+
this._remoteConnection = void 0;
|
|
2881
|
+
}
|
|
2882
|
+
this.closed.emit();
|
|
2883
|
+
log11("closed", void 0, {
|
|
2884
|
+
F: __dxlog_file12,
|
|
2885
|
+
L: 157,
|
|
2886
|
+
S: this,
|
|
2887
|
+
C: (f, a) => f(...a)
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
async onSignal({ payload }) {
|
|
2891
|
+
log11("received signal", {
|
|
2892
|
+
payload
|
|
2893
|
+
}, {
|
|
2894
|
+
F: __dxlog_file12,
|
|
2895
|
+
L: 161,
|
|
2896
|
+
S: this,
|
|
2897
|
+
C: (f, a) => f(...a)
|
|
2898
|
+
});
|
|
2899
|
+
if (!payload?.transportId) {
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2902
|
+
const transportId = payload.transportId;
|
|
2903
|
+
if (transportId) {
|
|
2904
|
+
const remoteId = PublicKey9.fromHex(transportId);
|
|
2905
|
+
this._remote.wake(remoteId);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
async getDetails() {
|
|
2909
|
+
return this._instanceId.toHex();
|
|
2910
|
+
}
|
|
2911
|
+
async getStats() {
|
|
2912
|
+
return {
|
|
2913
|
+
bytesSent: 0,
|
|
2914
|
+
bytesReceived: 0,
|
|
2915
|
+
packetsSent: 0,
|
|
2916
|
+
packetsReceived: 0
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2919
|
+
};
|
|
2920
|
+
_ts_decorate5([
|
|
2921
|
+
logInfo3
|
|
2922
|
+
], MemoryTransport.prototype, "_instanceId", void 0);
|
|
2923
|
+
_ts_decorate5([
|
|
2924
|
+
logInfo3
|
|
2925
|
+
], MemoryTransport.prototype, "_remoteInstanceId", void 0);
|
|
2926
|
+
var toError = (err) => err instanceof Error ? err : new Error(String(err));
|
|
2927
|
+
|
|
2928
|
+
// packages/core/mesh/network-manager/src/transport/transport.ts
|
|
2929
|
+
var TransportKind;
|
|
2930
|
+
(function(TransportKind2) {
|
|
2931
|
+
TransportKind2["SIMPLE_PEER"] = "SIMPLE_PEER";
|
|
2932
|
+
TransportKind2["SIMPLE_PEER_PROXY"] = "SIMPLE_PEER_PROXY";
|
|
2933
|
+
TransportKind2["LIBDATACHANNEL"] = "LIBDATACHANNEL";
|
|
2934
|
+
TransportKind2["MEMORY"] = "MEMORY";
|
|
2935
|
+
TransportKind2["TCP"] = "TCP";
|
|
2936
|
+
})(TransportKind || (TransportKind = {}));
|
|
2937
|
+
|
|
2938
|
+
// packages/core/mesh/network-manager/src/transport/simplepeer-transport.ts
|
|
2939
|
+
import SimplePeerConstructor from "simple-peer";
|
|
2940
|
+
import { Event as Event8, synchronized as synchronized5 } from "@dxos/async";
|
|
2941
|
+
import { ErrorStream as ErrorStream4, raise as raise2 } from "@dxos/debug";
|
|
2942
|
+
import { invariant as invariant11 } from "@dxos/invariant";
|
|
2943
|
+
import { PublicKey as PublicKey10 } from "@dxos/keys";
|
|
2944
|
+
import { log as log12 } from "@dxos/log";
|
|
2945
|
+
import { ConnectionResetError as ConnectionResetError2, ConnectivityError as ConnectivityError2, ProtocolError as ProtocolError2, UnknownProtocolError as UnknownProtocolError2, trace as trace4 } from "@dxos/protocols";
|
|
2946
|
+
|
|
2947
|
+
// packages/core/mesh/network-manager/src/transport/webrtc.ts
|
|
2948
|
+
var wrtc = null;
|
|
2949
|
+
try {
|
|
2950
|
+
wrtc = __require("@koush/wrtc");
|
|
2951
|
+
} catch {
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
// packages/core/mesh/network-manager/src/transport/simplepeer-transport.ts
|
|
2955
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
2956
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2957
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2958
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2959
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2960
|
+
}
|
|
2961
|
+
var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/simplepeer-transport.ts";
|
|
2962
|
+
var createSimplePeerTransportFactory = (webrtcConfig, iceProvider) => ({
|
|
2963
|
+
createTransport: (options) => new SimplePeerTransport({
|
|
2964
|
+
...options,
|
|
2965
|
+
webrtcConfig,
|
|
2966
|
+
iceProvider
|
|
2967
|
+
})
|
|
2968
|
+
});
|
|
2969
|
+
var SimplePeerTransport = class {
|
|
2970
|
+
get isOpen() {
|
|
2971
|
+
return this._piped && !this._closed;
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* @params opts.config formatted as per https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection
|
|
2975
|
+
*/
|
|
2976
|
+
constructor(_params) {
|
|
2977
|
+
this._params = _params;
|
|
2978
|
+
this._peer = void 0;
|
|
2979
|
+
this._closed = false;
|
|
2980
|
+
this._piped = false;
|
|
2981
|
+
this.closed = new Event8();
|
|
2982
|
+
this.connected = new Event8();
|
|
2983
|
+
this.errors = new ErrorStream4();
|
|
2984
|
+
this._instanceId = PublicKey10.random().toHex();
|
|
2985
|
+
}
|
|
2986
|
+
async getStats() {
|
|
2987
|
+
const stats = await this._getStats();
|
|
2988
|
+
if (!stats) {
|
|
2989
|
+
return {
|
|
2990
|
+
bytesSent: 0,
|
|
2991
|
+
bytesReceived: 0,
|
|
2992
|
+
packetsSent: 0,
|
|
2993
|
+
packetsReceived: 0,
|
|
2994
|
+
rawStats: {}
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
return {
|
|
2998
|
+
bytesSent: stats.transport.bytesSent,
|
|
2999
|
+
bytesReceived: stats.transport.bytesReceived,
|
|
3000
|
+
packetsSent: stats.transport.packetsSent,
|
|
3001
|
+
packetsReceived: stats.transport.packetsReceived,
|
|
3002
|
+
rawStats: stats.raw
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
async _getStats() {
|
|
3006
|
+
if (typeof this._peer?._pc?.getStats !== "function") {
|
|
3007
|
+
return null;
|
|
3008
|
+
}
|
|
3009
|
+
return await this._peer._pc.getStats().then((stats) => {
|
|
3010
|
+
const statsEntries = Array.from(stats.entries());
|
|
3011
|
+
const transport = statsEntries.filter((s) => s[1].type === "transport")[0][1];
|
|
3012
|
+
const candidatePair = statsEntries.filter((s) => s[0] === transport.selectedCandidatePairId);
|
|
3013
|
+
let selectedCandidatePair;
|
|
3014
|
+
let remoteCandidate;
|
|
3015
|
+
if (candidatePair.length > 0) {
|
|
3016
|
+
selectedCandidatePair = candidatePair[0][1];
|
|
3017
|
+
remoteCandidate = statsEntries.filter((s) => s[0] === selectedCandidatePair.remoteCandidateId)[0][1];
|
|
3018
|
+
}
|
|
3019
|
+
return {
|
|
3020
|
+
datachannel: statsEntries.filter((s) => s[1].type === "data-channel")[0][1],
|
|
3021
|
+
transport,
|
|
3022
|
+
selectedCandidatePair,
|
|
3023
|
+
remoteCandidate,
|
|
3024
|
+
raw: Object.fromEntries(stats.entries())
|
|
3025
|
+
};
|
|
3026
|
+
});
|
|
3027
|
+
}
|
|
3028
|
+
async getDetails() {
|
|
3029
|
+
const stats = await this._getStats();
|
|
3030
|
+
const rc = stats?.remoteCandidate;
|
|
3031
|
+
if (!rc) {
|
|
3032
|
+
return "unavailable";
|
|
3033
|
+
}
|
|
3034
|
+
if (rc.candidateType === "relay") {
|
|
3035
|
+
return `${rc.ip}:${rc.port}/${rc.protocol} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
|
|
3036
|
+
}
|
|
3037
|
+
return `${rc.ip}:${rc.port}/${rc.protocol} ${rc.candidateType}`;
|
|
3038
|
+
}
|
|
3039
|
+
async open() {
|
|
3040
|
+
log12.trace("dxos.mesh.webrtc-transport.open", trace4.begin({
|
|
3041
|
+
id: this._instanceId
|
|
3042
|
+
}), {
|
|
3043
|
+
F: __dxlog_file13,
|
|
3044
|
+
L: 122,
|
|
3045
|
+
S: this,
|
|
3046
|
+
C: (f, a) => f(...a)
|
|
3047
|
+
});
|
|
3048
|
+
log12("created connection", {
|
|
3049
|
+
params: this._params
|
|
3050
|
+
}, {
|
|
3051
|
+
F: __dxlog_file13,
|
|
3052
|
+
L: 123,
|
|
3053
|
+
S: this,
|
|
3054
|
+
C: (f, a) => f(...a)
|
|
3055
|
+
});
|
|
3056
|
+
const providedIceServers = await this._params.iceProvider?.getIceServers();
|
|
3057
|
+
if (!this._params.webrtcConfig) {
|
|
3058
|
+
this._params.webrtcConfig = {};
|
|
3059
|
+
}
|
|
3060
|
+
this._params.webrtcConfig.iceServers = [
|
|
3061
|
+
...this._params.webrtcConfig.iceServers ?? [],
|
|
3062
|
+
...providedIceServers ?? []
|
|
3063
|
+
];
|
|
3064
|
+
this._peer = new SimplePeerConstructor({
|
|
3065
|
+
channelName: "dxos.mesh.transport",
|
|
3066
|
+
initiator: this._params.initiator,
|
|
3067
|
+
wrtc: SimplePeerConstructor.WEBRTC_SUPPORT ? void 0 : wrtc ?? raise2(new Error("wrtc not available")),
|
|
3068
|
+
config: this._params.webrtcConfig
|
|
3069
|
+
});
|
|
3070
|
+
this._peer.on("signal", async (data) => {
|
|
3071
|
+
log12("signal", data, {
|
|
3072
|
+
F: __dxlog_file13,
|
|
3073
|
+
L: 142,
|
|
3074
|
+
S: this,
|
|
3075
|
+
C: (f, a) => f(...a)
|
|
3076
|
+
});
|
|
3077
|
+
await this._params.sendSignal({
|
|
3078
|
+
payload: {
|
|
3079
|
+
data
|
|
3080
|
+
}
|
|
3081
|
+
});
|
|
3082
|
+
});
|
|
3083
|
+
this._peer.on("connect", () => {
|
|
3084
|
+
log12("connected", void 0, {
|
|
3085
|
+
F: __dxlog_file13,
|
|
3086
|
+
L: 147,
|
|
3087
|
+
S: this,
|
|
3088
|
+
C: (f, a) => f(...a)
|
|
3089
|
+
});
|
|
3090
|
+
this._params.stream.pipe(this._peer).pipe(this._params.stream);
|
|
3091
|
+
this._piped = true;
|
|
3092
|
+
this.connected.emit();
|
|
3093
|
+
});
|
|
3094
|
+
this._peer.on("close", async () => {
|
|
3095
|
+
log12("closed", void 0, {
|
|
3096
|
+
F: __dxlog_file13,
|
|
3097
|
+
L: 154,
|
|
3098
|
+
S: this,
|
|
3099
|
+
C: (f, a) => f(...a)
|
|
3100
|
+
});
|
|
3101
|
+
await this.close();
|
|
3102
|
+
});
|
|
3103
|
+
this._peer.on("error", async (err) => {
|
|
3104
|
+
if (typeof RTCError !== "undefined" && err instanceof RTCError) {
|
|
3105
|
+
if (err.errorDetail === "sctp-failure") {
|
|
3106
|
+
this.errors.raise(new ConnectionResetError2("sctp-failure from RTCError", err));
|
|
3107
|
+
} else {
|
|
3108
|
+
log12.info("unknown RTCError", {
|
|
3109
|
+
err
|
|
3110
|
+
}, {
|
|
3111
|
+
F: __dxlog_file13,
|
|
3112
|
+
L: 165,
|
|
3113
|
+
S: this,
|
|
3114
|
+
C: (f, a) => f(...a)
|
|
3115
|
+
});
|
|
3116
|
+
this.errors.raise(new UnknownProtocolError2("unknown RTCError", err));
|
|
3117
|
+
}
|
|
3118
|
+
} else if ("code" in err) {
|
|
3119
|
+
log12.info("simple-peer error", err, {
|
|
3120
|
+
F: __dxlog_file13,
|
|
3121
|
+
L: 170,
|
|
3122
|
+
S: this,
|
|
3123
|
+
C: (f, a) => f(...a)
|
|
3124
|
+
});
|
|
3125
|
+
switch (err.code) {
|
|
3126
|
+
case "ERR_WEBRTC_SUPPORT":
|
|
3127
|
+
this.errors.raise(new ProtocolError2("WebRTC not supported", err));
|
|
3128
|
+
break;
|
|
3129
|
+
case "ERR_SIGNALING":
|
|
3130
|
+
this.errors.raise(new ConnectivityError2("signaling failure", err));
|
|
3131
|
+
break;
|
|
3132
|
+
case "ERR_ICE_CONNECTION_FAILURE":
|
|
3133
|
+
case "ERR_DATA_CHANNEL":
|
|
3134
|
+
case "ERR_CONNECTION_FAILURE":
|
|
3135
|
+
this.errors.raise(new ConnectivityError2("unknown communication failure", err));
|
|
3136
|
+
break;
|
|
3137
|
+
// errors due to library issues or improper API usage
|
|
3138
|
+
case "ERR_CREATE_OFFER":
|
|
3139
|
+
case "ERR_CREATE_ANSWER":
|
|
3140
|
+
case "ERR_SET_LOCAL_DESCRIPTION":
|
|
3141
|
+
case "ERR_SET_REMOTE_DESCRIPTION":
|
|
3142
|
+
case "ERR_ADD_ICE_CANDIDATE":
|
|
3143
|
+
this.errors.raise(new UnknownProtocolError2("unknown simple-peer library failure", err));
|
|
3144
|
+
break;
|
|
3145
|
+
default:
|
|
3146
|
+
this.errors.raise(new Error("unknown simple-peer error"));
|
|
3147
|
+
break;
|
|
3148
|
+
}
|
|
3149
|
+
} else {
|
|
3150
|
+
log12.info("unknown peer connection error", err, {
|
|
3151
|
+
F: __dxlog_file13,
|
|
3152
|
+
L: 196,
|
|
3153
|
+
S: this,
|
|
3154
|
+
C: (f, a) => f(...a)
|
|
3155
|
+
});
|
|
3156
|
+
this.errors.raise(err);
|
|
3157
|
+
}
|
|
3158
|
+
try {
|
|
3159
|
+
if (typeof this._peer?._pc?.getStats === "function") {
|
|
3160
|
+
this._peer._pc.getStats().then((stats) => {
|
|
3161
|
+
log12.info("report after webrtc error", {
|
|
3162
|
+
config: this._params.webrtcConfig,
|
|
3163
|
+
stats: Object.fromEntries(stats.entries())
|
|
3164
|
+
}, {
|
|
3165
|
+
F: __dxlog_file13,
|
|
3166
|
+
L: 204,
|
|
3167
|
+
S: this,
|
|
3168
|
+
C: (f, a) => f(...a)
|
|
3169
|
+
});
|
|
3170
|
+
});
|
|
3171
|
+
}
|
|
3172
|
+
} catch (err2) {
|
|
3173
|
+
log12.catch(err2, void 0, {
|
|
3174
|
+
F: __dxlog_file13,
|
|
3175
|
+
L: 211,
|
|
3176
|
+
S: this,
|
|
3177
|
+
C: (f, a) => f(...a)
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3180
|
+
await this.close();
|
|
3181
|
+
});
|
|
3182
|
+
log12.trace("dxos.mesh.webrtc-transport.open", trace4.end({
|
|
3183
|
+
id: this._instanceId
|
|
3184
|
+
}), {
|
|
3185
|
+
F: __dxlog_file13,
|
|
3186
|
+
L: 217,
|
|
3187
|
+
S: this,
|
|
3188
|
+
C: (f, a) => f(...a)
|
|
3189
|
+
});
|
|
3190
|
+
}
|
|
3191
|
+
async close() {
|
|
3192
|
+
log12("closing...", void 0, {
|
|
3193
|
+
F: __dxlog_file13,
|
|
3194
|
+
L: 222,
|
|
3195
|
+
S: this,
|
|
3196
|
+
C: (f, a) => f(...a)
|
|
3197
|
+
});
|
|
3198
|
+
if (this._closed) {
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
this._disconnectStreams();
|
|
3202
|
+
this._peer.destroy();
|
|
3203
|
+
this._closed = true;
|
|
3204
|
+
this.closed.emit();
|
|
3205
|
+
log12("closed", void 0, {
|
|
3206
|
+
F: __dxlog_file13,
|
|
3207
|
+
L: 230,
|
|
3208
|
+
S: this,
|
|
3209
|
+
C: (f, a) => f(...a)
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
async onSignal(signal) {
|
|
3213
|
+
if (this._closed) {
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
invariant11(signal.payload.data, "Signal message must contain signal data.", {
|
|
3217
|
+
F: __dxlog_file13,
|
|
3218
|
+
L: 239,
|
|
3219
|
+
S: this,
|
|
3220
|
+
A: [
|
|
3221
|
+
"signal.payload.data",
|
|
3222
|
+
"'Signal message must contain signal data.'"
|
|
3223
|
+
]
|
|
3224
|
+
});
|
|
3225
|
+
invariant11(this._peer, "Peer must be initialized before receiving signals.", {
|
|
3226
|
+
F: __dxlog_file13,
|
|
3227
|
+
L: 240,
|
|
3228
|
+
S: this,
|
|
3229
|
+
A: [
|
|
3230
|
+
"this._peer",
|
|
3231
|
+
"'Peer must be initialized before receiving signals.'"
|
|
3232
|
+
]
|
|
3233
|
+
});
|
|
3234
|
+
this._peer.signal(signal.payload.data);
|
|
3235
|
+
}
|
|
3236
|
+
_disconnectStreams() {
|
|
3237
|
+
if (this._piped) {
|
|
3238
|
+
this._params.stream.unpipe?.(this._peer)?.unpipe?.(this._params.stream);
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
_ts_decorate6([
|
|
3243
|
+
synchronized5
|
|
3244
|
+
], SimplePeerTransport.prototype, "open", null);
|
|
3245
|
+
_ts_decorate6([
|
|
3246
|
+
synchronized5
|
|
3247
|
+
], SimplePeerTransport.prototype, "close", null);
|
|
3248
|
+
_ts_decorate6([
|
|
3249
|
+
synchronized5
|
|
3250
|
+
], SimplePeerTransport.prototype, "onSignal", null);
|
|
3251
|
+
|
|
3252
|
+
// packages/core/mesh/network-manager/src/transport/simplepeer-transport-service.ts
|
|
3253
|
+
import { Duplex } from "node:stream";
|
|
3254
|
+
import { Stream } from "@dxos/codec-protobuf";
|
|
3255
|
+
import { invariant as invariant12 } from "@dxos/invariant";
|
|
3256
|
+
import { PublicKey as PublicKey11 } from "@dxos/keys";
|
|
3257
|
+
import { log as log13 } from "@dxos/log";
|
|
3258
|
+
import { ConnectionState as ConnectionState3 } from "@dxos/protocols/proto/dxos/mesh/bridge";
|
|
3259
|
+
import { ComplexMap as ComplexMap8 } from "@dxos/util";
|
|
3260
|
+
var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/simplepeer-transport-service.ts";
|
|
3261
|
+
var SimplePeerTransportService = class {
|
|
3262
|
+
constructor(_webrtcConfig, _iceProvider) {
|
|
3263
|
+
this._webrtcConfig = _webrtcConfig;
|
|
3264
|
+
this._iceProvider = _iceProvider;
|
|
3265
|
+
this.transports = new ComplexMap8(PublicKey11.hash);
|
|
3266
|
+
}
|
|
3267
|
+
open(request) {
|
|
3268
|
+
const rpcStream = new Stream(({ ready, next, close }) => {
|
|
3269
|
+
const duplex = new Duplex({
|
|
3270
|
+
read: () => {
|
|
3271
|
+
const callbacks = [
|
|
3272
|
+
...transportState.writeCallbacks
|
|
3273
|
+
];
|
|
3274
|
+
transportState.writeCallbacks.length = 0;
|
|
3275
|
+
for (const cb of callbacks) {
|
|
3276
|
+
cb();
|
|
3277
|
+
}
|
|
3278
|
+
},
|
|
3279
|
+
write: function(chunk, _, callback) {
|
|
3280
|
+
next({
|
|
3281
|
+
data: {
|
|
3282
|
+
payload: chunk
|
|
3283
|
+
}
|
|
3284
|
+
});
|
|
3285
|
+
callback();
|
|
3286
|
+
}
|
|
3287
|
+
});
|
|
3288
|
+
const transport = new SimplePeerTransport({
|
|
3289
|
+
initiator: request.initiator,
|
|
3290
|
+
stream: duplex,
|
|
3291
|
+
webrtcConfig: this._webrtcConfig,
|
|
3292
|
+
sendSignal: async (signal) => {
|
|
3293
|
+
next({
|
|
3294
|
+
signal: {
|
|
3295
|
+
payload: signal
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
},
|
|
3299
|
+
iceProvider: this._iceProvider
|
|
3300
|
+
});
|
|
3301
|
+
void transport.open();
|
|
3302
|
+
next({
|
|
3303
|
+
connection: {
|
|
3304
|
+
state: ConnectionState3.CONNECTING
|
|
3305
|
+
}
|
|
3306
|
+
});
|
|
3307
|
+
transport.connected.on(() => {
|
|
3308
|
+
next({
|
|
3309
|
+
connection: {
|
|
3310
|
+
state: ConnectionState3.CONNECTED
|
|
3311
|
+
}
|
|
3312
|
+
});
|
|
3313
|
+
});
|
|
3314
|
+
transport.errors.handle((err) => {
|
|
3315
|
+
next({
|
|
3316
|
+
connection: {
|
|
3317
|
+
state: ConnectionState3.CLOSED,
|
|
3318
|
+
error: err.toString()
|
|
3319
|
+
}
|
|
3320
|
+
});
|
|
3321
|
+
close(err);
|
|
3322
|
+
});
|
|
3323
|
+
transport.closed.on(() => {
|
|
3324
|
+
next({
|
|
3325
|
+
connection: {
|
|
3326
|
+
state: ConnectionState3.CLOSED
|
|
3327
|
+
}
|
|
3328
|
+
});
|
|
3329
|
+
close();
|
|
3330
|
+
});
|
|
3331
|
+
const transportState = {
|
|
3332
|
+
transport,
|
|
3333
|
+
stream: duplex,
|
|
3334
|
+
writeCallbacks: [],
|
|
3335
|
+
state: "OPEN"
|
|
3336
|
+
};
|
|
3337
|
+
ready();
|
|
3338
|
+
this.transports.set(request.proxyId, transportState);
|
|
3339
|
+
});
|
|
3340
|
+
return rpcStream;
|
|
3341
|
+
}
|
|
3342
|
+
async sendSignal({ proxyId, signal }) {
|
|
3343
|
+
invariant12(this.transports.has(proxyId), void 0, {
|
|
3344
|
+
F: __dxlog_file14,
|
|
3345
|
+
L: 124,
|
|
3346
|
+
S: this,
|
|
3347
|
+
A: [
|
|
3348
|
+
"this.transports.has(proxyId)",
|
|
3349
|
+
""
|
|
3350
|
+
]
|
|
3351
|
+
});
|
|
3352
|
+
await this.transports.get(proxyId).transport.onSignal(signal);
|
|
3353
|
+
}
|
|
3354
|
+
async getDetails({ proxyId }) {
|
|
3355
|
+
invariant12(this.transports.has(proxyId), void 0, {
|
|
3356
|
+
F: __dxlog_file14,
|
|
3357
|
+
L: 129,
|
|
3358
|
+
S: this,
|
|
3359
|
+
A: [
|
|
3360
|
+
"this.transports.has(proxyId)",
|
|
3361
|
+
""
|
|
3362
|
+
]
|
|
3363
|
+
});
|
|
3364
|
+
return {
|
|
3365
|
+
details: await this.transports.get(proxyId).transport.getDetails()
|
|
3366
|
+
};
|
|
3367
|
+
}
|
|
3368
|
+
async getStats({ proxyId }) {
|
|
3369
|
+
invariant12(this.transports.has(proxyId), void 0, {
|
|
3370
|
+
F: __dxlog_file14,
|
|
3371
|
+
L: 134,
|
|
3372
|
+
S: this,
|
|
3373
|
+
A: [
|
|
3374
|
+
"this.transports.has(proxyId)",
|
|
3375
|
+
""
|
|
3376
|
+
]
|
|
3377
|
+
});
|
|
3378
|
+
return {
|
|
3379
|
+
stats: await this.transports.get(proxyId).transport.getStats()
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
async sendData({ proxyId, payload }) {
|
|
3383
|
+
if (this.transports.get(proxyId)?.state !== "OPEN") {
|
|
3384
|
+
log13.debug("transport is closed", void 0, {
|
|
3385
|
+
F: __dxlog_file14,
|
|
3386
|
+
L: 140,
|
|
3387
|
+
S: this,
|
|
3388
|
+
C: (f, a) => f(...a)
|
|
3389
|
+
});
|
|
3390
|
+
}
|
|
3391
|
+
invariant12(this.transports.has(proxyId), void 0, {
|
|
3392
|
+
F: __dxlog_file14,
|
|
3393
|
+
L: 142,
|
|
3394
|
+
S: this,
|
|
3395
|
+
A: [
|
|
3396
|
+
"this.transports.has(proxyId)",
|
|
3397
|
+
""
|
|
3398
|
+
]
|
|
3399
|
+
});
|
|
3400
|
+
const state = this.transports.get(proxyId);
|
|
3401
|
+
const bufferHasSpace = state.stream.push(payload);
|
|
3402
|
+
if (!bufferHasSpace) {
|
|
3403
|
+
await new Promise((resolve) => {
|
|
3404
|
+
state.writeCallbacks.push(resolve);
|
|
3405
|
+
});
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
async close({ proxyId }) {
|
|
3409
|
+
await this.transports.get(proxyId)?.transport.close();
|
|
3410
|
+
await this.transports.get(proxyId)?.stream.end();
|
|
3411
|
+
if (this.transports.get(proxyId)) {
|
|
3412
|
+
this.transports.get(proxyId).state = "CLOSED";
|
|
3413
|
+
}
|
|
3414
|
+
log13("Closed.", void 0, {
|
|
3415
|
+
F: __dxlog_file14,
|
|
3416
|
+
L: 158,
|
|
3417
|
+
S: this,
|
|
3418
|
+
C: (f, a) => f(...a)
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3421
|
+
};
|
|
3422
|
+
|
|
3423
|
+
// packages/core/mesh/network-manager/src/transport/simplepeer-transport-proxy.ts
|
|
3424
|
+
import { Writable } from "node:stream";
|
|
3425
|
+
import { Event as Event9, scheduleTask as scheduleTask4 } from "@dxos/async";
|
|
3426
|
+
import { Context as Context6 } from "@dxos/context";
|
|
3427
|
+
import { ErrorStream as ErrorStream5 } from "@dxos/debug";
|
|
3428
|
+
import { invariant as invariant13 } from "@dxos/invariant";
|
|
3429
|
+
import { PublicKey as PublicKey12 } from "@dxos/keys";
|
|
3430
|
+
import { log as log14 } from "@dxos/log";
|
|
3431
|
+
import { ConnectionResetError as ConnectionResetError3, TimeoutError as TimeoutError3, ProtocolError as ProtocolError3, ConnectivityError as ConnectivityError3, UnknownProtocolError as UnknownProtocolError3 } from "@dxos/protocols";
|
|
3432
|
+
import { ConnectionState as ConnectionState4 } from "@dxos/protocols/proto/dxos/mesh/bridge";
|
|
3433
|
+
import { arrayToBuffer } from "@dxos/util";
|
|
3434
|
+
var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/simplepeer-transport-proxy.ts";
|
|
3435
|
+
var RPC_TIMEOUT = 1e4;
|
|
3436
|
+
var RESP_MIN_THRESHOLD = 500;
|
|
3437
|
+
var TIMEOUT_THRESHOLD = 10;
|
|
3438
|
+
var SimplePeerTransportProxy = class {
|
|
3439
|
+
constructor(_options) {
|
|
3440
|
+
this._options = _options;
|
|
3441
|
+
this._proxyId = PublicKey12.random();
|
|
3442
|
+
this._ctx = new Context6(void 0, {
|
|
3443
|
+
F: __dxlog_file15,
|
|
3444
|
+
L: 37
|
|
3445
|
+
});
|
|
3446
|
+
this._timeoutCount = 0;
|
|
3447
|
+
this.closed = new Event9();
|
|
3448
|
+
this.connected = new Event9();
|
|
3449
|
+
this.errors = new ErrorStream5();
|
|
3450
|
+
this._closed = false;
|
|
3451
|
+
}
|
|
3452
|
+
get isOpen() {
|
|
3453
|
+
return !this._closed;
|
|
3454
|
+
}
|
|
3455
|
+
async open() {
|
|
3456
|
+
this._serviceStream = this._options.bridgeService.open({
|
|
3457
|
+
proxyId: this._proxyId,
|
|
3458
|
+
initiator: this._options.initiator
|
|
3459
|
+
}, {
|
|
3460
|
+
timeout: RPC_TIMEOUT
|
|
3461
|
+
});
|
|
3462
|
+
this._serviceStream.waitUntilReady().then(() => {
|
|
3463
|
+
this._serviceStream.subscribe(async (event) => {
|
|
3464
|
+
log14("SimplePeerTransportProxy: event", event, {
|
|
3465
|
+
F: __dxlog_file15,
|
|
3466
|
+
L: 66,
|
|
3467
|
+
S: this,
|
|
3468
|
+
C: (f, a) => f(...a)
|
|
3469
|
+
});
|
|
3470
|
+
if (event.connection) {
|
|
3471
|
+
await this._handleConnection(event.connection);
|
|
3472
|
+
} else if (event.data) {
|
|
3473
|
+
this._handleData(event.data);
|
|
3474
|
+
} else if (event.signal) {
|
|
3475
|
+
await this._handleSignal(event.signal);
|
|
3476
|
+
}
|
|
3477
|
+
});
|
|
3478
|
+
const proxyStream = new Writable({
|
|
3479
|
+
write: (chunk, _, callback) => {
|
|
3480
|
+
const then = performance.now();
|
|
3481
|
+
this._options.bridgeService.sendData({
|
|
3482
|
+
proxyId: this._proxyId,
|
|
3483
|
+
payload: chunk
|
|
3484
|
+
}, {
|
|
3485
|
+
timeout: RPC_TIMEOUT
|
|
3486
|
+
}).then(() => {
|
|
3487
|
+
if (performance.now() - then > RESP_MIN_THRESHOLD) {
|
|
3488
|
+
log14("slow response, delaying callback", void 0, {
|
|
3489
|
+
F: __dxlog_file15,
|
|
3490
|
+
L: 90,
|
|
3491
|
+
S: this,
|
|
3492
|
+
C: (f, a) => f(...a)
|
|
3493
|
+
});
|
|
3494
|
+
scheduleTask4(this._ctx, () => callback(), RESP_MIN_THRESHOLD);
|
|
3495
|
+
} else {
|
|
3496
|
+
callback();
|
|
3497
|
+
}
|
|
3498
|
+
this._timeoutCount = 0;
|
|
3499
|
+
}, (err) => {
|
|
3500
|
+
if (err instanceof TimeoutError3 || err.constructor.name === "TimeoutError") {
|
|
3501
|
+
if (this._timeoutCount++ > TIMEOUT_THRESHOLD) {
|
|
3502
|
+
throw new TimeoutError3(`too many timeouts (${this._timeoutCount} > ${TIMEOUT_THRESHOLD}`);
|
|
3503
|
+
} else {
|
|
3504
|
+
log14("timeout error, but still invoking callback", void 0, {
|
|
3505
|
+
F: __dxlog_file15,
|
|
3506
|
+
L: 102,
|
|
3507
|
+
S: this,
|
|
3508
|
+
C: (f, a) => f(...a)
|
|
3509
|
+
});
|
|
3510
|
+
callback();
|
|
3511
|
+
}
|
|
3512
|
+
} else {
|
|
3513
|
+
log14.catch(err, void 0, {
|
|
3514
|
+
F: __dxlog_file15,
|
|
3515
|
+
L: 106,
|
|
3516
|
+
S: this,
|
|
3517
|
+
C: (f, a) => f(...a)
|
|
3518
|
+
});
|
|
3519
|
+
}
|
|
3520
|
+
});
|
|
3521
|
+
}
|
|
3522
|
+
});
|
|
3523
|
+
proxyStream.on("error", (err) => {
|
|
3524
|
+
log14("proxystream error", {
|
|
3525
|
+
err
|
|
3526
|
+
}, {
|
|
3527
|
+
F: __dxlog_file15,
|
|
3528
|
+
L: 114,
|
|
3529
|
+
S: this,
|
|
3530
|
+
C: (f, a) => f(...a)
|
|
3531
|
+
});
|
|
3532
|
+
});
|
|
3533
|
+
this._options.stream.pipe(proxyStream);
|
|
3534
|
+
}, (error) => log14.catch(error, void 0, {
|
|
3535
|
+
F: __dxlog_file15,
|
|
3536
|
+
L: 119,
|
|
3537
|
+
S: this,
|
|
3538
|
+
C: (f, a) => f(...a)
|
|
3539
|
+
}));
|
|
3540
|
+
}
|
|
3541
|
+
async close() {
|
|
3542
|
+
await this._ctx.dispose();
|
|
3543
|
+
if (this._closed) {
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
await this._serviceStream.close();
|
|
3547
|
+
try {
|
|
3548
|
+
await this._options.bridgeService.close({
|
|
3549
|
+
proxyId: this._proxyId
|
|
3550
|
+
}, {
|
|
3551
|
+
timeout: RPC_TIMEOUT
|
|
3552
|
+
});
|
|
3553
|
+
} catch (err) {
|
|
3554
|
+
log14.catch(err, void 0, {
|
|
3555
|
+
F: __dxlog_file15,
|
|
3556
|
+
L: 134,
|
|
3557
|
+
S: this,
|
|
3558
|
+
C: (f, a) => f(...a)
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
this.closed.emit();
|
|
3562
|
+
this._closed = true;
|
|
3563
|
+
}
|
|
3564
|
+
async onSignal(signal) {
|
|
3565
|
+
this._options.bridgeService.sendSignal({
|
|
3566
|
+
proxyId: this._proxyId,
|
|
3567
|
+
signal
|
|
3568
|
+
}, {
|
|
3569
|
+
timeout: RPC_TIMEOUT
|
|
3570
|
+
}).catch((err) => this.errors.raise(decodeError(err)));
|
|
3571
|
+
}
|
|
3572
|
+
async _handleConnection(connectionEvent) {
|
|
3573
|
+
if (connectionEvent.error) {
|
|
3574
|
+
this.errors.raise(decodeError(connectionEvent.error));
|
|
3575
|
+
}
|
|
3576
|
+
switch (connectionEvent.state) {
|
|
3577
|
+
case ConnectionState4.CONNECTED: {
|
|
3578
|
+
this.connected.emit();
|
|
3579
|
+
break;
|
|
3580
|
+
}
|
|
3581
|
+
case ConnectionState4.CLOSED: {
|
|
3582
|
+
await this.close();
|
|
3583
|
+
break;
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
_handleData(dataEvent) {
|
|
3588
|
+
this._options.stream.write(arrayToBuffer(dataEvent.payload));
|
|
3589
|
+
}
|
|
3590
|
+
async _handleSignal(signalEvent) {
|
|
3591
|
+
await this._options.sendSignal(signalEvent.payload);
|
|
3592
|
+
}
|
|
3593
|
+
async getDetails() {
|
|
3594
|
+
return (await this._options.bridgeService.getDetails({
|
|
3595
|
+
proxyId: this._proxyId
|
|
3596
|
+
}, {
|
|
3597
|
+
timeout: RPC_TIMEOUT
|
|
3598
|
+
})).details;
|
|
3599
|
+
}
|
|
3600
|
+
async getStats() {
|
|
3601
|
+
return (await this._options.bridgeService.getStats({
|
|
3602
|
+
proxyId: this._proxyId
|
|
3603
|
+
}, {
|
|
3604
|
+
timeout: RPC_TIMEOUT
|
|
3605
|
+
})).stats;
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* Called when underlying proxy service becomes unavailable.
|
|
3609
|
+
*/
|
|
3610
|
+
// TODO(burdon): Option on close method.
|
|
3611
|
+
forceClose() {
|
|
3612
|
+
void this._serviceStream.close();
|
|
3613
|
+
this.closed.emit();
|
|
3614
|
+
this._closed = true;
|
|
3615
|
+
}
|
|
3616
|
+
};
|
|
3617
|
+
var SimplePeerTransportProxyFactory = class {
|
|
3618
|
+
constructor() {
|
|
3619
|
+
this._connections = /* @__PURE__ */ new Set();
|
|
3620
|
+
}
|
|
3621
|
+
/**
|
|
3622
|
+
* Sets the current BridgeService to be used to open connections.
|
|
3623
|
+
* Calling this method will close any existing connections.
|
|
3624
|
+
*/
|
|
3625
|
+
setBridgeService(bridgeService) {
|
|
3626
|
+
this._bridgeService = bridgeService;
|
|
3627
|
+
for (const connection of this._connections) {
|
|
3628
|
+
connection.forceClose();
|
|
3629
|
+
}
|
|
3630
|
+
return this;
|
|
3631
|
+
}
|
|
3632
|
+
createTransport(options) {
|
|
3633
|
+
invariant13(this._bridgeService, "SimplePeerTransportProxyFactory is not ready to open connections", {
|
|
3634
|
+
F: __dxlog_file15,
|
|
3635
|
+
L: 218,
|
|
3636
|
+
S: this,
|
|
3637
|
+
A: [
|
|
3638
|
+
"this._bridgeService",
|
|
3639
|
+
"'SimplePeerTransportProxyFactory is not ready to open connections'"
|
|
3640
|
+
]
|
|
3641
|
+
});
|
|
3642
|
+
const transport = new SimplePeerTransportProxy({
|
|
3643
|
+
...options,
|
|
3644
|
+
bridgeService: this._bridgeService
|
|
3645
|
+
});
|
|
3646
|
+
this._connections.add(transport);
|
|
3647
|
+
transport.closed.on(() => this._connections.delete(transport));
|
|
3648
|
+
return transport;
|
|
3649
|
+
}
|
|
3650
|
+
};
|
|
3651
|
+
var decodeError = (err) => {
|
|
3652
|
+
const message = typeof err === "string" ? err : err.message;
|
|
3653
|
+
if (message.includes("CONNECTION_RESET")) {
|
|
3654
|
+
return new ConnectionResetError3(message);
|
|
3655
|
+
} else if (message.includes("TIMEOUT")) {
|
|
3656
|
+
return new TimeoutError3(message);
|
|
3657
|
+
} else if (message.includes("PROTOCOL_ERROR")) {
|
|
3658
|
+
return new ProtocolError3(message);
|
|
3659
|
+
} else if (message.includes("CONNECTIVITY_ERROR")) {
|
|
3660
|
+
return new ConnectivityError3(message);
|
|
3661
|
+
} else if (message.includes("UNKNOWN_PROTOCOL_ERROR")) {
|
|
3662
|
+
return new UnknownProtocolError3(message);
|
|
3663
|
+
} else {
|
|
3664
|
+
return typeof err === "string" ? new Error(err) : err;
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
|
|
3668
|
+
// packages/core/mesh/network-manager/src/transport/libdatachannel-transport.ts
|
|
3669
|
+
import { Duplex as Duplex2 } from "stream";
|
|
3670
|
+
import { Event as Event10, Trigger as Trigger3, synchronized as synchronized6 } from "@dxos/async";
|
|
3671
|
+
import { ErrorStream as ErrorStream6 } from "@dxos/debug";
|
|
3672
|
+
import { invariant as invariant14 } from "@dxos/invariant";
|
|
3673
|
+
import { log as log15 } from "@dxos/log";
|
|
3674
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
3675
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3676
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
3677
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
3678
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3679
|
+
}
|
|
3680
|
+
var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/libdatachannel-transport.ts";
|
|
3681
|
+
var DATACHANNEL_LABEL = "dxos.mesh.transport";
|
|
3682
|
+
var MAX_BUFFERED_AMOUNT = 64 * 1024;
|
|
3683
|
+
var MAX_MESSAGE_SIZE = 64 * 1024;
|
|
3684
|
+
var createLibDataChannelTransportFactory = (webrtcConfig, iceProvider) => {
|
|
3685
|
+
return {
|
|
3686
|
+
createTransport: (options) => new LibDataChannelTransport({
|
|
3687
|
+
...options,
|
|
3688
|
+
webrtcConfig,
|
|
3689
|
+
iceProvider
|
|
3690
|
+
})
|
|
3691
|
+
};
|
|
3692
|
+
};
|
|
3693
|
+
var LibDataChannelTransport = class _LibDataChannelTransport {
|
|
3694
|
+
static {
|
|
3695
|
+
this._instanceCount = 0;
|
|
3696
|
+
}
|
|
3697
|
+
constructor(_options) {
|
|
3698
|
+
this._options = _options;
|
|
3699
|
+
this._closed = false;
|
|
3700
|
+
this._connected = false;
|
|
3701
|
+
this._writeCallback = null;
|
|
3702
|
+
this._readyForCandidates = new Trigger3();
|
|
3703
|
+
this.closed = new Event10();
|
|
3704
|
+
this.connected = new Event10();
|
|
3705
|
+
this.errors = new ErrorStream6();
|
|
3706
|
+
}
|
|
3707
|
+
get isOpen() {
|
|
3708
|
+
return !!this._peer && !this._closed;
|
|
3709
|
+
}
|
|
3710
|
+
async open() {
|
|
3711
|
+
if (this._closed) {
|
|
3712
|
+
this.errors.raise(new Error("connection already closed"));
|
|
3713
|
+
}
|
|
3714
|
+
const { RTCPeerConnection } = await import("#node-datachannel/polyfill");
|
|
3715
|
+
const providedIceServers = await this._options.iceProvider?.getIceServers();
|
|
3716
|
+
if (!this._options.webrtcConfig) {
|
|
3717
|
+
this._options.webrtcConfig = {};
|
|
3718
|
+
}
|
|
3719
|
+
this._options.webrtcConfig.iceServers = [
|
|
3720
|
+
...this._options.webrtcConfig.iceServers ?? [],
|
|
3721
|
+
...providedIceServers ?? []
|
|
3722
|
+
];
|
|
3723
|
+
this._peer = new RTCPeerConnection(this._options.webrtcConfig);
|
|
3724
|
+
this._peer.onicecandidateerror = (event) => {
|
|
3725
|
+
log15.error("peer.onicecandidateerror", {
|
|
3726
|
+
event
|
|
3727
|
+
}, {
|
|
3728
|
+
F: __dxlog_file16,
|
|
3729
|
+
L: 92,
|
|
3730
|
+
S: this,
|
|
3731
|
+
C: (f, a) => f(...a)
|
|
3732
|
+
});
|
|
3733
|
+
};
|
|
3734
|
+
this._peer.onconnectionstatechange = (event) => {
|
|
3735
|
+
log15.debug("peer.onconnectionstatechange", {
|
|
3736
|
+
event,
|
|
3737
|
+
peerConnectionState: this._peer?.connectionState,
|
|
3738
|
+
transportConnectionState: this._connected
|
|
3739
|
+
}, {
|
|
3740
|
+
F: __dxlog_file16,
|
|
3741
|
+
L: 96,
|
|
3742
|
+
S: this,
|
|
3743
|
+
C: (f, a) => f(...a)
|
|
3744
|
+
});
|
|
3745
|
+
};
|
|
3746
|
+
this._peer.onicecandidate = async (event) => {
|
|
3747
|
+
log15.debug("peer.onicecandidate", {
|
|
3748
|
+
event
|
|
3749
|
+
}, {
|
|
3750
|
+
F: __dxlog_file16,
|
|
3751
|
+
L: 106,
|
|
3752
|
+
S: this,
|
|
3753
|
+
C: (f, a) => f(...a)
|
|
3754
|
+
});
|
|
3755
|
+
if (event.candidate) {
|
|
3756
|
+
try {
|
|
3757
|
+
await this._options.sendSignal({
|
|
3758
|
+
payload: {
|
|
3759
|
+
data: {
|
|
3760
|
+
type: "candidate",
|
|
3761
|
+
candidate: {
|
|
3762
|
+
candidate: event.candidate.candidate,
|
|
3763
|
+
// These fields never seem to be not null, but connecting to Chrome doesn't work if they are.
|
|
3764
|
+
sdpMLineIndex: event.candidate.sdpMLineIndex ?? 0,
|
|
3765
|
+
sdpMid: event.candidate.sdpMid ?? 0
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
});
|
|
3770
|
+
} catch (err) {
|
|
3771
|
+
log15.info("signaling error", {
|
|
3772
|
+
err
|
|
3773
|
+
}, {
|
|
3774
|
+
F: __dxlog_file16,
|
|
3775
|
+
L: 123,
|
|
3776
|
+
S: this,
|
|
3777
|
+
C: (f, a) => f(...a)
|
|
3778
|
+
});
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
};
|
|
3782
|
+
if (this._options.initiator) {
|
|
3783
|
+
invariant14(this._peer, "not open", {
|
|
3784
|
+
F: __dxlog_file16,
|
|
3785
|
+
L: 129,
|
|
3786
|
+
S: this,
|
|
3787
|
+
A: [
|
|
3788
|
+
"this._peer",
|
|
3789
|
+
"'not open'"
|
|
3790
|
+
]
|
|
3791
|
+
});
|
|
3792
|
+
this._peer.createOffer().then(async (offer) => {
|
|
3793
|
+
if (this._closed) {
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
if (this._peer?.connectionState !== "connecting") {
|
|
3797
|
+
log15.error("peer not connecting", {
|
|
3798
|
+
peer: this._peer
|
|
3799
|
+
}, {
|
|
3800
|
+
F: __dxlog_file16,
|
|
3801
|
+
L: 140,
|
|
3802
|
+
S: this,
|
|
3803
|
+
C: (f, a) => f(...a)
|
|
3804
|
+
});
|
|
3805
|
+
this.errors.raise(new Error("invalid state: peer is initiator, but other peer not in state connecting"));
|
|
3806
|
+
}
|
|
3807
|
+
log15.debug("creating offer", {
|
|
3808
|
+
peer: this._peer,
|
|
3809
|
+
offer
|
|
3810
|
+
}, {
|
|
3811
|
+
F: __dxlog_file16,
|
|
3812
|
+
L: 144,
|
|
3813
|
+
S: this,
|
|
3814
|
+
C: (f, a) => f(...a)
|
|
3815
|
+
});
|
|
3816
|
+
await this._peer.setLocalDescription(offer);
|
|
3817
|
+
await this._options.sendSignal({
|
|
3818
|
+
payload: {
|
|
3819
|
+
data: {
|
|
3820
|
+
type: offer.type,
|
|
3821
|
+
sdp: offer.sdp
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
});
|
|
3825
|
+
}).catch((err) => {
|
|
3826
|
+
this.errors.raise(err);
|
|
3827
|
+
});
|
|
3828
|
+
this._handleChannel(this._peer.createDataChannel(DATACHANNEL_LABEL));
|
|
3829
|
+
log15.debug("created data channel", void 0, {
|
|
3830
|
+
F: __dxlog_file16,
|
|
3831
|
+
L: 154,
|
|
3832
|
+
S: this,
|
|
3833
|
+
C: (f, a) => f(...a)
|
|
3834
|
+
});
|
|
3835
|
+
this._peer.ondatachannel = () => {
|
|
3836
|
+
this.errors.raise(new Error("unexpected ondatachannel event for initiator"));
|
|
3837
|
+
};
|
|
3838
|
+
} else {
|
|
3839
|
+
this._peer.ondatachannel = (event) => {
|
|
3840
|
+
log15.debug("peer.ondatachannel (non-initiator)", {
|
|
3841
|
+
event
|
|
3842
|
+
}, {
|
|
3843
|
+
F: __dxlog_file16,
|
|
3844
|
+
L: 160,
|
|
3845
|
+
S: this,
|
|
3846
|
+
C: (f, a) => f(...a)
|
|
3847
|
+
});
|
|
3848
|
+
if (event.channel.label !== DATACHANNEL_LABEL) {
|
|
3849
|
+
this.errors.raise(new Error(`unexpected channel label ${event.channel.label}`));
|
|
3850
|
+
}
|
|
3851
|
+
this._handleChannel(event.channel);
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
3854
|
+
_LibDataChannelTransport._instanceCount++;
|
|
3855
|
+
}
|
|
3856
|
+
async close() {
|
|
3857
|
+
await this._close();
|
|
3858
|
+
if (--_LibDataChannelTransport._instanceCount === 0) {
|
|
3859
|
+
(await import("#node-datachannel")).cleanup();
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
async _close() {
|
|
3863
|
+
if (this._closed) {
|
|
3864
|
+
return;
|
|
3865
|
+
}
|
|
3866
|
+
await this._disconnectStreams();
|
|
3867
|
+
try {
|
|
3868
|
+
this._peer?.close();
|
|
3869
|
+
} catch (err) {
|
|
3870
|
+
this.errors.raise(err);
|
|
3871
|
+
}
|
|
3872
|
+
this._peer = void 0;
|
|
3873
|
+
this._closed = true;
|
|
3874
|
+
this.closed.emit();
|
|
3875
|
+
}
|
|
3876
|
+
/**
|
|
3877
|
+
* Handle data channel events.
|
|
3878
|
+
*/
|
|
3879
|
+
_handleChannel(dataChannel) {
|
|
3880
|
+
this._channel = dataChannel;
|
|
3881
|
+
this._channel.onopen = () => {
|
|
3882
|
+
log15.debug("channel.onopen", void 0, {
|
|
3883
|
+
F: __dxlog_file16,
|
|
3884
|
+
L: 205,
|
|
3885
|
+
S: this,
|
|
3886
|
+
C: (f, a) => f(...a)
|
|
3887
|
+
});
|
|
3888
|
+
const duplex = new Duplex2({
|
|
3889
|
+
read: () => {
|
|
3890
|
+
},
|
|
3891
|
+
write: async (chunk, encoding, callback) => {
|
|
3892
|
+
if (chunk.length > MAX_MESSAGE_SIZE) {
|
|
3893
|
+
this.errors.raise(new Error(`message too large: ${chunk.length} > ${MAX_MESSAGE_SIZE}`));
|
|
3894
|
+
}
|
|
3895
|
+
try {
|
|
3896
|
+
dataChannel.send(chunk);
|
|
3897
|
+
} catch (err) {
|
|
3898
|
+
this.errors.raise(err);
|
|
3899
|
+
await this._close();
|
|
3900
|
+
}
|
|
3901
|
+
if (this._channel.bufferedAmount > MAX_BUFFERED_AMOUNT) {
|
|
3902
|
+
if (this._writeCallback !== null) {
|
|
3903
|
+
log15.error("consumer trying to write before we are ready for more data", void 0, {
|
|
3904
|
+
F: __dxlog_file16,
|
|
3905
|
+
L: 222,
|
|
3906
|
+
S: this,
|
|
3907
|
+
C: (f, a) => f(...a)
|
|
3908
|
+
});
|
|
3909
|
+
}
|
|
3910
|
+
this._writeCallback = callback;
|
|
3911
|
+
} else {
|
|
3912
|
+
callback();
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
});
|
|
3916
|
+
duplex.pipe(this._options.stream).pipe(duplex);
|
|
3917
|
+
this._stream = duplex;
|
|
3918
|
+
this._connected = true;
|
|
3919
|
+
this.connected.emit();
|
|
3920
|
+
};
|
|
3921
|
+
this._channel.onclose = async (err) => {
|
|
3922
|
+
log15.info("channel.onclose", {
|
|
3923
|
+
err
|
|
3924
|
+
}, {
|
|
3925
|
+
F: __dxlog_file16,
|
|
3926
|
+
L: 238,
|
|
3927
|
+
S: this,
|
|
3928
|
+
C: (f, a) => f(...a)
|
|
3929
|
+
});
|
|
3930
|
+
await this._close();
|
|
3931
|
+
};
|
|
3932
|
+
this._channel.onerror = async (err) => {
|
|
3933
|
+
this.errors.raise(new Error("channel error: " + err.toString()));
|
|
3934
|
+
await this._close();
|
|
3935
|
+
};
|
|
3936
|
+
this._channel.onbufferedamountlow = () => {
|
|
3937
|
+
const cb = this._writeCallback;
|
|
3938
|
+
this._writeCallback = null;
|
|
3939
|
+
cb?.();
|
|
3940
|
+
};
|
|
3941
|
+
this._channel.onmessage = (event) => {
|
|
3942
|
+
let data = event.data;
|
|
3943
|
+
if (data instanceof ArrayBuffer) {
|
|
3944
|
+
data = Buffer.from(data);
|
|
3945
|
+
}
|
|
3946
|
+
this._stream.push(data);
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
async onSignal(signal) {
|
|
3950
|
+
invariant14(this._peer, "not open", {
|
|
3951
|
+
F: __dxlog_file16,
|
|
3952
|
+
L: 264,
|
|
3953
|
+
S: this,
|
|
3954
|
+
A: [
|
|
3955
|
+
"this._peer",
|
|
3956
|
+
"'not open'"
|
|
3957
|
+
]
|
|
3958
|
+
});
|
|
3959
|
+
try {
|
|
3960
|
+
const data = signal.payload.data;
|
|
3961
|
+
switch (data.type) {
|
|
3962
|
+
case "offer": {
|
|
3963
|
+
if (this._peer.connectionState !== "new") {
|
|
3964
|
+
log15.error("received offer but peer not in state new", {
|
|
3965
|
+
peer: this._peer
|
|
3966
|
+
}, {
|
|
3967
|
+
F: __dxlog_file16,
|
|
3968
|
+
L: 271,
|
|
3969
|
+
S: this,
|
|
3970
|
+
C: (f, a) => f(...a)
|
|
3971
|
+
});
|
|
3972
|
+
this.errors.raise(new Error("invalid signalling state: received offer when peer is not in state new"));
|
|
3973
|
+
break;
|
|
3974
|
+
}
|
|
3975
|
+
try {
|
|
3976
|
+
await this._peer.setRemoteDescription({
|
|
3977
|
+
type: data.type,
|
|
3978
|
+
sdp: data.sdp
|
|
3979
|
+
});
|
|
3980
|
+
const answer = await this._peer.createAnswer();
|
|
3981
|
+
await this._peer.setLocalDescription(answer);
|
|
3982
|
+
await this._options.sendSignal({
|
|
3983
|
+
payload: {
|
|
3984
|
+
data: {
|
|
3985
|
+
type: answer.type,
|
|
3986
|
+
sdp: answer.sdp
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
});
|
|
3990
|
+
this._readyForCandidates.wake();
|
|
3991
|
+
} catch (err) {
|
|
3992
|
+
log15.error("cannot handle offer from signalling server", {
|
|
3993
|
+
err
|
|
3994
|
+
}, {
|
|
3995
|
+
F: __dxlog_file16,
|
|
3996
|
+
L: 283,
|
|
3997
|
+
S: this,
|
|
3998
|
+
C: (f, a) => f(...a)
|
|
3999
|
+
});
|
|
4000
|
+
this.errors.raise(new Error("error handling offer"));
|
|
4001
|
+
}
|
|
4002
|
+
break;
|
|
4003
|
+
}
|
|
4004
|
+
case "answer":
|
|
4005
|
+
try {
|
|
4006
|
+
await this._peer.setRemoteDescription({
|
|
4007
|
+
type: data.type,
|
|
4008
|
+
sdp: data.sdp
|
|
4009
|
+
});
|
|
4010
|
+
this._readyForCandidates.wake();
|
|
4011
|
+
} catch (err) {
|
|
4012
|
+
log15.error("cannot handle answer from signalling server", {
|
|
4013
|
+
err
|
|
4014
|
+
}, {
|
|
4015
|
+
F: __dxlog_file16,
|
|
4016
|
+
L: 294,
|
|
4017
|
+
S: this,
|
|
4018
|
+
C: (f, a) => f(...a)
|
|
4019
|
+
});
|
|
4020
|
+
this.errors.raise(new Error("error handling answer"));
|
|
4021
|
+
}
|
|
4022
|
+
break;
|
|
4023
|
+
case "candidate":
|
|
4024
|
+
await this._readyForCandidates.wait();
|
|
4025
|
+
await this._peer.addIceCandidate({
|
|
4026
|
+
candidate: data.candidate.candidate
|
|
4027
|
+
});
|
|
4028
|
+
break;
|
|
4029
|
+
default:
|
|
4030
|
+
log15.error("unhandled signal type", {
|
|
4031
|
+
type: data.type,
|
|
4032
|
+
signal
|
|
4033
|
+
}, {
|
|
4034
|
+
F: __dxlog_file16,
|
|
4035
|
+
L: 305,
|
|
4036
|
+
S: this,
|
|
4037
|
+
C: (f, a) => f(...a)
|
|
4038
|
+
});
|
|
4039
|
+
this.errors.raise(new Error(`unhandled signal type ${data.type}`));
|
|
4040
|
+
}
|
|
4041
|
+
} catch (err) {
|
|
4042
|
+
log15.catch(err, void 0, {
|
|
4043
|
+
F: __dxlog_file16,
|
|
4044
|
+
L: 309,
|
|
4045
|
+
S: this,
|
|
4046
|
+
C: (f, a) => f(...a)
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
async getDetails() {
|
|
4051
|
+
const stats = await this._getStats();
|
|
4052
|
+
const rc = stats?.remoteCandidate;
|
|
4053
|
+
if (!rc) {
|
|
4054
|
+
return "unavailable";
|
|
4055
|
+
}
|
|
4056
|
+
if (rc.candidateType === "relay") {
|
|
4057
|
+
return `${rc.ip}:${rc.port} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
|
|
4058
|
+
}
|
|
4059
|
+
return `${rc.ip}:${rc.port} ${rc.candidateType}`;
|
|
4060
|
+
}
|
|
4061
|
+
async getStats() {
|
|
4062
|
+
const stats = await this._getStats();
|
|
4063
|
+
if (!stats) {
|
|
4064
|
+
return {
|
|
4065
|
+
bytesSent: 0,
|
|
4066
|
+
bytesReceived: 0,
|
|
4067
|
+
packetsSent: 0,
|
|
4068
|
+
packetsReceived: 0,
|
|
4069
|
+
rawStats: {}
|
|
4070
|
+
};
|
|
4071
|
+
}
|
|
4072
|
+
return {
|
|
4073
|
+
bytesSent: stats.transport.bytesSent,
|
|
4074
|
+
bytesReceived: stats.transport.bytesReceived,
|
|
4075
|
+
packetsSent: 0,
|
|
4076
|
+
packetsReceived: 0,
|
|
4077
|
+
rawStats: stats.raw
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
async _getStats() {
|
|
4081
|
+
invariant14(this._peer, "not open", {
|
|
4082
|
+
F: __dxlog_file16,
|
|
4083
|
+
L: 349,
|
|
4084
|
+
S: this,
|
|
4085
|
+
A: [
|
|
4086
|
+
"this._peer",
|
|
4087
|
+
"'not open'"
|
|
4088
|
+
]
|
|
4089
|
+
});
|
|
4090
|
+
const stats = await this._peer.getStats();
|
|
4091
|
+
const statsEntries = Array.from(stats.entries());
|
|
4092
|
+
const transport = statsEntries.filter((s) => s[1].type === "transport")[0][1];
|
|
4093
|
+
const candidatePair = statsEntries.filter((s) => s[0] === transport.selectedCandidatePairId);
|
|
4094
|
+
let selectedCandidatePair;
|
|
4095
|
+
let remoteCandidate;
|
|
4096
|
+
if (candidatePair.length > 0) {
|
|
4097
|
+
selectedCandidatePair = candidatePair[0][1];
|
|
4098
|
+
remoteCandidate = statsEntries.filter((s) => s[0] === selectedCandidatePair.remoteCandidateId)[0][1];
|
|
4099
|
+
}
|
|
4100
|
+
return {
|
|
4101
|
+
transport,
|
|
4102
|
+
selectedCandidatePair,
|
|
4103
|
+
remoteCandidate,
|
|
4104
|
+
raw: Object.fromEntries(stats)
|
|
4105
|
+
};
|
|
4106
|
+
}
|
|
4107
|
+
async _disconnectStreams() {
|
|
4108
|
+
this._options.stream.unpipe?.(this._stream)?.unpipe?.(this._options.stream);
|
|
4109
|
+
}
|
|
4110
|
+
};
|
|
4111
|
+
_ts_decorate7([
|
|
4112
|
+
synchronized6
|
|
4113
|
+
], LibDataChannelTransport.prototype, "_close", null);
|
|
4114
|
+
|
|
4115
|
+
// packages/core/mesh/network-manager/src/transport/tcp-transport.ts
|
|
4116
|
+
import { Socket } from "node:net";
|
|
4117
|
+
import { Event as Event11 } from "@dxos/async";
|
|
4118
|
+
import { ErrorStream as ErrorStream7 } from "@dxos/debug";
|
|
4119
|
+
import { log as log16 } from "@dxos/log";
|
|
4120
|
+
var __dxlog_file17 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/tcp-transport.ts";
|
|
4121
|
+
var TcpTransportFactory = {
|
|
4122
|
+
createTransport: (options) => new TcpTransport(options)
|
|
4123
|
+
};
|
|
4124
|
+
var TcpTransport = class {
|
|
4125
|
+
constructor(options) {
|
|
4126
|
+
this.options = options;
|
|
4127
|
+
this._server = void 0;
|
|
4128
|
+
this._socket = void 0;
|
|
4129
|
+
this._connected = false;
|
|
4130
|
+
this._closed = false;
|
|
4131
|
+
this.closed = new Event11();
|
|
4132
|
+
this.connected = new Event11();
|
|
4133
|
+
this.errors = new ErrorStream7();
|
|
4134
|
+
}
|
|
4135
|
+
get isOpen() {
|
|
4136
|
+
return this._connected && !this._closed;
|
|
4137
|
+
}
|
|
4138
|
+
async open() {
|
|
4139
|
+
log16("opening", void 0, {
|
|
4140
|
+
F: __dxlog_file17,
|
|
4141
|
+
L: 39,
|
|
4142
|
+
S: this,
|
|
4143
|
+
C: (f, a) => f(...a)
|
|
4144
|
+
});
|
|
4145
|
+
if (this.options.initiator) {
|
|
4146
|
+
setTimeout(async () => {
|
|
4147
|
+
const { Server } = await import("node:net");
|
|
4148
|
+
this._server = new Server((socket) => {
|
|
4149
|
+
log16("new connection", void 0, {
|
|
4150
|
+
F: __dxlog_file17,
|
|
4151
|
+
L: 47,
|
|
4152
|
+
S: this,
|
|
4153
|
+
C: (f, a) => f(...a)
|
|
4154
|
+
});
|
|
4155
|
+
if (this._connected) {
|
|
4156
|
+
socket.destroy();
|
|
4157
|
+
}
|
|
4158
|
+
this._handleSocket(socket);
|
|
4159
|
+
});
|
|
4160
|
+
this._server.on("listening", () => {
|
|
4161
|
+
const { port } = this._server.address();
|
|
4162
|
+
log16("listening", {
|
|
4163
|
+
port
|
|
4164
|
+
}, {
|
|
4165
|
+
F: __dxlog_file17,
|
|
4166
|
+
L: 56,
|
|
4167
|
+
S: this,
|
|
4168
|
+
C: (f, a) => f(...a)
|
|
4169
|
+
});
|
|
4170
|
+
void this.options.sendSignal({
|
|
4171
|
+
payload: {
|
|
4172
|
+
port
|
|
4173
|
+
}
|
|
4174
|
+
}).catch((err) => {
|
|
4175
|
+
if (!this._closed) {
|
|
4176
|
+
this.errors.raise(err);
|
|
4177
|
+
}
|
|
4178
|
+
});
|
|
4179
|
+
});
|
|
4180
|
+
this._server.on("error", (err) => {
|
|
4181
|
+
this.errors.raise(err);
|
|
4182
|
+
});
|
|
4183
|
+
this._server.listen(0);
|
|
4184
|
+
});
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
async close() {
|
|
4188
|
+
log16("closing", void 0, {
|
|
4189
|
+
F: __dxlog_file17,
|
|
4190
|
+
L: 78,
|
|
4191
|
+
S: this,
|
|
4192
|
+
C: (f, a) => f(...a)
|
|
4193
|
+
});
|
|
4194
|
+
this._socket?.destroy();
|
|
4195
|
+
this._server?.close();
|
|
4196
|
+
this._closed = true;
|
|
4197
|
+
}
|
|
4198
|
+
async onSignal({ payload }) {
|
|
4199
|
+
log16("received signal", {
|
|
4200
|
+
payload
|
|
4201
|
+
}, {
|
|
4202
|
+
F: __dxlog_file17,
|
|
4203
|
+
L: 85,
|
|
4204
|
+
S: this,
|
|
4205
|
+
C: (f, a) => f(...a)
|
|
4206
|
+
});
|
|
4207
|
+
if (this.options.initiator || this._connected) {
|
|
4208
|
+
return;
|
|
4209
|
+
}
|
|
4210
|
+
const socket = new Socket();
|
|
4211
|
+
this._handleSocket(socket);
|
|
4212
|
+
socket.connect({
|
|
4213
|
+
port: payload.port,
|
|
4214
|
+
host: "localhost"
|
|
4215
|
+
});
|
|
4216
|
+
}
|
|
4217
|
+
async getDetails() {
|
|
4218
|
+
if (this.options.initiator) {
|
|
4219
|
+
const { port: port2, address: address2 } = this._server?.address();
|
|
4220
|
+
return `LISTEN ${address2}:${port2}`;
|
|
4221
|
+
}
|
|
4222
|
+
const { port, address } = this._socket?.address();
|
|
4223
|
+
return `ACCEPT ${address}:${port}`;
|
|
4224
|
+
}
|
|
4225
|
+
async getStats() {
|
|
4226
|
+
return {
|
|
4227
|
+
bytesSent: 0,
|
|
4228
|
+
bytesReceived: 0,
|
|
4229
|
+
packetsSent: 0,
|
|
4230
|
+
packetsReceived: 0
|
|
4231
|
+
};
|
|
4232
|
+
}
|
|
4233
|
+
_handleSocket(socket) {
|
|
4234
|
+
log16("handling socket", {
|
|
4235
|
+
remotePort: socket.remotePort,
|
|
4236
|
+
localPort: socket.localPort
|
|
4237
|
+
}, {
|
|
4238
|
+
F: __dxlog_file17,
|
|
4239
|
+
L: 115,
|
|
4240
|
+
S: this,
|
|
4241
|
+
C: (f, a) => f(...a)
|
|
4242
|
+
});
|
|
4243
|
+
this._socket = socket;
|
|
4244
|
+
this._socket.on("connect", () => {
|
|
4245
|
+
log16("connected to", {
|
|
4246
|
+
port: this._socket?.remotePort
|
|
4247
|
+
}, {
|
|
4248
|
+
F: __dxlog_file17,
|
|
4249
|
+
L: 119,
|
|
4250
|
+
S: this,
|
|
4251
|
+
C: (f, a) => f(...a)
|
|
4252
|
+
});
|
|
4253
|
+
this._connected = true;
|
|
4254
|
+
});
|
|
4255
|
+
this._socket.on("error", (err) => {
|
|
4256
|
+
this.errors.raise(err);
|
|
4257
|
+
});
|
|
4258
|
+
this._socket.on("close", () => {
|
|
4259
|
+
this.closed.emit();
|
|
4260
|
+
});
|
|
4261
|
+
this.connected.emit();
|
|
4262
|
+
this.options.stream.pipe(this._socket).pipe(this.options.stream);
|
|
4263
|
+
}
|
|
4264
|
+
};
|
|
4265
|
+
|
|
4266
|
+
// packages/core/mesh/network-manager/src/wire-protocol.ts
|
|
4267
|
+
import { Teleport } from "@dxos/teleport";
|
|
4268
|
+
var createTeleportProtocolFactory = (onConnection, defaultParams) => {
|
|
4269
|
+
return (params) => {
|
|
4270
|
+
const teleport = new Teleport({
|
|
4271
|
+
...defaultParams,
|
|
4272
|
+
...params
|
|
4273
|
+
});
|
|
4274
|
+
return {
|
|
4275
|
+
stream: teleport.stream,
|
|
4276
|
+
open: async (sessionId) => {
|
|
4277
|
+
await teleport.open(sessionId);
|
|
4278
|
+
await onConnection(teleport);
|
|
4279
|
+
},
|
|
4280
|
+
close: async () => {
|
|
4281
|
+
await teleport.close();
|
|
4282
|
+
},
|
|
4283
|
+
abort: async () => {
|
|
4284
|
+
await teleport.abort();
|
|
4285
|
+
}
|
|
4286
|
+
};
|
|
4287
|
+
};
|
|
4288
|
+
};
|
|
4289
|
+
|
|
4290
|
+
export {
|
|
4291
|
+
ConnectionState,
|
|
4292
|
+
Connection,
|
|
4293
|
+
createIceProvider,
|
|
4294
|
+
SwarmMessenger,
|
|
4295
|
+
Swarm,
|
|
4296
|
+
SwarmMapper,
|
|
4297
|
+
MAX_CONCURRENT_INITIATING_CONNECTIONS,
|
|
4298
|
+
ConnectionLimiter,
|
|
4299
|
+
EventType,
|
|
4300
|
+
ConnectionLog,
|
|
4301
|
+
SwarmNetworkManager,
|
|
4302
|
+
FullyConnectedTopology,
|
|
4303
|
+
MMSTTopology,
|
|
4304
|
+
StarTopology,
|
|
4305
|
+
MemoryTransportFactory,
|
|
4306
|
+
MemoryTransport,
|
|
4307
|
+
TransportKind,
|
|
4308
|
+
createSimplePeerTransportFactory,
|
|
4309
|
+
SimplePeerTransport,
|
|
4310
|
+
SimplePeerTransportService,
|
|
4311
|
+
SimplePeerTransportProxy,
|
|
4312
|
+
SimplePeerTransportProxyFactory,
|
|
4313
|
+
createLibDataChannelTransportFactory,
|
|
4314
|
+
LibDataChannelTransport,
|
|
4315
|
+
TcpTransportFactory,
|
|
4316
|
+
TcpTransport,
|
|
4317
|
+
createTeleportProtocolFactory
|
|
4318
|
+
};
|
|
4319
|
+
//# sourceMappingURL=chunk-X2RY5LSM.mjs.map
|