Skip to content

Commit 8897ccc

Browse files
committed
fix: harden datachannel close and state repair
1 parent 87052cf commit 8897ccc

2 files changed

Lines changed: 73 additions & 5 deletions

File tree

lib/index.js

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,13 +1365,16 @@ class RTCDataChannel extends SimpleEventTarget {
13651365
this._pc._pairDataChannelById(this, this._effectiveId());
13661366
}
13671367
const pairedChannel = this._pairedChannel;
1368-
const synthesizePairedClose =
1369-
pairedChannel && this._bufferedAmount === 0 && !this._hasPendingSends();
13701368
if (!this._nativeCloseScheduled) {
13711369
this._nativeCloseScheduled = true;
13721370
const shouldDrainBeforeClose = this._bufferedAmount > 0 || this._hasPendingSends();
13731371
const closeNative = () => {
13741372
const close = () => {
1373+
const synthesizePairedClose =
1374+
pairedChannel &&
1375+
pairedChannel.readyState !== "closed" &&
1376+
(pairedChannel._syntheticIncoming ||
1377+
(this._bufferedAmount === 0 && !this._hasPendingSends()));
13751378
this._native.close();
13761379
if (synthesizePairedClose) {
13771380
setTimeout(() => pairedChannel._handleRemoteChannelClose(), 0);
@@ -1877,6 +1880,7 @@ class RTCPeerConnection extends SimpleEventTarget {
18771880
this._sctpTransportUpdateScheduled = false;
18781881
this._sctpConnectPollScheduled = false;
18791882
this._sctpConnectPollDeadline = 0;
1883+
this._connectedStateRepairScheduled = false;
18801884
this._dataChannelOpenRepairScheduled = false;
18811885
this._dataChannelOpenRepairDeadline = 0;
18821886
this._dataChannelAnnouncementRepairScheduled = false;
@@ -2741,13 +2745,66 @@ class RTCPeerConnection extends SimpleEventTarget {
27412745
this._sctpTransport._setState(closed ? "closed" : connected ? "connected" : "connecting");
27422746
if (connected || closed) this._sctpConnectPollDeadline = 0;
27432747
if (connected) {
2748+
this._scheduleConnectedStateRepair();
27442749
this._scheduleDataChannelOpenRepair();
27452750
this._scheduleDataChannelAnnouncementRepair();
27462751
this._pairedPeer?._scheduleDataChannelAnnouncementRepair();
27472752
}
27482753
this._scheduleSctpConnectPollIfNeeded();
27492754
}
27502755

2756+
_shouldRepairConnectedState() {
2757+
return Boolean(
2758+
!this._closed &&
2759+
this._sctpTransport?.state === "connected" &&
2760+
this._sameProcessIceCandidateExchange &&
2761+
this._explicitIceCandidateExchange &&
2762+
this._hasNegotiatedDataTransport() &&
2763+
this._pairedPeer?._hasNegotiatedDataTransport() &&
2764+
this._operationsPending === 0 &&
2765+
this._pairedPeer._operationsPending === 0 &&
2766+
this._connectionState !== "failed" &&
2767+
this._connectionState !== "disconnected" &&
2768+
this._iceConnectionState !== "failed" &&
2769+
this._iceConnectionState !== "disconnected" &&
2770+
(this._connectionState !== "connected" ||
2771+
!["connected", "completed"].includes(this._iceConnectionState)),
2772+
);
2773+
}
2774+
2775+
_scheduleConnectedStateRepair() {
2776+
if (this._connectedStateRepairScheduled || !this._shouldRepairConnectedState()) return;
2777+
this._connectedStateRepairScheduled = true;
2778+
setTimeout(() => {
2779+
this._connectedStateRepairScheduled = false;
2780+
if (!this._shouldRepairConnectedState()) return;
2781+
2782+
if (this._iceConnectionState === "new") {
2783+
this._iceConnectionState = "checking";
2784+
this.dispatchEvent(makeEvent("iceconnectionstatechange"));
2785+
this._iceTransport()?._handlePeerIceConnectionState("checking");
2786+
}
2787+
if (this._connectionState === "new") {
2788+
this._connectionState = "connecting";
2789+
this.dispatchEvent(makeEvent("connectionstatechange"));
2790+
}
2791+
2792+
setTimeout(() => {
2793+
if (!this._shouldRepairConnectedState()) return;
2794+
if (!["connected", "completed"].includes(this._iceConnectionState)) {
2795+
this._iceConnectionState = "connected";
2796+
this.dispatchEvent(makeEvent("iceconnectionstatechange"));
2797+
this._iceTransport()?._handlePeerIceConnectionState("connected");
2798+
}
2799+
if (this._connectionState !== "connected") {
2800+
this._connectionState = "connected";
2801+
this._updateSctpTransport();
2802+
this.dispatchEvent(makeEvent("connectionstatechange"));
2803+
}
2804+
}, 0);
2805+
}, 0);
2806+
}
2807+
27512808
_hasNegotiatedDataTransport() {
27522809
return (
27532810
hasDataMediaSection(this._localDescription) && hasDataMediaSection(this._remoteDescription)
@@ -3493,7 +3550,11 @@ class RTCPeerConnection extends SimpleEventTarget {
34933550
}
34943551

34953552
_eventListenerAdded(type) {
3496-
if (type === "datachannel") this._scheduleDataChannelFlush();
3553+
if (type === "datachannel") {
3554+
this._scheduleDataChannelFlush();
3555+
this._scheduleDataChannelAnnouncementRepair();
3556+
this._pairedPeer?._scheduleDataChannelAnnouncementRepair();
3557+
}
34973558
}
34983559

34993560
_scheduleDataChannelFlush() {

src/native/addon.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <sstream>
1414
#include <stdexcept>
1515
#include <string>
16+
#include <thread>
1617
#include <unordered_map>
1718
#include <utility>
1819
#include <vector>
@@ -357,8 +358,14 @@ struct ChannelBinding : public std::enable_shared_from_this<ChannelBinding> {
357358

358359
void Close() {
359360
auto dc = dataChannel;
360-
if (dc)
361-
dc->close();
361+
if (!dc)
362+
return;
363+
std::thread([dc = std::move(dc)]() {
364+
try {
365+
dc->close();
366+
} catch (...) {
367+
}
368+
}).detach();
362369
}
363370

364371
void Destroy() {

0 commit comments

Comments
 (0)