From e9ba07ad48d0bc76006d5974089d9e63fa757d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Rokita?= Date: Sun, 18 Jan 2026 20:51:53 +0100 Subject: [PATCH] fix(outbound): send SIP CANCEL for early dialogs when closing Previously, Close() only sent BYE for answered calls but dropped state for ringing calls without sending any SIP signaling. This left the far-end device ringing indefinitely. Per RFC 3261 Section 9.1, a UAC should send CANCEL to terminate an early dialog. This adds sendCancel() and modifies Close() to use it when invite exists but inviteOk is nil. --- pkg/sip/outbound.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/sip/outbound.go b/pkg/sip/outbound.go index fb1946c9..47ee4dba 100644 --- a/pkg/sip/outbound.go +++ b/pkg/sip/outbound.go @@ -1039,6 +1039,24 @@ func (c *sipOutbound) sendBye(ctx context.Context) { sendAndACK(ctx, c, r) } +func (c *sipOutbound) sendCancel(ctx context.Context) { + ctx = context.WithoutCancel(ctx) + if c.invite == nil { + return + } + ctx, span := Tracer.Start(ctx, "sip.outbound.sendCancel") + defer span.End() + r := sip.NewCancelRequest(c.invite) + r.AppendHeader(sip.NewHeader("User-Agent", "LiveKit")) + if c.getHeaders != nil { + for k, v := range c.getHeaders(nil) { + r.AppendHeader(sip.NewHeader(k, v)) + } + } + _ = c.WriteRequest(r) + c.drop() +} + func (c *sipOutbound) drop() { c.invite = nil c.inviteOk = nil @@ -1123,6 +1141,8 @@ func (c *sipOutbound) Close(ctx context.Context) { defer c.mu.Unlock() if c.inviteOk != nil { c.sendBye(ctx) + } else if c.invite != nil { + c.sendCancel(ctx) } else { c.drop() }