Skip to content

Commit

Permalink
Some preparation for better client support
Browse files Browse the repository at this point in the history
  • Loading branch information
emiago committed Jan 29, 2023
1 parent 49fc953 commit 1fe088e
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 56 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ certs
*.pcap
/self-signed-root-ca-and-certificates.sh
/example/goproxy
/example/astscale
/example/astscale
/example/auth
/go.work
73 changes: 73 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import (

"github.com/emiago/sipgo/sip"
"github.com/emiago/sipgo/transport"
"github.com/google/uuid"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)

func Init() {
uuid.EnableRandPool()
}

type Client struct {
*UserAgent
log zerolog.Logger
Expand All @@ -32,7 +37,13 @@ func NewClient(ua *UserAgent, options ...ClientOption) (*Client, error) {
}

// TransactionRequest uses transaction layer to send request
// Customizing req can be done via options. NOTE: this overrides default header construction
func (c *Client) TransactionRequest(req *sip.Request, options ...ClientRequestOption) (sip.ClientTransaction, error) {
if len(options) == 0 {
clientRequestBuildReq(c, req)
return c.tx.Request(req)
}

for _, o := range options {
if err := o(c, req); err != nil {
return nil, err
Expand All @@ -42,7 +53,13 @@ func (c *Client) TransactionRequest(req *sip.Request, options ...ClientRequestOp
}

// WriteRequest sends request directly to transport layer
// Customizing req can be done via options same like TransactionRequest
func (c *Client) WriteRequest(req *sip.Request, options ...ClientRequestOption) error {
if len(options) == 0 {
clientRequestBuildReq(c, req)
return c.tp.WriteMsg(req)
}

for _, o := range options {
if err := o(c, req); err != nil {
return err
Expand All @@ -53,6 +70,62 @@ func (c *Client) WriteRequest(req *sip.Request, options ...ClientRequestOption)

type ClientRequestOption func(c *Client, req *sip.Request) error

func clientRequestBuildReq(c *Client, req *sip.Request) error {
// https://www.rfc-editor.org/rfc/rfc3261#section-8.1.1
// A valid SIP request formulated by a UAC MUST, at a minimum, contain
// the following header fields: To, From, CSeq, Call-ID, Max-Forwards,
// and Via;
ClientRequestAddVia(c, req)

from := sip.FromHeader{
DisplayName: c.name,
Address: sip.Uri{
User: c.name,
Host: c.host,
Port: c.port,
UriParams: sip.NewParams(),
Headers: sip.NewParams(),
},
}
req.AppendHeader(&from)

to := sip.ToHeader{
Address: sip.Uri{
Encrypted: req.Recipient.Encrypted,
User: req.Recipient.User,
Host: req.Recipient.Host,
Port: req.Recipient.Port,
UriParams: req.Recipient.UriParams,
Headers: req.Recipient.Headers,
},
}
req.AppendHeader(&to)

uuid, err := uuid.NewRandom()
if err != nil {
return err
}

callid := sip.CallIDHeader(uuid.String())
req.AppendHeader(&callid)

// TODO consider atomic increase cseq within Dialog
cseq := sip.CSeqHeader{
SeqNo: 1,
MethodName: req.Method,
}
req.AppendHeader(&cseq)

maxfwd := sip.MaxForwardsHeader(70)
req.AppendHeader(&maxfwd)

if req.Body() == nil {
req.SetBody(nil)
}

return nil
}

// ClientRequestAddVia is option for adding via header
// Based on proxy setup https://www.rfc-editor.org/rfc/rfc3261.html#section-16.6
func ClientRequestAddVia(c *Client, r *sip.Request) error {
Expand Down
10 changes: 5 additions & 5 deletions example/proxysip/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func setupSipProxy(proxydst string, ip string) *sipgo.Server {
defer clTx.Terminate()

// Keep monitoring transactions, and proxy client responses to server transaction
log.Debug().Str("req", req.Method().String()).Msg("Starting transaction")
log.Debug().Str("req", req.Method.String()).Msg("Starting transaction")
for {
select {

Expand All @@ -159,7 +159,7 @@ func setupSipProxy(proxydst string, ip string) *sipgo.Server {
}

// Early terminate
if req.Method() == sip.BYE {
if req.Method == sip.BYE {
// We will call client Terminate
return
}
Expand All @@ -177,15 +177,15 @@ func setupSipProxy(proxydst string, ip string) *sipgo.Server {
clTx.Cancel()

case err := <-clTx.Errors():
log.Error().Err(err).Str("caller", req.Method().String()).Msg("Client Transaction Error")
log.Error().Err(err).Str("caller", req.Method.String()).Msg("Client Transaction Error")
return

case err := <-tx.Errors():
log.Error().Err(err).Str("caller", req.Method().String()).Msg("Server transaction error")
log.Error().Err(err).Str("caller", req.Method.String()).Msg("Server transaction error")
return

case <-tx.Done():
log.Debug().Str("req", req.Method().String()).Msg("Transaction done")
log.Debug().Str("req", req.Method.String()).Msg("Transaction done")
return
}
}
Expand Down
2 changes: 1 addition & 1 deletion example/proxysip/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func inviteScenario(t testing.TB, client1, client2 fakes.TestConnection, p *pars
continue
}

if req, ok := resReceived.(*sip.Request); ok && req.Method() == sip.ACK {
if req, ok := resReceived.(*sip.Request); ok && req.Method == sip.ACK {
require.Nil(t, err)
t.Log("CLIENT2: Received ACK. Call established")
assert.Equal(t, ackReq.StartLine(), resReceived.StartLine())
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
Expand Down
2 changes: 1 addition & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (srv *Server) handleRequest(req *sip.Request, tx sip.ServerTransaction) {
mid(req)
}

handler := srv.getHandler(req.Method())
handler := srv.getHandler(req.Method)

if handler == nil {
srv.log.Warn().Msg("SIP request handler not found")
Expand Down
2 changes: 1 addition & 1 deletion server_dialog.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (s *ServerDialog) onRequestDialog(r *sip.Request, tx sip.ServerTransaction)
}

func (s *ServerDialog) handleRequestDialog(r *sip.Request, tx sip.ServerTransaction) {
switch r.Method() {
switch r.Method {
// Early state
// case sip.INVITE:

Expand Down
4 changes: 2 additions & 2 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func TestUDPUAS(t *testing.T) {
// Register all handlers
for _, method := range allmethods {
srv.OnRequest(method, func(req *sip.Request, tx sip.ServerTransaction) {
t.Log("New " + req.Method().String())
t.Log("New " + req.Method.String())
// Make all responses
res := sip.NewResponseFromRequest(req, 200, "OK", nil)
tx.Respond(res)
Expand Down Expand Up @@ -230,7 +230,7 @@ func TestTCPUAS(t *testing.T) {
// Register all handlers
for _, method := range allmethods {
srv.OnRequest(method, func(req *sip.Request, tx sip.ServerTransaction) {
t.Log("New " + req.Method().String())
t.Log("New " + req.Method.String())
// Make all responses
res := sip.NewResponseFromRequest(req, 200, "OK", nil)
tx.Respond(res)
Expand Down
1 change: 0 additions & 1 deletion sip/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,6 @@ type GenericHeader struct {
// The name of the header.
HeaderName string
// The contents of the header, including any parameters.
// This is transparent data that is not natively understood by gossip.
Contents string
}

Expand Down
62 changes: 22 additions & 40 deletions sip/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ import (
// Request RFC 3261 - 7.1.
type Request struct {
MessageData
method RequestMethod
recipient *Uri
Method RequestMethod
Recipient *Uri
}

// NewRequest creates base for building sip Request
// No headers are added. AppendHeader should be called to add Headers.
// r.SetBody can be called to set proper ContentLength header
func NewRequest(
method RequestMethod,
recipient *Uri,
sipVersion string,
) *Request {
func NewRequest(method RequestMethod, recipient *Uri, sipVersion string) *Request {
req := &Request{}
// req.startLineWrite = req.StartLineWrite
req.SipVersion = sipVersion
Expand All @@ -30,8 +26,8 @@ func NewRequest(
// headers: make(map[string]Header),
headerOrder: make([]Header, 0),
}
req.method = method
req.recipient = recipient
req.Method = method
req.Recipient = recipient
req.body = nil

return req
Expand All @@ -42,28 +38,14 @@ func (req *Request) Short() string {
return "<nil>"
}

return fmt.Sprintf("request method=%s recipient=%s transport=%s source=%s",
req.Method(),
req.Recipient().String(),
return fmt.Sprintf("request method=%s Recipient=%s transport=%s source=%s",
req.Method,
req.Recipient.String(),
req.Transport(),
req.Source(),
)
}

func (req *Request) Method() RequestMethod {
return req.method
}
func (req *Request) SetMethod(method RequestMethod) {
req.method = method
}

func (req *Request) Recipient() *Uri {
return req.recipient
}
func (req *Request) SetRecipient(recipient *Uri) {
req.recipient = recipient
}

// StartLine returns Request Line - RFC 2361 7.1.
func (req *Request) StartLine() string {
var buffer strings.Builder
Expand All @@ -72,9 +54,9 @@ func (req *Request) StartLine() string {
}

func (req *Request) StartLineWrite(buffer io.StringWriter) {
buffer.WriteString(string(req.Method()))
buffer.WriteString(string(req.Method))
buffer.WriteString(" ")
buffer.WriteString(req.Recipient().String())
buffer.WriteString(req.Recipient.String())
buffer.WriteString(" ")
buffer.WriteString(req.SipVersion)
}
Expand Down Expand Up @@ -103,15 +85,15 @@ func (req *Request) Clone() *Request {
}

func (req *Request) IsInvite() bool {
return req.Method() == INVITE
return req.Method == INVITE
}

func (req *Request) IsAck() bool {
return req.Method() == ACK
return req.Method == ACK
}

func (req *Request) IsCancel() bool {
return req.Method() == CANCEL
return req.Method == CANCEL
}

func (req *Request) Transport() string {
Expand All @@ -126,7 +108,7 @@ func (req *Request) Transport() string {
tp = DefaultProtocol
}

uri := req.Recipient()
uri := req.Recipient
if hdr := req.GetHeader("Route"); hdr != nil {
routeHeader := hdr.(*RouteHeader)
uri = &routeHeader.Address
Expand Down Expand Up @@ -204,7 +186,7 @@ func (req *Request) Destination() string {

}
if uri == nil {
if u := req.Recipient(); u != nil {
if u := req.Recipient; u != nil {
uri = u
} else {
return ""
Expand All @@ -223,16 +205,16 @@ func (req *Request) Destination() string {
// NewAckRequest creates ACK request for 2xx INVITE
// https://tools.ietf.org/html/rfc3261#section-13.2.2.4
func NewAckRequest(inviteRequest *Request, inviteResponse *Response, body []byte) *Request {
recipient := inviteRequest.Recipient()
Recipient := inviteRequest.Recipient
if contact, ok := inviteResponse.Contact(); ok {
// For ws and wss (like clients in browser), don't use Contact
if strings.Index(strings.ToLower(recipient.String()), "transport=ws") == -1 {
recipient = &contact.Address
if strings.Index(strings.ToLower(Recipient.String()), "transport=ws") == -1 {
Recipient = &contact.Address
}
}
ackRequest := NewRequest(
ACK,
recipient,
Recipient,
inviteRequest.SipVersion,
)
CopyHeaders("Via", inviteRequest, ackRequest)
Expand Down Expand Up @@ -284,7 +266,7 @@ func NewAckRequest(inviteRequest *Request, inviteResponse *Response, body []byte
func NewCancelRequest(requestForCancel *Request) *Request {
cancelReq := NewRequest(
CANCEL,
requestForCancel.Recipient(),
requestForCancel.Recipient,
requestForCancel.SipVersion,
)

Expand Down Expand Up @@ -319,8 +301,8 @@ func NewCancelRequest(requestForCancel *Request) *Request {

func cloneRequest(req *Request) *Request {
newReq := NewRequest(
req.Method(),
req.Recipient().Clone(),
req.Method,
req.Recipient.Clone(),
req.SipVersion,
)

Expand Down
8 changes: 8 additions & 0 deletions sip/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,11 @@ func ResolveSelfIP() (net.IP, error) {
}
return nil, errors.New("server not connected to any network")
}

func NonceWrite(buf []byte) {
const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
length := len(letterBytes)
for i := range buf {
buf[i] = letterBytes[rand.Intn(length)]
}
}
2 changes: 1 addition & 1 deletion transaction/server_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (tx *ServerTx) receiveRequest(req *sip.Request) (FsmInput, error) {
}

switch {
case req.Method() == tx.origin.Method():
case req.Method == tx.origin.Method:
return server_input_request, nil
case req.IsAck(): // ACK for non-2xx response
tx.lastAck = req
Expand Down
Loading

0 comments on commit 1fe088e

Please sign in to comment.