github.com/godaddy-x/freego@v1.0.156/node/node_websocket_client.go (about)

     1  package node
     2  
     3  import (
     4  	"github.com/godaddy-x/freego/ex"
     5  	"github.com/godaddy-x/freego/utils"
     6  	"github.com/godaddy-x/freego/zlog"
     7  	"golang.org/x/net/websocket"
     8  	"net/http"
     9  	"time"
    10  )
    11  
    12  type TokenAuth struct {
    13  	Token  string
    14  	Secret string
    15  }
    16  
    17  type WsClient struct {
    18  	Origin      string
    19  	Addr        string
    20  	Path        string
    21  	auth        TokenAuth
    22  	conn        *websocket.Conn
    23  	AuthCall    func() (string, string, error)
    24  	ReceiveCall func(message []byte) (interface{}, error) // 如响应数据为nil,则不回复服务端
    25  }
    26  
    27  type Ping struct {
    28  	HealthCheck string `json:"healthCheck"`
    29  }
    30  
    31  func authReq(path string, requestObj interface{}, secret string, encrypted ...bool) ([]byte, error) {
    32  	if len(path) == 0 || requestObj == nil {
    33  		return nil, ex.Throw{Msg: "params invalid"}
    34  	}
    35  	jsonData, err := utils.JsonMarshal(requestObj)
    36  	if err != nil {
    37  		return nil, ex.Throw{Msg: "request data JsonMarshal invalid"}
    38  	}
    39  	jsonBody := &JsonResp{
    40  		Code:  http.StatusOK,
    41  		Data:  jsonData,
    42  		Time:  utils.UnixSecond(),
    43  		Nonce: utils.RandNonce(),
    44  		Plan:  0,
    45  	}
    46  	if len(encrypted) > 0 && encrypted[0] {
    47  		d, err := utils.AesEncrypt(jsonBody.Data.([]byte), secret, utils.AddStr(jsonBody.Nonce, jsonBody.Time))
    48  		if err != nil {
    49  			return nil, ex.Throw{Msg: "request data AES encrypt failed"}
    50  		}
    51  		jsonBody.Data = d
    52  		jsonBody.Plan = 1
    53  	} else {
    54  		d := utils.Base64Encode(jsonBody.Data.([]byte))
    55  		jsonBody.Data = d
    56  	}
    57  	jsonBody.Sign = utils.HMAC_SHA256(utils.AddStr(path, jsonBody.Data.(string), jsonBody.Nonce, jsonBody.Time, jsonBody.Plan), secret, true)
    58  	bytesData, err := utils.JsonMarshal(jsonBody)
    59  	if err != nil {
    60  		return nil, ex.Throw{Msg: "jsonBody data JsonMarshal invalid"}
    61  	}
    62  	return bytesData, nil
    63  }
    64  
    65  func authRes(client *WsClient, respBytes []byte) ([]byte, error) {
    66  	if len(respBytes) == 0 {
    67  		return nil, ex.Throw{Msg: "message is nil"}
    68  	}
    69  	respData := &JsonResp{
    70  		Code:    utils.GetJsonInt(respBytes, "c"),
    71  		Message: utils.GetJsonString(respBytes, "m"),
    72  		Data:    utils.GetJsonString(respBytes, "d"),
    73  		Nonce:   utils.GetJsonString(respBytes, "n"),
    74  		Time:    int64(utils.GetJsonInt(respBytes, "t")),
    75  		Plan:    int64(utils.GetJsonInt(respBytes, "p")),
    76  		Sign:    utils.GetJsonString(respBytes, "s"),
    77  	}
    78  	if respData.Code != 200 {
    79  		if respData.Code > 0 {
    80  			return nil, ex.Throw{Code: respData.Code, Msg: respData.Message}
    81  		}
    82  		return nil, ex.Throw{Msg: respData.Message}
    83  	}
    84  	validSign := utils.HMAC_SHA256(utils.AddStr(client.Path, respData.Data, respData.Nonce, respData.Time, respData.Plan), client.auth.Secret, true)
    85  	if validSign != respData.Sign {
    86  		return nil, ex.Throw{Msg: "post response sign verify invalid"}
    87  	}
    88  	var err error
    89  	var dec []byte
    90  	if respData.Plan == 0 {
    91  		dec = utils.Base64Decode(respData.Data)
    92  	} else if respData.Plan == 1 {
    93  		dec, err = utils.AesDecrypt(respData.Data.(string), client.auth.Secret, utils.AddStr(respData.Nonce, respData.Time))
    94  		if err != nil {
    95  			return nil, ex.Throw{Msg: "post response data AES decrypt failed"}
    96  		}
    97  	} else {
    98  		return nil, ex.Throw{Msg: "response sign plan invalid"}
    99  	}
   100  	return dec, nil
   101  }
   102  
   103  func (client *WsClient) StartWebsocket(auto bool, n ...int) {
   104  	for {
   105  		if err := client.initClient(); err != nil {
   106  			zlog.Error("websocket client error", 0, zlog.AddError(err))
   107  			if !auto {
   108  				break
   109  			}
   110  		}
   111  		restart := time.Duration(10)
   112  		if len(n) > 0 && n[0] > 0 {
   113  			restart = time.Duration(n[0])
   114  		}
   115  		time.Sleep(restart * time.Second)
   116  	}
   117  }
   118  
   119  func (client *WsClient) Ready() bool {
   120  	return client.conn != nil && len(client.auth.Secret) > 0
   121  }
   122  
   123  func (client *WsClient) initClient() error {
   124  	if len(client.Addr) == 0 {
   125  		return utils.Error("client addr is nil")
   126  	}
   127  
   128  	if len(client.Path) == 0 {
   129  		return utils.Error("client path is nil")
   130  	}
   131  
   132  	if len(client.Origin) == 0 {
   133  		return utils.Error("client origin is nil")
   134  	}
   135  
   136  	if client.AuthCall == nil {
   137  		return utils.Error("client auth call is nil")
   138  	}
   139  
   140  	if client.ReceiveCall == nil {
   141  		return utils.Error("client receive call is nil")
   142  	}
   143  
   144  	// 创建 WebSocket 连接
   145  	config, err := websocket.NewConfig(client.Addr+client.Path, client.Origin)
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	token, secret, err := client.AuthCall()
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	if len(token) == 0 || len(secret) == 0 {
   156  		return utils.Error("token/secret invalid")
   157  	}
   158  
   159  	// 设置 JWT 头部
   160  	config.Header.Add("Authorization", token)
   161  
   162  	// 建立 WebSocket 连接
   163  	ws, err := websocket.DialConfig(config)
   164  	if err != nil {
   165  		return err
   166  	}
   167  	defer closeConn("ws client close", &DevConn{Conn: ws})
   168  
   169  	client.conn = ws
   170  	client.auth = TokenAuth{Token: token, Secret: secret}
   171  
   172  	zlog.Info("websocket connect success", 0, zlog.String("url", client.Addr+client.Path))
   173  
   174  	go client.ping()
   175  
   176  	return client.receive()
   177  }
   178  
   179  // receive 读取服务端消息
   180  func (client *WsClient) receive() error {
   181  	for {
   182  		var message []byte
   183  		if err := websocket.Message.Receive(client.conn, &message); err != nil {
   184  			return err
   185  		}
   186  		res, err := authRes(client, message)
   187  		if err != nil {
   188  			zlog.Error("websocket receive parse error", 0, zlog.AddError(err))
   189  			continue
   190  		}
   191  		reply, err := client.ReceiveCall(res)
   192  		if err != nil {
   193  			zlog.Error("websocket receive call error", 0, zlog.AddError(err))
   194  		}
   195  		if err := client.SendMessage(reply); err != nil {
   196  			break
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  func (client *WsClient) SendMessage(reply interface{}) error {
   203  	if reply == nil {
   204  		return nil
   205  	}
   206  	if !client.Ready() {
   207  		zlog.Warn("client not ready", 0)
   208  		return nil
   209  	}
   210  	data, err := authReq(client.Path, reply, client.auth.Secret)
   211  	if err != nil {
   212  		zlog.Error("websocket receive reply create error", 0, zlog.AddError(err))
   213  		return nil
   214  	}
   215  	if err := websocket.Message.Send(client.conn, data); err != nil {
   216  		zlog.Error("websocket client reply error", 0, zlog.AddError(err))
   217  		return err
   218  	}
   219  	return nil
   220  }
   221  
   222  // ping 持续心跳包
   223  func (client *WsClient) ping() {
   224  	for {
   225  		ping := Ping{
   226  			HealthCheck: pingCmd,
   227  		}
   228  		data, _ := authReq(client.Path, &ping, client.auth.Secret)
   229  		if err := websocket.Message.Send(client.conn, data); err != nil {
   230  			zlog.Error("websocket client ping error", 0, zlog.AddError(err))
   231  			break
   232  		}
   233  		time.Sleep(10 / 2 * time.Second)
   234  	}
   235  }