Skip to content

sctp: implement RFC 6525 stream reset for DataChannel close#39

Merged
nus merged 1 commit into
mainfrom
sctp-stream-reset
Jun 7, 2026
Merged

sctp: implement RFC 6525 stream reset for DataChannel close#39
nus merged 1 commit into
mainfrom
sctp-stream-reset

Conversation

@nus

@nus nus commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Closes the "DataChannel close doesn't send SCTP stream reset (RECONFIG)" BACKLOG item (RFC 6525 / RFC 8831 §6.7).

Problem

DataChannel.close() flipped local state only — it never sent an OUTGOING_SSN_RESET_REQUEST, the SCTP layer dropped incoming RE-CONFIG chunks (declared but unparsed), and the W3C closing state + onclosing event were missing. Result: closing a channel never told the peer, and a remote-initiated close was silently ignored, so neither side fired onclose.

Change

  • chunk.dart: RE-CONFIG chunk (0x82) + Outgoing/Incoming SSN Reset Request and Re-config Response parameters, with encode/parse. TLV framing is shared with the existing INIT-parameter path via _encodeTlv/_concatBytes.
  • state_machine.dart: resetStreams() sends an Outgoing SSN Reset Request (one in flight at a time, retransmit timer bounded by Max.Retrans); incoming requests reset the inbound stream, respond, and reset our own outbound side too (loop-guarded) so the channel closes both ways; responses finalize the reset. Streams reported once via a new onStreamReset callback; duplicate requests resend the cached response.
  • Interop fix: advertise RE-CONFIG support in INIT/INIT-ACK via the Supported Extensions Parameter (0x8008). Without this Chrome's dcSCTP rejects close() with UNSUPPORTED_OPERATION ("peer doesn't support it") — a gap the live-Chrome e2e caught that loopback tests could not.
  • data_channel.dart: real closing state + onClosing event; close() initiates the reset and defers closed/onClose to _finalizeClose() when the reset completes (immediate close when there's no association).
  • peer_connection.dart: wire closeresetStreams and onStreamReset_finalizeClose; PeerConnection.close() finalizes channels immediately.

Tests

  • Unit (reconfig_test.dart): chunk encode/parse round-trips, webdartc↔webdartc loopback reset (both peers observe the reset), duplicate-request handling; DataChannel close-state transitions (data_channel_test.dart).
  • Live Chrome e2e (e2e_test.dart Scenario 1): webdartc closes the channel and both peers fire onclose (webdartc's onClose + Chrome's dcClosed).

dart analyze clean; full suite passes (635 unit + 22 e2e). Reviewed with /simplify.

🤖 Generated with Claude Code

DataChannel.close() previously flipped local state only — it never sent an
OUTGOING_SSN_RESET_REQUEST, the SCTP layer dropped incoming RE-CONFIG
chunks (declared but unparsed), and the W3C `closing` state plus
`onclosing` event were missing. The result: closing a channel never told
the peer, and a remote-initiated close was silently ignored, so neither
side fired onclose.

Implement SCTP stream reconfiguration (RFC 6525 / RFC 8831 §6.7):
- chunk.dart: RE-CONFIG chunk (0x82) + Outgoing/Incoming SSN Reset Request
  and Re-config Response parameters, with encode + parse.
- state_machine.dart: resetStreams() sends an Outgoing SSN Reset Request
  (one in flight at a time, with a retransmit timer bounded by Max.Retrans);
  incoming requests reset the inbound stream, respond, and reset our own
  outbound side too (loop-guarded) so the channel closes both ways;
  responses finalize the outgoing reset. Streams are reported once via a
  new onStreamReset callback. Duplicate requests resend the cached response.
- state_machine.dart: advertise RE-CONFIG support in INIT/INIT-ACK via the
  Supported Extensions Parameter (0x8008). Without this Chrome's dcSCTP
  rejects close() with UNSUPPORTED_OPERATION ("peer doesn't support it") —
  caught only by the live-Chrome e2e below.
- data_channel.dart: real `closing` state + `onClosing` event; close()
  initiates the reset and defers `closed`/onClose to _finalizeClose() when
  the reset completes (immediate close when there is no association).
- peer_connection.dart: wire close → resetStreams and onStreamReset →
  _finalizeClose; PC.close() finalizes channels immediately.

Tested with a webdartc↔webdartc loopback reset, chunk round-trips,
duplicate-request handling, DataChannel close-state transitions, and a new
live-Chrome e2e (Scenario 1): webdartc closes the channel and both peers
fire onclose (webdartc's onClose + Chrome's dcClosed). Full suite incl.
e2e passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nus nus merged commit 8a59eb5 into main Jun 7, 2026
15 checks passed
@nus nus deleted the sctp-stream-reset branch June 7, 2026 18:10
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.

1 participant