// 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 signalling import ( "fmt" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/logger" "github.com/livekit/protocol/utils" "github.com/livekit/psrpc" "github.com/livekit/livekit-server/pkg/routing" "github.com/livekit/livekit-server/pkg/rtc/types" "google.golang.org/protobuf/proto" ) var _ ParticipantSignaller = (*signallerAsync)(nil) type SignallerAsyncParams struct { Logger logger.Logger Participant types.LocalParticipant OnHandshakeOpened func() } type signallerAsync struct { params SignallerAsyncParams *signallerAsyncBase } func NewSignallerAsync(params SignallerAsyncParams) ParticipantSignaller { return &signallerAsync{ params: params, signallerAsyncBase: newSignallerAsyncBase(signallerAsyncBaseParams{ Logger: params.Logger, OnHandshakeOpened: params.OnHandshakeOpened, }), } } func (s *signallerAsync) WriteMessage(msg proto.Message) error { if msg == nil { return nil } getMessageType := func(msg proto.Message) string { messageType := "unknown" if typed, ok := msg.(*livekit.SignalResponse); ok { messageType = fmt.Sprintf("%T", typed.Message) } return messageType } if s.params.Participant.IsDisconnected() { s.params.Logger.Debugw( "could not send message to participant, participant disconnected", "messageType", getMessageType(msg), ) return nil } // a signal connection opens with a join response, or with a reconnect response when // it is resumed or migrated in. The client reads that first message as the handshake, // so nothing may go out ahead of it. isHandshake := false isSdp := false if typed, ok := msg.(*livekit.SignalResponse); !ok { s.params.Logger.Warnw( "unknown message type", nil, "messageType", fmt.Sprintf("%T", msg), ) } else { isHandshake = typed.GetJoin() != nil || typed.GetReconnect() != nil isSdp = typed.GetOffer() != nil || typed.GetAnswer() != nil } if !isHandshake && (!s.params.Participant.IsReady() || s.HandshakePending()) { logFunc := s.params.Logger.Debugw if isSdp { // a dropped SDP leaves the negotiation waiting for the peer until the // negotiation state machine recovers it, so make it visible logFunc = func(msg string, keysAndValues ...any) { s.params.Logger.Infow(msg, keysAndValues...) } } logFunc( "dropping message, connection has not opened yet", "messageType", getMessageType(msg), ) return nil } sink := s.GetResponseSink() if sink == nil { s.params.Logger.Debugw( "could not send message to participant, no sink", "messageType", getMessageType(msg), ) return nil } err := sink.WriteMessage(msg) if err != nil { if utils.ErrorIsOneOf(err, psrpc.Canceled, routing.ErrChannelClosed) { s.params.Logger.Debugw( "could not send message to participant", "error", err, "messageType", getMessageType(msg), ) return nil } else { s.params.Logger.Warnw( "could not send message to participant", err, "messageType", getMessageType(msg), ) return err } } else { if isHandshake { // the connection is open, hold nothing back any more s.OpenHandshake() } s.params.Logger.Debugw("sent signal response", "response", logger.Proto(msg)) } return nil }