Skip to content

Commit fa41b34

Browse files
committed
Don't close candidate on eperm (#85)
1 parent cf1044c commit fa41b34

File tree

4 files changed

+123
-22
lines changed

4 files changed

+123
-22
lines changed

.tool-versions

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
erlang 27.3.1
2+
elixir 1.18.3-otp-27

lib/ex_ice/priv/checklist.ex

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ defmodule ExICE.Priv.Checklist do
2929
def get_valid_pair(checklist) do
3030
checklist
3131
|> Stream.map(fn {_id, pair} -> pair end)
32-
# pair might have been marked as failed if the associated
33-
# local candidate has been closed
34-
|> Stream.filter(fn pair -> pair.state == :succeeded end)
3532
|> Stream.filter(fn pair -> pair.valid? end)
3633
|> Enum.sort_by(fn pair -> pair.priority end, :desc)
3734
|> Enum.at(0)
@@ -89,7 +86,7 @@ defmodule ExICE.Priv.Checklist do
8986
def close_candidate(checklist, local_cand) do
9087
Enum.reduce(checklist, {[], checklist}, fn {pair_id, pair}, {failed_pair_ids, checklist} ->
9188
if pair.local_cand_id == local_cand.base.id and pair.state != :failed do
92-
checklist = Map.put(checklist, pair_id, %{pair | state: :failed})
89+
checklist = Map.put(checklist, pair_id, %{pair | state: :failed, valid?: false})
9390
{[pair_id | failed_pair_ids], checklist}
9491
else
9592
{failed_pair_ids, checklist}

lib/ex_ice/priv/ice_agent.ex

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,9 @@ defmodule ExICE.Priv.ICEAgent do
485485
Logger.debug("Setting end-of-candidates flag.")
486486
ice_agent = %{ice_agent | eoc: true}
487487
# check whether it's time to nominate and if yes, try noimnate
488-
maybe_nominate(ice_agent)
488+
ice_agent
489+
|> maybe_nominate()
490+
|> update_connection_state()
489491
end
490492

491493
@spec send_data(t(), binary()) :: t()
@@ -580,6 +582,7 @@ defmodule ExICE.Priv.ICEAgent do
580582
|> update_gathering_state()
581583
|> update_connection_state()
582584
|> maybe_nominate()
585+
|> update_connection_state()
583586

584587
if ice_agent.state in [:completed, :failed] do
585588
update_ta_timer(ice_agent)
@@ -591,7 +594,9 @@ defmodule ExICE.Priv.ICEAgent do
591594
ice_agent
592595

593596
{type, tr} ->
594-
execute_transaction(ice_agent, type, tr)
597+
ice_agent
598+
|> execute_transaction(type, tr)
599+
|> update_connection_state()
595600
end
596601

597602
# schedule next check and call update_ta_timer
@@ -1744,6 +1749,7 @@ defmodule ExICE.Priv.ICEAgent do
17441749
conn_check_pair = %CandidatePair{
17451750
conn_check_pair
17461751
| state: :failed,
1752+
valid?: false,
17471753
non_symmetric_responses_received: conn_check_pair.non_symmetric_responses_received + 1
17481754
}
17491755

@@ -1817,6 +1823,7 @@ defmodule ExICE.Priv.ICEAgent do
18171823
conn_check_pair = %CandidatePair{
18181824
conn_check_pair
18191825
| state: :failed,
1826+
valid?: false,
18201827
responses_received: conn_check_pair.responses_received + 1
18211828
}
18221829

@@ -3045,7 +3052,9 @@ defmodule ExICE.Priv.ICEAgent do
30453052
pair = %CandidatePair{
30463053
pair
30473054
| packets_discarded_on_send: pair.packets_discarded_on_send + 1,
3048-
bytes_discarded_on_send: pair.bytes_discarded_on_send + byte_size(raw_req)
3055+
bytes_discarded_on_send: pair.bytes_discarded_on_send + byte_size(raw_req),
3056+
state: :failed,
3057+
valid?: false
30493058
}
30503059

30513060
put_in(ice_agent.checklist[pair.id], pair)
@@ -3117,7 +3126,7 @@ defmodule ExICE.Priv.ICEAgent do
31173126
""")
31183127

31193128
ice_agent = put_in(ice_agent.local_cands[local_cand.base.id], local_cand)
3120-
ice_agent = close_candidate(ice_agent, local_cand)
3129+
31213130
{:error, ice_agent}
31223131
end
31233132
end

test/priv/ice_agent_test.exs

Lines changed: 107 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ defmodule ExICE.Priv.ICEAgentTest do
317317
ice_agent = put_in(ice_agent.local_cands[cand.base.id], cand)
318318

319319
[pair] = Map.values(ice_agent.checklist)
320-
pair = %{pair | state: :failed}
320+
pair = %{pair | state: :failed, valid?: false}
321321
ice_agent = put_in(ice_agent.checklist[pair.id], pair)
322322

323323
# try to feed data on closed candidate, it should be ignored
@@ -377,7 +377,7 @@ defmodule ExICE.Priv.ICEAgentTest do
377377

378378
assert ice_agent.state == :closed
379379
assert ice_agent.gathering_state == :complete
380-
assert [%{state: :failed} = pair] = Map.values(ice_agent.checklist)
380+
assert [%{state: :failed, valid?: false} = pair] = Map.values(ice_agent.checklist)
381381
assert [%{base: %{closed?: true}}] = Map.values(ice_agent.local_cands)
382382
# make sure that sockets and remote cands were not cleared
383383
assert [_remote_cand] = Map.values(ice_agent.remote_cands)
@@ -468,7 +468,7 @@ defmodule ExICE.Priv.ICEAgentTest do
468468

469469
# mark pair as failed
470470
[pair] = Map.values(ice_agent.checklist)
471-
ice_agent = put_in(ice_agent.checklist[pair.id], %{pair | state: :failed})
471+
ice_agent = put_in(ice_agent.checklist[pair.id], %{pair | state: :failed, valid?: false})
472472

473473
# clear ta_timer, ignore outgoing binding request that has been generated
474474
ice_agent = ICEAgent.handle_ta_timeout(ice_agent)
@@ -526,8 +526,7 @@ defmodule ExICE.Priv.ICEAgentTest do
526526

527527
# mark pair as failed
528528
[pair] = Map.values(ice_agent.checklist)
529-
ice_agent = put_in(ice_agent.checklist[pair.id], %{pair | state: :failed})
530-
529+
ice_agent = put_in(ice_agent.checklist[pair.id], %{pair | state: :failed, valid?: false})
531530
# clear ta_timer, ignore outgoing binding request that has been generated
532531
ice_agent = ICEAgent.handle_ta_timeout(ice_agent)
533532
assert ice_agent.ta_timer == nil
@@ -1259,6 +1258,9 @@ defmodule ExICE.Priv.ICEAgentTest do
12591258
end
12601259
end
12611260

1261+
@conn_check_byte_size 92
1262+
@conn_check_with_nomination_byte_size 96
1263+
12621264
describe "connectivity check" do
12631265
setup do
12641266
ice_agent =
@@ -1592,7 +1594,7 @@ defmodule ExICE.Priv.ICEAgentTest do
15921594
resp
15931595
)
15941596

1595-
assert [%CandidatePair{state: :failed}] = Map.values(ice_agent.checklist)
1597+
assert [%CandidatePair{state: :failed, valid?: false}] = Map.values(ice_agent.checklist)
15961598
assert [new_pair] = Map.values(ice_agent.checklist)
15971599
assert new_pair.state == :failed
15981600
assert new_pair.responses_received == pair.responses_received
@@ -1639,6 +1641,89 @@ defmodule ExICE.Priv.ICEAgentTest do
16391641

16401642
assert ice_agent.state == :completed
16411643
end
1644+
1645+
test "failure on send" do
1646+
# 1. replace candidate with the mock one that always fails to send data
1647+
# 2. assert that after unsuccessful conn check sending, ice_agent moves conn pair to the failed state
1648+
1649+
ice_agent =
1650+
ICEAgent.new(
1651+
controlling_process: self(),
1652+
role: :controlling,
1653+
if_discovery_module: IfDiscovery.MockSingle,
1654+
transport_module: Transport.Mock
1655+
)
1656+
|> ICEAgent.set_remote_credentials("someufrag", "somepwd")
1657+
|> ICEAgent.gather_candidates()
1658+
|> ICEAgent.add_remote_candidate(@remote_cand)
1659+
|> ICEAgent.end_of_candidates()
1660+
1661+
assert ice_agent.gathering_state == :complete
1662+
1663+
# replace candidate with the mock one
1664+
[local_cand] = Map.values(ice_agent.local_cands)
1665+
mock_cand = %Candidate.Mock{base: local_cand.base}
1666+
ice_agent = %{ice_agent | local_cands: %{mock_cand.base.id => mock_cand}}
1667+
1668+
# try to send conn check
1669+
ice_agent = ICEAgent.handle_ta_timeout(ice_agent)
1670+
1671+
# assert that the candidate pair has moved to a failed state
1672+
# and that the state was updated after the packet was discarded
1673+
assert [
1674+
%{
1675+
state: :failed,
1676+
valid?: false,
1677+
packets_discarded_on_send: 1,
1678+
bytes_discarded_on_send: @conn_check_byte_size
1679+
}
1680+
] = Map.values(ice_agent.checklist)
1681+
1682+
assert ice_agent.state == :failed
1683+
end
1684+
1685+
test "failure on send, when nominating" do
1686+
# 1. make ice agent connected
1687+
# 2. replace candidate with the mock one that always fails to send data
1688+
# 3. assert that after unsuccessful nomination sending, ice_agent moves conn pair to the failed state
1689+
1690+
ice_agent =
1691+
ICEAgent.new(
1692+
controlling_process: self(),
1693+
role: :controlling,
1694+
if_discovery_module: IfDiscovery.MockSingle,
1695+
transport_module: Transport.Mock
1696+
)
1697+
|> ICEAgent.set_remote_credentials("someufrag", "somepwd")
1698+
|> ICEAgent.gather_candidates()
1699+
|> ICEAgent.add_remote_candidate(@remote_cand)
1700+
1701+
assert ice_agent.gathering_state == :complete
1702+
1703+
# make ice_agent connected
1704+
ice_agent = connect(ice_agent)
1705+
1706+
# replace candidate with the mock one
1707+
[local_cand] = Map.values(ice_agent.local_cands)
1708+
mock_cand = %Candidate.Mock{base: local_cand.base}
1709+
ice_agent = %{ice_agent | local_cands: %{mock_cand.base.id => mock_cand}}
1710+
1711+
# trigger pair nomination
1712+
ice_agent = ICEAgent.end_of_candidates(ice_agent)
1713+
1714+
# assert that the candidate pair has moved to a failed state
1715+
# and that the state was updated after the packet was discarded
1716+
assert [
1717+
%{
1718+
state: :failed,
1719+
valid?: false,
1720+
packets_discarded_on_send: 1,
1721+
bytes_discarded_on_send: @conn_check_with_nomination_byte_size
1722+
}
1723+
] = Map.values(ice_agent.checklist)
1724+
1725+
assert ice_agent.state == :failed
1726+
end
16421727
end
16431728

16441729
describe "connectivity check with aggressive nomination" do
@@ -2019,7 +2104,7 @@ defmodule ExICE.Priv.ICEAgentTest do
20192104
ice_agent = ICEAgent.handle_pair_timeout(ice_agent)
20202105

20212106
# assert that the pair is marked as failed
2022-
assert [%CandidatePair{state: :failed}] = Map.values(ice_agent.checklist)
2107+
assert [%CandidatePair{state: :failed, valid?: false}] = Map.values(ice_agent.checklist)
20232108

20242109
# trigger eoc timeout
20252110
ice_agent = ICEAgent.handle_eoc_timeout(ice_agent)
@@ -2049,7 +2134,7 @@ defmodule ExICE.Priv.ICEAgentTest do
20492134

20502135
# mark pair as failed
20512136
[pair] = Map.values(ice_agent.checklist)
2052-
ice_agent = put_in(ice_agent.checklist[pair.id], %{pair | state: :failed})
2137+
ice_agent = put_in(ice_agent.checklist[pair.id], %{pair | state: :failed, valid?: false})
20532138

20542139
# set eoc flag
20552140
failed_ice_agent = ICEAgent.end_of_candidates(ice_agent)
@@ -2568,8 +2653,8 @@ defmodule ExICE.Priv.ICEAgentTest do
25682653
test "candidate fails to send data when ice is connected" do
25692654
# 1. make ice agent connected
25702655
# 2. replace candidate with the mock one that always fails to send data
2571-
# 3. assert that after unsuccessful data sending, ice_agent moves to the failed state
2572-
# as there are no other pairs
2656+
# 3. assert that after unsuccessful data sending, ice_agent doesn't move to the failed state
2657+
# even when there is only one pair
25732658

25742659
ice_agent =
25752660
ICEAgent.new(
@@ -2595,10 +2680,18 @@ defmodule ExICE.Priv.ICEAgentTest do
25952680
# try to send some data
25962681
ice_agent = ICEAgent.send_data(ice_agent, "somedata")
25972682

2598-
# assert that local cand has been closed and the agent moved to the failed state
2599-
assert [%{base: %{closed?: true}}] = Map.values(ice_agent.local_cands)
2600-
assert ice_agent.state == :failed
2601-
assert [%{state: :failed}] = Map.values(ice_agent.checklist)
2683+
# assert that the local candidate hasn't been closed and that the agent hasn't moved to a failed state
2684+
assert [%{base: %{closed?: false}}] = Map.values(ice_agent.local_cands)
2685+
assert ice_agent.state == :connected
2686+
2687+
# assert that the local candidate's state was updated after the packet was discarded
2688+
assert [
2689+
%{
2690+
state: :succeeded,
2691+
packets_discarded_on_send: 1,
2692+
bytes_discarded_on_send: 8
2693+
}
2694+
] = Map.values(ice_agent.checklist)
26022695
end
26032696

26042697
test "relay connection" do

0 commit comments

Comments
 (0)