Skip to content

Commit

Permalink
fix: dialog small refactor and expose dialog ID matching
Browse files Browse the repository at this point in the history
  • Loading branch information
emiago committed Apr 28, 2024
1 parent 40e650a commit 2ba54fb
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 59 deletions.
39 changes: 23 additions & 16 deletions dialog_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ func (s *DialogClient) loadDialog(id string) *DialogClientSession {
return t
}

func (s *DialogClient) MatchDialogRequest(req *sip.Request) (*DialogClientSession, error) {
id, err := sip.UACReadRequestDialogID(req)
if err != nil {
return nil, errors.Join(err, ErrDialogOutsideDialog)
}

dt := s.loadDialog(id)
if dt == nil {
return nil, ErrDialogDoesNotExists
}
return dt, nil
}

// NewDialogClient provides handle for managing UAC dialog
// Contact hdr must be provided for correct invite
// In case handling different transports you should have multiple instances per transport
Expand All @@ -59,7 +72,7 @@ func (e ErrDialogResponse) Error() string {
// Invite sends INVITE request and creates early dialog session.
// You need to call WaitAnswer after for establishing dialog
// For passing custom Invite request use WriteInvite
func (dc *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []byte, headers ...sip.Header) (*DialogClientSession, error) {
func (c *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []byte, headers ...sip.Header) (*DialogClientSession, error) {
req := sip.NewRequest(sip.INVITE, recipient)
if body != nil {
req.SetBody(body)
Expand All @@ -68,13 +81,13 @@ func (dc *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []by
for _, h := range headers {
req.AppendHeader(h)
}
return dc.WriteInvite(ctx, req)
return c.WriteInvite(ctx, req)
}

func (dc *DialogClient) WriteInvite(ctx context.Context, inviteRequest *sip.Request) (*DialogClientSession, error) {
cli := dc.c
func (c *DialogClient) WriteInvite(ctx context.Context, inviteRequest *sip.Request) (*DialogClientSession, error) {
cli := c.c

inviteRequest.AppendHeader(&dc.contactHDR)
inviteRequest.AppendHeader(&c.contactHDR)

// TODO passing client transaction options is now hidden
tx, err := cli.TransactionRequest(ctx, inviteRequest)
Expand All @@ -92,23 +105,17 @@ func (dc *DialogClient) WriteInvite(ctx context.Context, inviteRequest *sip.Requ
ctx: ctx,
cancel: cancel,
},
dc: dc,
dc: c,
inviteTx: tx,
}

return dtx, nil
}

func (dc *DialogClient) ReadBye(req *sip.Request, tx sip.ServerTransaction) error {
callid := req.CallID()
from := req.From()
to := req.To()

id := sip.MakeDialogID(callid.Value(), from.Params["tag"], to.Params["tag"])

dt := dc.loadDialog(id)
if dt == nil {
return fmt.Errorf("callid=%q: %w", callid.Value(), ErrDialogDoesNotExists)
func (c *DialogClient) ReadBye(req *sip.Request, tx sip.ServerTransaction) error {
dt, err := c.MatchDialogRequest(req)
if err != nil {
return err
}

dt.setState(sip.DialogStateEnded)
Expand Down
38 changes: 18 additions & 20 deletions dialog_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ func (s *DialogServer) loadDialog(id string) *DialogServerSession {
return t
}

func (s *DialogServer) MatchDialogRequest(req *sip.Request) (*DialogServerSession, error) {
id, err := sip.UASReadRequestDialogID(req)
if err != nil {
return nil, errors.Join(ErrDialogOutsideDialog, err)
}

dt := s.loadDialog(id)
if dt == nil {
return nil, ErrDialogDoesNotExists
}
return dt, nil
}

// NewDialogServer provides handle for managing UAS dialog
// Contact hdr must be provided for responses
// Client is needed for termination dialog session
Expand Down Expand Up @@ -68,36 +81,21 @@ func (s *DialogServer) ReadInvite(req *sip.Request, tx sip.ServerTransaction) (*

// ReadAck should read from your OnAck handler
func (s *DialogServer) ReadAck(req *sip.Request, tx sip.ServerTransaction) error {
id, err := sip.MakeDialogIDFromRequest(req)
dt, err := s.MatchDialogRequest(req)
if err != nil {
return errors.Join(ErrDialogOutsideDialog, err)
}

dt := s.loadDialog(id)
if dt == nil {
// res := sip.NewResponseFromRequest(req, sip.StatusCallTransactionDoesNotExists, "Call/Transaction Does Not Exist", nil)
// if err := tx.Respond(res); err != nil {
// return err
// }
return ErrDialogDoesNotExists
return err
}

dt.setState(sip.DialogStateConfirmed)

// Acks are normally just absorbed, but in case of proxy
// they still need to be passed
return nil
}

// ReadAck should read from your OnBye handler
// ReadBye should read from your OnBye handler
func (s *DialogServer) ReadBye(req *sip.Request, tx sip.ServerTransaction) error {
id, err := sip.MakeDialogIDFromRequest(req)
dt, err := s.MatchDialogRequest(req)
if err != nil {
return err
}

dt := s.loadDialog(id)
if dt == nil {
// https://datatracker.ietf.org/doc/html/rfc3261#section-15.1.2
// If the BYE does not
// match an existing dialog, the UAS core SHOULD generate a 481
Expand All @@ -106,7 +104,7 @@ func (s *DialogServer) ReadBye(req *sip.Request, tx sip.ServerTransaction) error
// if err := tx.Respond(res); err != nil {
// return err
// }
return ErrDialogDoesNotExists
return err
}
// Make sure this is bye for this dialog
if req.CSeq().SeqNo != (dt.lastCSeqNo + 1) {
Expand Down
49 changes: 28 additions & 21 deletions sip/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,37 +63,44 @@ func DefaultPort(transport string) int {
}
}

// MakeDialogIDFromMessage creates dialog ID of message.
// Use UASReadRequestDialogID UACReadRequestDialogID for more specific
// returns error if callid or to tag or from tag does not exists
func MakeDialogIDFromRequest(msg *Request) (string, error) {
var callID, innerID, externalID string = "", "", ""
if err := getDialogIDFromMessage(msg, &callID, &innerID, &externalID); err != nil {
return UASReadRequestDialogID(msg)
}

// MakeDialogIDFromResponse creates dialog ID of message.
// returns error if callid or to tag or from tag does not exists
func MakeDialogIDFromResponse(msg *Response) (string, error) {
var callID, toTag, fromTag string = "", "", ""
if err := getDialogIDFromMessage(msg, &callID, &toTag, &fromTag); err != nil {
return "", err
}
return MakeDialogID(callID, innerID, externalID), nil
return MakeDialogID(callID, toTag, fromTag), nil
}

func MakeDialogIDFromResponse(msg *Response) (string, error) {
var callID, innerID, externalID string = "", "", ""
// TODO switching this fields make no sense?
if err := getDialogIDFromMessage(msg, &callID, &innerID, &externalID); err != nil {
// UASReadRequestDialogID creates dialog ID of message if receiver has UAS role.
// returns error if callid or to tag or from tag does not exists
func UASReadRequestDialogID(msg *Request) (string, error) {
var callID, toTag, fromTag string = "", "", ""
if err := getDialogIDFromMessage(msg, &callID, &toTag, &fromTag); err != nil {
return "", err
}
return MakeDialogID(callID, innerID, externalID), nil
return MakeDialogID(callID, toTag, fromTag), nil
}

// MakeDialogIDFromMessage creates dialog ID of message.
// UACReadRequestDialogID creates dialog ID of message if receiver has UAC role.
// returns error if callid or to tag or from tag does not exists
// Deprecated! Will be removed
func MakeDialogIDFromMessage(msg Message) (string, error) {
switch m := msg.(type) {
case *Request:
return MakeDialogIDFromRequest(m)
case *Response:
return MakeDialogIDFromResponse(m)
func UACReadRequestDialogID(msg *Request) (string, error) {
var callID, toTag, fromTag string = "", "", ""
if err := getDialogIDFromMessage(msg, &callID, &toTag, &fromTag); err != nil {
return "", err
}
return "", fmt.Errorf("unknown message format")
return MakeDialogID(callID, fromTag, toTag), nil
}

func getDialogIDFromMessage(msg Message, callId, innerId, externalId *string) error {
func getDialogIDFromMessage(msg Message, callId, toHeaderTag, fromHeaderTag *string) error {
callID := msg.CallID()
if callID == nil {
return fmt.Errorf("missing Call-ID header")
Expand All @@ -119,11 +126,11 @@ func getDialogIDFromMessage(msg Message, callId, innerId, externalId *string) er
return fmt.Errorf("missing tag param in From header")
}
*callId = string(*callID)
*innerId = toTag
*externalId = fromTag
*toHeaderTag = toTag
*fromHeaderTag = fromTag
return nil
}

func MakeDialogID(callID, innerID, externalID string) string {
return strings.Join([]string{callID, innerID, externalID}, "__")
return strings.Join([]string{callID, innerID, externalID}, TxSeperator)
}
5 changes: 3 additions & 2 deletions sip/transaction_server_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"github.com/rs/zerolog"
)

type FSMfunc func() FSMfunc

type ServerTx struct {
commonTx
lastAck *Request
Expand Down Expand Up @@ -245,6 +243,9 @@ func (tx *ServerTx) initFSM() {
tx.fsmMu.Unlock()
}

func (tx *ServerTx) State() {
}

func (tx *ServerTx) delete() {
tx.closeOnce.Do(func() {
tx.mu.Lock()
Expand Down

0 comments on commit 2ba54fb

Please sign in to comment.