diff --git a/agent_stats.go b/agent_stats.go index 785e7ff7..b18c1381 100644 --- a/agent_stats.go +++ b/agent_stats.go @@ -54,6 +54,56 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { return res } +// GetSelectedCandidatePairStats returns a candidate pair stats for selected candidate pair. +// Returns false if there is no selected pair +func (a *Agent) GetSelectedCandidatePairStats() (CandidatePairStats, bool) { + isAvailable := false + var res CandidatePairStats + err := a.loop.Run(a.loop, func(_ context.Context) { + sp := a.getSelectedPair() + if sp == nil { + return + } + + isAvailable = true + res = CandidatePairStats{ + Timestamp: time.Now(), + LocalCandidateID: sp.Local.ID(), + RemoteCandidateID: sp.Remote.ID(), + State: sp.state, + Nominated: sp.nominated, + // PacketsSent uint32 + // PacketsReceived uint32 + // BytesSent uint64 + // BytesReceived uint64 + // LastPacketSentTimestamp time.Time + // LastPacketReceivedTimestamp time.Time + // FirstRequestTimestamp time.Time + // LastRequestTimestamp time.Time + // LastResponseTimestamp time.Time + TotalRoundTripTime: sp.TotalRoundTripTime(), + CurrentRoundTripTime: sp.CurrentRoundTripTime(), + // AvailableOutgoingBitrate float64 + // AvailableIncomingBitrate float64 + // CircuitBreakerTriggerCount uint32 + // RequestsReceived uint64 + // RequestsSent uint64 + ResponsesReceived: sp.ResponsesReceived(), + // ResponsesSent uint64 + // RetransmissionsReceived uint64 + // RetransmissionsSent uint64 + // ConsentRequestsSent uint64 + // ConsentExpiredTimestamp time.Time + } + }) + if err != nil { + a.log.Errorf("Failed to get selected candidate pair stats: %v", err) + return CandidatePairStats{}, false + } + + return res, isAvailable +} + // GetLocalCandidatesStats returns a list of local candidates stats func (a *Agent) GetLocalCandidatesStats() []CandidateStats { var res []CandidateStats diff --git a/agent_test.go b/agent_test.go index 5622ec15..168fcddb 100644 --- a/agent_test.go +++ b/agent_test.go @@ -635,7 +635,7 @@ func TestInvalidGather(t *testing.T) { }) } -func TestCandidatePairStats(t *testing.T) { +func TestCandidatePairsStats(t *testing.T) { defer test.CheckRoutines(t)() // Avoid deadlocks? @@ -789,6 +789,89 @@ func TestCandidatePairStats(t *testing.T) { } } +func TestSelectedCandidatePairStats(t *testing.T) { + defer test.CheckRoutines(t)() + + // Avoid deadlocks? + defer test.TimeOut(1 * time.Second).Stop() + + a, err := NewAgent(&AgentConfig{}) + if err != nil { + t.Fatalf("Failed to create agent: %s", err) + } + defer func() { + require.NoError(t, a.Close()) + }() + + hostConfig := &CandidateHostConfig{ + Network: "udp", + Address: "192.168.1.1", + Port: 19216, + Component: 1, + } + hostLocal, err := NewCandidateHost(hostConfig) + if err != nil { + t.Fatalf("Failed to construct local host candidate: %s", err) + } + + srflxConfig := &CandidateServerReflexiveConfig{ + Network: "udp", + Address: "10.10.10.2", + Port: 19218, + Component: 1, + RelAddr: "4.3.2.1", + RelPort: 43212, + } + srflxRemote, err := NewCandidateServerReflexive(srflxConfig) + if err != nil { + t.Fatalf("Failed to construct remote srflx candidate: %s", err) + } + + // no selected pair, should return not available + _, ok := a.GetSelectedCandidatePairStats() + require.False(t, ok) + + // add pair and populate some RTT stats + p := a.findPair(hostLocal, srflxRemote) + if p == nil { + a.addPair(hostLocal, srflxRemote) + p = a.findPair(hostLocal, srflxRemote) + } + for i := 0; i < 10; i++ { + p.UpdateRoundTripTime(time.Duration(i+1) * time.Second) + } + + // set the pair as selected + a.setSelectedPair(p) + + stats, ok := a.GetSelectedCandidatePairStats() + require.True(t, ok) + + if stats.LocalCandidateID != hostLocal.ID() { + t.Fatal("invalid local candidate id") + } + if stats.RemoteCandidateID != srflxRemote.ID() { + t.Fatal("invalid remote candidate id") + } + + expectedCurrentRoundTripTime := time.Duration(10) * time.Second + if stats.CurrentRoundTripTime != expectedCurrentRoundTripTime.Seconds() { + t.Fatalf("expected current round trip time to be %f, it is %f instead", + expectedCurrentRoundTripTime.Seconds(), stats.CurrentRoundTripTime) + } + + expectedTotalRoundTripTime := time.Duration(55) * time.Second + if stats.TotalRoundTripTime != expectedTotalRoundTripTime.Seconds() { + t.Fatalf("expected total round trip time to be %f, it is %f instead", + expectedTotalRoundTripTime.Seconds(), stats.TotalRoundTripTime) + } + + if stats.ResponsesReceived != 10 { + t.Fatalf("expected responses received to be 10, it is %d instead", + stats.ResponsesReceived) + } +} + func TestLocalCandidateStats(t *testing.T) { defer test.CheckRoutines(t)() diff --git a/go.mod b/go.mod index 8bd675f7..a779c0d0 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/google/uuid v1.6.0 - github.com/pion/dtls/v3 v3.0.2 + github.com/pion/dtls/v3 v3.0.3 github.com/pion/logging v0.2.2 github.com/pion/mdns/v2 v2.0.7 github.com/pion/randutil v0.1.0 @@ -12,7 +12,7 @@ require ( github.com/pion/transport/v3 v3.0.7 github.com/pion/turn/v4 v4.0.0 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.28.0 + golang.org/x/net v0.29.0 ) require ( @@ -20,8 +20,8 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.3 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sys v0.25.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8d41c1b8..48f0e2de 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pion/dtls/v3 v3.0.2 h1:425DEeJ/jfuTTghhUDW0GtYZYIwwMtnKKJNMcWccTX0= -github.com/pion/dtls/v3 v3.0.2/go.mod h1:dfIXcFkKoujDQ+jtd8M6RgqKK3DuaUilm3YatAbGp5k= +github.com/pion/dtls/v3 v3.0.3 h1:j5ajZbQwff7Z8k3pE3S+rQ4STvKvXUdKsi/07ka+OWM= +github.com/pion/dtls/v3 v3.0.3/go.mod h1:weOTUyIV4z0bQaVzKe8kpaP17+us3yAuiQsEAG1STMU= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= @@ -27,12 +27,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/udp_mux_test.go b/udp_mux_test.go index d2c14d79..fa24398f 100644 --- a/udp_mux_test.go +++ b/udp_mux_test.go @@ -8,7 +8,7 @@ package ice import ( "crypto/rand" - "crypto/sha1" //nolint:gosec + "crypto/sha256" "encoding/binary" "net" "sync" @@ -216,12 +216,12 @@ func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net for written := 0; written < targetSize; { buf := make([]byte, receiveMTU) // Byte 0-4: sequence - // Bytes 4-24: sha1 checksum - // Bytes2 4-mtu: random data - _, err := rand.Read(buf[24:]) + // Bytes 4-36: sha256 checksum + // Bytes2 36-mtu: random data + _, err := rand.Read(buf[36:]) require.NoError(t, err) - h := sha1.Sum(buf[24:]) //nolint:gosec - copy(buf[4:24], h[:]) + h := sha256.Sum256(buf[36:]) + copy(buf[4:36], h[:]) binary.LittleEndian.PutUint32(buf[0:4], uint32(sequence)) _, err = remoteConn.Write(buf) @@ -240,8 +240,8 @@ func testMuxConnectionPair(t *testing.T, pktConn net.PacketConn, remoteConn *net func verifyPacket(t *testing.T, b []byte, nextSeq uint32) { readSeq := binary.LittleEndian.Uint32(b[0:4]) require.Equal(t, nextSeq, readSeq) - h := sha1.Sum(b[24:]) //nolint:gosec - require.Equal(t, h[:], b[4:24]) + h := sha256.Sum256(b[36:]) + require.Equal(t, h[:], b[4:36]) } func TestUDPMux_Agent_Restart(t *testing.T) {