Repository: pion/ice

Issue:
Skip the two-second delay when ICETransportPolicy=ICETransportPolicyRelay
When looping back through a TURN relay, Chrome takes a few milliseconds to connect: https://github.com/jech/galene/blob/master/static/galene.js#L2307

The equivalent code on the server side takes 2 seconds: https://github.com/jech/galene/blob/master/ice/ice.go#L127

The reason for that is that the TURN delay is fixed to two seconds (`ice.defaultRelayAcceptanceMinWait`), and this delay is honoured even when `ICETransportPolicy` is set to relay.

Discussion:
@Sean-Der: This should be pretty easy to fix!

First I need to confirm if settings aren't properly getting passed [here](https://github.com/pion/ice/blob/master/agent_config.go#L179-L183)

Then we also need to lower timeouts depending on enabled types [here](https://github.com/pion/ice/blob/master/agent_config.go#L212) @jech: I've added this to the test:
```
	s.SetHostAcceptanceMinWait(0)
	s.SetSrflxAcceptanceMinWait(0)
	s.SetPrflxAcceptanceMinWait(0)
	s.SetRelayAcceptanceMinWait(0)
```
and the timing is similar to the browser's.@AnshulMalik: Let's say we add 0 wait times for all these, and now we got the relay candidate, so we nominate it and proceed with connection.
And say after a second, we also got a srlfx candidate. So I wanted to know, if we can now choose this instead of the relay one automatically?@jech: This issue is about the case when `ICETransportPolicy`=`ICETransportPolicyRelay`.  There can be no server-refexive candidates in that case, so it's safe to skip the initial delay.

The general case is interesting too, but it's a different issue.@stv0g: @jech Maybe fixing this in `pion/webrtc` is the better place? `ICETransportPolicy` is a setting from the `webrtc` module if I am not mistaken?

Solution patch:
diff --git a/agent_config.go b/agent_config.go
index f8aa97e..ae250e3 100644
--- a/agent_config.go
+++ b/agent_config.go
@@ -39,6 +39,9 @@ const (
 	// defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate.
 	defaultRelayAcceptanceMinWait = 2000 * time.Millisecond
 
+	// defaultRelayOnlyAcceptanceMinWait is the wait time before nominating with a relay only candidate.
+	defaultRelayOnlyAcceptanceMinWait = time.Duration(0)
+
 	// defaultSTUNGatherTimeout is the wait time for STUN responses.
 	defaultSTUNGatherTimeout = 5 * time.Second
 
@@ -237,7 +240,11 @@ func (config *AgentConfig) initWithDefaults(agent *Agent) { //nolint:cyclop
 	}
 
 	if config.RelayAcceptanceMinWait == nil {
-		agent.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait
+		if len(config.CandidateTypes) == 1 && config.CandidateTypes[0] == CandidateTypeRelay {
+			agent.relayAcceptanceMinWait = defaultRelayOnlyAcceptanceMinWait
+		} else {
+			agent.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait
+		}
 	} else {
 		agent.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait
 	}
@@ -294,7 +301,8 @@ func (config *AgentConfig) initExtIPMapping(agent *Agent) error { //nolint:cyclo
 	if agent.extIPMapper == nil {
 		return nil // This may happen when config.NAT1To1IPs is an empty array
 	}
-	if agent.extIPMapper.candidateType == CandidateTypeHost { //nolint:nestif
+	switch agent.extIPMapper.candidateType {
+	case CandidateTypeHost:
 		if agent.mDNSMode == MulticastDNSModeQueryAndGather {
 			return ErrMulticastDNSWithNAT1To1IPMapping
 		}
@@ -302,11 +310,13 @@ func (config *AgentConfig) initExtIPMapping(agent *Agent) error { //nolint:cyclo
 		if !candiHostEnabled {
 			return ErrIneffectiveNAT1To1IPMappingHost
 		}
-	} else if agent.extIPMapper.candidateType == CandidateTypeServerReflexive {
+	case CandidateTypeServerReflexive:
 		candiSrflxEnabled := slices.Contains(agent.candidateTypes, CandidateTypeServerReflexive)
 		if !candiSrflxEnabled {
 			return ErrIneffectiveNAT1To1IPMappingSrflx
 		}
+	default:
+		return nil
 	}
 
 	return nil



Test patch:
diff --git a/agent_config_test.go b/agent_config_test.go
new file mode 100644
index 0000000..a28dc5d
--- /dev/null
+++ b/agent_config_test.go
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAgentConfig_initWithDefaults(t *testing.T) {
+	relayAcceptanceMinWait := 5 * time.Second
+	tests := []struct {
+		name   string
+		config *AgentConfig
+		fn     func(*testing.T, *Agent)
+	}{
+		{
+			"default config",
+			&AgentConfig{},
+			func(t *testing.T, result *Agent) {
+				t.Helper()
+				assert.Equal(t, result.relayAcceptanceMinWait, defaultRelayAcceptanceMinWait)
+			},
+		},
+		{
+			"multiple relay candidate types",
+			&AgentConfig{CandidateTypes: []CandidateType{CandidateTypeHost, CandidateTypeServerReflexive}},
+			func(t *testing.T, result *Agent) {
+				t.Helper()
+				assert.Equal(t, result.relayAcceptanceMinWait, defaultRelayAcceptanceMinWait)
+			},
+		},
+		{
+			"host only candidate type",
+			&AgentConfig{CandidateTypes: []CandidateType{CandidateTypeHost}},
+			func(t *testing.T, result *Agent) {
+				t.Helper()
+				assert.Equal(t, result.relayAcceptanceMinWait, defaultRelayAcceptanceMinWait)
+			},
+		},
+		{
+			"relay only candidate type",
+			&AgentConfig{CandidateTypes: []CandidateType{CandidateTypeRelay}},
+			func(t *testing.T, result *Agent) {
+				t.Helper()
+				assert.Equal(t, result.relayAcceptanceMinWait, defaultRelayOnlyAcceptanceMinWait)
+			},
+		},
+		{
+			"relay only with relayAcceptanceMinWait set",
+			&AgentConfig{CandidateTypes: []CandidateType{CandidateTypeRelay}, RelayAcceptanceMinWait: &relayAcceptanceMinWait},
+			func(t *testing.T, result *Agent) {
+				t.Helper()
+				assert.Equal(t, result.relayAcceptanceMinWait, relayAcceptanceMinWait)
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			agent, err := NewAgent(test.config)
+			if !assert.NoError(t, err) {
+				return
+			}
+			defer func() { _ = agent.Close() }()
+			test.fn(t, agent)
+		})
+	}
+}