iot_server/internal/pkg/packets/publish.go

136 lines
3.2 KiB
Go

package packets
import (
"bytes"
"fmt"
"io"
"github.com/winc-link/hummingbird/internal/pkg/codes"
)
// Publish represents the MQTT Publish packet
type Publish struct {
Version Version
FixHeader *FixHeader
Dup bool //是否重发 [MQTT-3.3.1.-1]
Qos uint8 //qos等级
Retain bool //是否保留消息
TopicName []byte //主题名
PacketID //报文标识符
Payload []byte
Properties *Properties
}
func (p *Publish) String() string {
return fmt.Sprintf("Publish, Version: %v, Pid: %v, Dup: %v, Qos: %v, Retain: %v, TopicName: %s, Payload: %s, Properties: %s",
p.Version, p.PacketID, p.Dup, p.Qos, p.Retain, p.TopicName, p.Payload, p.Properties)
}
// NewPublishPacket returns a Publish instance by the given FixHeader and io.Reader.
func NewPublishPacket(fh *FixHeader, version Version, r io.Reader) (*Publish, error) {
p := &Publish{FixHeader: fh, Version: version}
p.Dup = (1 & (fh.Flags >> 3)) > 0
p.Qos = (fh.Flags >> 1) & 3
if p.Qos == 0 && p.Dup { //[MQTT-3.3.1-2]、 [MQTT-4.3.1-1]
return nil, codes.ErrMalformed
}
if p.Qos > Qos2 {
return nil, codes.ErrMalformed
}
if fh.Flags&1 == 1 { //保留标志
p.Retain = true
}
err := p.Unpack(r)
if err != nil {
return nil, err
}
return p, nil
}
// Pack encodes the packet struct into bytes and writes it into io.Writer.
func (p *Publish) Pack(w io.Writer) error {
p.FixHeader = &FixHeader{PacketType: PUBLISH}
bufw := &bytes.Buffer{}
var dup, retain byte
dup = 0
retain = 0
if p.Dup {
dup = 8
}
if p.Retain {
retain = 1
}
p.FixHeader.Flags = dup | retain | (p.Qos << 1)
writeBinary(bufw, p.TopicName)
if p.Qos == Qos1 || p.Qos == Qos2 {
writeUint16(bufw, p.PacketID)
}
if p.Version == Version5 {
p.Properties.Pack(bufw, PUBLISH)
}
bufw.Write(p.Payload)
p.FixHeader.RemainLength = bufw.Len()
err := p.FixHeader.Pack(w)
if err != nil {
return err
}
_, err = bufw.WriteTo(w)
return err
}
// Unpack read the packet bytes from io.Reader and decodes it into the packet struct.
func (p *Publish) Unpack(r io.Reader) error {
var err error
restBuffer := make([]byte, p.FixHeader.RemainLength)
_, err = io.ReadFull(r, restBuffer)
if err != nil {
return codes.ErrMalformed
}
bufr := bytes.NewBuffer(restBuffer)
p.TopicName, err = readUTF8String(true, bufr)
if err != nil {
return err
}
if !ValidTopicName(true, p.TopicName) {
return codes.ErrMalformed
}
if p.Qos > Qos0 {
p.PacketID, err = readUint16(bufr)
if err != nil {
return err
}
}
if p.Version == Version5 {
p.Properties = &Properties{}
if err := p.Properties.Unpack(bufr, PUBLISH); err != nil {
return err
}
}
p.Payload = bufr.Next(bufr.Len())
return nil
}
// NewPuback returns the puback struct related to the publish struct in QoS 1
func (p *Publish) NewPuback(code codes.Code, ppt *Properties) *Puback {
pub := &Puback{
Version: p.Version,
Code: code,
PacketID: p.PacketID,
Properties: ppt,
}
return pub
}
// NewPubrec returns the pubrec struct related to the publish struct in QoS 2
func (p *Publish) NewPubrec(code codes.Code, ppt *Properties) *Pubrec {
pub := &Pubrec{
Version: p.Version,
Code: code,
PacketID: p.PacketID,
Properties: ppt,
}
return pub
}