Skip to content

node-datachannel back to 0.32.3 (libdatachannel 0.24.3 DTLS startup race); 0.6.2 - #171

Merged
lannbot merged 1 commit into
mainfrom
node-datachannel-0.32.3
Sep 7, 2026
Merged

node-datachannel back to 0.32.3 (libdatachannel 0.24.3 DTLS startup race); 0.6.2#171
lannbot merged 1 commit into
mainfrom
node-datachannel-0.32.3

Conversation

@lannbot

@lannbot lannbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Chasing the polyengine-impl unit-test flake (connectPair yielding an already-failed peer, ~1 in 14 runs) found an upstream bug in what #170 shipped.

Root cause: node-datachannel 0.33.x bundles libdatachannel 0.24.3. That release added buffering of a DTLS ClientHello that arrives before the peer has a DTLS transport (libdatachannel#1554) and replays it synchronously from registerIncoming() — which DtlsTransport::start() calls before its own SSL_set_mtu/SSL_do_handshake. The replayed message reaches doRecv on a pool thread, whose BIO_write is not under mSslMutex, and races the initial handshake: Handshake failed: fatal I/O error → peer connection failed. The early ClientHello happens whenever the answerer's ICE connects before the offerer has applied the answer — i.e. with any signaling latency, and on loopback too.

Evidence: two-peer loop on the raw polyfill with libdatachannel's logger: failures at 3/150, 3/400, 1/500 on 0.33.2; the Verbose trace shows DtlsTransport::incoming size=232 logged between Registering incoming callback and DTLS MTU set to 1232, then the fatal I/O error. 0/600 on 0.32.3 (libdatachannel 0.24.2, which drops the early ClientHello and lets the client retransmit).

Upstream status: fixed by libdatachannel#1584 in libdatachannel 0.24.4 (2026-06-08) — same diagnosis, reproduced as intermittent Chrome↔libdatachannel failures on GitHub runners. No node-datachannel release bundles 0.24.4+ yet (0.33.2 and master pin 0.24.3). The README records the hold and its lifting condition.

Cost of reverting: loses libjuice 1.7.1 (in 0.24.3), notably the localhost >1000-datagrams-per-poll connection-kill fix — the likely cause of the much rarer pre-#170 channel-close-flush flake. A rare throughput kill beats a 1–2% handshake failure.

0.6.2 replaces 0.6.1 (which shipped the 0.33.2 pin). Patch: API/protocol unchanged.

Gates: just polyengine-check (13/13 ×4), 600-iteration connect loop clean, just conformance::run-polyengine 37/37.

Automerge armed.

node-datachannel 0.33.x bundles libdatachannel 0.24.3, whose new
early-ClientHello buffering (libdatachannel#1554) replays the message
inside DtlsTransport::start(), where doRecv's unlocked BIO_write races
the initial SSL_do_handshake. A ClientHello arrives early whenever the
answerer's ICE connects before the offerer has applied the answer —
routine with any signaling latency, and reproducible on loopback: 1-2%
of connections fail with "Handshake failed: fatal I/O error" (3/150,
3/400, 1/500 in a two-peer loop; 0/600 on 0.32.3). It surfaced as the
polyengine-impl unit suite failing one loopback test in ~1 of 14 runs.

The lock fix (libdatachannel#1584) is in libdatachannel 0.24.4, which no
node-datachannel release bundles yet. The README records the hold and
the condition for lifting it.

0.6.2 replaces 0.6.1, which shipped the 0.33.2 pin.
@lannbot
lannbot enabled auto-merge September 7, 2026 02:30
@lannbot
lannbot merged commit fe05ecf into main Sep 7, 2026
4 checks passed
@lannbot
lannbot deleted the node-datachannel-0.32.3 branch September 7, 2026 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants