mirror of
https://github.com/livekit/livekit.git
synced 2026-09-19 14:15:24 +00:00
getMessageBus built the bus from the redis client alone, so there was no way to reach the gzip settings psrpc v0.7.6 added at the bus boundary. Take rpc.PSRPCConfig, which the wire graph already provides, and pass its bus options to both the redis and the local bus. Compression is off by default. A peer on an older psrpc cannot decode a compressed payload and drops it silently, so egress, ingress, SIP and agent workers all have to be upgraded before quality is raised.
250 lines
8.3 KiB
Go
250 lines
8.3 KiB
Go
// Copyright 2023 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/urfave/cli/v3"
|
|
|
|
"github.com/livekit/livekit-server/pkg/config/configtest"
|
|
)
|
|
|
|
func TestConfig_UnmarshalKeys(t *testing.T) {
|
|
conf, err := NewConfig("", true, nil, nil)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, conf.unmarshalKeys("key1: secret1"))
|
|
require.Equal(t, "secret1", conf.Keys["key1"])
|
|
}
|
|
|
|
func TestConfig_DefaultsKept(t *testing.T) {
|
|
const content = `room:
|
|
empty_timeout: 10`
|
|
conf, err := NewConfig(content, true, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, conf.Room.AutoCreate)
|
|
require.Equal(t, uint32(10), conf.Room.EmptyTimeout)
|
|
}
|
|
|
|
func TestConfig_SignalMessageSizeLimitDefaults(t *testing.T) {
|
|
conf, err := NewConfig("", true, nil, nil)
|
|
require.NoError(t, err)
|
|
// both default to 2 MiB when not specified
|
|
require.Equal(t, int64(2<<20), conf.Limit.SignalMessageSizeLimit)
|
|
require.Equal(t, int64(2<<20), conf.Limit.AgentSignalMessageSizeLimit)
|
|
}
|
|
|
|
func TestConfig_SignalMessageSizeLimitOverride(t *testing.T) {
|
|
const content = `limit:
|
|
signal_message_size_limit: 1024
|
|
agent_signal_message_size_limit: 0`
|
|
conf, err := NewConfig(content, true, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1024), conf.Limit.SignalMessageSizeLimit)
|
|
// 0 explicitly disables the limit
|
|
require.Equal(t, int64(0), conf.Limit.AgentSignalMessageSizeLimit)
|
|
}
|
|
|
|
func TestConfig_PSRPCCompressionDefaults(t *testing.T) {
|
|
conf, err := NewConfig("", true, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, conf.PSRPC.Compression.Quality)
|
|
require.Equal(t, 1024, conf.PSRPC.Compression.Threshold)
|
|
require.Equal(t, 0, conf.PSRPC.Compression.MaxDecompressedSize)
|
|
}
|
|
|
|
func TestConfig_PSRPCCompressionOverride(t *testing.T) {
|
|
const content = `psrpc:
|
|
compression:
|
|
quality: 6
|
|
max_decompressed_size: 4096`
|
|
conf, err := NewConfig(content, true, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 6, conf.PSRPC.Compression.Quality)
|
|
require.Equal(t, 4096, conf.PSRPC.Compression.MaxDecompressedSize)
|
|
require.Equal(t, 1024, conf.PSRPC.Compression.Threshold)
|
|
require.Equal(t, 3, conf.PSRPC.MaxAttempts)
|
|
}
|
|
|
|
func TestConfig_UnknownKeys(t *testing.T) {
|
|
const content = `unknown: 10
|
|
room:
|
|
empty_timeout: 10`
|
|
_, err := NewConfig(content, true, nil, nil)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestGeneratedFlags(t *testing.T) {
|
|
generatedFlags, err := GenerateCLIFlags(nil, false)
|
|
require.NoError(t, err)
|
|
|
|
c := &cli.Command{}
|
|
c.Name = "test"
|
|
c.Flags = append(c.Flags, generatedFlags...)
|
|
|
|
c.Set("rtc.use_ice_lite", "true")
|
|
c.Set("redis.address", "localhost:6379")
|
|
c.Set("prometheus.port", "9999")
|
|
c.Set("rtc.allow_tcp_fallback", "true")
|
|
c.Set("rtc.reconnect_on_publication_error", "true")
|
|
c.Set("rtc.reconnect_on_subscription_error", "false")
|
|
|
|
conf, err := NewConfig("", true, c, nil)
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, conf.RTC.UseICELite)
|
|
require.Equal(t, "localhost:6379", conf.Redis.Address)
|
|
require.Equal(t, uint32(9999), conf.Prometheus.Port)
|
|
|
|
require.NotNil(t, conf.RTC.AllowTCPFallback)
|
|
require.True(t, *conf.RTC.AllowTCPFallback)
|
|
|
|
require.NotNil(t, conf.RTC.ReconnectOnPublicationError)
|
|
require.True(t, *conf.RTC.ReconnectOnPublicationError)
|
|
|
|
require.NotNil(t, conf.RTC.ReconnectOnSubscriptionError)
|
|
require.False(t, *conf.RTC.ReconnectOnSubscriptionError)
|
|
}
|
|
|
|
func TestYAMLTag(t *testing.T) {
|
|
require.NoError(t, configtest.CheckYAMLTags(Config{}))
|
|
}
|
|
|
|
func TestClampTURNTTLSeconds(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in int
|
|
want int
|
|
changed bool
|
|
}{
|
|
{"negative falls back to default", -1, DefaultTURNTTLSeconds, true},
|
|
{"large negative falls back to default", -1 << 40, DefaultTURNTTLSeconds, true},
|
|
{"zero falls back to default", 0, DefaultTURNTTLSeconds, true},
|
|
{"in range preserved", 600, 600, false},
|
|
{"max preserved", TURNMaxTTLSeconds, TURNMaxTTLSeconds, false},
|
|
{"over max clamps to max", TURNMaxTTLSeconds + 1, TURNMaxTTLSeconds, true},
|
|
{"overflowing value clamps to max", 1<<62 + 1, TURNMaxTTLSeconds, true},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
got, changed := ClampTURNTTLSeconds(c.in)
|
|
require.Equal(t, c.want, got)
|
|
require.Equal(t, c.changed, changed)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizeTURNTTLs(t *testing.T) {
|
|
conf, err := NewConfig("", true, nil, nil)
|
|
require.NoError(t, err)
|
|
|
|
conf.TURN.TTLSeconds = -5
|
|
conf.RTC.TURNServers = []TURNServer{
|
|
{Host: "over", TTL: TURNMaxTTLSeconds + 100},
|
|
{Host: "negative", TTL: -1},
|
|
{Host: "default", TTL: 0},
|
|
{Host: "ok", TTL: 600},
|
|
}
|
|
|
|
conf.NormalizeTURNTTLs()
|
|
|
|
// embedded TTL <= 0 falls back to the 5m default
|
|
require.Equal(t, DefaultTURNTTLSeconds, conf.TURN.TTLSeconds)
|
|
// external TTLs: only the upper bound is capped at load; 0/negative keep their
|
|
// "use the default" meaning and are resolved when credentials are generated
|
|
require.Equal(t, TURNMaxTTLSeconds, conf.RTC.TURNServers[0].TTL)
|
|
require.Equal(t, -1, conf.RTC.TURNServers[1].TTL)
|
|
require.Equal(t, 0, conf.RTC.TURNServers[2].TTL)
|
|
require.Equal(t, 600, conf.RTC.TURNServers[3].TTL)
|
|
}
|
|
|
|
func TestNewConfigNormalizesTURNTTL(t *testing.T) {
|
|
const content = `turn:
|
|
ttl_seconds: -10`
|
|
conf, err := NewConfig(content, true, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, DefaultTURNTTLSeconds, conf.TURN.TTLSeconds)
|
|
}
|
|
|
|
func writeSecretFile(t *testing.T, content string) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), "secret")
|
|
require.NoError(t, os.WriteFile(path, []byte(content), 0o600))
|
|
return path
|
|
}
|
|
|
|
func TestLoadTURNSecrets(t *testing.T) {
|
|
t.Run("empty secret file is rejected", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", SecretFile: writeSecretFile(t, "")}}
|
|
require.ErrorIs(t, conf.LoadTURNSecrets(), ErrTURNSecretEmpty)
|
|
})
|
|
|
|
t.Run("whitespace-only secret file is rejected", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", SecretFile: writeSecretFile(t, " \n\t ")}}
|
|
require.ErrorIs(t, conf.LoadTURNSecrets(), ErrTURNSecretEmpty)
|
|
})
|
|
|
|
t.Run("valid secret file is loaded and trimmed", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", SecretFile: writeSecretFile(t, " topsecret\n")}}
|
|
require.NoError(t, conf.LoadTURNSecrets())
|
|
require.Equal(t, "topsecret", conf.RTC.TURNServers[0].Secret)
|
|
})
|
|
|
|
t.Run("inline whitespace secret without static creds is rejected", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", Secret: " "}}
|
|
require.ErrorIs(t, conf.LoadTURNSecrets(), ErrTURNServerNoCredentials)
|
|
})
|
|
|
|
t.Run("no credentials at all is rejected", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h"}}
|
|
require.ErrorIs(t, conf.LoadTURNSecrets(), ErrTURNServerNoCredentials)
|
|
})
|
|
|
|
t.Run("static credentials are accepted", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", Username: "u", Credential: "c"}}
|
|
require.NoError(t, conf.LoadTURNSecrets())
|
|
})
|
|
|
|
t.Run("partial static credentials are rejected", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", Username: "u"}}
|
|
require.ErrorIs(t, conf.LoadTURNSecrets(), ErrTURNServerNoCredentials)
|
|
})
|
|
|
|
t.Run("inline secret takes precedence and is trimmed", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", Secret: " inline ", SecretFile: writeSecretFile(t, "fromfile")}}
|
|
require.NoError(t, conf.LoadTURNSecrets())
|
|
require.Equal(t, "inline", conf.RTC.TURNServers[0].Secret)
|
|
})
|
|
|
|
t.Run("blank inline secret falls back to secret file", func(t *testing.T) {
|
|
conf := &Config{}
|
|
conf.RTC.TURNServers = []TURNServer{{Host: "h", Secret: " ", SecretFile: writeSecretFile(t, "fromfile")}}
|
|
require.NoError(t, conf.LoadTURNSecrets())
|
|
require.Equal(t, "fromfile", conf.RTC.TURNServers[0].Secret)
|
|
})
|
|
}
|