121 lines
2.8 KiB
Go
121 lines
2.8 KiB
Go
package imq
|
|
|
|
import "errors"
|
|
|
|
// Message types the proxy reads or writes.
|
|
const (
|
|
C2gSendMessage = 1304
|
|
G2cResult = 3102
|
|
)
|
|
|
|
// SendMessage is a C2gSendMessage: a message the client sends to a queue's mount.
|
|
type SendMessage struct {
|
|
OpID uint64
|
|
Queue string
|
|
Mount string
|
|
Message []byte
|
|
}
|
|
|
|
// field is one protobuf field: its number and either a varint or bytes.
|
|
type field struct {
|
|
num uint64
|
|
varint uint64
|
|
bytes []byte
|
|
isLen bool
|
|
}
|
|
|
|
func fields(b []byte) ([]field, error) {
|
|
var out []field
|
|
for i := 0; i < len(b); {
|
|
key, n := varint(b[i:])
|
|
if n <= 0 {
|
|
return nil, ErrMalformed
|
|
}
|
|
i += n
|
|
f := field{num: key >> 3}
|
|
switch key & 7 {
|
|
case 0:
|
|
v, n := varint(b[i:])
|
|
if n <= 0 {
|
|
return nil, ErrMalformed
|
|
}
|
|
f.varint, i = v, i+n
|
|
case 2:
|
|
l, n := varint(b[i:])
|
|
if n <= 0 || l > uint64(len(b)-i-n) {
|
|
return nil, ErrMalformed
|
|
}
|
|
i += n
|
|
f.bytes, f.isLen, i = b[i:i+int(l)], true, i+int(l)
|
|
default:
|
|
return nil, ErrMalformed
|
|
}
|
|
out = append(out, f)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// ParseSendMessage reads a C2gSendMessage's data.
|
|
func ParseSendMessage(data []byte) (SendMessage, error) {
|
|
fs, err := fields(data)
|
|
if err != nil {
|
|
return SendMessage{}, err
|
|
}
|
|
var m SendMessage
|
|
seen := 0
|
|
for _, f := range fs {
|
|
switch {
|
|
case f.num == 1 && !f.isLen:
|
|
m.OpID, seen = f.varint, seen|1
|
|
case f.num == 2 && f.isLen:
|
|
m.Queue, seen = string(f.bytes), seen|2
|
|
case f.num == 3 && f.isLen:
|
|
m.Mount, seen = string(f.bytes), seen|4
|
|
case f.num == 4 && f.isLen:
|
|
m.Message, seen = f.bytes, seen|8
|
|
default:
|
|
return SendMessage{}, errors.New("imq: unexpected field in C2gSendMessage")
|
|
}
|
|
}
|
|
if seen != 15 {
|
|
return SendMessage{}, errors.New("imq: C2gSendMessage is missing fields")
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// Marshal encodes the message's data, fields in the client's order.
|
|
func (m SendMessage) Marshal() []byte {
|
|
var b []byte
|
|
b = appendVarintField(b, 1, m.OpID)
|
|
b = appendBytesField(b, 2, []byte(m.Queue))
|
|
b = appendBytesField(b, 3, []byte(m.Mount))
|
|
return appendBytesField(b, 4, m.Message)
|
|
}
|
|
|
|
// Result encodes a G2cResult's data: the answer to the operation opID (status 0 is success).
|
|
func Result(opID, status uint64) []byte {
|
|
return appendVarintField(appendVarintField(nil, 1, opID), 2, status)
|
|
}
|
|
|
|
// EncodeFrame wraps a message's data in a frame of the given type.
|
|
func EncodeFrame(t uint64, data []byte) []byte {
|
|
return appendBytesField(appendVarintField(nil, 1, t), 2, data)
|
|
}
|
|
|
|
func appendVarint(b []byte, v uint64) []byte {
|
|
for v >= 0x80 {
|
|
b = append(b, byte(v)|0x80)
|
|
v >>= 7
|
|
}
|
|
return append(b, byte(v))
|
|
}
|
|
|
|
func appendVarintField(b []byte, num, v uint64) []byte {
|
|
return appendVarint(appendVarint(b, num<<3), v)
|
|
}
|
|
|
|
func appendBytesField(b []byte, num uint64, v []byte) []byte {
|
|
b = appendVarint(appendVarint(b, num<<3|2), uint64(len(v)))
|
|
return append(b, v...)
|
|
}
|