packets

package
v2.6.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2024 License: MIT Imports: 12 Imported by: 53

Documentation

Index

Constants

View Source
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
	WillProperties byte = 99   // Special byte for validating Will Properties.
)

All valid packet types and their packet identifiers.

View Source
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
	PropSharedSubAvailable     byte = 42
)
View Source
const (
	TConnectMqtt31 byte = iota
	TConnectMqtt311
	TConnectMqtt5
	TConnectMqtt5LWT
	TConnectClean
	TConnectUserPass
	TConnectUserPassLWT
	TConnectMalProtocolName
	TConnectMalProtocolVersion
	TConnectMalFlags
	TConnectMalKeepalive
	TConnectMalClientID
	TConnectMalWillTopic
	TConnectMalWillFlag
	TConnectMalUsername
	TConnectMalPassword
	TConnectMalFixedHeader
	TConnectMalReservedBit
	TConnectMalProperties
	TConnectMalWillProperties
	TConnectInvalidProtocolName
	TConnectInvalidProtocolVersion
	TConnectInvalidProtocolVersion2
	TConnectInvalidReservedBit
	TConnectInvalidClientIDTooLong
	TConnectInvalidFlagNoUsername
	TConnectInvalidFlagNoPassword
	TConnectInvalidUsernameNoFlag
	TConnectInvalidPasswordNoFlag
	TConnectInvalidUsernameTooLong
	TConnectInvalidPasswordTooLong
	TConnectInvalidWillFlagNoPayload
	TConnectInvalidWillFlagQosOutOfRange
	TConnectInvalidWillSurplusRetain
	TConnectZeroByteUsername
	TConnectSpecInvalidUTF8D800
	TConnectSpecInvalidUTF8DFFF
	TConnectSpecInvalidUTF80000
	TConnectSpecInvalidUTF8NoSkip
	TConnackAcceptedNoSession
	TConnackAcceptedSessionExists
	TConnackAcceptedMqtt5
	TConnackAcceptedAdjustedExpiryInterval
	TConnackMinMqtt5
	TConnackMinCleanMqtt5
	TConnackServerKeepalive
	TConnackInvalidMinMqtt5
	TConnackBadProtocolVersion
	TConnackProtocolViolationNoSession
	TConnackBadClientID
	TConnackServerUnavailable
	TConnackBadUsernamePassword
	TConnackBadUsernamePasswordNoSession
	TConnackMqtt5BadUsernamePasswordNoSession
	TConnackNotAuthorised
	TConnackMalSessionPresent
	TConnackMalReturnCode
	TConnackMalProperties
	TConnackDropProperties
	TConnackDropPropertiesPartial
	TPublishNoPayload
	TPublishBasic
	TPublishBasicTopicAliasOnly
	TPublishBasicMqtt5
	TPublishMqtt5
	TPublishQos1
	TPublishQos1Mqtt5
	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
	TPubackMqtt5NotAuthorized
	TPubackMalPacketID
	TPubackMalProperties
	TPubackUnexpectedError
	TPubrec
	TPubrecMqtt5
	TPubrecMqtt5IDInUse
	TPubrecMqtt5NotAuthorized
	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
	TSubscribeInvalidSharedNoLocal
	TSubscribeInvalidFilter
	TSubscribeInvalidIdentifierOversize
	TSuback
	TSubackMany
	TSubackDeny
	TSubackUnspecifiedError
	TSubackUnspecifiedErrorMqtt5
	TSubackMqtt5
	TSubackPacketIDInUse
	TSubackInvalidFilter
	TSubackInvalidSharedNoLocal
	TSubackMalPacketID
	TSubackMalProperties
	TUnsubscribe
	TUnsubscribeMany
	TUnsubscribeMqtt5
	TUnsubscribeMalPacketID
	TUnsubscribeMalTopicName
	TUnsubscribeMalProperties
	TUnsubscribeInvalidQosMustPacketID
	TUnsubscribeSpecQosMustPacketID
	TUnsubscribeInvalidNoFilters
	TUnsuback
	TUnsubackMany
	TUnsubackMqtt5
	TUnsubackPacketIDInUse
	TUnsubackMalPacketID
	TUnsubackMalProperties
	TPingreq
	TPingresp
	TDisconnect
	TDisconnectTakeover
	TDisconnectMqtt5
	TDisconnectMqtt5DisconnectWithWillMessage
	TDisconnectSecondConnect
	TDisconnectReceiveMaximum
	TDisconnectDropProperties
	TDisconnectShuttingDown
	TDisconnectMalProperties
	TDisconnectMalReasonCode
	TDisconnectZeroNonZeroExpiry
	TAuth
	TAuthMalReasonCode
	TAuthMalProperties
	TAuthInvalidReason
	TAuthInvalidReason2
)

