feat(webrtc): add ICE config helper (Configuration + SettingEngine)
Vendors github.com/pion/webrtc/v4 v4.2.11 and its transitive
dependencies (datachannel, dtls/v3, ice/v4, interceptor, logging,
mdns/v2, sctp, sdp/v3, srtp/v3, stun/v3, transport/v4, turn/v4).
2026-04-17 08:46:27 -04:00
|
|
|
package webrtc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/pion/webrtc/v4"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestBuildICEConfig_Defaults(t *testing.T) {
|
|
|
|
|
c := DefaultConfig()
|
|
|
|
|
rtcConfig, _, err := BuildICEConfig(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("BuildICEConfig: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(rtcConfig.ICEServers) == 0 {
|
|
|
|
|
t.Error("ICEServers should not be empty")
|
|
|
|
|
}
|
|
|
|
|
// First default is Cloudflare STUN.
|
|
|
|
|
if rtcConfig.ICEServers[0].URLs[0] != "stun:stun.cloudflare.com:3478" {
|
|
|
|
|
t.Errorf("first ICE server = %q, want stun:stun.cloudflare.com:3478",
|
|
|
|
|
rtcConfig.ICEServers[0].URLs[0])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestBuildICEConfig_PublicIP(t *testing.T) {
|
|
|
|
|
c := DefaultConfig()
|
|
|
|
|
c.PublicIP = "203.0.113.10"
|
|
|
|
|
_, se, err := BuildICEConfig(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("BuildICEConfig: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if se == nil {
|
|
|
|
|
t.Fatal("SettingEngine should not be nil when PublicIP is set")
|
|
|
|
|
}
|
|
|
|
|
// We can't introspect NAT1To1IPs directly from Pion's public API; the
|
|
|
|
|
// smoke test is that building an API from this engine works.
|
|
|
|
|
api := webrtc.NewAPI(webrtc.WithSettingEngine(*se))
|
|
|
|
|
if api == nil {
|
|
|
|
|
t.Fatal("NewAPI returned nil")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestBuildICEConfig_InvalidConfig(t *testing.T) {
|
|
|
|
|
c := DefaultConfig()
|
|
|
|
|
c.WHEPListen = ""
|
|
|
|
|
_, _, err := BuildICEConfig(c)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Error("BuildICEConfig should reject invalid config")
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-10 14:02:10 -04:00
|
|
|
|
|
|
|
|
// TestBuildICEConfig_NAT1To1IPs_Multi verifies that a list of multiple
|
|
|
|
|
// NAT1To1 IPs is accepted and a SettingEngine is returned, allowing
|
|
|
|
|
// dual-homed servers to advertise host candidates on all interfaces.
|
|
|
|
|
func TestBuildICEConfig_NAT1To1IPs_Multi(t *testing.T) {
|
|
|
|
|
c := DefaultConfig()
|
|
|
|
|
c.NAT1To1IPs = []string{"10.0.0.1", "203.0.113.10"}
|
|
|
|
|
_, se, err := BuildICEConfig(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("BuildICEConfig with multiple NAT1To1IPs: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if se == nil {
|
|
|
|
|
t.Fatal("SettingEngine should not be nil when NAT1To1IPs is set")
|
|
|
|
|
}
|
|
|
|
|
// Smoke-test: Pion should accept the engine without panicking.
|
|
|
|
|
api := webrtc.NewAPI(webrtc.WithSettingEngine(*se))
|
|
|
|
|
if api == nil {
|
|
|
|
|
t.Fatal("NewAPI returned nil")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestBuildICEConfig_NAT1To1IPs_FallsBackToPublicIP verifies that when
|
|
|
|
|
// NAT1To1IPs is empty but PublicIP is set, the single IP is promoted to
|
|
|
|
|
// the NAT1To1 list (backward-compat path).
|
|
|
|
|
func TestBuildICEConfig_NAT1To1IPs_FallsBackToPublicIP(t *testing.T) {
|
|
|
|
|
c := DefaultConfig()
|
|
|
|
|
c.PublicIP = "198.51.100.1"
|
|
|
|
|
c.NAT1To1IPs = nil
|
|
|
|
|
_, se, err := BuildICEConfig(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("BuildICEConfig (PublicIP fallback): %v", err)
|
|
|
|
|
}
|
|
|
|
|
if se == nil {
|
|
|
|
|
t.Fatal("SettingEngine should be set when PublicIP is used as fallback")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestBuildICEConfig_NAT1To1IPs_TakesPrecedenceOverPublicIP verifies that
|
|
|
|
|
// when both NAT1To1IPs and PublicIP are set, a SettingEngine is still
|
|
|
|
|
// returned (the subsystem merges them; BuildICEConfig sees only NAT1To1IPs).
|
|
|
|
|
func TestBuildICEConfig_NAT1To1IPs_TakesPrecedenceOverPublicIP(t *testing.T) {
|
|
|
|
|
c := DefaultConfig()
|
|
|
|
|
c.PublicIP = "198.51.100.1"
|
|
|
|
|
c.NAT1To1IPs = []string{"10.0.0.1", "198.51.100.1"}
|
|
|
|
|
_, se, err := BuildICEConfig(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("BuildICEConfig (NAT1To1IPs + PublicIP): %v", err)
|
|
|
|
|
}
|
|
|
|
|
if se == nil {
|
|
|
|
|
t.Fatal("SettingEngine should not be nil when NAT1To1IPs is set")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestBuildICEConfig_NAT1To1IPs_NeitherSet verifies that with no IP hints
|
|
|
|
|
// the function still succeeds (STUN-only mode). A SettingEngine is still
|
|
|
|
|
// returned because the default UDPPortRange is non-zero.
|
|
|
|
|
func TestBuildICEConfig_NAT1To1IPs_NeitherSet(t *testing.T) {
|
|
|
|
|
c := DefaultConfig()
|
|
|
|
|
c.PublicIP = ""
|
|
|
|
|
c.NAT1To1IPs = nil
|
|
|
|
|
_, se, err := BuildICEConfig(c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("BuildICEConfig (STUN-only): %v", err)
|
|
|
|
|
}
|
|
|
|
|
// UDPPortRange.Low > 0 in DefaultConfig, so se is non-nil; verify it
|
|
|
|
|
// builds without error.
|
|
|
|
|
if se != nil {
|
|
|
|
|
api := webrtc.NewAPI(webrtc.WithSettingEngine(*se))
|
|
|
|
|
if api == nil {
|
|
|
|
|
t.Fatal("NewAPI returned nil in STUN-only mode")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|