mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
be32418bc1
Former-commit-id: 0ce38dbacc2458fe327fa4401fdde1e69c8aacb0
182 lines
4.4 KiB
Go
182 lines
4.4 KiB
Go
package websocket
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"strconv"
|
|
|
|
"github.com/kataras/iris/core/errors"
|
|
"github.com/valyala/bytebufferpool"
|
|
)
|
|
|
|
type (
|
|
messageType uint8
|
|
)
|
|
|
|
func (m messageType) String() string {
|
|
return strconv.Itoa(int(m))
|
|
}
|
|
|
|
func (m messageType) Name() string {
|
|
switch m {
|
|
case messageTypeString:
|
|
return "string"
|
|
case messageTypeInt:
|
|
return "int"
|
|
case messageTypeBool:
|
|
return "bool"
|
|
case messageTypeBytes:
|
|
return "[]byte"
|
|
case messageTypeJSON:
|
|
return "json"
|
|
default:
|
|
return "Invalid(" + m.String() + ")"
|
|
}
|
|
}
|
|
|
|
// The same values are exists on client side too.
|
|
const (
|
|
messageTypeString messageType = iota
|
|
messageTypeInt
|
|
messageTypeBool
|
|
messageTypeBytes
|
|
messageTypeJSON
|
|
)
|
|
|
|
const (
|
|
messageSeparator = ";"
|
|
)
|
|
|
|
var messageSeparatorByte = messageSeparator[0]
|
|
|
|
type messageSerializer struct {
|
|
prefix []byte
|
|
|
|
prefixLen int
|
|
separatorLen int
|
|
prefixAndSepIdx int
|
|
prefixIdx int
|
|
separatorIdx int
|
|
|
|
buf *bytebufferpool.Pool
|
|
}
|
|
|
|
func newMessageSerializer(messagePrefix []byte) *messageSerializer {
|
|
return &messageSerializer{
|
|
prefix: messagePrefix,
|
|
prefixLen: len(messagePrefix),
|
|
separatorLen: len(messageSeparator),
|
|
prefixAndSepIdx: len(messagePrefix) + len(messageSeparator) - 1,
|
|
prefixIdx: len(messagePrefix) - 1,
|
|
separatorIdx: len(messageSeparator) - 1,
|
|
|
|
buf: new(bytebufferpool.Pool),
|
|
}
|
|
}
|
|
|
|
var (
|
|
boolTrueB = []byte("true")
|
|
boolFalseB = []byte("false")
|
|
)
|
|
|
|
// websocketMessageSerialize serializes a custom websocket message from websocketServer to be delivered to the client
|
|
// returns the string form of the message
|
|
// Supported data types are: string, int, bool, bytes and JSON.
|
|
func (ms *messageSerializer) serialize(event string, data interface{}) ([]byte, error) {
|
|
b := ms.buf.Get()
|
|
b.Write(ms.prefix)
|
|
b.WriteString(event)
|
|
b.WriteByte(messageSeparatorByte)
|
|
|
|
switch v := data.(type) {
|
|
case string:
|
|
b.WriteString(messageTypeString.String())
|
|
b.WriteByte(messageSeparatorByte)
|
|
b.WriteString(v)
|
|
case int:
|
|
b.WriteString(messageTypeInt.String())
|
|
b.WriteByte(messageSeparatorByte)
|
|
binary.Write(b, binary.LittleEndian, v)
|
|
case bool:
|
|
b.WriteString(messageTypeBool.String())
|
|
b.WriteByte(messageSeparatorByte)
|
|
if v {
|
|
b.Write(boolTrueB)
|
|
} else {
|
|
b.Write(boolFalseB)
|
|
}
|
|
case []byte:
|
|
b.WriteString(messageTypeBytes.String())
|
|
b.WriteByte(messageSeparatorByte)
|
|
b.Write(v)
|
|
default:
|
|
//we suppose is json
|
|
res, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.WriteString(messageTypeJSON.String())
|
|
b.WriteByte(messageSeparatorByte)
|
|
b.Write(res)
|
|
}
|
|
|
|
message := b.Bytes()
|
|
ms.buf.Put(b)
|
|
|
|
return message, nil
|
|
}
|
|
|
|
var errInvalidTypeMessage = errors.New("Type %s is invalid for message: %s")
|
|
|
|
// deserialize deserializes a custom websocket message from the client
|
|
// ex: iris-websocket-message;chat;4;themarshaledstringfromajsonstruct will return 'hello' as string
|
|
// Supported data types are: string, int, bool, bytes and JSON.
|
|
func (ms *messageSerializer) deserialize(event []byte, websocketMessage []byte) (interface{}, error) {
|
|
dataStartIdx := ms.prefixAndSepIdx + len(event) + 3
|
|
if len(websocketMessage) <= dataStartIdx {
|
|
return nil, errors.New("websocket invalid message: " + string(websocketMessage))
|
|
}
|
|
|
|
typ, err := strconv.Atoi(string(websocketMessage[ms.prefixAndSepIdx+len(event)+1 : ms.prefixAndSepIdx+len(event)+2])) // in order to iris-websocket-message;user;-> 4
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data := websocketMessage[dataStartIdx:] // in order to iris-websocket-message;user;4; -> themarshaledstringfromajsonstruct
|
|
|
|
switch messageType(typ) {
|
|
case messageTypeString:
|
|
return string(data), nil
|
|
case messageTypeInt:
|
|
msg, err := strconv.Atoi(string(data))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return msg, nil
|
|
case messageTypeBool:
|
|
if bytes.Equal(data, boolTrueB) {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
case messageTypeBytes:
|
|
return data, nil
|
|
case messageTypeJSON:
|
|
var msg interface{}
|
|
err := json.Unmarshal(data, &msg)
|
|
return msg, err
|
|
default:
|
|
return nil, errInvalidTypeMessage.Format(messageType(typ).Name(), websocketMessage)
|
|
}
|
|
}
|
|
|
|
// getWebsocketCustomEvent return empty string when the websocketMessage is native message
|
|
func (ms *messageSerializer) getWebsocketCustomEvent(websocketMessage []byte) []byte {
|
|
if len(websocketMessage) < ms.prefixAndSepIdx {
|
|
return nil
|
|
}
|
|
s := websocketMessage[ms.prefixAndSepIdx:]
|
|
evt := s[:bytes.IndexByte(s, messageSeparatorByte)]
|
|
return evt
|
|
}
|