diff --git a/dialog.go b/dialog.go index 1334b0a..f02359e 100644 --- a/dialog.go +++ b/dialog.go @@ -10,7 +10,7 @@ import ( var ( ErrDialogOutsideDialog = errors.New("Call/Transaction outside dialog") - ErrDialogDoesNotExists = errors.New("Call/Transaction Does Not Exist") + ErrDialogDoesNotExists = errors.New("Dialog Does Not Exist") ErrDialogInviteNoContact = errors.New("No Contact header") ErrDialogCanceled = errors.New("Dialog canceled") ) diff --git a/dialog_client.go b/dialog_client.go index 2189875..819cbca 100644 --- a/dialog_client.go +++ b/dialog_client.go @@ -50,7 +50,8 @@ func (s *DialogClient) matchDialogRequest(req *sip.Request) (*DialogClientSessio } // NewDialogClient provides handle for managing UAC dialog -// Contact hdr must be provided for correct invite +// Contact hdr is default to be provided for correct invite. It is not used if you provided hdr as part of request, +// but contact hdr must be present so this makes sure correct dialog is established. // In case handling different transports you should have multiple instances per transport func NewDialogClient(client *Client, contactHDR sip.ContactHeader) *DialogClient { s := &DialogClient{ @@ -70,6 +71,7 @@ func (e ErrDialogResponse) Error() string { } // Invite sends INVITE request and creates early dialog session. +// This is actually not yet dialog (ID is empty) // You need to call WaitAnswer after for establishing dialog // For passing custom Invite request use WriteInvite func (c *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []byte, headers ...sip.Header) (*DialogClientSession, error) { @@ -87,9 +89,11 @@ func (c *DialogClient) Invite(ctx context.Context, recipient sip.Uri, body []byt func (c *DialogClient) WriteInvite(ctx context.Context, inviteRequest *sip.Request) (*DialogClientSession, error) { cli := c.c - inviteRequest.AppendHeader(&c.contactHDR) + if inviteRequest.Contact() == nil { + // Set contact only if not exists + inviteRequest.AppendHeader(&c.contactHDR) + } - // TODO passing client transaction options is now hidden tx, err := cli.TransactionRequest(ctx, inviteRequest) if err != nil { return nil, err @@ -407,7 +411,7 @@ func digestTransactionRequest(ctx context.Context, client *Client, req *sip.Requ // newByeRequestUAC creates bye request from established dialog // https://datatracker.ietf.org/doc/html/rfc3261#section-15.1.1 -// NOTE: it does not copy Via header. This is left to transport or caller to enforce +// NOTE: it does not copy Via header neither increases CSEQ. This is left to dialog transaction request func newByeRequestUAC(inviteRequest *sip.Request, inviteResponse *sip.Response, body []byte) *sip.Request { recipient := &inviteRequest.Recipient cont := inviteResponse.Contact() diff --git a/dialog_integration_test.go b/dialog_integration_test.go index a11ae45..93753a6 100644 --- a/dialog_integration_test.go +++ b/dialog_integration_test.go @@ -166,7 +166,7 @@ func TestIntegrationDialogBrokenUAC(t *testing.T) { defer cancel() uasContact := sip.ContactHeader{ - Address: sip.Uri{User: "test", Host: "127.0.0.200", Port: 5099}, + Address: sip.Uri{User: "test", Host: "127.0.0.201", Port: 5099}, } dialogSrv := NewDialogServer(cli, uasContact) @@ -207,7 +207,7 @@ func TestIntegrationDialogBrokenUAC(t *testing.T) { cli, _ := NewClient(ua) contactHDR := sip.ContactHeader{ - Address: sip.Uri{User: "test", Host: "127.0.0.200", Port: 5088}, + Address: sip.Uri{User: "test", Host: "127.0.0.201", Port: 5088}, } dialogCli := NewDialogClient(cli, contactHDR) @@ -227,7 +227,7 @@ func TestIntegrationDialogBrokenUAC(t *testing.T) { tx.Respond(sip.NewResponseFromRequest(req, sip.StatusInternalServerError, "", nil)) }) // INVITE - t.Log("UAC: INVITE") + t.Log("UAC: INVITE ", uasContact.Address.String()) sess, err := dialogCli.Invite(context.TODO(), uasContact.Address, nil) require.NoError(t, err) defer sess.Close() @@ -249,7 +249,7 @@ func TestIntegrationDialogBrokenUAC(t *testing.T) { t.Run("UAS ACK Error", func(t *testing.T) { // INVITE - t.Log("UAC: INVITE") + t.Log("UAC: INVITE ", uasContact.Address.String()) sess, err := dialogCli.Invite(context.TODO(), uasContact.Address, nil) require.NoError(t, err) defer sess.Close() diff --git a/dialog_server.go b/dialog_server.go index c91c7e5..d400c71 100644 --- a/dialog_server.go +++ b/dialog_server.go @@ -42,7 +42,7 @@ func (s *DialogServer) matchDialogRequest(req *sip.Request) (*DialogServerSessio } // NewDialogServer provides handle for managing UAS dialog -// Contact hdr must be provided for responses +// Contact hdr is default that is provided for responses. // Client is needed for termination dialog session // In case handling different transports you should have multiple instances per transport func NewDialogServer(client *Client, contactHDR sip.ContactHeader) *DialogServer { @@ -89,7 +89,7 @@ func (s *DialogServer) ReadInvite(req *sip.Request, tx sip.ServerTransaction) (* inviteTx: tx, s: s, } - + s.dialogs.Store(id, dtx) return dtx, nil } @@ -247,8 +247,11 @@ func (s *DialogServerSession) RespondSDP(sdp []byte) error { func (s *DialogServerSession) WriteResponse(res *sip.Response) error { tx := s.inviteTx - // Must add contact header - res.AppendHeader(&s.s.contactHDR) + if res.Contact() == nil { + // Add our default contact header + res.AppendHeader(&s.s.contactHDR) + } + s.Dialog.InviteResponse = res // Do we have cancel in meantime @@ -285,10 +288,7 @@ func (s *DialogServerSession) WriteResponse(res *sip.Response) error { return fmt.Errorf("ID do not match. Invite request has changed headers?") } - // We need to make dialog present as ACK can land immediately after - s.s.dialogs.Store(id, s) s.setState(sip.DialogStateEstablished) - if err := tx.Respond(res); err != nil { // We could also not delete this as Close will handle cleanup s.s.dialogs.Delete(id) @@ -317,7 +317,6 @@ func (s *DialogServerSession) Bye(ctx context.Context) error { } // This is tricky - defer s.Close() // Delete our dialog always defer s.inviteTx.Terminate() // Terminates INVITE in all cases // https://datatracker.ietf.org/doc/html/rfc3261#section-15