|
@@ -1,218 +0,0 @@
|
|
|
-// package web_socket -- реализация высокоуровнего веб-сокета для работы десктопа
|
|
|
|
|
-package web_socket
|
|
|
|
|
-
|
|
|
|
|
-import (
|
|
|
|
|
- "encoding/json"
|
|
|
|
|
- "fmt"
|
|
|
|
|
- "log"
|
|
|
|
|
- "net/url"
|
|
|
|
|
- "os"
|
|
|
|
|
- "sync"
|
|
|
|
|
- "time"
|
|
|
|
|
-
|
|
|
|
|
- "github.com/gorilla/websocket"
|
|
|
|
|
-
|
|
|
|
|
- "wartank/pkg/components/safebool"
|
|
|
|
|
- "wartank/pkg/types"
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-const (
|
|
|
|
|
- strWebSocket = "web_socket"
|
|
|
|
|
- TypeMsgBin = 2
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-// WebSocket -- реализация высокоуровнего веб-сокета для работы десктопа
|
|
|
|
|
-type WebSocket struct {
|
|
|
|
|
- kern types.IKernel
|
|
|
|
|
- slog types.ISlog
|
|
|
|
|
- url string
|
|
|
|
|
- isConnect *safebool.SafeBool
|
|
|
|
|
- ws *websocket.Conn
|
|
|
|
|
- block sync.RWMutex
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// NewWebSocket -- возвращает новый веб-сокет
|
|
|
|
|
-func NewWebSocket(kern types.IKernel) (*WebSocket, error) {
|
|
|
|
|
- log.Println("NewWebSocket()")
|
|
|
|
|
- if kern == nil {
|
|
|
|
|
- return nil, fmt.Errorf("NewWebSocket(): IKernel == nil")
|
|
|
|
|
- }
|
|
|
|
|
- url := os.Getenv("SERVER_URL")
|
|
|
|
|
- if url == "" {
|
|
|
|
|
- return nil, fmt.Errorf("NewWebSocket(): env SERVER_URL not set")
|
|
|
|
|
- }
|
|
|
|
|
- sf := &WebSocket{
|
|
|
|
|
- kern: kern,
|
|
|
|
|
- slog: kern.Slog(),
|
|
|
|
|
- url: url,
|
|
|
|
|
- isConnect: safebool.NewSafeBool(),
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- sf.connect()
|
|
|
|
|
- go sf.close()
|
|
|
|
|
- return sf, nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Подключает веб-сокет к серверу
|
|
|
|
|
-func (sf *WebSocket) connect() {
|
|
|
|
|
- log.Println("WebSocket.connect()")
|
|
|
|
|
- fnConnect := func() {
|
|
|
|
|
- u := url.URL{Scheme: "ws", Host: sf.url, Path: "/api/ws"}
|
|
|
|
|
- strUrl := u.String()
|
|
|
|
|
- log.Printf("WebSocket.connect(): wait connect to %q\n", strUrl)
|
|
|
|
|
- var err error
|
|
|
|
|
- sf.ws, _, err = websocket.DefaultDialer.Dial(strUrl, nil)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- log.Printf("WebSocket.connect(): in dial, err=\n\t%v\n", err)
|
|
|
|
|
- time.Sleep(time.Second * 2)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- sf.isConnect.Set()
|
|
|
|
|
- log.Println("WebSocket.connect(): ok")
|
|
|
|
|
- }
|
|
|
|
|
- for !sf.isConnect.Get() {
|
|
|
|
|
- select {
|
|
|
|
|
- case <-sf.kern.CtxApp().Done():
|
|
|
|
|
- return
|
|
|
|
|
- default:
|
|
|
|
|
- fnConnect()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Read -- потокобезопасное чтение топика сервера
|
|
|
|
|
-func (sf *WebSocket) Read(topic string) (map[string]string, error) {
|
|
|
|
|
- sf.block.Lock()
|
|
|
|
|
- defer sf.block.Unlock()
|
|
|
|
|
- dictResp, err := sf.read(topic)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, fmt.Errorf("WebSocket.Read(): in read, err=\n\t%w", err)
|
|
|
|
|
- }
|
|
|
|
|
- return dictResp, nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Скрытая потоко-небезопасна функция
|
|
|
|
|
-func (sf *WebSocket) read(topic string) (dictResp map[string]string, err error) {
|
|
|
|
|
- var binResp []byte
|
|
|
|
|
- for {
|
|
|
|
|
- dictReq := make(map[string]string)
|
|
|
|
|
- dictReq["topic"] = topic
|
|
|
|
|
- binReq, err := json.Marshal(dictReq)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, fmt.Errorf("WebSocket.read(): in marshall topic(%q), err=\n\t%w", topic, err)
|
|
|
|
|
- }
|
|
|
|
|
- err = sf.ws.WriteMessage(TypeMsgBin, binReq)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- sf.slog.Errorf("WebSocket.read(): in write msg, err=\n\t%v\n", err)
|
|
|
|
|
- sf.ws.Close()
|
|
|
|
|
- sf.isConnect.Reset()
|
|
|
|
|
- sf.connect()
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _, binResp, err = sf.ws.ReadMessage()
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- sf.slog.Errorf("WebSocket.read(): in read msg, err=\n\t%v\n", err)
|
|
|
|
|
- sf.ws.Close()
|
|
|
|
|
- sf.isConnect.Reset()
|
|
|
|
|
- sf.connect()
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
- dictResp = make(map[string]string)
|
|
|
|
|
- err = json.Unmarshal(binResp, &dictResp)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, fmt.Errorf("WebSocket.read(): in unmarshal binResp, err=\n\t%w", err)
|
|
|
|
|
- }
|
|
|
|
|
- return dictResp, nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Write -- потокобезопасная запись топика
|
|
|
|
|
-func (sf *WebSocket) Write(topic string, dictReq map[string]string) error {
|
|
|
|
|
- sf.block.Lock()
|
|
|
|
|
- defer sf.block.Unlock()
|
|
|
|
|
- err := sf.write(topic, dictReq)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return fmt.Errorf("WebSocket.Write(): in write, err=\n\t%w", err)
|
|
|
|
|
- }
|
|
|
|
|
- return nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Скрытая потоко-небезопасна функция
|
|
|
|
|
-func (sf *WebSocket) write(topic string, dictReq map[string]string) error {
|
|
|
|
|
- dictReq["topic"] = topic
|
|
|
|
|
- binData, err := json.Marshal(dictReq)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return fmt.Errorf("WebSocket.write(): in marshal msg, err=\n\t%w", err)
|
|
|
|
|
- }
|
|
|
|
|
- for {
|
|
|
|
|
- err = sf.ws.WriteMessage(TypeMsgBin, binData)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- sf.slog.Errorf("WebSocket.write(): in write msg, err=\n\t%v\n", err)
|
|
|
|
|
- sf.isConnect.Reset()
|
|
|
|
|
- sf.ws.Close()
|
|
|
|
|
- sf.connect()
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// IsConnect -- потокобезопасный признак подключенности сервера
|
|
|
|
|
-func (sf *WebSocket) IsConnect() bool {
|
|
|
|
|
- sf.block.RLock()
|
|
|
|
|
- defer sf.block.RUnlock()
|
|
|
|
|
- return sf.isConnect.Get()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Call -- потокобезопасный вызов удалённого топика
|
|
|
|
|
-func (sf *WebSocket) Call(topic string, dictReq map[string]string) (dictResp map[string]string, err error) {
|
|
|
|
|
- sf.block.Lock()
|
|
|
|
|
- defer sf.block.Unlock()
|
|
|
|
|
- var binResp []byte
|
|
|
|
|
- for {
|
|
|
|
|
- dictReq["topic"] = topic
|
|
|
|
|
- binReq, err := json.Marshal(dictReq)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, fmt.Errorf("WebSocket.Call(): in marshall topic(%q), err=\n\t%w", topic, err)
|
|
|
|
|
- }
|
|
|
|
|
- err = sf.ws.WriteMessage(TypeMsgBin, binReq)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- sf.slog.Errorf("WebSocket.Call(): in write msg, err=\n\t%v\n", err)
|
|
|
|
|
- sf.ws.Close()
|
|
|
|
|
- sf.isConnect.Reset()
|
|
|
|
|
- sf.connect()
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _, binResp, err = sf.ws.ReadMessage()
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- sf.slog.Errorf("WebSocket.Call(): in read msg, err=\n\t%v\n", err)
|
|
|
|
|
- sf.ws.Close()
|
|
|
|
|
- sf.isConnect.Reset()
|
|
|
|
|
- sf.connect()
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
- dictResp = make(map[string]string)
|
|
|
|
|
- err = json.Unmarshal(binResp, &dictResp)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, fmt.Errorf("WebSocket.Call(): in unmarshal binResp, err=\n\t%w", err)
|
|
|
|
|
- }
|
|
|
|
|
- return dictResp, nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Потокобезопасное ожидание закрытия в отдельном потоке
|
|
|
|
|
-func (sf *WebSocket) close() {
|
|
|
|
|
- <-sf.kern.Done()
|
|
|
|
|
- sf.block.Lock()
|
|
|
|
|
- defer sf.block.Unlock()
|
|
|
|
|
- if !sf.isConnect.Get() {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- sf.isConnect.Reset()
|
|
|
|
|
- sf.ws.Close()
|
|
|
|
|
-}
|
|
|