safing-portbase/api/client/message.go
2022-02-01 13:12:46 +01:00

95 lines
2 KiB
Go

package client
import (
"bytes"
"errors"
"github.com/tevino/abool"
"github.com/safing/portbase/container"
"github.com/safing/portbase/formats/dsd"
)
// ErrMalformedMessage is returned when a malformed message was encountered.
var ErrMalformedMessage = errors.New("malformed message")
// Message is an API message.
type Message struct {
OpID string
Type string
Key string
RawValue []byte
Value interface{}
sent *abool.AtomicBool
}
// ParseMessage parses the given raw data and returns a Message.
func ParseMessage(data []byte) (*Message, error) {
parts := bytes.SplitN(data, apiSeperatorBytes, 4)
if len(parts) < 2 {
return nil, ErrMalformedMessage
}
m := &Message{
OpID: string(parts[0]),
Type: string(parts[1]),
}
switch m.Type {
case MsgOk, MsgUpdate, MsgNew:
// parse key and data
// 127|ok|<key>|<data>
// 127|upd|<key>|<data>
// 127|new|<key>|<data>
if len(parts) != 4 {
return nil, ErrMalformedMessage
}
m.Key = string(parts[2])
m.RawValue = parts[3]
case MsgDelete:
// parse key
// 127|del|<key>
if len(parts) != 3 {
return nil, ErrMalformedMessage
}
m.Key = string(parts[2])
case MsgWarning, MsgError:
// parse message
// 127|error|<message>
// 127|warning|<message> // error with single record, operation continues
if len(parts) != 3 {
return nil, ErrMalformedMessage
}
m.Key = string(parts[2])
case MsgDone, MsgSuccess:
// nothing more to do
// 127|success
// 127|done
}
return m, nil
}
// Pack serializes a message into a []byte slice.
func (m *Message) Pack() ([]byte, error) {
c := container.New([]byte(m.OpID), apiSeperatorBytes, []byte(m.Type))
if m.Key != "" {
c.Append(apiSeperatorBytes)
c.Append([]byte(m.Key))
if len(m.RawValue) > 0 {
c.Append(apiSeperatorBytes)
c.Append(m.RawValue)
} else if m.Value != nil {
var err error
m.RawValue, err = dsd.Dump(m.Value, dsd.JSON)
if err != nil {
return nil, err
}
c.Append(apiSeperatorBytes)
c.Append(m.RawValue)
}
}
return c.CompileData(), nil
}