mirror of
https://github.com/livekit/livekit.git
synced 2026-09-16 06:22:36 +00:00
StartWebTransport built the node's only HTTP/3 listener from inside AgentService: it minted the TLS config, bound the UDP socket, assembled an h3 mux and returned a stop closure LivekitServer held in agentWTStop. Every other listener is constructed in NewLivekitServer and delegates to a service, and agents are the first consumer of HTTP/3 rather than the last. webtransport.go now carries the transport with no agent references: WebTransportTLS, NewWebTransportServer, NewWebTransportHandler, ListenWebTransport, and WithWebTransportServer/GetWebTransportServer, which put the serving server in the request context so a route that upgrades is a conventional http.Handler instead of a closure over the server. The listener is built in NewLivekitServer beside the prometheus and debug servers, bound in Start with the other listeners, and stopped once doneChan unblocks, so no stop closure is held on LivekitServer. /agent is a route on its mux, as it is on the API mux. UpgradeWebTransport unwraps the ResponseWriter before upgrading. webtransport-go type-asserts it to http3.Settingser and http3.HTTPStreamer without checking and negroni wraps the writer on every chain, so the h3 listener could not carry a middleware chain at all. It now runs the same recovery, api-key auth and path normalization as the TCP chain. AgentService splits into AgentWSService and AgentWTService over the shared AgentHandler, and the endpoint front becomes AgentEndpointService. wire builds all four and threads one endpoint.Registry into the handler and the front. NewAgentHandler takes the config and builds its own ServerInfo, so endpointsConfig and singleAPIKey are set at construction and the psrpc server is registered against a fully formed handler; it previously bound one whose embedded *AgentHandler was still nil. The listener's port and certificate move to a top-level webtransport config block. The listener no longer reads agents.endpoints.disabled, which continues to refuse endpoint registrations. ListenWebTransport binds one socket per bind address, where the h3 listener ignored bind_addresses that every TCP listener honoured. NewWorkerRegisterer takes a variadic WorkerRegisterHandler, each handler reading the request and filling its part of the response and the registration, replacing the single EndpointSettingsFunc threaded through HandleRegister. EndpointRegisterHandler is the endpoint declaration's turn, and endpoint.NegotiateSettings holds the protocol negotiation the OSS and cloud handshakes each carried a copy of. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
312 lines
7.9 KiB
Go
312 lines
7.9 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.
|
|
|
|
//go:build wireinject
|
|
// +build wireinject
|
|
|
|
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/google/wire"
|
|
"github.com/pion/turn/v5"
|
|
"github.com/pkg/errors"
|
|
"github.com/redis/go-redis/v9"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/livekit/protocol/auth"
|
|
"github.com/livekit/protocol/livekit"
|
|
"github.com/livekit/protocol/logger"
|
|
redisLiveKit "github.com/livekit/protocol/redis"
|
|
"github.com/livekit/protocol/rpc"
|
|
"github.com/livekit/protocol/utils"
|
|
"github.com/livekit/protocol/webhook"
|
|
"github.com/livekit/psrpc"
|
|
"github.com/livekit/psrpc/pkg/middleware/otelpsrpc"
|
|
|
|
"github.com/livekit/livekit-server/pkg/agent"
|
|
"github.com/livekit/livekit-server/pkg/agent/endpoint"
|
|
"github.com/livekit/livekit-server/pkg/config"
|
|
"github.com/livekit/livekit-server/pkg/routing"
|
|
"github.com/livekit/livekit-server/pkg/sfu"
|
|
"github.com/livekit/livekit-server/pkg/telemetry"
|
|
)
|
|
|
|
func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*LivekitServer, error) {
|
|
wire.Build(
|
|
getNodeID,
|
|
createRedisClient,
|
|
createStore,
|
|
wire.Bind(new(ServiceStore), new(ObjectStore)),
|
|
createKeyProvider,
|
|
createWebhookNotifier,
|
|
createForwardStats,
|
|
getNodeStatsConfig,
|
|
routing.CreateRouter,
|
|
getLimitConf,
|
|
getAPIConf,
|
|
wire.Bind(new(routing.MessageRouter), new(routing.Router)),
|
|
wire.Bind(new(livekit.RoomService), new(*RoomService)),
|
|
telemetry.NewAnalyticsService,
|
|
createTelemetryService,
|
|
getMessageBus,
|
|
NewIOInfoService,
|
|
wire.Bind(new(IOClient), new(*IOInfoService)),
|
|
rpc.NewEgressClient,
|
|
rpc.NewIngressClient,
|
|
getEgressStore,
|
|
NewEgressLauncher,
|
|
NewEgressService,
|
|
getIngressStore,
|
|
getIngressConfig,
|
|
NewIngressService,
|
|
newSIPClient,
|
|
getSIPStore,
|
|
getSIPConfig,
|
|
NewSIPService,
|
|
NewRoomAllocator,
|
|
NewRoomService,
|
|
NewRTCService,
|
|
NewWHIPService,
|
|
endpoint.NewRegistry,
|
|
NewAgentHandler,
|
|
NewAgentWSService,
|
|
NewAgentWTService,
|
|
NewAgentEndpointService,
|
|
NewAgentDispatchService,
|
|
getAgentConfig,
|
|
agent.NewAgentClient,
|
|
getAgentStore,
|
|
getSignalRelayConfig,
|
|
NewDefaultSignalServer,
|
|
routing.NewSignalClient,
|
|
getRoomConfig,
|
|
routing.NewRoomManagerClient,
|
|
rpc.NewKeepalivePubSub,
|
|
getPSRPCConfig,
|
|
getPSRPCClientParams,
|
|
rpc.NewTopicFormatter,
|
|
rpc.NewTypedRoomClient,
|
|
rpc.NewTypedParticipantClient,
|
|
rpc.NewTypedWHIPParticipantClient,
|
|
rpc.NewTypedAgentDispatchInternalClient,
|
|
NewLocalRoomManager,
|
|
NewTURNAuthHandler,
|
|
getTURNAuthHandlerFunc,
|
|
newInProcessTurnServer,
|
|
utils.NewDefaultTimedVersionGenerator,
|
|
NewLivekitServer,
|
|
)
|
|
return &LivekitServer{}, nil
|
|
}
|
|
|
|
func InitializeRouter(conf *config.Config, currentNode routing.LocalNode) (routing.Router, error) {
|
|
wire.Build(
|
|
createRedisClient,
|
|
getNodeID,
|
|
getMessageBus,
|
|
getSignalRelayConfig,
|
|
getPSRPCConfig,
|
|
getPSRPCClientParams,
|
|
routing.NewSignalClient,
|
|
getRoomConfig,
|
|
routing.NewRoomManagerClient,
|
|
rpc.NewKeepalivePubSub,
|
|
getNodeStatsConfig,
|
|
routing.CreateRouter,
|
|
)
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func getNodeID(currentNode routing.LocalNode) livekit.NodeID {
|
|
return currentNode.NodeID()
|
|
}
|
|
|
|
func createKeyProvider(conf *config.Config) (auth.KeyProvider, error) {
|
|
// prefer keyfile if set
|
|
if conf.KeyFile != "" {
|
|
var otherFilter os.FileMode = 0007
|
|
if st, err := os.Stat(conf.KeyFile); err != nil {
|
|
return nil, err
|
|
} else if st.Mode().Perm()&otherFilter != 0000 {
|
|
return nil, fmt.Errorf("key file others permissions must be set to 0")
|
|
}
|
|
f, err := os.Open(conf.KeyFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
_ = f.Close()
|
|
}()
|
|
decoder := yaml.NewDecoder(f)
|
|
if err = decoder.Decode(conf.Keys); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(conf.Keys) == 0 {
|
|
return nil, errors.New("one of key-file or keys must be provided in order to support a secure installation")
|
|
}
|
|
|
|
return auth.NewFileBasedKeyProviderFromMap(conf.Keys), nil
|
|
}
|
|
|
|
func createWebhookNotifier(conf *config.Config, provider auth.KeyProvider) (webhook.QueuedNotifier, error) {
|
|
wc := conf.WebHook
|
|
|
|
secret := provider.GetSecret(wc.APIKey)
|
|
if secret == "" && len(wc.URLs) > 0 {
|
|
return nil, ErrWebHookMissingAPIKey
|
|
}
|
|
|
|
return webhook.NewDefaultNotifier(wc, provider)
|
|
}
|
|
|
|
func createTelemetryService(notifier webhook.QueuedNotifier, analytics telemetry.AnalyticsService) telemetry.TelemetryService {
|
|
svc := telemetry.NewTelemetryService(notifier, analytics)
|
|
if notifier != nil {
|
|
notifier.RegisterProcessedHook(svc.Webhook)
|
|
}
|
|
return svc
|
|
}
|
|
|
|
func createRedisClient(conf *config.Config) (redis.UniversalClient, error) {
|
|
if !conf.Redis.IsConfigured() {
|
|
return nil, nil
|
|
}
|
|
return redisLiveKit.GetRedisClient(&conf.Redis)
|
|
}
|
|
|
|
func createStore(rc redis.UniversalClient) ObjectStore {
|
|
if rc != nil {
|
|
return NewRedisStore(rc)
|
|
}
|
|
return NewLocalStore()
|
|
}
|
|
|
|
func getMessageBus(rc redis.UniversalClient, psrpcConf rpc.PSRPCConfig) psrpc.MessageBus {
|
|
opts := psrpcConf.BusOptions()
|
|
if rc == nil {
|
|
return psrpc.NewLocalMessageBus(opts...)
|
|
}
|
|
return psrpc.NewRedisMessageBus(rc, opts...)
|
|
}
|
|
|
|
func getEgressStore(s ObjectStore) EgressStore {
|
|
switch store := s.(type) {
|
|
case *RedisStore:
|
|
return store
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func getIngressStore(s ObjectStore) IngressStore {
|
|
switch store := s.(type) {
|
|
case *RedisStore:
|
|
return store
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func getAgentStore(s ObjectStore) AgentStore {
|
|
switch store := s.(type) {
|
|
case *RedisStore:
|
|
return store
|
|
case *LocalStore:
|
|
return store
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func getIngressConfig(conf *config.Config) *config.IngressConfig {
|
|
return &conf.Ingress
|
|
}
|
|
|
|
func newSIPClient(p rpc.ClientParams) (rpc.SIPClient, error) {
|
|
// Do not pass parameters directly, as they set timeout that is too short,
|
|
// and might set retry policy that is not acceptable for SIP methods.
|
|
// Instead, set relevant parameters manually.
|
|
return rpc.NewSIPClientWithParams(rpc.ClientParams{
|
|
Bus: p.Bus,
|
|
ClientOptions: []psrpc.ClientOption{
|
|
rpc.WithClientLogger(p.Logger),
|
|
otelpsrpc.ClientOptions(otelpsrpc.Config{}),
|
|
},
|
|
})
|
|
}
|
|
|
|
func getSIPStore(s ObjectStore) SIPStore {
|
|
switch store := s.(type) {
|
|
case *RedisStore:
|
|
return store
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func getSIPConfig(conf *config.Config) *config.SIPConfig {
|
|
return &conf.SIP
|
|
}
|
|
|
|
func getLimitConf(config *config.Config) config.LimitConfig {
|
|
return config.Limit
|
|
}
|
|
|
|
func getRoomConfig(config *config.Config) config.RoomConfig {
|
|
return config.Room
|
|
}
|
|
|
|
func getSignalRelayConfig(config *config.Config) config.SignalRelayConfig {
|
|
return config.SignalRelay
|
|
}
|
|
|
|
func getPSRPCConfig(config *config.Config) rpc.PSRPCConfig {
|
|
return config.PSRPC
|
|
}
|
|
|
|
func getPSRPCClientParams(config rpc.PSRPCConfig, bus psrpc.MessageBus) rpc.ClientParams {
|
|
return rpc.NewClientParams(config, bus, logger.GetLogger(), rpc.PSRPCMetricsObserver{},
|
|
otelpsrpc.ClientOptions(otelpsrpc.Config{}),
|
|
)
|
|
}
|
|
|
|
func createForwardStats(conf *config.Config) *sfu.ForwardStats {
|
|
if conf.RTC.ForwardStats.SummaryInterval == 0 || conf.RTC.ForwardStats.ReportInterval == 0 || conf.RTC.ForwardStats.ReportWindow == 0 {
|
|
return nil
|
|
}
|
|
return sfu.NewForwardStats(conf.RTC.ForwardStats.SummaryInterval, conf.RTC.ForwardStats.ReportInterval, conf.RTC.ForwardStats.ReportWindow)
|
|
}
|
|
|
|
func newInProcessTurnServer(conf *config.Config, authHandler turn.AuthHandler) (*turn.Server, error) {
|
|
return NewTurnServer(conf, authHandler, false)
|
|
}
|
|
|
|
func getNodeStatsConfig(config *config.Config) config.NodeStatsConfig {
|
|
return config.NodeStats
|
|
}
|
|
|
|
func getAgentConfig(config *config.Config) agent.Config {
|
|
return config.Agents
|
|
}
|
|
|
|
func getAPIConf(config *config.Config) config.APIConfig {
|
|
return config.API
|
|
}
|