diff --git a/dialog_client.go b/dialog_client.go index bf93051..54aff7a 100644 --- a/dialog_client.go +++ b/dialog_client.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "sync" + "time" "github.com/emiago/sipgo/sip" "github.com/icholy/digest" @@ -174,6 +175,10 @@ type AnswerOptions struct { Password string } +var ( + WaitAnswerForceCancelErr = errors.New("Context cancel forced") +) + // WaitAnswer waits for success response or returns ErrDialogResponse in case non 2xx // Canceling context while waiting 2xx will send Cancel request. It will block until 1xx provisional is not received // If Canceling succesfull context.Canceled error is returned @@ -182,7 +187,6 @@ type AnswerOptions struct { // - any internal in case waiting answer failed for different reasons func (s *DialogClientSession) WaitAnswer(ctx context.Context, opts AnswerOptions) error { tx, inviteRequest := s.inviteTx, s.InviteRequest - var r *sip.Response var err error for { @@ -197,6 +201,11 @@ func (s *DialogClientSession) WaitAnswer(ctx context.Context, opts AnswerOptions // We will wait until transaction timeous out (TimerB) defer tx.Terminate() + if err := context.Cause(ctx); err == WaitAnswerForceCancelErr { + // In case caller wants to force cancelation exit. + return ctx.Err() + } + if s.InviteResponse == nil { select { case r = <-tx.Responses(): @@ -219,7 +228,12 @@ func (s *DialogClientSession) WaitAnswer(ctx context.Context, opts AnswerOptions return fmt.Errorf("cancel failed with non 200. code=%d", res.StatusCode) } - // Wait for 487 or just timeout per TimerB (tx.Done) + // Wait for 487 or just timeout + // https://datatracker.ietf.org/doc/html/rfc3261#section-9.1 + // UAC canceling a request cannot rely on receiving a 487 (Request + // Terminated) response for the original request, as an RFC 2543- + // compliant UAS will not generate such a response. If there is no + // final response for the original request in 64*T1 seconds loop_487: for { select { @@ -231,6 +245,8 @@ func (s *DialogClientSession) WaitAnswer(ctx context.Context, opts AnswerOptions break loop_487 case <-tx.Done(): return tx.Err() + case <-time.After(64 * sip.T1): + break loop_487 } }