From 5eeca886e90f85963b334cfb2483310840a4b4a1 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sun, 1 Feb 2026 16:33:21 +0100 Subject: [PATCH] fix: register dialog state read channel before sending 200 OK This patch addresses a race where the 200 OK would be retransmitted forever because the ACK arrived before we started waiting for dialog state changes. The easiest way to reproduce this is to either produce lots of load and get lucky, or introduce a short sleep after sending the 200 OK, causing it to be retransmitted forever (and eventually return an error after a timeout): ```diff diff --git a/dialog_server.go b/dialog_server.go index e9b3a0e..03067f9 100644 --- a/dialog_server.go +++ b/dialog_server.go @@ -314,6 +314,8 @@ func (s *DialogServerSession) WriteResponse(res *sip.Response) error { return err } + time.Sleep(10 * time.Millisecond) + // Wait now for ACK for our 2xx // https://datatracker.ietf.org/doc/html/rfc3261#section-13.3.1.4 ``` --- dialog_server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dialog_server.go b/dialog_server.go index e9b3a0e..aa0a99a 100644 --- a/dialog_server.go +++ b/dialog_server.go @@ -310,6 +310,11 @@ func (s *DialogServerSession) WriteResponse(res *sip.Response) error { } s.setState(sip.DialogStateEstablished) + + // Register dialog state read channel before transmitting 200 OK. This prevents a race + // condition where the ACK is received before we start waiting for it. + readStateCh := s.StateRead() + if err := tx.Respond(res); err != nil { return err } @@ -322,7 +327,6 @@ func (s *DialogServerSession) WriteResponse(res *sip.Response) error { defer timer.Stop() state := sip.DialogStateEstablished - readStateCh := s.StateRead() for state == sip.DialogStateEstablished { select { case <-timer.C: