diff --git a/dialog_server_session.go b/dialog_server_session.go index fd8628a..d17daa6 100644 --- a/dialog_server_session.go +++ b/dialog_server_session.go @@ -5,6 +5,7 @@ package diago import ( "context" + "errors" "fmt" "sync/atomic" "time" @@ -160,10 +161,83 @@ func (d *DialogServerSession) answerSession(rtpSess *media.RTPSession) error { if state == sip.DialogStateConfirmed { return nil } + if state == sip.DialogStateEnded { + return fmt.Errorf("dialog ended on ack") + } } } } +// AnswerLate does answer with Late offer. +func (d *DialogServerSession) AnswerLate() error { + sess, err := d.createMediaSessionConf(d.mediaConf) + if err != nil { + return err + } + rtpSess := media.NewRTPSession(sess) + localSDP := sess.LocalSDP() + + d.mu.Lock() + d.initRTPSessionUnsafe(sess, rtpSess) + // Close RTP session + d.onCloseUnsafe(func() { + if err := rtpSess.Close(); err != nil { + log.Error().Err(err).Msg("Closing session") + } + }) + d.mu.Unlock() + + if err := d.RespondSDP(localSDP); err != nil { + return err + } + // Wait ACK + // It is expected that on ReadACK SDP will be updated + for { + select { + case <-time.After(10 * time.Second): + return fmt.Errorf("no ACK received") + case state := <-d.StateRead(): + if state == sip.DialogStateConfirmed { + // Must be called after media and reader writer is setup + return rtpSess.MonitorBackground() + } + if state == sip.DialogStateEnded { + return fmt.Errorf("dialog ended on ack") + } + } + } +} + +func (d *DialogServerSession) ReadAck(req *sip.Request, tx sip.ServerTransaction) error { + // Check do we have some session + err := func() error { + d.mu.Lock() + defer d.mu.Unlock() + sess := d.mediaSession + if sess == nil { + return nil + } + contentType := req.ContentType() + if contentType == nil { + return nil + } + body := req.Body() + if body != nil && contentType.Value() == "application/sdp" { + // This is Late offer response + if err := sess.RemoteSDP(body); err != nil { + return err + } + } + return nil + }() + if err != nil { + e := d.Hangup(d.Context()) + return errors.Join(err, e) + } + + return d.DialogServerSession.ReadAck(req, tx) +} + func (d *DialogServerSession) Hangup(ctx context.Context) error { state := d.LoadState() if state == sip.DialogStateConfirmed { diff --git a/digest_auth.go b/digest_auth.go index 82e70a9..29e81f0 100644 --- a/digest_auth.go +++ b/digest_auth.go @@ -119,8 +119,9 @@ func (s *DigestAuthServer) AuthorizeDialog(d *DialogServerSession, auth DigestAu res, err := s.AuthorizeRequest(req, auth) if err == nil && res.StatusCode != 200 { err = fmt.Errorf("not authorized") + return errors.Join(err, d.WriteResponse(res)) } - return errors.Join(err, d.WriteResponse(res)) + return errors.Join(err, nil) } func generateNonce() (string, error) { diff --git a/media/rtp_session.go b/media/rtp_session.go index 52127fd..1f61ab8 100644 --- a/media/rtp_session.go +++ b/media/rtp_session.go @@ -296,9 +296,9 @@ func (s *RTPSession) Monitor() error { // MonitorBackground is helper to keep monitoring in background // MUST Be called after session REMOTE SDP is parsed -func (s *RTPSession) MonitorBackground() { +func (s *RTPSession) MonitorBackground() error { if s.Sess.Raddr == nil || s.Sess.rtcpRaddr == nil { - panic("raddr of RTP is not present. You must call this after RemoteSDP is parsed") + return fmt.Errorf("raddr of RTP is not present. Is RemoteSDP called. Monitor RTP Session failed") } go func() { @@ -342,6 +342,7 @@ func (s *RTPSession) MonitorBackground() { } } }() + return nil } func (s *RTPSession) readRTCP() error {