socket.io library. current version.
This commit is contained in:
45
protocol/message.go
Normal file
45
protocol/message.go
Normal file
@ -0,0 +1,45 @@
|
||||
package protocol
|
||||
|
||||
const (
|
||||
/**
|
||||
Message with connection options
|
||||
*/
|
||||
MessageTypeOpen = iota
|
||||
/**
|
||||
Close connection and destroy all handle routines
|
||||
*/
|
||||
MessageTypeClose = iota
|
||||
/**
|
||||
Ping request message
|
||||
*/
|
||||
MessageTypePing = iota
|
||||
/**
|
||||
Pong response message
|
||||
*/
|
||||
MessageTypePong = iota
|
||||
/**
|
||||
Empty message
|
||||
*/
|
||||
MessageTypeEmpty = iota
|
||||
/**
|
||||
Emit request, no response
|
||||
*/
|
||||
MessageTypeEmit = iota
|
||||
/**
|
||||
Emit request, wait for response (ack)
|
||||
*/
|
||||
MessageTypeAckRequest = iota
|
||||
/**
|
||||
ack response
|
||||
*/
|
||||
MessageTypeAckResponse = iota
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Type int
|
||||
AckId int
|
||||
Method string
|
||||
Args string
|
||||
Source string
|
||||
}
|
||||
|
214
protocol/socketio.go
Normal file
214
protocol/socketio.go
Normal file
@ -0,0 +1,214 @@
|
||||
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
|
||||
}
|
||||
|
||||
return result + "[" + string(jsonMethod) + "," + msg.Args + "]", 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
|
||||
}
|
Reference in New Issue
Block a user