mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-09-04 22:53:17 +08:00
217 lines
4.6 KiB
Go
217 lines
4.6 KiB
Go
package api
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
"unicode/utf8"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/openimsdk/protocol/constant"
|
|
"github.com/openimsdk/protocol/msg"
|
|
"github.com/openimsdk/tools/a2r"
|
|
"github.com/openimsdk/tools/apiresp"
|
|
"github.com/openimsdk/tools/errs"
|
|
"github.com/openimsdk/tools/log"
|
|
)
|
|
|
|
func (m *MessageApi) GetStreamMsg(c *gin.Context) {
|
|
a2r.Call(c, msg.MsgClient.GetStreamMsg, m.Client)
|
|
}
|
|
|
|
func (m *MessageApi) AppendStreamMsg(c *gin.Context) {
|
|
a2r.Call(c, msg.MsgClient.AppendStreamMsg, m.Client)
|
|
}
|
|
|
|
func (m *MessageApi) PutStreamMsg(c *gin.Context) {
|
|
var (
|
|
conversationID string
|
|
clientMsgID string
|
|
)
|
|
{
|
|
operationID := c.GetHeader(constant.OperationID)
|
|
if operationID == "" {
|
|
operationID = c.Query(constant.OperationID)
|
|
}
|
|
if operationID == "" {
|
|
m.putErr(c, errs.ErrArgs.WrapMsg("operationID is empty"))
|
|
return
|
|
}
|
|
c.Set(constant.OperationID, operationID)
|
|
conversationID = c.Query("conversationID")
|
|
if conversationID == "" {
|
|
conversationID = c.GetHeader("conversationID")
|
|
}
|
|
if conversationID == "" {
|
|
m.putErr(c, errs.ErrArgs.WrapMsg("conversationID is empty"))
|
|
return
|
|
}
|
|
clientMsgID = c.Query("clientMsgID")
|
|
if clientMsgID == "" {
|
|
clientMsgID = c.GetHeader("clientMsgID")
|
|
}
|
|
if clientMsgID == "" {
|
|
m.putErr(c, errs.ErrArgs.WrapMsg("clientMsgID is empty"))
|
|
return
|
|
}
|
|
token := c.GetHeader("token")
|
|
if token == "" {
|
|
token = c.Query("token")
|
|
}
|
|
if token == "" {
|
|
m.putErr(c, errs.ErrTokenInvalid.WrapMsg("token is empty"))
|
|
return
|
|
}
|
|
resp, err := m.authClient.ParseToken(c, token)
|
|
if err != nil {
|
|
m.putErr(c, err)
|
|
return
|
|
}
|
|
c.Set(constant.OpUserPlatform, constant.PlatformIDToName(int(resp.PlatformID)))
|
|
c.Set(constant.OpUserID, resp.UserID)
|
|
}
|
|
done := make(chan struct{})
|
|
streamCh := make(chan string, 8)
|
|
|
|
go func() {
|
|
defer func() {
|
|
close(streamCh)
|
|
c.Request.Body.Close()
|
|
}()
|
|
buf := make([]byte, 256)
|
|
body := NewUTF8Reader(c.Request.Body)
|
|
for i := 1; ; i++ {
|
|
n, err := body.Read(buf)
|
|
if n > 0 {
|
|
select {
|
|
case streamCh <- string(buf[:n]):
|
|
case <-done:
|
|
return
|
|
}
|
|
}
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
log.ZDebug(c, "read request body stream msg done", "clientMsgID", clientMsgID)
|
|
} else {
|
|
log.ZError(c, "read request body stream msg failed", err, "clientMsgID", clientMsgID, "error", err)
|
|
}
|
|
return
|
|
}
|
|
if n < 10 {
|
|
time.Sleep(time.Millisecond * 10)
|
|
}
|
|
}
|
|
}()
|
|
|
|
var (
|
|
packet []string
|
|
end bool
|
|
index int
|
|
errCount int
|
|
lastErr error
|
|
)
|
|
defer func() {
|
|
close(done)
|
|
if lastErr == nil {
|
|
apiresp.GinSuccess(c, nil)
|
|
} else {
|
|
m.putErr(c, lastErr)
|
|
}
|
|
}()
|
|
doAppend := func() {
|
|
if end == false && len(packet) == 0 {
|
|
return
|
|
}
|
|
ctx, cancel := context.WithTimeout(c, time.Second*10)
|
|
defer cancel()
|
|
req := &msg.AppendStreamMsgReq{
|
|
ConversationID: conversationID,
|
|
ClientMsgID: clientMsgID,
|
|
StartIndex: int64(index),
|
|
Packets: packet,
|
|
End: end,
|
|
}
|
|
_, lastErr = m.Client.AppendStreamMsg(ctx, req)
|
|
if lastErr == nil {
|
|
log.ZDebug(ctx, "AppendStreamMsg ok", "clientMsgID", clientMsgID)
|
|
index += len(packet)
|
|
packet = packet[:0]
|
|
errCount = 0
|
|
return
|
|
}
|
|
errCount++
|
|
if errs.ErrRecordNotFound.Is(lastErr) {
|
|
log.ZWarn(c, "msg not found", nil, "clientMsgID", clientMsgID)
|
|
return
|
|
} else if errs.ErrNoPermission.Is(lastErr) {
|
|
log.ZError(c, "msg permission error", nil, "clientMsgID", clientMsgID)
|
|
return
|
|
} else {
|
|
log.ZError(c, "append stream msg failed", lastErr, "clientMsgID", clientMsgID, "errCount", errCount)
|
|
time.Sleep(time.Millisecond * 50 * time.Duration(errCount))
|
|
}
|
|
}
|
|
for errCount < 10 {
|
|
select {
|
|
case s, ok := <-streamCh:
|
|
if ok {
|
|
packet = append(packet, s)
|
|
}
|
|
if !ok {
|
|
end = true
|
|
}
|
|
doAppend()
|
|
if end == true && lastErr == nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func NewUTF8Reader(r io.Reader) io.Reader {
|
|
return &UTF8Reader{
|
|
r: bufio.NewReaderSize(r, 512),
|
|
}
|
|
}
|
|
|
|
type UTF8Reader struct {
|
|
r *bufio.Reader
|
|
buf bytes.Buffer
|
|
}
|
|
|
|
func (r *UTF8Reader) Read(b []byte) (int, error) {
|
|
for {
|
|
n, err := r.r.Read(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
r.buf.Write(b[:n])
|
|
data := r.buf.Bytes()
|
|
minIndex := min(len(b), len(data))
|
|
if minIndex == 0 {
|
|
continue
|
|
}
|
|
for i := minIndex; i > 0; i-- {
|
|
if utf8.Valid(data[:i]) {
|
|
n, err := r.buf.Read(b[:i])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if n != i {
|
|
return 0, fmt.Errorf("invalid UTF-8 encoding")
|
|
}
|
|
return n, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *MessageApi) putErr(c *gin.Context, err error) {
|
|
c.JSON(http.StatusOK, apiresp.ParseError(err))
|
|
}
|