Skip to content

Commit

Permalink
client and server seperated
Browse files Browse the repository at this point in the history
  • Loading branch information
emiago committed Oct 7, 2022
1 parent 5ba84ae commit 25a66ab
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 105 deletions.
53 changes: 36 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ To find out more about performance check the latest results:
(Contributions are welcome, I would place your results here)
## Usage

Lib allows you to write easily stateful proxies, registrar or any sip routing.
Lib allows you to write easily client or server or to build up stateful proxies, registrar or any sip routing.
Writing in GO we are not limited to handle SIP requests/responses in many ways, or to integrate and scale with any external services (databases, caches...).


### UAS build

```go
srv := sipgo.NewServer()
ua, _ := sipgo.NewUA() // Build user agent
srv, _ := sipgo.NewServer(ua) // Creating server handle
srv.OnRegister(registerHandler)
srv.OnInvite(inviteHandler)
srv.OnAck(ackHandler)
Expand All @@ -31,6 +35,7 @@ srv.Listen("tcp", "127.0.0.1:5061")
// Start serving
srv.Serve()
```


### Server Transaction

Expand All @@ -56,13 +61,32 @@ srv.OnInvite(func(req *sip.Request, tx sip.ServerTransaction) {

```

### Stateless response

```go
srv := sipgo.NewServer()
...
func ackHandler(req *sip.Request, tx sip.ServerTransaction) {
res := sip.NewResponseFromRequest(req, code, reason, body)
srv.WriteResponse(res)
}
srv.OnACK(ackHandler)
```


### UAC build
```go
ua, _ := sipgo.NewUA() // Build user agent
client, _ := sipgo.NewClient(ua) // Creating client handle
```

### Client Transaction

```go

// Request is either from server request handler or created
req.SetDestination("10.1.2.3") // Change sip.Request destination
tx, err := srv.TransactionRequest(req) // Send request and get client transaction handle
tx, err := client.TransactionRequest(req) // Send request and get client transaction handle

defer tx.Terminate() // Client Transaction must be terminated for cleanup
...
Expand All @@ -77,25 +101,18 @@ select {

```

### Stateless response
## Proxy build

```go
srv := sipgo.NewServer()
...
func ackHandler(req *sip.Request, tx sip.ServerTransaction) {
res := sip.NewResponseFromRequest(req, code, reason, body)
srv.WriteResponse(res)
}
srv.OnACK(ackHandler)
```
Proxy is combination client and server handle.
Checkout `/example` directory for examples how to build simple stateful proxy.

### Dialogs
### Dialogs (experiment)

ServerDialog is extended type of Server with Dialog support.
More features may come.
`ServerDialog` is extended type of Server with Dialog support.
For now this is in experiment.

```go
srv, err := sipgo.NewServerDialog()
srv, err := sipgo.NewServerDialog(ua)
...
srv.OnDialog(func(d sip.Dialog) {
switch d.State {
Expand All @@ -110,6 +127,8 @@ srv.OnDialog(func(d sip.Dialog) {

```

`ClientDialog` TODO...

## Documentation
More on documentation you can find on [Go doc](https://pkg.go.dev/github.com/emiraganov/sipgo)

Expand Down
84 changes: 52 additions & 32 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import (
type Client struct {
*UserAgent
log zerolog.Logger

AddViaHeader bool
AddRecordRoute bool
}

type ClientOption func(c *Client) error
Expand All @@ -36,47 +33,70 @@ func NewClient(ua *UserAgent, options ...ClientOption) (*Client, error) {
}

// TransactionRequest uses transaction layer to send request
func (c *Client) TransactionRequest(req *sip.Request) (sip.ClientTransaction, error) {
c.updateRequest(req)
func (c *Client) TransactionRequest(req *sip.Request, options ...ClientRequestOption) (sip.ClientTransaction, error) {
for _, o := range options {
if err := o(c, req); err != nil {
return nil, err
}
}
return c.tx.Request(req)
}

// WriteRequest sends request directly to transport layer
func (c *Client) WriteRequest(req *sip.Request) error {
return c.tp.WriteMsg(req)
}

func (c *Client) updateRequest(r *sip.Request) {
// We handle here only INVITE and BYE
// https://www.rfc-editor.org/rfc/rfc3261.html#section-16.6
if c.AddViaHeader {
if via, exists := r.Via(); exists {
newvia := via.Clone()
newvia.Host = c.host
newvia.Port = c.port
r.PrependHeader(newvia)
type ClientRequestOption func(c *Client, req *sip.Request) error

if via.Params.Has("rport") {
h, p, _ := net.SplitHostPort(r.Source())
via.Params.Add("rport", p)
via.Params.Add("received", h)
}
// ClientRequestAddVia is option for adding via header
// https://www.rfc-editor.org/rfc/rfc3261.html#section-16.6
func ClientRequestAddVia(c *Client, r *sip.Request) error {
if via, exists := r.Via(); exists {
newvia := via.Clone()
newvia.Host = c.host
newvia.Port = c.port
r.PrependHeader(newvia)

if via.Params.Has("rport") {
h, p, _ := net.SplitHostPort(r.Source())
via.Params.Add("rport", p)
via.Params.Add("received", h)
}
}
return nil
}

if c.AddRecordRoute {
rr := &sip.RecordRouteHeader{
Address: sip.Uri{
Host: c.host,
Port: c.port,
UriParams: sip.HeaderParams{
// Transport must be provided as well
// https://datatracker.ietf.org/doc/html/rfc5658
"transport": transport.NetworkToLower(r.Transport()),
"lr": "",
},
// ClientRequestAddRecordRoute is option for adding record route header
func ClientRequestAddRecordRoute(c *Client, r *sip.Request) error {
rr := &sip.RecordRouteHeader{
Address: sip.Uri{
Host: c.host,
Port: c.port,
UriParams: sip.HeaderParams{
// Transport must be provided as well
// https://datatracker.ietf.org/doc/html/rfc5658
"transport": transport.NetworkToLower(r.Transport()),
"lr": "",
},
}
},
}

r.PrependHeader(rr)
r.PrependHeader(rr)
return nil
}

// ClientResponseRemoveVia is needed when handling client transaction response, where previously used in
// TransactionRequest with ClientRequestAddVia
func ClientResponseRemoveVia(c *Client, r *sip.Response) {
if via, exists := r.Via(); exists {
if via.Host == c.host {
// In case it is multipart Via remove only one
if via.Next != nil {
via.Remove()
} else {
r.RemoveHeader("Via")
}
}
}
}
5 changes: 3 additions & 2 deletions example/dialog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (h *Handler) route(req *sip.Request, tx sip.ServerTransaction) {
}

// Start client transaction and relay our request
clTx, err := h.c.TransactionRequest(req)
clTx, err := h.c.TransactionRequest(req, sipgo.ClientRequestAddVia, sipgo.ClientRequestAddRecordRoute)
if err != nil {
log.Error().Err(err).Msg("RequestWithContext failed")
reply(tx, req, 500, "")
Expand All @@ -108,7 +108,8 @@ func (h *Handler) route(req *sip.Request, tx sip.ServerTransaction) {
return
}
res.SetDestination(req.Source())
if err := h.s.TransactionReply(tx, res); err != nil {
sipgo.ClientResponseRemoveVia(h.c, res)
if err := tx.Respond(res); err != nil {
log.Error().Err(err).Msg("ResponseHandler transaction respond failed")
}

Expand Down
9 changes: 3 additions & 6 deletions example/proxysip/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ func setupSipProxy(proxydst string, ip string) *sipgo.Server {
log.Fatal().Err(err).Msg("Fail to setup client handle")
}

client.AddViaHeader = true // Adds via header before shiping request
client.AddRecordRoute = true // Adds record route header before shiping request
srv.RemoveViaHeader = true

registry := NewRegistry()

var reply = func(tx sip.ServerTransaction, req *sip.Request, code sip.StatusCode, reason string) {
Expand Down Expand Up @@ -130,7 +126,7 @@ func setupSipProxy(proxydst string, ip string) *sipgo.Server {

req.SetDestination(dst)
// Start client transaction and relay our request
clTx, err := client.TransactionRequest(req)
clTx, err := client.TransactionRequest(req, sipgo.ClientRequestAddVia, sipgo.ClientRequestAddRecordRoute)
if err != nil {
log.Error().Err(err).Msg("RequestWithContext failed")
reply(tx, req, 500, "")
Expand All @@ -148,7 +144,8 @@ func setupSipProxy(proxydst string, ip string) *sipgo.Server {
return
}
res.SetDestination(req.Source())
if err := srv.TransactionReply(tx, res); err != nil {
sipgo.ClientResponseRemoveVia(client, res) // For now this needs to be called manually
if err := tx.Respond(res); err != nil {
log.Error().Err(err).Msg("ResponseHandler transaction respond failed")
}

Expand Down
48 changes: 0 additions & 48 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,38 +133,11 @@ func (srv *Server) handleRequest(req *sip.Request, tx sip.ServerTransaction) {
}
}

// TransactionReply is wrapper for calling tx.Respond
// it handles removing Via header by default
func (srv *Server) TransactionReply(tx sip.ServerTransaction, res *sip.Response) error {
srv.updateResponse(res)
return tx.Respond(res)
}

// WriteResponse will proxy message to transport layer. Use it in stateless mode
func (srv *Server) WriteResponse(r *sip.Response) error {
return srv.tp.WriteMsg(r)
}

func (srv *Server) updateResponse(r *sip.Response) {
if srv.RemoveViaHeader {
srv.RemoveVia(r)
}
}

// RemoveVia can be used in case of sending response.
func (srv *Server) RemoveVia(r *sip.Response) {
if via, exists := r.Via(); exists {
if via.Host == srv.host {
// In case it is multipart Via remove only one
if via.Next != nil {
via.Remove()
} else {
r.RemoveHeader("Via")
}
}
}
}

// Shutdown gracefully shutdowns SIP server
func (srv *Server) shutdown() {
// stop transaction layer
Expand Down Expand Up @@ -286,24 +259,3 @@ func (srv *Server) onTransportMessage(m sip.Message) {
func (srv *Server) TransportLayer() *transport.Layer {
return srv.tp
}

// func (srv *Server) OnDialog(f func(d Dialog)) {
// dialogs := make(map[string]Dialog)

// srv.responseMiddlewares = append(srv.responseMiddlewares, func(r *sip.Response) {
// if r.IsInvite() {

// }

// sip.MakeDialogIDFromMessage()
// })

// srv.requestMiddlewares = append(srv.requestMiddlewares, func(r *sip.Request) {
// if r.IsInvite() {

// }

// sip.MakeDialogIDFromMessage()
// })

// }

0 comments on commit 25a66ab

Please sign in to comment.