2018-09-04 22:55:52 +00:00
|
|
|
|
package websocket
|
|
|
|
|
|
|
|
|
|
import (
|
2023-09-19 01:58:15 +00:00
|
|
|
|
"crypto/tls"
|
|
|
|
|
"encoding/json"
|
2023-09-19 02:01:38 +00:00
|
|
|
|
"gitea.watsonlabs.net/watsonb8/fayec/message"
|
|
|
|
|
"gitea.watsonlabs.net/watsonb8/fayec/transport"
|
2018-09-04 22:55:52 +00:00
|
|
|
|
"github.com/gorilla/websocket"
|
2018-09-07 12:32:26 +00:00
|
|
|
|
"log"
|
2023-09-19 01:58:15 +00:00
|
|
|
|
"net"
|
2018-09-05 12:56:32 +00:00
|
|
|
|
"sync"
|
2018-09-04 22:55:52 +00:00
|
|
|
|
"sync/atomic"
|
|
|
|
|
)
|
|
|
|
|
|
2018-09-04 23:22:43 +00:00
|
|
|
|
const transportName = "websocket"
|
|
|
|
|
|
2018-09-04 22:55:52 +00:00
|
|
|
|
func init() {
|
|
|
|
|
transport.RegisterTransport(&Websocket{})
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 01:58:15 +00:00
|
|
|
|
// Websocket represents an websocket transport for the faye protocol
|
2018-09-04 22:55:52 +00:00
|
|
|
|
type Websocket struct {
|
2018-09-11 10:09:30 +00:00
|
|
|
|
topts *transport.Options
|
2018-09-06 15:59:56 +00:00
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
connMu sync.Mutex
|
|
|
|
|
conn *websocket.Conn
|
2018-09-05 12:56:32 +00:00
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
advice atomic.Value //type message.Advise //todo move to dispatcher
|
2018-09-06 12:28:54 +00:00
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
stopCh chan error //todo replace wth context
|
2018-09-06 13:29:49 +00:00
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
onMsg func(msg *message.Message)
|
|
|
|
|
onError func(err error)
|
|
|
|
|
onTransportDown func(err error)
|
|
|
|
|
onTransportUp func()
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ transport.Transport = (*Websocket)(nil)
|
|
|
|
|
|
2023-09-19 01:58:15 +00:00
|
|
|
|
// Init initializes the transport with the provided options
|
2018-09-11 10:09:30 +00:00
|
|
|
|
func (w *Websocket) Init(endpoint string, options *transport.Options) error {
|
2018-09-04 22:55:52 +00:00
|
|
|
|
var (
|
2018-09-24 14:13:57 +00:00
|
|
|
|
err error
|
2018-09-04 22:55:52 +00:00
|
|
|
|
)
|
2018-09-11 10:09:30 +00:00
|
|
|
|
w.topts = options
|
2018-09-24 14:13:57 +00:00
|
|
|
|
|
2018-09-06 13:29:49 +00:00
|
|
|
|
w.stopCh = make(chan error)
|
2018-09-11 11:30:07 +00:00
|
|
|
|
w.conn, _, err = websocket.DefaultDialer.Dial(endpoint, options.Headers)
|
2023-09-19 01:58:15 +00:00
|
|
|
|
err = w.conn.UnderlyingConn().(*tls.Conn).NetConn().(*net.TCPConn).SetKeepAlive(true)
|
2018-09-04 22:55:52 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-09-24 14:13:57 +00:00
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-09-04 22:55:52 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-09-05 12:56:32 +00:00
|
|
|
|
|
2023-09-19 01:58:15 +00:00
|
|
|
|
// Init initializes the transport with the provided options
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) SetOnErrorHandler(onError func(err error)) {
|
|
|
|
|
w.onError = onError
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-05 12:56:32 +00:00
|
|
|
|
func (w *Websocket) readWorker() error {
|
|
|
|
|
for {
|
2018-09-06 12:28:54 +00:00
|
|
|
|
select {
|
2018-09-06 13:29:49 +00:00
|
|
|
|
case err := <-w.stopCh:
|
|
|
|
|
return err
|
2018-09-06 12:28:54 +00:00
|
|
|
|
default:
|
2023-09-19 01:58:15 +00:00
|
|
|
|
var payload []message.Message
|
|
|
|
|
_, data, err := w.conn.ReadMessage()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = json.Unmarshal(data, &payload)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//dispatch
|
|
|
|
|
msg := &payload[0]
|
|
|
|
|
w.onMsg(msg)
|
2018-09-24 14:13:57 +00:00
|
|
|
|
|
2018-09-05 12:56:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 01:58:15 +00:00
|
|
|
|
// name returns the transport name (websocket)
|
2018-09-04 22:55:52 +00:00
|
|
|
|
func (w *Websocket) Name() string {
|
2018-09-04 23:22:43 +00:00
|
|
|
|
return transportName
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
2018-09-05 12:56:32 +00:00
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) SendMessage(m *message.Message) error {
|
2018-09-06 15:59:56 +00:00
|
|
|
|
w.connMu.Lock()
|
|
|
|
|
defer w.connMu.Unlock()
|
2018-09-05 12:56:32 +00:00
|
|
|
|
var payload []message.Message
|
|
|
|
|
payload = append(payload, *m)
|
2018-09-07 16:31:52 +00:00
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
again: //todo move this to scheduler
|
2018-09-07 16:31:52 +00:00
|
|
|
|
err := w.conn.WriteJSON(payload)
|
|
|
|
|
if websocket.IsUnexpectedCloseError(err) {
|
|
|
|
|
advise := w.advice.Load().(*message.Advise)
|
|
|
|
|
if advise.Reconnect == message.ReconnectNone {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
//reconnect
|
|
|
|
|
//we should re-register again subscriptions again
|
|
|
|
|
goto again
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2018-09-05 12:56:32 +00:00
|
|
|
|
}
|
2018-09-04 22:55:52 +00:00
|
|
|
|
|
2023-09-19 01:58:15 +00:00
|
|
|
|
// Options return the transport Options
|
2018-09-04 22:55:52 +00:00
|
|
|
|
func (w *Websocket) Options() *transport.Options {
|
2018-09-11 10:09:30 +00:00
|
|
|
|
return w.topts
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
2018-09-05 12:56:32 +00:00
|
|
|
|
|
2023-09-19 01:58:15 +00:00
|
|
|
|
// Handshake initiates a connection negotiation by sending a message to the /meta/handshake channel.
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) Handshake(msg *message.Message) (resp *message.Message, err error) {
|
|
|
|
|
err = w.SendMessage(msg)
|
|
|
|
|
|
2018-09-05 12:56:32 +00:00
|
|
|
|
if err != nil {
|
2018-09-24 14:13:57 +00:00
|
|
|
|
return nil, err
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hsResps []message.Message
|
2018-09-04 23:56:25 +00:00
|
|
|
|
if err = w.conn.ReadJSON(&hsResps); err != nil {
|
2018-09-24 14:13:57 +00:00
|
|
|
|
return nil, err
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
resp = &hsResps[0]
|
|
|
|
|
return resp, nil
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 02:01:38 +00:00
|
|
|
|
// Init is called after a client has discovered the server’s capabilities with a handshake exchange,
|
|
|
|
|
// a connection is established by sending a message to the /meta/connect channel
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) Connect(msg *message.Message) error {
|
|
|
|
|
go func() {
|
|
|
|
|
log.Fatal(w.readWorker())
|
|
|
|
|
}()
|
|
|
|
|
return w.SendMessage(msg)
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) SetOnTransportDownHandler(onTransportDown func(err error)) {
|
|
|
|
|
w.onTransportDown = onTransportDown
|
2018-09-06 12:28:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) SetOnTransportUpHandler(onTransportUp func()) {
|
|
|
|
|
w.onTransportUp = onTransportUp
|
2018-09-06 13:29:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-19 01:58:15 +00:00
|
|
|
|
// Disconnect closes all subscriptions and inform the server to remove any client-related state.
|
|
|
|
|
// any subsequent method call to the client object will result in undefined behaviour.
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) Disconnect(m *message.Message) error {
|
|
|
|
|
w.stopCh <- nil
|
|
|
|
|
close(w.stopCh)
|
|
|
|
|
return w.SendMessage(m)
|
2018-09-04 22:55:52 +00:00
|
|
|
|
}
|
2018-09-06 10:27:19 +00:00
|
|
|
|
|
2018-09-24 14:13:57 +00:00
|
|
|
|
func (w *Websocket) SetOnMessageReceivedHandler(onMsg func(*message.Message)) {
|
|
|
|
|
w.onMsg = onMsg
|
2018-09-06 11:23:53 +00:00
|
|
|
|
}
|