golang-socketio/protocol/socketio.go

221 lines
4.0 KiB
Go

package protocol
import (
"encoding/json"
"errors"
"strconv"
"strings"
)
const (
open = "0"
msg = "4"
emptyMessage = "40"
commonMessage = "42"
ackMessage = "43"
CloseMessage = "1"
PingMessage = "2"
PongMessage = "3"
)
var (
ErrorWrongMessageType = errors.New("Wrong message type")
ErrorWrongPacket = errors.New("Wrong packet")
)
func typeToText(msgType int) (string, error) {
switch msgType {
case MessageTypeOpen:
return open, nil
case MessageTypeClose:
return CloseMessage, nil
case MessageTypePing:
return PingMessage, nil
case MessageTypePong:
return PongMessage, nil
case MessageTypeEmpty:
return emptyMessage, nil
case MessageTypeEmit, MessageTypeAckRequest:
return commonMessage, nil
case MessageTypeAckResponse:
return ackMessage, nil
}
return "", ErrorWrongMessageType
}
func Encode(msg *Message) (string, error) {
result, err := typeToText(msg.Type)
if err != nil {
return "", err
}
if msg.Type == MessageTypeEmpty || msg.Type == MessageTypePing ||
msg.Type == MessageTypePong {
return result, nil
}
if msg.Type == MessageTypeAckRequest || msg.Type == MessageTypeAckResponse {
result += strconv.Itoa(msg.AckId)
}
if msg.Type == MessageTypeOpen || msg.Type == MessageTypeClose {
return result + msg.Args, nil
}
if msg.Type == MessageTypeAckResponse {
return result + "[" + msg.Args + "]", nil
}
jsonMethod, err := json.Marshal(&msg.Method)
if err != nil {
return "", err
}
encoded := result + "[" + string(jsonMethod)
if msg.Args != "" {
encoded += "," + msg.Args
}
encoded += "]"
return encoded, nil
}
func MustEncode(msg *Message) string {
result, err := Encode(msg)
if err != nil {
panic(err)
}
return result
}
func getMessageType(data string) (int, error) {
if len(data) == 0 {
return 0, ErrorWrongMessageType
}
switch data[0:1] {
case open:
return MessageTypeOpen, nil
case CloseMessage:
return MessageTypeClose, nil
case PingMessage:
return MessageTypePing, nil
case PongMessage:
return MessageTypePong, nil
case msg:
if len(data) == 1 {
return 0, ErrorWrongMessageType
}
switch data[0:2] {
case emptyMessage:
return MessageTypeEmpty, nil
case commonMessage:
return MessageTypeAckRequest, nil
case ackMessage:
return MessageTypeAckResponse, nil
}
}
return 0, ErrorWrongMessageType
}
/**
Get ack id of current packet, if present
*/
func getAck(text string) (ackId int, restText string, err error) {
if len(text) < 4 {
return 0, "", ErrorWrongPacket
}
text = text[2:]
pos := strings.IndexByte(text, '[')
if pos == -1 {
return 0, "", ErrorWrongPacket
}
ack, err := strconv.Atoi(text[0:pos])
if err != nil {
return 0, "", err
}
return ack, text[pos:], nil
}
/**
Get message method of current packet, if present
*/
func getMethod(text string) (method, restText string, err error) {
var start, end, rest, countQuote int
for i, c := range text {
if c == '"' {
switch countQuote {
case 0:
start = i + 1
case 1:
end = i
rest = i + 1
default:
return "", "", ErrorWrongPacket
}
countQuote++
}
if c == ',' {
if countQuote < 2 {
continue
}
rest = i + 1
break
}
}
if (end < start) || (rest >= len(text)) {
return "", "", ErrorWrongPacket
}
return text[start:end], text[rest : len(text)-1], nil
}
func Decode(data string) (*Message, error) {
var err error
msg := &Message{}
msg.Source = data
msg.Type, err = getMessageType(data)
if err != nil {
return nil, err
}
if msg.Type == MessageTypeOpen {
msg.Args = data[1:]
return msg, nil
}
if msg.Type == MessageTypeClose || msg.Type == MessageTypePing ||
msg.Type == MessageTypePong || msg.Type == MessageTypeEmpty {
return msg, nil
}
ack, rest, err := getAck(data)
msg.AckId = ack
if msg.Type == MessageTypeAckResponse {
if err != nil {
return nil, err
}
msg.Args = rest[1 : len(rest)-1]
return msg, nil
}
if err != nil {
msg.Type = MessageTypeEmit
rest = data[2:]
}
msg.Method, msg.Args, err = getMethod(rest)
if err != nil {
return nil, err
}
return msg, nil
}