feat(webrtc): WebRTC-Direct v2 flow (libp2p/specs#715) — listener accepts v2, dialer opt-in - #1459
Open
yashksaini-coder wants to merge 8 commits into
Open
feat(webrtc): WebRTC-Direct v2 flow (libp2p/specs#715) — listener accepts v2, dialer opt-in#1459yashksaini-coder wants to merge 8 commits into
yashksaini-coder wants to merge 8 commits into
Conversation
…rity by DTLS role go-libp2p (go-msgio pbio fallback) writes the uvarint prefix and the protobuf body as two SCTP messages, so every py<->go WebRTC-Direct connection died at Noise msg#1 with "malformed handshake frame length". New stream._decode_frames pops complete frames off an accumulating buffer (frames may be split or batched across SCTP messages, bounded by MAX_MESSAGE_SIZE); DataChannelReadWriter and WebRTCStream.on_data both use it. A malformed frame now resets the stream instead of dropping one message and desynchronising. With the handshake passing, a py listener then hit aiortc's `assert stream_id not in self._data_channels`: aiortc picks DCEP stream id parity from the ICE role, which is inverted for a WebRTC-Direct listener (ICE-controlled, DTLS server), so it used even ids like go's dialer. _create_channel now passes an explicit id by DTLS role (RFC 8832: client even, server odd). Verified live against go-libp2p v0.49.0: go->py and py->go, v1 and v2. Refs libp2p#1437
yashksaini-coder
force-pushed
the
feat/webrtc-direct-v2
branch
from
August 28, 2026 06:22
f53ceb2 to
f42dae9
Compare
- keep aiortc allocating (and recycling) SCTP stream ids; only re-seed the parity from the DTLS role (explicit ids tied the 16-bit space to our never-recycled counter) - on_data: deliver frames decoded before a malformed one, reset the peer (RESET + cleanup) instead of a local-only reset, ignore bytes after reset, and apply a whole SCTP message in one trio hop - _decode_frames: derive the max prefix length from MAX_MESSAGE_SIZE Refs libp2p#1437
The test performs two full dials and two bounded peer-connection closes; on Windows each close can take ~5 s when a datagram write is in flight, so 30 s was flaky (seen once on CI, no socket errors logged). Refs libp2p#1437
yashksaini-coder
force-pushed
the
feat/webrtc-direct-v2
branch
from
August 28, 2026 07:18
f42dae9 to
a7bc836
Compare
… collides Harness mode binds TCP on the same port number as the OS-chosen UDP mux port; Windows reserves port ranges per protocol, so that TCP bind can fail with WinError 10013 (seen on CI in the harness loopback test). Retry up to five times with a new UDP port before giving up; explicit ports still fail loudly. Refs libp2p#1437
yashksaini-coder
force-pushed
the
feat/webrtc-direct-v2
branch
from
August 29, 2026 06:45
a7bc836 to
466ca00
Compare
…epts v2, dialer opt-in Listener: stop dropping libp2p+webrtc+v2/ first contacts. The server ufrag minus the prefix is the dialer's ICE pwd (must be a valid one, 22..256 ice-chars, else rejected in parse_direct_username); the inferred offer uses client_ufrag + that pwd, and our local ufrag/pwd stay server_ufrag verbatim as in v1. _accept_v1 -> _accept(client_pwd=...). Dialer: new WebRTCTransportConfig.webrtc_direct_dial_version (default 1). v2 keeps the aioice ufrag/pwd (no munging) and sets the synthetic answer's ufrag == pwd to "libp2p+webrtc+v2/" + local_password. Default stays v1 while specs#715 is unmerged; unknown values raise WebRTCConnectionError. Tests: v2 parse/reject cases, SDP shape, v2 loopback echo, no-munge check, short-suffix rejection, mixed v1+v2 dialers on one listener. Refs libp2p#1437
- validate webrtc_direct_dial_version in the config (was only checked mid-dial after the PC was built) - parse_direct_username returns the validated client pwd; make_v2_credential; one pwd check instead of two version branches - inline the v1 credential writes; trim the dial() comment Refs libp2p#1437
yashksaini-coder
force-pushed
the
feat/webrtc-direct-v2
branch
from
August 30, 2026 14:52
466ca00 to
167a507
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The v2 flow from libp2p/specs#715, on top of the v1 listener that landed in #1449.
libp2p+webrtc+v2/first contacts alongside v1: recoversclient_pwdby stripping the prefix (rejected unless a valid ice-pwd, 22–256), infers the offer withice-ufrag=client_ufrag/ice-pwd=client_pwd, and sets its own ufrag and pwd toserver_ufragverbatim. Unknown/missing prefix still rejected; v1 unchanged (pwd = client half).WebRTCTransportConfig(webrtc_direct_dial_version=2): no munging — it reads its aioice-generated pwd and encodes it in the synthetic answer's ufrag/pwd aslibp2p+webrtc+v2/<client_pwd>. Default stays v1 until specs#715 merges.Verified
Stacked on #1460. Refs #1437 (v2 item); go/js interop tests come next.