mirror of
				https://github.com/mochi-mqtt/server.git
				synced 2025-10-31 19:42:38 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			238 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // SPDX-License-Identifier: MIT
 | |
| // SPDX-FileCopyrightText: 2022 mochi-co
 | |
| // SPDX-FileContributor: mochi-co
 | |
| 
 | |
| package packets
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/stretchr/testify/require"
 | |
| )
 | |
| 
 | |
| type fixedHeaderTable struct {
 | |
| 	desc        string
 | |
| 	rawBytes    []byte
 | |
| 	header      FixedHeader
 | |
| 	packetError bool
 | |
| 	expect      error
 | |
| }
 | |
| 
 | |
| var fixedHeaderExpected = []fixedHeaderTable{
 | |
| 	{
 | |
| 		desc:     "connect",
 | |
| 		rawBytes: []byte{Connect << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Connect, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "connack",
 | |
| 		rawBytes: []byte{Connack << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Connack, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish",
 | |
| 		rawBytes: []byte{Publish << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish qos 1",
 | |
| 		rawBytes: []byte{Publish<<4 | 1<<1, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 1, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish qos 1 retain",
 | |
| 		rawBytes: []byte{Publish<<4 | 1<<1 | 1, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 1, Retain: true, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish qos 2",
 | |
| 		rawBytes: []byte{Publish<<4 | 2<<1, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 2, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish qos 2 retain",
 | |
| 		rawBytes: []byte{Publish<<4 | 2<<1 | 1, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 2, Retain: true, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish dup qos 0",
 | |
| 		rawBytes: []byte{Publish<<4 | 1<<3, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: true, Qos: 0, Retain: false, Remaining: 0},
 | |
| 		expect:   ErrProtocolViolationDupNoQos,
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish dup qos 0 retain",
 | |
| 		rawBytes: []byte{Publish<<4 | 1<<3 | 1, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: true, Qos: 0, Retain: true, Remaining: 0},
 | |
| 		expect:   ErrProtocolViolationDupNoQos,
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish dup qos 1 retain",
 | |
| 		rawBytes: []byte{Publish<<4 | 1<<3 | 1<<1 | 1, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: true, Qos: 1, Retain: true, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "publish dup qos 2 retain",
 | |
| 		rawBytes: []byte{Publish<<4 | 1<<3 | 2<<1 | 1, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: true, Qos: 2, Retain: true, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "puback",
 | |
| 		rawBytes: []byte{Puback << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Puback, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "pubrec",
 | |
| 		rawBytes: []byte{Pubrec << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Pubrec, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "pubrel",
 | |
| 		rawBytes: []byte{Pubrel<<4 | 1<<1, 0x00},
 | |
| 		header:   FixedHeader{Type: Pubrel, Dup: false, Qos: 1, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "pubcomp",
 | |
| 		rawBytes: []byte{Pubcomp << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Pubcomp, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "subscribe",
 | |
| 		rawBytes: []byte{Subscribe<<4 | 1<<1, 0x00},
 | |
| 		header:   FixedHeader{Type: Subscribe, Dup: false, Qos: 1, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "suback",
 | |
| 		rawBytes: []byte{Suback << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Suback, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "unsubscribe",
 | |
| 		rawBytes: []byte{Unsubscribe<<4 | 1<<1, 0x00},
 | |
| 		header:   FixedHeader{Type: Unsubscribe, Dup: false, Qos: 1, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "unsuback",
 | |
| 		rawBytes: []byte{Unsuback << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Unsuback, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "pingreq",
 | |
| 		rawBytes: []byte{Pingreq << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Pingreq, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "pingresp",
 | |
| 		rawBytes: []byte{Pingresp << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Pingresp, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "disconnect",
 | |
| 		rawBytes: []byte{Disconnect << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Disconnect, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "auth",
 | |
| 		rawBytes: []byte{Auth << 4, 0x00},
 | |
| 		header:   FixedHeader{Type: Auth, Dup: false, Qos: 0, Retain: false, Remaining: 0},
 | |
| 	},
 | |
| 
 | |
| 	// remaining length
 | |
| 	{
 | |
| 		desc:     "remaining length 10",
 | |
| 		rawBytes: []byte{Publish << 4, 0x0a},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 0, Retain: false, Remaining: 10},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "remaining length 512",
 | |
| 		rawBytes: []byte{Publish << 4, 0x80, 0x04},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 0, Retain: false, Remaining: 512},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "remaining length 978",
 | |
| 		rawBytes: []byte{Publish << 4, 0xd2, 0x07},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 0, Retain: false, Remaining: 978},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "remaining length 20202",
 | |
| 		rawBytes: []byte{Publish << 4, 0x86, 0x9d, 0x01},
 | |
| 		header:   FixedHeader{Type: Publish, Dup: false, Qos: 0, Retain: false, Remaining: 20102},
 | |
| 	},
 | |
| 	{
 | |
| 		desc:        "remaining length oversize",
 | |
| 		rawBytes:    []byte{Publish << 4, 0xd5, 0x86, 0xf9, 0x9e, 0x01},
 | |
| 		header:      FixedHeader{Type: Publish, Dup: false, Qos: 0, Retain: false, Remaining: 333333333},
 | |
| 		packetError: true,
 | |
| 	},
 | |
| 
 | |
| 	// Invalid flags for packet
 | |
| 	{
 | |
| 		desc:     "invalid type dup is true",
 | |
| 		rawBytes: []byte{Connect<<4 | 1<<3, 0x00},
 | |
| 		header:   FixedHeader{Type: Connect, Dup: true, Qos: 0, Retain: false, Remaining: 0},
 | |
| 		expect:   ErrMalformedFlags,
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "invalid type qos is 1",
 | |
| 		rawBytes: []byte{Connect<<4 | 1<<1, 0x00},
 | |
| 		header:   FixedHeader{Type: Connect, Dup: false, Qos: 1, Retain: false, Remaining: 0},
 | |
| 		expect:   ErrMalformedFlags,
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "invalid type retain is true",
 | |
| 		rawBytes: []byte{Connect<<4 | 1, 0x00},
 | |
| 		header:   FixedHeader{Type: Connect, Dup: false, Qos: 0, Retain: true, Remaining: 0},
 | |
| 		expect:   ErrMalformedFlags,
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "invalid publish qos bits 1 + 2 set",
 | |
| 		rawBytes: []byte{Publish<<4 | 1<<1 | 1<<2, 0x00},
 | |
| 		header:   FixedHeader{Type: Publish},
 | |
| 		expect:   ErrProtocolViolationQosOutOfRange,
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "invalid pubrel bits 3,2,1,0 should be 0,0,1,0",
 | |
| 		rawBytes: []byte{Pubrel<<4 | 1<<2 | 1<<0, 0x00},
 | |
| 		header:   FixedHeader{Type: Pubrel, Qos: 1},
 | |
| 		expect:   ErrMalformedFlags,
 | |
| 	},
 | |
| 	{
 | |
| 		desc:     "invalid subscribe bits 3,2,1,0 should be 0,0,1,0",
 | |
| 		rawBytes: []byte{Subscribe<<4 | 1<<2, 0x00},
 | |
| 		header:   FixedHeader{Type: Subscribe, Qos: 1},
 | |
| 		expect:   ErrMalformedFlags,
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func TestFixedHeaderEncode(t *testing.T) {
 | |
| 	for _, wanted := range fixedHeaderExpected {
 | |
| 		t.Run(wanted.desc, func(t *testing.T) {
 | |
| 			buf := new(bytes.Buffer)
 | |
| 			wanted.header.Encode(buf)
 | |
| 			if wanted.expect == nil {
 | |
| 				require.Equal(t, len(wanted.rawBytes), len(buf.Bytes()))
 | |
| 				require.EqualValues(t, wanted.rawBytes, buf.Bytes())
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestFixedHeaderDecode(t *testing.T) {
 | |
| 	for _, wanted := range fixedHeaderExpected {
 | |
| 		t.Run(wanted.desc, func(t *testing.T) {
 | |
| 			fh := new(FixedHeader)
 | |
| 			err := fh.Decode(wanted.rawBytes[0])
 | |
| 			if wanted.expect != nil {
 | |
| 				require.Equal(t, wanted.expect, err)
 | |
| 			} else {
 | |
| 				require.NoError(t, err)
 | |
| 				require.Equal(t, wanted.header.Type, fh.Type)
 | |
| 				require.Equal(t, wanted.header.Dup, fh.Dup)
 | |
| 				require.Equal(t, wanted.header.Qos, fh.Qos)
 | |
| 				require.Equal(t, wanted.header.Retain, fh.Retain)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
