-
-
Notifications
You must be signed in to change notification settings - Fork 445
Open
Description
Summary
When both peers use negotiated (Option=Some(id), out-of-band) RTCDataChannels with the same id, if the remote peer sends user data before the local negotiated channel is created, the local side may hit ErrStreamAlreadyExist or fail to receive messages.
Observed behavior
- Remote sends DATA on stream id (no DCEP in negotiated mode). Receiver creates/attaches a stream for that id. When the local app later dials the same id,
AssociationInternal::open_streamreturnsErrStreamAlreadyExistbecause the stream already exists. - Some paths expect DCEP only for non‑negotiated channels. If the negotiated channel isn’t created locally yet, the first inbound user‑data frame (non‑DCEP PPID) can be rejected by the server path.
Code references
- Duplicate id check (returns ErrStreamAlreadyExist):
sctp/src/association/association_internal.rs(open_stream) - Dial path that always opens the stream:
data/src/data_channel/mod.rs(DataChannel::dial) - Server path that expects DCEP for non‑negotiated:
data/src/data_channel/mod.rs(server path check for DCEP PPID) - ID allocation context (non‑negotiated, even/odd by DTLS role):
webrtc/src/sctp_transport/mod.rs(generate_and_set_data_channel_id)
Expected behavior
- For negotiated channels (Option), allow attaching an inbound stream by known id without DCEP, even if the local app hasn’t created the channel yet; or
- On local open collision, gracefully attach to the existing inbound stream instead of returning
ErrStreamAlreadyExist, so the application always gets a single consistent RTCDataChannel object.
Proposed improvements
- In negotiated mode, match inbound stream id to known negotiated channels and bind directly (bypass DCEP requirement).
- On
open_streamErrStreamAlreadyExistfor a negotiated id, bind the negotiated channel to the existing inbound stream and return it to the caller. - Optionally buffer inbound data on that stream until the negotiated channel is created.
Reproduction
- Both sides create channels with
negotiated=Some(42). - Answerer sends data immediately after SCTP is established.
- Offerer creates the negotiated channel later and tries to dial id=42.
- Offerer sees
ErrStreamAlreadyExistor “no messages” on the locally created object, while data actually flows to the inbound-created object.
Why this matters
Negotiated mode avoids DCEP by design per spec, but real apps can send early. Improving robustness makes negotiated channels less timing‑fragile without changing non‑negotiated semantics.
References
- W3C negotiated attribute: https://w3c.github.io/webrtc-pc/#dom-rtcdatachannelinit-negotiated
- RFC 8831 (WebRTC Data Channels): https://datatracker.ietf.org/doc/html/rfc8831
- RFC 8832 (DCEP): https://datatracker.ietf.org/doc/html/rfc8832
Metadata
Metadata
Assignees
Labels
No labels