package protocol
import (
"encoding/binary"
"errors"
"io"
)
const (
PING byte = iota
PONG
AUTH
NUSR
SYNC
SUSR
SPWD
SUCC
FAIL
MaxPayloadSize uint16 = 50 * (2 << 9) // 50KiB
)
var ErrMaxSizeExceeded = errors.New("maximum paylod size exceeded")
type Payload struct {
payloadType byte
bytes []byte
}
func (m *Payload) Type() byte {
return m.payloadType
}
// Returns a string representation of the payload type
// for logging and debugging purposes
func (m *Payload) TypeString() string {
switch m.payloadType {
case PING:
return "PING"
case PONG:
return "PONG"
case AUTH:
return "AUTH"
case NUSR:
return "NUSR"
case SYNC:
return "SYNC"
case SUSR:
return "SUSR"
case SPWD:
return "SPWD"
case SUCC:
return "SUCC"
case FAIL:
return "FAIL"
}
return "INVALID TYPE"
}
func (m *Payload) Bytes() []byte {
return m.bytes
}
func (m *Payload) String() string {
return string(m.bytes)
}
func (m *Payload) WriteTo(w io.Writer) (int64, error) {
bytes := []byte{m.payloadType}
lenBytes := make([]byte, 2)
binary.BigEndian.PutUint16(lenBytes, uint16(len(m.Bytes())))
bytes = append(bytes, lenBytes...)
bytes = append(bytes, m.Bytes()...)
n, err := w.Write(bytes)
if err != nil {
return int64(n), err
}
return int64(n), nil
}
func (m *Payload) ReadFrom(r io.Reader) (int64, error) {
err := binary.Read(r, binary.BigEndian, &m.payloadType)
if err != nil {
return 0, err
}
var n int64 = 1
var size uint16
err = binary.Read(r, binary.BigEndian, &size)
if err != nil {
return 0, err
}
n += 4
if size > MaxPayloadSize {
return n, ErrMaxSizeExceeded
}
m.bytes = make([]byte, size)
b, err := r.Read(m.bytes)
if err != nil {
return n, err
}
return n + int64(b), err
}
func NewPing() *Payload {
return &Payload{PING, []byte{}}
}
func NewPong() *Payload {
return &Payload{PONG, []byte{}}
}
func NewSucc() *Payload {
return &Payload{SUCC, []byte{}}
}
func NewSuccWithData(data []byte) *Payload {
// Panic here to keep things simple when calling this function
// SUCC data should never be more than a few bytes, and if it is I've
// done something wrong
if len(data) > int(MaxPayloadSize) {
panic(ErrMaxSizeExceeded)
}
// Recepient will just have to know what to do with the data
return &Payload{SUCC, data}
}
func NewFail(message string) *Payload {
return &Payload{FAIL, []byte(message)}
}
func NewPayload(payloadType byte, bytes []byte) (*Payload, error) {
if len(bytes) > int(MaxPayloadSize) {
return nil, ErrMaxSizeExceeded
}
return &Payload{payloadType, bytes}, nil
}