Skip to content

Commit

Permalink
Adding Websocket support
Browse files Browse the repository at this point in the history
  • Loading branch information
emiago committed Dec 21, 2022
1 parent 262f20f commit 00e905d
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 13 deletions.
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,29 @@ Lib allows you to write easily client or server or to build up stateful proxies,
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
### UAS/UAC build

```go
ua, _ := sipgo.NewUA() // Build user agent
srv, _ := sipgo.NewServer(ua) // Creating server handle
srv.OnRegister(registerHandler)
client, _ := sipgo.NewClient(ua) // Creating client handle
srv.OnInvite(inviteHandler)
srv.OnAck(ackHandler)
srv.OnCancel(cancelHandler)
srv.OnBye(byeHandler)

// For registrars
// srv.OnRegister(registerHandler)


// Add listeners
srv.Listen("udp", "127.0.0.1:5060")
srv.Listen("tcp", "127.0.0.1:5061")
...
// Start serving
// fire server
srv.Serve()
```



### Server Transaction
Expand All @@ -69,7 +74,7 @@ srv.OnInvite(func(req *sip.Request, tx sip.ServerTransaction) {

```

### Stateless response
### Server stateless response

```go
srv := sipgo.NewServer()
Expand All @@ -82,15 +87,13 @@ srv.OnACK(ackHandler)
```


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

### Client Transaction

**NOTE**: UA needs server handle and listener on same network before sending request


```go
client, _ := sipgo.NewClient(ua) // Creating client handle

// Request is either from server request handler or created
req.SetDestination("10.1.2.3") // Change sip.Request destination
Expand All @@ -109,6 +112,15 @@ select {

```

### Client stateless request

```go
client, _ := sipgo.NewClient(ua) // Creating client handle
req := sip.NewRequest(method, &recipment, "SIP/2.0")
// Send request and forget
client.WriteRequest(req)
```

## Proxy build

Proxy is combination client and server handle.
Expand Down Expand Up @@ -147,7 +159,7 @@ More on documentation you can find on [Go doc](https://pkg.go.dev/github.com/emi
- [x] UDP
- [x] TCP
- [ ] TLS
- [ ] WS
- [x] WS
- [ ] WSS


Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module github.com/emiraganov/sipgo

go 1.18
go 1.19

require (
github.com/gobwas/ws v1.1.0
github.com/prometheus/client_golang v1.12.0
github.com/rs/zerolog v1.26.1
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
Expand All @@ -14,6 +15,8 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
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/kr/pretty v0.2.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down Expand Up @@ -327,6 +333,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
65 changes: 65 additions & 0 deletions server_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package sipgo

import (
"testing"
"time"

"github.com/emiraganov/sipgo/sip"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestWebsocket(t *testing.T) {
ua, _ := NewUA()
// transport.SIPDebug = true
log.Logger = log.Level(zerolog.DebugLevel)

// Build UAS
srv, err := NewServer(ua)
if err != nil {
log.Fatal().Err(err).Msg("Fail to setup dialog server")
}
srv.OnInvite(func(req *sip.Request, tx sip.ServerTransaction) {
t.Log("Invite received")
res := sip.NewResponseFromRequest(req, 200, "OK", nil)
if err := tx.Respond(res); err != nil {
t.Fatal(err)
}
<-tx.Done()
})

srv.Listen("ws", "127.0.0.1:5060")

go func() {
if err := srv.Serve(); err != nil {
log.Error().Err(err).Msg("Fail to serve")
}
}()

// Build UAC
ua, _ = NewUA()
client, err := NewClient(ua)
require.Nil(t, err)

csrv, err := NewServer(ua) // Create server handle
require.Nil(t, err)
csrv.Listen("ws", "127.0.0.2:5060")
go func() {
if err := csrv.Serve(); err != nil {
log.Error().Err(err).Msg("Fail to serve")
}
}()

time.Sleep(2 * time.Second)

req, _, _ := createTestInvite(t, "WS", client.ip.String())
// err = client.WriteRequest(req)
// require.Nil(t, err)
// time.Sleep(2 * time.Second)
tx, err := client.TransactionRequest(req)
require.Nil(t, err)
res := <-tx.Responses()
assert.Equal(t, sip.StatusCode(200), res.StatusCode())
}
27 changes: 26 additions & 1 deletion transport/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package transport

import (
"context"
"errors"
"fmt"
"math/rand"
"net"
Expand All @@ -17,6 +18,10 @@ import (
"github.com/rs/zerolog/log"
)

var (
ErrNetworkExists = errors.New("network is already served")
)

func init() {
rand.Seed(time.Now().UnixNano())
}
Expand Down Expand Up @@ -97,7 +102,7 @@ func (l *Layer) ServeUDP(c net.PacketConn) error {
return transport.ServeConn(c, l.handleMessage)
}

// ServeTCP will listen on udp connection
// ServeTCP will listen on tcp connection
func (l *Layer) ServeTCP(c net.Listener) error {
_, port, err := sip.ParseAddr(c.Addr().String())
if err != nil {
Expand All @@ -110,6 +115,19 @@ func (l *Layer) ServeTCP(c net.Listener) error {
return transport.ServeConn(c, l.handleMessage)
}

// ServeWS will listen on ws connection
func (l *Layer) ServeWS(c net.Listener) error {
_, port, err := sip.ParseAddr(c.Addr().String())
if err != nil {
return err
}

transport := NewWSTransport(c.Addr().String(), parser.NewParser())
l.addTransport(transport, port)

return transport.ServeConn(c, l.handleMessage)
}

// Serve on any network. This function will block
func (l *Layer) Serve(ctx context.Context, network string, addr string) error {
network = strings.ToLower(network)
Expand All @@ -118,6 +136,11 @@ func (l *Layer) Serve(ctx context.Context, network string, addr string) error {
return err
}

_, exists := l.transports[network]
if exists {
return ErrNetworkExists
}

p := parser.NewParser()

var t Transport
Expand All @@ -126,6 +149,8 @@ func (l *Layer) Serve(ctx context.Context, network string, addr string) error {
t = NewUDPTransport(addr, p)
case "tcp":
t = NewTCPTransport(addr, p)
case "ws":
t = NewWSTransport(addr, p)
case "tls":
fallthrough
default:
Expand Down
1 change: 1 addition & 0 deletions transport/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
TransportUDP = "UDP"
TransportTCP = "TCP"
TransportTLS = "TLS"
TransportWS = "WS"
)

// Protocol implements network specific features.
Expand Down
Loading

0 comments on commit 00e905d

Please sign in to comment.