Factor out serialization/parsing code into protocol.Model
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/pr Build is pending Details

This commit is contained in:
Sarah Jamie Lewis 2022-01-25 15:39:23 -08:00
parent 6bb510e39e
commit ff4249e2bc
2 changed files with 52 additions and 35 deletions

View File

@ -1,11 +1,9 @@
package connections
import (
"bytes"
"cwtch.im/cwtch/event"
model2 "cwtch.im/cwtch/protocol/model"
"encoding/json"
"errors"
"git.openprivacy.ca/cwtch.im/tapir"
"git.openprivacy.ca/cwtch.im/tapir/applications"
"git.openprivacy.ca/openprivacy/log"
@ -103,39 +101,14 @@ func (pa *PeerApp) listen() {
if pa.version.Load() == 0x01 {
err = json.Unmarshal(message, &packet)
} else if pa.version.Load() == 0x02 {
// assume the encoding is invalid
err = errors.New("invalid message")
// find the identifier prefix
idTerminator := bytes.IndexByte(message, '|')
if idTerminator != -1 && idTerminator+1 < len(message) {
// find the context terminator prefix
contextbegin := idTerminator + 1
contextTerminator := bytes.IndexByte(message[contextbegin:], '|')
if contextTerminator != -1 {
err = nil
// check that we have data
dataBegin := contextbegin + contextTerminator + 1
var data []byte
if dataBegin < len(message) {
data = message[dataBegin:]
}
// compile the message
packet = model2.PeerMessage{
ID: string(message[0:idTerminator]),
Context: string(message[contextbegin : contextbegin+contextTerminator]),
Data: data,
}
}
}
parsePacket, parseErr := model2.ParsePeerMessage(message)
// if all else fails...attempt to process this message as a version 1 message
if err != nil {
if parseErr != nil {
err = json.Unmarshal(message, &packet)
} else {
packet = *parsePacket
}
} else {
log.Errorf("invalid version")
pa.OnClose(pa.connection.Hostname())
@ -170,7 +143,7 @@ func (pa *PeerApp) SendMessage(message model2.PeerMessage) error {
if pa.version.Load() == 0x02 {
// treat data as a pre-serialized string, not as a byte array (which will be base64 encoded and bloat the packet size)
serialized = append(append([]byte(message.ID+"|"), []byte(message.Context+"|")...), message.Data...)
serialized = message.Serialize()
} else {
serialized, err = json.Marshal(message)
}

View File

@ -1,8 +1,52 @@
package model
import (
"bytes"
"errors"
)
// PeerMessage is an encapsulation that can be used by higher level applications
type PeerMessage struct {
ID string // A unique Message ID (primarily used for acknowledgments)
// ID **must** only contain alphanumeric characters separated by period.
ID string // A unique Message ID (primarily used for acknowledgments)
// Context **must** only contain alphanumeric characters separated by period.
Context string // A unique context identifier i.e. im.cwtch.chat
Data []byte // A data packet.
// Data can contain anything
Data []byte // A data packet.
}
// Serialize constructs an efficient serialized representation
func (m *PeerMessage) Serialize() []byte {
return append(append([]byte(m.ID+"|"), []byte(m.Context+"|")...), m.Data...)
}
// ParsePeerMessage returns either a deserialized PeerMessage or an error if it is malformed
func ParsePeerMessage(message []byte) (*PeerMessage, error) {
// find the identifier prefix
idTerminator := bytes.IndexByte(message, '|')
if idTerminator != -1 && idTerminator+1 < len(message) {
// find the context terminator prefix
contextbegin := idTerminator + 1
contextTerminator := bytes.IndexByte(message[contextbegin:], '|')
if contextTerminator != -1 {
// check that we have data
dataBegin := contextbegin + contextTerminator + 1
var data []byte
if dataBegin < len(message) {
data = message[dataBegin:]
}
// compile the message
return &PeerMessage{
ID: string(message[0:idTerminator]),
Context: string(message[contextbegin : contextbegin+contextTerminator]),
Data: data,
}, nil
}
}
return nil, errors.New("invalid message")
}