cloudflared/quic/v3/manager_test.go
João "Pisco" Fernandes bf4954e96a TUN-8861: Add session limiter to UDP session manager
## Summary
In order to make cloudflared behavior more predictable and
prevent an exhaustion of resources, we have decided to add
session limits that can be configured by the user. This first
commit introduces the session limiter and adds it to the UDP
handling path. For now the limiter is set to run only in
unlimited mode.
2025-01-20 02:52:32 -08:00

108 lines
3.4 KiB
Go

package v3_test
import (
"errors"
"net/netip"
"strings"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"github.com/cloudflare/cloudflared/mocks"
"github.com/cloudflare/cloudflared/ingress"
v3 "github.com/cloudflare/cloudflared/quic/v3"
cfdsession "github.com/cloudflare/cloudflared/session"
)
func TestRegisterSession(t *testing.T) {
log := zerolog.Nop()
manager := v3.NewSessionManager(&noopMetrics{}, &log, ingress.DialUDPAddrPort, cfdsession.NewLimiter(0))
request := v3.UDPSessionRegistrationDatagram{
RequestID: testRequestID,
Dest: netip.MustParseAddrPort("127.0.0.1:5000"),
Traced: false,
IdleDurationHint: 5 * time.Second,
Payload: nil,
}
session, err := manager.RegisterSession(&request, &noopEyeball{})
if err != nil {
t.Fatalf("register session should've succeeded: %v", err)
}
if request.RequestID != session.ID() {
t.Fatalf("session id doesn't match: %v != %v", request.RequestID, session.ID())
}
// We shouldn't be able to register another session with the same request id
_, err = manager.RegisterSession(&request, &noopEyeball{})
if !errors.Is(err, v3.ErrSessionAlreadyRegistered) {
t.Fatalf("session is already registered for this connection: %v", err)
}
// We shouldn't be able to register another session with the same request id for a different connection
_, err = manager.RegisterSession(&request, &noopEyeball{connID: 1})
if !errors.Is(err, v3.ErrSessionBoundToOtherConn) {
t.Fatalf("session is already registered for a separate connection: %v", err)
}
// Get session
sessionGet, err := manager.GetSession(request.RequestID)
if err != nil {
t.Fatalf("get session failed: %v", err)
}
if session.ID() != sessionGet.ID() {
t.Fatalf("session's do not match: %v != %v", session.ID(), sessionGet.ID())
}
// Remove the session
manager.UnregisterSession(request.RequestID)
// Get session should fail
_, err = manager.GetSession(request.RequestID)
if !errors.Is(err, v3.ErrSessionNotFound) {
t.Fatalf("get session failed: %v", err)
}
// Closing the original session should return that the socket is already closed (by the session unregistration)
err = session.Close()
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
t.Fatalf("session should've closed without issue: %v", err)
}
}
func TestGetSession_Empty(t *testing.T) {
log := zerolog.Nop()
manager := v3.NewSessionManager(&noopMetrics{}, &log, ingress.DialUDPAddrPort, cfdsession.NewLimiter(0))
_, err := manager.GetSession(testRequestID)
if !errors.Is(err, v3.ErrSessionNotFound) {
t.Fatalf("get session find no session: %v", err)
}
}
func TestRegisterSessionRateLimit(t *testing.T) {
log := zerolog.Nop()
ctrl := gomock.NewController(t)
sessionLimiterMock := mocks.NewMockLimiter(ctrl)
sessionLimiterMock.EXPECT().Acquire("udp").Return(cfdsession.ErrTooManyActiveSessions)
sessionLimiterMock.EXPECT().Release().Times(0)
manager := v3.NewSessionManager(&noopMetrics{}, &log, ingress.DialUDPAddrPort, sessionLimiterMock)
request := v3.UDPSessionRegistrationDatagram{
RequestID: testRequestID,
Dest: netip.MustParseAddrPort("127.0.0.1:5000"),
Traced: false,
IdleDurationHint: 5 * time.Second,
Payload: nil,
}
_, err := manager.RegisterSession(&request, &noopEyeball{})
require.ErrorIs(t, err, v3.ErrSessionRegistrationRateLimited)
}