werift 0.24.0 → 0.24.2
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/lib/common/src/binary.js +2 -3
- package/lib/common/src/binary.js.map +1 -1
- package/lib/dtls/src/context/transport.d.ts +2 -2
- package/lib/dtls/src/context/transport.js +2 -2
- package/lib/dtls/src/context/transport.js.map +1 -1
- package/lib/dtls/src/socket.d.ts +3 -2
- package/lib/dtls/src/socket.js +2 -2
- package/lib/dtls/src/socket.js.map +1 -1
- package/lib/ice/src/ice.d.ts +20 -0
- package/lib/ice/src/ice.js +263 -66
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +22 -1
- package/lib/ice/src/iceBase.js +30 -1
- package/lib/ice/src/iceBase.js.map +1 -1
- package/lib/ice/src/internal/selectAddresses.d.ts +9 -0
- package/lib/ice/src/internal/selectAddresses.js +40 -0
- package/lib/ice/src/internal/selectAddresses.js.map +1 -0
- package/lib/ice/src/stun/protocol.d.ts +2 -2
- package/lib/ice/src/stun/protocol.js +17 -3
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/tcpProtocol.d.ts +2 -2
- package/lib/ice/src/stun/tcpProtocol.js +13 -3
- package/lib/ice/src/stun/tcpProtocol.js.map +1 -1
- package/lib/ice/src/stun/transaction.d.ts +41 -4
- package/lib/ice/src/stun/transaction.js +193 -24
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/turn/protocol.d.ts +3 -3
- package/lib/ice/src/turn/protocol.js +31 -6
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +27 -1
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/ice/src/utils.js +2 -31
- package/lib/ice/src/utils.js.map +1 -1
- package/lib/ice-server/src/stun/attributes.js +9 -47
- package/lib/ice-server/src/stun/attributes.js.map +1 -1
- package/lib/ice-server/src/stun/ip.d.ts +12 -0
- package/lib/ice-server/src/stun/ip.js +88 -0
- package/lib/ice-server/src/stun/ip.js.map +1 -0
- package/lib/ice-server/src/stun/message.js +8 -0
- package/lib/ice-server/src/stun/message.js.map +1 -1
- package/lib/index.mjs +652 -234
- package/lib/nonstandard/index.mjs +121 -54
- package/lib/rtp/src/codec/av1.js +11 -5
- package/lib/rtp/src/codec/av1.js.map +1 -1
- package/lib/rtp/src/codec/leb128.d.ts +5 -0
- package/lib/rtp/src/codec/leb128.js +24 -0
- package/lib/rtp/src/codec/leb128.js.map +1 -0
- package/lib/rtp/src/rtp/rtx.js +4 -6
- package/lib/rtp/src/rtp/rtx.js.map +1 -1
- package/lib/rtp/src/srtp/context/context.d.ts +2 -0
- package/lib/rtp/src/srtp/context/context.js +12 -12
- package/lib/rtp/src/srtp/context/context.js.map +1 -1
- package/lib/sctp/src/param.js +31 -15
- package/lib/sctp/src/param.js.map +1 -1
- package/lib/sctp/src/sctp.js +10 -9
- package/lib/sctp/src/sctp.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.js +2 -3
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +2 -2
- package/lib/webrtc/src/sdp.js +1 -35
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/secureTransportManager.d.ts +1 -1
- package/lib/webrtc/src/secureTransportManager.js +12 -0
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +1 -1
- package/lib/webrtc/src/transport/sctp.js +17 -16
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/package.json +1 -7
package/lib/ice/src/ice.js
CHANGED
|
@@ -32,16 +32,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.Connection = void 0;
|
|
40
37
|
const crypto_1 = require("crypto");
|
|
41
38
|
const net_1 = require("net");
|
|
42
|
-
const Int64 = __importStar(require("int64-buffer"));
|
|
43
39
|
const timers = __importStar(require("node:timers/promises"));
|
|
44
|
-
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
45
40
|
const common_1 = require("./imports/common");
|
|
46
41
|
const candidate_1 = require("./candidate");
|
|
47
42
|
const lookup_1 = require("./dns/lookup");
|
|
@@ -150,7 +145,7 @@ class Connection {
|
|
|
150
145
|
enumerable: true,
|
|
151
146
|
configurable: true,
|
|
152
147
|
writable: true,
|
|
153
|
-
value:
|
|
148
|
+
value: (0, crypto_1.randomBytes)(8).readBigUInt64BE(0)
|
|
154
149
|
});
|
|
155
150
|
Object.defineProperty(this, "state", {
|
|
156
151
|
enumerable: true,
|
|
@@ -225,6 +220,32 @@ class Connection {
|
|
|
225
220
|
writable: true,
|
|
226
221
|
value: void 0
|
|
227
222
|
});
|
|
223
|
+
/** RFC 7675 consent-to-send: application data may use the selected pair. */
|
|
224
|
+
Object.defineProperty(this, "consentFresh", {
|
|
225
|
+
enumerable: true,
|
|
226
|
+
configurable: true,
|
|
227
|
+
writable: true,
|
|
228
|
+
value: false
|
|
229
|
+
});
|
|
230
|
+
/** Invalidates in-flight consent callbacks on restart / close / replace / expire. */
|
|
231
|
+
Object.defineProperty(this, "consentSessionId", {
|
|
232
|
+
enumerable: true,
|
|
233
|
+
configurable: true,
|
|
234
|
+
writable: true,
|
|
235
|
+
value: 0
|
|
236
|
+
});
|
|
237
|
+
Object.defineProperty(this, "consentExpiryTimer", {
|
|
238
|
+
enumerable: true,
|
|
239
|
+
configurable: true,
|
|
240
|
+
writable: true,
|
|
241
|
+
value: void 0
|
|
242
|
+
});
|
|
243
|
+
Object.defineProperty(this, "consentRequestAbort", {
|
|
244
|
+
enumerable: true,
|
|
245
|
+
configurable: true,
|
|
246
|
+
writable: true,
|
|
247
|
+
value: void 0
|
|
248
|
+
});
|
|
228
249
|
Object.defineProperty(this, "onData", {
|
|
229
250
|
enumerable: true,
|
|
230
251
|
configurable: true,
|
|
@@ -243,7 +264,7 @@ class Connection {
|
|
|
243
264
|
writable: true,
|
|
244
265
|
value: new common_1.Event()
|
|
245
266
|
});
|
|
246
|
-
//
|
|
267
|
+
// RFC 7675 consent freshness
|
|
247
268
|
Object.defineProperty(this, "queryConsent", {
|
|
248
269
|
enumerable: true,
|
|
249
270
|
configurable: true,
|
|
@@ -252,70 +273,164 @@ class Connection {
|
|
|
252
273
|
if (this.iceLite) {
|
|
253
274
|
return;
|
|
254
275
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.
|
|
259
|
-
|
|
276
|
+
// Invalidate any previous consent session before starting a new one.
|
|
277
|
+
this.stopConsentLifecycle();
|
|
278
|
+
const sessionId = this.consentSessionId;
|
|
279
|
+
this.consentFresh = true;
|
|
280
|
+
const handle = (0, helper_1.cancelable)(async (_, __, onCancel) => {
|
|
260
281
|
let canceled = false;
|
|
261
282
|
const cancelEvent = new AbortController();
|
|
283
|
+
const clearConsentExpiry = () => {
|
|
284
|
+
if (this.consentExpiryTimer === undefined) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
clearTimeout(this.consentExpiryTimer);
|
|
288
|
+
this.consentExpiryTimer = undefined;
|
|
289
|
+
};
|
|
290
|
+
const refreshConsentExpiry = () => {
|
|
291
|
+
// Only the active session may refresh the shared expiry timer.
|
|
292
|
+
if (canceled || sessionId !== this.consentSessionId) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
clearConsentExpiry();
|
|
296
|
+
this.consentExpiryTimer = setTimeout(() => {
|
|
297
|
+
this.consentExpiryTimer = undefined;
|
|
298
|
+
if (canceled || sessionId !== this.consentSessionId) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (this.state === "closed" || this.state === "failed") {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
log("Consent to send expired");
|
|
305
|
+
// Expire independently of request cadence / failure count (RFC 7675).
|
|
306
|
+
this.consentFresh = false;
|
|
307
|
+
this.consentSessionId++;
|
|
308
|
+
this.consentRequestAbort?.abort();
|
|
309
|
+
this.consentRequestAbort = undefined;
|
|
310
|
+
if (this.queryConsentHandle === handle) {
|
|
311
|
+
this.queryConsentHandle = undefined;
|
|
312
|
+
}
|
|
313
|
+
canceled = true;
|
|
314
|
+
cancelEvent.abort();
|
|
315
|
+
// Transport stays available for ICE restart; explicit close() uses "closed".
|
|
316
|
+
this.setState("failed");
|
|
317
|
+
}, iceBase_1.CONSENT_TIMEOUT * 1000);
|
|
318
|
+
};
|
|
262
319
|
onCancel.once(() => {
|
|
263
320
|
canceled = true;
|
|
264
|
-
|
|
321
|
+
// Avoid clearing a replacement session's timer/handle.
|
|
322
|
+
if (sessionId === this.consentSessionId) {
|
|
323
|
+
clearConsentExpiry();
|
|
324
|
+
this.consentRequestAbort?.abort();
|
|
325
|
+
this.consentRequestAbort = undefined;
|
|
326
|
+
}
|
|
265
327
|
cancelEvent.abort();
|
|
266
|
-
this.queryConsentHandle
|
|
328
|
+
if (this.queryConsentHandle === handle) {
|
|
329
|
+
this.queryConsentHandle = undefined;
|
|
330
|
+
}
|
|
267
331
|
});
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
//
|
|
332
|
+
// Initial ICE check success is the first valid consent response.
|
|
333
|
+
refreshConsentExpiry();
|
|
334
|
+
const randomizedConsentInterval = () => iceBase_1.CONSENT_INTERVAL * (0.8 + 0.4 * Math.random()) * 1000;
|
|
335
|
+
// Cadence is measured between request *starts*, not response completions.
|
|
336
|
+
let nextConsentAt = Date.now() + randomizedConsentInterval();
|
|
337
|
+
const isTerminalState = () => this.state === "closed" || this.state === "failed";
|
|
272
338
|
try {
|
|
273
|
-
while (
|
|
274
|
-
|
|
275
|
-
|
|
339
|
+
while (!isTerminalState() &&
|
|
340
|
+
!canceled &&
|
|
341
|
+
sessionId === this.consentSessionId) {
|
|
342
|
+
await this.abortableDelay(Math.max(0, nextConsentAt - Date.now()), cancelEvent.signal);
|
|
343
|
+
if (canceled ||
|
|
344
|
+
isTerminalState() ||
|
|
345
|
+
sessionId !== this.consentSessionId) {
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
// Fix next start time before awaiting any response (independent timers).
|
|
349
|
+
nextConsentAt = Date.now() + randomizedConsentInterval();
|
|
276
350
|
const nominated = this.nominated;
|
|
277
|
-
if (!nominated
|
|
351
|
+
if (!nominated) {
|
|
278
352
|
break;
|
|
279
353
|
}
|
|
354
|
+
const pairId = nominated.id;
|
|
355
|
+
const generation = this.generation;
|
|
356
|
+
const remotePassword = this.remotePassword;
|
|
357
|
+
const { localUsername, remoteUsername, iceControlling } = this;
|
|
280
358
|
const request = this.buildRequest({
|
|
281
|
-
nominate:
|
|
359
|
+
nominate: this.shouldNominateConsentRequest(nominated),
|
|
282
360
|
localUsername,
|
|
283
361
|
remoteUsername,
|
|
284
362
|
iceControlling,
|
|
285
363
|
localCandidate: nominated.localCandidate,
|
|
286
364
|
});
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
365
|
+
this.consentRequestAbort?.abort();
|
|
366
|
+
const requestAbort = new AbortController();
|
|
367
|
+
this.consentRequestAbort = requestAbort;
|
|
368
|
+
nominated.consentRequestsSent++;
|
|
369
|
+
nominated.requestsSent++;
|
|
370
|
+
// RTT-aware wait (floor 500ms); independent of retransmissions: 0.
|
|
371
|
+
const responseTimeout = (0, iceBase_1.consentResponseTimeoutMs)(nominated.rtt);
|
|
372
|
+
const requestStartedAt = performance.now();
|
|
373
|
+
// Do not await here: response wait must not stretch the 4–6s cadence.
|
|
374
|
+
nominated.protocol
|
|
375
|
+
.request(request, nominated.remoteAddr, Buffer.from(remotePassword, "utf8"), {
|
|
376
|
+
retransmissions: 0,
|
|
377
|
+
responseTimeout,
|
|
378
|
+
signal: requestAbort.signal,
|
|
379
|
+
onRequestSent: (attempt) => {
|
|
291
380
|
if (attempt > 0) {
|
|
292
381
|
nominated.retransmissionsSent++;
|
|
293
382
|
}
|
|
294
|
-
}
|
|
383
|
+
},
|
|
384
|
+
})
|
|
385
|
+
.then(() => {
|
|
386
|
+
// Accept only responses for the current pair / generation / session.
|
|
387
|
+
// Address / MESSAGE-INTEGRITY / response class are enforced in Transaction + protocol.
|
|
388
|
+
if (sessionId !== this.consentSessionId || canceled) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const state = this.state;
|
|
392
|
+
if (state === "closed" || state === "failed") {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (this.nominated?.id !== pairId) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (this.generation !== generation) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (this.remotePassword !== remotePassword) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const rtt = (performance.now() - requestStartedAt) / 1000; // seconds
|
|
405
|
+
nominated.rtt = rtt;
|
|
406
|
+
nominated.totalRoundTripTime += rtt;
|
|
407
|
+
nominated.roundTripTimeMeasurements++;
|
|
295
408
|
nominated.responsesReceived++;
|
|
296
|
-
|
|
297
|
-
|
|
409
|
+
this.consentFresh = true;
|
|
410
|
+
refreshConsentExpiry();
|
|
411
|
+
if (state === "disconnected") {
|
|
298
412
|
this.setState("connected");
|
|
299
413
|
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
break;
|
|
414
|
+
})
|
|
415
|
+
.catch((error) => {
|
|
416
|
+
// Individual request loss is expected; keep monitoring (RFC 7675).
|
|
417
|
+
if (sessionId === this.consentSessionId &&
|
|
418
|
+
this.nominated?.id === pairId) {
|
|
419
|
+
log("no stun response", error);
|
|
307
420
|
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
// Abort during delay is normal on cancel / expire.
|
|
426
|
+
}
|
|
427
|
+
finally {
|
|
428
|
+
if (sessionId === this.consentSessionId) {
|
|
429
|
+
clearConsentExpiry();
|
|
315
430
|
}
|
|
316
431
|
}
|
|
317
|
-
catch (error) { }
|
|
318
432
|
});
|
|
433
|
+
this.queryConsentHandle = handle;
|
|
319
434
|
}
|
|
320
435
|
});
|
|
321
436
|
Object.defineProperty(this, "send", {
|
|
@@ -323,17 +438,15 @@ class Connection {
|
|
|
323
438
|
configurable: true,
|
|
324
439
|
writable: true,
|
|
325
440
|
value: async (data) => {
|
|
326
|
-
|
|
327
|
-
if (
|
|
328
|
-
await activePair.protocol.sendData(data, activePair.remoteAddr);
|
|
329
|
-
// Update statistics
|
|
330
|
-
activePair.packetsSent++;
|
|
331
|
-
activePair.bytesSent += data.length;
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
// log("Cannot send data, ice not connected");
|
|
441
|
+
// RFC 7675: after consent expiry, do not send application data on the 5-tuple.
|
|
442
|
+
if (!this.canSendApplicationData()) {
|
|
335
443
|
return;
|
|
336
444
|
}
|
|
445
|
+
const activePair = this.nominated;
|
|
446
|
+
await activePair.protocol.sendData(data, activePair.remoteAddr);
|
|
447
|
+
// Update statistics
|
|
448
|
+
activePair.packetsSent++;
|
|
449
|
+
activePair.bytesSent += data.length;
|
|
337
450
|
}
|
|
338
451
|
});
|
|
339
452
|
// 3. Terminology : Check
|
|
@@ -421,7 +534,8 @@ class Connection {
|
|
|
421
534
|
}
|
|
422
535
|
}
|
|
423
536
|
// # check remote address matches
|
|
424
|
-
if (
|
|
537
|
+
if (result.addr[0] !== pair.remoteAddr[0] ||
|
|
538
|
+
result.addr[1] !== pair.remoteAddr[1]) {
|
|
425
539
|
pair.updateState(iceBase_1.CandidatePairState.FAILED);
|
|
426
540
|
this.checkComplete(pair);
|
|
427
541
|
r();
|
|
@@ -496,13 +610,13 @@ class Connection {
|
|
|
496
610
|
if (this.iceLite) {
|
|
497
611
|
value = false;
|
|
498
612
|
}
|
|
499
|
-
|
|
613
|
+
// While a pair is selected, keep the negotiated role. ICE restart clears
|
|
614
|
+
// `nominated`, so offer/answer role and RFC 8445 role-conflict repair can
|
|
615
|
+
// reassign controlling/controlled for the new generation.
|
|
616
|
+
if (this.nominated) {
|
|
500
617
|
return;
|
|
501
618
|
}
|
|
502
|
-
this.
|
|
503
|
-
for (const pair of this.checkList) {
|
|
504
|
-
pair.iceControlling = value;
|
|
505
|
-
}
|
|
619
|
+
this.applyIceControlling(value);
|
|
506
620
|
}
|
|
507
621
|
get iceLite() {
|
|
508
622
|
return this.options.iceLite;
|
|
@@ -542,13 +656,15 @@ class Connection {
|
|
|
542
656
|
protocol.localCandidate.ufrag = this.localUsername;
|
|
543
657
|
}
|
|
544
658
|
}
|
|
545
|
-
|
|
546
|
-
this.
|
|
659
|
+
// Tear down consent timers/transactions; new credentials require a new session.
|
|
660
|
+
this.stopConsentLifecycle();
|
|
547
661
|
}
|
|
548
662
|
resetNominatedPair() {
|
|
549
663
|
log("resetNominatedPair");
|
|
550
664
|
this.nominated = undefined;
|
|
551
665
|
this.nominating = false;
|
|
666
|
+
// Drop old pair's consent timers/transactions; restarted when a new pair is nominated.
|
|
667
|
+
this.stopConsentLifecycle();
|
|
552
668
|
}
|
|
553
669
|
setRemoteParams({ iceLite, usernameFragment, password, }) {
|
|
554
670
|
log("setRemoteParams", { iceLite, usernameFragment, password });
|
|
@@ -560,6 +676,15 @@ class Connection {
|
|
|
560
676
|
async gatherCandidates() {
|
|
561
677
|
if (!this.localCandidatesStart) {
|
|
562
678
|
this.localCandidatesStart = true;
|
|
679
|
+
// ICE restart keeps transport protocols; re-advertise their host candidates
|
|
680
|
+
// with the new generation / ufrag before gathering additional addresses.
|
|
681
|
+
for (const protocol of this.protocols) {
|
|
682
|
+
if (protocol.localCandidate) {
|
|
683
|
+
protocol.localCandidate.generation = this.generation;
|
|
684
|
+
protocol.localCandidate.ufrag = this.localUsername;
|
|
685
|
+
this.appendLocalCandidate(protocol.localCandidate);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
563
688
|
let address = (0, utils_1.getHostAddresses)(this.options.useIpv4, this.options.useIpv6, {
|
|
564
689
|
useLinkLocalAddress: this.options.useLinkLocalAddress,
|
|
565
690
|
});
|
|
@@ -921,13 +1046,69 @@ class Connection {
|
|
|
921
1046
|
}
|
|
922
1047
|
return false;
|
|
923
1048
|
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Stop consent request cadence, expiry timer, and outstanding transactions.
|
|
1051
|
+
* Does not change ICE state by itself.
|
|
1052
|
+
*/
|
|
1053
|
+
stopConsentLifecycle() {
|
|
1054
|
+
this.consentSessionId++;
|
|
1055
|
+
this.consentFresh = false;
|
|
1056
|
+
if (this.consentExpiryTimer !== undefined) {
|
|
1057
|
+
clearTimeout(this.consentExpiryTimer);
|
|
1058
|
+
this.consentExpiryTimer = undefined;
|
|
1059
|
+
}
|
|
1060
|
+
this.consentRequestAbort?.abort();
|
|
1061
|
+
this.consentRequestAbort = undefined;
|
|
1062
|
+
const handle = this.queryConsentHandle;
|
|
1063
|
+
this.queryConsentHandle = undefined;
|
|
1064
|
+
// Resolve after clearing the field so a stale onCancel cannot wipe a replacement.
|
|
1065
|
+
handle?.resolve?.();
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* ICE-lite interop only (not required by RFC 7675): mirror libwebrtc
|
|
1069
|
+
* semi-aggressive nomination — attach USE-CANDIDATE when we are controlling,
|
|
1070
|
+
* the remote is ICE-lite, and the target is the current selected pair.
|
|
1071
|
+
*/
|
|
1072
|
+
shouldNominateConsentRequest(pair) {
|
|
1073
|
+
return (this.iceControlling && this.remoteIsLite && this.nominated?.id === pair.id);
|
|
1074
|
+
}
|
|
1075
|
+
canSendApplicationData() {
|
|
1076
|
+
if (!this.nominated) {
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
if (this.state === "closed" || this.state === "failed") {
|
|
1080
|
+
return false;
|
|
1081
|
+
}
|
|
1082
|
+
// Local ICE-lite does not run consent checks; full agents need fresh consent.
|
|
1083
|
+
if (this.iceLite) {
|
|
1084
|
+
return true;
|
|
1085
|
+
}
|
|
1086
|
+
return this.consentFresh;
|
|
1087
|
+
}
|
|
1088
|
+
abortableDelay(ms, signal) {
|
|
1089
|
+
return new Promise((resolve, reject) => {
|
|
1090
|
+
if (signal.aborted) {
|
|
1091
|
+
reject(new DOMException("The operation was aborted", "AbortError"));
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
const timer = setTimeout(() => {
|
|
1095
|
+
signal.removeEventListener("abort", onAbort);
|
|
1096
|
+
resolve();
|
|
1097
|
+
}, ms);
|
|
1098
|
+
const onAbort = () => {
|
|
1099
|
+
clearTimeout(timer);
|
|
1100
|
+
reject(new DOMException("The operation was aborted", "AbortError"));
|
|
1101
|
+
};
|
|
1102
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
924
1105
|
async close() {
|
|
925
1106
|
// """
|
|
926
1107
|
// Close the connection.
|
|
927
1108
|
// """
|
|
928
1109
|
this.setState("closed");
|
|
929
1110
|
// # stop consent freshness tests
|
|
930
|
-
this.
|
|
1111
|
+
this.stopConsentLifecycle();
|
|
931
1112
|
// # stop check list
|
|
932
1113
|
if (this.checkList && !this.checkListDone) {
|
|
933
1114
|
this.checkListState.put(new Promise((r) => {
|
|
@@ -1013,13 +1194,23 @@ class Connection {
|
|
|
1013
1194
|
(0, iceBase_1.sortCandidatePairs)(this.checkList, this.iceControlling);
|
|
1014
1195
|
}
|
|
1015
1196
|
findPair(protocol, remoteCandidate) {
|
|
1016
|
-
const pair = this.checkList.find((pair) =>
|
|
1017
|
-
(0, fast_deep_equal_1.default)(pair.remoteCandidate, remoteCandidate));
|
|
1197
|
+
const pair = this.checkList.find((pair) => pair.protocol === protocol && pair.remoteCandidate === remoteCandidate);
|
|
1018
1198
|
return pair;
|
|
1019
1199
|
}
|
|
1200
|
+
applyIceControlling(iceControlling) {
|
|
1201
|
+
this._iceControlling = iceControlling;
|
|
1202
|
+
for (const pair of this.checkList) {
|
|
1203
|
+
pair.iceControlling = iceControlling;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1020
1206
|
switchRole(iceControlling) {
|
|
1021
1207
|
log("switch role", iceControlling);
|
|
1022
|
-
|
|
1208
|
+
// Role conflicts must be repaired even after a prior generation or while
|
|
1209
|
+
// connectivity checks are in flight (RFC 8445 §7.2.5.1 / §7.3.1.1).
|
|
1210
|
+
if (this.iceLite) {
|
|
1211
|
+
iceControlling = false;
|
|
1212
|
+
}
|
|
1213
|
+
this.applyIceControlling(iceControlling);
|
|
1023
1214
|
this.sortCheckList();
|
|
1024
1215
|
}
|
|
1025
1216
|
checkComplete(pair) {
|
|
@@ -1040,6 +1231,12 @@ class Connection {
|
|
|
1040
1231
|
this.nominated = pair;
|
|
1041
1232
|
this.nominating = false;
|
|
1042
1233
|
this.pruneTcpConnections(pair);
|
|
1234
|
+
// After resetNominatedPair / renomination while already connected,
|
|
1235
|
+
// restart consent freshness on the new selected pair.
|
|
1236
|
+
if (!this.iceLite &&
|
|
1237
|
+
(this.state === "connected" || this.state === "completed")) {
|
|
1238
|
+
this.queryConsent();
|
|
1239
|
+
}
|
|
1043
1240
|
// 8.1.2. Updating States
|
|
1044
1241
|
// The agent MUST remove all Waiting and Frozen pairs in the check
|
|
1045
1242
|
// list and triggered check queue for the same component as the
|