Skip to content

Commit f0d7050

Browse files
authored
Update prflx candidate if it is explicitly added as a remote candidate (#74)
1 parent a110e16 commit f0d7050

File tree

2 files changed

+111
-21
lines changed

2 files changed

+111
-21
lines changed

lib/ex_ice/priv/ice_agent.ex

+38-19
Original file line numberDiff line numberDiff line change
@@ -399,28 +399,47 @@ defmodule ExICE.Priv.ICEAgent do
399399
def add_remote_candidate(ice_agent, remote_cand) do
400400
Logger.debug("Trying to add a new remote candidate: #{inspect(remote_cand)}")
401401

402-
uniq? = fn remote_cands, remote_cand ->
403-
not Enum.any?(remote_cands, fn cand ->
404-
cand.address == remote_cand.address and cand.port == remote_cand.port
405-
end)
406-
end
402+
found_cand =
403+
find_remote_cand(Map.values(ice_agent.remote_cands), remote_cand.address, remote_cand.port)
407404

408-
if uniq?.(Map.values(ice_agent.remote_cands), remote_cand) do
409-
ice_agent = do_add_remote_candidate(ice_agent, remote_cand)
410-
Logger.debug("Successfully added remote candidate.")
405+
case found_cand do
406+
nil ->
407+
ice_agent = do_add_remote_candidate(ice_agent, remote_cand)
408+
Logger.debug("Successfully added remote candidate.")
411409

412-
ice_agent
413-
|> update_connection_state()
414-
|> update_ta_timer()
415-
else
416-
# This is pretty common case (we can get conn-check
417-
# before getting a remote candidate), hence debug.
418-
Logger.debug("""
419-
Duplicated remote candidate. Ignoring.
420-
Candidate: #{inspect(remote_cand)}\
421-
""")
410+
ice_agent
411+
|> update_connection_state()
412+
|> update_ta_timer()
422413

423-
ice_agent
414+
%ExICE.Candidate{type: :prflx} ->
415+
# if there already is such candidate but discovered via received
416+
# binding request (i.e. this is prflx candidate), update its type
417+
# and priority, and update also pairs and potentially selected pair
418+
Logger.debug(
419+
"Remote candidate already discovered as prflx. Updating its type and priority. Recomputing pair prios."
420+
)
421+
422+
found_cand = %ExICE.Candidate{found_cand | type: :host, priority: remote_cand.priority}
423+
ice_agent = put_in(ice_agent.remote_cands[found_cand.id], found_cand)
424+
checklist = recompute_pair_prios(ice_agent)
425+
ice_agent = %__MODULE__{ice_agent | checklist: checklist}
426+
427+
if ice_agent.selected_pair_id != nil do
428+
%CandidatePair{} = best_valid_pair = Checklist.get_valid_pair(ice_agent.checklist)
429+
430+
if best_valid_pair.id != ice_agent.selected_pair_id do
431+
Logger.debug("New best valid pair: #{best_valid_pair.id}. Selecting.")
432+
%__MODULE__{ice_agent | selected_pair_id: best_valid_pair.id}
433+
else
434+
ice_agent
435+
end
436+
else
437+
ice_agent
438+
end
439+
440+
%ExICE.Candidate{} ->
441+
Logger.debug("Duplicated remote candidate. Ignoring. Candidate: #{inspect(remote_cand)}")
442+
ice_agent
424443
end
425444
end
426445

test/priv/ice_agent_test.exs

+73-2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,77 @@ defmodule ExICE.Priv.ICEAgentTest do
128128
assert cand_pair.remote_cand_id == r_cand.id
129129
end
130130

131+
test "with duplicated prflx candidate", %{ice_agent: ice_agent} do
132+
# Try to add a remote host candidate that has already been discovered as prflx candidate.
133+
# This should result in updating candidate's type and priority and pair's priority.
134+
# Also, selected_pair_id should change.
135+
assert %{} == ice_agent.remote_cands
136+
assert %{} == ice_agent.checklist
137+
138+
# prepare candiadtes
139+
remote_cand1 =
140+
ExICE.Candidate.new(:host, address: {192, 168, 0, 2}, port: 8445, priority: 123)
141+
142+
remote_cand2 =
143+
ExICE.Candidate.new(:host, address: {192, 168, 0, 3}, port: 8445, priority: 122)
144+
145+
[socket] = ice_agent.sockets
146+
147+
# discover prflx candidate
148+
req =
149+
binding_request(
150+
ice_agent.role,
151+
ice_agent.tiebreaker,
152+
ice_agent.remote_ufrag,
153+
ice_agent.local_ufrag,
154+
ice_agent.local_pwd,
155+
priority: 120
156+
)
157+
158+
ice_agent =
159+
ICEAgent.handle_udp(ice_agent, socket, remote_cand1.address, remote_cand1.port, req)
160+
161+
assert [prflx_cand] = Map.values(ice_agent.remote_cands)
162+
assert [prflx_pair] = Map.values(ice_agent.checklist)
163+
assert prflx_cand.type == :prflx
164+
prflx_pair = %CandidatePair{prflx_pair | state: :succeeded, valid?: true}
165+
ice_agent = put_in(ice_agent.checklist[prflx_pair.id], prflx_pair)
166+
167+
# add another remote candidate that will result in higher pair priority
168+
ice_agent = ICEAgent.add_remote_candidate(ice_agent, remote_cand2)
169+
170+
host_pair =
171+
Enum.find(Map.values(ice_agent.checklist), fn pair ->
172+
pair.remote_cand_id == remote_cand2.id
173+
end)
174+
175+
assert host_pair.priority > prflx_pair.priority
176+
host_pair = %CandidatePair{host_pair | state: :succeeded, valid?: true}
177+
ice_agent = put_in(ice_agent.checklist[host_pair.id], host_pair)
178+
ice_agent = %ICEAgent{ice_agent | selected_pair_id: host_pair.id}
179+
180+
# try to add host candidate that is the same as prflx candidate
181+
ice_agent = ICEAgent.add_remote_candidate(ice_agent, remote_cand1)
182+
183+
host_pair2 =
184+
Enum.find(Map.values(ice_agent.checklist), fn pair ->
185+
pair.remote_cand_id == prflx_cand.id
186+
end)
187+
188+
# assert that prflx candidate change its type and priority, and the relevant pair
189+
# also changed its priority and became a new selected pair
190+
host_cand =
191+
Enum.find(Map.values(ice_agent.remote_cands), fn cand ->
192+
cand.id == prflx_cand.id
193+
end)
194+
195+
assert host_cand.type == :host
196+
assert host_cand.priority > prflx_cand.priority
197+
assert host_pair2.priority > prflx_pair.priority
198+
assert host_pair2.priority > host_pair.priority
199+
assert ice_agent.selected_pair_id == host_pair2.id
200+
end
201+
131202
test "with duplicated remote candidate", %{ice_agent: ice_agent} do
132203
ice_agent = ICEAgent.add_remote_candidate(ice_agent, @remote_cand)
133204
ice_agent = ICEAgent.add_remote_candidate(ice_agent, @remote_cand)
@@ -2566,7 +2637,7 @@ defmodule ExICE.Priv.ICEAgentTest do
25662637
Message.new(%Type{class: :indication, method: :binding}) |> Message.encode()
25672638
end
25682639

2569-
defp binding_request(role, tiebreaker, local_ufrag, remote_ufrag, remote_pwd) do
2640+
defp binding_request(role, tiebreaker, local_ufrag, remote_ufrag, remote_pwd, opts \\ []) do
25702641
ice_attrs =
25712642
if role == :controlled do
25722643
[%ICEControlling{tiebreaker: tiebreaker + 1}, %UseCandidate{}]
@@ -2577,7 +2648,7 @@ defmodule ExICE.Priv.ICEAgentTest do
25772648
attrs =
25782649
[
25792650
%Username{value: "#{remote_ufrag}:#{local_ufrag}"},
2580-
%Priority{priority: 1234}
2651+
%Priority{priority: opts[:priority] || 1234}
25812652
] ++ ice_attrs
25822653

25832654
request =

0 commit comments

Comments
 (0)