Skip to content

feat(webrtc): add UdpMux for shared-port WebRTC-Direct inbound dispatch#1397

Open
yashksaini-coder wants to merge 1 commit into
libp2p:mainfrom
yashksaini-coder:feat/webrtc-udpmux-1352
Open

feat(webrtc): add UdpMux for shared-port WebRTC-Direct inbound dispatch#1397
yashksaini-coder wants to merge 1 commit into
libp2p:mainfrom
yashksaini-coder:feat/webrtc-udpmux-1352

Conversation

@yashksaini-coder

Copy link
Copy Markdown
Contributor

Fixes #1352. Recreates #1381, which was auto-closed when the source fork was recreated — same diff, no code changes.

Summary

  • Adds libp2p/transport/webrtc/_udp_mux.py — a UdpMux class (a shared asyncio.DatagramProtocol) that owns one OS UDP socket and demultiplexes concurrent inbound WebRTC-Direct dials
  • Pre-ICE: routes STUN BINDING REQUESTs by USERNAME ufrag prefix (parsed from the STUN message)
  • Post-ICE: routes DTLS/SCTP frames by remote (host, port) (registered after ICE nomination)
  • _MuxedTransport adapter gives each aioice.Connection a fake transport backed by the shared socket, bypassing _gather_candidates entirely so aioice never binds its own port
  • add_ice_connection(ufrag, pwd, host=) creates an aioice.Connection with preset local_username/local_password and injects a mux-backed StunProtocol into connection._protocols
  • 11 tests covering all dispatch paths, transport adapter, and connection factory

Why

Closes the ICE mux gap identified in the spike (#1352). A spec-aligned WebRTC-Direct listener (libp2p/specs#715) must advertise a single fixed UDP port and demux concurrent dials on that port — impossible with the current aioice architecture where each Connection binds its own ephemeral socket. UdpMux solves this without modifying aioice.

Architecture note

StunProtocol is an internal aioice class. We access it directly because we're injecting into aioice's _protocols list — intentional monkey-patching. The two # type: ignore annotations mark aioice's connection_lost(exc: Exception) annotation gap (should be Optional[Exception] per asyncio docs).

What's next

Follow-up PR: wire UdpMux into listener.py to replace the current HTTP-signaling harness with a spec-aligned STUN-dispatch listener (WebRTC-Direct v2, libp2p/specs#715).

Test plan

  • uv run pytest tests/core/transport/webrtc/test_udp_mux.py -v → 11/11 pass (skipped when aioice not installed)
  • uv run make lint → all pre-commit hooks pass (mypy + pyrefly clean)

Implements _udp_mux.UdpMux — a shared UDP DatagramProtocol that owns a
single OS socket and demultiplexes concurrent inbound WebRTC-Direct dials:

- Pre-ICE STUN BINDING REQUESTs routed by USERNAME ufrag prefix
- Post-ICE DTLS/SCTP frames routed by remote (host, port)
- _MuxedTransport adapter gives each aioice.Connection a fake transport
  backed by the shared socket, bypassing _gather_candidates entirely
- add_ice_connection() creates an aioice.Connection with preset
  local_username/local_password and injects a mux-backed StunProtocol
  into its _protocols list without binding a new UDP socket

Foundational primitive for a spec-aligned WebRTC-Direct listener
(libp2p/specs#715) advertising one fixed UDP port in its multiaddr.

Closes libp2p#1352.
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.

spike: aiortc ICE mux / STUN USERNAME exposure for webrtc-direct v2 listener

1 participant