diff --git a/enterprise/media-server/internal/server/handlers.go b/enterprise/media-server/internal/server/handlers.go index 3051dd4fa..5719a3fa0 100644 --- a/enterprise/media-server/internal/server/handlers.go +++ b/enterprise/media-server/internal/server/handlers.go @@ -297,9 +297,16 @@ func (h *Handlers) AgentAnswer(w http.ResponseWriter, r *http.Request) { writeError(w, http.StatusBadRequest, "sdp_answer is required") return } + // When peer_id is omitted (single-agent sessions), fall back to the only + // agent peer attached to the session. Rails doesn't currently surface + // peer_id through ActionCable, so browsers just send the SDP answer. if req.PeerID == "" { - writeError(w, http.StatusBadRequest, "peer_id is required") - return + if only, ok := sess.SoleAgentPeerID(); ok { + req.PeerID = only + } else { + writeError(w, http.StatusBadRequest, "peer_id is required (session has multiple agent peers)") + return + } } if err := sess.SetAgentAnswer(req.PeerID, req.SDPAnswer); err != nil { diff --git a/enterprise/media-server/internal/session/session.go b/enterprise/media-server/internal/session/session.go index 5b6222d66..7d819b2ca 100644 --- a/enterprise/media-server/internal/session/session.go +++ b/enterprise/media-server/internal/session/session.go @@ -204,6 +204,22 @@ func (s *Session) CreateAgentPeer(peerID string, role peer.PeerRole, iceServers return sdpOffer, nil } +// SoleAgentPeerID returns the peer_id when the session has exactly one agent +// peer. Used by the agent-answer handler as a fallback for clients that don't +// track peer ids. +func (s *Session) SoleAgentPeerID() (string, bool) { + s.mu.Lock() + defer s.mu.Unlock() + + if len(s.AgentPeers) != 1 { + return "", false + } + for id := range s.AgentPeers { + return id, true + } + return "", false +} + // SetAgentAnswer sets the agent browser's SDP answer on the specified agent // peer, completing the WebRTC handshake. func (s *Session) SetAgentAnswer(peerID, sdpAnswer string) error {