@streamr/dht 103.3.0 → 103.6.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/exports-browser.cjs +549 -100
- package/dist/exports-browser.cjs.map +1 -1
- package/dist/exports-browser.d.ts +6 -4
- package/dist/exports-browser.js +530 -101
- package/dist/exports-browser.js.map +1 -1
- package/dist/exports-nodejs.cjs +10 -1
- package/dist/exports-nodejs.cjs.map +1 -1
- package/dist/exports-nodejs.d.ts +9 -4
- package/dist/exports-nodejs.js +10 -2
- package/dist/exports-nodejs.js.map +1 -1
- package/package.json +13 -8
package/dist/exports-browser.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
require('timers-browserify');
|
|
3
4
|
var utils = require('@streamr/utils');
|
|
4
5
|
var eventemitter3 = require('eventemitter3');
|
|
5
6
|
var sample = require('lodash/sample');
|
|
@@ -10,6 +11,7 @@ var runtimeRpc = require('@protobuf-ts/runtime-rpc');
|
|
|
10
11
|
var uuid = require('uuid');
|
|
11
12
|
var protoRpc = require('@streamr/proto-rpc');
|
|
12
13
|
var ipaddr = require('ipaddr.js');
|
|
14
|
+
var Comlink = require('comlink');
|
|
13
15
|
var websocket = require('websocket');
|
|
14
16
|
var autocertifierClient = require('@streamr/autocertifier-client');
|
|
15
17
|
var shuffle = require('lodash/shuffle');
|
|
@@ -20,6 +22,25 @@ var lruCache = require('lru-cache');
|
|
|
20
22
|
var cdnLocation = require('@streamr/cdn-location');
|
|
21
23
|
var Heap = require('heap');
|
|
22
24
|
|
|
25
|
+
function _interopNamespaceDefault(e) {
|
|
26
|
+
var n = Object.create(null);
|
|
27
|
+
if (e) {
|
|
28
|
+
Object.keys(e).forEach(function (k) {
|
|
29
|
+
if (k !== 'default') {
|
|
30
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
31
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () { return e[k]; }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
n.default = e;
|
|
39
|
+
return Object.freeze(n);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
var Comlink__namespace = /*#__PURE__*/_interopNamespaceDefault(Comlink);
|
|
43
|
+
|
|
23
44
|
const getPeerDistance = (nodeIdOrDataKeyRaw1, nodeIdOrDataKeyRaw2) => {
|
|
24
45
|
return KBucket.distance(nodeIdOrDataKeyRaw1, nodeIdOrDataKeyRaw2);
|
|
25
46
|
};
|
|
@@ -1799,7 +1820,7 @@ const createRandomConnectionId = () => {
|
|
|
1799
1820
|
return uuid.v4();
|
|
1800
1821
|
};
|
|
1801
1822
|
|
|
1802
|
-
const logger$
|
|
1823
|
+
const logger$D = new utils.Logger('ManagedConnection');
|
|
1803
1824
|
// ManagedConnection is a component used as a wrapper for IConnection after they have been successfully handshaked.
|
|
1804
1825
|
// Should only be used in the ConnectionManager.
|
|
1805
1826
|
class ManagedConnection extends eventemitter3.EventEmitter {
|
|
@@ -1829,7 +1850,7 @@ class ManagedConnection extends eventemitter3.EventEmitter {
|
|
|
1829
1850
|
this.remotePeerDescriptor = peerDescriptor;
|
|
1830
1851
|
}
|
|
1831
1852
|
onDisconnected(gracefulLeave) {
|
|
1832
|
-
logger$
|
|
1853
|
+
logger$D.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' onDisconnected() ' + gracefulLeave);
|
|
1833
1854
|
if (!this.replacedAsDuplicate) {
|
|
1834
1855
|
this.emit('disconnected', gracefulLeave);
|
|
1835
1856
|
}
|
|
@@ -1838,7 +1859,7 @@ class ManagedConnection extends eventemitter3.EventEmitter {
|
|
|
1838
1859
|
// TODO: Can this be removed if ManagedConnections can never be duplicates?
|
|
1839
1860
|
// Handle duplicates in the ConncetorFacade and no longer have PendingConnections in ConnectionManager
|
|
1840
1861
|
replaceAsDuplicate() {
|
|
1841
|
-
logger$
|
|
1862
|
+
logger$D.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
|
|
1842
1863
|
this.replacedAsDuplicate = true;
|
|
1843
1864
|
}
|
|
1844
1865
|
send(data) {
|
|
@@ -1985,10 +2006,10 @@ class RpcRemote {
|
|
|
1985
2006
|
}
|
|
1986
2007
|
}
|
|
1987
2008
|
|
|
1988
|
-
const logger$
|
|
2009
|
+
const logger$C = new utils.Logger('ConnectionLockRpcRemote');
|
|
1989
2010
|
class ConnectionLockRpcRemote extends RpcRemote {
|
|
1990
2011
|
async lockRequest(lockId) {
|
|
1991
|
-
logger$
|
|
2012
|
+
logger$C.trace(`Requesting locked connection to ${toNodeId(this.getPeerDescriptor())}`);
|
|
1992
2013
|
const request = {
|
|
1993
2014
|
lockId
|
|
1994
2015
|
};
|
|
@@ -1998,12 +2019,12 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
1998
2019
|
return res.accepted;
|
|
1999
2020
|
}
|
|
2000
2021
|
catch (err) {
|
|
2001
|
-
logger$
|
|
2022
|
+
logger$C.debug('Connection lock rejected', { err });
|
|
2002
2023
|
return false;
|
|
2003
2024
|
}
|
|
2004
2025
|
}
|
|
2005
2026
|
unlockRequest(lockId) {
|
|
2006
|
-
logger$
|
|
2027
|
+
logger$C.trace(`Requesting connection to be unlocked from ${toNodeId(this.getPeerDescriptor())}`);
|
|
2007
2028
|
const request = {
|
|
2008
2029
|
lockId
|
|
2009
2030
|
};
|
|
@@ -2011,11 +2032,11 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
2011
2032
|
notification: true
|
|
2012
2033
|
});
|
|
2013
2034
|
this.getClient().unlockRequest(request, options).catch((_e) => {
|
|
2014
|
-
logger$
|
|
2035
|
+
logger$C.trace('failed to send unlockRequest');
|
|
2015
2036
|
});
|
|
2016
2037
|
}
|
|
2017
2038
|
async gracefulDisconnect(disconnectMode) {
|
|
2018
|
-
logger$
|
|
2039
|
+
logger$C.trace(`Notifying a graceful disconnect to ${toNodeId(this.getPeerDescriptor())}`);
|
|
2019
2040
|
const request = {
|
|
2020
2041
|
disconnectMode
|
|
2021
2042
|
};
|
|
@@ -2027,7 +2048,7 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
2027
2048
|
await this.getClient().gracefulDisconnect(request, options);
|
|
2028
2049
|
}
|
|
2029
2050
|
async setPrivate(isPrivate) {
|
|
2030
|
-
logger$
|
|
2051
|
+
logger$C.trace(`Setting isPrivate: ${isPrivate} for ${toNodeId(this.getPeerDescriptor())}`);
|
|
2031
2052
|
const request = {
|
|
2032
2053
|
isPrivate
|
|
2033
2054
|
};
|
|
@@ -2039,7 +2060,7 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
2039
2060
|
}
|
|
2040
2061
|
}
|
|
2041
2062
|
|
|
2042
|
-
const logger$
|
|
2063
|
+
const logger$B = new utils.Logger('ConnectionLockRpcLocal');
|
|
2043
2064
|
class ConnectionLockRpcLocal {
|
|
2044
2065
|
options;
|
|
2045
2066
|
constructor(options) {
|
|
@@ -2068,7 +2089,7 @@ class ConnectionLockRpcLocal {
|
|
|
2068
2089
|
}
|
|
2069
2090
|
async gracefulDisconnect(disconnectNotice, context) {
|
|
2070
2091
|
const senderPeerDescriptor = context.incomingSourceDescriptor;
|
|
2071
|
-
logger$
|
|
2092
|
+
logger$B.trace(getNodeIdOrUnknownFromPeerDescriptor(senderPeerDescriptor) + ' received gracefulDisconnect notice');
|
|
2072
2093
|
if (disconnectNotice.disconnectMode === DisconnectMode.LEAVING) {
|
|
2073
2094
|
await this.options.closeConnection(senderPeerDescriptor, true, 'graceful leave notified');
|
|
2074
2095
|
}
|
|
@@ -2119,7 +2140,7 @@ var NatType;
|
|
|
2119
2140
|
NatType["OPEN_INTERNET"] = "open_internet";
|
|
2120
2141
|
NatType["UNKNOWN"] = "unknown";
|
|
2121
2142
|
})(NatType || (NatType = {}));
|
|
2122
|
-
const logger$
|
|
2143
|
+
const logger$A = new utils.Logger('ConnectionManager');
|
|
2123
2144
|
var ConnectionManagerState;
|
|
2124
2145
|
(function (ConnectionManagerState) {
|
|
2125
2146
|
ConnectionManagerState["IDLE"] = "idle";
|
|
@@ -2169,7 +2190,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2169
2190
|
getLocalPeerDescriptor: () => this.getLocalPeerDescriptor(),
|
|
2170
2191
|
setPrivate: (id, isPrivate) => {
|
|
2171
2192
|
if (!this.options.allowIncomingPrivateConnections) {
|
|
2172
|
-
logger$
|
|
2193
|
+
logger$A.debug(`node ${id} attemted to set a connection as private, but it is not allowed`);
|
|
2173
2194
|
return;
|
|
2174
2195
|
}
|
|
2175
2196
|
if (isPrivate) {
|
|
@@ -2203,7 +2224,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2203
2224
|
const connection = endpoint.connection;
|
|
2204
2225
|
const nodeId = connection.getNodeId();
|
|
2205
2226
|
if (!this.locks.isLocked(nodeId) && !this.locks.isPrivate(nodeId) && Date.now() - connection.getLastUsedTimestamp() > maxIdleTime) {
|
|
2206
|
-
logger$
|
|
2227
|
+
logger$A.trace('disconnecting in timeout interval: ' + getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor()));
|
|
2207
2228
|
disconnectionCandidates.addContact(connection);
|
|
2208
2229
|
}
|
|
2209
2230
|
}
|
|
@@ -2211,7 +2232,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2211
2232
|
const disconnectables = disconnectionCandidates.getFurthestContacts(this.endpoints.size - maxConnections);
|
|
2212
2233
|
for (const disconnectable of disconnectables) {
|
|
2213
2234
|
const peerDescriptor = disconnectable.getPeerDescriptor();
|
|
2214
|
-
logger$
|
|
2235
|
+
logger$A.trace('garbageCollecting ' + toNodeId(peerDescriptor));
|
|
2215
2236
|
this.gracefullyDisconnectAsync(peerDescriptor, DisconnectMode.NORMAL).catch((_e) => { });
|
|
2216
2237
|
}
|
|
2217
2238
|
}
|
|
@@ -2220,11 +2241,11 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2220
2241
|
throw new CouldNotStart(`Cannot start already ${this.state} module`);
|
|
2221
2242
|
}
|
|
2222
2243
|
this.state = ConnectionManagerState.RUNNING;
|
|
2223
|
-
logger$
|
|
2244
|
+
logger$A.trace(`Starting ConnectionManager...`);
|
|
2224
2245
|
await this.connectorFacade.start((connection) => this.onNewConnection(connection), (nodeId) => this.hasConnection(nodeId), this);
|
|
2225
2246
|
// Garbage collection of connections
|
|
2226
2247
|
this.disconnectorIntervalRef = setInterval(() => {
|
|
2227
|
-
logger$
|
|
2248
|
+
logger$A.trace('disconnectorInterval');
|
|
2228
2249
|
const LAST_USED_LIMIT = 20000;
|
|
2229
2250
|
this.garbageCollectConnections(this.options.maxConnections ?? 80, LAST_USED_LIMIT);
|
|
2230
2251
|
}, 5000); // TODO use options option or named constant?
|
|
@@ -2234,7 +2255,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2234
2255
|
return;
|
|
2235
2256
|
}
|
|
2236
2257
|
this.state = ConnectionManagerState.STOPPING;
|
|
2237
|
-
logger$
|
|
2258
|
+
logger$A.trace(`Stopping ConnectionManager`);
|
|
2238
2259
|
if (this.disconnectorIntervalRef) {
|
|
2239
2260
|
clearInterval(this.disconnectorIntervalRef);
|
|
2240
2261
|
}
|
|
@@ -2244,23 +2265,23 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2244
2265
|
await this.gracefullyDisconnectAsync(endpoint.connection.getPeerDescriptor(), DisconnectMode.LEAVING);
|
|
2245
2266
|
}
|
|
2246
2267
|
catch (e) {
|
|
2247
|
-
logger$
|
|
2268
|
+
logger$A.error(e);
|
|
2248
2269
|
}
|
|
2249
2270
|
}
|
|
2250
2271
|
else {
|
|
2251
2272
|
const connection = endpoint.connection;
|
|
2252
|
-
logger$
|
|
2273
|
+
logger$A.trace('handshake of connection not completed, force-closing');
|
|
2253
2274
|
// TODO use options option or named constant?
|
|
2254
2275
|
const eventReceived = utils.waitForEvent(connection, 'disconnected', 2000);
|
|
2255
2276
|
// TODO should we have some handling for this floating promise?
|
|
2256
2277
|
connection.close(true);
|
|
2257
2278
|
try {
|
|
2258
2279
|
await eventReceived;
|
|
2259
|
-
logger$
|
|
2280
|
+
logger$A.trace('resolving after receiving disconnected event from non-handshaked connection');
|
|
2260
2281
|
}
|
|
2261
2282
|
catch (e) {
|
|
2262
2283
|
endpoint.buffer.reject();
|
|
2263
|
-
logger$
|
|
2284
|
+
logger$A.trace('force-closing non-handshaked connection timed out ' + e);
|
|
2264
2285
|
}
|
|
2265
2286
|
}
|
|
2266
2287
|
}));
|
|
@@ -2289,7 +2310,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2289
2310
|
throw new CannotConnectToSelf('Cannot send to self');
|
|
2290
2311
|
}
|
|
2291
2312
|
const nodeId = toNodeId(peerDescriptor);
|
|
2292
|
-
logger$
|
|
2313
|
+
logger$A.trace(`Sending message to: ${nodeId}`);
|
|
2293
2314
|
message = {
|
|
2294
2315
|
...message,
|
|
2295
2316
|
sourceDescriptor: this.getLocalPeerDescriptor()
|
|
@@ -2344,13 +2365,13 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2344
2365
|
}
|
|
2345
2366
|
handleMessage(message) {
|
|
2346
2367
|
const messageType = message.body.oneofKind;
|
|
2347
|
-
logger$
|
|
2368
|
+
logger$A.trace('Received message of type ' + messageType);
|
|
2348
2369
|
if (messageType !== 'rpcMessage') {
|
|
2349
|
-
logger$
|
|
2370
|
+
logger$A.trace('Filtered out non-RPC message of type ' + messageType);
|
|
2350
2371
|
return;
|
|
2351
2372
|
}
|
|
2352
2373
|
if (this.duplicateMessageDetector.isMostLikelyDuplicate(message.messageId)) {
|
|
2353
|
-
logger$
|
|
2374
|
+
logger$A.trace('handleMessage filtered duplicate ' + toNodeId(message.sourceDescriptor)
|
|
2354
2375
|
+ ' ' + message.serviceId + ' ' + message.messageId);
|
|
2355
2376
|
return;
|
|
2356
2377
|
}
|
|
@@ -2359,7 +2380,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2359
2380
|
this.rpcCommunicator?.handleMessageFromPeer(message);
|
|
2360
2381
|
}
|
|
2361
2382
|
else {
|
|
2362
|
-
logger$
|
|
2383
|
+
logger$A.trace('emit "message" ' + toNodeId(message.sourceDescriptor)
|
|
2363
2384
|
+ ' ' + message.serviceId + ' ' + message.messageId);
|
|
2364
2385
|
this.emit('message', message);
|
|
2365
2386
|
}
|
|
@@ -2376,7 +2397,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2376
2397
|
}
|
|
2377
2398
|
catch (e) {
|
|
2378
2399
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
2379
|
-
logger$
|
|
2400
|
+
logger$A.debug(`Parsing incoming data into Message failed: ${e}`);
|
|
2380
2401
|
return;
|
|
2381
2402
|
}
|
|
2382
2403
|
message.sourceDescriptor = peerDescriptor;
|
|
@@ -2385,7 +2406,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2385
2406
|
}
|
|
2386
2407
|
catch (e) {
|
|
2387
2408
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
2388
|
-
logger$
|
|
2409
|
+
logger$A.debug(`Handling incoming data failed: ${e}`);
|
|
2389
2410
|
}
|
|
2390
2411
|
}
|
|
2391
2412
|
onConnected(peerDescriptor, connection) {
|
|
@@ -2398,7 +2419,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2398
2419
|
const pendingConnection = endpoint.connection;
|
|
2399
2420
|
const buffer = outputBuffer.getBuffer();
|
|
2400
2421
|
while (buffer.length > 0) {
|
|
2401
|
-
logger$
|
|
2422
|
+
logger$A.trace('emptying buffer');
|
|
2402
2423
|
managedConnection.send(buffer.shift());
|
|
2403
2424
|
}
|
|
2404
2425
|
outputBuffer.resolve();
|
|
@@ -2415,7 +2436,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2415
2436
|
}
|
|
2416
2437
|
onDisconnected(peerDescriptor, gracefulLeave) {
|
|
2417
2438
|
const nodeId = toNodeId(peerDescriptor);
|
|
2418
|
-
logger$
|
|
2439
|
+
logger$A.trace(nodeId + ' onDisconnected() gracefulLeave: ' + gracefulLeave);
|
|
2419
2440
|
const endpoint = this.endpoints.get(nodeId);
|
|
2420
2441
|
if (endpoint) {
|
|
2421
2442
|
this.locks.clearAllLocks(nodeId);
|
|
@@ -2423,7 +2444,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2423
2444
|
endpoint.buffer.reject();
|
|
2424
2445
|
}
|
|
2425
2446
|
this.endpoints.delete(nodeId);
|
|
2426
|
-
logger$
|
|
2447
|
+
logger$A.trace(nodeId + ' deleted connection in onDisconnected() gracefulLeave: ' + gracefulLeave);
|
|
2427
2448
|
this.emit('disconnected', peerDescriptor, gracefulLeave);
|
|
2428
2449
|
this.onConnectionCountChange();
|
|
2429
2450
|
}
|
|
@@ -2432,7 +2453,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2432
2453
|
if (this.state === ConnectionManagerState.STOPPED) {
|
|
2433
2454
|
return false;
|
|
2434
2455
|
}
|
|
2435
|
-
logger$
|
|
2456
|
+
logger$A.trace('onNewConnection()');
|
|
2436
2457
|
if (!this.acceptNewConnection(connection)) {
|
|
2437
2458
|
return false;
|
|
2438
2459
|
}
|
|
@@ -2442,7 +2463,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2442
2463
|
}
|
|
2443
2464
|
acceptNewConnection(newConnection) {
|
|
2444
2465
|
const nodeId = toNodeId(newConnection.getPeerDescriptor());
|
|
2445
|
-
logger$
|
|
2466
|
+
logger$A.trace(nodeId + ' acceptNewConnection()');
|
|
2446
2467
|
if (this.endpoints.has(nodeId)) {
|
|
2447
2468
|
if (getOfferer(toNodeId(this.getLocalPeerDescriptor()), nodeId) === 'remote') {
|
|
2448
2469
|
let buffer;
|
|
@@ -2451,14 +2472,14 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2451
2472
|
// Could be related to WS client connections not realizing that they have been disconnected.
|
|
2452
2473
|
// Makes refactoring duplicate connection handling to the connectors very difficult.
|
|
2453
2474
|
if (this.endpoints.get(nodeId).connected) {
|
|
2454
|
-
logger$
|
|
2475
|
+
logger$A.debug('replacing connected connection', { nodeId });
|
|
2455
2476
|
buffer = new OutputBuffer();
|
|
2456
2477
|
}
|
|
2457
2478
|
else {
|
|
2458
2479
|
buffer = endpoint.buffer;
|
|
2459
2480
|
}
|
|
2460
2481
|
const oldConnection = endpoint.connection;
|
|
2461
|
-
logger$
|
|
2482
|
+
logger$A.trace('replaced: ' + nodeId);
|
|
2462
2483
|
oldConnection.replaceAsDuplicate();
|
|
2463
2484
|
this.endpoints.set(nodeId, { connected: false, connection: newConnection, buffer: buffer });
|
|
2464
2485
|
return true;
|
|
@@ -2467,7 +2488,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2467
2488
|
return false;
|
|
2468
2489
|
}
|
|
2469
2490
|
}
|
|
2470
|
-
logger$
|
|
2491
|
+
logger$A.trace(nodeId + ' added to connections at acceptNewConnection');
|
|
2471
2492
|
this.endpoints.set(nodeId, {
|
|
2472
2493
|
connected: false,
|
|
2473
2494
|
buffer: new OutputBuffer(),
|
|
@@ -2477,14 +2498,14 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2477
2498
|
}
|
|
2478
2499
|
async closeConnection(peerDescriptor, gracefulLeave, reason) {
|
|
2479
2500
|
const nodeId = toNodeId(peerDescriptor);
|
|
2480
|
-
logger$
|
|
2501
|
+
logger$A.trace(nodeId + ' ' + 'closeConnection() ' + reason);
|
|
2481
2502
|
this.locks.clearAllLocks(nodeId);
|
|
2482
2503
|
if (this.endpoints.has(nodeId)) {
|
|
2483
2504
|
const connectionToClose = this.endpoints.get(nodeId).connection;
|
|
2484
2505
|
await connectionToClose.close(gracefulLeave);
|
|
2485
2506
|
}
|
|
2486
2507
|
else {
|
|
2487
|
-
logger$
|
|
2508
|
+
logger$A.trace(nodeId + ' ' + 'closeConnection() this.endpoints did not have the id');
|
|
2488
2509
|
this.emit('disconnected', peerDescriptor, false);
|
|
2489
2510
|
}
|
|
2490
2511
|
}
|
|
@@ -2496,8 +2517,8 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2496
2517
|
const rpcRemote = new ConnectionLockRpcRemote(this.getLocalPeerDescriptor(), targetDescriptor, this.rpcCommunicator, ConnectionLockRpcClient);
|
|
2497
2518
|
this.locks.addLocalLocked(nodeId, lockId);
|
|
2498
2519
|
rpcRemote.lockRequest(lockId)
|
|
2499
|
-
.then((_accepted) => logger$
|
|
2500
|
-
.catch((err) => { logger$
|
|
2520
|
+
.then((_accepted) => logger$A.trace('LockRequest successful'))
|
|
2521
|
+
.catch((err) => { logger$A.debug(err); });
|
|
2501
2522
|
}
|
|
2502
2523
|
unlockConnection(targetDescriptor, lockId) {
|
|
2503
2524
|
if (this.state === ConnectionManagerState.STOPPED || areEqualPeerDescriptors(targetDescriptor, this.getLocalPeerDescriptor())) {
|
|
@@ -2549,7 +2570,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2549
2570
|
async gracefullyDisconnectAsync(targetDescriptor, disconnectMode) {
|
|
2550
2571
|
const endpoint = this.endpoints.get(toNodeId(targetDescriptor));
|
|
2551
2572
|
if (!endpoint) {
|
|
2552
|
-
logger$
|
|
2573
|
+
logger$A.debug('gracefullyDisconnectedAsync() tried on a non-existing connection');
|
|
2553
2574
|
return;
|
|
2554
2575
|
}
|
|
2555
2576
|
if (endpoint.connected) {
|
|
@@ -2558,15 +2579,15 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2558
2579
|
// TODO use options option or named constant?
|
|
2559
2580
|
// eslint-disable-next-line promise/catch-or-return
|
|
2560
2581
|
utils.waitForEvent(connection, 'disconnected', 2000).then(() => {
|
|
2561
|
-
logger$
|
|
2582
|
+
logger$A.trace('disconnected event received in gracefullyDisconnectAsync()');
|
|
2562
2583
|
})
|
|
2563
2584
|
.catch((e) => {
|
|
2564
|
-
logger$
|
|
2585
|
+
logger$A.trace('force-closing connection after timeout ' + e);
|
|
2565
2586
|
// TODO should we have some handling for this floating promise?
|
|
2566
2587
|
connection.close(true);
|
|
2567
2588
|
})
|
|
2568
2589
|
.finally(() => {
|
|
2569
|
-
logger$
|
|
2590
|
+
logger$A.trace('resolving after receiving disconnected event');
|
|
2570
2591
|
resolve();
|
|
2571
2592
|
});
|
|
2572
2593
|
});
|
|
@@ -2581,13 +2602,13 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2581
2602
|
}
|
|
2582
2603
|
async doGracefullyDisconnectAsync(targetDescriptor, disconnectMode) {
|
|
2583
2604
|
const nodeId = toNodeId(targetDescriptor);
|
|
2584
|
-
logger$
|
|
2605
|
+
logger$A.trace(nodeId + ' gracefullyDisconnectAsync()');
|
|
2585
2606
|
const rpcRemote = new ConnectionLockRpcRemote(this.getLocalPeerDescriptor(), targetDescriptor, this.rpcCommunicator, ConnectionLockRpcClient);
|
|
2586
2607
|
try {
|
|
2587
2608
|
await rpcRemote.gracefulDisconnect(disconnectMode);
|
|
2588
2609
|
}
|
|
2589
2610
|
catch (ex) {
|
|
2590
|
-
logger$
|
|
2611
|
+
logger$A.trace(nodeId + ' remote.gracefulDisconnect() failed' + ex);
|
|
2591
2612
|
}
|
|
2592
2613
|
}
|
|
2593
2614
|
getConnections() {
|
|
@@ -2654,7 +2675,7 @@ function protoToString(protoObj, objectType) {
|
|
|
2654
2675
|
return ret;
|
|
2655
2676
|
}
|
|
2656
2677
|
|
|
2657
|
-
const logger$
|
|
2678
|
+
const logger$z = new utils.Logger('SimulatorConnection');
|
|
2658
2679
|
class SimulatorConnection extends Connection {
|
|
2659
2680
|
stopped = false;
|
|
2660
2681
|
localPeerDescriptor;
|
|
@@ -2676,46 +2697,46 @@ class SimulatorConnection extends Connection {
|
|
|
2676
2697
|
this.doDisconnect = this.doDisconnect.bind(this);
|
|
2677
2698
|
}
|
|
2678
2699
|
send(data) {
|
|
2679
|
-
logger$
|
|
2700
|
+
logger$z.trace('send()');
|
|
2680
2701
|
if (!this.stopped) {
|
|
2681
2702
|
this.simulator.send(this, data);
|
|
2682
2703
|
}
|
|
2683
2704
|
else {
|
|
2684
2705
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2685
2706
|
const targetNodeId = toNodeId(this.targetPeerDescriptor);
|
|
2686
|
-
logger$
|
|
2707
|
+
logger$z.error(localNodeId + ', ' + targetNodeId + 'tried to send() on a stopped connection');
|
|
2687
2708
|
}
|
|
2688
2709
|
}
|
|
2689
2710
|
async close(gracefulLeave) {
|
|
2690
2711
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2691
2712
|
const targetNodeId = toNodeId(this.targetPeerDescriptor);
|
|
2692
|
-
logger$
|
|
2713
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close()');
|
|
2693
2714
|
if (!this.stopped) {
|
|
2694
|
-
logger$
|
|
2715
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() not stopped');
|
|
2695
2716
|
this.stopped = true;
|
|
2696
2717
|
try {
|
|
2697
|
-
logger$
|
|
2718
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() calling simulator.disconnect()');
|
|
2698
2719
|
this.simulator.close(this);
|
|
2699
|
-
logger$
|
|
2720
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() simulator.disconnect returned');
|
|
2700
2721
|
}
|
|
2701
2722
|
catch (e) {
|
|
2702
|
-
logger$
|
|
2723
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + 'close aborted' + e);
|
|
2703
2724
|
}
|
|
2704
2725
|
finally {
|
|
2705
|
-
logger$
|
|
2726
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' calling this.doDisconnect');
|
|
2706
2727
|
this.doDisconnect(gracefulLeave);
|
|
2707
2728
|
}
|
|
2708
2729
|
}
|
|
2709
2730
|
else {
|
|
2710
|
-
logger$
|
|
2731
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() tried to close a stopped connection');
|
|
2711
2732
|
}
|
|
2712
2733
|
}
|
|
2713
2734
|
connect() {
|
|
2714
2735
|
if (!this.stopped) {
|
|
2715
|
-
logger$
|
|
2736
|
+
logger$z.trace('connect() called');
|
|
2716
2737
|
this.simulator.connect(this, this.targetPeerDescriptor, (error) => {
|
|
2717
2738
|
if (error !== undefined) {
|
|
2718
|
-
logger$
|
|
2739
|
+
logger$z.trace(error);
|
|
2719
2740
|
this.doDisconnect(false);
|
|
2720
2741
|
}
|
|
2721
2742
|
else {
|
|
@@ -2724,46 +2745,46 @@ class SimulatorConnection extends Connection {
|
|
|
2724
2745
|
});
|
|
2725
2746
|
}
|
|
2726
2747
|
else {
|
|
2727
|
-
logger$
|
|
2748
|
+
logger$z.trace('tried to connect() a stopped connection');
|
|
2728
2749
|
}
|
|
2729
2750
|
}
|
|
2730
2751
|
handleIncomingData(data) {
|
|
2731
2752
|
if (!this.stopped) {
|
|
2732
|
-
logger$
|
|
2753
|
+
logger$z.trace('handleIncomingData() ' + protoToString(Message.fromBinary(data), Message));
|
|
2733
2754
|
this.emit('data', data);
|
|
2734
2755
|
}
|
|
2735
2756
|
else {
|
|
2736
|
-
logger$
|
|
2757
|
+
logger$z.trace('tried to call handleIncomingData() a stopped connection');
|
|
2737
2758
|
}
|
|
2738
2759
|
}
|
|
2739
2760
|
handleIncomingDisconnection() {
|
|
2740
2761
|
if (!this.stopped) {
|
|
2741
2762
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2742
|
-
logger$
|
|
2763
|
+
logger$z.trace(localNodeId + ' handleIncomingDisconnection()');
|
|
2743
2764
|
this.stopped = true;
|
|
2744
2765
|
this.doDisconnect(false);
|
|
2745
2766
|
}
|
|
2746
2767
|
else {
|
|
2747
|
-
logger$
|
|
2768
|
+
logger$z.trace('tried to call handleIncomingDisconnection() a stopped connection');
|
|
2748
2769
|
}
|
|
2749
2770
|
}
|
|
2750
2771
|
destroy() {
|
|
2751
2772
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2752
2773
|
if (!this.stopped) {
|
|
2753
|
-
logger$
|
|
2774
|
+
logger$z.trace(localNodeId + ' destroy()');
|
|
2754
2775
|
this.removeAllListeners();
|
|
2755
2776
|
this.close(false).catch((_e) => { });
|
|
2756
2777
|
}
|
|
2757
2778
|
else {
|
|
2758
|
-
logger$
|
|
2779
|
+
logger$z.trace(localNodeId + ' tried to call destroy() a stopped connection');
|
|
2759
2780
|
}
|
|
2760
2781
|
}
|
|
2761
2782
|
doDisconnect(gracefulLeave) {
|
|
2762
2783
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2763
2784
|
const targetNodeId = toNodeId(this.targetPeerDescriptor);
|
|
2764
|
-
logger$
|
|
2785
|
+
logger$z.trace(localNodeId + ' doDisconnect()');
|
|
2765
2786
|
this.stopped = true;
|
|
2766
|
-
logger$
|
|
2787
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' doDisconnect emitting');
|
|
2767
2788
|
this.emit('disconnected', gracefulLeave);
|
|
2768
2789
|
}
|
|
2769
2790
|
}
|
|
@@ -2801,9 +2822,9 @@ const parseVersion = (version) => {
|
|
|
2801
2822
|
}
|
|
2802
2823
|
};
|
|
2803
2824
|
|
|
2804
|
-
var version = "103.
|
|
2825
|
+
var version = "103.6.0-rc.0";
|
|
2805
2826
|
|
|
2806
|
-
const logger$
|
|
2827
|
+
const logger$y = new utils.Logger('Handshaker');
|
|
2807
2828
|
// Optimally the Outgoing and Incoming Handshakers could be their own separate classes
|
|
2808
2829
|
// However, in cases where the PeerDescriptor of the other end of the connection can be known
|
|
2809
2830
|
// only after a HandshakeRequest a base Handshaker class is needed as the IncomingHandshaker currently
|
|
@@ -2825,7 +2846,7 @@ const createOutgoingHandshaker = (localPeerDescriptor, pendingConnection, connec
|
|
|
2825
2846
|
}
|
|
2826
2847
|
};
|
|
2827
2848
|
const handshakeCompletedListener = (peerDescriptor) => {
|
|
2828
|
-
logger$
|
|
2849
|
+
logger$y.trace('handshake completed for outgoing connection, ' + toNodeId(peerDescriptor));
|
|
2829
2850
|
pendingConnection.onHandshakeCompleted(connection);
|
|
2830
2851
|
stopHandshaker();
|
|
2831
2852
|
};
|
|
@@ -2925,12 +2946,12 @@ class Handshaker extends eventemitter3.EventEmitter {
|
|
|
2925
2946
|
try {
|
|
2926
2947
|
const message = Message.fromBinary(data);
|
|
2927
2948
|
if (message.body.oneofKind === 'handshakeRequest') {
|
|
2928
|
-
logger$
|
|
2949
|
+
logger$y.trace('handshake request received');
|
|
2929
2950
|
const handshake = message.body.handshakeRequest;
|
|
2930
2951
|
this.emit('handshakeRequest', handshake.sourcePeerDescriptor, handshake.protocolVersion, handshake.targetPeerDescriptor);
|
|
2931
2952
|
}
|
|
2932
2953
|
if (message.body.oneofKind === 'handshakeResponse') {
|
|
2933
|
-
logger$
|
|
2954
|
+
logger$y.trace('handshake response received');
|
|
2934
2955
|
const handshake = message.body.handshakeResponse;
|
|
2935
2956
|
const error = !isMaybeSupportedProtocolVersion(handshake.protocolVersion)
|
|
2936
2957
|
? HandshakeError.UNSUPPORTED_PROTOCOL_VERSION : handshake.error;
|
|
@@ -2943,18 +2964,18 @@ class Handshaker extends eventemitter3.EventEmitter {
|
|
|
2943
2964
|
}
|
|
2944
2965
|
}
|
|
2945
2966
|
catch (err) {
|
|
2946
|
-
logger$
|
|
2967
|
+
logger$y.debug('error while parsing handshake message', err);
|
|
2947
2968
|
}
|
|
2948
2969
|
}
|
|
2949
2970
|
sendHandshakeRequest(remotePeerDescriptor) {
|
|
2950
2971
|
const msg = createHandshakeRequest(this.localPeerDescriptor, remotePeerDescriptor);
|
|
2951
2972
|
this.connection.send(Message.toBinary(msg));
|
|
2952
|
-
logger$
|
|
2973
|
+
logger$y.trace('handshake request sent');
|
|
2953
2974
|
}
|
|
2954
2975
|
sendHandshakeResponse(error) {
|
|
2955
2976
|
const msg = createHandshakeResponse(this.localPeerDescriptor, error);
|
|
2956
2977
|
this.connection.send(Message.toBinary(msg));
|
|
2957
|
-
logger$
|
|
2978
|
+
logger$y.trace('handshake response sent');
|
|
2958
2979
|
}
|
|
2959
2980
|
stop() {
|
|
2960
2981
|
this.connection.off('data', this.onDataListener);
|
|
@@ -2962,7 +2983,7 @@ class Handshaker extends eventemitter3.EventEmitter {
|
|
|
2962
2983
|
}
|
|
2963
2984
|
}
|
|
2964
2985
|
|
|
2965
|
-
const logger$
|
|
2986
|
+
const logger$x = new utils.Logger('PendingConnection');
|
|
2966
2987
|
// PendingConnection is used as a reference to a connection that should be opened and handshaked to a given PeerDescriptor
|
|
2967
2988
|
// It does not hold a connection internally. The public method onHandshakedCompleted should be called once a connection for the
|
|
2968
2989
|
// remotePeerDescriptor is opened and handshaked successfully.
|
|
@@ -2980,7 +3001,7 @@ class PendingConnection extends eventemitter3.EventEmitter {
|
|
|
2980
3001
|
}, timeout, this.connectingAbortController.signal);
|
|
2981
3002
|
}
|
|
2982
3003
|
replaceAsDuplicate() {
|
|
2983
|
-
logger$
|
|
3004
|
+
logger$x.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
|
|
2984
3005
|
this.replacedAsDuplicate = true;
|
|
2985
3006
|
}
|
|
2986
3007
|
onHandshakeCompleted(connection) {
|
|
@@ -3011,7 +3032,7 @@ class PendingConnection extends eventemitter3.EventEmitter {
|
|
|
3011
3032
|
}
|
|
3012
3033
|
}
|
|
3013
3034
|
|
|
3014
|
-
const logger$
|
|
3035
|
+
const logger$w = new utils.Logger('SimulatorConnector');
|
|
3015
3036
|
class SimulatorConnector {
|
|
3016
3037
|
connectingConnections = new Map();
|
|
3017
3038
|
stopped = false;
|
|
@@ -3024,7 +3045,7 @@ class SimulatorConnector {
|
|
|
3024
3045
|
this.onNewConnection = onNewConnection;
|
|
3025
3046
|
}
|
|
3026
3047
|
connect(targetPeerDescriptor) {
|
|
3027
|
-
logger$
|
|
3048
|
+
logger$w.trace('connect() ' + toNodeId(targetPeerDescriptor));
|
|
3028
3049
|
const nodeId = toNodeId(targetPeerDescriptor);
|
|
3029
3050
|
const existingConnection = this.connectingConnections.get(nodeId);
|
|
3030
3051
|
if (existingConnection) {
|
|
@@ -3053,18 +3074,18 @@ class SimulatorConnector {
|
|
|
3053
3074
|
// connection is incoming, so remotePeerDescriptor is localPeerDescriptor
|
|
3054
3075
|
const remotePeerDescriptor = sourceConnection.localPeerDescriptor;
|
|
3055
3076
|
const remoteNodeId = toNodeId(sourceConnection.localPeerDescriptor);
|
|
3056
|
-
logger$
|
|
3077
|
+
logger$w.trace(remoteNodeId + ' incoming connection, stopped: ' + this.stopped);
|
|
3057
3078
|
if (this.stopped) {
|
|
3058
3079
|
return;
|
|
3059
3080
|
}
|
|
3060
3081
|
const connection = new SimulatorConnection(this.localPeerDescriptor, remotePeerDescriptor, exports.ConnectionType.SIMULATOR_SERVER, this.simulator);
|
|
3061
3082
|
const pendingConnection = new PendingConnection(remotePeerDescriptor);
|
|
3062
3083
|
const handshaker = createIncomingHandshaker(this.localPeerDescriptor, pendingConnection, connection);
|
|
3063
|
-
logger$
|
|
3084
|
+
logger$w.trace('connected');
|
|
3064
3085
|
handshaker.once('handshakeRequest', () => {
|
|
3065
|
-
logger$
|
|
3086
|
+
logger$w.trace(remoteNodeId + ' incoming handshake request');
|
|
3066
3087
|
if (this.onNewConnection(pendingConnection)) {
|
|
3067
|
-
logger$
|
|
3088
|
+
logger$w.trace(remoteNodeId + ' calling acceptHandshake');
|
|
3068
3089
|
acceptHandshake(handshaker, pendingConnection, connection);
|
|
3069
3090
|
}
|
|
3070
3091
|
else {
|
|
@@ -3104,6 +3125,8 @@ class ListeningRpcCommunicator extends RoutingRpcCommunicator {
|
|
|
3104
3125
|
}
|
|
3105
3126
|
}
|
|
3106
3127
|
|
|
3128
|
+
const isWorkerEnvironment = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
|
|
3129
|
+
|
|
3107
3130
|
var RtcDescription;
|
|
3108
3131
|
(function (RtcDescription) {
|
|
3109
3132
|
RtcDescription["OFFER"] = "offer";
|
|
@@ -3118,8 +3141,8 @@ var DisconnectedRtcPeerConnectionStateEnum;
|
|
|
3118
3141
|
DisconnectedRtcPeerConnectionStateEnum["FAILED"] = "failed";
|
|
3119
3142
|
DisconnectedRtcPeerConnectionStateEnum["CLOSED"] = "closed";
|
|
3120
3143
|
})(DisconnectedRtcPeerConnectionStateEnum || (DisconnectedRtcPeerConnectionStateEnum = {}));
|
|
3121
|
-
const logger$
|
|
3122
|
-
class
|
|
3144
|
+
const logger$v = new utils.Logger('DirectWebrtcConnection (browser)');
|
|
3145
|
+
class DirectWebrtcConnection extends eventemitter3.EventEmitter {
|
|
3123
3146
|
connectionId;
|
|
3124
3147
|
connectionType = exports.ConnectionType.WEBRTC;
|
|
3125
3148
|
// We need to keep track of connection state ourselves because
|
|
@@ -3157,7 +3180,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3157
3180
|
}
|
|
3158
3181
|
};
|
|
3159
3182
|
this.peerConnection.onicegatheringstatechange = () => {
|
|
3160
|
-
logger$
|
|
3183
|
+
logger$v.trace(`conn.onGatheringStateChange: ${this.peerConnection?.iceGatheringState}`);
|
|
3161
3184
|
};
|
|
3162
3185
|
this.peerConnection.onconnectionstatechange = () => this.onStateChange();
|
|
3163
3186
|
if (isOffering) {
|
|
@@ -3168,7 +3191,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3168
3191
|
await this.peerConnection.setLocalDescription();
|
|
3169
3192
|
}
|
|
3170
3193
|
catch (err) {
|
|
3171
|
-
logger$
|
|
3194
|
+
logger$v.warn('Failed to set local description', { err });
|
|
3172
3195
|
}
|
|
3173
3196
|
if (this.peerConnection.localDescription !== null) {
|
|
3174
3197
|
this.emit('localDescription', this.peerConnection.localDescription?.sdp, this.peerConnection.localDescription?.type);
|
|
@@ -3196,14 +3219,14 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3196
3219
|
clearTimeout(this.earlyTimeout);
|
|
3197
3220
|
}
|
|
3198
3221
|
catch (err) {
|
|
3199
|
-
logger$
|
|
3222
|
+
logger$v.warn('Failed to set remote description', { err });
|
|
3200
3223
|
}
|
|
3201
3224
|
if ((type.toLowerCase() === RtcDescription.OFFER) && (this.peerConnection !== undefined)) {
|
|
3202
3225
|
try {
|
|
3203
3226
|
await this.peerConnection.setLocalDescription();
|
|
3204
3227
|
}
|
|
3205
3228
|
catch (err) {
|
|
3206
|
-
logger$
|
|
3229
|
+
logger$v.warn('Failed to set local description', { err });
|
|
3207
3230
|
}
|
|
3208
3231
|
if (this.peerConnection.localDescription !== null) {
|
|
3209
3232
|
this.emit('localDescription', this.peerConnection.localDescription.sdp, this.peerConnection.localDescription.type);
|
|
@@ -3213,7 +3236,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3213
3236
|
addRemoteCandidate(candidate, mid) {
|
|
3214
3237
|
this.peerConnection?.addIceCandidate({ candidate: candidate, sdpMid: mid })
|
|
3215
3238
|
.catch((err) => {
|
|
3216
|
-
logger$
|
|
3239
|
+
logger$v.warn('Failed to add ICE candidate', { err });
|
|
3217
3240
|
});
|
|
3218
3241
|
}
|
|
3219
3242
|
isOpen() {
|
|
@@ -3236,7 +3259,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3236
3259
|
this.dataChannel.close();
|
|
3237
3260
|
}
|
|
3238
3261
|
catch (err) {
|
|
3239
|
-
logger$
|
|
3262
|
+
logger$v.warn('Failed to close data channel', { err });
|
|
3240
3263
|
}
|
|
3241
3264
|
}
|
|
3242
3265
|
this.dataChannel = undefined;
|
|
@@ -3245,7 +3268,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3245
3268
|
this.peerConnection.close();
|
|
3246
3269
|
}
|
|
3247
3270
|
catch (err) {
|
|
3248
|
-
logger$
|
|
3271
|
+
logger$v.warn('Failed to close connection', { err });
|
|
3249
3272
|
}
|
|
3250
3273
|
}
|
|
3251
3274
|
this.peerConnection = undefined;
|
|
@@ -3265,7 +3288,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3265
3288
|
}
|
|
3266
3289
|
}
|
|
3267
3290
|
else {
|
|
3268
|
-
logger$
|
|
3291
|
+
logger$v.warn('Tried to send on a connection with last state ' + this.lastState);
|
|
3269
3292
|
}
|
|
3270
3293
|
}
|
|
3271
3294
|
setupDataChannel(dataChannel) {
|
|
@@ -3273,22 +3296,22 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3273
3296
|
this.dataChannel.binaryType = 'arraybuffer';
|
|
3274
3297
|
this.dataChannel.bufferedAmountLowThreshold = this.bufferThresholdLow;
|
|
3275
3298
|
dataChannel.onopen = () => {
|
|
3276
|
-
logger$
|
|
3299
|
+
logger$v.trace('dc.onOpen');
|
|
3277
3300
|
this.onDataChannelOpen();
|
|
3278
3301
|
};
|
|
3279
3302
|
dataChannel.onclose = () => {
|
|
3280
|
-
logger$
|
|
3303
|
+
logger$v.trace('dc.onClosed');
|
|
3281
3304
|
this.doClose(false);
|
|
3282
3305
|
};
|
|
3283
3306
|
dataChannel.onerror = (err) => {
|
|
3284
|
-
logger$
|
|
3307
|
+
logger$v.warn('Data channel error', { err });
|
|
3285
3308
|
};
|
|
3286
3309
|
dataChannel.onmessage = (msg) => {
|
|
3287
|
-
logger$
|
|
3310
|
+
logger$v.trace('dc.onmessage');
|
|
3288
3311
|
this.emit('data', new Uint8Array(msg.data));
|
|
3289
3312
|
};
|
|
3290
3313
|
dataChannel.onbufferedamountlow = () => {
|
|
3291
|
-
logger$
|
|
3314
|
+
logger$v.trace('dc.onBufferedAmountLow');
|
|
3292
3315
|
while (this.messageQueue.length > 0 && this.dataChannel.bufferedAmount < this.bufferThresholdHigh) {
|
|
3293
3316
|
const data = this.messageQueue.shift();
|
|
3294
3317
|
this.dataChannel.send(data);
|
|
@@ -3327,6 +3350,431 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3327
3350
|
}
|
|
3328
3351
|
}
|
|
3329
3352
|
|
|
3353
|
+
/**
|
|
3354
|
+
* WebrtcBridge — runs on the MAIN THREAD.
|
|
3355
|
+
*
|
|
3356
|
+
* Manages RTCPeerConnection instances on behalf of a worker that cannot
|
|
3357
|
+
* access them directly. Each logical connection is identified by a
|
|
3358
|
+
* `connectionId` string supplied by the worker.
|
|
3359
|
+
*
|
|
3360
|
+
* Signaling (ICE candidates, SDP offers/answers, connection-state changes)
|
|
3361
|
+
* is relayed to the worker through Comlink proxy callbacks.
|
|
3362
|
+
*
|
|
3363
|
+
* Once a DataChannel is created (offerer) or received (answerer) it is
|
|
3364
|
+
* **transferred** to the worker so that all data-path events fire inside
|
|
3365
|
+
* the worker's event loop — the main thread never touches data traffic.
|
|
3366
|
+
*/
|
|
3367
|
+
// ── Bridge implementation ───────────────────────────────────────────
|
|
3368
|
+
class WebrtcBridge {
|
|
3369
|
+
connections = new Map();
|
|
3370
|
+
async start(connectionId, iceServers, isOffering, callbacks) {
|
|
3371
|
+
const pc = new RTCPeerConnection({ iceServers });
|
|
3372
|
+
const conn = {
|
|
3373
|
+
pc,
|
|
3374
|
+
callbacks,
|
|
3375
|
+
isOffering,
|
|
3376
|
+
makingOffer: false,
|
|
3377
|
+
};
|
|
3378
|
+
this.connections.set(connectionId, conn);
|
|
3379
|
+
// ── ICE candidates ──────────────────────────────────────
|
|
3380
|
+
pc.onicecandidate = (event) => {
|
|
3381
|
+
if (event.candidate !== null && event.candidate.sdpMid !== null) {
|
|
3382
|
+
callbacks.onLocalCandidate(event.candidate.candidate, event.candidate.sdpMid);
|
|
3383
|
+
}
|
|
3384
|
+
};
|
|
3385
|
+
// ── Connection state → forwarded to worker ──────────────
|
|
3386
|
+
pc.onconnectionstatechange = () => {
|
|
3387
|
+
callbacks.onConnectionStateChange(pc.connectionState);
|
|
3388
|
+
};
|
|
3389
|
+
// ── Offerer path ────────────────────────────────────────
|
|
3390
|
+
if (isOffering) {
|
|
3391
|
+
pc.onnegotiationneeded = async () => {
|
|
3392
|
+
conn.makingOffer = true;
|
|
3393
|
+
try {
|
|
3394
|
+
await pc.setLocalDescription();
|
|
3395
|
+
}
|
|
3396
|
+
catch (_err) {
|
|
3397
|
+
// intentionally swallowed – mirrors DirectWebrtcConnection
|
|
3398
|
+
}
|
|
3399
|
+
if (pc.localDescription !== null) {
|
|
3400
|
+
callbacks.onLocalDescription(pc.localDescription.sdp, pc.localDescription.type);
|
|
3401
|
+
}
|
|
3402
|
+
conn.makingOffer = false;
|
|
3403
|
+
};
|
|
3404
|
+
const dc = pc.createDataChannel('streamrDataChannel');
|
|
3405
|
+
// Transfer DataChannel ownership to the worker immediately.
|
|
3406
|
+
// The worker will attach onopen/onclose/onmessage handlers.
|
|
3407
|
+
callbacks.onDataChannel(Comlink__namespace.transfer(dc, [dc]));
|
|
3408
|
+
}
|
|
3409
|
+
else {
|
|
3410
|
+
// ── Answerer path ───────────────────────────────────
|
|
3411
|
+
pc.ondatachannel = (event) => {
|
|
3412
|
+
callbacks.onDataChannel(Comlink__namespace.transfer(event.channel, [event.channel]));
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
async setRemoteDescription(connectionId, description, type) {
|
|
3417
|
+
const conn = this.connections.get(connectionId);
|
|
3418
|
+
if (!conn) {
|
|
3419
|
+
return false;
|
|
3420
|
+
}
|
|
3421
|
+
const lowerType = type.toLowerCase();
|
|
3422
|
+
// Perfect-negotiation collision detection
|
|
3423
|
+
const offerCollision = lowerType === 'offer' &&
|
|
3424
|
+
(conn.makingOffer || conn.pc.signalingState !== 'stable');
|
|
3425
|
+
if (conn.isOffering && offerCollision) {
|
|
3426
|
+
return false;
|
|
3427
|
+
}
|
|
3428
|
+
try {
|
|
3429
|
+
await conn.pc.setRemoteDescription({ sdp: description, type: lowerType });
|
|
3430
|
+
}
|
|
3431
|
+
catch (_err) {
|
|
3432
|
+
return false;
|
|
3433
|
+
}
|
|
3434
|
+
// If we received an offer, create an answer
|
|
3435
|
+
if (lowerType === 'offer') {
|
|
3436
|
+
try {
|
|
3437
|
+
await conn.pc.setLocalDescription();
|
|
3438
|
+
}
|
|
3439
|
+
catch (_err) {
|
|
3440
|
+
// intentionally swallowed
|
|
3441
|
+
}
|
|
3442
|
+
if (conn.pc.localDescription !== null) {
|
|
3443
|
+
conn.callbacks.onLocalDescription(conn.pc.localDescription.sdp, conn.pc.localDescription.type);
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
return true;
|
|
3447
|
+
}
|
|
3448
|
+
async addRemoteCandidate(connectionId, candidate, mid) {
|
|
3449
|
+
const conn = this.connections.get(connectionId);
|
|
3450
|
+
if (!conn) {
|
|
3451
|
+
return;
|
|
3452
|
+
}
|
|
3453
|
+
try {
|
|
3454
|
+
await conn.pc.addIceCandidate({ candidate, sdpMid: mid });
|
|
3455
|
+
}
|
|
3456
|
+
catch (_err) {
|
|
3457
|
+
// intentionally swallowed
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
async renameConnection(oldId, newId) {
|
|
3461
|
+
const conn = this.connections.get(oldId);
|
|
3462
|
+
if (conn) {
|
|
3463
|
+
this.connections.delete(oldId);
|
|
3464
|
+
this.connections.set(newId, conn);
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
async close(connectionId) {
|
|
3468
|
+
const conn = this.connections.get(connectionId);
|
|
3469
|
+
if (!conn) {
|
|
3470
|
+
return;
|
|
3471
|
+
}
|
|
3472
|
+
this.connections.delete(connectionId);
|
|
3473
|
+
// Tear down event handlers
|
|
3474
|
+
conn.pc.onicecandidate = null;
|
|
3475
|
+
conn.pc.onconnectionstatechange = null;
|
|
3476
|
+
conn.pc.onnegotiationneeded = null;
|
|
3477
|
+
conn.pc.ondatachannel = null;
|
|
3478
|
+
try {
|
|
3479
|
+
conn.pc.close();
|
|
3480
|
+
}
|
|
3481
|
+
catch (_err) {
|
|
3482
|
+
// intentionally swallowed
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
/**
|
|
3488
|
+
* Call this function on the **main thread** before the worker starts using
|
|
3489
|
+
* WebRTC connections. It creates a dedicated `MessageChannel`, exposes a
|
|
3490
|
+
* {@link WebrtcBridge} instance on one port, and sends the other port to
|
|
3491
|
+
* the worker so that `WorkerWebrtcConnection` can reach the bridge.
|
|
3492
|
+
*
|
|
3493
|
+
* @example
|
|
3494
|
+
* ```ts
|
|
3495
|
+
* import { installWebrtcBridge } from '@streamr/dht'
|
|
3496
|
+
*
|
|
3497
|
+
* const worker = new Worker('./my-worker.ts', { type: 'module' })
|
|
3498
|
+
* installWebrtcBridge(worker)
|
|
3499
|
+
* ```
|
|
3500
|
+
*/
|
|
3501
|
+
const WEBRTC_BRIDGE_PORT_MESSAGE_TYPE = 'streamr-webrtc-bridge-port';
|
|
3502
|
+
function installWebrtcBridge(worker) {
|
|
3503
|
+
const bridge = new WebrtcBridge();
|
|
3504
|
+
const channel = new MessageChannel();
|
|
3505
|
+
// Expose the bridge API on port1 — the worker will Comlink.wrap(port2).
|
|
3506
|
+
Comlink__namespace.expose(bridge, channel.port1);
|
|
3507
|
+
// Send port2 to the worker. It is transferred (not cloned).
|
|
3508
|
+
worker.postMessage({ type: WEBRTC_BRIDGE_PORT_MESSAGE_TYPE, port: channel.port2 }, [channel.port2]);
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3511
|
+
/**
|
|
3512
|
+
* WorkerWebrtcConnection — runs inside a **Web Worker**.
|
|
3513
|
+
*
|
|
3514
|
+
* Implements the same IWebrtcConnection + IConnection interfaces as
|
|
3515
|
+
* DirectWebrtcConnection, but delegates RTCPeerConnection management to
|
|
3516
|
+
* the main-thread {@link WebrtcBridge} via Comlink.
|
|
3517
|
+
*
|
|
3518
|
+
* The RTCDataChannel is **transferred** from the main thread and lives
|
|
3519
|
+
* entirely in the worker — all data events (onmessage, onopen, onclose,
|
|
3520
|
+
* onbufferedamountlow) fire in the worker's event loop. The main thread
|
|
3521
|
+
* is never involved in the data path.
|
|
3522
|
+
*/
|
|
3523
|
+
// ── Module-level bridge client (initialized once per worker) ────────
|
|
3524
|
+
let resolveBridgeProxy;
|
|
3525
|
+
const bridgeProxyPromise = new Promise((resolve) => {
|
|
3526
|
+
resolveBridgeProxy = resolve;
|
|
3527
|
+
});
|
|
3528
|
+
// Listen for the bridge port message from the main thread.
|
|
3529
|
+
// This is guarded so it only runs inside a worker context.
|
|
3530
|
+
if (isWorkerEnvironment) {
|
|
3531
|
+
const handler = (e) => {
|
|
3532
|
+
if (e.data?.type === WEBRTC_BRIDGE_PORT_MESSAGE_TYPE && e.data.port) {
|
|
3533
|
+
const proxy = Comlink__namespace.wrap(e.data.port);
|
|
3534
|
+
resolveBridgeProxy(proxy);
|
|
3535
|
+
self.removeEventListener('message', handler);
|
|
3536
|
+
}
|
|
3537
|
+
};
|
|
3538
|
+
self.addEventListener('message', handler);
|
|
3539
|
+
}
|
|
3540
|
+
function getBridgeProxy() {
|
|
3541
|
+
return bridgeProxyPromise;
|
|
3542
|
+
}
|
|
3543
|
+
// ── Disconnection states ────────────────────────────────────────────
|
|
3544
|
+
var DisconnectedState;
|
|
3545
|
+
(function (DisconnectedState) {
|
|
3546
|
+
DisconnectedState["DISCONNECTED"] = "disconnected";
|
|
3547
|
+
DisconnectedState["FAILED"] = "failed";
|
|
3548
|
+
DisconnectedState["CLOSED"] = "closed";
|
|
3549
|
+
})(DisconnectedState || (DisconnectedState = {}));
|
|
3550
|
+
const logger$u = new utils.Logger('WorkerWebrtcConnection');
|
|
3551
|
+
// ── WorkerWebrtcConnection ──────────────────────────────────────────
|
|
3552
|
+
class WorkerWebrtcConnection extends eventemitter3.EventEmitter {
|
|
3553
|
+
connectionId;
|
|
3554
|
+
connectionType = exports.ConnectionType.WEBRTC;
|
|
3555
|
+
iceServers;
|
|
3556
|
+
bufferThresholdHigh;
|
|
3557
|
+
bufferThresholdLow;
|
|
3558
|
+
dataChannel;
|
|
3559
|
+
bridge;
|
|
3560
|
+
closed = false;
|
|
3561
|
+
connected = false;
|
|
3562
|
+
earlyTimeout;
|
|
3563
|
+
messageQueue = [];
|
|
3564
|
+
startPromise;
|
|
3565
|
+
constructor(params) {
|
|
3566
|
+
super();
|
|
3567
|
+
this.connectionId = createRandomConnectionId();
|
|
3568
|
+
this.iceServers = params.iceServers ?? [];
|
|
3569
|
+
this.bufferThresholdHigh = params.bufferThresholdHigh ?? 2 ** 17;
|
|
3570
|
+
this.bufferThresholdLow = params.bufferThresholdLow ?? 2 ** 15;
|
|
3571
|
+
this.earlyTimeout = setTimeout(() => {
|
|
3572
|
+
this.doClose(false, 'timed out due to remote descriptor not being set');
|
|
3573
|
+
}, EARLY_TIMEOUT);
|
|
3574
|
+
}
|
|
3575
|
+
// ── IWebrtcConnection ───────────────────────────────────────
|
|
3576
|
+
start(isOffering) {
|
|
3577
|
+
this.startPromise = this.doStart(isOffering);
|
|
3578
|
+
this.startPromise.catch((err) => {
|
|
3579
|
+
logger$u.warn('Failed to start worker WebRTC connection', { err });
|
|
3580
|
+
this.doClose(false, 'Failed to start');
|
|
3581
|
+
});
|
|
3582
|
+
}
|
|
3583
|
+
async doStart(isOffering) {
|
|
3584
|
+
this.bridge = await getBridgeProxy();
|
|
3585
|
+
const iceServers = this.iceServers.map(({ url, port, username, password }) => ({
|
|
3586
|
+
urls: `${url}:${port}`,
|
|
3587
|
+
username,
|
|
3588
|
+
credential: password,
|
|
3589
|
+
}));
|
|
3590
|
+
await this.bridge.start(this.connectionId, iceServers, isOffering, Comlink__namespace.proxy({
|
|
3591
|
+
onLocalCandidate: (candidate, mid) => {
|
|
3592
|
+
if (!this.closed) {
|
|
3593
|
+
this.emit('localCandidate', candidate, mid);
|
|
3594
|
+
}
|
|
3595
|
+
},
|
|
3596
|
+
onLocalDescription: (description, type) => {
|
|
3597
|
+
if (!this.closed) {
|
|
3598
|
+
this.emit('localDescription', description, type);
|
|
3599
|
+
}
|
|
3600
|
+
},
|
|
3601
|
+
onConnectionStateChange: (state) => {
|
|
3602
|
+
if (state === DisconnectedState.CLOSED ||
|
|
3603
|
+
state === DisconnectedState.DISCONNECTED ||
|
|
3604
|
+
state === DisconnectedState.FAILED) {
|
|
3605
|
+
this.doClose(false);
|
|
3606
|
+
}
|
|
3607
|
+
},
|
|
3608
|
+
onDataChannel: (channel) => {
|
|
3609
|
+
if (!this.closed) {
|
|
3610
|
+
this.setupDataChannel(channel);
|
|
3611
|
+
// If the channel was already open at transfer time
|
|
3612
|
+
if (channel.readyState === 'open') {
|
|
3613
|
+
this.onDataChannelOpen();
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
},
|
|
3617
|
+
}));
|
|
3618
|
+
}
|
|
3619
|
+
async setRemoteDescription(description, type) {
|
|
3620
|
+
if (this.startPromise) {
|
|
3621
|
+
await this.startPromise;
|
|
3622
|
+
}
|
|
3623
|
+
if (!this.bridge || this.closed) {
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
const wasSet = await this.bridge.setRemoteDescription(this.connectionId, description, type);
|
|
3627
|
+
if (wasSet) {
|
|
3628
|
+
clearTimeout(this.earlyTimeout);
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
addRemoteCandidate(candidate, mid) {
|
|
3632
|
+
this.doAddRemoteCandidate(candidate, mid).catch((err) => {
|
|
3633
|
+
logger$u.warn('Failed to add remote candidate via bridge', { err });
|
|
3634
|
+
});
|
|
3635
|
+
}
|
|
3636
|
+
async doAddRemoteCandidate(candidate, mid) {
|
|
3637
|
+
if (this.startPromise) {
|
|
3638
|
+
await this.startPromise;
|
|
3639
|
+
}
|
|
3640
|
+
if (!this.bridge || this.closed) {
|
|
3641
|
+
return;
|
|
3642
|
+
}
|
|
3643
|
+
await this.bridge.addRemoteCandidate(this.connectionId, candidate, mid);
|
|
3644
|
+
}
|
|
3645
|
+
isOpen() {
|
|
3646
|
+
return this.connected;
|
|
3647
|
+
}
|
|
3648
|
+
// ── IConnection ─────────────────────────────────────────────
|
|
3649
|
+
async close(gracefulLeave, reason) {
|
|
3650
|
+
this.doClose(gracefulLeave, reason);
|
|
3651
|
+
}
|
|
3652
|
+
destroy() {
|
|
3653
|
+
this.removeAllListeners();
|
|
3654
|
+
this.doClose(false);
|
|
3655
|
+
}
|
|
3656
|
+
send(data) {
|
|
3657
|
+
if (this.connected && this.dataChannel) {
|
|
3658
|
+
if (this.dataChannel.bufferedAmount > this.bufferThresholdHigh) {
|
|
3659
|
+
this.messageQueue.push(data);
|
|
3660
|
+
}
|
|
3661
|
+
else {
|
|
3662
|
+
this.dataChannel.send(data);
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
else if (!this.closed) {
|
|
3666
|
+
this.messageQueue.push(data);
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
setConnectionId(connectionId) {
|
|
3670
|
+
const oldId = this.connectionId;
|
|
3671
|
+
this.connectionId = connectionId;
|
|
3672
|
+
if (this.bridge && oldId !== connectionId) {
|
|
3673
|
+
this.bridge.renameConnection(oldId, connectionId).catch(() => { });
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
// ── DataChannel handling (runs entirely in the worker) ──────
|
|
3677
|
+
setupDataChannel(dataChannel) {
|
|
3678
|
+
this.dataChannel = dataChannel;
|
|
3679
|
+
this.dataChannel.binaryType = 'arraybuffer';
|
|
3680
|
+
this.dataChannel.bufferedAmountLowThreshold = this.bufferThresholdLow;
|
|
3681
|
+
dataChannel.onopen = () => {
|
|
3682
|
+
logger$u.trace('dc.onOpen (worker)');
|
|
3683
|
+
this.onDataChannelOpen();
|
|
3684
|
+
};
|
|
3685
|
+
dataChannel.onclose = () => {
|
|
3686
|
+
logger$u.trace('dc.onClosed (worker)');
|
|
3687
|
+
this.doClose(false);
|
|
3688
|
+
};
|
|
3689
|
+
dataChannel.onerror = (err) => {
|
|
3690
|
+
logger$u.warn('Data channel error (worker)', { err });
|
|
3691
|
+
};
|
|
3692
|
+
dataChannel.onmessage = (msg) => {
|
|
3693
|
+
logger$u.trace('dc.onmessage (worker)');
|
|
3694
|
+
this.emit('data', new Uint8Array(msg.data));
|
|
3695
|
+
};
|
|
3696
|
+
dataChannel.onbufferedamountlow = () => {
|
|
3697
|
+
logger$u.trace('dc.onBufferedAmountLow (worker)');
|
|
3698
|
+
while (this.messageQueue.length > 0 &&
|
|
3699
|
+
this.dataChannel.bufferedAmount < this.bufferThresholdHigh) {
|
|
3700
|
+
const data = this.messageQueue.shift();
|
|
3701
|
+
this.dataChannel.send(data);
|
|
3702
|
+
}
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
onDataChannelOpen() {
|
|
3706
|
+
this.connected = true;
|
|
3707
|
+
this.flushMessageQueue();
|
|
3708
|
+
this.emit('connected');
|
|
3709
|
+
}
|
|
3710
|
+
flushMessageQueue() {
|
|
3711
|
+
while (this.messageQueue.length > 0 &&
|
|
3712
|
+
this.dataChannel &&
|
|
3713
|
+
this.dataChannel.bufferedAmount < this.bufferThresholdHigh) {
|
|
3714
|
+
const data = this.messageQueue.shift();
|
|
3715
|
+
this.dataChannel.send(data);
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
// ── Teardown ────────────────────────────────────────────────
|
|
3719
|
+
doClose(gracefulLeave, reason) {
|
|
3720
|
+
if (!this.closed) {
|
|
3721
|
+
this.closed = true;
|
|
3722
|
+
this.connected = false;
|
|
3723
|
+
this.messageQueue.length = 0;
|
|
3724
|
+
clearTimeout(this.earlyTimeout);
|
|
3725
|
+
this.stopListening();
|
|
3726
|
+
this.emit('disconnected', gracefulLeave, undefined, reason);
|
|
3727
|
+
this.removeAllListeners();
|
|
3728
|
+
if (this.dataChannel !== undefined) {
|
|
3729
|
+
try {
|
|
3730
|
+
this.dataChannel.close();
|
|
3731
|
+
}
|
|
3732
|
+
catch (err) {
|
|
3733
|
+
logger$u.warn('Failed to close data channel (worker)', { err });
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
this.dataChannel = undefined;
|
|
3737
|
+
// Tell the main-thread bridge to tear down the RTCPeerConnection.
|
|
3738
|
+
// Fire-and-forget — we don't block on this.
|
|
3739
|
+
this.bridge
|
|
3740
|
+
?.close(this.connectionId)
|
|
3741
|
+
.catch(() => {
|
|
3742
|
+
// intentionally swallowed
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
stopListening() {
|
|
3747
|
+
if (this.dataChannel !== undefined) {
|
|
3748
|
+
this.dataChannel.onopen = null;
|
|
3749
|
+
this.dataChannel.onclose = null;
|
|
3750
|
+
this.dataChannel.onerror = null;
|
|
3751
|
+
this.dataChannel.onbufferedamountlow = null;
|
|
3752
|
+
this.dataChannel.onmessage = null;
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
|
|
3757
|
+
/**
|
|
3758
|
+
* Conditional re-export of the browser WebrtcConnection.
|
|
3759
|
+
*
|
|
3760
|
+
* At module-load time we detect whether we are running inside a Web Worker.
|
|
3761
|
+
* - **Main thread** → use {@link DirectWebrtcConnection} which owns the
|
|
3762
|
+
* `RTCPeerConnection` and `RTCDataChannel` directly.
|
|
3763
|
+
* - **Worker thread** → use {@link WorkerWebrtcConnection} which delegates
|
|
3764
|
+
* `RTCPeerConnection` signaling to the main thread via a Comlink bridge
|
|
3765
|
+
* and receives a transferred `RTCDataChannel` that lives entirely in the
|
|
3766
|
+
* worker.
|
|
3767
|
+
*
|
|
3768
|
+
* Both classes implement `IWebrtcConnection & IConnection` and expose the
|
|
3769
|
+
* same public API, so upstream code (WebrtcConnector, etc.) is unaffected.
|
|
3770
|
+
*/
|
|
3771
|
+
// The constructor — points to the right class based on the runtime
|
|
3772
|
+
// environment. The type assertion is safe because both implementations
|
|
3773
|
+
// share the same public interface surface.
|
|
3774
|
+
const WebrtcConnection = (isWorkerEnvironment
|
|
3775
|
+
? WorkerWebrtcConnection
|
|
3776
|
+
: DirectWebrtcConnection);
|
|
3777
|
+
|
|
3330
3778
|
const logger$t = new utils.Logger('WebrtcConnectorRpcRemote');
|
|
3331
3779
|
class WebrtcConnectorRpcRemote extends RpcRemote {
|
|
3332
3780
|
requestConnection() {
|
|
@@ -7629,6 +8077,7 @@ exports.areEqualPeerDescriptors = areEqualPeerDescriptors;
|
|
|
7629
8077
|
exports.createOutgoingHandshaker = createOutgoingHandshaker;
|
|
7630
8078
|
exports.getRandomRegion = getRandomRegion;
|
|
7631
8079
|
exports.getRegionDelayMatrix = getRegionDelayMatrix;
|
|
8080
|
+
exports.installWebrtcBridge = installWebrtcBridge;
|
|
7632
8081
|
exports.randomDhtAddress = randomDhtAddress;
|
|
7633
8082
|
exports.toDhtAddress = toDhtAddress;
|
|
7634
8083
|
exports.toDhtAddressRaw = toDhtAddressRaw;
|