Variables

View Source
var (
	// QosCodes indicates the reason codes for each Qos byte.
	QosCodes = map[byte]Code{
		0: CodeGrantedQos0,
		1: CodeGrantedQos1,
		2: CodeGrantedQos2,
	}

	CodeSuccessIgnore                         = Code{Code: 0x00, Reason: "ignore packet"}
	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"}
	ErrProtocolViolationInvalidSharedNoLocal  = Code{Code: 0x82, Reason: "protocol violation: invalid shared no local"}
	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"}
	ErrServerUnavailable                      = Code{Code: 0x88, Reason: "server unavailable"}
	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"}
	ErrSharedSubscriptionsNotSupported        = Code{Code: 0x9E, Reason: "shared subscriptions not supported"}
	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"}
	ErrInlineSubscriptionHandlerInvalid       = Code{Code: 0xA3, Reason: "inline subscription handler not valid."}

	// MQTTv3 specific bytes.
	Err3UnsupportedProtocolVersion = Code{Code: 0x01}
	Err3ClientIdentifierNotValid   = Code{Code: 0x02}
	Err3ServerUnavailable          = Code{Code: 0x03}
	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,
	}
)
View Source
var (
	// ErrNoValidPacketAvailable indicates the packet type byte provided does not exist in the mqtt specification.
	ErrNoValidPacketAvailable = 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",
	}
)
View Source
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, 8,
				0,
				CodeSuccess.Code,
				5,
				17, 0, 0, 0, 120,
			},
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Connack,
					Remaining: 8,
				},
				ReasonCode: CodeSuccess.Code,
				Properties: Properties{
					SessionExpiryInterval:     uint32(120),
					SessionExpiryIntervalFlag: 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, 13,
				1,
				CodeSuccess.Code,
				10,
				18, 0, 5, 'm', 'o', 'c', 'h', 'i',
				36, 1,
			},
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Connack,
					Remaining: 13,
				},
				SessionPresent: true,
				ReasonCode:     CodeSuccess.Code,
				Properties: Properties{
					AssignedClientID: "mochi",
					MaximumQos:       byte(1),
					MaximumQosFlag:   true,
				},
			},
		},
		{
			Case:    TConnackMinCleanMqtt5,
			Desc:    "accepted min properties mqtt5b",
			Primary: true,
			RawBytes: []byte{
				Connack << 4, 3,
				0,
				CodeSuccess.Code,
				0,
			},
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Connack,
					Remaining: 16,
				},
				SessionPresent: false,
				ReasonCode:     CodeSuccess.Code,
			},
		},
		{
			Case:    TConnackServerKeepalive,
			Desc:    "server set keepalive",
			Primary: true,
			RawBytes: []byte{
				Connack << 4, 6,
				1,
				CodeSuccess.Code,
				3,
				19, 0, 10,
			},
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Connack,
					Remaining: 6,
				},
				SessionPresent: true,
				ReasonCode:     CodeSuccess.Code,
				Properties: Properties{
					ServerKeepAlive:     uint16(10),
					ServerKeepAliveFlag: true,
				},
			},
		},
		{
			Case:    TConnackInvalidMinMqtt5,
			Desc:    "failure min properties mqtt5",
			Primary: true,
			RawBytes: append([]byte{
				Connack << 4, 23,
				0,
				ErrUnspecifiedError.Code,

				20,
				31, 0, 17,
			}, []byte(ErrUnspecifiedError.Reason)...),
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Connack,
					Remaining: 23,
				},
				SessionPresent: false,
				ReasonCode:     ErrUnspecifiedError.Code,
				Properties: Properties{
					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:    TPublishQos1Mqtt5,
			Desc:    "mqtt v5",
			Primary: true,
			RawBytes: []byte{
				Publish<<4 | 1<<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:       1,
				},
				PacketID:  7,
				TopicName: "a/b/c",
				Properties: Properties{
					User: []UserProperty{
						{
							Key: "hello",
							Val: "世界",
						},
					},
				},
				Payload: []byte("hello mochi"),
			},
		},

		{
			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:    TPubackMqtt5NotAuthorized,
			Desc:    "QOS 1 publish not authorized mqtt5",
			Primary: true,
			RawBytes: []byte{
				Puback << 4, 37,
				0, 7,
				ErrNotAuthorized.Code,
				33,
				31, 0, 14, 'n', 'o', 't', ' ', 'a', 'u',
				't', 'h', 'o', 'r', 'i', 'z', 'e', 'd',
				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: 31,
				},
				PacketID:   7,
				ReasonCode: ErrNotAuthorized.Code,
				Properties: Properties{
					ReasonString: ErrNotAuthorized.Reason,
					User: []UserProperty{
						{
							Key: "hello",
							Val: "世界",
						},
					},
				},
			},
		},
		{
			Case:  TPubackUnexpectedError,
			Desc:  "unexpected error",
			Group: "decode",
			RawBytes: []byte{
				Puback << 4, 29,
				0, 7,
				ErrPayloadFormatInvalid.Code,
				25,
				31, 0, 22, 'p', 'a', 'y', 'l', 'o', 'a', 'd',
				' ', 'f', 'o', 'r', 'm', 'a', 't',
				' ', 'i', 'n', 'v', 'a', 'l', 'i', 'd',
			},
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Puback,
					Remaining: 28,
				},
				PacketID:   7,
				ReasonCode: ErrPayloadFormatInvalid.Code,
				Properties: Properties{
					ReasonString: ErrPayloadFormatInvalid.Reason,
				},
			},
		},

		{
			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, 47,
				0, 7,
				ErrPacketIdentifierInUse.Code,
				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,
			},
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Pubrec,
					Remaining: 31,
				},
				PacketID:   7,
				ReasonCode: ErrPacketIdentifierInUse.Code,
				Properties: Properties{
					ReasonString: ErrPacketIdentifierInUse.Reason,
					User: []UserProperty{
						{
							Key: "hello",
							Val: "世界",
						},
					},
				},
			},
		},
		{
			Case:    TPubrecMqtt5NotAuthorized,
			Desc:    "QOS 2 publish not authorized mqtt5",
			Primary: true,
			RawBytes: []byte{
				Pubrec << 4, 37,
				0, 7,
				ErrNotAuthorized.Code,
				33,
				31, 0, 14, 'n', 'o', 't', ' ', 'a', 'u',
				't', 'h', 'o', 'r', 'i', 'z', 'e', 'd',
				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: 31,
				},
				PacketID:   7,
				ReasonCode: ErrNotAuthorized.Code,
				Properties: Properties{
					ReasonString: ErrNotAuthorized.Reason,
					User: []UserProperty{
						{
							Key: "hello",
							Val: "世界",
						},
					},
				},
			},
		},
		{
			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:    TDisconnectMqtt5DisconnectWithWillMessage,
			Desc:    "mqtt5 disconnect with will message",
			Primary: true,
			RawBytes: append([]byte{
				Disconnect << 4, 38,
				CodeDisconnectWillMessage.Code,
				36,
				17, 0, 0, 0, 120,
				31, 0, 28,
			}, []byte(CodeDisconnectWillMessage.Reason)...),
			Packet: &Packet{
				ProtocolVersion: 5,
				FixedHeader: FixedHeader{
					Type:      Disconnect,
					Remaining: 22,
				},
				ReasonCode: CodeDisconnectWillMessage.Code,
				Properties: Properties{
					ReasonString:              CodeDisconnectWillMessage.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 DecodeLength

func DecodeLength(b io.ByteReader) (n, bu int, err error)

Types

type Code

type Code struct {
	Reason string
	Code   byte
}

Code contains a reason code and reason string for a response.

func (Code) Error

func (c Code) Error() string

Error returns the readable reason for a code.

func (Code) String

func (c Code) String() string

String returns the readable reason for a code.

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)
	Ignore          bool          // if true, do not perform any message forwarding operations
}

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

func (pk *Packet) AuthDecode(buf []byte) error

AuthDecode decodes an Auth packet.

func (*Packet) AuthEncode

func (pk *Packet) AuthEncode(buf *bytes.Buffer) error

AuthEncode encodes an Auth packet.

func (*Packet) AuthValidate

func (pk *Packet) AuthValidate() Code

AuthValidate returns success if the auth packet is valid.

func (*Packet) ConnackDecode

func (pk *Packet) ConnackDecode(buf []byte) error

ConnackDecode decodes a Connack packet.

func (*Packet) ConnackEncode

func (pk *Packet) ConnackEncode(buf *bytes.Buffer) error

ConnackEncode encodes a Connack packet.

func (*Packet) ConnectDecode

func (pk *Packet) ConnectDecode(buf []byte) error

ConnectDecode decodes a connect packet.

func (*Packet) ConnectEncode

func (pk *Packet) ConnectEncode(buf *bytes.Buffer) error

ConnectEncode encodes a connect packet.

func (*Packet) ConnectValidate

func (pk *Packet) ConnectValidate() Code

ConnectValidate ensures the connect packet is compliant.

func (*Packet) Copy

func (pk *Packet) Copy(allowTransfer bool) Packet

Copy creates a new instance of a packet, but with an empty header for inheriting new QoS flags, etc.

func (*Packet) DisconnectDecode

func (pk *Packet) DisconnectDecode(buf []byte) error

DisconnectDecode decodes a Disconnect packet.

func (*Packet) DisconnectEncode

func (pk *Packet) DisconnectEncode(buf *bytes.Buffer) error

DisconnectEncode encodes a Disconnect packet.

func (*Packet) FormatID

func (pk *Packet) FormatID() string

FormatID returns the PacketID field as a decimal integer.

func (*Packet) PingreqDecode

func (pk *Packet) PingreqDecode(buf []byte) error

PingreqDecode decodes a Pingreq packet.

func (*Packet) PingreqEncode

func (pk *Packet) PingreqEncode(buf *bytes.Buffer) error

PingreqEncode encodes a Pingreq packet.

func (*Packet) PingrespDecode

func (pk *Packet) PingrespDecode(buf []byte) error

PingrespDecode decodes a Pingres packet.

func (*Packet) PingrespEncode

func (pk *Packet) PingrespEncode(buf *bytes.Buffer) error

PingrespEncode encodes a Pingresp packet.

func (*Packet) PubackDecode

func (pk *Packet) PubackDecode(buf []byte) error

PubackDecode decodes a Puback packet.

func (*Packet) PubackEncode

func (pk *Packet) PubackEncode(buf *bytes.Buffer) error

PubackEncode encodes a Puback packet.

func (*Packet) PubcompDecode

func (pk *Packet) PubcompDecode(buf []byte) error

PubcompDecode decodes a Pubcomp packet.

func (*Packet) PubcompEncode

func (pk *Packet) PubcompEncode(buf *bytes.Buffer) error

PubcompEncode encodes a Pubcomp packet.

func (*Packet) PublishDecode

func (pk *Packet) PublishDecode(buf []byte) error

PublishDecode extracts the data values from the packet.

func (*Packet) PublishEncode

func (pk *Packet) PublishEncode(buf *bytes.Buffer) error

PublishEncode encodes a Publish packet.

func (*Packet) PublishValidate

func (pk *Packet) PublishValidate(topicAliasMaximum uint16) Code

PublishValidate validates a publish packet.

func (*Packet) PubrecDecode

func (pk *Packet) PubrecDecode(buf []byte) error

PubrecDecode decodes a Pubrec packet.

func (*Packet) PubrecEncode

func (pk *Packet) PubrecEncode(buf *bytes.Buffer) error

PubrecEncode encodes a Pubrec packet.

func (*Packet) PubrelDecode

func (pk *Packet) PubrelDecode(buf []byte) error

PubrelDecode decodes a Pubrel packet.

func (*Packet) PubrelEncode

func (pk *Packet) PubrelEncode(buf *bytes.Buffer) error

PubrelEncode encodes a Pubrel packet.

func (*Packet) ReasonCodeValid

func (pk *Packet) ReasonCodeValid() bool

ReasonCodeValid returns true if the provided reason code is valid for the packet type.

func (*Packet) SubackDecode

func (pk *Packet) SubackDecode(buf []byte) error

SubackDecode decodes a Suback packet.

func (*Packet) SubackEncode

func (pk *Packet) SubackEncode(buf *bytes.Buffer) error

SubackEncode encodes a Suback packet.

func (*Packet) SubscribeDecode

func (pk *Packet) SubscribeDecode(buf []byte) error

SubscribeDecode decodes a Subscribe packet.

func (*Packet) SubscribeEncode

func (pk *Packet) SubscribeEncode(buf *bytes.Buffer) error

SubscribeEncode encodes a Subscribe packet.

func (*Packet) SubscribeValidate

func (pk *Packet) SubscribeValidate() Code

SubscribeValidate ensures the packet is compliant.

func (*Packet) UnsubackDecode

func (pk *Packet) UnsubackDecode(buf []byte) error

UnsubackDecode decodes an Unsuback packet.

func (*Packet) UnsubackEncode

func (pk *Packet) UnsubackEncode(buf *bytes.Buffer) error

UnsubackEncode encodes an Unsuback packet.

func (*Packet) UnsubscribeDecode

func (pk *Packet) UnsubscribeDecode(buf []byte) error

UnsubscribeDecode decodes an Unsubscribe packet.

func (*Packet) UnsubscribeEncode

func (pk *Packet) UnsubscribeEncode(buf *bytes.Buffer) error

UnsubscribeEncode encodes an Unsubscribe packet.

func (*Packet) UnsubscribeValidate

func (pk *Packet) UnsubscribeValidate() Code

UnsubscribeValidate validates an Unsubscribe packet.

type Packets

type Packets struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Packets is a concurrency safe map of packets.

func NewPackets

func NewPackets() *Packets

NewPackets returns a new instance of Packets.

func (*Packets) Add

func (p *Packets) Add(id string, val Packet)

Add adds a new packet to the map.

func (*Packets) Delete

func (p *Packets) Delete(id string)

Delete removes a packet from the map by packet id.

func (*Packets) Get

func (p *Packets) Get(id string) (val Packet, ok bool)

Get returns a specific packet in the map by packet id.

func (*Packets) GetAll

func (p *Packets) GetAll() map[string]Packet

GetAll returns all packets in the map.

func (*Packets) Len

func (p *Packets) Len() int

Len returns the number of packets in the map.

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"`
	SharedSubAvailable        byte           `json:"ssa"`
	SharedSubAvailableFlag    bool           `json:"fssa"`
}

Properties contains all 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.

func (*Properties) Decode

func (p *Properties) Decode(pkt byte, b *bytes.Buffer) (n int, err error)

Decode decodes property bytes into a properties struct.

func (*Properties) Encode

func (p *Properties) Encode(pkt byte, mods Mods, b *bytes.Buffer, n int)

Encode encodes properties into a bytes buffer.

type Subscription

type Subscription struct {
	ShareName         []string
	Filter            string
	Identifier        int
	Identifiers       map[string]int
	RetainHandling    byte
	Qos               byte
	RetainAsPublished bool
	NoLocal           bool
	FwdRetainedFlag   bool // true if the subscription forms part of a publish response to a client subscription and packet is retained.
}

Subscription contains details about a client subscription to a topic filter.

func (Subscription) Merge

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

type UserProperty struct {
	Key string `json:"k"`
	Val string `json:"v"`
}

UserProperty is an arbitrary key-value pair for a packet user properties array.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL