forked from emiago/sipgo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dialog_ua.go
108 lines (91 loc) · 2.66 KB
/
dialog_ua.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package sipgo
import (
"context"
"fmt"
"github.com/emiago/sipgo/sip"
uuid "github.com/satori/go.uuid"
)
// DialogUA defines UserAgent that will be used in controling your dialog.
// It needs client handle for cancelation or sending more subsequent request during dialog
type DialogUA struct {
// Client (required) is used to build and send subsequent request (CANCEL, BYE)
Client *Client
// ContactHDR (required) is used as default one to build request/response.
// You can pass custom on each request, but in dialog it is required to be present
ContactHDR sip.ContactHeader
RewriteContact bool
}
func (c *DialogUA) ReadInvite(inviteReq *sip.Request, tx sip.ServerTransaction) (*DialogServerSession, error) {
cont := inviteReq.Contact()
if cont == nil {
return nil, ErrDialogInviteNoContact
}
// Prebuild already to tag for response as it must be same for all responds
// NewResponseFromRequest will skip this for all 100
uuid, err := uuid.NewV4()
if err != nil {
return nil, fmt.Errorf("generating dialog to tag failed: %w", err)
}
inviteReq.To().Params["tag"] = uuid.String()
id, err := sip.UASReadRequestDialogID(inviteReq)
if err != nil {
return nil, err
}
// do some minimal validation
if inviteReq.CSeq() == nil {
return nil, fmt.Errorf("no CSEQ header present")
}
if inviteReq.Contact() == nil {
return nil, fmt.Errorf("no Contact header present")
}
dtx := &DialogServerSession{
Dialog: Dialog{
ID: id, // this id has already prebuilt tag
InviteRequest: inviteReq,
},
inviteTx: tx,
ua: c,
}
dtx.Init()
// Temporarly fix
if stx, ok := tx.(*sip.ServerTx); ok {
stx.OnTerminate(func(key string) {
state := dtx.LoadState()
if state < sip.DialogStateEstablished {
// It is canceled if transaction died before answer
dtx.setState(sip.DialogStateEnded)
}
})
}
return dtx, nil
}
func (ua *DialogUA) 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)
}
for _, h := range headers {
req.AppendHeader(h)
}
return ua.WriteInvite(ctx, req)
}
func (c *DialogUA) WriteInvite(ctx context.Context, inviteReq *sip.Request, options ...ClientRequestOption) (*DialogClientSession, error) {
cli := c.Client
if inviteReq.Contact() == nil {
// Set contact only if not exists
inviteReq.AppendHeader(&c.ContactHDR)
}
tx, err := cli.TransactionRequest(ctx, inviteReq, options...)
if err != nil {
return nil, err
}
dtx := &DialogClientSession{
Dialog: Dialog{
InviteRequest: inviteReq,
},
ua: c,
inviteTx: tx,
}
dtx.Init()
return dtx, nil
}