Documentation ¶
Index ¶
- Constants
- Variables
- func BytesToString(bs []byte) string
- func DecodeByte(buf []byte, offset int) (byte, int, error)
- func DecodeByteBool(buf []byte, offset int) (bool, int, error)
- func DecodeBytes(buf []byte, offset int) ([]byte, int, error)
- func DecodeLength(b io.ByteReader) (n, bu int, err error)
- func DecodeString(buf []byte, offset int) (string, int, error)
- func DecodeUint16(buf []byte, offset int) (uint16, int, error)
- func DecodeUint32(buf []byte, offset int) (uint32, int, error)
- func EncodeBool(b bool) byte
- func EncodeBytes(val []byte) []byte
- func EncodeLength(b *bytes.Buffer, length int64)
- func EncodeString(val string) []byte
- func EncodeUint16(val uint16) []byte
- func EncodeUint32(val uint32) []byte
- type Code
- type ConnectParams
- type FixedHeader
- type Mods
- type Packet
- func (pk *Packet) AuthDecode(buf []byte) error
- func (pk *Packet) AuthEncode(buf *bytes.Buffer) error
- func (pk *Packet) AuthValidate() Code
- func (pk *Packet) ConnackDecode(buf []byte) error
- func (pk *Packet) ConnackEncode(buf *bytes.Buffer) error
- func (pk *Packet) ConnectDecode(buf []byte) error
- func (pk *Packet) ConnectEncode(buf *bytes.Buffer) error
- func (pk *Packet) ConnectValidate() Code
- func (pk *Packet) Copy(allowTransfer bool) Packet
- func (pk *Packet) DisconnectDecode(buf []byte) error
- func (pk *Packet) DisconnectEncode(buf *bytes.Buffer) error
- func (pk *Packet) FormatID() string
- func (pk *Packet) PingreqDecode(buf []byte) error
- func (pk *Packet) PingreqEncode(buf *bytes.Buffer) error
- func (pk *Packet) PingrespDecode(buf []byte) error
- func (pk *Packet) PingrespEncode(buf *bytes.Buffer) error
- func (pk *Packet) PubackDecode(buf []byte) error
- func (pk *Packet) PubackEncode(buf *bytes.Buffer) error
- func (pk *Packet) PubcompDecode(buf []byte) error
- func (pk *Packet) PubcompEncode(buf *bytes.Buffer) error
- func (pk *Packet) PublishDecode(buf []byte) error
- func (pk *Packet) PublishEncode(buf *bytes.Buffer) error
- func (pk *Packet) PublishValidate(topicAliasMaximum uint16) Code
- func (pk *Packet) PubrecDecode(buf []byte) error
- func (pk *Packet) PubrecEncode(buf *bytes.Buffer) error
- func (pk *Packet) PubrelDecode(buf []byte) error
- func (pk *Packet) PubrelEncode(buf *bytes.Buffer) error
- func (pk *Packet) ReasonCodeValid() bool
- func (pk *Packet) SubackDecode(buf []byte) error
- func (pk *Packet) SubackEncode(buf *bytes.Buffer) error
- func (pk *Packet) SubscribeDecode(buf []byte) error
- func (pk *Packet) SubscribeEncode(buf *bytes.Buffer) error
- func (pk *Packet) SubscribeValidate() Code
- func (pk *Packet) UnsubackDecode(buf []byte) error
- func (pk *Packet) UnsubackEncode(buf *bytes.Buffer) error
- func (pk *Packet) UnsubscribeDecode(buf []byte) error
- func (pk *Packet) UnsubscribeEncode(buf *bytes.Buffer) error
- func (pk *Packet) UnsubscribeValidate() Code
- type Packets
- type Properties
- type Subscription
- type Subscriptions
- type TPacketCase
- type TPacketCases
- type UserProperty
Constants ¶
const ( Reserved byte = iota // 0 - we use this in packet tests to indicate special-test or all packets. Connect // 1 Connack // 2 Publish // 3 Puback // 4 Pubrec // 5 Pubrel // 6 Pubcomp // 7 Subscribe // 8 Suback // 9 Unsubscribe // 10 Unsuback // 11 Pingreq // 12 Pingresp // 13 Disconnect // 14 Auth // 15 )
All of the valid packet types and their packet identifier.
const ( PropPayloadFormat byte = 1 PropMessageExpiryInterval byte = 2 PropContentType byte = 3 PropResponseTopic byte = 8 PropCorrelationData byte = 9 PropSubscriptionIdentifier byte = 11 PropSessionExpiryInterval byte = 17 PropAssignedClientID byte = 18 PropServerKeepAlive byte = 19 PropAuthenticationMethod byte = 21 PropAuthenticationData byte = 22 PropRequestProblemInfo byte = 23 PropWillDelayInterval byte = 24 PropRequestResponseInfo byte = 25 PropResponseInfo byte = 26 PropServerReference byte = 28 PropReasonString byte = 31 PropReceiveMaximum byte = 33 PropTopicAliasMaximum byte = 34 PropTopicAlias byte = 35 PropMaximumQos byte = 36 PropRetainAvailable byte = 37 PropUser byte = 38 PropMaximumPacketSize byte = 39 PropWildcardSubAvailable byte = 40 PropSubIDAvailable byte = 41 )
const ( TConnectMqtt31 byte = iota TConnectMqtt311 TConnectMqtt5 TConnectMqtt5LWT TConnectClean TConnectCleanLWT TConnectUserPass TConnectUserPassLWT TConnectMalProtocolName TConnectMalProtocolVersion TConnectMalFlags TConnectMalKeepalive TConnectMalClientID TConnectMalWillTopic TConnectMalWillFlag TConnectMalUsername TConnectMalPassword TConnectMalFixedHeader TConnectMalReservedBit TConnectMalProperties TConnectMalWillProperties TConnectInvalidProtocolName TConnectInvalidProtocolVersion TConnectInvalidProtocolVersion2 TConnectInvalidReservedBit TConnectInvalidClientIDTooLong TConnectInvalidPasswordNoUsername TConnectInvalidFlagNoUsername TConnectInvalidFlagNoPassword TConnectInvalidUsernameNoFlag TConnectInvalidPasswordNoFlag TConnectInvalidUsernameTooLong TConnectInvalidPasswordTooLong TConnectInvalidWillFlagNoPayload TConnectInvalidWillFlagQosOutOfRange TConnectInvalidWillSurplusRetain TConnectZeroByteUsername TConnectSpecInvalidUTF8D800 TConnectSpecInvalidUTF8DFFF TConnectSpecInvalidUTF80000 TConnectSpecInvalidUTF8NoSkip TConnackAcceptedNoSession TConnackAcceptedSessionExists TConnackAcceptedMqtt5 TConnackAcceptedAdjustedExpiryInterval TConnackMinMqtt5 TConnackMinCleanMqtt5 TConnackInvalidMinMqtt5 TConnackBadProtocolVersion TConnackProtocolViolationNoSession TConnackBadClientID TConnackBadUsernamePassword TConnackBadUsernamePasswordNoSession TConnackMqtt5BadUsernamePasswordNoSession TConnackNotAuthorised TConnackMalSessionPresent TConnackMalReturnCode TConnackMalProperties TConnackDropProperties TConnackDropPropertiesPartial TPublishNoPayload TPublishBasic TPublishBasicTopicAliasOnly TPublishBasicMqtt5 TPublishMqtt5 TPublishQos1 TPublishQos1NoPayload TPublishQos1Dup TPublishQos2 TPublishQos2Mqtt5 TPublishQos2Upgraded TPublishSubscriberIdentifier TPublishRetain TPublishRetainMqtt5 TPublishDup TPublishMalTopicName TPublishMalPacketID TPublishMalProperties TPublishCopyBasic TPublishSpecQos0NoPacketID TPublishSpecQosMustPacketID TPublishDropOversize TPublishInvalidQos0NoPacketID TPublishInvalidQosMustPacketID TPublishInvalidSurplusSubID TPublishInvalidSurplusWildcard TPublishInvalidSurplusWildcard2 TPublishInvalidNoTopic TPublishInvalidTopicAlias TPublishInvalidExcessTopicAlias TPublishSpecDenySysTopic TPuback TPubackMqtt5 TPubackMalPacketID TPubackMalProperties TPubrec TPubrecMqtt5 TPubrecMqtt5IDInUse TPubrecMalPacketID TPubrecMalProperties TPubrecMalReasonCode TPubrecInvalidReason TPubrel TPubrelMqtt5 TPubrelMqtt5AckNoPacket TPubrelMalPacketID TPubrelMalProperties TPubrelInvalidReason TPubcomp TPubcompMqtt5 TPubcompMqtt5AckNoPacket TPubcompMalPacketID TPubcompMalProperties TPubcompInvalidReason TSubscribe TSubscribeMany TSubscribeMqtt5 TSubscribeRetainHandling1 TSubscribeRetainHandling2 TSubscribeRetainAsPublished TSubscribeMalPacketID TSubscribeMalTopic TSubscribeMalQos TSubscribeMalQosRange TSubscribeMalProperties TSubscribeInvalidQosMustPacketID TSubscribeSpecQosMustPacketID TSubscribeInvalidNoFilters TSubscribeInvalidFilter TSubscribeInvalidIdentifierOversize TSuback TSubackMany TSubackDeny TSubackUnspecifiedError TSubackUnspecifiedErrorMqtt5 TSubackMqtt5 TSubackPacketIDInUse TSubackInvalidFilter TSubackMalPacketID TSubackMalProperties TUnsubscribe TUnsubscribeMany TUnsubscribeMqtt5 TUnsubscribeDropProperties TUnsubscribeMalPacketID TUnsubscribeMalTopicName TUnsubscribeMalProperties TUnsubscribeInvalidQosMustPacketID TUnsubscribeSpecQosMustPacketID TUnsubscribeInvalidNoFilters TUnsuback TUnsubackMany TUnsubackMqtt5 TUnsubackPacketIDInUse TUnsubackMalPacketID TUnsubackMalProperties TPingreq TPingresp TDisconnect TDisconnectTakeover TDisconnectMqtt5 TDisconnectNormalMqtt5 TDisconnectSecondConnect TDisconnectReceiveMaximum TDisconnectDropProperties TDisconnectShuttingDown TDisconnectMalProperties TDisconnectMalReasonCode TDisconnectZeroNonZeroExpiry TAuth TAuthMalReasonCode TAuthMalProperties TAuthInvalidReason TAuthInvalidReason2 )
Variables ¶
var ( // QosCodes indicicates the reason codes for each Qos byte. QosCodes = map[byte]Code{ 0: CodeGrantedQos0, 1: CodeGrantedQos1, 2: CodeGrantedQos2, } CodeSuccess = Code{Code: 0x00, Reason: "success"} CodeDisconnect = Code{Code: 0x00, Reason: "disconnected"} CodeGrantedQos0 = Code{Code: 0x00, Reason: "granted qos 0"} CodeGrantedQos1 = Code{Code: 0x01, Reason: "granted qos 1"} CodeGrantedQos2 = Code{Code: 0x02, Reason: "granted qos 2"} CodeDisconnectWillMessage = Code{Code: 0x04, Reason: "disconnect with will message"} CodeNoMatchingSubscribers = Code{Code: 0x10, Reason: "no matching subscribers"} CodeNoSubscriptionExisted = Code{Code: 0x11, Reason: "no subscription existed"} CodeContinueAuthentication = Code{Code: 0x18, Reason: "continue authentication"} CodeReAuthenticate = Code{Code: 0x19, Reason: "re-authenticate"} ErrUnspecifiedError = Code{Code: 0x80, Reason: "unspecified error"} ErrMalformedPacket = Code{Code: 0x81, Reason: "malformed packet"} ErrMalformedProtocolName = Code{Code: 0x81, Reason: "malformed packet: protocol name"} ErrMalformedProtocolVersion = Code{Code: 0x81, Reason: "malformed packet: protocol version"} ErrMalformedFlags = Code{Code: 0x81, Reason: "malformed packet: flags"} ErrMalformedKeepalive = Code{Code: 0x81, Reason: "malformed packet: keepalive"} ErrMalformedPacketID = Code{Code: 0x81, Reason: "malformed packet: packet identifier"} ErrMalformedTopic = Code{Code: 0x81, Reason: "malformed packet: topic"} ErrMalformedWillTopic = Code{Code: 0x81, Reason: "malformed packet: will topic"} ErrMalformedWillPayload = Code{Code: 0x81, Reason: "malformed packet: will message"} ErrMalformedUsername = Code{Code: 0x81, Reason: "malformed packet: username"} ErrMalformedPassword = Code{Code: 0x81, Reason: "malformed packet: password"} ErrMalformedQos = Code{Code: 0x81, Reason: "malformed packet: qos"} ErrMalformedOffsetUintOutOfRange = Code{Code: 0x81, Reason: "malformed packet: offset uint out of range"} ErrMalformedOffsetBytesOutOfRange = Code{Code: 0x81, Reason: "malformed packet: offset bytes out of range"} ErrMalformedOffsetByteOutOfRange = Code{Code: 0x81, Reason: "malformed packet: offset byte out of range"} ErrMalformedOffsetBoolOutOfRange = Code{Code: 0x81, Reason: "malformed packet: offset boolean out of range"} ErrMalformedInvalidUTF8 = Code{Code: 0x81, Reason: "malformed packet: invalid utf-8 string"} ErrMalformedVariableByteInteger = Code{Code: 0x81, Reason: "malformed packet: variable byte integer out of range"} ErrMalformedBadProperty = Code{Code: 0x81, Reason: "malformed packet: unknown property"} ErrMalformedProperties = Code{Code: 0x81, Reason: "malformed packet: properties"} ErrMalformedWillProperties = Code{Code: 0x81, Reason: "malformed packet: will properties"} ErrMalformedSessionPresent = Code{Code: 0x81, Reason: "malformed packet: session present"} ErrMalformedReasonCode = Code{Code: 0x81, Reason: "malformed packet: reason code"} ErrProtocolViolation = Code{Code: 0x82, Reason: "protocol violation"} ErrProtocolViolationProtocolName = Code{Code: 0x82, Reason: "protocol violation: protocol name"} ErrProtocolViolationProtocolVersion = Code{Code: 0x82, Reason: "protocol violation: protocol version"} ErrProtocolViolationReservedBit = Code{Code: 0x82, Reason: "protocol violation: reserved bit not 0"} ErrProtocolViolationFlagNoUsername = Code{Code: 0x82, Reason: "protocol violation: username flag set but no value"} ErrProtocolViolationFlagNoPassword = Code{Code: 0x82, Reason: "protocol violation: password flag set but no value"} ErrProtocolViolationUsernameNoFlag = Code{Code: 0x82, Reason: "protocol violation: username set but no flag"} ErrProtocolViolationPasswordNoFlag = Code{Code: 0x82, Reason: "protocol violation: username set but no flag"} ErrProtocolViolationPasswordTooLong = Code{Code: 0x82, Reason: "protocol violation: password too long"} ErrProtocolViolationUsernameTooLong = Code{Code: 0x82, Reason: "protocol violation: username too long"} ErrProtocolViolationNoPacketID = Code{Code: 0x82, Reason: "protocol violation: missing packet id"} ErrProtocolViolationSurplusPacketID = Code{Code: 0x82, Reason: "protocol violation: surplus packet id"} ErrProtocolViolationQosOutOfRange = Code{Code: 0x82, Reason: "protocol violation: qos out of range"} ErrProtocolViolationSecondConnect = Code{Code: 0x82, Reason: "protocol violation: second connect packet"} ErrProtocolViolationZeroNonZeroExpiry = Code{Code: 0x82, Reason: "protocol violation: non-zero expiry"} ErrProtocolViolationRequireFirstConnect = Code{Code: 0x82, Reason: "protocol violation: first packet must be connect"} ErrProtocolViolationWillFlagNoPayload = Code{Code: 0x82, Reason: "protocol violation: will flag no payload"} ErrProtocolViolationWillFlagSurplusRetain = Code{Code: 0x82, Reason: "protocol violation: will flag surplus retain"} ErrProtocolViolationSurplusWildcard = Code{Code: 0x82, Reason: "protocol violation: topic contains wildcards"} ErrProtocolViolationSurplusSubID = Code{Code: 0x82, Reason: "protocol violation: contained subscription identifier"} ErrProtocolViolationInvalidTopic = Code{Code: 0x82, Reason: "protocol violation: invalid topic"} ErrProtocolViolationNoFilters = Code{Code: 0x82, Reason: "protocol violation: must contain at least one filter"} ErrProtocolViolationInvalidReason = Code{Code: 0x82, Reason: "protocol violation: invalid reason"} ErrProtocolViolationOversizeSubID = Code{Code: 0x82, Reason: "protocol violation: oversize subscription id"} ErrProtocolViolationDupNoQos = Code{Code: 0x82, Reason: "protocol violation: dup true with no qos"} ErrProtocolViolationUnsupportedProperty = Code{Code: 0x82, Reason: "protocol violation: unsupported property"} ErrProtocolViolationNoTopic = Code{Code: 0x82, Reason: "protocol violation: no topic or alias"} ErrImplementationSpecificError = Code{Code: 0x83, Reason: "implementation specific error"} ErrRejectPacket = Code{Code: 0x83, Reason: "packet rejected"} ErrUnsupportedProtocolVersion = Code{Code: 0x84, Reason: "unsupported protocol version"} ErrClientIdentifierNotValid = Code{Code: 0x85, Reason: "client identifier not valid"} ErrClientIdentifierTooLong = Code{Code: 0x85, Reason: "client identifier too long"} ErrBadUsernameOrPassword = Code{Code: 0x86, Reason: "bad username or password"} ErrNotAuthorized = Code{Code: 0x87, Reason: "not authorized"} ErrServerBusy = Code{Code: 0x89, Reason: "server busy"} ErrBanned = Code{Code: 0x8A, Reason: "banned"} ErrServerShuttingDown = Code{Code: 0x8B, Reason: "server shutting down"} ErrBadAuthenticationMethod = Code{Code: 0x8C, Reason: "bad authentication method"} ErrKeepAliveTimeout = Code{Code: 0x8D, Reason: "keep alive timeout"} ErrSessionTakenOver = Code{Code: 0x8E, Reason: "session takeover"} ErrTopicFilterInvalid = Code{Code: 0x8F, Reason: "topic filter invalid"} ErrTopicNameInvalid = Code{Code: 0x90, Reason: "topic name invalid"} ErrPacketIdentifierInUse = Code{Code: 0x91, Reason: "packet identifier in use"} ErrPacketIdentifierNotFound = Code{Code: 0x92, Reason: "packet identifier not found"} ErrReceiveMaximum = Code{Code: 0x93, Reason: "receive maximum exceeded"} ErrTopicAliasInvalid = Code{Code: 0x94, Reason: "topic alias invalid"} ErrPacketTooLarge = Code{Code: 0x95, Reason: "packet too large"} ErrMessageRateTooHigh = Code{Code: 0x96, Reason: "message rate too high"} ErrQuotaExceeded = Code{Code: 0x97, Reason: "quota exceeded"} ErrPendingClientWritesExceeded = Code{Code: 0x97, Reason: "too many pending writes"} ErrAdministrativeAction = Code{Code: 0x98, Reason: "administrative action"} ErrPayloadFormatInvalid = Code{Code: 0x99, Reason: "payload format invalid"} ErrRetainNotSupported = Code{Code: 0x9A, Reason: "retain not supported"} ErrQosNotSupported = Code{Code: 0x9B, Reason: "qos not supported"} ErrUseAnotherServer = Code{Code: 0x9C, Reason: "use another server"} ErrServerMoved = Code{Code: 0x9D, Reason: "server moved"} ErrConnectionRateExceeded = Code{Code: 0x9F, Reason: "connection rate exceeded"} ErrMaxConnectTime = Code{Code: 0xA0, Reason: "maximum connect time"} ErrSubscriptionIdentifiersNotSupported = Code{Code: 0xA1, Reason: "subscription identifiers not supported"} ErrWildcardSubscriptionsNotSupported = Code{Code: 0xA2, Reason: "wildcard subscriptions not supported"} // MQTTv3 specific bytes. Err3UnsupportedProtocolVersion = Code{Code: 0x01} Err3ClientIdentifierNotValid = Code{Code: 0x02} ErrMalformedUsernameOrPassword = Code{Code: 0x04} Err3NotAuthorized = Code{Code: 0x05} // V5CodesToV3 maps MQTTv5 Connack reason codes to MQTTv3 return codes. // This is required because MQTTv3 has different return byte specification. // See http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349257 V5CodesToV3 = map[Code]Code{ ErrUnsupportedProtocolVersion: Err3UnsupportedProtocolVersion, ErrClientIdentifierNotValid: Err3ClientIdentifierNotValid, ErrServerUnavailable: Err3ServerUnavailable, ErrMalformedUsername: ErrMalformedUsernameOrPassword, ErrMalformedPassword: ErrMalformedUsernameOrPassword, ErrBadUsernameOrPassword: Err3NotAuthorized, } )
var ( // ErrNoValidPacketAvailable indicates the packet type byte provided does not exist in the mqtt specification. ErrNoValidPacketAvailable error = errors.New("no valid packet available") // PacketNames is a map of packet bytes to human readable names, for easier debugging. PacketNames = map[byte]string{ 0: "Reserved", 1: "Connect", 2: "Connack", 3: "Publish", 4: "Puback", 5: "Pubrec", 6: "Pubrel", 7: "Pubcomp", 8: "Subscribe", 9: "Suback", 10: "Unsubscribe", 11: "Unsuback", 12: "Pingreq", 13: "Pingresp", 14: "Disconnect", 15: "Auth", } )
var TPacketData = map[byte]TPacketCases{ Connect: { { Case: TConnectMqtt31, Desc: "mqtt v3.1", Primary: true, RawBytes: []byte{ Connect << 4, 17, 0, 6, 'M', 'Q', 'I', 's', 'd', 'p', 3, 0, 0, 30, 0, 3, 'z', 'e', 'n', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 17, }, ProtocolVersion: 3, Connect: ConnectParams{ ProtocolName: []byte("MQIsdp"), Clean: false, Keepalive: 30, ClientIdentifier: "zen", }, }, }, { Case: TConnectMqtt311, Desc: "mqtt v3.1.1", Primary: true, RawBytes: []byte{ Connect << 4, 15, 0, 4, 'M', 'Q', 'T', 'T', 4, 0, 0, 60, 0, 3, 'z', 'e', 'n', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 15, }, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Clean: false, Keepalive: 60, ClientIdentifier: "zen", }, }, }, { Case: TConnectMqtt5, Desc: "mqtt v5", Primary: true, RawBytes: []byte{ Connect << 4, 87, 0, 4, 'M', 'Q', 'T', 'T', 5, 0, 0, 30, 71, 17, 0, 0, 0, 120, 21, 0, 5, 'S', 'H', 'A', '-', '1', 22, 0, 9, 'a', 'u', 't', 'h', '-', 'd', 'a', 't', 'a', 23, 1, 25, 1, 33, 1, 244, 34, 3, 231, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, 38, 0, 4, 'k', 'e', 'y', '2', 0, 6, 'v', 'a', 'l', 'u', 'e', '2', 39, 0, 0, 125, 0, 0, 3, 'z', 'e', 'n', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 87, }, ProtocolVersion: 5, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Clean: false, Keepalive: 30, ClientIdentifier: "zen", }, Properties: Properties{ SessionExpiryInterval: uint32(120), SessionExpiryIntervalFlag: true, AuthenticationMethod: "SHA-1", AuthenticationData: []byte("auth-data"), RequestProblemInfo: byte(1), RequestProblemInfoFlag: true, RequestResponseInfo: byte(1), ReceiveMaximum: uint16(500), TopicAliasMaximum: uint16(999), User: []UserProperty{ { Key: "hello", Val: "世界", }, { Key: "key2", Val: "value2", }, }, MaximumPacketSize: uint32(32000), }, }, }, { Case: TConnectClean, Desc: "mqtt 3.1.1, clean session", RawBytes: []byte{ Connect << 4, 15, 0, 4, 'M', 'Q', 'T', 'T', 4, 2, 0, 45, 0, 3, 'z', 'e', 'n', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 15, }, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Clean: true, Keepalive: 45, ClientIdentifier: "zen", }, }, }, { Case: TConnectMqtt5LWT, Desc: "mqtt 5 clean session, lwt", RawBytes: []byte{ Connect << 4, 47, 0, 4, 'M', 'Q', 'T', 'T', 5, 14, 0, 30, 10, 17, 0, 0, 0, 120, 39, 0, 0, 125, 0, 0, 3, 'z', 'e', 'n', 5, 24, 0, 0, 2, 88, 0, 3, 'l', 'w', 't', 0, 8, 'n', 'o', 't', 'a', 'g', 'a', 'i', 'n', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connect, Remaining: 42, }, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Clean: true, Keepalive: 30, ClientIdentifier: "zen", WillFlag: true, WillTopic: "lwt", WillPayload: []byte("notagain"), WillQos: 1, WillProperties: Properties{ WillDelayInterval: uint32(600), }, }, Properties: Properties{ SessionExpiryInterval: uint32(120), SessionExpiryIntervalFlag: true, MaximumPacketSize: uint32(32000), }, }, }, { Case: TConnectUserPass, Desc: "mqtt 3.1.1, username, password", RawBytes: []byte{ Connect << 4, 28, 0, 4, 'M', 'Q', 'T', 'T', 4, 0 | 1<<6 | 1<<7, 0, 20, 0, 3, 'z', 'e', 'n', 0, 5, 'm', 'o', 'c', 'h', 'i', 0, 4, ',', '.', '/', ';', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 28, }, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Clean: false, Keepalive: 20, ClientIdentifier: "zen", UsernameFlag: true, PasswordFlag: true, Username: []byte("mochi"), Password: []byte(",./;"), }, }, }, { Case: TConnectUserPassLWT, Desc: "mqtt 3.1.1, username, password, lwt", Primary: true, RawBytes: []byte{ Connect << 4, 44, 0, 4, 'M', 'Q', 'T', 'T', 4, 206, 0, 120, 0, 3, 'z', 'e', 'n', 0, 3, 'l', 'w', 't', 0, 9, 'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n', 0, 5, 'm', 'o', 'c', 'h', 'i', 0, 4, ',', '.', '/', ';', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 44, }, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Clean: true, Keepalive: 120, ClientIdentifier: "zen", UsernameFlag: true, PasswordFlag: true, Username: []byte("mochi"), Password: []byte(",./;"), WillFlag: true, WillTopic: "lwt", WillPayload: []byte("not again"), WillQos: 1, }, }, }, { Case: TConnectZeroByteUsername, Desc: "username flag but 0 byte username", Group: "decode", RawBytes: []byte{ Connect << 4, 23, 0, 4, 'M', 'Q', 'T', 'T', 5, 130, 0, 30, 5, 17, 0, 0, 0, 120, 0, 3, 'z', 'e', 'n', 0, 0, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 23, }, ProtocolVersion: 5, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Clean: true, Keepalive: 30, ClientIdentifier: "zen", Username: []byte{}, UsernameFlag: true, }, Properties: Properties{ SessionExpiryInterval: uint32(120), SessionExpiryIntervalFlag: true, }, }, }, { Case: TConnectMalProtocolName, Desc: "malformed protocol name", Group: "decode", FailFirst: ErrMalformedProtocolName, RawBytes: []byte{ Connect << 4, 0, 0, 7, 'M', 'Q', 'I', 's', 'd', }, }, { Case: TConnectMalProtocolVersion, Desc: "malformed protocol version", Group: "decode", FailFirst: ErrMalformedProtocolVersion, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', }, }, { Case: TConnectMalFlags, Desc: "malformed flags", Group: "decode", FailFirst: ErrMalformedFlags, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, }, }, { Case: TConnectMalKeepalive, Desc: "malformed keepalive", Group: "decode", FailFirst: ErrMalformedKeepalive, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 0, }, }, { Case: TConnectMalClientID, Desc: "malformed client id", Group: "decode", FailFirst: ErrClientIdentifierNotValid, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 0, 0, 20, 0, 3, 'z', 'e', }, }, { Case: TConnectMalWillTopic, Desc: "malformed will topic", Group: "decode", FailFirst: ErrMalformedWillTopic, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 14, 0, 20, 0, 3, 'z', 'e', 'n', 0, 6, 'l', }, }, { Case: TConnectMalWillFlag, Desc: "malformed will flag", Group: "decode", FailFirst: ErrMalformedWillPayload, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 14, 0, 20, 0, 3, 'z', 'e', 'n', 0, 3, 'l', 'w', 't', 0, 9, 'n', 'o', 't', ' ', 'a', }, }, { Case: TConnectMalUsername, Desc: "malformed username", Group: "decode", FailFirst: ErrMalformedUsername, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 206, 0, 20, 0, 3, 'z', 'e', 'n', 0, 3, 'l', 'w', 't', 0, 9, 'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n', 0, 5, 'm', 'o', 'c', }, }, { Case: TConnectInvalidFlagNoUsername, Desc: "username flag with no username bytes", Group: "decode", FailFirst: ErrProtocolViolationFlagNoUsername, RawBytes: []byte{ Connect << 4, 17, 0, 4, 'M', 'Q', 'T', 'T', 5, 130, 0, 20, 0, 0, 3, 'z', 'e', 'n', }, }, { Case: TConnectMalPassword, Desc: "malformed password", Group: "decode", FailFirst: ErrMalformedPassword, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 206, 0, 20, 0, 3, 'z', 'e', 'n', 0, 3, 'l', 'w', 't', 0, 9, 'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n', 0, 5, 'm', 'o', 'c', 'h', 'i', 0, 4, ',', '.', }, }, { Case: TConnectMalFixedHeader, Desc: "malformed fixedheader oversize", Group: "decode", FailFirst: ErrMalformedProtocolName, RawBytes: []byte{ Connect << 4, 255, 255, 255, 255, 255, }, }, { Case: TConnectMalReservedBit, Desc: "reserved bit not 0", Group: "nodecode", FailFirst: ErrProtocolViolation, RawBytes: []byte{ Connect << 4, 15, 0, 4, 'M', 'Q', 'T', 'T', 4, 1, 0, 45, 0, 3, 'z', 'e', 'n', }, }, { Case: TConnectMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Connect << 4, 47, 0, 4, 'M', 'Q', 'T', 'T', 5, 14, 0, 30, 10, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TConnectMalWillProperties, Desc: "malformed will properties", Group: "decode", FailFirst: ErrMalformedWillProperties, RawBytes: []byte{ Connect << 4, 47, 0, 4, 'M', 'Q', 'T', 'T', 5, 14, 0, 30, 10, 17, 0, 0, 0, 120, 39, 0, 0, 125, 0, 0, 3, 'z', 'e', 'n', 5, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TConnectInvalidProtocolName, Desc: "invalid protocol name", Group: "validate", Expect: ErrProtocolViolationProtocolName, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, Connect: ConnectParams{ ProtocolName: []byte("stuff"), }, }, }, { Case: TConnectInvalidProtocolVersion, Desc: "invalid protocol version", Group: "validate", Expect: ErrProtocolViolationProtocolVersion, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 2, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), }, }, }, { Case: TConnectInvalidProtocolVersion2, Desc: "invalid protocol version", Group: "validate", Expect: ErrProtocolViolationProtocolVersion, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 2, Connect: ConnectParams{ ProtocolName: []byte("MQIsdp"), }, }, }, { Case: TConnectInvalidReservedBit, Desc: "reserved bit not 0", Group: "validate", Expect: ErrProtocolViolationReservedBit, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), }, ReservedBit: 1, }, }, { Case: TConnectInvalidClientIDTooLong, Desc: "client id too long", Group: "validate", Expect: ErrClientIdentifierNotValid, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), ClientIdentifier: func() string { return string(make([]byte, 65536)) }(), }, }, }, { Case: TConnectInvalidUsernameNoFlag, Desc: "has username but no flag", Group: "validate", Expect: ErrProtocolViolationUsernameNoFlag, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Username: []byte("username"), }, }, }, { Case: TConnectInvalidFlagNoPassword, Desc: "has password flag but no password", Group: "validate", Expect: ErrProtocolViolationFlagNoPassword, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), PasswordFlag: true, }, }, }, { Case: TConnectInvalidPasswordNoFlag, Desc: "has password flag but no password", Group: "validate", Expect: ErrProtocolViolationPasswordNoFlag, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Password: []byte("password"), }, }, }, { Case: TConnectInvalidUsernameTooLong, Desc: "username too long", Group: "validate", Expect: ErrProtocolViolationUsernameTooLong, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), UsernameFlag: true, Username: func() []byte { return make([]byte, 65536) }(), }, }, }, { Case: TConnectInvalidPasswordTooLong, Desc: "password too long", Group: "validate", Expect: ErrProtocolViolationPasswordTooLong, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), UsernameFlag: true, Username: []byte{}, PasswordFlag: true, Password: func() []byte { return make([]byte, 65536) }(), }, }, }, { Case: TConnectInvalidWillFlagNoPayload, Desc: "will flag no payload", Group: "validate", Expect: ErrProtocolViolationWillFlagNoPayload, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), WillFlag: true, }, }, }, { Case: TConnectInvalidWillFlagQosOutOfRange, Desc: "will flag no payload", Group: "validate", Expect: ErrProtocolViolationQosOutOfRange, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), WillFlag: true, WillTopic: "a/b/c", WillPayload: []byte{'b'}, WillQos: 4, }, }, }, { Case: TConnectInvalidWillSurplusRetain, Desc: "no will flag surplus retain", Group: "validate", Expect: ErrProtocolViolationWillFlagSurplusRetain, Packet: &Packet{ FixedHeader: FixedHeader{Type: Connect}, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), WillRetain: true, }, }, }, { Case: TConnectSpecInvalidUTF8D800, Desc: "invalid utf8 string (a) - code point U+D800", Group: "decode", FailFirst: ErrClientIdentifierNotValid, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 0, 0, 20, 0, 4, 'e', 0xed, 0xa0, 0x80, }, }, { Case: TConnectSpecInvalidUTF8DFFF, Desc: "invalid utf8 string (b) - code point U+DFFF", Group: "decode", FailFirst: ErrClientIdentifierNotValid, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 0, 0, 20, 0, 4, 'e', 0xed, 0xa3, 0xbf, }, }, { Case: TConnectSpecInvalidUTF80000, Desc: "invalid utf8 string (c) - code point U+0000", Group: "decode", FailFirst: ErrClientIdentifierNotValid, RawBytes: []byte{ Connect << 4, 0, 0, 4, 'M', 'Q', 'T', 'T', 4, 0, 0, 20, 0, 3, 'e', 0xc0, 0x80, }, }, { Case: TConnectSpecInvalidUTF8NoSkip, Desc: "utf8 string must not skip or strip code point U+FEFF", RawBytes: []byte{ Connect << 4, 18, 0, 4, 'M', 'Q', 'T', 'T', 4, 0, 0, 20, 0, 6, 'e', 'b', 0xEF, 0xBB, 0xBF, 'd', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connect, Remaining: 16, }, ProtocolVersion: 4, Connect: ConnectParams{ ProtocolName: []byte("MQTT"), Keepalive: 20, ClientIdentifier: string([]byte{'e', 'b', 0xEF, 0xBB, 0xBF, 'd'}), }, }, }, }, Connack: { { Case: TConnackAcceptedNoSession, Desc: "accepted, no session", Primary: true, RawBytes: []byte{ Connack << 4, 2, 0, CodeSuccess.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, SessionPresent: false, ReasonCode: CodeSuccess.Code, }, }, { Case: TConnackAcceptedSessionExists, Desc: "accepted, session exists", Primary: true, RawBytes: []byte{ Connack << 4, 2, 1, CodeSuccess.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, SessionPresent: true, ReasonCode: CodeSuccess.Code, }, }, { Case: TConnackAcceptedAdjustedExpiryInterval, Desc: "accepted, no session, adjusted expiry interval mqtt5", Primary: true, RawBytes: []byte{ Connack << 4, 11, 0, CodeSuccess.Code, 8, 17, 0, 0, 0, 120, 19, 0, 10, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 11, }, ReasonCode: CodeSuccess.Code, Properties: Properties{ SessionExpiryInterval: uint32(120), SessionExpiryIntervalFlag: true, ServerKeepAlive: uint16(10), ServerKeepAliveFlag: true, }, }, }, { Case: TConnackAcceptedMqtt5, Desc: "accepted no session mqtt5", Primary: true, RawBytes: []byte{ Connack << 4, 124, 0, CodeSuccess.Code, 121, 17, 0, 0, 0, 120, 18, 0, 8, 'm', 'o', 'c', 'h', 'i', '-', 'v', '5', 19, 0, 20, 21, 0, 5, 'S', 'H', 'A', '-', '1', 22, 0, 9, 'a', 'u', 't', 'h', '-', 'd', 'a', 't', 'a', 26, 0, 8, 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', 28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', 31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', 33, 1, 244, 34, 3, 231, 36, 1, 37, 1, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, 38, 0, 4, 'k', 'e', 'y', '2', 0, 6, 'v', 'a', 'l', 'u', 'e', '2', 39, 0, 0, 125, 0, 40, 1, 41, 1, 42, 1, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 124, }, SessionPresent: false, ReasonCode: CodeSuccess.Code, Properties: Properties{ SessionExpiryInterval: uint32(120), SessionExpiryIntervalFlag: true, AssignedClientID: "mochi-v5", ServerKeepAlive: uint16(20), ServerKeepAliveFlag: true, AuthenticationMethod: "SHA-1", AuthenticationData: []byte("auth-data"), ResponseInfo: "response", ServerReference: "mochi-2", ReasonString: "reason", ReceiveMaximum: uint16(500), TopicAliasMaximum: uint16(999), MaximumQos: byte(1), MaximumQosFlag: true, RetainAvailable: byte(1), RetainAvailableFlag: true, User: []UserProperty{ { Key: "hello", Val: "世界", }, { Key: "key2", Val: "value2", }, }, MaximumPacketSize: uint32(32000), WildcardSubAvailable: byte(1), WildcardSubAvailableFlag: true, SubIDAvailable: byte(1), SubIDAvailableFlag: true, SharedSubAvailable: byte(1), SharedSubAvailableFlag: true, }, }, }, { Case: TConnackMinMqtt5, Desc: "accepted min properties mqtt5", Primary: true, RawBytes: []byte{ Connack << 4, 16, 1, CodeSuccess.Code, 13, 18, 0, 5, 'm', 'o', 'c', 'h', 'i', 19, 0, 20, 36, 1, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 16, }, SessionPresent: true, ReasonCode: CodeSuccess.Code, Properties: Properties{ ServerKeepAlive: uint16(20), ServerKeepAliveFlag: true, AssignedClientID: "mochi", MaximumQos: byte(1), MaximumQosFlag: true, }, }, }, { Case: TConnackMinCleanMqtt5, Desc: "accepted min properties mqtt5b", Primary: true, RawBytes: []byte{ Connack << 4, 6, 0, CodeSuccess.Code, 3, 19, 0, 10, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 16, }, SessionPresent: false, ReasonCode: CodeSuccess.Code, Properties: Properties{ ServerKeepAlive: uint16(10), ServerKeepAliveFlag: true, }, }, }, { Case: TConnackInvalidMinMqtt5, Desc: "failure min properties mqtt5", Primary: true, RawBytes: append([]byte{ Connack << 4, 26, 0, ErrUnspecifiedError.Code, 23, 19, 0, 20, 31, 0, 17, }, []byte(ErrUnspecifiedError.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 25, }, SessionPresent: false, ReasonCode: ErrUnspecifiedError.Code, Properties: Properties{ ServerKeepAlive: uint16(20), ServerKeepAliveFlag: true, ReasonString: ErrUnspecifiedError.Reason, }, }, }, { Case: TConnackProtocolViolationNoSession, Desc: "miscellaneous protocol violation", RawBytes: []byte{ Connack << 4, 2, 0, ErrProtocolViolation.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, ReasonCode: ErrProtocolViolation.Code, }, }, { Case: TConnackBadProtocolVersion, Desc: "bad protocol version", RawBytes: []byte{ Connack << 4, 2, 1, ErrProtocolViolationProtocolVersion.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, SessionPresent: true, ReasonCode: ErrProtocolViolationProtocolVersion.Code, }, }, { Case: TConnackBadClientID, Desc: "bad client id", RawBytes: []byte{ Connack << 4, 2, 1, ErrClientIdentifierNotValid.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, SessionPresent: true, ReasonCode: ErrClientIdentifierNotValid.Code, }, }, { Case: TConnackServerUnavailable, Desc: "server unavailable", RawBytes: []byte{ Connack << 4, 2, 1, ErrServerUnavailable.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, SessionPresent: true, ReasonCode: ErrServerUnavailable.Code, }, }, { Case: TConnackBadUsernamePassword, Desc: "bad username or password", RawBytes: []byte{ Connack << 4, 2, 1, ErrBadUsernameOrPassword.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, SessionPresent: true, ReasonCode: ErrBadUsernameOrPassword.Code, }, }, { Case: TConnackBadUsernamePasswordNoSession, Desc: "bad username or password no session", RawBytes: []byte{ Connack << 4, 2, 0, Err3NotAuthorized.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, ReasonCode: Err3NotAuthorized.Code, }, }, { Case: TConnackMqtt5BadUsernamePasswordNoSession, Desc: "mqtt5 bad username or password no session", RawBytes: []byte{ Connack << 4, 3, 0, ErrBadUsernameOrPassword.Code, 0, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, ReasonCode: ErrBadUsernameOrPassword.Code, }, }, { Case: TConnackNotAuthorised, Desc: "not authorised", RawBytes: []byte{ Connack << 4, 2, 1, ErrNotAuthorized.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Connack, Remaining: 2, }, SessionPresent: true, ReasonCode: ErrNotAuthorized.Code, }, }, { Case: TConnackDropProperties, Desc: "drop oversize properties", Group: "encode", RawBytes: []byte{ Connack << 4, 40, 0, CodeSuccess.Code, 19, 28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', 31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, ActualBytes: []byte{ Connack << 4, 13, 0, CodeSuccess.Code, 10, 28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', }, Packet: &Packet{ Mods: Mods{ MaxSize: 5, }, ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 40, }, ReasonCode: CodeSuccess.Code, Properties: Properties{ ReasonString: "reason", ServerReference: "mochi-2", User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TConnackDropPropertiesPartial, Desc: "drop oversize properties partial", Group: "encode", RawBytes: []byte{ Connack << 4, 40, 0, CodeSuccess.Code, 19, 28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', 31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, ActualBytes: []byte{ Connack << 4, 22, 0, CodeSuccess.Code, 19, 28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', 31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', }, Packet: &Packet{ Mods: Mods{ MaxSize: 18, }, ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Connack, Remaining: 40, }, ReasonCode: CodeSuccess.Code, Properties: Properties{ ReasonString: "reason", ServerReference: "mochi-2", User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TConnackMalSessionPresent, Desc: "malformed session present", Group: "decode", FailFirst: ErrMalformedSessionPresent, RawBytes: []byte{ Connect << 4, 2, }, }, { Case: TConnackMalReturnCode, Desc: "malformed bad return Code", Group: "decode", FailFirst: ErrMalformedReasonCode, RawBytes: []byte{ Connect << 4, 2, 0, }, }, { Case: TConnackMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Connack << 4, 40, 0, CodeSuccess.Code, 19, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Publish: { { Case: TPublishNoPayload, Desc: "no payload", Primary: true, RawBytes: []byte{ Publish << 4, 7, 0, 5, 'a', '/', 'b', '/', 'c', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Remaining: 7, }, TopicName: "a/b/c", Payload: []byte{}, }, }, { Case: TPublishBasic, Desc: "basic", Primary: true, RawBytes: []byte{ Publish << 4, 18, 0, 5, 'a', '/', 'b', '/', 'c', 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Remaining: 18, }, TopicName: "a/b/c", Payload: []byte("hello mochi"), }, }, { Case: TPublishMqtt5, Desc: "mqtt v5", Primary: true, RawBytes: []byte{ Publish << 4, 77, 0, 5, 'a', '/', 'b', '/', 'c', 58, 1, 1, 2, 0, 0, 0, 2, 3, 0, 10, 't', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n', 8, 0, 5, 'a', '/', 'b', '/', 'c', 9, 0, 4, 'd', 'a', 't', 'a', 11, 202, 212, 19, 35, 0, 3, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Publish, Remaining: 77, }, TopicName: "a/b/c", Properties: Properties{ PayloadFormat: byte(1), PayloadFormatFlag: true, MessageExpiryInterval: uint32(2), ContentType: "text/plain", ResponseTopic: "a/b/c", CorrelationData: []byte("data"), SubscriptionIdentifier: []int{322122}, TopicAlias: uint16(3), TopicAliasFlag: true, User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, Payload: []byte("hello mochi"), }, }, { Case: TPublishBasicTopicAliasOnly, Desc: "mqtt v5 topic alias only", Primary: true, RawBytes: []byte{ Publish << 4, 17, 0, 0, 3, 35, 0, 1, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Publish, Remaining: 17, }, Properties: Properties{ TopicAlias: 1, TopicAliasFlag: true, }, Payload: []byte("hello mochi"), }, }, { Case: TPublishBasicMqtt5, Desc: "mqtt basic v5", Primary: true, RawBytes: []byte{ Publish << 4, 22, 0, 5, 'a', '/', 'b', '/', 'c', 3, 35, 0, 1, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Publish, Remaining: 22, }, TopicName: "a/b/c", Properties: Properties{ TopicAlias: uint16(1), TopicAliasFlag: true, }, Payload: []byte("hello mochi"), }, }, { Case: TPublishQos1, Desc: "qos:1, packet id", Primary: true, RawBytes: []byte{ Publish<<4 | 1<<1, 20, 0, 5, 'a', '/', 'b', '/', 'c', 0, 7, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Qos: 1, Remaining: 20, }, TopicName: "a/b/c", Payload: []byte("hello mochi"), PacketID: 7, }, }, { Case: TPublishQos1Dup, Desc: "qos:1, dup:true, packet id", Primary: true, RawBytes: []byte{ Publish<<4 | 2 | 8, 20, 0, 5, 'a', '/', 'b', '/', 'c', 0, 7, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Qos: 1, Remaining: 20, Dup: true, }, TopicName: "a/b/c", Payload: []byte("hello mochi"), PacketID: 7, }, }, { Case: TPublishQos1NoPayload, Desc: "qos:1, packet id, no payload", Primary: true, RawBytes: []byte{ Publish<<4 | 2, 9, 0, 5, 'y', '/', 'u', '/', 'i', 0, 7, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Qos: 1, Remaining: 9, }, TopicName: "y/u/i", PacketID: 7, Payload: []byte{}, }, }, { Case: TPublishQos2, Desc: "qos:2, packet id", Primary: true, RawBytes: []byte{ Publish<<4 | 2<<1, 14, 0, 5, 'a', '/', 'b', '/', 'c', 0, 7, 'h', 'e', 'l', 'l', 'o', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Qos: 2, Remaining: 14, }, TopicName: "a/b/c", Payload: []byte("hello"), PacketID: 7, }, }, { Case: TPublishQos2Mqtt5, Desc: "mqtt v5", Primary: true, RawBytes: []byte{ Publish<<4 | 2<<1, 37, 0, 5, 'a', '/', 'b', '/', 'c', 0, 7, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Publish, Remaining: 37, Qos: 2, }, PacketID: 7, TopicName: "a/b/c", Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, Payload: []byte("hello mochi"), }, }, { Case: TPublishSubscriberIdentifier, Desc: "subscription identifiers", Primary: true, RawBytes: []byte{ Publish << 4, 23, 0, 5, 'a', '/', 'b', '/', 'c', 4, 11, 2, 11, 3, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Publish, Remaining: 23, }, TopicName: "a/b/c", Properties: Properties{ SubscriptionIdentifier: []int{2, 3}, }, Payload: []byte("hello mochi"), }, }, { Case: TPublishQos2Upgraded, Desc: "qos:2, upgraded from publish to qos2 sub", Primary: true, RawBytes: []byte{ Publish<<4 | 2<<1, 20, 0, 5, 'a', '/', 'b', '/', 'c', 0, 1, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Qos: 2, Remaining: 18, }, TopicName: "a/b/c", Payload: []byte("hello mochi"), PacketID: 1, }, }, { Case: TPublishRetain, Desc: "retain", RawBytes: []byte{ Publish<<4 | 1<<0, 18, 0, 5, 'a', '/', 'b', '/', 'c', 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Retain: true, }, TopicName: "a/b/c", Payload: []byte("hello mochi"), }, }, { Case: TPublishRetainMqtt5, Desc: "retain mqtt5", RawBytes: []byte{ Publish<<4 | 1<<0, 19, 0, 5, 'a', '/', 'b', '/', 'c', 0, 'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Publish, Retain: true, Remaining: 19, }, TopicName: "a/b/c", Properties: Properties{}, Payload: []byte("hello mochi"), }, }, { Case: TPublishDup, Desc: "dup", RawBytes: []byte{ Publish<<4 | 8, 10, 0, 3, 'a', '/', 'b', 'h', 'e', 'l', 'l', 'o', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Dup: true, }, TopicName: "a/b", Payload: []byte("hello"), }, }, { Case: TPublishMalTopicName, Desc: "malformed topic name", Group: "decode", FailFirst: ErrMalformedTopic, RawBytes: []byte{ Publish << 4, 7, 0, 5, 'a', '/', 0, 11, }, }, { Case: TPublishMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Publish<<4 | 2, 7, 0, 5, 'x', '/', 'y', '/', 'z', 0, }, }, { Case: TPublishMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Publish << 4, 35, 0, 5, 'a', '/', 'b', '/', 'c', 16, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TPublishCopyBasic, Desc: "basic copyable", Group: "copy", RawBytes: []byte{ Publish << 4, 18, 0, 5, 'z', '/', 'e', '/', 'n', 'm', 'o', 'c', 'h', 'i', ' ', 'm', 'o', 'c', 'h', 'i', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Dup: true, Retain: true, Qos: 1, }, TopicName: "z/e/n", Payload: []byte("mochi mochi"), }, }, { Case: TPublishSpecQos0NoPacketID, Desc: "packet id must be 0 if qos is 0 (a)", Group: "encode", RawBytes: []byte{ Publish << 4, 12, 0, 5, 'a', '/', 'b', '/', 'c', 0, 3, 'h', 'e', 'l', 'l', 'o', }, ActualBytes: []byte{ Publish << 4, 12, 0, 5, 'a', '/', 'b', '/', 'c', 'h', 'e', 'l', 'l', 'o', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Remaining: 12, }, TopicName: "a/b/c", Payload: []byte("hello"), }, }, { Case: TPublishSpecQosMustPacketID, Desc: "no packet id with qos > 0", Group: "encode", Expect: ErrProtocolViolationNoPacketID, RawBytes: []byte{ Publish<<4 | 2, 14, 0, 5, 'a', '/', 'b', '/', 'c', 0, 0, 'h', 'e', 'l', 'l', 'o', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Qos: 1, }, TopicName: "a/b/c", Payload: []byte("hello"), PacketID: 0, }, }, { Case: TPublishDropOversize, Desc: "drop oversized publish packet", Group: "encode", FailFirst: ErrPacketTooLarge, RawBytes: []byte{ Publish << 4, 10, 0, 3, 'a', '/', 'b', 'h', 'e', 'l', 'l', 'o', }, Packet: &Packet{ Mods: Mods{ MaxSize: 2, }, FixedHeader: FixedHeader{ Type: Publish, }, TopicName: "a/b", Payload: []byte("hello"), }, }, { Case: TPublishInvalidQos0NoPacketID, Desc: "packet id must be 0 if qos is 0 (b)", Group: "validate", Expect: ErrProtocolViolationSurplusPacketID, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Remaining: 12, Qos: 0, }, TopicName: "a/b/c", Payload: []byte("hello"), PacketID: 3, }, }, { Case: TPublishInvalidQosMustPacketID, Desc: "no packet id with qos > 0", Group: "validate", Expect: ErrProtocolViolationNoPacketID, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, Qos: 1, }, PacketID: 0, }, }, { Case: TPublishInvalidSurplusSubID, Desc: "surplus subscription identifier", Group: "validate", Expect: ErrProtocolViolationSurplusSubID, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, }, Properties: Properties{ SubscriptionIdentifier: []int{1}, }, TopicName: "a/b", }, }, { Case: TPublishInvalidSurplusWildcard, Desc: "topic contains wildcards", Group: "validate", Expect: ErrProtocolViolationSurplusWildcard, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, }, TopicName: "a/+", }, }, { Case: TPublishInvalidSurplusWildcard2, Desc: "topic contains wildcards 2", Group: "validate", Expect: ErrProtocolViolationSurplusWildcard, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, }, TopicName: "a/#", }, }, { Case: TPublishInvalidNoTopic, Desc: "no topic or alias specified", Group: "validate", Expect: ErrProtocolViolationNoTopic, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, }, }, }, { Case: TPublishInvalidExcessTopicAlias, Desc: "topic alias over maximum", Group: "validate", Expect: ErrTopicAliasInvalid, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, }, Properties: Properties{ TopicAlias: 1025, }, TopicName: "a/b", }, }, { Case: TPublishInvalidTopicAlias, Desc: "topic alias flag and no alias", Group: "validate", Expect: ErrTopicAliasInvalid, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, }, Properties: Properties{ TopicAliasFlag: true, TopicAlias: 0, }, TopicName: "a/b/", }, }, { Case: TPublishSpecDenySysTopic, Desc: "deny publishing to $SYS topics", Group: "validate", Expect: CodeSuccess, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Publish, }, TopicName: "$SYS/any", Payload: []byte("y"), }, RawBytes: []byte{ Publish << 4, 11, 0, 5, '$', 'S', 'Y', 'S', '/', 'a', 'n', 'y', 'y', }, }, }, Puback: { { Case: TPuback, Desc: "puback", Primary: true, RawBytes: []byte{ Puback << 4, 2, 0, 7, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Puback, Remaining: 2, }, PacketID: 7, }, }, { Case: TPubackMqtt5, Desc: "mqtt5", Primary: true, RawBytes: []byte{ Puback << 4, 20, 0, 7, CodeGrantedQos0.Code, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Puback, Remaining: 20, }, PacketID: 7, ReasonCode: CodeGrantedQos0.Code, Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TPubackMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Puback << 4, 2, 0, }, }, { Case: TPubackMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Puback << 4, 20, 0, 7, CodeGrantedQos0.Code, 16, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Pubrec: { { Case: TPubrec, Desc: "pubrec", Primary: true, RawBytes: []byte{ Pubrec << 4, 2, 0, 7, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pubrec, Remaining: 2, }, PacketID: 7, }, }, { Case: TPubrecMqtt5, Desc: "pubrec mqtt5", Primary: true, RawBytes: []byte{ Pubrec << 4, 20, 0, 7, CodeSuccess.Code, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Pubrec, Remaining: 20, }, PacketID: 7, ReasonCode: CodeSuccess.Code, Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TPubrecMqtt5IDInUse, Desc: "packet id in use mqtt5", Primary: true, RawBytes: []byte{ Pubrec << 4, 31, 0, 7, ErrPacketIdentifierInUse.Code, 27, 31, 0, 24, 'p', 'a', 'c', 'k', 'e', 't', ' ', 'i', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r', ' ', 'i', 'n', ' ', 'u', 's', 'e', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Pubrec, Remaining: 31, }, PacketID: 7, ReasonCode: ErrPacketIdentifierInUse.Code, Properties: Properties{ ReasonString: ErrPacketIdentifierInUse.Reason, }, }, }, { Case: TPubrecMalReasonCode, Desc: "malformed reason code", Group: "decode", FailFirst: ErrMalformedReasonCode, RawBytes: []byte{ Pubrec << 4, 31, 0, 7, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TPubrecInvalidReason, Desc: "invalid reason code", Group: "validate", FailFirst: ErrProtocolViolationInvalidReason, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pubrec, }, PacketID: 7, ReasonCode: ErrConnectionRateExceeded.Code, }, }, { Case: TPubrecMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Pubrec << 4, 2, 0, }, }, { Case: TPubrecMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Pubrec << 4, 31, 0, 7, ErrPacketIdentifierInUse.Code, 27, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Pubrel: { { Case: TPubrel, Desc: "pubrel", Primary: true, RawBytes: []byte{ Pubrel<<4 | 1<<1, 2, 0, 7, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pubrel, Remaining: 2, Qos: 1, }, PacketID: 7, }, }, { Case: TPubrelMqtt5, Desc: "pubrel mqtt5", Primary: true, RawBytes: []byte{ Pubrel<<4 | 1<<1, 20, 0, 7, CodeSuccess.Code, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Pubrel, Remaining: 20, Qos: 1, }, PacketID: 7, ReasonCode: CodeSuccess.Code, Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TPubrelMqtt5AckNoPacket, Desc: "mqtt5 no packet id ack", Primary: true, RawBytes: append([]byte{ Pubrel<<4 | 1<<1, 34, 0, 7, ErrPacketIdentifierNotFound.Code, 30, 31, 0, byte(len(ErrPacketIdentifierNotFound.Reason)), }, []byte(ErrPacketIdentifierNotFound.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Pubrel, Remaining: 34, Qos: 1, }, PacketID: 7, ReasonCode: ErrPacketIdentifierNotFound.Code, Properties: Properties{ ReasonString: ErrPacketIdentifierNotFound.Reason, }, }, }, { Case: TPubrelInvalidReason, Desc: "invalid reason code", Group: "validate", FailFirst: ErrProtocolViolationInvalidReason, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pubrel, }, PacketID: 7, ReasonCode: ErrConnectionRateExceeded.Code, }, }, { Case: TPubrelMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Pubrel << 4, 2, 0, }, }, { Case: TPubrelMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Pubrel<<4 | 1<<1, 20, 0, 7, CodeSuccess.Code, 16, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Pubcomp: { { Case: TPubcomp, Desc: "pubcomp", Primary: true, RawBytes: []byte{ Pubcomp << 4, 2, 0, 7, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pubcomp, Remaining: 2, }, PacketID: 7, }, }, { Case: TPubcompMqtt5, Desc: "mqtt5", Primary: true, RawBytes: []byte{ Pubcomp << 4, 20, 0, 7, CodeSuccess.Code, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Pubcomp, Remaining: 20, }, PacketID: 7, ReasonCode: CodeSuccess.Code, Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TPubcompMqtt5AckNoPacket, Desc: "mqtt5 no packet id ack", Primary: true, RawBytes: append([]byte{ Pubcomp << 4, 34, 0, 7, ErrPacketIdentifierNotFound.Code, 30, 31, 0, byte(len(ErrPacketIdentifierNotFound.Reason)), }, []byte(ErrPacketIdentifierNotFound.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Pubcomp, Remaining: 34, }, PacketID: 7, ReasonCode: ErrPacketIdentifierNotFound.Code, Properties: Properties{ ReasonString: ErrPacketIdentifierNotFound.Reason, }, }, }, { Case: TPubcompInvalidReason, Desc: "invalid reason code", Group: "validate", FailFirst: ErrProtocolViolationInvalidReason, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pubcomp, }, ReasonCode: ErrConnectionRateExceeded.Code, }, }, { Case: TPubcompMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Pubcomp << 4, 2, 0, }, }, { Case: TPubcompMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Pubcomp << 4, 34, 0, 7, ErrPacketIdentifierNotFound.Code, 22, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Subscribe: { { Case: TSubscribe, Desc: "subscribe", Primary: true, RawBytes: []byte{ Subscribe<<4 | 1<<1, 10, 0, 15, 0, 5, 'a', '/', 'b', '/', 'c', 0, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Remaining: 10, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ {Filter: "a/b/c"}, }, }, }, { Case: TSubscribeMany, Desc: "many", Primary: true, RawBytes: []byte{ Subscribe<<4 | 1<<1, 30, 0, 15, 0, 3, 'a', '/', 'b', 0, 0, 11, 'd', '/', 'e', '/', 'f', '/', 'g', '/', 'h', '/', 'i', 1, 0, 5, 'x', '/', 'y', '/', 'z', 2, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Remaining: 30, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ {Filter: "a/b", Qos: 0}, {Filter: "d/e/f/g/h/i", Qos: 1}, {Filter: "x/y/z", Qos: 2}, }, }, }, { Case: TSubscribeMqtt5, Desc: "mqtt5", Primary: true, RawBytes: []byte{ Subscribe<<4 | 1<<1, 31, 0, 15, 20, 11, 202, 212, 19, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, 0, 5, 'a', '/', 'b', '/', 'c', 46, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Subscribe, Remaining: 31, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ { Filter: "a/b/c", Qos: 2, NoLocal: true, RetainAsPublished: true, RetainHandling: 2, Identifier: 322122, }, }, Properties: Properties{ SubscriptionIdentifier: []int{322122}, User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TSubscribeRetainHandling1, Desc: "retain handling 1", Primary: true, RawBytes: []byte{ Subscribe<<4 | 1<<1, 11, 0, 15, 0, 0, 5, 'a', '/', 'b', '/', 'c', 0 | 1<<4, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Subscribe, Remaining: 11, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ { Filter: "a/b/c", RetainHandling: 1, }, }, }, }, { Case: TSubscribeRetainHandling2, Desc: "retain handling 2", Primary: true, RawBytes: []byte{ Subscribe<<4 | 1<<1, 11, 0, 15, 0, 0, 5, 'a', '/', 'b', '/', 'c', 0 | 2<<4, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Subscribe, Remaining: 11, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ { Filter: "a/b/c", RetainHandling: 2, }, }, }, }, { Case: TSubscribeRetainAsPublished, Desc: "retain as published", Primary: true, RawBytes: []byte{ Subscribe<<4 | 1<<1, 11, 0, 15, 0, 0, 5, 'a', '/', 'b', '/', 'c', 0 | 1<<3, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Subscribe, Remaining: 11, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ { Filter: "a/b/c", RetainAsPublished: true, }, }, }, }, { Case: TSubscribeInvalidFilter, Desc: "invalid filter", Group: "reference", Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ {Filter: "$SHARE/#", Identifier: 5}, }, }, }, { Case: TSubscribeInvalidSharedNoLocal, Desc: "shared and no local", Group: "reference", Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ {Filter: "$SHARE/tmp/a/b/c", Identifier: 5, NoLocal: true}, }, }, }, { Case: TSubscribeMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Subscribe<<4 | 1<<1, 2, 0, }, }, { Case: TSubscribeMalTopic, Desc: "malformed topic", Group: "decode", FailFirst: ErrMalformedTopic, RawBytes: []byte{ Subscribe<<4 | 1<<1, 2, 0, 21, 0, 3, 'a', '/', }, }, { Case: TSubscribeMalQos, Desc: "malformed subscribe - qos", Group: "decode", FailFirst: ErrMalformedQos, RawBytes: []byte{ Subscribe<<4 | 1<<1, 2, 0, 22, 0, 3, 'j', '/', 'b', }, }, { Case: TSubscribeMalQosRange, Desc: "malformed qos out of range", Group: "decode", FailFirst: ErrProtocolViolationQosOutOfRange, RawBytes: []byte{ Subscribe<<4 | 1<<1, 2, 0, 22, 0, 3, 'c', '/', 'd', 5, }, }, { Case: TSubscribeMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Subscribe << 4, 11, 0, 15, 4, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TSubscribeInvalidQosMustPacketID, Desc: "no packet id with qos > 0", Group: "validate", Expect: ErrProtocolViolationNoPacketID, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Qos: 1, }, PacketID: 0, Filters: Subscriptions{ {Filter: "a/b"}, }, }, }, { Case: TSubscribeInvalidNoFilters, Desc: "no filters", Group: "validate", Expect: ErrProtocolViolationNoFilters, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Qos: 1, }, PacketID: 2, }, }, { Case: TSubscribeInvalidIdentifierOversize, Desc: "oversize identifier", Group: "validate", Expect: ErrProtocolViolationOversizeSubID, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Qos: 1, }, PacketID: 2, Filters: Subscriptions{ {Filter: "a/b", Identifier: 5}, {Filter: "d/f", Identifier: 268435456}, }, }, }, { Case: TSubscribeSpecQosMustPacketID, Desc: "no packet id with qos > 0", Group: "encode", Expect: ErrProtocolViolationNoPacketID, RawBytes: []byte{ Subscribe<<4 | 1<<1, 10, 0, 0, 0, 5, 'a', '/', 'b', '/', 'c', 1, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Subscribe, Qos: 1, Remaining: 10, }, Filters: Subscriptions{ {Filter: "a/b/c", Qos: 1}, }, PacketID: 0, }, }, }, Suback: { { Case: TSuback, Desc: "suback", Primary: true, RawBytes: []byte{ Suback << 4, 3, 0, 15, 0, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Suback, Remaining: 3, }, PacketID: 15, ReasonCodes: []byte{0}, }, }, { Case: TSubackMany, Desc: "many", Primary: true, RawBytes: []byte{ Suback << 4, 6, 0, 15, 0, 1, 2, 0x80, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Suback, Remaining: 6, }, PacketID: 15, ReasonCodes: []byte{0, 1, 2, 0x80}, }, }, { Case: TSubackDeny, Desc: "deny mqtt5", Primary: true, RawBytes: []byte{ Suback << 4, 4, 0, 15, 0, ErrNotAuthorized.Code, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Suback, Remaining: 4, }, PacketID: 15, ReasonCodes: []byte{ErrNotAuthorized.Code}, }, }, { Case: TSubackUnspecifiedError, Desc: "unspecified error", Primary: true, RawBytes: []byte{ Suback << 4, 3, 0, 15, ErrUnspecifiedError.Code, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Suback, Remaining: 3, }, PacketID: 15, ReasonCodes: []byte{ErrUnspecifiedError.Code}, }, }, { Case: TSubackUnspecifiedErrorMqtt5, Desc: "unspecified error mqtt5", Primary: true, RawBytes: []byte{ Suback << 4, 4, 0, 15, 0, ErrUnspecifiedError.Code, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Suback, Remaining: 4, }, PacketID: 15, ReasonCodes: []byte{ErrUnspecifiedError.Code}, }, }, { Case: TSubackMqtt5, Desc: "mqtt5", Primary: true, RawBytes: []byte{ Suback << 4, 20, 0, 15, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, CodeGrantedQos2.Code, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Suback, Remaining: 20, }, PacketID: 15, ReasonCodes: []byte{CodeGrantedQos2.Code}, Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TSubackPacketIDInUse, Desc: "packet id in use", Primary: true, RawBytes: []byte{ Suback << 4, 47, 0, 15, 43, 31, 0, 24, 'p', 'a', 'c', 'k', 'e', 't', ' ', 'i', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r', ' ', 'i', 'n', ' ', 'u', 's', 'e', 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, ErrPacketIdentifierInUse.Code, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Suback, Remaining: 47, }, PacketID: 15, ReasonCodes: []byte{ErrPacketIdentifierInUse.Code}, Properties: Properties{ ReasonString: ErrPacketIdentifierInUse.Reason, User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TSubackMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Suback << 4, 2, 0, }, }, { Case: TSubackMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Suback << 4, 47, 0, 15, 43, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TSubackInvalidFilter, Desc: "malformed packet id", Group: "reference", RawBytes: []byte{ Suback << 4, 4, 0, 15, 0, ErrTopicFilterInvalid.Code, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TSubackInvalidSharedNoLocal, Desc: "invalid shared no local", Group: "reference", RawBytes: []byte{ Suback << 4, 4, 0, 15, 0, ErrProtocolViolationInvalidSharedNoLocal.Code, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Unsubscribe: { { Case: TUnsubscribe, Desc: "unsubscribe", Primary: true, RawBytes: []byte{ Unsubscribe<<4 | 1<<1, 9, 0, 15, 0, 5, 'a', '/', 'b', '/', 'c', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Unsubscribe, Remaining: 9, Qos: 1, }, PacketID: 15, Filters: Subscriptions{ {Filter: "a/b/c"}, }, }, }, { Case: TUnsubscribeMany, Desc: "unsubscribe many", Primary: true, RawBytes: []byte{ Unsubscribe<<4 | 1<<1, 27, 0, 35, 0, 3, 'a', '/', 'b', 0, 11, 'd', '/', 'e', '/', 'f', '/', 'g', '/', 'h', '/', 'i', 0, 5, 'x', '/', 'y', '/', 'z', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Unsubscribe, Remaining: 27, Qos: 1, }, PacketID: 35, Filters: Subscriptions{ {Filter: "a/b"}, {Filter: "d/e/f/g/h/i"}, {Filter: "x/y/z"}, }, }, }, { Case: TUnsubscribeMqtt5, Desc: "mqtt5", Primary: true, RawBytes: []byte{ Unsubscribe<<4 | 1<<1, 31, 0, 15, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, 0, 3, 'a', '/', 'b', 0, 5, 'x', '/', 'y', '/', 'w', }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Unsubscribe, Remaining: 31, Qos: 1, }, PacketID: 15, Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, Filters: Subscriptions{ {Filter: "a/b"}, {Filter: "x/y/w"}, }, }, }, { Case: TUnsubscribeMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Unsubscribe << 4, 2, 0, }, }, { Case: TUnsubscribeMalTopicName, Desc: "malformed topic", Group: "decode", FailFirst: ErrMalformedTopic, RawBytes: []byte{ Unsubscribe << 4, 2, 0, 21, 0, 3, 'a', '/', }, }, { Case: TUnsubscribeMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Unsubscribe<<4 | 1<<1, 31, 0, 15, 16, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TUnsubscribeInvalidQosMustPacketID, Desc: "no packet id with qos > 0", Group: "validate", Expect: ErrProtocolViolationNoPacketID, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Unsubscribe, Qos: 1, }, PacketID: 0, Filters: Subscriptions{ Subscription{Filter: "a/b"}, }, }, }, { Case: TUnsubscribeInvalidNoFilters, Desc: "no filters", Group: "validate", Expect: ErrProtocolViolationNoFilters, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Unsubscribe, Qos: 1, }, PacketID: 2, }, }, { Case: TUnsubscribeSpecQosMustPacketID, Desc: "no packet id with qos > 0", Group: "encode", Expect: ErrProtocolViolationNoPacketID, RawBytes: []byte{ Unsubscribe<<4 | 1<<1, 9, 0, 0, 0, 5, 'a', '/', 'b', '/', 'c', }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Unsubscribe, Qos: 1, Remaining: 9, }, PacketID: 0, Filters: Subscriptions{ {Filter: "a/b/c"}, }, }, }, }, Unsuback: { { Case: TUnsuback, Desc: "unsuback", Primary: true, RawBytes: []byte{ Unsuback << 4, 2, 0, 15, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Unsuback, Remaining: 2, }, PacketID: 15, }, }, { Case: TUnsubackMany, Desc: "unsuback many", Primary: true, RawBytes: []byte{ Unsuback << 4, 5, 0, 15, 0, CodeSuccess.Code, CodeSuccess.Code, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Unsuback, Remaining: 5, }, PacketID: 15, ReasonCodes: []byte{CodeSuccess.Code, CodeSuccess.Code}, }, }, { Case: TUnsubackMqtt5, Desc: "mqtt5", Primary: true, RawBytes: []byte{ Unsuback << 4, 21, 0, 15, 16, 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, CodeSuccess.Code, CodeNoSubscriptionExisted.Code, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Unsuback, Remaining: 21, }, PacketID: 15, ReasonCodes: []byte{CodeSuccess.Code, CodeNoSubscriptionExisted.Code}, Properties: Properties{ User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TUnsubackPacketIDInUse, Desc: "packet id in use", Primary: true, RawBytes: []byte{ Unsuback << 4, 48, 0, 15, 43, 31, 0, 24, 'p', 'a', 'c', 'k', 'e', 't', ' ', 'i', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r', ' ', 'i', 'n', ' ', 'u', 's', 'e', 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, ErrPacketIdentifierInUse.Code, ErrPacketIdentifierInUse.Code, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Unsuback, Remaining: 48, }, PacketID: 15, ReasonCodes: []byte{ErrPacketIdentifierInUse.Code, ErrPacketIdentifierInUse.Code}, Properties: Properties{ ReasonString: ErrPacketIdentifierInUse.Reason, User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TUnsubackMalPacketID, Desc: "malformed packet id", Group: "decode", FailFirst: ErrMalformedPacketID, RawBytes: []byte{ Unsuback << 4, 2, 0, }, }, { Case: TUnsubackMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Unsuback << 4, 48, 0, 15, 43, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Pingreq: { { Case: TPingreq, Desc: "ping request", Primary: true, RawBytes: []byte{ Pingreq << 4, 0, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pingreq, Remaining: 0, }, }, }, }, Pingresp: { { Case: TPingresp, Desc: "ping response", Primary: true, RawBytes: []byte{ Pingresp << 4, 0, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Pingresp, Remaining: 0, }, }, }, }, Disconnect: { { Case: TDisconnect, Desc: "disconnect", Primary: true, RawBytes: []byte{ Disconnect << 4, 0, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 0, }, }, }, { Case: TDisconnectTakeover, Desc: "takeover", Primary: true, RawBytes: append([]byte{ Disconnect << 4, 21, ErrSessionTakenOver.Code, 19, 31, 0, 16, }, []byte(ErrSessionTakenOver.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 0, }, ReasonCode: ErrSessionTakenOver.Code, Properties: Properties{ ReasonString: ErrSessionTakenOver.Reason, }, }, }, { Case: TDisconnectShuttingDown, Desc: "shutting down", Primary: true, RawBytes: append([]byte{ Disconnect << 4, 25, ErrServerShuttingDown.Code, 23, 31, 0, 20, }, []byte(ErrServerShuttingDown.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 0, }, ReasonCode: ErrServerShuttingDown.Code, Properties: Properties{ ReasonString: ErrServerShuttingDown.Reason, }, }, }, { Case: TDisconnectMqtt5, Desc: "mqtt5", Primary: true, RawBytes: append([]byte{ Disconnect << 4, 22, CodeDisconnect.Code, 20, 17, 0, 0, 0, 120, 31, 0, 12, }, []byte(CodeDisconnect.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 22, }, ReasonCode: CodeDisconnect.Code, Properties: Properties{ ReasonString: CodeDisconnect.Reason, SessionExpiryInterval: 120, SessionExpiryIntervalFlag: true, }, }, }, { Case: TDisconnectSecondConnect, Desc: "second connect packet mqtt5", RawBytes: append([]byte{ Disconnect << 4, 46, ErrProtocolViolationSecondConnect.Code, 44, 31, 0, 41, }, []byte(ErrProtocolViolationSecondConnect.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 45, }, ReasonCode: ErrProtocolViolationSecondConnect.Code, Properties: Properties{ ReasonString: ErrProtocolViolationSecondConnect.Reason, }, }, }, { Case: TDisconnectZeroNonZeroExpiry, Desc: "zero non zero expiry", RawBytes: []byte{ Disconnect << 4, 2, ErrProtocolViolationZeroNonZeroExpiry.Code, 0, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 2, }, ReasonCode: ErrProtocolViolationZeroNonZeroExpiry.Code, }, }, { Case: TDisconnectReceiveMaximum, Desc: "receive maximum mqtt5", RawBytes: append([]byte{ Disconnect << 4, 29, ErrReceiveMaximum.Code, 27, 31, 0, 24, }, []byte(ErrReceiveMaximum.Reason)...), Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 29, }, ReasonCode: ErrReceiveMaximum.Code, Properties: Properties{ ReasonString: ErrReceiveMaximum.Reason, }, }, }, { Case: TDisconnectDropProperties, Desc: "drop oversize properties partial", Group: "encode", RawBytes: []byte{ Disconnect << 4, 39, CodeDisconnect.Code, 19, 28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', 31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, ActualBytes: []byte{ Disconnect << 4, 12, CodeDisconnect.Code, 10, 28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', }, Packet: &Packet{ Mods: Mods{ MaxSize: 3, }, ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Disconnect, Remaining: 40, }, ReasonCode: CodeSuccess.Code, Properties: Properties{ ReasonString: "reason", ServerReference: "mochi-2", User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TDisconnectMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Disconnect << 4, 48, CodeDisconnect.Code, 46, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TDisconnectMalReasonCode, Desc: "malformed reason code", Group: "decode", FailFirst: ErrMalformedReasonCode, RawBytes: []byte{ Disconnect << 4, 48, }, Packet: &Packet{ ProtocolVersion: 5, }, }, }, Auth: { { Case: TAuth, Desc: "auth", Primary: true, RawBytes: []byte{ Auth << 4, 47, CodeSuccess.Code, 45, 21, 0, 5, 'S', 'H', 'A', '-', '1', 22, 0, 9, 'a', 'u', 't', 'h', '-', 'd', 'a', 't', 'a', 31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', 38, 0, 5, 'h', 'e', 'l', 'l', 'o', 0, 6, 228, 184, 150, 231, 149, 140, }, Packet: &Packet{ ProtocolVersion: 5, FixedHeader: FixedHeader{ Type: Auth, Remaining: 47, }, ReasonCode: CodeSuccess.Code, Properties: Properties{ AuthenticationMethod: "SHA-1", AuthenticationData: []byte("auth-data"), ReasonString: "reason", User: []UserProperty{ { Key: "hello", Val: "世界", }, }, }, }, }, { Case: TAuthMalReasonCode, Desc: "malformed reason code", Group: "decode", FailFirst: ErrMalformedReasonCode, RawBytes: []byte{ Auth << 4, 47, }, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Auth, }, ReasonCode: CodeNoMatchingSubscribers.Code, }, }, { Case: TAuthMalProperties, Desc: "malformed properties", Group: "decode", FailFirst: ErrMalformedProperties, RawBytes: []byte{ Auth << 4, 3, CodeSuccess.Code, 12, }, Packet: &Packet{ ProtocolVersion: 5, }, }, { Case: TAuthInvalidReason, Desc: "invalid reason code", Group: "validate", Expect: ErrProtocolViolationInvalidReason, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Auth, }, ReasonCode: CodeNoMatchingSubscribers.Code, }, }, { Case: TAuthInvalidReason2, Desc: "invalid reason code", Group: "validate", Expect: ErrProtocolViolationInvalidReason, Packet: &Packet{ FixedHeader: FixedHeader{ Type: Auth, }, ReasonCode: CodeNoMatchingSubscribers.Code, }, }, }, }
TPacketData contains individual encoding and decoding scenarios for each packet type.
Functions ¶
func BytesToString ¶
BytesToString provides a zero-alloc no-copy byte to string conversion. via https://github.com/golang/go/issues/25484#issuecomment-391415660
func DecodeByte ¶
DecodeByte extracts the value of a byte from a byte array.
func DecodeByteBool ¶
DecodeByteBool extracts the value of a byte from a byte array and returns a bool.
func DecodeBytes ¶
DecodeBytes extracts a byte array from a byte array, beginning at an offset. Used primarily for message payloads.
func DecodeLength ¶
func DecodeLength(b io.ByteReader) (n, bu int, err error)
func DecodeString ¶
DecodeString extracts a string from a byte array, beginning at an offset.
func DecodeUint16 ¶
DecodeUint16 extracts the value of two bytes from a byte array.
func DecodeUint32 ¶
DecodeUint32 extracts the value of four bytes from a byte array.
func EncodeBytes ¶
EncodeBytes encodes a byte array to a byte array. Used primarily for message payloads.
func EncodeLength ¶
EncodeLength writes length bits for the header.
func EncodeString ¶
EncodeString encodes a string to a byte array.
func EncodeUint16 ¶
EncodeUint16 encodes a uint16 value to a byte array.
func EncodeUint32 ¶
EncodeUint32 encodes a uint16 value to a byte array.
Types ¶
type ConnectParams ¶
type ConnectParams struct { WillProperties Properties `json:"willProperties"` // - Password []byte `json:"password"` // - Username []byte `json:"username"` // - ProtocolName []byte `json:"protocolName"` // - WillPayload []byte `json:"willPayload"` // - ClientIdentifier string `json:"clientId"` // - WillTopic string `json:"willTopic"` // - Keepalive uint16 `json:"keepalive"` // - PasswordFlag bool `json:"passwordFlag"` // - UsernameFlag bool `json:"usernameFlag"` // - WillQos byte `json:"willQos"` // - WillFlag bool `json:"willFlag"` // - WillRetain bool `json:"willRetain"` // - Clean bool `json:"clean"` // CleanSession in v3.1.1, CleanStart in v5 }
ConnectParams contains packet values which are specifically related to connect packets.
type FixedHeader ¶
type FixedHeader struct { Remaining int `json:"remaining"` // the number of remaining bytes in the payload. Type byte `json:"type"` // the type of the packet (PUBLISH, SUBSCRIBE, etc) from bits 7 - 4 (byte 1). Qos byte `json:"qos"` // indicates the quality of service expected. Dup bool `json:"dup"` // indicates if the packet was already sent at an earlier time. Retain bool `json:"retain"` // whether the message should be retained. }
FixedHeader contains the values of the fixed header portion of the MQTT packet.
func (*FixedHeader) Decode ¶
func (fh *FixedHeader) Decode(hb byte) error
Decode extracts the specification bits from the header byte.
func (*FixedHeader) Encode ¶
func (fh *FixedHeader) Encode(buf *bytes.Buffer)
Encode encodes the FixedHeader and returns a bytes buffer.
type Mods ¶
type Mods struct { MaxSize uint32 // the maximum packet size specified by the client / server DisallowProblemInfo bool // if problem info is disallowed AllowResponseInfo bool // if response info is disallowed }
Mods specifies certain values required for certain mqtt v5 compliance within packet encoding/decoding.
type Packet ¶
type Packet struct { Connect ConnectParams // parameters for connect packets (just for organisation) Properties Properties // all mqtt v5 packet properties Payload []byte // a message/payload for publish packets ReasonCodes []byte // one or more reason codes for multi-reason responses (suback, etc) Filters Subscriptions // a list of subscription filters and their properties (subscribe, unsubscribe) TopicName string // the topic a payload is being published to Origin string // client id of the client who is issuing the packet (mostly internal use) FixedHeader FixedHeader // - Created int64 // unix timestamp indicating time packet was created/received on the server Expiry int64 // unix timestamp indicating when the packet will expire and should be deleted Mods Mods // internal broker control values for controlling certain mqtt v5 compliance PacketID uint16 // packet id for the packet (publish, qos, etc) ProtocolVersion byte // protocol version of the client the packet belongs to SessionPresent bool // session existed for connack ReasonCode byte // reason code for a packet response (acks, etc) ReservedBit byte // reserved, do not use (except in testing) }
Packet represents an MQTT packet. Instead of providing a packet interface variant packet structs, this is a single concrete packet type to cover all packet types, which allows us to take advantage of various compiler optimizations. It contains a combination of mqtt spec values and internal broker control codes.
func (*Packet) AuthDecode ¶
AuthDecode decodes an Auth packet.
func (*Packet) AuthEncode ¶
AuthEncode encodes an Auth packet.
func (*Packet) AuthValidate ¶
AuthValidate returns success if the auth packet is valid.
func (*Packet) ConnackDecode ¶
ConnackDecode decodes a Connack packet.
func (*Packet) ConnackEncode ¶
ConnackEncode encodes a Connack packet.
func (*Packet) ConnectDecode ¶
ConnectDecode decodes a connect packet.
func (*Packet) ConnectEncode ¶
ConnectEncode encodes a connect packet.
func (*Packet) ConnectValidate ¶
ConnectValidate ensures the connect packet is compliant.
func (*Packet) Copy ¶
Copy creates a new instance of a packet, but with an empty header for inheriting new QoS flags, etc.
func (*Packet) DisconnectDecode ¶
DisconnectDecode decodes a Disconnect packet.
func (*Packet) DisconnectEncode ¶
DisconnectEncode encodes a Disconnect packet.
func (*Packet) PingreqDecode ¶
PingreqDecode decodes a Pingreq packet.
func (*Packet) PingreqEncode ¶
PingreqEncode encodes a Pingreq packet.
func (*Packet) PingrespDecode ¶
PingrespDecode decodes a Pingres packet.
func (*Packet) PingrespEncode ¶
PingrespEncode encodes a Pingresp packet.
func (*Packet) PubackDecode ¶
PubackDecode decodes a Puback packet.
func (*Packet) PubackEncode ¶
PubackEncode encodes a Puback packet.
func (*Packet) PubcompDecode ¶
PubcompDecode decodes a Pubcomp packet.
func (*Packet) PubcompEncode ¶
PubcompEncode encodes a Pubcomp packet.
func (*Packet) PublishDecode ¶
PublishDecode extracts the data values from the packet.
func (*Packet) PublishEncode ¶
PublishEncode encodes a Publish packet.
func (*Packet) PublishValidate ¶
PublishValidate validates a publish packet.
func (*Packet) PubrecDecode ¶
PubrecDecode decodes a Pubrec packet.
func (*Packet) PubrecEncode ¶
PubrecEncode encodes a Pubrec packet.
func (*Packet) PubrelDecode ¶
PubrelDecode decodes a Pubrel packet.
func (*Packet) PubrelEncode ¶
PubrelEncode encodes a Pubrel packet.
func (*Packet) ReasonCodeValid ¶
ReasonCodeValid returns true if the provided reason code is valid for the packet type.
func (*Packet) SubackDecode ¶
SubackDecode decodes a Suback packet.
func (*Packet) SubackEncode ¶
SubackEncode encodes a Suback packet.
func (*Packet) SubscribeDecode ¶
SubscribeDecode decodes a Subscribe packet.
func (*Packet) SubscribeEncode ¶
SubscribeEncode encodes a Subscribe packet.
func (*Packet) SubscribeValidate ¶
SubscribeValidate ensures the packet is compliant.
func (*Packet) UnsubackDecode ¶
UnsubackDecode decodes an Unsuback packet.
func (*Packet) UnsubackEncode ¶
UnsubackEncode encodes an Unsuback packet.
func (*Packet) UnsubscribeDecode ¶
UnsubscribeDecode decodes an Unsubscribe packet.
func (*Packet) UnsubscribeEncode ¶
UnsubscribeEncode encodes an Unsubscribe packet.
func (*Packet) UnsubscribeValidate ¶
UnsubscribeValidate validates an Unsubscribe packet.
type Packets ¶
Packets is a concurrency safe map of packets.
type Properties ¶
type Properties struct { CorrelationData []byte `json:"cd"` SubscriptionIdentifier []int `json:"si"` AuthenticationData []byte `json:"ad"` User []UserProperty `json:"user"` ContentType string `json:"ct"` ResponseTopic string `json:"rt"` AssignedClientID string `json:"aci"` AuthenticationMethod string `json:"am"` ResponseInfo string `json:"ri"` ServerReference string `json:"sr"` ReasonString string `json:"rs"` MessageExpiryInterval uint32 `json:"me"` SessionExpiryInterval uint32 `json:"sei"` WillDelayInterval uint32 `json:"wdi"` MaximumPacketSize uint32 `json:"mps"` ServerKeepAlive uint16 `json:"ska"` ReceiveMaximum uint16 `json:"rm"` TopicAliasMaximum uint16 `json:"tam"` TopicAlias uint16 `json:"ta"` PayloadFormat byte `json:"pf"` PayloadFormatFlag bool `json:"fpf"` SessionExpiryIntervalFlag bool `json:"fsei"` ServerKeepAliveFlag bool `json:"fska"` RequestProblemInfo byte `json:"rpi"` RequestProblemInfoFlag bool `json:"frpi"` RequestResponseInfo byte `json:"rri"` TopicAliasFlag bool `json:"fta"` MaximumQos byte `json:"mqos"` MaximumQosFlag bool `json:"fmqos"` RetainAvailable byte `json:"ra"` RetainAvailableFlag bool `json:"fra"` WildcardSubAvailable byte `json:"wsa"` WildcardSubAvailableFlag bool `json:"fwsa"` SubIDAvailable byte `json:"sida"` SubIDAvailableFlag bool `json:"fsida"` }
Properties contains all of the mqtt v5 properties available for a packet. Some properties have valid values of 0 or not-present. In this case, we opt for property flags to indicate the usage of property. Refer to mqtt v5 2.2.2.2 Property spec for more information.
func (*Properties) Copy ¶
func (p *Properties) Copy(allowTransfer bool) Properties
Copy creates a new Properties struct with copies of the values.
type Subscription ¶
type Subscription struct { Filter string Identifier int Identifiers map[string]int RetainHandling byte Qos byte RetainAsPublished bool NoLocal bool }
Subscription contains details about a client subscription to a topic filter.
func (Subscription) Merge ¶
func (s Subscription) Merge(n Subscription) Subscription
Merge merges a new subscription with a base subscription, preserving the highest qos value, matched identifiers and any special properties.
type Subscriptions ¶
type Subscriptions []Subscription // must be a slice to retain order.
Subscriptions is a slice of Subscription.
type TPacketCase ¶
type TPacketCase struct { RawBytes []byte // the bytes that make the packet ActualBytes []byte // the actual byte array that is created in the event of a byte mutation Group string // a group that should run the test, blank for all Desc string // a description of the test FailFirst error // expected fail result to be run immediately after the method is called Packet *Packet // the packet that is Expected ActualPacket *Packet // the actual packet after mutations Expect error // generic Expected fail result to be checked Isolate bool // isolate can be used to isolate a test Primary bool // primary is a test that should be run using readPackets Case byte // the identifying byte of the case }
TPacketCase contains data for cross-checking the encoding and decoding of packets and expected scenarios.
type TPacketCases ¶
type TPacketCases []TPacketCase
TPacketCases is a slice of TPacketCase.
func (TPacketCases) Get ¶
func (f TPacketCases) Get(b byte) TPacketCase
Get returns a case matching a given T byte.
type UserProperty ¶
UserProperty is an arbitrary key-value pair for a packet user properties array.