mirror of
https://github.com/mochi-mqtt/server.git
synced 2025-09-27 04:26:23 +08:00

* Allow 0 byte usernames if correctly formed * Allow 0 byte usernames if correctly formed
3854 lines
92 KiB
Go
3854 lines
92 KiB
Go
// SPDX-License-Identifier: MIT
|
|
// SPDX-FileCopyrightText: 2022 mochi-co
|
|
// SPDX-FileContributor: mochi-co
|
|
|
|
package packets
|
|
|
|
// TPacketCase contains data for cross-checking the encoding and decoding
|
|
// of packets and expected scenarios.
|
|
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
|
|
}
|
|
|
|
// TPacketCases is a slice of TPacketCase.
|
|
type TPacketCases []TPacketCase
|
|
|
|
// Get returns a case matching a given T byte.
|
|
func (f TPacketCases) Get(b byte) TPacketCase {
|
|
for _, v := range f {
|
|
if v.Case == b {
|
|
return v
|
|
}
|
|
}
|
|
|
|
return TPacketCase{}
|
|
}
|
|
|
|
const (
|
|
TConnectMqtt31 byte = iota
|
|
TConnectMqtt311
|
|
TConnectMqtt5
|
|
TConnectMqtt5LWT
|
|
TConnectClean
|
|
TConnectCleanLWT
|
|
TConnectUserPass
|
|
TConnectUserPassLWT
|
|
TConnectMalProtocolName
|
|
TConnectMalProtocolVersion
|
|
TConnectMalFlags
|
|
TConnectMalKeepalive
|
|
TConnectMalClientID
|
|
TConnectMalWillTopic
|
|
TConnectMalWillFlag
|
|
TConnectMalUsername
|
|
TConnectMalPassword
|
|
TConnectMalFixedHeader
|
|
TConnectMalReservedBit
|
|
TConnectMalProperties
|
|
TConnectMalWillProperties
|
|
TConnectInvalidProtocolName
|
|
TConnectInvalidProtocolVersion
|
|
TConnectInvalidProtocolVersion2
|
|
TConnectInvalidReservedBit
|
|
TConnectInvalidClientIDTooLong
|
|
TConnectInvalidPasswordNoUsername
|
|
TConnectInvalidFlagNoUsername
|
|
TConnectInvalidFlagNoPassword
|
|
TConnectInvalidUsernameNoFlag
|
|
TConnectInvalidPasswordNoFlag
|
|
TConnectInvalidUsernameTooLong
|
|
TConnectInvalidPasswordTooLong
|
|
TConnectInvalidWillFlagNoPayload
|
|
TConnectInvalidWillFlagQosOutOfRange
|
|
TConnectInvalidWillSurplusRetain
|
|
TConnectZeroByteUsername
|
|
TConnectSpecInvalidUTF8D800
|
|
TConnectSpecInvalidUTF8DFFF
|
|
TConnectSpecInvalidUTF80000
|
|
TConnectSpecInvalidUTF8NoSkip
|
|
TConnackAcceptedNoSession
|
|
TConnackAcceptedSessionExists
|
|
TConnackAcceptedMqtt5
|
|
TConnackAcceptedAdjustedExpiryInterval
|
|
TConnackMinMqtt5
|
|
TConnackMinCleanMqtt5
|
|
TConnackInvalidMinMqtt5
|
|
TConnackBadProtocolVersion
|
|
TConnackProtocolViolationNoSession
|
|
TConnackBadClientID
|
|
TConnackServerUnavailable
|
|
TConnackBadUsernamePassword
|
|
TConnackBadUsernamePasswordNoSession
|
|
TConnackMqtt5BadUsernamePasswordNoSession
|
|
TConnackNotAuthorised
|
|
TConnackMalSessionPresent
|
|
TConnackMalReturnCode
|
|
TConnackMalProperties
|
|
TConnackDropProperties
|
|
TConnackDropPropertiesPartial
|
|
TPublishNoPayload
|
|
TPublishBasic
|
|
TPublishBasicTopicAliasOnly
|
|
TPublishBasicMqtt5
|
|
TPublishMqtt5
|
|
TPublishQos1
|
|
TPublishQos1NoPayload
|
|
TPublishQos1Dup
|
|
TPublishQos2
|
|
TPublishQos2Mqtt5
|
|
TPublishQos2Upgraded
|
|
TPublishSubscriberIdentifier
|
|
TPublishRetain
|
|
TPublishRetainMqtt5
|
|
TPublishDup
|
|
TPublishMalTopicName
|
|
TPublishMalPacketID
|
|
TPublishMalProperties
|
|
TPublishCopyBasic
|
|
TPublishSpecQos0NoPacketID
|
|
TPublishSpecQosMustPacketID
|
|
TPublishDropOversize
|
|
TPublishInvalidQos0NoPacketID
|
|
TPublishInvalidQosMustPacketID
|
|
TPublishInvalidSurplusSubID
|
|
TPublishInvalidSurplusWildcard
|
|
TPublishInvalidSurplusWildcard2
|
|
TPublishInvalidNoTopic
|
|
TPublishInvalidTopicAlias
|
|
TPublishInvalidExcessTopicAlias
|
|
TPublishSpecDenySysTopic
|
|
TPuback
|
|
TPubackMqtt5
|
|
TPubackMalPacketID
|
|
TPubackMalProperties
|
|
TPubrec
|
|
TPubrecMqtt5
|
|
TPubrecMqtt5IDInUse
|
|
TPubrecMalPacketID
|
|
TPubrecMalProperties
|
|
TPubrecMalReasonCode
|
|
TPubrecInvalidReason
|
|
TPubrel
|
|
TPubrelMqtt5
|
|
TPubrelMqtt5AckNoPacket
|
|
TPubrelMalPacketID
|
|
TPubrelMalProperties
|
|
TPubrelInvalidReason
|
|
TPubcomp
|
|
TPubcompMqtt5
|
|
TPubcompMqtt5AckNoPacket
|
|
TPubcompMalPacketID
|
|
TPubcompMalProperties
|
|
TPubcompInvalidReason
|
|
TSubscribe
|
|
TSubscribeMany
|
|
TSubscribeMqtt5
|
|
TSubscribeRetainHandling1
|
|
TSubscribeRetainHandling2
|
|
TSubscribeRetainAsPublished
|
|
TSubscribeMalPacketID
|
|
TSubscribeMalTopic
|
|
TSubscribeMalQos
|
|
TSubscribeMalQosRange
|
|
TSubscribeMalProperties
|
|
TSubscribeInvalidQosMustPacketID
|
|
TSubscribeSpecQosMustPacketID
|
|
TSubscribeInvalidNoFilters
|
|
TSubscribeInvalidSharedNoLocal
|
|
TSubscribeInvalidFilter
|
|
TSubscribeInvalidIdentifierOversize
|
|
TSuback
|
|
TSubackMany
|
|
TSubackDeny
|
|
TSubackUnspecifiedError
|
|
TSubackUnspecifiedErrorMqtt5
|
|
TSubackMqtt5
|
|
TSubackPacketIDInUse
|
|
TSubackInvalidFilter
|
|
TSubackInvalidSharedNoLocal
|
|
TSubackMalPacketID
|
|
TSubackMalProperties
|
|
TUnsubscribe
|
|
TUnsubscribeMany
|
|
TUnsubscribeMqtt5
|
|
TUnsubscribeDropProperties
|
|
TUnsubscribeMalPacketID
|
|
TUnsubscribeMalTopicName
|
|
TUnsubscribeMalProperties
|
|
TUnsubscribeInvalidQosMustPacketID
|
|
TUnsubscribeSpecQosMustPacketID
|
|
TUnsubscribeInvalidNoFilters
|
|
TUnsuback
|
|
TUnsubackMany
|
|
TUnsubackMqtt5
|
|
TUnsubackPacketIDInUse
|
|
TUnsubackMalPacketID
|
|
TUnsubackMalProperties
|
|
TPingreq
|
|
TPingresp
|
|
TDisconnect
|
|
TDisconnectTakeover
|
|
TDisconnectMqtt5
|
|
TDisconnectNormalMqtt5
|
|
TDisconnectSecondConnect
|
|
TDisconnectReceiveMaximum
|
|
TDisconnectDropProperties
|
|
TDisconnectShuttingDown
|
|
TDisconnectMalProperties
|
|
TDisconnectMalReasonCode
|
|
TDisconnectZeroNonZeroExpiry
|
|
TAuth
|
|
TAuthMalReasonCode
|
|
TAuthMalProperties
|
|
TAuthInvalidReason
|
|
TAuthInvalidReason2
|
|
)
|
|
|
|
// TPacketData contains individual encoding and decoding scenarios for each packet type.
|
|
var TPacketData = map[byte]TPacketCases{
|
|
Connect: {
|
|
{
|
|
Case: TConnectMqtt31,
|
|
Desc: "mqtt v3.1",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Connect << 4, 17, // Fixed header
|
|
0, 6, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'I', 's', 'd', 'p', // Protocol Name
|
|
3, // Protocol Version
|
|
0, // Packet Flags
|
|
0, 30, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
},
|
|
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, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0, // Packet Flags
|
|
0, 60, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
},
|
|
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, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
5, // Protocol Version
|
|
0, // Packet Flags
|
|
0, 30, // Keepalive
|
|
|
|
// Properties
|
|
71, // length
|
|
17, 0, 0, 0, 120, // Session Expiry Interval (17)
|
|
21, 0, 5, 'S', 'H', 'A', '-', '1', // Authentication Method (21)
|
|
22, 0, 9, 'a', 'u', 't', 'h', '-', 'd', 'a', 't', 'a', // Authentication Data (22)
|
|
23, 1, // Request Problem Info (23)
|
|
25, 1, // Request Response Info (25)
|
|
33, 1, 244, // Receive Maximum (33)
|
|
34, 3, 231, // Topic Alias Maximum (34)
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
38, // User Properties (38)
|
|
0, 4, 'k', 'e', 'y', '2',
|
|
0, 6, 'v', 'a', 'l', 'u', 'e', '2',
|
|
39, 0, 0, 125, 0, // Maximum Packet Size (39)
|
|
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
},
|
|
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, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
2, // Packet Flags
|
|
0, 45, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
},
|
|
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, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
5, // Protocol Version
|
|
14, // Packet Flags
|
|
0, 30, // Keepalive
|
|
|
|
// Properties
|
|
10, // length
|
|
17, 0, 0, 0, 120, // Session Expiry Interval (17)
|
|
39, 0, 0, 125, 0, // Maximum Packet Size (39)
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
5, // will properties length
|
|
24, 0, 0, 2, 88, // will delay interval (24)
|
|
|
|
0, 3, // Will Topic - MSB+LSB
|
|
'l', 'w', 't',
|
|
0, 8, // Will Message MSB+LSB
|
|
'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, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0 | 1<<6 | 1<<7, // Packet Flags
|
|
0, 20, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
0, 5, // Username MSB+LSB
|
|
'm', 'o', 'c', 'h', 'i',
|
|
0, 4, // Password MSB+LSB
|
|
',', '.', '/', ';',
|
|
},
|
|
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, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
206, // Packet Flags
|
|
0, 120, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
0, 3, // Will Topic - MSB+LSB
|
|
'l', 'w', 't',
|
|
0, 9, // Will Message MSB+LSB
|
|
'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
|
|
0, 5, // Username MSB+LSB
|
|
'm', 'o', 'c', 'h', 'i',
|
|
0, 4, // Password MSB+LSB
|
|
',', '.', '/', ';',
|
|
},
|
|
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, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
5, // Protocol Version
|
|
130, // Packet Flags
|
|
0, 30, // Keepalive
|
|
5, // length
|
|
17, 0, 0, 0, 120, // Session Expiry Interval (17)
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
0, 0, // Username MSB+LSB
|
|
},
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
|
|
// Fail States
|
|
{
|
|
Case: TConnectMalProtocolName,
|
|
Desc: "malformed protocol name",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProtocolName,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 7, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'I', 's', 'd', // Protocol Name
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalProtocolVersion,
|
|
Desc: "malformed protocol version",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProtocolVersion,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalFlags,
|
|
Desc: "malformed flags",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedFlags,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalKeepalive,
|
|
Desc: "malformed keepalive",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedKeepalive,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0, // Flags
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalClientID,
|
|
Desc: "malformed client id",
|
|
Group: "decode",
|
|
FailFirst: ErrClientIdentifierNotValid,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0, // Flags
|
|
0, 20, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', // Client ID "zen"
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalWillTopic,
|
|
Desc: "malformed will topic",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedWillTopic,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
14, // Flags
|
|
0, 20, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
0, 6, // Will Topic - MSB+LSB
|
|
'l',
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalWillFlag,
|
|
Desc: "malformed will flag",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedWillPayload,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
14, // Flags
|
|
0, 20, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
0, 3, // Will Topic - MSB+LSB
|
|
'l', 'w', 't',
|
|
0, 9, // Will Message MSB+LSB
|
|
'n', 'o', 't', ' ', 'a',
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalUsername,
|
|
Desc: "malformed username",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedUsername,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
206, // Flags
|
|
0, 20, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
0, 3, // Will Topic - MSB+LSB
|
|
'l', 'w', 't',
|
|
0, 9, // Will Message MSB+LSB
|
|
'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
|
|
0, 5, // Username MSB+LSB
|
|
'm', 'o', 'c',
|
|
},
|
|
},
|
|
|
|
{
|
|
Case: TConnectInvalidFlagNoUsername,
|
|
Desc: "username flag with no username bytes",
|
|
Group: "decode",
|
|
FailFirst: ErrProtocolViolationFlagNoUsername,
|
|
RawBytes: []byte{
|
|
Connect << 4, 17, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
5, // Protocol Version
|
|
130, // Flags
|
|
0, 20, // Keepalive
|
|
0,
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalPassword,
|
|
Desc: "malformed password",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPassword,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
206, // Flags
|
|
0, 20, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
0, 3, // Will Topic - MSB+LSB
|
|
'l', 'w', 't',
|
|
0, 9, // Will Message MSB+LSB
|
|
'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
|
|
0, 5, // Username MSB+LSB
|
|
'm', 'o', 'c', 'h', 'i',
|
|
0, 4, // Password MSB+LSB
|
|
',', '.',
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalFixedHeader,
|
|
Desc: "malformed fixedheader oversize",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProtocolName, // packet test doesn't test fixedheader oversize
|
|
RawBytes: []byte{
|
|
Connect << 4, 255, 255, 255, 255, 255, // Fixed header
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalReservedBit,
|
|
Desc: "reserved bit not 0",
|
|
Group: "nodecode",
|
|
FailFirst: ErrProtocolViolation,
|
|
RawBytes: []byte{
|
|
Connect << 4, 15, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
1, // Packet Flags
|
|
0, 45, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n',
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Connect << 4, 47, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
5, // Protocol Version
|
|
14, // Packet Flags
|
|
0, 30, // Keepalive
|
|
10,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectMalWillProperties,
|
|
Desc: "malformed will properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedWillProperties,
|
|
RawBytes: []byte{
|
|
Connect << 4, 47, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
5, // Protocol Version
|
|
14, // Packet Flags
|
|
0, 30, // Keepalive
|
|
10, // length
|
|
17, 0, 0, 0, 120, // Session Expiry Interval (17)
|
|
39, 0, 0, 125, 0, // Maximum Packet Size (39)
|
|
0, 3, // Client ID - MSB+LSB
|
|
'z', 'e', 'n', // Client ID "zen"
|
|
5, // will properties length
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
|
|
// Validation Tests
|
|
{
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
|
|
// Spec Tests
|
|
{
|
|
Case: TConnectSpecInvalidUTF8D800,
|
|
Desc: "invalid utf8 string (a) - code point U+D800",
|
|
Group: "decode",
|
|
FailFirst: ErrClientIdentifierNotValid,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0, // Flags
|
|
0, 20, // Keepalive
|
|
0, 4, // Client ID - MSB+LSB
|
|
'e', 0xed, 0xa0, 0x80, // Client id bearing U+D800
|
|
},
|
|
},
|
|
{
|
|
Case: TConnectSpecInvalidUTF8DFFF,
|
|
Desc: "invalid utf8 string (b) - code point U+DFFF",
|
|
Group: "decode",
|
|
FailFirst: ErrClientIdentifierNotValid,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0, // Flags
|
|
0, 20, // Keepalive
|
|
0, 4, // Client ID - MSB+LSB
|
|
'e', 0xed, 0xa3, 0xbf, // Client id bearing U+D8FF
|
|
},
|
|
},
|
|
|
|
{
|
|
Case: TConnectSpecInvalidUTF80000,
|
|
Desc: "invalid utf8 string (c) - code point U+0000",
|
|
Group: "decode",
|
|
FailFirst: ErrClientIdentifierNotValid,
|
|
RawBytes: []byte{
|
|
Connect << 4, 0, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0, // Flags
|
|
0, 20, // Keepalive
|
|
0, 3, // Client ID - MSB+LSB
|
|
'e', 0xc0, 0x80, // Client id bearing U+0000
|
|
},
|
|
},
|
|
|
|
{
|
|
Case: TConnectSpecInvalidUTF8NoSkip,
|
|
Desc: "utf8 string must not skip or strip code point U+FEFF",
|
|
//Group: "decode",
|
|
//FailFirst: ErrMalformedClientID,
|
|
RawBytes: []byte{
|
|
Connect << 4, 18, // Fixed header
|
|
0, 4, // Protocol Name - MSB+LSB
|
|
'M', 'Q', 'T', 'T', // Protocol Name
|
|
4, // Protocol Version
|
|
0, // Flags
|
|
0, 20, // Keepalive
|
|
0, 6, // Client ID - MSB+LSB
|
|
'e', 'b', 0xEF, 0xBB, 0xBF, 'd', // Client id bearing U+FEFF
|
|
},
|
|
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, // fixed header
|
|
0, // No existing session
|
|
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, // fixed header
|
|
1, // Session present
|
|
CodeSuccess.Code,
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Connack,
|
|
Remaining: 2,
|
|
},
|
|
SessionPresent: true,
|
|
ReasonCode: CodeSuccess.Code,
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackAcceptedAdjustedExpiryInterval,
|
|
Desc: "accepted, no session, adjusted expiry interval mqtt5",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Connack << 4, 11, // fixed header
|
|
0, // Session present
|
|
CodeSuccess.Code,
|
|
8, // length
|
|
17, 0, 0, 0, 120, // Session Expiry Interval (17)
|
|
19, 0, 10, // Server Keep Alive (19)
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Connack,
|
|
Remaining: 11,
|
|
},
|
|
ReasonCode: CodeSuccess.Code,
|
|
Properties: Properties{
|
|
SessionExpiryInterval: uint32(120),
|
|
SessionExpiryIntervalFlag: true,
|
|
ServerKeepAlive: uint16(10),
|
|
ServerKeepAliveFlag: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackAcceptedMqtt5,
|
|
Desc: "accepted no session mqtt5",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Connack << 4, 124, // fixed header
|
|
0, // No existing session
|
|
CodeSuccess.Code,
|
|
// Properties
|
|
121, // length
|
|
17, 0, 0, 0, 120, // Session Expiry Interval (17)
|
|
18, 0, 8, 'm', 'o', 'c', 'h', 'i', '-', 'v', '5', // Assigned Client ID (18)
|
|
19, 0, 20, // Server Keep Alive (19)
|
|
21, 0, 5, 'S', 'H', 'A', '-', '1', // Authentication Method (21)
|
|
22, 0, 9, 'a', 'u', 't', 'h', '-', 'd', 'a', 't', 'a', // Authentication Data (22)
|
|
26, 0, 8, 'r', 'e', 's', 'p', 'o', 'n', 's', 'e', // Response Info (26)
|
|
28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', // Server Reference (28)
|
|
31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', // Reason String (31)
|
|
33, 1, 244, // Receive Maximum (33)
|
|
34, 3, 231, // Topic Alias Maximum (34)
|
|
36, 1, // Maximum Qos (36)
|
|
37, 1, // Retain Available (37)
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
38, // User Properties (38)
|
|
0, 4, 'k', 'e', 'y', '2',
|
|
0, 6, 'v', 'a', 'l', 'u', 'e', '2',
|
|
39, 0, 0, 125, 0, // Maximum Packet Size (39)
|
|
40, 1, // Wildcard Subscriptions Available (40)
|
|
41, 1, // Subscription ID Available (41)
|
|
42, 1, // Shared Subscriptions Available (42)
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Connack,
|
|
Remaining: 124,
|
|
},
|
|
SessionPresent: false,
|
|
ReasonCode: CodeSuccess.Code,
|
|
Properties: Properties{
|
|
SessionExpiryInterval: uint32(120),
|
|
SessionExpiryIntervalFlag: true,
|
|
AssignedClientID: "mochi-v5",
|
|
ServerKeepAlive: uint16(20),
|
|
ServerKeepAliveFlag: true,
|
|
AuthenticationMethod: "SHA-1",
|
|
AuthenticationData: []byte("auth-data"),
|
|
ResponseInfo: "response",
|
|
ServerReference: "mochi-2",
|
|
ReasonString: "reason",
|
|
ReceiveMaximum: uint16(500),
|
|
TopicAliasMaximum: uint16(999),
|
|
MaximumQos: byte(1),
|
|
MaximumQosFlag: true,
|
|
RetainAvailable: byte(1),
|
|
RetainAvailableFlag: true,
|
|
User: []UserProperty{
|
|
{
|
|
Key: "hello",
|
|
Val: "世界",
|
|
},
|
|
{
|
|
Key: "key2",
|
|
Val: "value2",
|
|
},
|
|
},
|
|
MaximumPacketSize: uint32(32000),
|
|
WildcardSubAvailable: byte(1),
|
|
WildcardSubAvailableFlag: true,
|
|
SubIDAvailable: byte(1),
|
|
SubIDAvailableFlag: true,
|
|
SharedSubAvailable: byte(1),
|
|
SharedSubAvailableFlag: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackMinMqtt5,
|
|
Desc: "accepted min properties mqtt5",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Connack << 4, 16, // fixed header
|
|
1, // existing session
|
|
CodeSuccess.Code,
|
|
13, // Properties length
|
|
18, 0, 5, 'm', 'o', 'c', 'h', 'i', // Assigned Client ID (18)
|
|
19, 0, 20, // Server Keep Alive (19)
|
|
36, 1, // Maximum Qos (36)
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Connack,
|
|
Remaining: 16,
|
|
},
|
|
SessionPresent: true,
|
|
ReasonCode: CodeSuccess.Code,
|
|
Properties: Properties{
|
|
ServerKeepAlive: uint16(20),
|
|
ServerKeepAliveFlag: true,
|
|
AssignedClientID: "mochi",
|
|
MaximumQos: byte(1),
|
|
MaximumQosFlag: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackMinCleanMqtt5,
|
|
Desc: "accepted min properties mqtt5b",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Connack << 4, 6, // fixed header
|
|
0, // existing session
|
|
CodeSuccess.Code,
|
|
3, // Properties length
|
|
19, 0, 10, // server keepalive
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Connack,
|
|
Remaining: 16,
|
|
},
|
|
SessionPresent: false,
|
|
ReasonCode: CodeSuccess.Code,
|
|
Properties: Properties{
|
|
ServerKeepAlive: uint16(10),
|
|
ServerKeepAliveFlag: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackInvalidMinMqtt5,
|
|
Desc: "failure min properties mqtt5",
|
|
Primary: true,
|
|
RawBytes: append([]byte{
|
|
Connack << 4, 26, // fixed header
|
|
0, // No existing session
|
|
ErrUnspecifiedError.Code,
|
|
// Properties
|
|
23, // length
|
|
19, 0, 20, // Server Keep Alive (19)
|
|
31, 0, 17, // Reason String (31)
|
|
}, []byte(ErrUnspecifiedError.Reason)...),
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Connack,
|
|
Remaining: 25,
|
|
},
|
|
SessionPresent: false,
|
|
ReasonCode: ErrUnspecifiedError.Code,
|
|
Properties: Properties{
|
|
ServerKeepAlive: uint16(20),
|
|
ServerKeepAliveFlag: true,
|
|
ReasonString: ErrUnspecifiedError.Reason,
|
|
},
|
|
},
|
|
},
|
|
|
|
{
|
|
Case: TConnackProtocolViolationNoSession,
|
|
Desc: "miscellaneous protocol violation",
|
|
RawBytes: []byte{
|
|
Connack << 4, 2, // fixed header
|
|
0, // Session present
|
|
ErrProtocolViolation.Code,
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Connack,
|
|
Remaining: 2,
|
|
},
|
|
ReasonCode: ErrProtocolViolation.Code,
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackBadProtocolVersion,
|
|
Desc: "bad protocol version",
|
|
RawBytes: []byte{
|
|
Connack << 4, 2, // fixed header
|
|
1, // Session present
|
|
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, // fixed header
|
|
1, // Session present
|
|
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, // fixed header
|
|
1, // Session present
|
|
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, // fixed header
|
|
1, // Session present
|
|
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, // fixed header
|
|
0, // No session present
|
|
Err3NotAuthorized.Code, // use v3 remapping
|
|
},
|
|
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, // fixed header
|
|
0, // No session present
|
|
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, // fixed header
|
|
1, // Session present
|
|
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, // fixed header
|
|
0, // No existing session
|
|
CodeSuccess.Code,
|
|
19, // length
|
|
28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', // Server Reference (28)
|
|
31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', // Reason String (31)
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
},
|
|
ActualBytes: []byte{
|
|
Connack << 4, 13, // fixed header
|
|
0, // No existing session
|
|
CodeSuccess.Code,
|
|
10, // length
|
|
28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', // Server Reference (28)
|
|
},
|
|
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, // fixed header
|
|
0, // No existing session
|
|
CodeSuccess.Code,
|
|
19, // length
|
|
28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', // Server Reference (28)
|
|
31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', // Reason String (31)
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
},
|
|
ActualBytes: []byte{
|
|
Connack << 4, 22, // fixed header
|
|
0, // No existing session
|
|
CodeSuccess.Code,
|
|
19, // length
|
|
28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', // Server Reference (28)
|
|
31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', // Reason String (31)
|
|
},
|
|
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: "世界",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// Fail States
|
|
{
|
|
Case: TConnackMalSessionPresent,
|
|
Desc: "malformed session present",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedSessionPresent,
|
|
RawBytes: []byte{
|
|
Connect << 4, 2, // Fixed header
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackMalReturnCode,
|
|
Desc: "malformed bad return Code",
|
|
Group: "decode",
|
|
//Primary: true,
|
|
FailFirst: ErrMalformedReasonCode,
|
|
RawBytes: []byte{
|
|
Connect << 4, 2, // Fixed header
|
|
0,
|
|
},
|
|
},
|
|
{
|
|
Case: TConnackMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Connack << 4, 40, // fixed header
|
|
0, // No existing session
|
|
CodeSuccess.Code,
|
|
19, // length
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
|
|
Publish: {
|
|
{
|
|
Case: TPublishNoPayload,
|
|
Desc: "no payload",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Publish << 4, 7, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
58, // length
|
|
1, 1, // Payload Format (1)
|
|
2, 0, 0, 0, 2, // Message Expiry (2)
|
|
3, 0, 10, 't', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n', // Content Type (3)
|
|
8, 0, 5, 'a', '/', 'b', '/', 'c', // Response Topic (8)
|
|
9, 0, 4, 'd', 'a', 't', 'a', // Correlations Data (9)
|
|
11, 202, 212, 19, // Subscription Identifier (11)
|
|
35, 0, 3, // Topic Alias (35)
|
|
38, // User Properties (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', // Payload
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Publish,
|
|
Remaining: 77,
|
|
},
|
|
TopicName: "a/b/c",
|
|
Properties: Properties{
|
|
PayloadFormat: byte(1), // UTF-8 Format
|
|
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, // Fixed header
|
|
0, 0, // Topic Name - LSB+MSB
|
|
3, // length
|
|
35, 0, 1, // Topic Alias (35)
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
3, // length
|
|
35, 0, 1, // Topic Alias (35)
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, 7, // Packet ID - LSB+MSB
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Publish,
|
|
Qos: 1,
|
|
Remaining: 20,
|
|
},
|
|
TopicName: "a/b/c",
|
|
Payload: []byte("hello mochi"),
|
|
PacketID: 7,
|
|
},
|
|
},
|
|
{
|
|
Case: TPublishQos1Dup,
|
|
Desc: "qos:1, dup:true, packet id",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Publish<<4 | 2 | 8, 20, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, 7, // Packet ID - LSB+MSB
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'y', '/', 'u', '/', 'i', // Topic Name
|
|
0, 7, // Packet ID - LSB+MSB
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, 7, // Packet ID - LSB+MSB
|
|
'h', 'e', 'l', 'l', 'o', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, 7, // Packet ID - LSB+MSB
|
|
// Properties
|
|
16, // length
|
|
38, // User Properties (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', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
4, // properties length
|
|
11, 2, // Subscription Identifier (11)
|
|
11, 3, // Subscription Identifier (11)
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, 1, // Packet ID - LSB+MSB
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, // properties length
|
|
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', // Topic Name
|
|
'h', 'e', 'l', 'l', 'o', // Payload
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Publish,
|
|
Dup: true,
|
|
},
|
|
TopicName: "a/b",
|
|
Payload: []byte("hello"),
|
|
},
|
|
},
|
|
|
|
// Fail States
|
|
{
|
|
Case: TPublishMalTopicName,
|
|
Desc: "malformed topic name",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedTopic,
|
|
RawBytes: []byte{
|
|
Publish << 4, 7, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/',
|
|
0, 11, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TPublishMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Publish<<4 | 2, 7, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'x', '/', 'y', '/', 'z', // Topic Name
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TPublishMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Publish << 4, 35, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
16,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
|
|
// Copy tests
|
|
{
|
|
Case: TPublishCopyBasic,
|
|
Desc: "basic copyable",
|
|
Group: "copy",
|
|
RawBytes: []byte{
|
|
Publish << 4, 18, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'z', '/', 'e', '/', 'n', // Topic Name
|
|
'm', 'o', 'c', 'h', 'i', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Publish,
|
|
Dup: true,
|
|
Retain: true,
|
|
Qos: 1,
|
|
},
|
|
TopicName: "z/e/n",
|
|
Payload: []byte("mochi mochi"),
|
|
},
|
|
},
|
|
|
|
// Spec tests
|
|
{
|
|
Case: TPublishSpecQos0NoPacketID,
|
|
Desc: "packet id must be 0 if qos is 0 (a)",
|
|
Group: "encode",
|
|
// this version tests for correct byte array mutuation.
|
|
// this does not check if -incoming- Packets are parsed as correct,
|
|
// it is impossible for the parser to determine if the payload start is incorrect.
|
|
RawBytes: []byte{
|
|
Publish << 4, 12, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, 3, // Packet ID - LSB+MSB
|
|
'h', 'e', 'l', 'l', 'o', // Payload
|
|
},
|
|
ActualBytes: []byte{
|
|
Publish << 4, 12, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
// Packet ID is removed.
|
|
'h', 'e', 'l', 'l', 'o', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, 0, // Packet ID - LSB+MSB
|
|
'h', 'e', 'l', 'l', 'o', // Payload
|
|
},
|
|
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, // Fixed header
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', // Topic Name
|
|
'h', 'e', 'l', 'l', 'o', // Payload
|
|
},
|
|
Packet: &Packet{
|
|
Mods: Mods{
|
|
MaxSize: 2,
|
|
},
|
|
FixedHeader: FixedHeader{
|
|
Type: Publish,
|
|
},
|
|
TopicName: "a/b",
|
|
Payload: []byte("hello"),
|
|
},
|
|
},
|
|
|
|
// Validation Tests
|
|
{
|
|
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, // Fixed header
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'$', 'S', 'Y', 'S', '/', 'a', 'n', 'y', // Topic Name
|
|
'y', // Payload
|
|
},
|
|
},
|
|
},
|
|
|
|
Puback: {
|
|
{
|
|
Case: TPuback,
|
|
Desc: "puback",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Puback << 4, 2, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Puback,
|
|
Remaining: 2,
|
|
},
|
|
PacketID: 7,
|
|
},
|
|
},
|
|
{
|
|
Case: TPubackMqtt5,
|
|
Desc: "mqtt5",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Puback << 4, 20, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
CodeGrantedQos0.Code, // Reason Code
|
|
16, // Properties Length
|
|
// 31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', // Reason String (31)
|
|
38, // User Properties (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: "世界",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Fail states
|
|
{
|
|
Case: TPubackMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Puback << 4, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TPubackMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Puback << 4, 20, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
CodeGrantedQos0.Code, // Reason Code
|
|
16,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
Pubrec: {
|
|
{
|
|
Case: TPubrec,
|
|
Desc: "pubrec",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pubrec << 4, 2, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Pubrec,
|
|
Remaining: 2,
|
|
},
|
|
PacketID: 7,
|
|
},
|
|
},
|
|
{
|
|
Case: TPubrecMqtt5,
|
|
Desc: "pubrec mqtt5",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pubrec << 4, 20, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
CodeSuccess.Code, // Reason Code
|
|
16, // Properties Length
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Pubrec,
|
|
Remaining: 20,
|
|
},
|
|
PacketID: 7,
|
|
ReasonCode: CodeSuccess.Code,
|
|
Properties: Properties{
|
|
User: []UserProperty{
|
|
{
|
|
Key: "hello",
|
|
Val: "世界",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TPubrecMqtt5IDInUse,
|
|
Desc: "packet id in use mqtt5",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pubrec << 4, 31, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
ErrPacketIdentifierInUse.Code, // Reason Code
|
|
27, // Properties Length
|
|
31, 0, 24, 'p', 'a', 'c', 'k', 'e', 't',
|
|
' ', 'i', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r',
|
|
' ', 'i', 'n',
|
|
' ', 'u', 's', 'e', // Reason String (31)
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Pubrec,
|
|
Remaining: 31,
|
|
},
|
|
PacketID: 7,
|
|
ReasonCode: ErrPacketIdentifierInUse.Code,
|
|
Properties: Properties{
|
|
ReasonString: ErrPacketIdentifierInUse.Reason,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TPubrecMalReasonCode,
|
|
Desc: "malformed reason code",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedReasonCode,
|
|
RawBytes: []byte{
|
|
Pubrec << 4, 31, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
// Validation
|
|
{
|
|
Case: TPubrecInvalidReason,
|
|
Desc: "invalid reason code",
|
|
Group: "validate",
|
|
FailFirst: ErrProtocolViolationInvalidReason,
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Pubrec,
|
|
},
|
|
PacketID: 7,
|
|
ReasonCode: ErrConnectionRateExceeded.Code,
|
|
},
|
|
},
|
|
// Fail states
|
|
{
|
|
Case: TPubrecMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Pubrec << 4, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TPubrecMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Pubrec << 4, 31, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
ErrPacketIdentifierInUse.Code, // Reason Code
|
|
27,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
Pubrel: {
|
|
{
|
|
Case: TPubrel,
|
|
Desc: "pubrel",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pubrel<<4 | 1<<1, 2, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
},
|
|
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, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
CodeSuccess.Code, // Reason Code
|
|
16, // Properties Length
|
|
38, // User Properties (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, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
ErrPacketIdentifierNotFound.Code, // Reason Code
|
|
30, // Properties Length
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
// Validation
|
|
{
|
|
Case: TPubrelInvalidReason,
|
|
Desc: "invalid reason code",
|
|
Group: "validate",
|
|
FailFirst: ErrProtocolViolationInvalidReason,
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Pubrel,
|
|
},
|
|
PacketID: 7,
|
|
ReasonCode: ErrConnectionRateExceeded.Code,
|
|
},
|
|
},
|
|
// Fail states
|
|
{
|
|
Case: TPubrelMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Pubrel << 4, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TPubrelMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Pubrel<<4 | 1<<1, 20, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
CodeSuccess.Code, // Reason Code
|
|
16,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
Pubcomp: {
|
|
{
|
|
Case: TPubcomp,
|
|
Desc: "pubcomp",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pubcomp << 4, 2, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Pubcomp,
|
|
Remaining: 2,
|
|
},
|
|
PacketID: 7,
|
|
},
|
|
},
|
|
{
|
|
Case: TPubcompMqtt5,
|
|
Desc: "mqtt5",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pubcomp << 4, 20, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
CodeSuccess.Code, // Reason Code
|
|
16, // Properties Length
|
|
38, // User Properties (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, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
ErrPacketIdentifierNotFound.Code, // Reason Code
|
|
30, // Properties Length
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
// Validation
|
|
{
|
|
Case: TPubcompInvalidReason,
|
|
Desc: "invalid reason code",
|
|
Group: "validate",
|
|
FailFirst: ErrProtocolViolationInvalidReason,
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Pubcomp,
|
|
},
|
|
ReasonCode: ErrConnectionRateExceeded.Code,
|
|
},
|
|
},
|
|
// Fail states
|
|
{
|
|
Case: TPubcompMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Pubcomp << 4, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TPubcompMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Pubcomp << 4, 34, // Fixed header
|
|
0, 7, // Packet ID - LSB+MSB
|
|
ErrPacketIdentifierNotFound.Code, // Reason Code
|
|
22,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
Subscribe: {
|
|
{
|
|
Case: TSubscribe,
|
|
Desc: "subscribe",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Subscribe<<4 | 1<<1, 10, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0, // QoS
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', // Topic Name
|
|
0, // QoS
|
|
|
|
0, 11, // Topic Name - LSB+MSB
|
|
'd', '/', 'e', '/', 'f', '/', 'g', '/', 'h', '/', 'i', // Topic Name
|
|
1, // QoS
|
|
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'x', '/', 'y', '/', 'z', // Topic Name
|
|
2, // QoS
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
20,
|
|
11, 202, 212, 19, // Subscription Identifier (11)
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
|
|
0, 5, 'a', '/', 'b', '/', 'c', // Topic Name
|
|
46, // subscription options
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, // no properties
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0 | 1<<4, // subscription options
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, // no properties
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0 | 2<<4, // subscription options
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, // no properties
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
0 | 1<<3, // subscription options
|
|
},
|
|
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},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Fail states
|
|
{
|
|
Case: TSubscribeMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Subscribe<<4 | 1<<1, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TSubscribeMalTopic,
|
|
Desc: "malformed topic",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedTopic,
|
|
RawBytes: []byte{
|
|
Subscribe<<4 | 1<<1, 2, // Fixed header
|
|
0, 21, // Packet ID - LSB+MSB
|
|
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'a', '/',
|
|
},
|
|
},
|
|
{
|
|
Case: TSubscribeMalQos,
|
|
Desc: "malformed subscribe - qos",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedQos,
|
|
RawBytes: []byte{
|
|
Subscribe<<4 | 1<<1, 2, // Fixed header
|
|
0, 22, // Packet ID - LSB+MSB
|
|
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'j', '/', 'b', // Topic Name
|
|
|
|
},
|
|
},
|
|
{
|
|
Case: TSubscribeMalQosRange,
|
|
Desc: "malformed qos out of range",
|
|
Group: "decode",
|
|
FailFirst: ErrProtocolViolationQosOutOfRange,
|
|
RawBytes: []byte{
|
|
Subscribe<<4 | 1<<1, 2, // Fixed header
|
|
0, 22, // Packet ID - LSB+MSB
|
|
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'c', '/', 'd', // Topic Name
|
|
5, // QoS
|
|
|
|
},
|
|
},
|
|
{
|
|
Case: TSubscribeMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Subscribe << 4, 11, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
4,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
|
|
// Validation
|
|
{
|
|
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},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Spec tests
|
|
{
|
|
Case: TSubscribeSpecQosMustPacketID,
|
|
Desc: "no packet id with qos > 0",
|
|
Group: "encode",
|
|
Expect: ErrProtocolViolationNoPacketID,
|
|
RawBytes: []byte{
|
|
Subscribe<<4 | 1<<1, 10, // Fixed header
|
|
0, 0, // Packet ID - LSB+MSB
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
1, // QoS
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, // Return Code QoS 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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, // Return Code QoS 0
|
|
1, // Return Code QoS 1
|
|
2, // Return Code QoS 2
|
|
0x80, // Return Code fail
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, // no properties
|
|
ErrNotAuthorized.Code, // Return Code QoS 0
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
ErrUnspecifiedError.Code, // Return Code QoS 0
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, // no properties
|
|
ErrUnspecifiedError.Code, // Return Code QoS 0
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID
|
|
16, // Properties Length
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
CodeGrantedQos2.Code, // Return Code QoS 0
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID
|
|
43, // Properties Length
|
|
31, 0, 24, 'p', 'a', 'c', 'k', 'e', 't',
|
|
' ', 'i', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r',
|
|
' ', 'i', 'n',
|
|
' ', 'u', 's', 'e', // Reason String (31)
|
|
38, // User Properties (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: "世界",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Fail states
|
|
{
|
|
Case: TSubackMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Suback << 4, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
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, // no properties
|
|
ErrTopicFilterInvalid.Code,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
{
|
|
Case: TSubackInvalidSharedNoLocal,
|
|
Desc: "invalid shared no local",
|
|
Group: "reference",
|
|
RawBytes: []byte{
|
|
Suback << 4, 4,
|
|
0, 15,
|
|
0, // no properties
|
|
ErrProtocolViolationInvalidSharedNoLocal.Code,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
|
|
Unsubscribe: {
|
|
{
|
|
Case: TUnsubscribe,
|
|
Desc: "unsubscribe",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Unsubscribe<<4 | 1<<1, 9, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
|
|
},
|
|
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, // Fixed header
|
|
0, 35, // Packet ID - LSB+MSB
|
|
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', // Topic Name
|
|
|
|
0, 11, // Topic Name - LSB+MSB
|
|
'd', '/', 'e', '/', 'f', '/', 'g', '/', 'h', '/', 'i', // Topic Name
|
|
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'x', '/', 'y', '/', 'z', // Topic Name
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
|
|
16,
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'a', '/', 'b',
|
|
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'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"},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Fail states
|
|
{
|
|
Case: TUnsubscribeMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Unsubscribe << 4, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TUnsubscribeMalTopicName,
|
|
Desc: "malformed topic",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedTopic,
|
|
RawBytes: []byte{
|
|
Unsubscribe << 4, 2, // Fixed header
|
|
0, 21, // Packet ID - LSB+MSB
|
|
0, 3, // Topic Name - LSB+MSB
|
|
'a', '/',
|
|
},
|
|
},
|
|
{
|
|
Case: TUnsubscribeMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Unsubscribe<<4 | 1<<1, 31, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
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, // Fixed header
|
|
0, 0, // Packet ID - LSB+MSB
|
|
0, 5, // Topic Name - LSB+MSB
|
|
'a', '/', 'b', '/', 'c', // Topic Name
|
|
},
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Unsuback,
|
|
Remaining: 2,
|
|
},
|
|
PacketID: 15,
|
|
},
|
|
},
|
|
{
|
|
Case: TUnsubackMany,
|
|
Desc: "unsuback many",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Unsuback << 4, 5, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
16, // Properties Length
|
|
38, // User Properties (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, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
43, // Properties Length
|
|
31, 0, 24, 'p', 'a', 'c', 'k', 'e', 't',
|
|
' ', 'i', 'd', 'e', 'n', 't', 'i', 'f', 'i', 'e', 'r',
|
|
' ', 'i', 'n',
|
|
' ', 'u', 's', 'e', // Reason String (31)
|
|
38, // User Properties (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: "世界",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Fail states
|
|
{
|
|
Case: TUnsubackMalPacketID,
|
|
Desc: "malformed packet id",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedPacketID,
|
|
RawBytes: []byte{
|
|
Unsuback << 4, 2, // Fixed header
|
|
0, // Packet ID - LSB+MSB
|
|
},
|
|
},
|
|
{
|
|
Case: TUnsubackMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Unsuback << 4, 48, // Fixed header
|
|
0, 15, // Packet ID - LSB+MSB
|
|
43,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
|
|
Pingreq: {
|
|
{
|
|
Case: TPingreq,
|
|
Desc: "ping request",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pingreq << 4, 0, // fixed header
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Pingreq,
|
|
Remaining: 0,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Pingresp: {
|
|
{
|
|
Case: TPingresp,
|
|
Desc: "ping response",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Pingresp << 4, 0, // fixed header
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Pingresp,
|
|
Remaining: 0,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
Disconnect: {
|
|
{
|
|
Case: TDisconnect,
|
|
Desc: "disconnect",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Disconnect << 4, 0, // fixed header
|
|
},
|
|
Packet: &Packet{
|
|
FixedHeader: FixedHeader{
|
|
Type: Disconnect,
|
|
Remaining: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TDisconnectTakeover,
|
|
Desc: "takeover",
|
|
Primary: true,
|
|
RawBytes: append([]byte{
|
|
Disconnect << 4, 21, // fixed header
|
|
ErrSessionTakenOver.Code, // Reason Code
|
|
19, // Properties Length
|
|
31, 0, 16, // Reason String (31)
|
|
}, []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, // fixed header
|
|
ErrServerShuttingDown.Code, // Reason Code
|
|
23, // Properties Length
|
|
31, 0, 20, // Reason String (31)
|
|
}, []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, // fixed header
|
|
CodeDisconnect.Code, // Reason Code
|
|
20, // Properties Length
|
|
17, 0, 0, 0, 120, // Session Expiry Interval (17)
|
|
31, 0, 12, // Reason String (31)
|
|
}, []byte(CodeDisconnect.Reason)...),
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
FixedHeader: FixedHeader{
|
|
Type: Disconnect,
|
|
Remaining: 22,
|
|
},
|
|
ReasonCode: CodeDisconnect.Code,
|
|
Properties: Properties{
|
|
ReasonString: CodeDisconnect.Reason,
|
|
SessionExpiryInterval: 120,
|
|
SessionExpiryIntervalFlag: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Case: TDisconnectSecondConnect,
|
|
Desc: "second connect packet mqtt5",
|
|
RawBytes: append([]byte{
|
|
Disconnect << 4, 46, // fixed header
|
|
ErrProtocolViolationSecondConnect.Code,
|
|
44,
|
|
31, 0, 41, // Reason String (31)
|
|
}, []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, // fixed header
|
|
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, // fixed header
|
|
ErrReceiveMaximum.Code,
|
|
27, // Properties Length
|
|
31, 0, 24, // Reason String (31)
|
|
}, []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, // fixed header
|
|
CodeDisconnect.Code,
|
|
19, // length
|
|
28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', // Server Reference (28)
|
|
31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', // Reason String (31)
|
|
38, // User Properties (38)
|
|
0, 5, 'h', 'e', 'l', 'l', 'o',
|
|
0, 6, 228, 184, 150, 231, 149, 140,
|
|
},
|
|
ActualBytes: []byte{
|
|
Disconnect << 4, 12, // fixed header
|
|
CodeDisconnect.Code,
|
|
10, // length
|
|
28, 0, 7, 'm', 'o', 'c', 'h', 'i', '-', '2', // Server Reference (28)
|
|
},
|
|
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: "世界",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// fail states
|
|
{
|
|
Case: TDisconnectMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Disconnect << 4, 48, // fixed header
|
|
CodeDisconnect.Code, // Reason Code
|
|
46,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
{
|
|
Case: TDisconnectMalReasonCode,
|
|
Desc: "malformed reason code",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedReasonCode,
|
|
RawBytes: []byte{
|
|
Disconnect << 4, 48, // fixed header
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
},
|
|
Auth: {
|
|
{
|
|
Case: TAuth,
|
|
Desc: "auth",
|
|
Primary: true,
|
|
RawBytes: []byte{
|
|
Auth << 4, 47,
|
|
CodeSuccess.Code, // reason code
|
|
45,
|
|
21, 0, 5, 'S', 'H', 'A', '-', '1', // Authentication Method (21)
|
|
22, 0, 9, 'a', 'u', 't', 'h', '-', 'd', 'a', 't', 'a', // Authentication Data (22)
|
|
31, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', // Reason String (31)
|
|
38, // User Properties (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,
|
|
},
|
|
},
|
|
// fail states
|
|
{
|
|
Case: TAuthMalProperties,
|
|
Desc: "malformed properties",
|
|
Group: "decode",
|
|
FailFirst: ErrMalformedProperties,
|
|
RawBytes: []byte{
|
|
Auth << 4, 3,
|
|
CodeSuccess.Code,
|
|
12,
|
|
},
|
|
Packet: &Packet{
|
|
ProtocolVersion: 5,
|
|
},
|
|
},
|
|
// Validation
|
|
{
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
}
|