XGW.ICE.Checklist (xgw v0.1.0)

Copy Markdown View Source

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

pair()

@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

pair_state()

@type pair_state() :: :waiting | :in_progress | :succeeded | :failed | :frozen

Checklist pair state (:waiting | :in_progress | :succeeded | :failed | :frozen).

Functions

all_failed?(pairs)

@spec all_failed?([pair()]) :: boolean()

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

build(locals, remotes, role)

Builds UDP component-1 pairs from local and remote candidate lists.

extend(existing, locals, remotes, role)

Extends an existing checklist with pairs for new locals/remotes without resetting in-progress or succeeded pair state (trickle during checking).

find_by_remote(pairs, arg, local \\ nil)

@spec find_by_remote([pair()], XGW.ICE.endpoint(), XMediaLib.SDP.Candidate.t() | nil) ::
  pair() | nil

Finds pair matching remote endpoint and optional local candidate.

find_by_transaction(pairs, transaction_id)

@spec find_by_transaction([pair()], non_neg_integer()) :: pair() | nil

Finds pair by outbound transaction id.

mark_failed(pairs, target)

@spec mark_failed([pair()], pair()) :: [pair()]

Marks a pair failed (RTO exhausted).

mark_in_progress(pairs, target, transaction_id, now_ms)

@spec mark_in_progress([pair()], pair(), non_neg_integer(), integer()) :: [pair()]

Marks a pair :in_progress with a new transaction id.

mark_pair_succeeded(pairs, target)

@spec mark_pair_succeeded([pair()], pair()) :: [pair()]

Marks a specific pair succeeded by identity.

mark_succeeded(pairs, transaction_id)

@spec mark_succeeded([pair()], non_neg_integer()) :: [pair()]

Marks the pair matching transaction_id as succeeded.

next_check(pairs)

@spec next_check([pair()]) :: pair() | nil

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}

nominate(pairs, local, remote)

Marks the pair matching remote/local as nominated.

nominated_succeeded?(pairs)

@spec nominated_succeeded?([pair()]) :: boolean()

Returns true when a nominated pair has succeeded.

Examples

iex> XGW.ICE.Checklist.nominated_succeeded?([
...>   %{nominated?: true, state: :succeeded}
...> ])
true

prune_redundant(pairs)

@spec prune_redundant([pair()]) :: [pair()]

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

rto_expired?(arg1, now_ms)

@spec rto_expired?(pair(), integer()) :: boolean()

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

same_pair?(a, b)

@spec same_pair?(pair(), pair()) :: boolean()

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

schedule_retransmit(pairs, target)

@spec schedule_retransmit([pair()], pair()) :: [pair()]

Schedules retransmit: doubles RTO unless max retransmits reached (-> failed).

sort(pairs)

@spec sort([pair()]) :: [pair()]

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]