Forms, sorts, and advances ICE candidate pairs through check states.
Why a checklist exists
Each local candidate can be paired with each remote candidate that shares component id, transport, and address family. ICE tries those pairs in priority order with STUN Binding checks. This module is the pure state machine for that list: build/extend pairs, mark in-progress/succeeded/failed, nominate, and pick the next check.
States follow RFC 8445 (:waiting, :in_progress, :succeeded, :failed,
:frozen).
ponytail: serial check queue (one :in_progress pair at a time); upgrade path:
parallel checks per foundation with per-pair RTO timers.
RFCs
- RFC 8445 -- candidate pair formation, priority, checklist states, nomination
Summary
Types
One local x remote candidate pair on the checklist.
Checklist pair state (:waiting | :in_progress | :succeeded | :failed | :frozen).
Functions
Returns true when all pairs are failed.
Builds UDP component-1 pairs from local and remote candidate lists.
Extends an existing checklist with pairs for new locals/remotes without resetting in-progress or succeeded pair state (trickle during checking).
Finds pair matching remote endpoint and optional local candidate.
Finds pair by outbound transaction id.
Marks a pair failed (RTO exhausted).
Marks a pair :in_progress with a new transaction id.
Marks a specific pair succeeded by identity.
Marks the pair matching transaction_id as succeeded.
Returns the next pair needing an outbound check, or nil.
Marks the pair matching remote/local as nominated.
Returns true when a nominated pair has succeeded.
Drops lower-priority pairs sharing the same local and remote foundation.
Returns true when RTO has expired for an in-progress pair.
Returns true when two pairs share the same local and remote foundations.
Schedules retransmit: doubles RTO unless max retransmits reached (-> failed).
Sorts pairs descending by priority.
Types
@type pair() :: %{ local: XMediaLib.SDP.Candidate.t(), remote: XMediaLib.SDP.Candidate.t(), priority: non_neg_integer(), state: pair_state(), nominated?: boolean(), transaction_id: non_neg_integer() | nil, rto_ms: non_neg_integer(), retransmit_count: non_neg_integer(), last_sent_ms: integer() | nil }
One local x remote candidate pair on the checklist.
Fields
:local/:remote- ICE candidates forming the pair:priority- RFC 8445 pair priority:state- checklist state:nominated?- true after USE-CANDIDATE / nomination:transaction_id- STUN transaction for the in-flight check:rto_ms/:retransmit_count/:last_sent_ms- retransmit timer state
@type pair_state() :: :waiting | :in_progress | :succeeded | :failed | :frozen
Checklist pair state (:waiting | :in_progress | :succeeded | :failed | :frozen).
Functions
Returns true when all pairs are failed.
Examples
iex> XGW.ICE.Checklist.all_failed?([])
false
iex> XGW.ICE.Checklist.all_failed?([%{state: :failed}, %{state: :failed}])
true
@spec build( [XMediaLib.SDP.Candidate.t()], [XMediaLib.SDP.Candidate.t()], XGW.ICE.role() ) :: [pair()]
Builds UDP component-1 pairs from local and remote candidate lists.
@spec extend( [pair()], [XMediaLib.SDP.Candidate.t()], [XMediaLib.SDP.Candidate.t()], XGW.ICE.role() ) :: [pair()]
Extends an existing checklist with pairs for new locals/remotes without resetting in-progress or succeeded pair state (trickle during checking).
@spec find_by_remote([pair()], XGW.ICE.endpoint(), XMediaLib.SDP.Candidate.t() | nil) :: pair() | nil
Finds pair matching remote endpoint and optional local candidate.
@spec find_by_transaction([pair()], non_neg_integer()) :: pair() | nil
Finds pair by outbound transaction id.
Marks a pair failed (RTO exhausted).
@spec mark_in_progress([pair()], pair(), non_neg_integer(), integer()) :: [pair()]
Marks a pair :in_progress with a new transaction id.
Marks a specific pair succeeded by identity.
@spec mark_succeeded([pair()], non_neg_integer()) :: [pair()]
Marks the pair matching transaction_id as succeeded.
Returns the next pair needing an outbound check, or nil.
Prefers an already :in_progress pair (serial queue) over the next :waiting.
Examples
iex> waiting = %{state: :waiting}
iex> busy = %{state: :in_progress}
iex> XGW.ICE.Checklist.next_check([waiting, busy])
%{state: :in_progress}
@spec nominate([pair()], XMediaLib.SDP.Candidate.t(), XMediaLib.SDP.Candidate.t()) :: [pair()]
Marks the pair matching remote/local as nominated.
Returns true when a nominated pair has succeeded.
Examples
iex> XGW.ICE.Checklist.nominated_succeeded?([
...> %{nominated?: true, state: :succeeded}
...> ])
true
Drops lower-priority pairs sharing the same local and remote foundation.
Examples
iex> a = %{local: %{foundation: "1"}, remote: %{foundation: "a"}, priority: 10}
iex> b = %{local: %{foundation: "1"}, remote: %{foundation: "a"}, priority: 1}
iex> c = %{local: %{foundation: "2"}, remote: %{foundation: "a"}, priority: 5}
iex> length(XGW.ICE.Checklist.prune_redundant([a, b, c]))
2
Returns true when RTO has expired for an in-progress pair.
Examples
iex> XGW.ICE.Checklist.rto_expired?(
...> %{state: :in_progress, last_sent_ms: 0, rto_ms: 500},
...> 500
...> )
true
iex> XGW.ICE.Checklist.rto_expired?(%{state: :waiting}, 1000)
false
Returns true when two pairs share the same local and remote foundations.
Examples
iex> a = %{local: %{foundation: "1"}, remote: %{foundation: "x"}}
iex> b = %{local: %{foundation: "1"}, remote: %{foundation: "x"}}
iex> XGW.ICE.Checklist.same_pair?(a, b)
true
Schedules retransmit: doubles RTO unless max retransmits reached (-> failed).
Sorts pairs descending by priority.
Examples
iex> pairs = [%{priority: 1}, %{priority: 9}, %{priority: 5}]
iex> Enum.map(XGW.ICE.Checklist.sort(pairs), & &1.priority)
[9, 5, 1]