diff --git a/protocol/connections/peerapp.go b/protocol/connections/peerapp.go index 7d6492e..b4e3158 100644 --- a/protocol/connections/peerapp.go +++ b/protocol/connections/peerapp.go @@ -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) } diff --git a/protocol/model/peermessage.go.go b/protocol/model/peermessage.go.go index 58be2ed..b62a41d 100644 --- a/protocol/model/peermessage.go.go +++ b/protocol/model/peermessage.go.go @@ -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") }