You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Open-IM-Server/internal/msggateway/client.go

255 lines
7.1 KiB

2 years ago
package msggateway
import (
"context"
"errors"
"fmt"
2 years ago
"github.com/OpenIMSDK/Open-IM-Server/pkg/apiresp"
2 years ago
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
2 years ago
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
2 years ago
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext"
2 years ago
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
2 years ago
"github.com/golang/protobuf/proto"
"runtime/debug"
"sync"
)
2 years ago
var ErrConnClosed = errors.New("conn has closed")
var ErrNotSupportMessageProtocol = errors.New("not support message protocol")
var ErrClientClosed = errors.New("client actively close the connection")
2 years ago
var ErrPanic = errors.New("panic error")
2 years ago
const (
// MessageText is for UTF-8 encoded text messages like JSON.
2 years ago
MessageText = iota + 1
// MessageBinary is for binary messages like protobufs.
MessageBinary
// CloseMessage denotes a close control message. The optional message
// payload contains a numeric code and text. Use the FormatCloseMessage
// function to format a close message payload.
CloseMessage = 8
// PingMessage denotes a ping control message. The optional message payload
// is UTF-8 encoded text.
PingMessage = 9
// PongMessage denotes a pong control message. The optional message payload
// is UTF-8 encoded text.
PongMessage = 10
)
2 years ago
type PongHandler func(string) error
type Client struct {
2 years ago
w *sync.Mutex
conn LongConn
platformID int
isCompress bool
2 years ago
userID string
isBackground bool
2 years ago
ctx *UserConnContext
2 years ago
longConnServer LongConnServer
2 years ago
closed bool
2 years ago
closedErr error
}
2 years ago
func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client {
return &Client{
2 years ago
w: new(sync.Mutex),
conn: conn,
platformID: utils.StringToInt(ctx.GetPlatformID()),
isCompress: isCompress,
userID: ctx.GetUserID(),
2 years ago
ctx: ctx,
}
}
2 years ago
func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, isCompress bool, longConnServer LongConnServer) {
c.w = new(sync.Mutex)
c.conn = conn
c.platformID = utils.StringToInt(ctx.GetPlatformID())
c.isCompress = isCompress
c.userID = ctx.GetUserID()
2 years ago
c.ctx = ctx
2 years ago
c.longConnServer = longConnServer
2 years ago
c.isBackground = false
c.closed = false
c.closedErr = nil
}
2 years ago
func (c *Client) pongHandler(_ string) error {
c.conn.SetReadDeadline(pongWait)
return nil
}
2 years ago
func (c *Client) readMessage() {
defer func() {
2 years ago
if r := recover(); r != nil {
2 years ago
c.closedErr = ErrPanic
fmt.Println("socket have panic err:", r, string(debug.Stack()))
}
2 years ago
c.close()
}()
2 years ago
c.conn.SetReadLimit(maxMessageSize)
_ = c.conn.SetReadDeadline(pongWait)
c.conn.SetPongHandler(c.pongHandler)
2 years ago
for {
messageType, message, returnErr := c.conn.ReadMessage()
if returnErr != nil {
2 years ago
c.closedErr = returnErr
return
2 years ago
}
2 years ago
log.ZDebug(c.ctx, "readMessage", "messageType", messageType)
if c.closed == true { //连接刚置位已经关闭,但是协程还没退出的场景
2 years ago
c.closedErr = ErrConnClosed
return
}
switch messageType {
2 years ago
case MessageBinary:
parseDataErr := c.handleMessage(message)
if parseDataErr != nil {
c.closedErr = parseDataErr
return
}
case MessageText:
c.closedErr = ErrNotSupportMessageProtocol
return
case PingMessage:
2 years ago
err := c.writePongMsg()
log.ZError(c.ctx, "writePongMsg", err)
case CloseMessage:
2 years ago
c.closedErr = ErrClientClosed
return
2 years ago
default:
}
}
}
2 years ago
func (c *Client) handleMessage(message []byte) error {
2 years ago
if c.isCompress {
var decompressErr error
2 years ago
message, decompressErr = c.longConnServer.DeCompress(message)
if decompressErr != nil {
2 years ago
return utils.Wrap(decompressErr, "")
}
}
2 years ago
var binaryReq Req
2 years ago
err := c.longConnServer.Decode(message, &binaryReq)
if err != nil {
2 years ago
return utils.Wrap(err, "")
}
2 years ago
if err := c.longConnServer.Validate(binaryReq); err != nil {
2 years ago
return utils.Wrap(err, "")
}
if binaryReq.SendID != c.userID {
2 years ago
return utils.Wrap(errors.New("exception conn userID not same to req userID"), binaryReq.String())
}
2 years ago
ctx := mcontext.WithMustInfoCtx([]string{binaryReq.OperationID, binaryReq.SendID, constant.PlatformIDToName(c.platformID), c.ctx.GetConnID()})
2 years ago
log.ZDebug(ctx, "gateway req message", "req", binaryReq.String())
var messageErr error
2 years ago
var resp []byte
switch binaryReq.ReqIdentifier {
2 years ago
case WSGetNewestSeq:
resp, messageErr = c.longConnServer.GetSeq(ctx, binaryReq)
case WSSendMsg:
resp, messageErr = c.longConnServer.SendMessage(ctx, binaryReq)
case WSSendSignalMsg:
resp, messageErr = c.longConnServer.SendSignalMessage(ctx, binaryReq)
case WSPullMsgBySeqList:
resp, messageErr = c.longConnServer.PullMessageBySeqList(ctx, binaryReq)
case WsLogoutMsg:
resp, messageErr = c.longConnServer.UserLogout(ctx, binaryReq)
case WsSetBackgroundStatus:
2 years ago
resp, messageErr = c.setAppBackgroundStatus(ctx, binaryReq)
default:
2 years ago
return errors.New(fmt.Sprintf("ReqIdentifier failed,sendID:%d,msgIncr:%s,reqIdentifier:%s", binaryReq.SendID, binaryReq.MsgIncr, binaryReq.ReqIdentifier))
}
2 years ago
c.replyMessage(ctx, &binaryReq, messageErr, resp)
2 years ago
return nil
2 years ago
}
func (c *Client) setAppBackgroundStatus(ctx context.Context, req Req) ([]byte, error) {
2 years ago
resp, isBackground, messageErr := c.longConnServer.SetUserDeviceBackground(ctx, req)
2 years ago
if messageErr != nil {
return nil, messageErr
}
c.isBackground = isBackground
//todo callback
return resp, nil
}
2 years ago
func (c *Client) close() {
c.w.Lock()
defer c.w.Unlock()
2 years ago
c.closed = true
2 years ago
c.conn.Close()
2 years ago
c.longConnServer.UnRegister(c)
}
2 years ago
func (c *Client) replyMessage(ctx context.Context, binaryReq *Req, err error, resp []byte) {
2 years ago
errResp := apiresp.ParseError(err)
2 years ago
mReply := Resp{
ReqIdentifier: binaryReq.ReqIdentifier,
MsgIncr: binaryReq.MsgIncr,
OperationID: binaryReq.OperationID,
2 years ago
ErrCode: errResp.ErrCode,
ErrMsg: errResp.ErrMsg,
2 years ago
Data: resp,
}
2 years ago
log.ZDebug(ctx, "gateway reply message", "resp", mReply.String())
err = c.writeBinaryMsg(mReply)
if err != nil {
log.ZWarn(ctx, "wireBinaryMsg replyMessage", err, "resp", mReply.String())
}
2 years ago
}
2 years ago
func (c *Client) PushMessage(ctx context.Context, msgData *sdkws.MsgData) error {
2 years ago
data, err := proto.Marshal(msgData)
if err != nil {
return err
}
resp := Resp{
ReqIdentifier: WSPushMsg,
OperationID: mcontext.GetOperationID(ctx),
Data: data,
}
2 years ago
return c.writeBinaryMsg(resp)
2 years ago
}
func (c *Client) KickOnlineMessage(ctx context.Context) error {
return nil
}
2 years ago
2 years ago
func (c *Client) writeBinaryMsg(resp Resp) error {
2 years ago
c.w.Lock()
defer c.w.Unlock()
if c.closed == true {
return nil
}
encodedBuf := bufferPool.Get().([]byte)
resultBuf := bufferPool.Get().([]byte)
2 years ago
encodeBuf, err := c.longConnServer.Encode(resp)
2 years ago
if err != nil {
return utils.Wrap(err, "")
}
2 years ago
_ = c.conn.SetWriteDeadline(writeWait)
if c.isCompress {
2 years ago
var compressErr error
2 years ago
resultBuf, compressErr = c.longConnServer.Compress(encodeBuf)
2 years ago
if compressErr != nil {
return utils.Wrap(compressErr, "")
}
return c.conn.WriteMessage(MessageBinary, resultBuf)
} else {
return c.conn.WriteMessage(MessageBinary, encodedBuf)
}
}
2 years ago
func (c *Client) writePongMsg() error {
c.w.Lock()
defer c.w.Unlock()
if c.closed == true {
return nil
}
_ = c.conn.SetWriteDeadline(writeWait)
return c.conn.WriteMessage(PongMessage, nil)
}