@streamr/dht 103.3.1 → 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 +548 -100
- package/dist/exports-browser.cjs.map +1 -1
- package/dist/exports-browser.d.ts +6 -4
- package/dist/exports-browser.js +529 -101
- package/dist/exports-browser.js.map +1 -1
- package/dist/exports-nodejs.cjs +9 -1
- package/dist/exports-nodejs.cjs.map +1 -1
- package/dist/exports-nodejs.d.ts +9 -4
- package/dist/exports-nodejs.js +9 -2
- package/dist/exports-nodejs.js.map +1 -1
- package/package.json +12 -8
package/dist/exports-browser.cjs
CHANGED
|
@@ -11,6 +11,7 @@ var runtimeRpc = require('@protobuf-ts/runtime-rpc');
|
|
|
11
11
|
var uuid = require('uuid');
|
|
12
12
|
var protoRpc = require('@streamr/proto-rpc');
|
|
13
13
|
var ipaddr = require('ipaddr.js');
|
|
14
|
+
var Comlink = require('comlink');
|
|
14
15
|
var websocket = require('websocket');
|
|
15
16
|
var autocertifierClient = require('@streamr/autocertifier-client');
|
|
16
17
|
var shuffle = require('lodash/shuffle');
|
|
@@ -21,6 +22,25 @@ var lruCache = require('lru-cache');
|
|
|
21
22
|
var cdnLocation = require('@streamr/cdn-location');
|
|
22
23
|
var Heap = require('heap');
|
|
23
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
|
+
|
|
24
44
|
const getPeerDistance = (nodeIdOrDataKeyRaw1, nodeIdOrDataKeyRaw2) => {
|
|
25
45
|
return KBucket.distance(nodeIdOrDataKeyRaw1, nodeIdOrDataKeyRaw2);
|
|
26
46
|
};
|
|
@@ -1800,7 +1820,7 @@ const createRandomConnectionId = () => {
|
|
|
1800
1820
|
return uuid.v4();
|
|
1801
1821
|
};
|
|
1802
1822
|
|
|
1803
|
-
const logger$
|
|
1823
|
+
const logger$D = new utils.Logger('ManagedConnection');
|
|
1804
1824
|
// ManagedConnection is a component used as a wrapper for IConnection after they have been successfully handshaked.
|
|
1805
1825
|
// Should only be used in the ConnectionManager.
|
|
1806
1826
|
class ManagedConnection extends eventemitter3.EventEmitter {
|
|
@@ -1830,7 +1850,7 @@ class ManagedConnection extends eventemitter3.EventEmitter {
|
|
|
1830
1850
|
this.remotePeerDescriptor = peerDescriptor;
|
|
1831
1851
|
}
|
|
1832
1852
|
onDisconnected(gracefulLeave) {
|
|
1833
|
-
logger$
|
|
1853
|
+
logger$D.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' onDisconnected() ' + gracefulLeave);
|
|
1834
1854
|
if (!this.replacedAsDuplicate) {
|
|
1835
1855
|
this.emit('disconnected', gracefulLeave);
|
|
1836
1856
|
}
|
|
@@ -1839,7 +1859,7 @@ class ManagedConnection extends eventemitter3.EventEmitter {
|
|
|
1839
1859
|
// TODO: Can this be removed if ManagedConnections can never be duplicates?
|
|
1840
1860
|
// Handle duplicates in the ConncetorFacade and no longer have PendingConnections in ConnectionManager
|
|
1841
1861
|
replaceAsDuplicate() {
|
|
1842
|
-
logger$
|
|
1862
|
+
logger$D.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
|
|
1843
1863
|
this.replacedAsDuplicate = true;
|
|
1844
1864
|
}
|
|
1845
1865
|
send(data) {
|
|
@@ -1986,10 +2006,10 @@ class RpcRemote {
|
|
|
1986
2006
|
}
|
|
1987
2007
|
}
|
|
1988
2008
|
|
|
1989
|
-
const logger$
|
|
2009
|
+
const logger$C = new utils.Logger('ConnectionLockRpcRemote');
|
|
1990
2010
|
class ConnectionLockRpcRemote extends RpcRemote {
|
|
1991
2011
|
async lockRequest(lockId) {
|
|
1992
|
-
logger$
|
|
2012
|
+
logger$C.trace(`Requesting locked connection to ${toNodeId(this.getPeerDescriptor())}`);
|
|
1993
2013
|
const request = {
|
|
1994
2014
|
lockId
|
|
1995
2015
|
};
|
|
@@ -1999,12 +2019,12 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
1999
2019
|
return res.accepted;
|
|
2000
2020
|
}
|
|
2001
2021
|
catch (err) {
|
|
2002
|
-
logger$
|
|
2022
|
+
logger$C.debug('Connection lock rejected', { err });
|
|
2003
2023
|
return false;
|
|
2004
2024
|
}
|
|
2005
2025
|
}
|
|
2006
2026
|
unlockRequest(lockId) {
|
|
2007
|
-
logger$
|
|
2027
|
+
logger$C.trace(`Requesting connection to be unlocked from ${toNodeId(this.getPeerDescriptor())}`);
|
|
2008
2028
|
const request = {
|
|
2009
2029
|
lockId
|
|
2010
2030
|
};
|
|
@@ -2012,11 +2032,11 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
2012
2032
|
notification: true
|
|
2013
2033
|
});
|
|
2014
2034
|
this.getClient().unlockRequest(request, options).catch((_e) => {
|
|
2015
|
-
logger$
|
|
2035
|
+
logger$C.trace('failed to send unlockRequest');
|
|
2016
2036
|
});
|
|
2017
2037
|
}
|
|
2018
2038
|
async gracefulDisconnect(disconnectMode) {
|
|
2019
|
-
logger$
|
|
2039
|
+
logger$C.trace(`Notifying a graceful disconnect to ${toNodeId(this.getPeerDescriptor())}`);
|
|
2020
2040
|
const request = {
|
|
2021
2041
|
disconnectMode
|
|
2022
2042
|
};
|
|
@@ -2028,7 +2048,7 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
2028
2048
|
await this.getClient().gracefulDisconnect(request, options);
|
|
2029
2049
|
}
|
|
2030
2050
|
async setPrivate(isPrivate) {
|
|
2031
|
-
logger$
|
|
2051
|
+
logger$C.trace(`Setting isPrivate: ${isPrivate} for ${toNodeId(this.getPeerDescriptor())}`);
|
|
2032
2052
|
const request = {
|
|
2033
2053
|
isPrivate
|
|
2034
2054
|
};
|
|
@@ -2040,7 +2060,7 @@ class ConnectionLockRpcRemote extends RpcRemote {
|
|
|
2040
2060
|
}
|
|
2041
2061
|
}
|
|
2042
2062
|
|
|
2043
|
-
const logger$
|
|
2063
|
+
const logger$B = new utils.Logger('ConnectionLockRpcLocal');
|
|
2044
2064
|
class ConnectionLockRpcLocal {
|
|
2045
2065
|
options;
|
|
2046
2066
|
constructor(options) {
|
|
@@ -2069,7 +2089,7 @@ class ConnectionLockRpcLocal {
|
|
|
2069
2089
|
}
|
|
2070
2090
|
async gracefulDisconnect(disconnectNotice, context) {
|
|
2071
2091
|
const senderPeerDescriptor = context.incomingSourceDescriptor;
|
|
2072
|
-
logger$
|
|
2092
|
+
logger$B.trace(getNodeIdOrUnknownFromPeerDescriptor(senderPeerDescriptor) + ' received gracefulDisconnect notice');
|
|
2073
2093
|
if (disconnectNotice.disconnectMode === DisconnectMode.LEAVING) {
|
|
2074
2094
|
await this.options.closeConnection(senderPeerDescriptor, true, 'graceful leave notified');
|
|
2075
2095
|
}
|
|
@@ -2120,7 +2140,7 @@ var NatType;
|
|
|
2120
2140
|
NatType["OPEN_INTERNET"] = "open_internet";
|
|
2121
2141
|
NatType["UNKNOWN"] = "unknown";
|
|
2122
2142
|
})(NatType || (NatType = {}));
|
|
2123
|
-
const logger$
|
|
2143
|
+
const logger$A = new utils.Logger('ConnectionManager');
|
|
2124
2144
|
var ConnectionManagerState;
|
|
2125
2145
|
(function (ConnectionManagerState) {
|
|
2126
2146
|
ConnectionManagerState["IDLE"] = "idle";
|
|
@@ -2170,7 +2190,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2170
2190
|
getLocalPeerDescriptor: () => this.getLocalPeerDescriptor(),
|
|
2171
2191
|
setPrivate: (id, isPrivate) => {
|
|
2172
2192
|
if (!this.options.allowIncomingPrivateConnections) {
|
|
2173
|
-
logger$
|
|
2193
|
+
logger$A.debug(`node ${id} attemted to set a connection as private, but it is not allowed`);
|
|
2174
2194
|
return;
|
|
2175
2195
|
}
|
|
2176
2196
|
if (isPrivate) {
|
|
@@ -2204,7 +2224,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2204
2224
|
const connection = endpoint.connection;
|
|
2205
2225
|
const nodeId = connection.getNodeId();
|
|
2206
2226
|
if (!this.locks.isLocked(nodeId) && !this.locks.isPrivate(nodeId) && Date.now() - connection.getLastUsedTimestamp() > maxIdleTime) {
|
|
2207
|
-
logger$
|
|
2227
|
+
logger$A.trace('disconnecting in timeout interval: ' + getNodeIdOrUnknownFromPeerDescriptor(connection.getPeerDescriptor()));
|
|
2208
2228
|
disconnectionCandidates.addContact(connection);
|
|
2209
2229
|
}
|
|
2210
2230
|
}
|
|
@@ -2212,7 +2232,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2212
2232
|
const disconnectables = disconnectionCandidates.getFurthestContacts(this.endpoints.size - maxConnections);
|
|
2213
2233
|
for (const disconnectable of disconnectables) {
|
|
2214
2234
|
const peerDescriptor = disconnectable.getPeerDescriptor();
|
|
2215
|
-
logger$
|
|
2235
|
+
logger$A.trace('garbageCollecting ' + toNodeId(peerDescriptor));
|
|
2216
2236
|
this.gracefullyDisconnectAsync(peerDescriptor, DisconnectMode.NORMAL).catch((_e) => { });
|
|
2217
2237
|
}
|
|
2218
2238
|
}
|
|
@@ -2221,11 +2241,11 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2221
2241
|
throw new CouldNotStart(`Cannot start already ${this.state} module`);
|
|
2222
2242
|
}
|
|
2223
2243
|
this.state = ConnectionManagerState.RUNNING;
|
|
2224
|
-
logger$
|
|
2244
|
+
logger$A.trace(`Starting ConnectionManager...`);
|
|
2225
2245
|
await this.connectorFacade.start((connection) => this.onNewConnection(connection), (nodeId) => this.hasConnection(nodeId), this);
|
|
2226
2246
|
// Garbage collection of connections
|
|
2227
2247
|
this.disconnectorIntervalRef = setInterval(() => {
|
|
2228
|
-
logger$
|
|
2248
|
+
logger$A.trace('disconnectorInterval');
|
|
2229
2249
|
const LAST_USED_LIMIT = 20000;
|
|
2230
2250
|
this.garbageCollectConnections(this.options.maxConnections ?? 80, LAST_USED_LIMIT);
|
|
2231
2251
|
}, 5000); // TODO use options option or named constant?
|
|
@@ -2235,7 +2255,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2235
2255
|
return;
|
|
2236
2256
|
}
|
|
2237
2257
|
this.state = ConnectionManagerState.STOPPING;
|
|
2238
|
-
logger$
|
|
2258
|
+
logger$A.trace(`Stopping ConnectionManager`);
|
|
2239
2259
|
if (this.disconnectorIntervalRef) {
|
|
2240
2260
|
clearInterval(this.disconnectorIntervalRef);
|
|
2241
2261
|
}
|
|
@@ -2245,23 +2265,23 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2245
2265
|
await this.gracefullyDisconnectAsync(endpoint.connection.getPeerDescriptor(), DisconnectMode.LEAVING);
|
|
2246
2266
|
}
|
|
2247
2267
|
catch (e) {
|
|
2248
|
-
logger$
|
|
2268
|
+
logger$A.error(e);
|
|
2249
2269
|
}
|
|
2250
2270
|
}
|
|
2251
2271
|
else {
|
|
2252
2272
|
const connection = endpoint.connection;
|
|
2253
|
-
logger$
|
|
2273
|
+
logger$A.trace('handshake of connection not completed, force-closing');
|
|
2254
2274
|
// TODO use options option or named constant?
|
|
2255
2275
|
const eventReceived = utils.waitForEvent(connection, 'disconnected', 2000);
|
|
2256
2276
|
// TODO should we have some handling for this floating promise?
|
|
2257
2277
|
connection.close(true);
|
|
2258
2278
|
try {
|
|
2259
2279
|
await eventReceived;
|
|
2260
|
-
logger$
|
|
2280
|
+
logger$A.trace('resolving after receiving disconnected event from non-handshaked connection');
|
|
2261
2281
|
}
|
|
2262
2282
|
catch (e) {
|
|
2263
2283
|
endpoint.buffer.reject();
|
|
2264
|
-
logger$
|
|
2284
|
+
logger$A.trace('force-closing non-handshaked connection timed out ' + e);
|
|
2265
2285
|
}
|
|
2266
2286
|
}
|
|
2267
2287
|
}));
|
|
@@ -2290,7 +2310,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2290
2310
|
throw new CannotConnectToSelf('Cannot send to self');
|
|
2291
2311
|
}
|
|
2292
2312
|
const nodeId = toNodeId(peerDescriptor);
|
|
2293
|
-
logger$
|
|
2313
|
+
logger$A.trace(`Sending message to: ${nodeId}`);
|
|
2294
2314
|
message = {
|
|
2295
2315
|
...message,
|
|
2296
2316
|
sourceDescriptor: this.getLocalPeerDescriptor()
|
|
@@ -2345,13 +2365,13 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2345
2365
|
}
|
|
2346
2366
|
handleMessage(message) {
|
|
2347
2367
|
const messageType = message.body.oneofKind;
|
|
2348
|
-
logger$
|
|
2368
|
+
logger$A.trace('Received message of type ' + messageType);
|
|
2349
2369
|
if (messageType !== 'rpcMessage') {
|
|
2350
|
-
logger$
|
|
2370
|
+
logger$A.trace('Filtered out non-RPC message of type ' + messageType);
|
|
2351
2371
|
return;
|
|
2352
2372
|
}
|
|
2353
2373
|
if (this.duplicateMessageDetector.isMostLikelyDuplicate(message.messageId)) {
|
|
2354
|
-
logger$
|
|
2374
|
+
logger$A.trace('handleMessage filtered duplicate ' + toNodeId(message.sourceDescriptor)
|
|
2355
2375
|
+ ' ' + message.serviceId + ' ' + message.messageId);
|
|
2356
2376
|
return;
|
|
2357
2377
|
}
|
|
@@ -2360,7 +2380,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2360
2380
|
this.rpcCommunicator?.handleMessageFromPeer(message);
|
|
2361
2381
|
}
|
|
2362
2382
|
else {
|
|
2363
|
-
logger$
|
|
2383
|
+
logger$A.trace('emit "message" ' + toNodeId(message.sourceDescriptor)
|
|
2364
2384
|
+ ' ' + message.serviceId + ' ' + message.messageId);
|
|
2365
2385
|
this.emit('message', message);
|
|
2366
2386
|
}
|
|
@@ -2377,7 +2397,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2377
2397
|
}
|
|
2378
2398
|
catch (e) {
|
|
2379
2399
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
2380
|
-
logger$
|
|
2400
|
+
logger$A.debug(`Parsing incoming data into Message failed: ${e}`);
|
|
2381
2401
|
return;
|
|
2382
2402
|
}
|
|
2383
2403
|
message.sourceDescriptor = peerDescriptor;
|
|
@@ -2386,7 +2406,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2386
2406
|
}
|
|
2387
2407
|
catch (e) {
|
|
2388
2408
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
2389
|
-
logger$
|
|
2409
|
+
logger$A.debug(`Handling incoming data failed: ${e}`);
|
|
2390
2410
|
}
|
|
2391
2411
|
}
|
|
2392
2412
|
onConnected(peerDescriptor, connection) {
|
|
@@ -2399,7 +2419,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2399
2419
|
const pendingConnection = endpoint.connection;
|
|
2400
2420
|
const buffer = outputBuffer.getBuffer();
|
|
2401
2421
|
while (buffer.length > 0) {
|
|
2402
|
-
logger$
|
|
2422
|
+
logger$A.trace('emptying buffer');
|
|
2403
2423
|
managedConnection.send(buffer.shift());
|
|
2404
2424
|
}
|
|
2405
2425
|
outputBuffer.resolve();
|
|
@@ -2416,7 +2436,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2416
2436
|
}
|
|
2417
2437
|
onDisconnected(peerDescriptor, gracefulLeave) {
|
|
2418
2438
|
const nodeId = toNodeId(peerDescriptor);
|
|
2419
|
-
logger$
|
|
2439
|
+
logger$A.trace(nodeId + ' onDisconnected() gracefulLeave: ' + gracefulLeave);
|
|
2420
2440
|
const endpoint = this.endpoints.get(nodeId);
|
|
2421
2441
|
if (endpoint) {
|
|
2422
2442
|
this.locks.clearAllLocks(nodeId);
|
|
@@ -2424,7 +2444,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2424
2444
|
endpoint.buffer.reject();
|
|
2425
2445
|
}
|
|
2426
2446
|
this.endpoints.delete(nodeId);
|
|
2427
|
-
logger$
|
|
2447
|
+
logger$A.trace(nodeId + ' deleted connection in onDisconnected() gracefulLeave: ' + gracefulLeave);
|
|
2428
2448
|
this.emit('disconnected', peerDescriptor, gracefulLeave);
|
|
2429
2449
|
this.onConnectionCountChange();
|
|
2430
2450
|
}
|
|
@@ -2433,7 +2453,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2433
2453
|
if (this.state === ConnectionManagerState.STOPPED) {
|
|
2434
2454
|
return false;
|
|
2435
2455
|
}
|
|
2436
|
-
logger$
|
|
2456
|
+
logger$A.trace('onNewConnection()');
|
|
2437
2457
|
if (!this.acceptNewConnection(connection)) {
|
|
2438
2458
|
return false;
|
|
2439
2459
|
}
|
|
@@ -2443,7 +2463,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2443
2463
|
}
|
|
2444
2464
|
acceptNewConnection(newConnection) {
|
|
2445
2465
|
const nodeId = toNodeId(newConnection.getPeerDescriptor());
|
|
2446
|
-
logger$
|
|
2466
|
+
logger$A.trace(nodeId + ' acceptNewConnection()');
|
|
2447
2467
|
if (this.endpoints.has(nodeId)) {
|
|
2448
2468
|
if (getOfferer(toNodeId(this.getLocalPeerDescriptor()), nodeId) === 'remote') {
|
|
2449
2469
|
let buffer;
|
|
@@ -2452,14 +2472,14 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2452
2472
|
// Could be related to WS client connections not realizing that they have been disconnected.
|
|
2453
2473
|
// Makes refactoring duplicate connection handling to the connectors very difficult.
|
|
2454
2474
|
if (this.endpoints.get(nodeId).connected) {
|
|
2455
|
-
logger$
|
|
2475
|
+
logger$A.debug('replacing connected connection', { nodeId });
|
|
2456
2476
|
buffer = new OutputBuffer();
|
|
2457
2477
|
}
|
|
2458
2478
|
else {
|
|
2459
2479
|
buffer = endpoint.buffer;
|
|
2460
2480
|
}
|
|
2461
2481
|
const oldConnection = endpoint.connection;
|
|
2462
|
-
logger$
|
|
2482
|
+
logger$A.trace('replaced: ' + nodeId);
|
|
2463
2483
|
oldConnection.replaceAsDuplicate();
|
|
2464
2484
|
this.endpoints.set(nodeId, { connected: false, connection: newConnection, buffer: buffer });
|
|
2465
2485
|
return true;
|
|
@@ -2468,7 +2488,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2468
2488
|
return false;
|
|
2469
2489
|
}
|
|
2470
2490
|
}
|
|
2471
|
-
logger$
|
|
2491
|
+
logger$A.trace(nodeId + ' added to connections at acceptNewConnection');
|
|
2472
2492
|
this.endpoints.set(nodeId, {
|
|
2473
2493
|
connected: false,
|
|
2474
2494
|
buffer: new OutputBuffer(),
|
|
@@ -2478,14 +2498,14 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2478
2498
|
}
|
|
2479
2499
|
async closeConnection(peerDescriptor, gracefulLeave, reason) {
|
|
2480
2500
|
const nodeId = toNodeId(peerDescriptor);
|
|
2481
|
-
logger$
|
|
2501
|
+
logger$A.trace(nodeId + ' ' + 'closeConnection() ' + reason);
|
|
2482
2502
|
this.locks.clearAllLocks(nodeId);
|
|
2483
2503
|
if (this.endpoints.has(nodeId)) {
|
|
2484
2504
|
const connectionToClose = this.endpoints.get(nodeId).connection;
|
|
2485
2505
|
await connectionToClose.close(gracefulLeave);
|
|
2486
2506
|
}
|
|
2487
2507
|
else {
|
|
2488
|
-
logger$
|
|
2508
|
+
logger$A.trace(nodeId + ' ' + 'closeConnection() this.endpoints did not have the id');
|
|
2489
2509
|
this.emit('disconnected', peerDescriptor, false);
|
|
2490
2510
|
}
|
|
2491
2511
|
}
|
|
@@ -2497,8 +2517,8 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2497
2517
|
const rpcRemote = new ConnectionLockRpcRemote(this.getLocalPeerDescriptor(), targetDescriptor, this.rpcCommunicator, ConnectionLockRpcClient);
|
|
2498
2518
|
this.locks.addLocalLocked(nodeId, lockId);
|
|
2499
2519
|
rpcRemote.lockRequest(lockId)
|
|
2500
|
-
.then((_accepted) => logger$
|
|
2501
|
-
.catch((err) => { logger$
|
|
2520
|
+
.then((_accepted) => logger$A.trace('LockRequest successful'))
|
|
2521
|
+
.catch((err) => { logger$A.debug(err); });
|
|
2502
2522
|
}
|
|
2503
2523
|
unlockConnection(targetDescriptor, lockId) {
|
|
2504
2524
|
if (this.state === ConnectionManagerState.STOPPED || areEqualPeerDescriptors(targetDescriptor, this.getLocalPeerDescriptor())) {
|
|
@@ -2550,7 +2570,7 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2550
2570
|
async gracefullyDisconnectAsync(targetDescriptor, disconnectMode) {
|
|
2551
2571
|
const endpoint = this.endpoints.get(toNodeId(targetDescriptor));
|
|
2552
2572
|
if (!endpoint) {
|
|
2553
|
-
logger$
|
|
2573
|
+
logger$A.debug('gracefullyDisconnectedAsync() tried on a non-existing connection');
|
|
2554
2574
|
return;
|
|
2555
2575
|
}
|
|
2556
2576
|
if (endpoint.connected) {
|
|
@@ -2559,15 +2579,15 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2559
2579
|
// TODO use options option or named constant?
|
|
2560
2580
|
// eslint-disable-next-line promise/catch-or-return
|
|
2561
2581
|
utils.waitForEvent(connection, 'disconnected', 2000).then(() => {
|
|
2562
|
-
logger$
|
|
2582
|
+
logger$A.trace('disconnected event received in gracefullyDisconnectAsync()');
|
|
2563
2583
|
})
|
|
2564
2584
|
.catch((e) => {
|
|
2565
|
-
logger$
|
|
2585
|
+
logger$A.trace('force-closing connection after timeout ' + e);
|
|
2566
2586
|
// TODO should we have some handling for this floating promise?
|
|
2567
2587
|
connection.close(true);
|
|
2568
2588
|
})
|
|
2569
2589
|
.finally(() => {
|
|
2570
|
-
logger$
|
|
2590
|
+
logger$A.trace('resolving after receiving disconnected event');
|
|
2571
2591
|
resolve();
|
|
2572
2592
|
});
|
|
2573
2593
|
});
|
|
@@ -2582,13 +2602,13 @@ class ConnectionManager extends eventemitter3.EventEmitter {
|
|
|
2582
2602
|
}
|
|
2583
2603
|
async doGracefullyDisconnectAsync(targetDescriptor, disconnectMode) {
|
|
2584
2604
|
const nodeId = toNodeId(targetDescriptor);
|
|
2585
|
-
logger$
|
|
2605
|
+
logger$A.trace(nodeId + ' gracefullyDisconnectAsync()');
|
|
2586
2606
|
const rpcRemote = new ConnectionLockRpcRemote(this.getLocalPeerDescriptor(), targetDescriptor, this.rpcCommunicator, ConnectionLockRpcClient);
|
|
2587
2607
|
try {
|
|
2588
2608
|
await rpcRemote.gracefulDisconnect(disconnectMode);
|
|
2589
2609
|
}
|
|
2590
2610
|
catch (ex) {
|
|
2591
|
-
logger$
|
|
2611
|
+
logger$A.trace(nodeId + ' remote.gracefulDisconnect() failed' + ex);
|
|
2592
2612
|
}
|
|
2593
2613
|
}
|
|
2594
2614
|
getConnections() {
|
|
@@ -2655,7 +2675,7 @@ function protoToString(protoObj, objectType) {
|
|
|
2655
2675
|
return ret;
|
|
2656
2676
|
}
|
|
2657
2677
|
|
|
2658
|
-
const logger$
|
|
2678
|
+
const logger$z = new utils.Logger('SimulatorConnection');
|
|
2659
2679
|
class SimulatorConnection extends Connection {
|
|
2660
2680
|
stopped = false;
|
|
2661
2681
|
localPeerDescriptor;
|
|
@@ -2677,46 +2697,46 @@ class SimulatorConnection extends Connection {
|
|
|
2677
2697
|
this.doDisconnect = this.doDisconnect.bind(this);
|
|
2678
2698
|
}
|
|
2679
2699
|
send(data) {
|
|
2680
|
-
logger$
|
|
2700
|
+
logger$z.trace('send()');
|
|
2681
2701
|
if (!this.stopped) {
|
|
2682
2702
|
this.simulator.send(this, data);
|
|
2683
2703
|
}
|
|
2684
2704
|
else {
|
|
2685
2705
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2686
2706
|
const targetNodeId = toNodeId(this.targetPeerDescriptor);
|
|
2687
|
-
logger$
|
|
2707
|
+
logger$z.error(localNodeId + ', ' + targetNodeId + 'tried to send() on a stopped connection');
|
|
2688
2708
|
}
|
|
2689
2709
|
}
|
|
2690
2710
|
async close(gracefulLeave) {
|
|
2691
2711
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2692
2712
|
const targetNodeId = toNodeId(this.targetPeerDescriptor);
|
|
2693
|
-
logger$
|
|
2713
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close()');
|
|
2694
2714
|
if (!this.stopped) {
|
|
2695
|
-
logger$
|
|
2715
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() not stopped');
|
|
2696
2716
|
this.stopped = true;
|
|
2697
2717
|
try {
|
|
2698
|
-
logger$
|
|
2718
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() calling simulator.disconnect()');
|
|
2699
2719
|
this.simulator.close(this);
|
|
2700
|
-
logger$
|
|
2720
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() simulator.disconnect returned');
|
|
2701
2721
|
}
|
|
2702
2722
|
catch (e) {
|
|
2703
|
-
logger$
|
|
2723
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + 'close aborted' + e);
|
|
2704
2724
|
}
|
|
2705
2725
|
finally {
|
|
2706
|
-
logger$
|
|
2726
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' calling this.doDisconnect');
|
|
2707
2727
|
this.doDisconnect(gracefulLeave);
|
|
2708
2728
|
}
|
|
2709
2729
|
}
|
|
2710
2730
|
else {
|
|
2711
|
-
logger$
|
|
2731
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' close() tried to close a stopped connection');
|
|
2712
2732
|
}
|
|
2713
2733
|
}
|
|
2714
2734
|
connect() {
|
|
2715
2735
|
if (!this.stopped) {
|
|
2716
|
-
logger$
|
|
2736
|
+
logger$z.trace('connect() called');
|
|
2717
2737
|
this.simulator.connect(this, this.targetPeerDescriptor, (error) => {
|
|
2718
2738
|
if (error !== undefined) {
|
|
2719
|
-
logger$
|
|
2739
|
+
logger$z.trace(error);
|
|
2720
2740
|
this.doDisconnect(false);
|
|
2721
2741
|
}
|
|
2722
2742
|
else {
|
|
@@ -2725,46 +2745,46 @@ class SimulatorConnection extends Connection {
|
|
|
2725
2745
|
});
|
|
2726
2746
|
}
|
|
2727
2747
|
else {
|
|
2728
|
-
logger$
|
|
2748
|
+
logger$z.trace('tried to connect() a stopped connection');
|
|
2729
2749
|
}
|
|
2730
2750
|
}
|
|
2731
2751
|
handleIncomingData(data) {
|
|
2732
2752
|
if (!this.stopped) {
|
|
2733
|
-
logger$
|
|
2753
|
+
logger$z.trace('handleIncomingData() ' + protoToString(Message.fromBinary(data), Message));
|
|
2734
2754
|
this.emit('data', data);
|
|
2735
2755
|
}
|
|
2736
2756
|
else {
|
|
2737
|
-
logger$
|
|
2757
|
+
logger$z.trace('tried to call handleIncomingData() a stopped connection');
|
|
2738
2758
|
}
|
|
2739
2759
|
}
|
|
2740
2760
|
handleIncomingDisconnection() {
|
|
2741
2761
|
if (!this.stopped) {
|
|
2742
2762
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2743
|
-
logger$
|
|
2763
|
+
logger$z.trace(localNodeId + ' handleIncomingDisconnection()');
|
|
2744
2764
|
this.stopped = true;
|
|
2745
2765
|
this.doDisconnect(false);
|
|
2746
2766
|
}
|
|
2747
2767
|
else {
|
|
2748
|
-
logger$
|
|
2768
|
+
logger$z.trace('tried to call handleIncomingDisconnection() a stopped connection');
|
|
2749
2769
|
}
|
|
2750
2770
|
}
|
|
2751
2771
|
destroy() {
|
|
2752
2772
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2753
2773
|
if (!this.stopped) {
|
|
2754
|
-
logger$
|
|
2774
|
+
logger$z.trace(localNodeId + ' destroy()');
|
|
2755
2775
|
this.removeAllListeners();
|
|
2756
2776
|
this.close(false).catch((_e) => { });
|
|
2757
2777
|
}
|
|
2758
2778
|
else {
|
|
2759
|
-
logger$
|
|
2779
|
+
logger$z.trace(localNodeId + ' tried to call destroy() a stopped connection');
|
|
2760
2780
|
}
|
|
2761
2781
|
}
|
|
2762
2782
|
doDisconnect(gracefulLeave) {
|
|
2763
2783
|
const localNodeId = toNodeId(this.localPeerDescriptor);
|
|
2764
2784
|
const targetNodeId = toNodeId(this.targetPeerDescriptor);
|
|
2765
|
-
logger$
|
|
2785
|
+
logger$z.trace(localNodeId + ' doDisconnect()');
|
|
2766
2786
|
this.stopped = true;
|
|
2767
|
-
logger$
|
|
2787
|
+
logger$z.trace(localNodeId + ', ' + targetNodeId + ' doDisconnect emitting');
|
|
2768
2788
|
this.emit('disconnected', gracefulLeave);
|
|
2769
2789
|
}
|
|
2770
2790
|
}
|
|
@@ -2802,9 +2822,9 @@ const parseVersion = (version) => {
|
|
|
2802
2822
|
}
|
|
2803
2823
|
};
|
|
2804
2824
|
|
|
2805
|
-
var version = "103.
|
|
2825
|
+
var version = "103.6.0-rc.0";
|
|
2806
2826
|
|
|
2807
|
-
const logger$
|
|
2827
|
+
const logger$y = new utils.Logger('Handshaker');
|
|
2808
2828
|
// Optimally the Outgoing and Incoming Handshakers could be their own separate classes
|
|
2809
2829
|
// However, in cases where the PeerDescriptor of the other end of the connection can be known
|
|
2810
2830
|
// only after a HandshakeRequest a base Handshaker class is needed as the IncomingHandshaker currently
|
|
@@ -2826,7 +2846,7 @@ const createOutgoingHandshaker = (localPeerDescriptor, pendingConnection, connec
|
|
|
2826
2846
|
}
|
|
2827
2847
|
};
|
|
2828
2848
|
const handshakeCompletedListener = (peerDescriptor) => {
|
|
2829
|
-
logger$
|
|
2849
|
+
logger$y.trace('handshake completed for outgoing connection, ' + toNodeId(peerDescriptor));
|
|
2830
2850
|
pendingConnection.onHandshakeCompleted(connection);
|
|
2831
2851
|
stopHandshaker();
|
|
2832
2852
|
};
|
|
@@ -2926,12 +2946,12 @@ class Handshaker extends eventemitter3.EventEmitter {
|
|
|
2926
2946
|
try {
|
|
2927
2947
|
const message = Message.fromBinary(data);
|
|
2928
2948
|
if (message.body.oneofKind === 'handshakeRequest') {
|
|
2929
|
-
logger$
|
|
2949
|
+
logger$y.trace('handshake request received');
|
|
2930
2950
|
const handshake = message.body.handshakeRequest;
|
|
2931
2951
|
this.emit('handshakeRequest', handshake.sourcePeerDescriptor, handshake.protocolVersion, handshake.targetPeerDescriptor);
|
|
2932
2952
|
}
|
|
2933
2953
|
if (message.body.oneofKind === 'handshakeResponse') {
|
|
2934
|
-
logger$
|
|
2954
|
+
logger$y.trace('handshake response received');
|
|
2935
2955
|
const handshake = message.body.handshakeResponse;
|
|
2936
2956
|
const error = !isMaybeSupportedProtocolVersion(handshake.protocolVersion)
|
|
2937
2957
|
? HandshakeError.UNSUPPORTED_PROTOCOL_VERSION : handshake.error;
|
|
@@ -2944,18 +2964,18 @@ class Handshaker extends eventemitter3.EventEmitter {
|
|
|
2944
2964
|
}
|
|
2945
2965
|
}
|
|
2946
2966
|
catch (err) {
|
|
2947
|
-
logger$
|
|
2967
|
+
logger$y.debug('error while parsing handshake message', err);
|
|
2948
2968
|
}
|
|
2949
2969
|
}
|
|
2950
2970
|
sendHandshakeRequest(remotePeerDescriptor) {
|
|
2951
2971
|
const msg = createHandshakeRequest(this.localPeerDescriptor, remotePeerDescriptor);
|
|
2952
2972
|
this.connection.send(Message.toBinary(msg));
|
|
2953
|
-
logger$
|
|
2973
|
+
logger$y.trace('handshake request sent');
|
|
2954
2974
|
}
|
|
2955
2975
|
sendHandshakeResponse(error) {
|
|
2956
2976
|
const msg = createHandshakeResponse(this.localPeerDescriptor, error);
|
|
2957
2977
|
this.connection.send(Message.toBinary(msg));
|
|
2958
|
-
logger$
|
|
2978
|
+
logger$y.trace('handshake response sent');
|
|
2959
2979
|
}
|
|
2960
2980
|
stop() {
|
|
2961
2981
|
this.connection.off('data', this.onDataListener);
|
|
@@ -2963,7 +2983,7 @@ class Handshaker extends eventemitter3.EventEmitter {
|
|
|
2963
2983
|
}
|
|
2964
2984
|
}
|
|
2965
2985
|
|
|
2966
|
-
const logger$
|
|
2986
|
+
const logger$x = new utils.Logger('PendingConnection');
|
|
2967
2987
|
// PendingConnection is used as a reference to a connection that should be opened and handshaked to a given PeerDescriptor
|
|
2968
2988
|
// It does not hold a connection internally. The public method onHandshakedCompleted should be called once a connection for the
|
|
2969
2989
|
// remotePeerDescriptor is opened and handshaked successfully.
|
|
@@ -2981,7 +3001,7 @@ class PendingConnection extends eventemitter3.EventEmitter {
|
|
|
2981
3001
|
}, timeout, this.connectingAbortController.signal);
|
|
2982
3002
|
}
|
|
2983
3003
|
replaceAsDuplicate() {
|
|
2984
|
-
logger$
|
|
3004
|
+
logger$x.trace(getNodeIdOrUnknownFromPeerDescriptor(this.remotePeerDescriptor) + ' replaceAsDuplicate');
|
|
2985
3005
|
this.replacedAsDuplicate = true;
|
|
2986
3006
|
}
|
|
2987
3007
|
onHandshakeCompleted(connection) {
|
|
@@ -3012,7 +3032,7 @@ class PendingConnection extends eventemitter3.EventEmitter {
|
|
|
3012
3032
|
}
|
|
3013
3033
|
}
|
|
3014
3034
|
|
|
3015
|
-
const logger$
|
|
3035
|
+
const logger$w = new utils.Logger('SimulatorConnector');
|
|
3016
3036
|
class SimulatorConnector {
|
|
3017
3037
|
connectingConnections = new Map();
|
|
3018
3038
|
stopped = false;
|
|
@@ -3025,7 +3045,7 @@ class SimulatorConnector {
|
|
|
3025
3045
|
this.onNewConnection = onNewConnection;
|
|
3026
3046
|
}
|
|
3027
3047
|
connect(targetPeerDescriptor) {
|
|
3028
|
-
logger$
|
|
3048
|
+
logger$w.trace('connect() ' + toNodeId(targetPeerDescriptor));
|
|
3029
3049
|
const nodeId = toNodeId(targetPeerDescriptor);
|
|
3030
3050
|
const existingConnection = this.connectingConnections.get(nodeId);
|
|
3031
3051
|
if (existingConnection) {
|
|
@@ -3054,18 +3074,18 @@ class SimulatorConnector {
|
|
|
3054
3074
|
// connection is incoming, so remotePeerDescriptor is localPeerDescriptor
|
|
3055
3075
|
const remotePeerDescriptor = sourceConnection.localPeerDescriptor;
|
|
3056
3076
|
const remoteNodeId = toNodeId(sourceConnection.localPeerDescriptor);
|
|
3057
|
-
logger$
|
|
3077
|
+
logger$w.trace(remoteNodeId + ' incoming connection, stopped: ' + this.stopped);
|
|
3058
3078
|
if (this.stopped) {
|
|
3059
3079
|
return;
|
|
3060
3080
|
}
|
|
3061
3081
|
const connection = new SimulatorConnection(this.localPeerDescriptor, remotePeerDescriptor, exports.ConnectionType.SIMULATOR_SERVER, this.simulator);
|
|
3062
3082
|
const pendingConnection = new PendingConnection(remotePeerDescriptor);
|
|
3063
3083
|
const handshaker = createIncomingHandshaker(this.localPeerDescriptor, pendingConnection, connection);
|
|
3064
|
-
logger$
|
|
3084
|
+
logger$w.trace('connected');
|
|
3065
3085
|
handshaker.once('handshakeRequest', () => {
|
|
3066
|
-
logger$
|
|
3086
|
+
logger$w.trace(remoteNodeId + ' incoming handshake request');
|
|
3067
3087
|
if (this.onNewConnection(pendingConnection)) {
|
|
3068
|
-
logger$
|
|
3088
|
+
logger$w.trace(remoteNodeId + ' calling acceptHandshake');
|
|
3069
3089
|
acceptHandshake(handshaker, pendingConnection, connection);
|
|
3070
3090
|
}
|
|
3071
3091
|
else {
|
|
@@ -3105,6 +3125,8 @@ class ListeningRpcCommunicator extends RoutingRpcCommunicator {
|
|
|
3105
3125
|
}
|
|
3106
3126
|
}
|
|
3107
3127
|
|
|
3128
|
+
const isWorkerEnvironment = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
|
|
3129
|
+
|
|
3108
3130
|
var RtcDescription;
|
|
3109
3131
|
(function (RtcDescription) {
|
|
3110
3132
|
RtcDescription["OFFER"] = "offer";
|
|
@@ -3119,8 +3141,8 @@ var DisconnectedRtcPeerConnectionStateEnum;
|
|
|
3119
3141
|
DisconnectedRtcPeerConnectionStateEnum["FAILED"] = "failed";
|
|
3120
3142
|
DisconnectedRtcPeerConnectionStateEnum["CLOSED"] = "closed";
|
|
3121
3143
|
})(DisconnectedRtcPeerConnectionStateEnum || (DisconnectedRtcPeerConnectionStateEnum = {}));
|
|
3122
|
-
const logger$
|
|
3123
|
-
class
|
|
3144
|
+
const logger$v = new utils.Logger('DirectWebrtcConnection (browser)');
|
|
3145
|
+
class DirectWebrtcConnection extends eventemitter3.EventEmitter {
|
|
3124
3146
|
connectionId;
|
|
3125
3147
|
connectionType = exports.ConnectionType.WEBRTC;
|
|
3126
3148
|
// We need to keep track of connection state ourselves because
|
|
@@ -3158,7 +3180,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3158
3180
|
}
|
|
3159
3181
|
};
|
|
3160
3182
|
this.peerConnection.onicegatheringstatechange = () => {
|
|
3161
|
-
logger$
|
|
3183
|
+
logger$v.trace(`conn.onGatheringStateChange: ${this.peerConnection?.iceGatheringState}`);
|
|
3162
3184
|
};
|
|
3163
3185
|
this.peerConnection.onconnectionstatechange = () => this.onStateChange();
|
|
3164
3186
|
if (isOffering) {
|
|
@@ -3169,7 +3191,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3169
3191
|
await this.peerConnection.setLocalDescription();
|
|
3170
3192
|
}
|
|
3171
3193
|
catch (err) {
|
|
3172
|
-
logger$
|
|
3194
|
+
logger$v.warn('Failed to set local description', { err });
|
|
3173
3195
|
}
|
|
3174
3196
|
if (this.peerConnection.localDescription !== null) {
|
|
3175
3197
|
this.emit('localDescription', this.peerConnection.localDescription?.sdp, this.peerConnection.localDescription?.type);
|
|
@@ -3197,14 +3219,14 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3197
3219
|
clearTimeout(this.earlyTimeout);
|
|
3198
3220
|
}
|
|
3199
3221
|
catch (err) {
|
|
3200
|
-
logger$
|
|
3222
|
+
logger$v.warn('Failed to set remote description', { err });
|
|
3201
3223
|
}
|
|
3202
3224
|
if ((type.toLowerCase() === RtcDescription.OFFER) && (this.peerConnection !== undefined)) {
|
|
3203
3225
|
try {
|
|
3204
3226
|
await this.peerConnection.setLocalDescription();
|
|
3205
3227
|
}
|
|
3206
3228
|
catch (err) {
|
|
3207
|
-
logger$
|
|
3229
|
+
logger$v.warn('Failed to set local description', { err });
|
|
3208
3230
|
}
|
|
3209
3231
|
if (this.peerConnection.localDescription !== null) {
|
|
3210
3232
|
this.emit('localDescription', this.peerConnection.localDescription.sdp, this.peerConnection.localDescription.type);
|
|
@@ -3214,7 +3236,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3214
3236
|
addRemoteCandidate(candidate, mid) {
|
|
3215
3237
|
this.peerConnection?.addIceCandidate({ candidate: candidate, sdpMid: mid })
|
|
3216
3238
|
.catch((err) => {
|
|
3217
|
-
logger$
|
|
3239
|
+
logger$v.warn('Failed to add ICE candidate', { err });
|
|
3218
3240
|
});
|
|
3219
3241
|
}
|
|
3220
3242
|
isOpen() {
|
|
@@ -3237,7 +3259,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3237
3259
|
this.dataChannel.close();
|
|
3238
3260
|
}
|
|
3239
3261
|
catch (err) {
|
|
3240
|
-
logger$
|
|
3262
|
+
logger$v.warn('Failed to close data channel', { err });
|
|
3241
3263
|
}
|
|
3242
3264
|
}
|
|
3243
3265
|
this.dataChannel = undefined;
|
|
@@ -3246,7 +3268,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3246
3268
|
this.peerConnection.close();
|
|
3247
3269
|
}
|
|
3248
3270
|
catch (err) {
|
|
3249
|
-
logger$
|
|
3271
|
+
logger$v.warn('Failed to close connection', { err });
|
|
3250
3272
|
}
|
|
3251
3273
|
}
|
|
3252
3274
|
this.peerConnection = undefined;
|
|
@@ -3266,7 +3288,7 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3266
3288
|
}
|
|
3267
3289
|
}
|
|
3268
3290
|
else {
|
|
3269
|
-
logger$
|
|
3291
|
+
logger$v.warn('Tried to send on a connection with last state ' + this.lastState);
|
|
3270
3292
|
}
|
|
3271
3293
|
}
|
|
3272
3294
|
setupDataChannel(dataChannel) {
|
|
@@ -3274,22 +3296,22 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3274
3296
|
this.dataChannel.binaryType = 'arraybuffer';
|
|
3275
3297
|
this.dataChannel.bufferedAmountLowThreshold = this.bufferThresholdLow;
|
|
3276
3298
|
dataChannel.onopen = () => {
|
|
3277
|
-
logger$
|
|
3299
|
+
logger$v.trace('dc.onOpen');
|
|
3278
3300
|
this.onDataChannelOpen();
|
|
3279
3301
|
};
|
|
3280
3302
|
dataChannel.onclose = () => {
|
|
3281
|
-
logger$
|
|
3303
|
+
logger$v.trace('dc.onClosed');
|
|
3282
3304
|
this.doClose(false);
|
|
3283
3305
|
};
|
|
3284
3306
|
dataChannel.onerror = (err) => {
|
|
3285
|
-
logger$
|
|
3307
|
+
logger$v.warn('Data channel error', { err });
|
|
3286
3308
|
};
|
|
3287
3309
|
dataChannel.onmessage = (msg) => {
|
|
3288
|
-
logger$
|
|
3310
|
+
logger$v.trace('dc.onmessage');
|
|
3289
3311
|
this.emit('data', new Uint8Array(msg.data));
|
|
3290
3312
|
};
|
|
3291
3313
|
dataChannel.onbufferedamountlow = () => {
|
|
3292
|
-
logger$
|
|
3314
|
+
logger$v.trace('dc.onBufferedAmountLow');
|
|
3293
3315
|
while (this.messageQueue.length > 0 && this.dataChannel.bufferedAmount < this.bufferThresholdHigh) {
|
|
3294
3316
|
const data = this.messageQueue.shift();
|
|
3295
3317
|
this.dataChannel.send(data);
|
|
@@ -3328,6 +3350,431 @@ class WebrtcConnection extends eventemitter3.EventEmitter {
|
|
|
3328
3350
|
}
|
|
3329
3351
|
}
|
|
3330
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
|
+
|
|
3331
3778
|
const logger$t = new utils.Logger('WebrtcConnectorRpcRemote');
|
|
3332
3779
|
class WebrtcConnectorRpcRemote extends RpcRemote {
|
|
3333
3780
|
requestConnection() {
|
|
@@ -7630,6 +8077,7 @@ exports.areEqualPeerDescriptors = areEqualPeerDescriptors;
|
|
|
7630
8077
|
exports.createOutgoingHandshaker = createOutgoingHandshaker;
|
|
7631
8078
|
exports.getRandomRegion = getRandomRegion;
|
|
7632
8079
|
exports.getRegionDelayMatrix = getRegionDelayMatrix;
|
|
8080
|
+
exports.installWebrtcBridge = installWebrtcBridge;
|
|
7633
8081
|
exports.randomDhtAddress = randomDhtAddress;
|
|
7634
8082
|
exports.toDhtAddress = toDhtAddress;
|
|
7635
8083
|
exports.toDhtAddressRaw = toDhtAddressRaw;
|