Liu Song’s Projects


~/Projects/mqtt-go

git clone https://code.lsong.org/mqtt-go

Commit

Commit
a5aba5c9c79791d97e3d1a3407dcdc915f65b4f9
Author
Jonathan B <[email protected]>
Date
2019-12-07 16:13:27 +0000 +0000
Diffstat
  | 4 
  | 7 
  | 16 
 clients.go | 257 -----
 clients_test.go | 318 ------
 cmd/main.go | 7 
 go.mod | 14 
 go.sum | 23 
 internal/circ/buffer.go | 206 ++++
 internal/circ/buffer_test.go | 304 ++++++
 internal/circ/pool.go | 32 
 internal/circ/pool_test.go | 46 +
 internal/circ/reader.go | 96 ++
 internal/circ/reader_test.go | 125 ++
 internal/circ/writer.go | 104 ++
 internal/circ/writer_test.go | 155 +++
 internal/clients/clients.go | 461 ++++++++++
 internal/clients/clients_test.go | 969 +++++++++++++++++++++
 internal/listeners/tcp_test.go | 164 +++
 internal/packets/packets.go | 672 ++++++++++++++
 internal/packets/packets_test.go | 1082 +++++++++++++++++++++++
 listeners/config.go | 20 
  | 83 
  | 64 
  | 50 
 listeners/tcp_test.go | 129 --
  | 0 
 mqtt.go | 708 ++++----------
 mqtt_test.go | 1557 +++++++++++++--------------------
 packets/README.md | 75 -
  | 22 
  | 11 
 packets/connack.go | 50 -
 packets/connack_test.go | 101 --
 packets/connect.go | 189 ----
 packets/connect_test.go | 142 ---
 packets/disconnect.go | 26 
 packets/disconnect_test.go | 77 -
 packets/errors.go | 67 -
  | 33 
  | 8 
 packets/packets.go | 79 -
 packets/packets_test.go | 14 
 packets/parser.go | 204 ----
 packets/parser_test.go | 342 -------
 packets/pingreq.go | 26 
 packets/pingreq_test.go | 79 -
 packets/pingresp.go | 26 
 packets/pingresp_test.go | 78 -
 packets/puback.go | 35 
 packets/puback_test.go | 99 --
 packets/pubcomp.go | 35 
 packets/pubcomp_test.go | 99 --
 packets/publish.go | 93 --
 packets/publish_test.go | 163 ---
 packets/pubrec.go | 38 
 packets/pubrec_test.go | 102 --
 packets/pubrel.go | 35 
 packets/pubrel_test.go | 102 --
 packets/spec_test.go | 546 -----------
 packets/suback.go | 48 -
 packets/suback_test.go | 106 --
 packets/subscribe.go | 95 --
 packets/subscribe_test.go | 127 --
  | 174 +-
 packets/unsuback.go | 35 
 packets/unsuback_test.go | 102 --
 packets/unsubscribe.go | 86 -
 packets/unsubscribe_test.go | 127 --
 pools/bufio.go | 67 -
 pools/bufio_test.go | 115 --
 pools/bytesbuffer.go | 33 
 pools/bytesbuffer_test.go | 52 -
 topics/topics.go | 27 
  | 81 
  | 61 

Merge pull request #1 from mochi-co/iobuffers

Iobuffers


diff --git a/auth/auth.go b/auth/auth.go
deleted file mode 100644
index f7dba898595bf534bd17c82b8ffa269eb8aced91..0000000000000000000000000000000000000000
--- a/auth/auth.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package auth
-
-// Controller is an interface for authentication controllers.
-type Controller interface {
-
-	// Authenticate authenticates a user on CONNECT and returns true if a user is
-	// allowed to join the server.
-	Authenticate(user string, password string) bool
-
-	// ACL returns true if a user has read or write access to a given topic.
-	ACL(user string, topic string, write bool) bool
-}




diff --git a/auth/defaults.go b/auth/defaults.go
deleted file mode 100644
index f92e11aaab9997a91f9e854052caa5220f66cdea..0000000000000000000000000000000000000000
--- a/auth/defaults.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package auth
-
-// Allow is an auth controller which allows access to all connections and topics.
-type Allow struct{}
-
-// Auth returns true if a username and password are acceptable. Allow always
-// returns true.
-func (a *Allow) Authenticate(user, password string) bool {
-	return true
-}
-
-// ACL returns true if a user has access permissions to read or write on a topic.
-// Allow always returns true.
-func (a *Allow) ACL(user, topic string, write bool) bool {
-	return true
-}
-
-// Disallow is an auth controller which disallows access to all connections and topics.
-type Disallow struct{}
-
-// Auth returns true if a username and password are acceptable. Disallow always
-// returns false.
-func (d *Disallow) Authenticate(user, password string) bool {
-	return false
-}
-
-// ACL returns true if a user has access permissions to read or write on a topic.
-// Disallow always returns false.
-func (d *Disallow) ACL(user, topic string, write bool) bool {
-	return false
-}




diff --git a/auth/defaults_test.go b/auth/defaults_test.go
deleted file mode 100644
index 3e2b0e1cd60ada67603354081b37364ec58f0758..0000000000000000000000000000000000000000
--- a/auth/defaults_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package auth
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestAllowAuth(t *testing.T) {
-	ac := new(Allow)
-	require.Equal(t, true, ac.Authenticate("user", "pass"))
-}
-
-func BenchmarkAllowAuth(b *testing.B) {
-	ac := new(Allow)
-	for n := 0; n < b.N; n++ {
-		ac.Authenticate("user", "pass")
-	}
-}
-
-func TestAllowACL(t *testing.T) {
-	ac := new(Allow)
-	require.Equal(t, true, ac.ACL("user", "topic", true))
-}
-
-func BenchmarkAllowACL(b *testing.B) {
-	ac := new(Allow)
-	for n := 0; n < b.N; n++ {
-		ac.ACL("user", "pass", true)
-	}
-}
-
-func TestDisallowAuth(t *testing.T) {
-	ac := new(Disallow)
-	require.Equal(t, false, ac.Authenticate("user", "pass"))
-}
-
-func BenchmarkDisallowAuth(b *testing.B) {
-	ac := new(Disallow)
-	for n := 0; n < b.N; n++ {
-		ac.Authenticate("user", "pass")
-	}
-}
-
-func TestDisallowACL(t *testing.T) {
-	ac := new(Disallow)
-	require.Equal(t, false, ac.ACL("user", "topic", true))
-}
-
-func BenchmarkDisallowACL(b *testing.B) {
-	ac := new(Disallow)
-	for n := 0; n < b.N; n++ {
-		ac.ACL("user", "pass", true)
-	}
-}




diff --git a/clients.go b/clients.go
deleted file mode 100644
index 695c500a256b6c56722dc7010dd09130b921d5b1..0000000000000000000000000000000000000000
--- a/clients.go
+++ /dev/null
@@ -1,257 +0,0 @@
-package mqtt
-
-import (
-	"sync"
-	"sync/atomic"
-
-	"github.com/rs/xid"
-
-	"github.com/mochi-co/mqtt/auth"
-	"github.com/mochi-co/mqtt/packets"
-)
-
-var (
-	// defaultClientKeepalive is the default keepalive time in seconds.
-	defaultClientKeepalive uint16 = 60
-)
-
-// clients contains a map of the clients known by the broker.
-type clients struct {
-	sync.RWMutex
-
-	// internal is a map of the clients known by the broker, keyed on client id.
-	internal map[string]*client
-}
-
-// newClients returns an instance of clients.
-func newClients() clients {
-	return clients{
-		internal: make(map[string]*client),
-	}
-}
-
-// Add adds a new client to the clients map, keyed on client id.
-func (cl *clients) add(val *client) {
-	cl.Lock()
-	cl.internal[val.id] = val
-	cl.Unlock()
-}
-
-// Get returns the value of a client if it exists.
-func (cl *clients) get(id string) (*client, bool) {
-	cl.RLock()
-	val, ok := cl.internal[id]
-	cl.RUnlock()
-	return val, ok
-}
-
-// Len returns the length of the clients map.
-func (cl *clients) len() int {
-	cl.RLock()
-	val := len(cl.internal)
-	cl.RUnlock()
-	return val
-}
-
-// Delete removes a client from the internal map.
-func (cl *clients) delete(id string) {
-	cl.Lock()
-	delete(cl.internal, id)
-	cl.Unlock()
-}
-
-// getByListener returns clients matching a listener id.
-func (cl *clients) getByListener(id string) []*client {
-	clients := make([]*client, 0, cl.len())
-	cl.RLock()
-	for _, v := range cl.internal {
-		if v.listener == id {
-			clients = append(clients, v)
-		}
-	}
-	cl.RUnlock()
-	return clients
-}
-
-// Client contains information about a client known by the broker.
-type client struct {
-	sync.RWMutex
-
-	// p is a packets parser which reads incoming packets.
-	p *packets.Parser
-
-	// ac is a pointer to an auth controller inherited from the listener.
-	ac auth.Controller
-
-	// end is a channel that indicates the client should be halted.
-	end chan struct{}
-
-	// done can be called to ensure the close methods are only called once.
-	done *sync.Once
-
-	// id is the client id.
-	id string
-
-	// listener is the id of the listener the client is connected to.
-	listener string
-
-	// user is the username the client authenticated with.
-	user string
-
-	// keepalive is the number of seconds the connection can stay open without
-	// receiving a message from the client.
-	keepalive uint16
-
-	// cleanSession indicates if the client expects a cleansession.
-	cleanSession bool
-
-	// packetID is the current highest packetID.
-	packetID uint32
-
-	// lwt contains the last will and testament for the client.
-	lwt lwt
-
-	// inFlight is a map of messages which are in-flight,awaiting completiong of
-	// a QoS pattern.
-	inFlight inFlight
-
-	// subscriptions is a map of the subscription filters a client maintains.
-	subscriptions map[string]byte
-
-	// wasClosed indicates that the connection was closed deliberately.
-	wasClosed bool
-}
-
-// newClient creates a new instance of client.
-func newClient(p *packets.Parser, pk *packets.ConnectPacket, ac auth.Controller) *client {
-	cl := &client{
-		p:    p,
-		ac:   ac,
-		end:  make(chan struct{}),
-		done: new(sync.Once),
-
-		id:           pk.ClientIdentifier,
-		user:         pk.Username,
-		keepalive:    pk.Keepalive,
-		cleanSession: pk.CleanSession,
-		inFlight: inFlight{
-			internal: make(map[uint16]*inFlightMessage),
-		},
-		subscriptions: make(map[string]byte),
-	}
-
-	// If no client id was provided, generate a new one.
-	if cl.id == "" {
-		cl.id = xid.New().String()
-	}
-
-	// if no deadline value was provided, set it to the default seconds.
-	if cl.keepalive == 0 {
-		cl.keepalive = defaultClientKeepalive
-	}
-
-	// If a last will and testament has been provided, record it.
-	if pk.WillFlag {
-		cl.lwt = lwt{
-			topic:   pk.WillTopic,
-			message: pk.WillMessage,
-			qos:     pk.WillQos,
-			retain:  pk.WillRetain,
-		}
-	}
-
-	return cl
-}
-
-// nextPacketID returns the next packet id for a client, looping back to 0
-// if the maximum ID has been reached.
-func (cl *client) nextPacketID() uint32 {
-	i := atomic.LoadUint32(&cl.packetID)
-	if i == uint32(65535) || i == uint32(0) {
-		atomic.StoreUint32(&cl.packetID, 1)
-		return 1
-	}
-
-	return atomic.AddUint32(&cl.packetID, 1)
-}
-
-// noteSubscription makes a note of a subscription for the client.
-func (c *client) noteSubscription(filter string, qos byte) {
-	c.Lock()
-	c.subscriptions[filter] = qos
-	c.Unlock()
-}
-
-// forgetSubscription forgests a subscription note for the client.
-func (c *client) forgetSubscription(filter string) {
-	c.Lock()
-	delete(c.subscriptions, filter)
-	c.Unlock()
-}
-
-// close attempts to gracefully close a client connection.
-func (cl *client) close() {
-	cl.done.Do(func() {
-		close(cl.end) // Signal to stop listening for packets.
-
-		// Close the network connection.
-		cl.p.Conn.Close() // Error is irrelevant so can be ommitted here.
-		cl.p.Conn = nil
-	})
-}
-
-// lwt contains the last will and testament details for a client connection.
-type lwt struct {
-
-	// topic is the topic the will message shall be sent to.
-	topic string
-
-	// message is the message that shall be sent when the client disconnects.
-	message []byte
-
-	// qos is the quality of service byte desired for the will message.
-	qos byte
-
-	// retain indicates whether the will message should be retained.
-	retain bool
-}
-
-// inFlightMessage contains data about a packet which is currently in-flight.
-type inFlightMessage struct {
-
-	// packet is the packet currently in-flight.
-	packet packets.Packet
-
-	// sent is the last time the message was sent (for retries) in unixtime.
-	sent int64
-}
-
-// inFlight is a map of inFlightMessage keyed on packet id.
-type inFlight struct {
-	sync.RWMutex
-
-	// internal contains the inflight messages.
-	internal map[uint16]*inFlightMessage
-}
-
-// set stores the packet of an in-flight message, keyed on message id.
-func (i *inFlight) set(key uint16, in *inFlightMessage) {
-	i.Lock()
-	i.internal[key] = in
-	i.Unlock()
-}
-
-// get returns the value of an in-flight message if it exists.
-func (i *inFlight) get(key uint16) (*inFlightMessage, bool) {
-	i.RLock()
-	val, ok := i.internal[key]
-	i.RUnlock()
-	return val, ok
-}
-
-// delete removes an in-flight message from the map.
-func (i *inFlight) delete(key uint16) {
-	i.Lock()
-	delete(i.internal, key)
-	i.Unlock()
-}




diff --git a/clients_test.go b/clients_test.go
deleted file mode 100644
index 110d91781e82b6ae498b6eb40772477ce372d83f..0000000000000000000000000000000000000000
--- a/clients_test.go
+++ /dev/null
@@ -1,318 +0,0 @@
-package mqtt
-
-import (
-	"log"
-	"net"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-
-	"github.com/mochi-co/mqtt/auth"
-	"github.com/mochi-co/mqtt/packets"
-)
-
-func TestNewClients(t *testing.T) {
-	cl := newClients()
-	require.NotNil(t, cl.internal)
-}
-
-func BenchmarkNewClients(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		newClients()
-	}
-}
-
-func TestClientsAdd(t *testing.T) {
-	cl := newClients()
-	cl.add(&client{id: "t1"})
-	require.Contains(t, cl.internal, "t1")
-}
-
-func BenchmarkClientsAdd(b *testing.B) {
-	cl := newClients()
-	client := &client{id: "t1"}
-	for n := 0; n < b.N; n++ {
-		cl.add(client)
-	}
-}
-
-func TestClientsGet(t *testing.T) {
-	cl := newClients()
-	cl.add(&client{id: "t1"})
-	cl.add(&client{id: "t2"})
-	require.Contains(t, cl.internal, "t1")
-	require.Contains(t, cl.internal, "t2")
-
-	client, ok := cl.get("t1")
-	require.Equal(t, true, ok)
-	require.Equal(t, "t1", client.id)
-}
-
-func BenchmarkClientsGet(b *testing.B) {
-	cl := newClients()
-	cl.add(&client{id: "t1"})
-	for n := 0; n < b.N; n++ {
-		cl.get("t1")
-	}
-}
-
-func TestClientsLen(t *testing.T) {
-	cl := newClients()
-	cl.add(&client{id: "t1"})
-	cl.add(&client{id: "t2"})
-	require.Contains(t, cl.internal, "t1")
-	require.Contains(t, cl.internal, "t2")
-	require.Equal(t, 2, cl.len())
-}
-
-func BenchmarkClientsLen(b *testing.B) {
-	cl := newClients()
-	cl.add(&client{id: "t1"})
-	for n := 0; n < b.N; n++ {
-		cl.len()
-	}
-}
-
-func TestClientsDelete(t *testing.T) {
-	cl := newClients()
-	cl.add(&client{id: "t1"})
-	require.Contains(t, cl.internal, "t1")
-
-	cl.delete("t1")
-	_, ok := cl.get("t1")
-	require.Equal(t, false, ok)
-	require.Nil(t, cl.internal["t1"])
-}
-
-func BenchmarkClientsDelete(b *testing.B) {
-	cl := newClients()
-	cl.add(&client{id: "t1"})
-	for n := 0; n < b.N; n++ {
-		cl.delete("t1")
-	}
-}
-
-func TestClientsGetByListener(t *testing.T) {
-	cl := newClients()
-	cl.add(&client{id: "t1", listener: "tcp1"})
-	cl.add(&client{id: "t2", listener: "ws1"})
-	require.Contains(t, cl.internal, "t1")
-	require.Contains(t, cl.internal, "t2")
-
-	clients := cl.getByListener("tcp1")
-	log.Println(clients)
-	require.NotEmpty(t, clients)
-	require.Equal(t, 1, len(clients))
-	require.Equal(t, "tcp1", clients[0].listener)
-}
-
-func BenchmarkClientsGetByListener(b *testing.B) {
-	cl := newClients()
-	cl.add(&client{id: "t1", listener: "tcp1"})
-	cl.add(&client{id: "t2", listener: "ws1"})
-	for n := 0; n < b.N; n++ {
-		cl.getByListener("tcp1")
-	}
-}
-
-func TestNewClient(t *testing.T) {
-	r, _ := net.Pipe()
-	p := packets.NewParser(r, newBufioReader(r), newBufioWriter(r))
-	r.Close()
-	pk := &packets.ConnectPacket{
-		FixedHeader: packets.FixedHeader{
-			Type:      packets.Connect,
-			Remaining: 16,
-		},
-		ProtocolName:     "MQTT",
-		ProtocolVersion:  4,
-		CleanSession:     true,
-		Keepalive:        60,
-		ClientIdentifier: "zen3",
-	}
-
-	cl := newClient(p, pk, new(auth.Allow))
-	require.NotNil(t, cl)
-	require.NotNil(t, cl.inFlight.internal)
-	require.NotNil(t, cl.subscriptions)
-	require.Equal(t, pk.Keepalive, cl.keepalive)
-	require.Equal(t, pk.CleanSession, cl.cleanSession)
-	require.Equal(t, pk.ClientIdentifier, cl.id)
-
-	// Autogenerate id.
-	pk = new(packets.ConnectPacket)
-	cl = newClient(p, pk, new(auth.Allow))
-	require.NotNil(t, cl)
-	require.NotEmpty(t, cl.id)
-
-	// Autoset keepalive
-	pk = new(packets.ConnectPacket)
-	cl = newClient(p, pk, new(auth.Allow))
-	require.NotNil(t, cl)
-	require.Equal(t, defaultClientKeepalive, cl.keepalive)
-}
-
-func TestNewClientLWT(t *testing.T) {
-	r, _ := net.Pipe()
-	p := packets.NewParser(r, newBufioReader(r), newBufioWriter(r))
-	r.Close()
-	pk := &packets.ConnectPacket{
-		FixedHeader: packets.FixedHeader{
-			Type:      packets.Connect,
-			Remaining: 29,
-		},
-		ProtocolName:     "MQTT",
-		ProtocolVersion:  4,
-		CleanSession:     true,
-		Keepalive:        60,
-		ClientIdentifier: "zen",
-		WillFlag:         true,
-		WillTopic:        "lwt",
-		WillMessage:      []byte("lol gg"),
-		WillQos:          1,
-		WillRetain:       false,
-	}
-
-	cl := newClient(p, pk, new(auth.Allow))
-	require.Equal(t, pk.WillTopic, cl.lwt.topic)
-	require.Equal(t, pk.WillMessage, cl.lwt.message)
-	require.Equal(t, pk.WillQos, cl.lwt.qos)
-	require.Equal(t, pk.WillRetain, cl.lwt.retain)
-}
-
-func BenchmarkNewClient(b *testing.B) {
-	r, _ := net.Pipe()
-	p := packets.NewParser(r, newBufioReader(r), newBufioWriter(r))
-	r.Close()
-	pk := new(packets.ConnectPacket)
-
-	for n := 0; n < b.N; n++ {
-		newClient(p, pk, new(auth.Allow))
-	}
-}
-
-func TestNextPacketID(t *testing.T) {
-	_, _, _, cl := setupClient("zen")
-
-	require.Equal(t, uint32(1), cl.nextPacketID())
-	require.Equal(t, uint32(2), cl.nextPacketID())
-
-	cl.packetID = uint32(65534)
-	require.Equal(t, uint32(65535), cl.nextPacketID())
-	require.Equal(t, uint32(1), cl.nextPacketID())
-}
-
-func BenchmarkNextPacketID(b *testing.B) {
-	_, _, _, cl := setupClient("zen")
-
-	for n := 0; n < b.N; n++ {
-		cl.nextPacketID()
-	}
-}
-
-func TestClientNoteSubscription(t *testing.T) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	require.NotNil(t, client)
-	client.noteSubscription("a/b/c", 0)
-	require.Contains(t, client.subscriptions, "a/b/c")
-	require.Equal(t, byte(0), client.subscriptions["a/b/c"])
-}
-
-func BenchmarkClientNoteSubscription(b *testing.B) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	for n := 0; n < b.N; n++ {
-		client.noteSubscription("a/b/c", 0)
-	}
-}
-
-func TestClientForgetSubscription(t *testing.T) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	require.NotNil(t, client)
-	client.subscriptions = map[string]byte{
-		"a/b/c/": 1,
-	}
-	client.forgetSubscription("a/b/c/")
-	require.Empty(t, client.subscriptions["a/b/c"])
-}
-
-func BenchmarkClientForgetSubscription(b *testing.B) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	for n := 0; n < b.N; n++ {
-		client.noteSubscription("a/b/c", 0)
-		client.forgetSubscription("a/b/c/")
-	}
-}
-
-func TestClientClose(t *testing.T) {
-	r, w := net.Pipe()
-	p := packets.NewParser(r, newBufioReader(r), newBufioWriter(w))
-	pk := &packets.ConnectPacket{
-		ClientIdentifier: "zen3",
-	}
-
-	client := newClient(p, pk, new(auth.Allow))
-	require.NotNil(t, client)
-
-	client.close()
-
-	var ok bool
-	select {
-	case _, ok = <-client.end:
-	}
-	require.Equal(t, false, ok)
-	require.Nil(t, client.p.Conn)
-	r.Close()
-	w.Close()
-}
-
-func TestInFlightSet(t *testing.T) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	client.inFlight.set(1, &inFlightMessage{packet: new(packets.PublishPacket), sent: 0})
-	require.NotNil(t, client.inFlight.internal[1])
-	require.NotEqual(t, 0, client.inFlight.internal[1].sent)
-}
-
-func BenchmarkInFlightSet(b *testing.B) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	in := &inFlightMessage{packet: new(packets.PublishPacket), sent: 0}
-	for n := 0; n < b.N; n++ {
-		client.inFlight.set(1, in)
-	}
-}
-
-func TestInFlightGet(t *testing.T) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	client.inFlight.set(2, &inFlightMessage{packet: new(packets.PublishPacket), sent: 0})
-
-	msg, ok := client.inFlight.get(2)
-	require.Equal(t, true, ok)
-	require.NotEqual(t, 0, msg.sent)
-}
-
-func BenchmarkInFlightGet(b *testing.B) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	client.inFlight.set(2, &inFlightMessage{packet: new(packets.PublishPacket), sent: 0})
-	for n := 0; n < b.N; n++ {
-		client.inFlight.get(2)
-	}
-}
-
-func TestInFlightDelete(t *testing.T) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	client.inFlight.set(3, &inFlightMessage{packet: new(packets.PublishPacket), sent: 0})
-	require.NotNil(t, client.inFlight.internal[3])
-
-	client.inFlight.delete(3)
-	require.Nil(t, client.inFlight.internal[3])
-
-	_, ok := client.inFlight.get(3)
-	require.Equal(t, false, ok)
-}
-
-func BenchmarInFlightDelete(b *testing.B) {
-	client := newClient(nil, new(packets.ConnectPacket), new(auth.Allow))
-	for n := 0; n < b.N; n++ {
-		client.inFlight.set(4, &inFlightMessage{packet: new(packets.PublishPacket), sent: 0})
-		client.inFlight.delete(4)
-	}
-}




diff --git a/cmd/main.go b/cmd/main.go
index 826ea681a9d6302dfc0e46de5d20f473644c92f1..bf50966f5b35f9c919cff439a7066e2b20805b22 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -3,6 +3,7 @@
 import (
 	"fmt"
 	"log"
+	//	"net/http"
 	"os"
 	"os/signal"
 	"syscall"
@@ -10,11 +11,15 @@
 	"github.com/logrusorgru/aurora"
 
 	"github.com/mochi-co/mqtt"
-package main
+	"github.com/mochi-co/mqtt/internal/listeners"
+	"fmt"
 package main
 )
 
 func main() {
+	go func() {
+		//	log.Println(http.ListenAndServe("localhost:6060", nil))
+	}()
 
 	sigs := make(chan os.Signal, 1)
 	done := make(chan bool, 1)




diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..2d0ba40999460a7655ecf3b2f1367fa9f6c6a9ea
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,14 @@
+module github.com/mochi-co/mqtt
+
+go 1.13
+
+require (
+	github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
+	github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23
+	github.com/mochi-co/debug v0.0.0-20191124131204-24fd1e001164
+	github.com/pkg/profile v1.4.0
+	github.com/rs/xid v1.2.1
+	github.com/stretchr/testify v1.4.0
+)
+
+replace github.com/mochi-co/debug => /Users/mochimochi/Development/Go/src/github.com/mochi-co/debug




diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..550f6c0a424729981316d830ca81e37bc15d4609
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,23 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
+github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
+github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23 h1:Wp7NjqGKGN9te9N/rvXYRhlVcrulGdxnz8zadXWs7fc=
+github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/mochi-co/debug v0.0.0-20191124114744-82bf8b6739b8 h1:BIY2BMCLHm6hE/SUNspw31SaSsJZZbIpQPdf+rn5SsY=
+github.com/mochi-co/debug v0.0.0-20191124114744-82bf8b6739b8/go.mod h1:AqE7zHPhLOj61seX0vXvzpGiD9Q3Bx5LQPf/FleHKWc=
+github.com/mochi-co/debug v0.0.0-20191124131204-24fd1e001164 h1:XGYo79ZRE9pQE9B5iZCYw3VLaq88PfxcdvDf9crG+dQ=
+github.com/mochi-co/debug v0.0.0-20191124131204-24fd1e001164/go.mod h1:LfBrWXdsMaDKL0ZjcbnLjeYL48Nlo1nW4MltMDYqr44=
+github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI=
+github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
+github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=




diff --git a/internal/auth/auth.go b/internal/auth/auth.go
new file mode 100644
index 0000000000000000000000000000000000000000..3cbd2f4a7b7ece42bb7fe646d44df9dccd7e1ab5
--- /dev/null
+++ b/internal/auth/auth.go
@@ -0,0 +1,12 @@
+package auth
+
+// Controller is an interface for authentication controllers.
+type Controller interface {
+
+	// Authenticate authenticates a user on CONNECT and returns true if a user is
+	// allowed to join the server.
+	Authenticate(user, password []byte) bool
+
+	// ACL returns true if a user has read or write access to a given topic.
+	ACL(user []byte, topic string, write bool) bool
+}




diff --git a/internal/auth/defaults.go b/internal/auth/defaults.go
new file mode 100644
index 0000000000000000000000000000000000000000..cf503f53ec33153b3aea2de018bf7d8c1bc4a42e
--- /dev/null
+++ b/internal/auth/defaults.go
@@ -0,0 +1,31 @@
+package auth
+
+// Allow is an auth controller which allows access to all connections and topics.
+type Allow struct{}
+
+// Auth returns true if a username and password are acceptable. Allow always
+// returns true.
+func (a *Allow) Authenticate(user, password []byte) bool {
+	return true
+}
+
+// ACL returns true if a user has access permissions to read or write on a topic.
+// Allow always returns true.
+func (a *Allow) ACL(user []byte, topic string, write bool) bool {
+	return true
+}
+
+// Disallow is an auth controller which disallows access to all connections and topics.
+type Disallow struct{}
+
+// Auth returns true if a username and password are acceptable. Disallow always
+// returns false.
+func (d *Disallow) Authenticate(user, password []byte) bool {
+	return false
+}
+
+// ACL returns true if a user has access permissions to read or write on a topic.
+// Disallow always returns false.
+func (d *Disallow) ACL(user []byte, topic string, write bool) bool {
+	return false
+}




diff --git a/internal/auth/defaults_test.go b/internal/auth/defaults_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..fa594a907083368288c9d301b87c74ed6b7f3e89
--- /dev/null
+++ b/internal/auth/defaults_test.go
@@ -0,0 +1,55 @@
+package auth
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestAllowAuth(t *testing.T) {
+	ac := new(Allow)
+	require.Equal(t, true, ac.Authenticate([]byte("user"), []byte("pass")))
+}
+
+func BenchmarkAllowAuth(b *testing.B) {
+	ac := new(Allow)
+	for n := 0; n < b.N; n++ {
+		ac.Authenticate([]byte("user"), []byte("pass"))
+	}
+}
+
+func TestAllowACL(t *testing.T) {
+	ac := new(Allow)
+	require.Equal(t, true, ac.ACL([]byte("user"), "topic", true))
+}
+
+func BenchmarkAllowACL(b *testing.B) {
+	ac := new(Allow)
+	for n := 0; n < b.N; n++ {
+		ac.ACL([]byte("user"), "pass", true)
+	}
+}
+
+func TestDisallowAuth(t *testing.T) {
+	ac := new(Disallow)
+	require.Equal(t, false, ac.Authenticate([]byte("user"), []byte("pass")))
+}
+
+func BenchmarkDisallowAuth(b *testing.B) {
+	ac := new(Disallow)
+	for n := 0; n < b.N; n++ {
+		ac.Authenticate([]byte("user"), []byte("pass"))
+	}
+}
+
+func TestDisallowACL(t *testing.T) {
+	ac := new(Disallow)
+	require.Equal(t, false, ac.ACL([]byte("user"), "topic", true))
+}
+
+func BenchmarkDisallowACL(b *testing.B) {
+	ac := new(Disallow)
+	for n := 0; n < b.N; n++ {
+		ac.ACL([]byte("user"), "pass", true)
+	}
+}




diff --git a/internal/circ/buffer.go b/internal/circ/buffer.go
new file mode 100644
index 0000000000000000000000000000000000000000..84a36272250a785376a25047ab34910269ccc636
--- /dev/null
+++ b/internal/circ/buffer.go
@@ -0,0 +1,206 @@
+package circ
+
+import (
+	"errors"
+	"io"
+	"sync"
+	"sync/atomic"
+)
+
+var (
+	DefaultBufferSize int = 1024 * 256 // the default size of the buffer in bytes.
+	DefaultBlockSize  int = 1024 * 8   // the default size per R/W block in bytes.
+
+	ErrOutOfRange        = errors.New("Indexes out of range")
+	ErrInsufficientBytes = errors.New("Insufficient bytes to return")
+)
+
+// buffer contains core values and methods to be included in a reader or writer.
+type Buffer struct {
+	Mu    sync.RWMutex // the buffer needs it's own mutex to work properly.
+	ID    string       // the identifier of the buffer. This is used in debug output.
+	size  int          // the size of the buffer.
+	mask  int          // a bitmask of the buffer size (size-1).
+	block int          // the size of the R/W block.
+	buf   []byte       // the bytes buffer.
+	tmp   []byte       // a temporary buffer.
+	head  int64        // the current position in the sequence - a forever increasing index.
+	tail  int64        // the committed position in the sequence - a forever increasing index.
+	rcond *sync.Cond   // the sync condition for the buffer reader.
+	wcond *sync.Cond   // the sync condition for the buffer writer.
+	done  int64        // indicates that the buffer is closed.
+	State int64        // indicates whether the buffer is reading from (1) or writing to (2).
+}
+
+// NewBuffer returns a new instance of buffer. You should call NewReader or
+// NewWriter instead of this function.
+func NewBuffer(size, block int) Buffer {
+	if size == 0 {
+		size = DefaultBufferSize
+	}
+
+	if block == 0 {
+		block = DefaultBlockSize
+	}
+
+	if size < 2*block {
+		size = 2 * block
+	}
+
+	return Buffer{
+		size:  size,
+		mask:  size - 1,
+		block: block,
+		buf:   make([]byte, size),
+		rcond: sync.NewCond(new(sync.Mutex)),
+		wcond: sync.NewCond(new(sync.Mutex)),
+	}
+}
+
+// NewBufferFromSlice returns a new instance of buffer using a
+// pre-existing byte slice.
+func NewBufferFromSlice(block int, buf []byte) Buffer {
+	l := len(buf)
+
+	if block == 0 {
+		block = DefaultBlockSize
+	}
+
+	b := Buffer{
+		size:  l,
+		mask:  l - 1,
+		block: block,
+		buf:   buf,
+		rcond: sync.NewCond(new(sync.Mutex)),
+		wcond: sync.NewCond(new(sync.Mutex)),
+	}
+
+	return b
+}
+
+// Get will return the tail and head positions of the buffer.
+// This method is for use with testing.
+func (b *Buffer) GetPos() (int64, int64) {
+	return atomic.LoadInt64(&b.tail), atomic.LoadInt64(&b.head)
+}
+
+// SetPos sets the head and tail of the buffer.
+func (b *Buffer) SetPos(tail, head int64) {
+	atomic.StoreInt64(&b.tail, tail)
+	atomic.StoreInt64(&b.head, head)
+}
+
+// Get returns the internal buffer.
+func (b *Buffer) Get() []byte {
+	b.Mu.Lock()
+	defer b.Mu.Unlock()
+	return b.buf
+}
+
+// Set writes bytes to a range of indexes in the byte buffer.
+func (b *Buffer) Set(p []byte, start, end int) error {
+	b.Mu.Lock()
+	defer b.Mu.Unlock()
+
+	if end > b.size || start > b.size {
+		return ErrOutOfRange
+	}
+
+	o := 0
+	for i := start; i < end; i++ {
+		b.buf[i] = p[o]
+		o++
+	}
+
+	return nil
+}
+
+// Index returns the buffer-relative index of an integer.
+func (b *Buffer) Index(i int64) int {
+	return b.mask & int(i)
+}
+
+// awaitEmpty will block until there is at least n bytes between
+// the head and the tail (looking forward).
+func (b *Buffer) awaitEmpty(n int) error {
+	// If the head has wrapped behind the tail, and next will overrun tail,
+	// then wait until tail has moved.
+	b.rcond.L.Lock()
+	for !b.checkEmpty(n) {
+		if atomic.LoadInt64(&b.done) == 1 {
+			b.rcond.L.Unlock()
+			return io.EOF
+		}
+		b.rcond.Wait()
+	}
+	b.rcond.L.Unlock()
+
+	return nil
+}
+
+// awaitFilled will block until there are at least n bytes between the
+// tail and the head (looking forward).
+func (b *Buffer) awaitFilled(n int) error {
+	// Because awaitCapacity prevents the head from overrunning the t
+	// able on write, we can simply ensure there is enough space
+	// the forever-incrementing tail and head integers.
+	b.wcond.L.Lock()
+	for !b.checkFilled(n) {
+		if atomic.LoadInt64(&b.done) == 1 {
+			b.wcond.L.Unlock()
+			return io.EOF
+		}
+
+		b.wcond.Wait()
+	}
+	b.wcond.L.Unlock()
+
+	return nil
+}
+
+// checkEmpty returns true if there are at least n bytes between the head and
+// the tail.
+func (b *Buffer) checkEmpty(n int) bool {
+	head := atomic.LoadInt64(&b.head)
+	next := head + int64(n)
+	tail := atomic.LoadInt64(&b.tail)
+	if next-tail > int64(b.size) {
+		return false
+	}
+
+	return true
+}
+
+// checkFilled returns true if there are at least n bytes between the tail and
+// the head.
+func (b *Buffer) checkFilled(n int) bool {
+	if atomic.LoadInt64(&b.tail)+int64(n) <= atomic.LoadInt64(&b.head) {
+		return true
+	}
+
+	return false
+}
+
+// CommitTail moves the tail position of the buffer n bytes.
+func (b *Buffer) CommitTail(n int) {
+	atomic.AddInt64(&b.tail, int64(n))
+	b.rcond.L.Lock()
+	b.rcond.Broadcast()
+	b.rcond.L.Unlock()
+}
+
+// CapDelta returns the difference between the head and tail.
+func (b *Buffer) CapDelta() int {
+	return int(atomic.LoadInt64(&b.head) - atomic.LoadInt64(&b.tail))
+}
+
+// Stop signals the buffer to stop processing.
+func (b *Buffer) Stop() {
+	atomic.StoreInt64(&b.done, 1)
+	b.rcond.L.Lock()
+	b.rcond.Broadcast()
+	b.rcond.L.Unlock()
+	b.wcond.L.Lock()
+	b.wcond.Broadcast()
+	b.wcond.L.Unlock()
+}




diff --git a/internal/circ/buffer_test.go b/internal/circ/buffer_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b36a7fb405a5cfe9277540708d8ffb0ac1d67c29
--- /dev/null
+++ b/internal/circ/buffer_test.go
@@ -0,0 +1,304 @@
+package circ
+
+import (
+	//"fmt"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestNewBuffer(t *testing.T) {
+	var size int = 16
+	var block int = 4
+	buf := NewBuffer(size, block)
+
+	require.NotNil(t, buf.buf)
+	require.NotNil(t, buf.rcond)
+	require.NotNil(t, buf.wcond)
+	require.Equal(t, size, len(buf.buf))
+	require.Equal(t, size, buf.size)
+	require.Equal(t, block, buf.block)
+}
+
+func TestNewBuffer0Size(t *testing.T) {
+	buf := NewBuffer(0, 0)
+	require.NotNil(t, buf.buf)
+	require.Equal(t, DefaultBufferSize, buf.size)
+	require.Equal(t, DefaultBlockSize, buf.block)
+}
+
+func TestNewBufferUndersize(t *testing.T) {
+	buf := NewBuffer(DefaultBlockSize+10, DefaultBlockSize)
+	require.NotNil(t, buf.buf)
+	require.Equal(t, DefaultBlockSize*2, buf.size)
+	require.Equal(t, DefaultBlockSize, buf.block)
+}
+
+func TestNewBufferFromSlice(t *testing.T) {
+	b := NewBytesPool(256)
+	buf := NewBufferFromSlice(DefaultBlockSize, b.Get())
+	require.NotNil(t, buf.buf)
+	require.Equal(t, 256, cap(buf.buf))
+}
+
+func TestNewBufferFromSlice0Size(t *testing.T) {
+	b := NewBytesPool(256)
+	buf := NewBufferFromSlice(0, b.Get())
+	require.NotNil(t, buf.buf)
+	require.Equal(t, 256, cap(buf.buf))
+}
+
+func TestGetPos(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	tail, head := buf.GetPos()
+	require.Equal(t, int64(0), tail)
+	require.Equal(t, int64(0), head)
+
+	buf.tail = 3
+	buf.head = 11
+
+	tail, head = buf.GetPos()
+	require.Equal(t, int64(3), tail)
+	require.Equal(t, int64(11), head)
+}
+
+func TestGet(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	require.Equal(t, make([]byte, 16), buf.Get())
+
+	buf.buf[0] = 1
+	buf.buf[15] = 1
+	require.Equal(t, []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, buf.Get())
+}
+
+func TestSetPos(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	require.Equal(t, int64(0), buf.tail)
+	require.Equal(t, int64(0), buf.head)
+
+	buf.SetPos(4, 8)
+	require.Equal(t, int64(4), buf.tail)
+	require.Equal(t, int64(8), buf.head)
+}
+
+func TestSet(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	err := buf.Set([]byte{1, 1, 1, 1}, 17, 19)
+	require.Error(t, err)
+
+	err = buf.Set([]byte{1, 1, 1, 1}, 4, 8)
+	require.NoError(t, err)
+	require.Equal(t, []byte{0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, buf.buf)
+}
+
+func TestIndex(t *testing.T) {
+	buf := NewBuffer(1024, 4)
+	require.Equal(t, 512, buf.Index(512))
+	require.Equal(t, 0, buf.Index(1024))
+	require.Equal(t, 6, buf.Index(1030))
+	require.Equal(t, 6, buf.Index(61446))
+}
+
+func TestAwaitFilled(t *testing.T) {
+	tests := []struct {
+		tail  int64
+		head  int64
+		n     int
+		await int
+		desc  string
+	}{
+		{tail: 0, head: 4, n: 4, await: 1, desc: "OK 0, 4"},
+		{tail: 8, head: 11, n: 4, await: 1, desc: "OK 8, 11"},
+		{tail: 102, head: 103, n: 4, await: 3, desc: "OK 102, 103"},
+	}
+
+	for i, tt := range tests {
+		//fmt.Println(i)
+		buf := NewBuffer(16, 4)
+		buf.SetPos(tt.tail, tt.head)
+		o := make(chan error)
+		go func() {
+			o <- buf.awaitFilled(4)
+		}()
+
+		time.Sleep(time.Millisecond)
+		atomic.AddInt64(&buf.head, int64(tt.await))
+		buf.wcond.L.Lock()
+		buf.wcond.Broadcast()
+		buf.wcond.L.Unlock()
+
+		require.NoError(t, <-o, "Unexpected Error [i:%d] %s", i, tt.desc)
+	}
+}
+
+func TestAwaitFilledEnded(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	o := make(chan error)
+	go func() {
+		o <- buf.awaitFilled(4)
+	}()
+	time.Sleep(time.Millisecond)
+	atomic.StoreInt64(&buf.done, 1)
+	buf.wcond.L.Lock()
+	buf.wcond.Broadcast()
+	buf.wcond.L.Unlock()
+
+	require.Error(t, <-o)
+}
+
+func TestAwaitEmptyOK(t *testing.T) {
+	tests := []struct {
+		tail  int64
+		head  int64
+		await int
+		desc  string
+	}{
+		{tail: 0, head: 0, await: 0, desc: "OK 0, 0"},
+		{tail: 0, head: 5, await: 0, desc: "OK 0, 5"},
+		{tail: 0, head: 14, await: 3, desc: "OK wrap 0, 14 "},
+		{tail: 22, head: 35, await: 2, desc: "OK wrap 0, 14 "},
+		{tail: 15, head: 17, await: 7, desc: "OK 15,2"},
+		{tail: 0, head: 10, await: 2, desc: "OK 0, 10"},
+		{tail: 1, head: 15, await: 4, desc: "OK 2, 14"},
+	}
+
+	for i, tt := range tests {
+		buf := NewBuffer(16, 4)
+		buf.SetPos(tt.tail, tt.head)
+		o := make(chan error)
+		go func() {
+			o <- buf.awaitEmpty(4)
+		}()
+
+		time.Sleep(time.Millisecond)
+		atomic.AddInt64(&buf.tail, int64(tt.await))
+		buf.rcond.L.Lock()
+		buf.rcond.Broadcast()
+		buf.rcond.L.Unlock()
+
+		require.NoError(t, <-o, "Unexpected Error [i:%d] %s", i, tt.desc)
+	}
+}
+
+func TestAwaitEmptyEnded(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	buf.SetPos(1, 15)
+	o := make(chan error)
+	go func() {
+		o <- buf.awaitEmpty(4)
+	}()
+	time.Sleep(time.Millisecond)
+	atomic.StoreInt64(&buf.done, 1)
+	buf.rcond.L.Lock()
+	buf.rcond.Broadcast()
+	buf.rcond.L.Unlock()
+
+	require.Error(t, <-o)
+}
+
+func TestCheckEmpty(t *testing.T) {
+	buf := NewBuffer(16, 4)
+
+	tests := []struct {
+		head int64
+		tail int64
+		want bool
+		desc string
+	}{
+		{tail: 0, head: 0, want: true, desc: "0, 0 true"},
+		{tail: 3, head: 4, want: true, desc: "4, 3 true"},
+		{tail: 15, head: 17, want: true, desc: "15, 17(1) true"},
+		{tail: 1, head: 30, want: false, desc: "1, 30(14) false"},
+		{tail: 15, head: 30, want: false, desc: "15, 30(14) false; head has caught up to tail"},
+	}
+	for i, tt := range tests {
+		buf.SetPos(tt.tail, tt.head)
+		require.Equal(t, tt.want, buf.checkEmpty(4), "Mismatched bool wanted [i:%d] %s", i, tt.desc)
+	}
+}
+
+func TestCheckFilled(t *testing.T) {
+	buf := NewBuffer(16, 4)
+
+	tests := []struct {
+		head int64
+		tail int64
+		want bool
+		desc string
+	}{
+		{tail: 0, head: 0, want: false, desc: "0, 0 false"},
+		{tail: 0, head: 4, want: true, desc: "0, 4 true"},
+		{tail: 14, head: 16, want: false, desc: "14,16 false"},
+		{tail: 14, head: 18, want: true, desc: "14,16 true"},
+	}
+
+	for i, tt := range tests {
+		buf.SetPos(tt.tail, tt.head)
+		require.Equal(t, tt.want, buf.checkFilled(4), "Mismatched bool wanted [i:%d] %s", i, tt.desc)
+	}
+
+}
+
+func TestCommitTail(t *testing.T) {
+	tests := []struct {
+		tail  int64
+		head  int64
+		n     int
+		next  int64
+		await int
+		desc  string
+	}{
+		{tail: 0, head: 5, n: 4, next: 4, await: 0, desc: "OK 0, 4"},
+		{tail: 0, head: 5, n: 6, next: 6, await: 1, desc: "OK 0, 5"},
+	}
+
+	for i, tt := range tests {
+		buf := NewBuffer(16, 4)
+		buf.SetPos(tt.tail, tt.head)
+		go func() {
+			buf.CommitTail(tt.n)
+		}()
+
+		time.Sleep(time.Millisecond)
+		for j := 0; j < tt.await; j++ {
+			atomic.AddInt64(&buf.head, 1)
+			buf.wcond.L.Lock()
+			buf.wcond.Broadcast()
+			buf.wcond.L.Unlock()
+		}
+		require.Equal(t, tt.next, buf.tail, "Next tail mismatch [i:%d] %s", i, tt.desc)
+	}
+}
+
+/*
+func TestCommitTailEnded(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	o := make(chan error)
+	go func() {
+		o <- buf.CommitTail(5)
+	}()
+	time.Sleep(time.Millisecond)
+	atomic.StoreInt64(&buf.done, 1)
+	buf.wcond.L.Lock()
+	buf.wcond.Broadcast()
+	buf.wcond.L.Unlock()
+
+	require.Error(t, <-o)
+}
+*/
+func TestCapDelta(t *testing.T) {
+	buf := NewBuffer(16, 4)
+
+	require.Equal(t, 0, buf.CapDelta())
+
+	buf.SetPos(10, 15)
+	require.Equal(t, 5, buf.CapDelta())
+}
+
+func TestStop(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	buf.Stop()
+	require.Equal(t, int64(1), buf.done)
+}




diff --git a/internal/circ/pool.go b/internal/circ/pool.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc3fa97bf56390fb2718a8976ed46ef86073f426
--- /dev/null
+++ b/internal/circ/pool.go
@@ -0,0 +1,32 @@
+package circ
+
+import (
+	"sync"
+)
+
+// BytesPool is a pool of []byte.
+type BytesPool struct {
+	pool sync.Pool
+}
+
+// NewBytesPool returns a sync.pool of []byte.
+func NewBytesPool(n int) BytesPool {
+	return BytesPool{
+		pool: sync.Pool{
+			New: func() interface{} {
+				return make([]byte, n)
+			},
+		},
+	}
+}
+
+// Get returns a pooled bytes.Buffer.
+func (b BytesPool) Get() []byte {
+	return b.pool.Get().([]byte)
+}
+
+// Put puts the byte slice back into the pool.
+func (b BytesPool) Put(x []byte) {
+	x = x[:0]
+	b.pool.Put(x)
+}




diff --git a/internal/circ/pool_test.go b/internal/circ/pool_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bbcc94d27f17251cf575f6fc53fc3e9e5d7a220a
--- /dev/null
+++ b/internal/circ/pool_test.go
@@ -0,0 +1,46 @@
+package circ
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestNewBytesPool(t *testing.T) {
+	bpool := NewBytesPool(256)
+	require.NotNil(t, bpool.pool)
+}
+
+func BenchmarkNewBytesPool(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		NewBytesPool(256)
+	}
+}
+
+func TestNewBytesPoolGet(t *testing.T) {
+	bpool := NewBytesPool(256)
+	buf := bpool.Get()
+
+	require.Equal(t, make([]byte, 256), buf)
+}
+
+func BenchmarkBytesPoolGet(b *testing.B) {
+	bpool := NewBytesPool(256)
+	for n := 0; n < b.N; n++ {
+		bpool.Get()
+	}
+}
+
+func TestNewBytesPoolPut(t *testing.T) {
+	bpool := NewBytesPool(256)
+	buf := bpool.Get()
+	bpool.Put(buf)
+}
+
+func BenchmarkBytesPoolPut(b *testing.B) {
+	bpool := NewBytesPool(256)
+	buf := bpool.Get()
+	for n := 0; n < b.N; n++ {
+		bpool.Put(buf)
+	}
+}




diff --git a/internal/circ/reader.go b/internal/circ/reader.go
new file mode 100644
index 0000000000000000000000000000000000000000..3b9b9693901ab5055edbecdbf2651c669b6960ed
--- /dev/null
+++ b/internal/circ/reader.go
@@ -0,0 +1,96 @@
+package circ
+
+import (
+	"io"
+	"sync/atomic"
+)
+
+// Reader is a circular buffer for reading data from an io.Reader.
+type Reader struct {
+	Buffer
+}
+
+// NewReader returns a new Circular Reader.
+func NewReader(size, block int) *Reader {
+	b := NewBuffer(size, block)
+	b.ID = "\treader"
+	return &Reader{
+		b,
+	}
+}
+
+// NewReaderFromSlice returns a new Circular Reader using a pre-exising
+// byte slice.
+func NewReaderFromSlice(block int, p []byte) *Reader {
+	b := NewBufferFromSlice(block, p)
+	b.ID = "\treader"
+	return &Reader{
+		b,
+	}
+}
+
+// ReadFrom reads bytes from an io.Reader and commits them to the buffer when
+// there is sufficient capacity to do so.
+func (b *Reader) ReadFrom(r io.Reader) (total int64, err error) {
+	atomic.StoreInt64(&b.State, 1)
+	defer atomic.StoreInt64(&b.State, 0)
+	for {
+		if atomic.LoadInt64(&b.done) == 1 {
+			return total, nil
+		}
+
+		// Wait until there's enough capacity in the buffer before
+		// trying to read more bytes from the io.Reader.
+		err := b.awaitEmpty(b.block)
+		if err != nil {
+			// b.done is the only error condition for awaitCapacity
+			// so loop around and return properly.
+			continue
+		}
+
+		// If the block will overrun the circle end, just fill up
+		// and collect the rest on the next pass.
+		start := b.Index(atomic.LoadInt64(&b.head))
+		end := start + b.block
+		if end > b.size {
+			end = b.size
+		}
+
+		// Read into the buffer between the start and end indexes only.
+		n, err := r.Read(b.buf[start:end])
+		total += int64(n) // incr total bytes read.
+		if err != nil {
+			return total, nil
+		}
+
+		// Move the head forward however many bytes were read.
+		atomic.AddInt64(&b.head, int64(n))
+
+		b.wcond.L.Lock()
+		b.wcond.Broadcast()
+		b.wcond.L.Unlock()
+	}
+}
+
+// Read reads n bytes from the buffer, and will block until at n bytes
+// exist in the buffer to read.
+func (b *Buffer) Read(n int) (p []byte, err error) {
+	err = b.awaitFilled(n)
+	if err != nil {
+		return
+	}
+
+	tail := atomic.LoadInt64(&b.tail)
+	next := tail + int64(n)
+
+	// If the read overruns the buffer, get everything until the end
+	// and then whatever is left from the start.
+	if b.Index(tail) > b.Index(next) {
+		b.tmp = b.buf[b.Index(tail):]
+		b.tmp = append(b.tmp, b.buf[:b.Index(next)]...)
+	} else {
+		b.tmp = b.buf[b.Index(tail):b.Index(next)] // Otherwise, simple tail:next read.
+	}
+
+	return b.tmp, nil
+}




diff --git a/internal/circ/reader_test.go b/internal/circ/reader_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e535bbb3f0b41299a4f78f60747c3b2d9c423a73
--- /dev/null
+++ b/internal/circ/reader_test.go
@@ -0,0 +1,125 @@
+package circ
+
+import (
+	"bytes"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestNewReader(t *testing.T) {
+	var size = 16
+	var block = 4
+	buf := NewReader(size, block)
+
+	require.NotNil(t, buf.buf)
+	require.Equal(t, size, len(buf.buf))
+	require.Equal(t, size, buf.size)
+	require.Equal(t, block, buf.block)
+}
+
+func TestNewReaderFromSlice(t *testing.T) {
+	b := NewBytesPool(256)
+	buf := NewReaderFromSlice(DefaultBlockSize, b.Get())
+	require.NotNil(t, buf.buf)
+	require.Equal(t, 256, cap(buf.buf))
+}
+
+func TestReadFrom(t *testing.T) {
+	buf := NewReader(16, 4)
+
+	b4 := bytes.Repeat([]byte{'-'}, 4)
+	br := bytes.NewReader(b4)
+
+	_, err := buf.ReadFrom(br)
+	require.NoError(t, err)
+	require.Equal(t, bytes.Repeat([]byte{'-'}, 4), buf.buf[:4])
+	require.Equal(t, int64(4), buf.head)
+
+	br.Reset(b4)
+	_, err = buf.ReadFrom(br)
+	require.Equal(t, int64(8), buf.head)
+
+	br.Reset(b4)
+	_, err = buf.ReadFrom(br)
+	require.Equal(t, int64(12), buf.head)
+}
+
+func TestReadFromWrap(t *testing.T) {
+	buf := NewReader(16, 4)
+	buf.buf = bytes.Repeat([]byte{'-'}, 16)
+	buf.SetPos(8, 14)
+	br := bytes.NewReader(bytes.Repeat([]byte{'/'}, 8))
+
+	o := make(chan error)
+	go func() {
+		_, err := buf.ReadFrom(br)
+		o <- err
+	}()
+	time.Sleep(time.Millisecond * 100)
+	go func() {
+		atomic.StoreInt64(&buf.done, 1)
+		buf.rcond.L.Lock()
+		buf.rcond.Broadcast()
+		buf.rcond.L.Unlock()
+	}()
+	<-o
+	require.Equal(t, []byte{'/', '/', '/', '/', '/', '/', '-', '-', '-', '-', '-', '-', '-', '-', '/', '/'}, buf.Get())
+	require.Equal(t, int64(22), atomic.LoadInt64(&buf.head))
+	require.Equal(t, 6, buf.Index(atomic.LoadInt64(&buf.head)))
+}
+
+func TestReadOK(t *testing.T) {
+	buf := NewReader(16, 4)
+	buf.buf = []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}
+
+	tests := []struct {
+		tail  int64
+		head  int64
+		n     int
+		bytes []byte
+		desc  string
+	}{
+		{tail: 0, head: 4, n: 4, bytes: []byte{'a', 'b', 'c', 'd'}, desc: "0, 4 OK"},
+		{tail: 3, head: 15, n: 8, bytes: []byte{'d', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}, desc: "3, 15 OK"},
+		{tail: 14, head: 15, n: 6, bytes: []byte{'o', 'p', 'a', 'b', 'c', 'd'}, desc: "14, 2 wrapped OK"},
+	}
+
+	for i, tt := range tests {
+		buf.SetPos(tt.tail, tt.head)
+		o := make(chan []byte)
+		go func() {
+			p, _ := buf.Read(tt.n)
+			o <- p
+		}()
+
+		time.Sleep(time.Millisecond)
+		atomic.StoreInt64(&buf.head, buf.head+int64(tt.n))
+
+		buf.wcond.L.Lock()
+		buf.wcond.Broadcast()
+		buf.wcond.L.Unlock()
+
+		done := <-o
+		require.Equal(t, tt.bytes, done, "Peeked bytes mismatch [i:%d] %s", i, tt.desc)
+
+	}
+}
+
+func TestReadEnded(t *testing.T) {
+	buf := NewBuffer(16, 4)
+	o := make(chan error)
+	go func() {
+		_, err := buf.Read(4)
+		o <- err
+	}()
+	time.Sleep(time.Millisecond)
+	atomic.StoreInt64(&buf.done, 1)
+	buf.wcond.L.Lock()
+	buf.wcond.Broadcast()
+	buf.wcond.L.Unlock()
+
+	require.Error(t, <-o)
+}




diff --git a/internal/circ/writer.go b/internal/circ/writer.go
new file mode 100644
index 0000000000000000000000000000000000000000..d6b45aa90c33a27203a5563bcdfd9e6b24de6825
--- /dev/null
+++ b/internal/circ/writer.go
@@ -0,0 +1,104 @@
+package circ
+
+import (
+	"io"
+	"sync/atomic"
+)
+
+// Writer is a circular buffer for writing data to an io.Writer.
+type Writer struct {
+	Buffer
+}
+
+// NewWriter returns a pointer to a new Circular Writer.
+func NewWriter(size, block int) *Writer {
+	b := NewBuffer(size, block)
+	b.ID = "writer"
+	return &Writer{
+		b,
+	}
+}
+
+// NewWriterFromSlice returns a new Circular Writer using a pre-exising
+// byte slice.
+func NewWriterFromSlice(block int, p []byte) *Writer {
+	b := NewBufferFromSlice(block, p)
+	b.ID = "writer"
+	return &Writer{
+		b,
+	}
+}
+
+// WriteTo writes the contents of the buffer to an io.Writer.
+func (b *Writer) WriteTo(w io.Writer) (total int, err error) {
+	atomic.StoreInt64(&b.State, 2)
+	defer atomic.StoreInt64(&b.State, 0)
+	for {
+		if atomic.LoadInt64(&b.done) == 1 && b.CapDelta() == 0 {
+			return total, io.EOF
+		}
+
+		// Read from the buffer until there is at least 1 byte to write.
+		err = b.awaitFilled(1)
+		if err != nil {
+			return
+		}
+
+		// Get all the bytes between the tail and head, wrapping if necessary.
+		tail := atomic.LoadInt64(&b.tail)
+		rTail := b.Index(tail)
+		rHead := b.Index(atomic.LoadInt64(&b.head))
+		n := b.CapDelta()
+		p := make([]byte, 0, n)
+
+		if rTail > rHead {
+			p = append(p, b.buf[rTail:]...)
+			p = append(p, b.buf[:rHead]...)
+		} else {
+			p = append(p, b.buf[rTail:rHead]...)
+		}
+
+		n, err = w.Write(p)
+		total += n
+		if err != nil {
+			return
+		}
+
+		// Move the tail forward the bytes written and broadcast change.
+		atomic.StoreInt64(&b.tail, tail+int64(n))
+		b.rcond.L.Lock()
+		b.rcond.Broadcast()
+		b.rcond.L.Unlock()
+	}
+}
+
+// Write writes the buffer to the buffer p, returning the number of bytes written.
+func (b *Writer) Write(p []byte) (total int, err error) {
+	err = b.awaitEmpty(len(p))
+	if err != nil {
+		return
+	}
+
+	total = b.writeBytes(p)
+	atomic.AddInt64(&b.head, int64(total))
+	b.wcond.L.Lock()
+	b.wcond.Broadcast()
+	b.wcond.L.Unlock()
+
+	return
+}
+
+// writeBytes writes bytes to the buffer from the start position, and returns
+// the new head position. This function does not wait for capacity and will
+// overwrite any existing bytes.
+func (b *Writer) writeBytes(p []byte) int {
+	var o int
+	var n int
+	for i := 0; i < len(p); i++ {
+		o = b.Index(atomic.LoadInt64(&b.head) + int64(i))
+		b.buf[o] = p[i]
+		n++
+	}
+
+	return n
+}




diff --git a/internal/circ/writer_test.go b/internal/circ/writer_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..9ea7118761994795421814d06941351939bcb6fc
--- /dev/null
+++ b/internal/circ/writer_test.go
@@ -0,0 +1,155 @@
+package circ
+
+import (
+	"bufio"
+	"bytes"
+	"net"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestNewWriter(t *testing.T) {
+	var size = 16
+	var block = 4
+	buf := NewWriter(size, block)
+
+	require.NotNil(t, buf.buf)
+	require.Equal(t, size, len(buf.buf))
+	require.Equal(t, size, buf.size)
+	require.Equal(t, block, buf.block)
+}
+
+func TestNewWriterFromSlice(t *testing.T) {
+	b := NewBytesPool(256)
+	buf := NewWriterFromSlice(DefaultBlockSize, b.Get())
+	require.NotNil(t, buf.buf)
+	require.Equal(t, 256, cap(buf.buf))
+}
+
+func TestWriteTo(t *testing.T) {
+	tests := []struct {
+		tail  int64
+		head  int64
+		bytes []byte
+		await int
+		total int
+		err   error
+		desc  string
+	}{
+		{tail: 0, head: 5, bytes: []byte{'a', 'b', 'c', 'd', 'e'}, desc: "0,5 OK"},
+		{tail: 14, head: 21, bytes: []byte{'o', 'p', 'a', 'b', 'c', 'd', 'e'}, desc: "14,16(2) OK"},
+	}
+
+	for i, tt := range tests {
+		bb := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}
+		buf := NewWriter(16, 4)
+		buf.Set(bb, 0, 16)
+		buf.SetPos(tt.tail, tt.head)
+
+		var b bytes.Buffer
+		w := bufio.NewWriter(&b)
+
+		nc := make(chan int)
+		go func() {
+			n, _ := buf.WriteTo(w)
+			nc <- n
+		}()
+
+		time.Sleep(time.Millisecond * 100)
+		atomic.StoreInt64(&buf.done, 1)
+		buf.wcond.L.Lock()
+		buf.wcond.Broadcast()
+		buf.wcond.L.Unlock()
+
+		w.Flush()
+		require.Equal(t, tt.bytes, b.Bytes(), "Written bytes mismatch [i:%d] %s", i, tt.desc)
+	}
+}
+
+func TestWriteToEndedFirst(t *testing.T) {
+	buf := NewWriter(16, 4)
+	buf.done = 1
+
+	var b bytes.Buffer
+	w := bufio.NewWriter(&b)
+	_, err := buf.WriteTo(w)
+	require.Error(t, err)
+}
+
+func TestWriteToBadWriter(t *testing.T) {
+	bb := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}
+	buf := NewWriter(16, 4)
+	buf.Set(bb, 0, 16)
+	buf.SetPos(0, 6)
+	r, w := net.Pipe()
+
+	w.Close()
+	_, err := buf.WriteTo(w)
+	require.Error(t, err)
+	r.Close()
+}
+
+func TestWrite(t *testing.T) {
+	tests := []struct {
+		tail  int64
+		head  int64
+		rHead int64
+		bytes []byte
+		want  []byte
+		desc  string
+	}{
+		{tail: 0, head: 0, rHead: 4, bytes: []byte{'a', 'b', 'c', 'd'}, want: []byte{'a', 'b', 'c', 'd', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, desc: "0>4 OK"},
+		{tail: 4, head: 14, rHead: 2, bytes: []byte{'a', 'b', 'c', 'd'}, want: []byte{'c', 'd', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'a', 'b'}, desc: "14>2 OK"},
+	}
+
+	for i, tt := range tests {
+		buf := NewWriter(16, 4)
+		buf.SetPos(tt.tail, tt.head)
+
+		o := make(chan []interface{})
+		go func() {
+			nn, err := buf.Write(tt.bytes)
+			o <- []interface{}{nn, err}
+		}()
+
+		done := <-o
+		require.Equal(t, tt.want, buf.buf, "Wanted written mismatch [i:%d] %s", i, tt.desc)
+		require.Nil(t, done[1], "Unexpected Error [i:%d] %s", i, tt.desc)
+	}
+}
+
+func TestWriteEnded(t *testing.T) {
+	buf := NewWriter(16, 4)
+	buf.SetPos(15, 30)
+	buf.done = 1
+
+	_, err := buf.Write([]byte{'a', 'b', 'c', 'd'})
+	require.Error(t, err)
+}
+
+func TestWriteBytes(t *testing.T) {
+	tests := []struct {
+		tail  int64
+		head  int64
+		bytes []byte
+		want  []byte
+		start int
+		desc  string
+	}{
+		{tail: 0, head: 0, bytes: []byte{'a', 'b', 'c', 'd'}, want: []byte{'a', 'b', 'c', 'd', 0, 0, 0, 0}, desc: "0,4 OK"},
+		{tail: 6, head: 6, bytes: []byte{'a', 'b', 'c', 'd'}, want: []byte{'c', 'd', 0, 0, 0, 0, 'a', 'b'}, desc: "6,2 OK wrapped"},
+	}
+
+	for i, tt := range tests {
+		buf := NewWriter(8, 4)
+		buf.SetPos(tt.tail, tt.head)
+		n := buf.writeBytes(tt.bytes)
+
+		require.Equal(t, tt.want, buf.buf, "Buffer mistmatch [i:%d] %s", i, tt.desc)
+		require.Equal(t, len(tt.bytes), n)
+	}
+
+}




diff --git a/internal/clients/clients.go b/internal/clients/clients.go
new file mode 100644
index 0000000000000000000000000000000000000000..a98c0cd5edffd7d478991111cbfec906dde1250b
--- /dev/null
+++ b/internal/clients/clients.go
@@ -0,0 +1,461 @@
+package clients
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"net"
+	"sync"
+	"sync/atomic"
+	"time"
+
+	"github.com/rs/xid"
+
+	"github.com/mochi-co/mqtt/internal/auth"
+	"github.com/mochi-co/mqtt/internal/circ"
+	"github.com/mochi-co/mqtt/internal/packets"
+	"github.com/mochi-co/mqtt/internal/topics"
+)
+
+var (
+	defaultKeepalive uint16 = 10 // in seconds.
+
+	ErrConnectionClosed = errors.New("Connection not open")
+)
+
+// Clients contains a map of the clients known by the broker.
+type Clients struct {
+	sync.RWMutex
+	internal map[string]*Client // clients known by the broker, keyed on client id.
+}
+
+// New returns an instance of Clients.
+func New() Clients {
+	return Clients{
+		internal: make(map[string]*Client),
+	}
+}
+
+// Add adds a new client to the clients map, keyed on client id.
+func (cl *Clients) Add(val *Client) {
+	cl.Lock()
+	cl.internal[val.ID] = val
+	cl.Unlock()
+}
+
+// Get returns the value of a client if it exists.
+func (cl *Clients) Get(id string) (*Client, bool) {
+	cl.RLock()
+	val, ok := cl.internal[id]
+	cl.RUnlock()
+	return val, ok
+}
+
+// Len returns the length of the clients map.
+func (cl *Clients) Len() int {
+	cl.RLock()
+	val := len(cl.internal)
+	cl.RUnlock()
+	return val
+}
+
+// Delete removes a client from the internal map.
+func (cl *Clients) Delete(id string) {
+	cl.Lock()
+	delete(cl.internal, id)
+	cl.Unlock()
+}
+
+// GetByListener returns clients matching a listener id.
+func (cl *Clients) GetByListener(id string) []*Client {
+	clients := make([]*Client, 0, cl.Len())
+	cl.RLock()
+	for _, v := range cl.internal {
+		if v.Listener == id && atomic.LoadInt64(&v.state.done) == 0 {
+			clients = append(clients, v)
+		}
+	}
+	cl.RUnlock()
+	return clients
+}
+
+// Client contains information about a client known by the broker.
+type Client struct {
+	sync.RWMutex
+	conn          net.Conn             // the net.Conn used to establish the connection.
+	r             *circ.Reader         // a reader for reading incoming bytes.
+	w             *circ.Writer         // a writer for writing outgoing bytes.
+	ID            string               // the client id.
+	AC            auth.Controller      // an auth controller inherited from the listener.
+	Subscriptions topics.Subscriptions // a map of the subscription filters a client maintains.
+	Listener      string               // the id of the listener the client is connected to.
+	InFlight      InFlight             // a map of in-flight qos messages.
+	Username      []byte               // the username the client authenticated with.
+	keepalive     uint16               // the number of seconds the connection can wait.
+	cleanSession  bool                 // indicates if the client expects a clean-session.
+	packetID      uint32               // the current highest packetID.
+	LWT           LWT                  // the last will and testament for the client.
+	state         clientState          // the operational state of the client.
+}
+
+// clientState tracks the state of the client.
+type clientState struct {
+	started *sync.WaitGroup // tracks the goroutines which have been started.
+	endedW  *sync.WaitGroup // tracks when the writer has ended.
+	endedR  *sync.WaitGroup // tracks when the reader has ended.
+	done    int64           // atomic counter which indicates that the client has closed.
+}
+
+// NewClient returns a new instance of Client.
+func NewClient(c net.Conn, r *circ.Reader, w *circ.Writer) *Client {
+	cl := &Client{
+		conn: c,
+		r:    r,
+		w:    w,
+
+		keepalive: defaultKeepalive,
+		InFlight: InFlight{
+			internal: make(map[uint16]InFlightMessage),
+		},
+		Subscriptions: make(map[string]byte),
+
+		state: clientState{
+			started: new(sync.WaitGroup),
+			endedW:  new(sync.WaitGroup),
+			endedR:  new(sync.WaitGroup),
+		},
+	}
+	return cl
+}
+
+// Identify sets the identification values of a client instance.
+func (cl *Client) Identify(lid string, pk packets.Packet, ac auth.Controller) {
+	cl.Listener = lid
+	cl.AC = ac
+
+	cl.ID = pk.ClientIdentifier
+	if cl.ID == "" {
+		cl.ID = xid.New().String()
+	}
+
+	cl.r.ID = cl.ID + " READER"
+	cl.w.ID = cl.ID + " WRITER"
+
+	cl.Username = pk.Username
+	cl.keepalive = pk.Keepalive
+	cl.cleanSession = pk.CleanSession
+
+	if pk.WillFlag {
+		cl.LWT = LWT{
+			Topic:   pk.WillTopic,
+			Message: pk.WillMessage,
+			Qos:     pk.WillQos,
+			Retain:  pk.WillRetain,
+		}
+	}
+}
+
+// refreshDeadline refreshes the read/write deadline for the net.Conn connection.
+func (cl *Client) refreshDeadline(keepalive uint16) {
+	if cl.conn != nil {
+		expiry := time.Duration(keepalive+(keepalive/2)) * time.Second
+		cl.conn.SetDeadline(time.Now().Add(expiry))
+	}
+}
+
+// NextPacketID returns the next packet id for a client, looping back to 0
+// if the maximum ID has been reached.
+func (cl *Client) NextPacketID() uint32 {
+	i := atomic.LoadUint32(&cl.packetID)
+	if i == uint32(65535) || i == uint32(0) {
+		atomic.StoreUint32(&cl.packetID, 1)
+		return 1
+	}
+
+	return atomic.AddUint32(&cl.packetID, 1)
+}
+
+// NoteSubscription makes a note of a subscription for the client.
+func (cl *Client) NoteSubscription(filter string, qos byte) {
+	cl.Lock()
+	cl.Subscriptions[filter] = qos
+	cl.Unlock()
+}
+
+// ForgetSubscription forgests a subscription note for the client.
+func (cl *Client) ForgetSubscription(filter string) {
+	cl.Lock()
+	delete(cl.Subscriptions, filter)
+	cl.Unlock()
+}
+
+// Start begins the client goroutines reading and writing packets.
+func (cl *Client) Start() {
+	cl.state.started.Add(2)
+
+	go func() {
+		cl.state.started.Done()
+		cl.w.WriteTo(cl.conn)
+		cl.state.endedW.Done()
+		cl.Stop()
+	}()
+	cl.state.endedW.Add(1)
+
+	go func() {
+		cl.state.started.Done()
+		cl.r.ReadFrom(cl.conn)
+		cl.state.endedR.Done()
+		cl.Stop()
+	}()
+	cl.state.endedR.Add(1)
+
+	cl.state.started.Wait()
+}
+
+// Stop instructs the client to shut down all processing goroutines and disconnect.
+func (cl *Client) Stop() {
+	if atomic.LoadInt64(&cl.state.done) == 0 {
+		cl.r.Stop()
+		cl.w.Stop()
+		cl.state.endedW.Wait()
+
+		cl.conn.Close()
+
+		cl.state.endedR.Wait()
+		atomic.StoreInt64(&cl.state.done, 1)
+	}
+}
+
+// readFixedHeader reads in the values of the next packet's fixed header.
+func (cl *Client) ReadFixedHeader(fh *packets.FixedHeader) error {
+	p, err := cl.r.Read(1)
+	if err != nil {
+		return err
+	}
+
+	err = fh.Decode(p[0])
+	if err != nil {
+		return err
+	}
+
+	// The remaining length value can be up to 5 bytes. Read through each byte
+	// looking for continue values, and if found increase the read. Otherwise
+	// decode the bytes that were legit.
+	buf := make([]byte, 0, 6)
+	i := 1
+	n := 2
+	for ; n < 6; n++ {
+		p, err = cl.r.Read(n)
+		if err != nil {
+			return err
+		}
+
+		buf = append(buf, p[i])
+
+		// If it's not a continuation flag, end here.
+		if p[i] < 128 {
+			break
+		}
+
+		// If i has reached 4 without a length terminator, return a protocol violation.
+		i++
+		if i == 4 {
+			return packets.ErrOversizedLengthIndicator
+		}
+	}
+
+	// Calculate and store the remaining length of the packet payload.
+	rem, _ := binary.Uvarint(buf)
+	fh.Remaining = int(rem)
+
+	// Having successfully read n bytes, commit the tail forward.
+	cl.r.CommitTail(n)
+
+	return nil
+}
+
+// Read reads new packets from a client connection
+func (cl *Client) Read(h func(*Client, packets.Packet) error) error {
+	for {
+		if atomic.LoadInt64(&cl.state.done) == 1 && cl.r.CapDelta() == 0 {
+			return nil
+		}
+
+		cl.refreshDeadline(cl.keepalive)
+		fh := new(packets.FixedHeader)
+		err := cl.ReadFixedHeader(fh)
+		if err != nil {
+			return err
+		}
+
+		//if fh.Type == packets.Disconnect {
+		//	return nil
+		//}
+
+		pk, err := cl.ReadPacket(fh)
+		if err != nil {
+			return err
+		}
+
+		err = h(cl, pk) // Process inbound packet.
+		if err != nil {
+			return err
+		}
+	}
+}
+
+// ReadPacket reads the remaining buffer into an MQTT packet.
+func (cl *Client) ReadPacket(fh *packets.FixedHeader) (pk packets.Packet, err error) {
+	pk.FixedHeader = *fh
+	if pk.FixedHeader.Remaining == 0 {
+		return
+	}
+
+	p, err := cl.r.Read(pk.FixedHeader.Remaining)
+	if err != nil {
+		return pk, err
+	}
+
+	// Decode the remaining packet values using a fresh copy of the bytes,
+	// otherwise the next packet will change the data of this one.
+	px := append([]byte{}, p[:]...)
+
+	switch pk.FixedHeader.Type {
+	case packets.Connect:
+		err = pk.ConnectDecode(px)
+	case packets.Connack:
+		err = pk.ConnackDecode(px)
+	case packets.Publish:
+		err = pk.PublishDecode(px)
+	case packets.Puback:
+		err = pk.PubackDecode(px)
+	case packets.Pubrec:
+		err = pk.PubrecDecode(px)
+	case packets.Pubrel:
+		err = pk.PubrelDecode(px)
+	case packets.Pubcomp:
+		err = pk.PubcompDecode(px)
+	case packets.Subscribe:
+		err = pk.SubscribeDecode(px)
+	case packets.Suback:
+		err = pk.SubackDecode(px)
+	case packets.Unsubscribe:
+		err = pk.UnsubscribeDecode(px)
+	case packets.Unsuback:
+		err = pk.UnsubackDecode(px)
+	case packets.Pingreq:
+	case packets.Pingresp:
+	case packets.Disconnect:
+	default:
+		err = fmt.Errorf("No valid packet available; %v", pk.FixedHeader.Type)
+	}
+
+	cl.r.CommitTail(pk.FixedHeader.Remaining)
+
+	return
+}
+
+// WritePacket encodes and writes a packet to the client.
+func (cl *Client) WritePacket(pk packets.Packet) (n int, err error) {
+	if atomic.LoadInt64(&cl.state.done) == 1 {
+		return 0, ErrConnectionClosed
+	}
+
+	cl.w.Mu.Lock()
+	defer cl.w.Mu.Unlock()
+
+	buf := new(bytes.Buffer)
+	switch pk.FixedHeader.Type {
+	case packets.Connect:
+		err = pk.ConnectEncode(buf)
+	case packets.Connack:
+		err = pk.ConnackEncode(buf)
+	case packets.Publish:
+		err = pk.PublishEncode(buf)
+	case packets.Puback:
+		err = pk.PubackEncode(buf)
+	case packets.Pubrec:
+		err = pk.PubrecEncode(buf)
+	case packets.Pubrel:
+		err = pk.PubrelEncode(buf)
+	case packets.Pubcomp:
+		err = pk.PubcompEncode(buf)
+	case packets.Subscribe:
+		err = pk.SubscribeEncode(buf)
+	case packets.Suback:
+		err = pk.SubackEncode(buf)
+	case packets.Unsubscribe:
+		err = pk.UnsubscribeEncode(buf)
+	case packets.Unsuback:
+		err = pk.UnsubackEncode(buf)
+	case packets.Pingreq:
+		err = pk.PingreqEncode(buf)
+	case packets.Pingresp:
+		err = pk.PingrespEncode(buf)
+	case packets.Disconnect:
+		err = pk.DisconnectEncode(buf)
+	default:
+		err = fmt.Errorf("No valid packet available; %v", pk.FixedHeader.Type)
+	}
+	if err != nil {
+		return
+	}
+
+	n, err = cl.w.Write(buf.Bytes())
+	if err != nil {
+		return
+	}
+
+	cl.refreshDeadline(cl.keepalive)
+
+	return
+}
+
+// LWT contains the last will and testament details for a client connection.
+type LWT struct {
+	Topic   string // the topic the will message shall be sent to.
+	Message []byte // the message that shall be sent when the client disconnects.
+	Qos     byte   // the quality of service desired.
+	Retain  bool   // indicates whether the will message should be retained
+}
+
+// InFlightMessage contains data about a packet which is currently in-flight.
+type InFlightMessage struct {
+	Packet packets.Packet // the packet currently in-flight.
+	Sent   int64          // the last time the message was sent (for retries) in unixtime.
+}
+
+// InFlight is a map of InFlightMessage keyed on packet id.
+type InFlight struct {
+	sync.RWMutex
+	internal map[uint16]InFlightMessage // internal contains the inflight messages.
+}
+
+// set stores the packet of an in-flight message, keyed on message id.
+func (i *InFlight) Set(key uint16, in InFlightMessage) {
+	i.Lock()
+	i.internal[key] = in
+	i.Unlock()
+}
+
+// get returns the value of an in-flight message if it exists.
+func (i *InFlight) Get(key uint16) (InFlightMessage, bool) {
+	i.RLock()
+	val, ok := i.internal[key]
+	i.RUnlock()
+	return val, ok
+}
+
+func (i *InFlight) GetAll() map[uint16]InFlightMessage {
+	i.RLock()
+	defer i.RUnlock()
+	return i.internal
+}
+
+// delete removes an in-flight message from the map.
+func (i *InFlight) Delete(key uint16) {
+	i.Lock()
+	delete(i.internal, key)
+	i.Unlock()
+}




diff --git a/internal/clients/clients_test.go b/internal/clients/clients_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d4c8feaed2ea61c88f29f484c6e098b7fd03511d
--- /dev/null
+++ b/internal/clients/clients_test.go
@@ -0,0 +1,969 @@
+package clients
+
+import (
+	"errors"
+	"io"
+	"io/ioutil"
+	"net"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"github.com/mochi-co/mqtt/internal/auth"
+	"github.com/mochi-co/mqtt/internal/circ"
+	"github.com/mochi-co/mqtt/internal/packets"
+	"github.com/stretchr/testify/require"
+)
+
+func genClient() *Client {
+	c, _ := net.Pipe()
+	return NewClient(c, circ.NewReader(128, 8), circ.NewWriter(128, 8))
+}
+
+func TestNewClients(t *testing.T) {
+	cl := New()
+	require.NotNil(t, cl.internal)
+}
+
+func BenchmarkNewClients(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		New()
+	}
+}
+
+func TestClientsAdd(t *testing.T) {
+	cl := New()
+	cl.Add(&Client{ID: "t1"})
+	require.Contains(t, cl.internal, "t1")
+}
+
+func BenchmarkClientsAdd(b *testing.B) {
+	cl := New()
+	client := &Client{ID: "t1"}
+	for n := 0; n < b.N; n++ {
+		cl.Add(client)
+	}
+}
+
+func TestClientsGet(t *testing.T) {
+	cl := New()
+	cl.Add(&Client{ID: "t1"})
+	cl.Add(&Client{ID: "t2"})
+	require.Contains(t, cl.internal, "t1")
+	require.Contains(t, cl.internal, "t2")
+
+	client, ok := cl.Get("t1")
+	require.Equal(t, true, ok)
+	require.Equal(t, "t1", client.ID)
+}
+
+func BenchmarkClientsGet(b *testing.B) {
+	cl := New()
+	cl.Add(&Client{ID: "t1"})
+	for n := 0; n < b.N; n++ {
+		cl.Get("t1")
+	}
+}
+
+func TestClientsLen(t *testing.T) {
+	cl := New()
+	cl.Add(&Client{ID: "t1"})
+	cl.Add(&Client{ID: "t2"})
+	require.Contains(t, cl.internal, "t1")
+	require.Contains(t, cl.internal, "t2")
+	require.Equal(t, 2, cl.Len())
+}
+
+func BenchmarkClientsLen(b *testing.B) {
+	cl := New()
+	cl.Add(&Client{ID: "t1"})
+	for n := 0; n < b.N; n++ {
+		cl.Len()
+	}
+}
+
+func TestClientsDelete(t *testing.T) {
+	cl := New()
+	cl.Add(&Client{ID: "t1"})
+	require.Contains(t, cl.internal, "t1")
+
+	cl.Delete("t1")
+	_, ok := cl.Get("t1")
+	require.Equal(t, false, ok)
+	require.Nil(t, cl.internal["t1"])
+}
+
+func BenchmarkClientsDelete(b *testing.B) {
+	cl := New()
+	cl.Add(&Client{ID: "t1"})
+	for n := 0; n < b.N; n++ {
+		cl.Delete("t1")
+	}
+}
+
+func TestClientsGetByListener(t *testing.T) {
+	cl := New()
+	cl.Add(&Client{ID: "t1", Listener: "tcp1"})
+	cl.Add(&Client{ID: "t2", Listener: "ws1"})
+	require.Contains(t, cl.internal, "t1")
+	require.Contains(t, cl.internal, "t2")
+
+	clients := cl.GetByListener("tcp1")
+	require.NotEmpty(t, clients)
+	require.Equal(t, 1, len(clients))
+	require.Equal(t, "tcp1", clients[0].Listener)
+}
+
+func BenchmarkClientsGetByListener(b *testing.B) {
+	cl := New()
+	cl.Add(&Client{ID: "t1", Listener: "tcp1"})
+	cl.Add(&Client{ID: "t2", Listener: "ws1"})
+	for n := 0; n < b.N; n++ {
+		cl.GetByListener("tcp1")
+	}
+}
+
+func TestNewClient(t *testing.T) {
+	cl := genClient()
+
+	require.NotNil(t, cl)
+	require.NotNil(t, cl.InFlight.internal)
+	require.NotNil(t, cl.Subscriptions)
+	require.NotNil(t, cl.r)
+	require.NotNil(t, cl.w)
+	require.NotNil(t, cl.state.started)
+	require.NotNil(t, cl.state.endedW)
+	require.NotNil(t, cl.state.endedR)
+}
+
+func BenchmarkNewClient(b *testing.B) {
+	c, _ := net.Pipe()
+	for n := 0; n < b.N; n++ {
+		NewClient(c, circ.NewReader(16, 4), circ.NewWriter(16, 4))
+	}
+}
+
+func TestClientIdentify(t *testing.T) {
+	cl := genClient()
+
+	pk := packets.Packet{
+		FixedHeader: packets.FixedHeader{
+			Type:      packets.Connect,
+			Remaining: 16,
+		},
+		ProtocolName:     []byte{'M', 'Q', 'T', 'T'},
+		ProtocolVersion:  4,
+		CleanSession:     true,
+		Keepalive:        60,
+		ClientIdentifier: "mochi",
+	}
+
+	cl.Identify("tcp1", pk, new(auth.Allow))
+	require.Equal(t, pk.Keepalive, cl.keepalive)
+	require.Equal(t, pk.CleanSession, cl.cleanSession)
+	require.Equal(t, pk.ClientIdentifier, cl.ID)
+}
+
+func BenchmarkClientIdentify(b *testing.B) {
+	cl := genClient()
+
+	pk := packets.Packet{
+		FixedHeader: packets.FixedHeader{
+			Type:      packets.Connect,
+			Remaining: 16,
+		},
+		ProtocolName:     []byte{'M', 'Q', 'T', 'T'},
+		ProtocolVersion:  4,
+		CleanSession:     true,
+		Keepalive:        60,
+		ClientIdentifier: "mochi",
+	}
+
+	for n := 0; n < b.N; n++ {
+		cl.Identify("tcp1", pk, new(auth.Allow))
+	}
+}
+
+func TestClientIdentifyNoID(t *testing.T) {
+	cl := genClient()
+
+	pk := packets.Packet{
+		FixedHeader: packets.FixedHeader{
+			Type:      packets.Connect,
+			Remaining: 16,
+		},
+		ProtocolName:    []byte{'M', 'Q', 'T', 'T'},
+		ProtocolVersion: 4,
+		CleanSession:    true,
+		Keepalive:       60,
+	}
+
+	cl.Identify("tcp1", pk, new(auth.Allow))
+	require.NotEmpty(t, cl.ID)
+}
+
+func TestClientIdentifyLWT(t *testing.T) {
+	cl := genClient()
+
+	pk := packets.Packet{
+		FixedHeader: packets.FixedHeader{
+			Type:      packets.Connect,
+			Remaining: 16,
+		},
+		ProtocolName:     []byte{'M', 'Q', 'T', 'T'},
+		ProtocolVersion:  4,
+		CleanSession:     true,
+		Keepalive:        60,
+		ClientIdentifier: "mochi",
+		WillFlag:         true,
+		WillTopic:        "lwt",
+		WillMessage:      []byte("lol gg"),
+		WillQos:          1,
+		WillRetain:       false,
+	}
+
+	cl.Identify("tcp1", pk, new(auth.Allow))
+	require.Equal(t, pk.WillTopic, cl.LWT.Topic)
+	require.Equal(t, pk.WillMessage, cl.LWT.Message)
+	require.Equal(t, pk.WillQos, cl.LWT.Qos)
+	require.Equal(t, pk.WillRetain, cl.LWT.Retain)
+}
+
+func TestClientNextPacketID(t *testing.T) {
+	cl := genClient()
+
+	require.Equal(t, uint32(1), cl.NextPacketID())
+	require.Equal(t, uint32(2), cl.NextPacketID())
+
+	cl.packetID = uint32(65534)
+	require.Equal(t, uint32(65535), cl.NextPacketID())
+	require.Equal(t, uint32(1), cl.NextPacketID())
+}
+
+func BenchmarkClientNextPacketID(b *testing.B) {
+	cl := genClient()
+
+	for n := 0; n < b.N; n++ {
+		cl.NextPacketID()
+	}
+}
+
+func TestClientNoteSubscription(t *testing.T) {
+	cl := genClient()
+
+	cl.NoteSubscription("a/b/c", 0)
+	require.Contains(t, cl.Subscriptions, "a/b/c")
+	require.Equal(t, byte(0), cl.Subscriptions["a/b/c"])
+}
+
+func BenchmarkClientNoteSubscription(b *testing.B) {
+	cl := genClient()
+	for n := 0; n < b.N; n++ {
+		cl.NoteSubscription("a/b/c", 0)
+	}
+}
+
+func TestClientForgetSubscription(t *testing.T) {
+	cl := genClient()
+	require.NotNil(t, cl)
+	cl.Subscriptions = map[string]byte{
+		"a/b/c/": 1,
+	}
+	cl.ForgetSubscription("a/b/c/")
+	require.Empty(t, cl.Subscriptions["a/b/c"])
+}
+
+func BenchmarkClientForgetSubscription(b *testing.B) {
+	cl := genClient()
+	for n := 0; n < b.N; n++ {
+		cl.NoteSubscription("a/b/c", 0)
+		cl.ForgetSubscription("a/b/c/")
+	}
+}
+
+func TestClientRefreshDeadline(t *testing.T) {
+	cl := genClient()
+	cl.refreshDeadline(10)
+
+	// How do we check net.Conn deadline?
+	require.NotNil(t, cl.conn)
+}
+
+func BenchmarkClientRefreshDeadline(b *testing.B) {
+	cl := genClient()
+	for n := 0; n < b.N; n++ {
+		cl.refreshDeadline(10)
+	}
+}
+
+func TestClientStart(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+	time.Sleep(time.Millisecond)
+	require.Equal(t, int64(1), atomic.LoadInt64(&cl.r.State))
+	require.Equal(t, int64(2), atomic.LoadInt64(&cl.w.State))
+}
+
+func BenchmarkClientStart(b *testing.B) {
+	cl := genClient()
+	defer cl.Stop()
+
+	for n := 0; n < b.N; n++ {
+		cl.Start()
+	}
+}
+
+func TestClientReadFixedHeader(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	cl.r.Set([]byte{packets.Connect << 4, 0x00}, 0, 2)
+	cl.r.SetPos(0, 2)
+
+	fh := new(packets.FixedHeader)
+	err := cl.ReadFixedHeader(fh)
+	require.NoError(t, err)
+
+	tail, head := cl.r.GetPos()
+	require.Equal(t, int64(2), tail)
+	require.Equal(t, int64(2), head)
+}
+
+func TestClientReadFixedHeaderDecodeError(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	o := make(chan error)
+	go func() {
+		fh := new(packets.FixedHeader)
+		cl.r.Set([]byte{packets.Connect<<4 | 1<<1, 0x00, 0x00}, 0, 2)
+		cl.r.SetPos(0, 2)
+		o <- cl.ReadFixedHeader(fh)
+	}()
+	time.Sleep(time.Millisecond)
+	require.Error(t, <-o)
+}
+
+func TestClientReadFixedHeaderReadEOF(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	o := make(chan error)
+	go func() {
+		fh := new(packets.FixedHeader)
+		cl.r.Set([]byte{packets.Connect << 4, 0x00}, 0, 2)
+		cl.r.SetPos(0, 1)
+		o <- cl.ReadFixedHeader(fh)
+	}()
+	time.Sleep(time.Millisecond)
+	cl.r.Stop()
+	err := <-o
+	require.Error(t, err)
+	require.Equal(t, io.EOF, err)
+}
+
+func TestClientReadFixedHeaderNoLengthTerminator(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	o := make(chan error)
+	go func() {
+		fh := new(packets.FixedHeader)
+		err := cl.r.Set([]byte{packets.Connect << 4, 0xd5, 0x86, 0xf9, 0x9e, 0x01}, 0, 5)
+		require.NoError(t, err)
+		cl.r.SetPos(0, 5)
+		o <- cl.ReadFixedHeader(fh)
+	}()
+	time.Sleep(time.Millisecond)
+	require.Error(t, <-o)
+}
+
+func TestClientReadOK(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	// Two packets in a row...
+	b := []byte{
+		byte(packets.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,
+		byte(packets.Publish << 4), 11, // Fixed header
+		0, 5, // Topic Name - LSB+MSB
+		'd', '/', 'e', '/', 'f', // Topic Name
+		'y', 'e', 'a', 'h', // Payload
+	}
+
+	err := cl.r.Set(b, 0, len(b))
+	require.NoError(t, err)
+	cl.r.SetPos(0, int64(len(b)))
+
+	o := make(chan error)
+	var pks []packets.Packet
+	go func() {
+		o <- cl.Read(func(cl *Client, pk packets.Packet) error {
+			pks = append(pks, pk)
+			return nil
+		})
+	}()
+
+	time.Sleep(time.Millisecond)
+	cl.r.Stop()
+
+	err = <-o
+	require.Error(t, err)
+	require.Equal(t, io.EOF, err)
+	require.Equal(t, 2, len(pks))
+	require.Equal(t, pks, []packets.Packet{
+		{
+			FixedHeader: packets.FixedHeader{
+				Type:      packets.Publish,
+				Remaining: 18,
+			},
+			TopicName: "a/b/c",
+			Payload:   []byte("hello mochi"),
+		},
+		{
+			FixedHeader: packets.FixedHeader{
+				Type:      packets.Publish,
+				Remaining: 11,
+			},
+			TopicName: "d/e/f",
+			Payload:   []byte("yeah"),
+		},
+	})
+
+}
+
+func TestClientReadDone(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+	cl.state.done = 1
+
+	err := cl.Read(func(cl *Client, pk packets.Packet) error {
+		return nil
+	})
+
+	require.NoError(t, err)
+}
+
+func TestClientReadPacketError(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	b := []byte{
+		0, 18,
+		0, 5,
+		'a', '/', 'b', '/', 'c',
+		'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i',
+	}
+	err := cl.r.Set(b, 0, len(b))
+	require.NoError(t, err)
+	cl.r.SetPos(0, int64(len(b)))
+
+	o := make(chan error)
+	go func() {
+		o <- cl.Read(func(cl *Client, pk packets.Packet) error {
+			return nil
+		})
+	}()
+
+	require.Error(t, <-o)
+}
+
+func TestClientReadHandlerErr(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	b := []byte{
+		byte(packets.Publish << 4), 11, // Fixed header
+		0, 5, // Topic Name - LSB+MSB
+		'd', '/', 'e', '/', 'f', // Topic Name
+		'y', 'e', 'a', 'h', // Payload
+	}
+
+	err := cl.r.Set(b, 0, len(b))
+	require.NoError(t, err)
+	cl.r.SetPos(0, int64(len(b)))
+
+	err = cl.Read(func(cl *Client, pk packets.Packet) error {
+		return errors.New("test")
+	})
+
+	require.Error(t, err)
+}
+
+func TestClientReadPacketOK(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	err := cl.r.Set([]byte{
+		byte(packets.Publish << 4), 11, // Fixed header
+		0, 5,
+		'd', '/', 'e', '/', 'f',
+		'y', 'e', 'a', 'h',
+	}, 0, 13)
+	require.NoError(t, err)
+	cl.r.SetPos(0, 13)
+
+	fh := new(packets.FixedHeader)
+	err = cl.ReadFixedHeader(fh)
+	require.NoError(t, err)
+
+	pk, err := cl.ReadPacket(fh)
+	require.NoError(t, err)
+	require.NotNil(t, pk)
+
+	require.Equal(t, packets.Packet{
+		FixedHeader: packets.FixedHeader{
+			Type:      packets.Publish,
+			Remaining: 11,
+		},
+		TopicName: "d/e/f",
+		Payload:   []byte("yeah"),
+	}, pk)
+}
+
+func TestClientReadPacket(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	for i, tt := range pkTable {
+		err := cl.r.Set(tt.bytes, 0, len(tt.bytes))
+		require.NoError(t, err)
+		cl.r.SetPos(0, int64(len(tt.bytes)))
+
+		fh := new(packets.FixedHeader)
+		err = cl.ReadFixedHeader(fh)
+		require.NoError(t, err)
+
+		pk, err := cl.ReadPacket(fh)
+		require.NoError(t, err)
+		require.NotNil(t, pk)
+
+		require.Equal(t, tt.packet, pk, "Mismatched packet: [i:%d] %d", i, tt.bytes[0])
+	}
+}
+
+func TestClientReadPacketReadingError(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+
+	err := cl.r.Set([]byte{
+		0, 11, // Fixed header
+		0, 5,
+		'd', '/', 'e', '/', 'f',
+		'y', 'e', 'a', 'h',
+	}, 0, 13)
+	require.NoError(t, err)
+	cl.r.SetPos(2, 13)
+
+	_, err = cl.ReadPacket(&packets.FixedHeader{
+		Type:      0,
+		Remaining: 11,
+	})
+	require.Error(t, err)
+}
+
+func TestClientReadPacketReadError(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+	cl.r.Stop()
+
+	_, err := cl.ReadPacket(&packets.FixedHeader{
+		Remaining: 1,
+	})
+	require.Error(t, err)
+	require.Equal(t, io.EOF, err)
+}
+
+func TestClientReadPacketReadUnknown(t *testing.T) {
+	cl := genClient()
+	cl.Start()
+	defer cl.Stop()
+	cl.r.Stop()
+
+	_, err := cl.ReadPacket(&packets.FixedHeader{
+		Remaining: 1,
+	})
+	require.Error(t, err)
+}
+
+func TestClientWritePacket(t *testing.T) {
+	for i, tt := range pkTable {
+		r, w := net.Pipe()
+		cl := NewClient(r, circ.NewReader(128, 8), circ.NewWriter(128, 8))
+		cl.Start()
+
+		o := make(chan []byte)
+		go func() {
+			buf, err := ioutil.ReadAll(w)
+			require.NoError(t, err)
+			o <- buf
+		}()
+
+		n, err := cl.WritePacket(tt.packet)
+		require.NoError(t, err, "Error [i:%d] %d", i, tt.packet)
+		require.Equal(t, len(tt.bytes), n, "Mismatched written [i:%d] %d", i, tt.packet)
+
+		time.Sleep(time.Millisecond)
+		r.Close()
+
+		require.Equal(t, tt.bytes, <-o, "Mismatched packet: [i:%d] %d", i, tt.bytes[0])
+		cl.Stop()
+	}
+}
+
+func TestClientWritePacketWriteNoConn(t *testing.T) {
+	c, _ := net.Pipe()
+	cl := NewClient(c, circ.NewReader(16, 4), circ.NewWriter(16, 4))
+	cl.w.SetPos(0, 16)
+	cl.Stop()
+
+	_, err := cl.WritePacket(pkTable[1].packet)
+	require.Error(t, err)
+	require.Equal(t, ErrConnectionClosed, err)
+}
+
+func TestClientWritePacketWriteError(t *testing.T) {
+	c, _ := net.Pipe()
+	cl := NewClient(c, circ.NewReader(16, 4), circ.NewWriter(16, 4))
+	cl.w.SetPos(0, 16)
+	cl.w.Stop()
+
+	_, err := cl.WritePacket(pkTable[1].packet)
+	require.Error(t, err)
+}
+
+func TestClientWritePacketInvalidPacket(t *testing.T) {
+	c, _ := net.Pipe()
+	cl := NewClient(c, circ.NewReader(16, 4), circ.NewWriter(16, 4))
+	cl.Start()
+
+	_, err := cl.WritePacket(packets.Packet{})
+	require.Error(t, err)
+}
+
+/////
+
+func TestInFlightSet(t *testing.T) {
+	cl := genClient()
+	cl.InFlight.Set(1, InFlightMessage{Packet: packets.Packet{}, Sent: 0})
+	require.NotNil(t, cl.InFlight.internal[1])
+	require.NotEqual(t, 0, cl.InFlight.internal[1].Sent)
+}
+
+func BenchmarkInFlightSet(b *testing.B) {
+	cl := genClient()
+	in := InFlightMessage{Packet: packets.Packet{}, Sent: 0}
+	for n := 0; n < b.N; n++ {
+		cl.InFlight.Set(1, in)
+	}
+}
+
+func TestInFlightGet(t *testing.T) {
+	cl := genClient()
+	cl.InFlight.Set(2, InFlightMessage{Packet: packets.Packet{}, Sent: 0})
+
+	msg, ok := cl.InFlight.Get(2)
+	require.Equal(t, true, ok)
+	require.NotEqual(t, 0, msg.Sent)
+}
+
+func BenchmarkInFlightGet(b *testing.B) {
+	cl := genClient()
+	cl.InFlight.Set(2, InFlightMessage{Packet: packets.Packet{}, Sent: 0})
+	for n := 0; n < b.N; n++ {
+		cl.InFlight.Get(2)
+	}
+}
+
+func TestInFlightGetAll(t *testing.T) {
+	cl := genClient()
+	cl.InFlight.Set(2, InFlightMessage{})
+
+	m := cl.InFlight.GetAll()
+	o := map[uint16]InFlightMessage{
+		2: InFlightMessage{},
+	}
+	require.Equal(t, o, m)
+}
+
+func BenchmarkInFlightGetAll(b *testing.B) {
+	cl := genClient()
+	cl.InFlight.Set(2, InFlightMessage{Packet: packets.Packet{}, Sent: 0})
+	for n := 0; n < b.N; n++ {
+		cl.InFlight.Get(2)
+	}
+}
+
+func TestInFlightDelete(t *testing.T) {
+	cl := genClient()
+	cl.InFlight.Set(3, InFlightMessage{Packet: packets.Packet{}, Sent: 0})
+	require.NotNil(t, cl.InFlight.internal[3])
+
+	cl.InFlight.Delete(3)
+	require.Equal(t, int64(0), cl.InFlight.internal[3].Sent)
+
+	_, ok := cl.InFlight.Get(3)
+	require.Equal(t, false, ok)
+}
+
+func BenchmarkInFlightDelete(b *testing.B) {
+	cl := genClient()
+	for n := 0; n < b.N; n++ {
+		cl.InFlight.Set(4, InFlightMessage{Packet: packets.Packet{}, Sent: 0})
+		cl.InFlight.Delete(4)
+	}
+}
+
+var (
+	pkTable = []struct {
+		bytes  []byte
+		packet packets.Packet
+	}{
+		{
+			bytes: []byte{
+				byte(packets.Connect << 4), 16, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				4,     // Protocol Version
+				0,     // Packet Flags
+				0, 60, // Keepalive
+				0, 4, // Client ID - MSB+LSB
+				'z', 'e', 'n', '3',
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Connect,
+					Remaining: 16,
+				},
+				ProtocolName:     []byte("MQTT"),
+				ProtocolVersion:  4,
+				CleanSession:     false,
+				Keepalive:        60,
+				ClientIdentifier: "zen3",
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Connack << 4), 2,
+				0,
+				packets.Accepted,
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Connack,
+					Remaining: 2,
+				},
+				SessionPresent: false,
+				ReturnCode:     packets.Accepted,
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Publish << 4), 18,
+				0, 5,
+				'a', '/', 'b', '/', 'c',
+				'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i',
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Publish,
+					Remaining: 18,
+				},
+				TopicName: "a/b/c",
+				Payload:   []byte("hello mochi"),
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Puback << 4), 2, // Fixed header
+				0, 11, // Packet ID - LSB+MSB
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Puback,
+					Remaining: 2,
+				},
+				PacketID: 11,
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Pubrec << 4), 2, // Fixed header
+				0, 12, // Packet ID - LSB+MSB
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Pubrec,
+					Remaining: 2,
+				},
+				PacketID: 12,
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Pubrel<<4) | 2, 2, // Fixed header
+				0, 12, // Packet ID - LSB+MSB
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Pubrel,
+					Remaining: 2,
+					Qos:       1,
+				},
+				PacketID: 12,
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Pubcomp << 4), 2, // Fixed header
+				0, 14, // Packet ID - LSB+MSB
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Pubcomp,
+					Remaining: 2,
+				},
+				PacketID: 14,
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Subscribe << 4), 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: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Subscribe,
+					Remaining: 30,
+				},
+				PacketID: 15,
+				Topics: []string{
+					"a/b",
+					"d/e/f/g/h/i",
+					"x/y/z",
+				},
+				Qoss: []byte{0, 1, 2},
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Suback << 4), 6, // Fixed header
+				0, 17, // Packet ID - LSB+MSB
+				0,    // Return Code QoS 0
+				1,    // Return Code QoS 1
+				2,    // Return Code QoS 2
+				0x80, // Return Code fail
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Suback,
+					Remaining: 6,
+				},
+				PacketID:    17,
+				ReturnCodes: []byte{0, 1, 2, 0x80},
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Unsubscribe << 4), 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: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Unsubscribe,
+					Remaining: 27,
+				},
+				PacketID: 35,
+				Topics: []string{
+					"a/b",
+					"d/e/f/g/h/i",
+					"x/y/z",
+				},
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Unsuback << 4), 2, // Fixed header
+				0, 37, // Packet ID - LSB+MSB
+
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Unsuback,
+					Remaining: 2,
+				},
+				PacketID: 37,
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Pingreq << 4), 0, // fixed header
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Pingreq,
+					Remaining: 0,
+				},
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Pingresp << 4), 0, // fixed header
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Pingresp,
+					Remaining: 0,
+				},
+			},
+		},
+		{
+			bytes: []byte{
+				byte(packets.Disconnect << 4), 0, // fixed header
+			},
+			packet: packets.Packet{
+				FixedHeader: packets.FixedHeader{
+					Type:      packets.Disconnect,
+					Remaining: 0,
+				},
+			},
+		},
+	}
+)




diff --git a/internal/listeners/listeners.go b/internal/listeners/listeners.go
new file mode 100644
index 0000000000000000000000000000000000000000..0fc5782cdb575d15eb79a9e2b13aab94f35633a3
--- /dev/null
+++ b/internal/listeners/listeners.go
@@ -0,0 +1,227 @@
+package listeners
+
+import (
+	"fmt"
+	"net"
+	"sync"
+
+	"github.com/mochi-co/mqtt/internal/auth"
+)
+
+// Config contains configuration values for a listener.
+type Config struct {
+	Auth auth.Controller // an authentication controller containing auth and ACL logic.
+	TLS  *TLS            // the TLS certficates and settings for the connection.
+}
+
+// TLS contains the TLS certificates and settings for the listener connection.
+type TLS struct {
+	// ...
+}
+
+// EstablishFunc is a callback function for establishing new clients.
+type EstablishFunc func(id string, c net.Conn, ac auth.Controller) error
+
+// CloseFunc is a callback function for closing all listener clients.
+type CloseFunc func(id string)
+
+// Listener is an interface for network listeners. A network listener listens
+// for incoming client connections and adds them to the server.
+type Listener interface {
+	SetConfig(*Config)   // set the listener config.
+	Listen() error       // open the network address.
+	Serve(EstablishFunc) // starting actively listening for new connections.
+	ID() string          // return the id of the listener.
+	Close(CloseFunc)     // stop and close the listener.
+}
+
+// Listeners contains the network listeners for the broker.
+type Listeners struct {
+	sync.RWMutex
+	wg       sync.WaitGroup      // a waitgroup that waits for all listeners to finish.
+	internal map[string]Listener // a map of active listeners.
+}
+
+// New returns a new instance of Listeners.
+func New() Listeners {
+	return Listeners{
+		internal: map[string]Listener{},
+	}
+}
+
+// Add adds a new listener to the listeners map, keyed on id.
+func (l *Listeners) Add(val Listener) {
+	l.Lock()
+	l.internal[val.ID()] = val
+	l.Unlock()
+}
+
+// Get returns the value of a listener if it exists.
+func (l *Listeners) Get(id string) (Listener, bool) {
+	l.RLock()
+	val, ok := l.internal[id]
+	l.RUnlock()
+	return val, ok
+}
+
+// Len returns the length of the listeners map.
+func (l *Listeners) Len() int {
+	l.RLock()
+	val := len(l.internal)
+	l.RUnlock()
+	return val
+}
+
+// Delete removes a listener from the internal map.
+func (l *Listeners) Delete(id string) {
+	l.Lock()
+	delete(l.internal, id)
+	l.Unlock()
+}
+
+// Serve starts a listener serving from the internal map.
+func (l *Listeners) Serve(id string, establisher EstablishFunc) error {
+	l.RLock()
+	listener := l.internal[id]
+	l.RUnlock()
+
+	// Start listening on the network address.
+	err := listener.Listen()
+	if err != nil {
+		return err
+	}
+
+	go func(e EstablishFunc) {
+		defer l.wg.Done()
+		l.wg.Add(1)
+		listener.Serve(e)
+
+	}(establisher)
+
+	return nil
+}
+
+// ServeAll starts all listeners serving from the internal map.
+func (l *Listeners) ServeAll(establisher EstablishFunc) error {
+	l.RLock()
+	i := 0
+	ids := make([]string, len(l.internal))
+	for id := range l.internal {
+		ids[i] = id
+		i++
+	}
+	l.RUnlock()
+
+	for _, id := range ids {
+		err := l.Serve(id, establisher)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Close stops a listener from the internal map.
+func (l *Listeners) Close(id string, closer CloseFunc) {
+	l.RLock()
+	listener := l.internal[id]
+	l.RUnlock()
+	listener.Close(closer)
+}
+
+// CloseAll iterates and closes all registered listeners.
+func (l *Listeners) CloseAll(closer CloseFunc) {
+	l.RLock()
+	i := 0
+	ids := make([]string, len(l.internal))
+	for id := range l.internal {
+		ids[i] = id
+		i++
+	}
+	l.RUnlock()
+
+	for _, id := range ids {
+		l.Close(id, closer)
+	}
+	l.wg.Wait()
+}
+
+// MockCloser is a function signature which can be used in testing.
+func MockCloser(id string) {}
+
+// MockEstablisher is a function signature which can be used in testing.
+func MockEstablisher(id string, c net.Conn, ac auth.Controller) error {
+	return nil
+}
+
+// MockListener is a mock listener for establishing client connections.
+type MockListener struct {
+	sync.RWMutex
+	id          string
+	Config      *Config
+	address     string
+	IsListening bool
+	IsServing   bool
+	done        chan bool
+	errListen   bool
+}
+
+// NewMockListener returns a new instance of MockListener
+func NewMockListener(id, address string) *MockListener {
+	return &MockListener{
+		id:      id,
+		address: address,
+		done:    make(chan bool),
+	}
+}
+
+// Serve serves the mock listener.
+func (l *MockListener) Serve(establisher EstablishFunc) {
+	l.Lock()
+	l.IsServing = true
+	l.Unlock()
+DONE:
+	for {
+		select {
+		case <-l.done:
+			break DONE
+		}
+	}
+}
+
+// SetConfig sets the configuration values of the mock listener.
+func (l *MockListener) Listen() error {
+	if l.errListen {
+		return fmt.Errorf("listen failure")
+	}
+
+	l.Lock()
+	l.IsListening = true
+	l.Unlock()
+	return nil
+}
+
+// SetConfig sets the configuration values of the mock listener.
+func (l *MockListener) SetConfig(config *Config) {
+	l.Lock()
+	l.Config = config
+	l.Unlock()
+}
+
+// ID returns the id of the mock listener.
+func (l *MockListener) ID() string {
+	l.RLock()
+	id := l.id
+	l.RUnlock()
+	return id
+}
+
+// Close closes the mock listener.
+func (l *MockListener) Close(closer CloseFunc) {
+	l.Lock()
+	defer l.Unlock()
+	l.IsServing = false
+	closer(l.id)
+	close(l.done)
+}




diff --git a/internal/listeners/listeners_test.go b/internal/listeners/listeners_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5f0259a54cde4946ef1ab0371058f56d8f53c082
--- /dev/null
+++ b/internal/listeners/listeners_test.go
@@ -0,0 +1,280 @@
+package listeners
+
+import (
+	"net"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/mochi-co/mqtt/internal/auth"
+)
+
+func TestMockEstablisher(t *testing.T) {
+	_, w := net.Pipe()
+	err := MockEstablisher("t1", w, new(auth.Allow))
+	require.NoError(t, err)
+	w.Close()
+}
+
+func TestNewMockListener(t *testing.T) {
+	mocked := NewMockListener("t1", ":1882")
+	require.Equal(t, "t1", mocked.id)
+	require.Equal(t, ":1882", mocked.address)
+}
+
+func TestNewMockListenerListen(t *testing.T) {
+	mocked := NewMockListener("t1", ":1882")
+	require.Equal(t, "t1", mocked.id)
+	require.Equal(t, ":1882", mocked.address)
+
+	require.Equal(t, false, mocked.IsListening)
+	mocked.Listen()
+	require.Equal(t, true, mocked.IsListening)
+}
+
+func TestMockListenerServe(t *testing.T) {
+	mocked := NewMockListener("t1", ":1882")
+	require.Equal(t, false, mocked.IsServing)
+
+	o := make(chan bool)
+	go func(o chan bool) {
+		mocked.Serve(MockEstablisher)
+		o <- true
+	}(o)
+
+	time.Sleep(time.Millisecond) // easy non-channel wait for start of serving
+	require.Equal(t, true, mocked.IsServing)
+
+	var closed bool
+	mocked.Close(func(id string) {
+		closed = true
+	})
+	require.Equal(t, true, closed)
+	<-o
+
+	mocked.Listen()
+}
+
+func TestMockListenerSetConfig(t *testing.T) {
+	mocked := NewMockListener("t1", ":1883")
+	mocked.SetConfig(new(Config))
+	require.NotNil(t, mocked.Config)
+}
+
+func TestMockListenerClose(t *testing.T) {
+	mocked := NewMockListener("t1", ":1882")
+	var closed bool
+	mocked.Close(func(id string) {
+		closed = true
+	})
+	require.Equal(t, true, closed)
+}
+
+func TestNew(t *testing.T) {
+	l := New()
+	require.NotNil(t, l.internal)
+}
+
+func BenchmarkNewListeners(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		New()
+	}
+}
+
+func TestAddListener(t *testing.T) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	require.Contains(t, l.internal, "t1")
+}
+
+func BenchmarkAddListener(b *testing.B) {
+	l := New()
+	mocked := NewMockListener("t1", ":1882")
+	for n := 0; n < b.N; n++ {
+		l.Add(mocked)
+	}
+}
+
+func TestGetListener(t *testing.T) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	l.Add(NewMockListener("t2", ":1882"))
+	require.Contains(t, l.internal, "t1")
+	require.Contains(t, l.internal, "t2")
+
+	g, ok := l.Get("t1")
+	require.Equal(t, true, ok)
+	require.Equal(t, g.ID(), "t1")
+}
+
+func BenchmarkGetListener(b *testing.B) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	for n := 0; n < b.N; n++ {
+		l.Get("t1")
+	}
+}
+
+func TestLenListener(t *testing.T) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	l.Add(NewMockListener("t2", ":1882"))
+	require.Contains(t, l.internal, "t1")
+	require.Contains(t, l.internal, "t2")
+	require.Equal(t, 2, l.Len())
+}
+
+func BenchmarkLenListener(b *testing.B) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	for n := 0; n < b.N; n++ {
+		l.Len()
+	}
+}
+
+func TestDeleteListener(t *testing.T) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	require.Contains(t, l.internal, "t1")
+
+	l.Delete("t1")
+	_, ok := l.Get("t1")
+	require.Equal(t, false, ok)
+	require.Nil(t, l.internal["t1"])
+}
+
+func BenchmarkDeleteListener(b *testing.B) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	for n := 0; n < b.N; n++ {
+		l.Delete("t1")
+	}
+}
+
+func TestServeListener(t *testing.T) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	l.Serve("t1", MockEstablisher)
+	time.Sleep(time.Millisecond)
+	require.Equal(t, true, l.internal["t1"].(*MockListener).IsServing)
+
+	l.Close("t1", MockCloser)
+	require.Equal(t, false, l.internal["t1"].(*MockListener).IsServing)
+}
+
+func TestServeListenerFailure(t *testing.T) {
+	l := New()
+	m := NewMockListener("t1", ":1882")
+	m.errListen = true
+	l.Add(m)
+	err := l.Serve("t1", MockEstablisher)
+	require.Error(t, err)
+}
+
+func BenchmarkServeListener(b *testing.B) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	for n := 0; n < b.N; n++ {
+		l.Serve("t1", MockEstablisher)
+	}
+}
+
+func TestServeAllListeners(t *testing.T) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	l.Add(NewMockListener("t2", ":1882"))
+	l.Add(NewMockListener("t3", ":1882"))
+	err := l.ServeAll(MockEstablisher)
+	require.NoError(t, err)
+	time.Sleep(time.Millisecond)
+
+	require.Equal(t, true, l.internal["t1"].(*MockListener).IsServing)
+	require.Equal(t, true, l.internal["t2"].(*MockListener).IsServing)
+	require.Equal(t, true, l.internal["t3"].(*MockListener).IsServing)
+
+	l.Close("t1", MockCloser)
+	l.Close("t2", MockCloser)
+	l.Close("t3", MockCloser)
+
+	require.Equal(t, false, l.internal["t1"].(*MockListener).IsServing)
+	require.Equal(t, false, l.internal["t2"].(*MockListener).IsServing)
+	require.Equal(t, false, l.internal["t3"].(*MockListener).IsServing)
+}
+
+func TestServeAllListenersFailure(t *testing.T) {
+	l := New()
+	m := NewMockListener("t1", ":1882")
+	m.errListen = true
+	l.Add(m)
+	err := l.ServeAll(MockEstablisher)
+	require.Error(t, err)
+}
+
+func BenchmarkServeAllListeners(b *testing.B) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	l.Add(NewMockListener("t2", ":1883"))
+	l.Add(NewMockListener("t3", ":1884"))
+	for n := 0; n < b.N; n++ {
+		l.ServeAll(MockEstablisher)
+	}
+}
+
+func TestCloseListener(t *testing.T) {
+	l := New()
+	mocked := NewMockListener("t1", ":1882")
+	l.Add(mocked)
+	l.Serve("t1", MockEstablisher)
+	time.Sleep(time.Millisecond)
+	var closed bool
+	l.Close("t1", func(id string) {
+		closed = true
+	})
+	require.Equal(t, true, closed)
+}
+
+func BenchmarkCloseListener(b *testing.B) {
+	l := New()
+	mocked := NewMockListener("t1", ":1882")
+	l.Add(mocked)
+	l.Serve("t1", MockEstablisher)
+	for n := 0; n < b.N; n++ {
+		l.internal["t1"].(*MockListener).done = make(chan bool)
+		l.Close("t1", MockCloser)
+	}
+}
+
+func TestCloseAllListeners(t *testing.T) {
+	l := New()
+	l.Add(NewMockListener("t1", ":1882"))
+	l.Add(NewMockListener("t2", ":1882"))
+	l.Add(NewMockListener("t3", ":1882"))
+	l.ServeAll(MockEstablisher)
+	time.Sleep(time.Millisecond)
+	require.Equal(t, true, l.internal["t1"].(*MockListener).IsServing)
+	require.Equal(t, true, l.internal["t2"].(*MockListener).IsServing)
+	require.Equal(t, true, l.internal["t3"].(*MockListener).IsServing)
+
+	closed := make(map[string]bool)
+	l.CloseAll(func(id string) {
+		closed[id] = true
+	})
+	require.Contains(t, closed, "t1")
+	require.Contains(t, closed, "t2")
+	require.Contains(t, closed, "t3")
+	require.Equal(t, true, closed["t1"])
+	require.Equal(t, true, closed["t2"])
+	require.Equal(t, true, closed["t3"])
+}
+
+func BenchmarkCloseAllListeners(b *testing.B) {
+	l := New()
+	mocked := NewMockListener("t1", ":1882")
+	l.Add(mocked)
+	l.Serve("t1", MockEstablisher)
+	for n := 0; n < b.N; n++ {
+		l.internal["t1"].(*MockListener).done = make(chan bool)
+		l.Close("t1", MockCloser)
+	}
+}




diff --git a/internal/listeners/tcp.go b/internal/listeners/tcp.go
new file mode 100644
index 0000000000000000000000000000000000000000..fc2e26830c08dd5f379511a0a768d9c09b3e2510
--- /dev/null
+++ b/internal/listeners/tcp.go
@@ -0,0 +1,118 @@
+package listeners
+
+import (
+	"net"
+	"sync"
+	"sync/atomic"
+
+	"github.com/mochi-co/mqtt/internal/auth"
+)
+
+// TCP is a listener for establishing client connections on basic TCP protocol.
+type TCP struct {
+	sync.RWMutex
+	id       string       // the internal id of the listener.
+	config   *Config      // configuration values for the listener.
+	protocol string       // the TCP protocol to use.
+	address  string       // the network address to bind to.
+	listen   net.Listener // a net.Listener which will listen for new clients.
+	done     chan bool    //  a channel which indicates the process is done and should end.
+	start    *sync.Once   // ensure the serve methods are only called once.
+	end      *sync.Once   // ensure the close methods are only called once.
+	ending   int64        // indicates no more connections should be established.
+}
+
+// NewTCP initialises and returns a new TCP listener, listening on an address.
+func NewTCP(id, address string) *TCP {
+	return &TCP{
+		id:       id,
+		protocol: "tcp",
+		address:  address,
+		done:     make(chan bool),
+		start:    new(sync.Once),
+		end:      new(sync.Once),
+		config: &Config{ // default configuration.
+			Auth: new(auth.Allow),
+			TLS:  new(TLS),
+		},
+	}
+}
+
+// SetConfig sets the configuration values for the listener config.
+func (l *TCP) SetConfig(config *Config) {
+	l.Lock()
+	if config != nil {
+		l.config = config
+
+		// If a config has been passed without an auth controller,
+		// it may be a mistake, so disallow all traffic.
+		if l.config.Auth == nil {
+			l.config.Auth = new(auth.Disallow)
+		}
+	}
+
+	l.Unlock()
+}
+
+// ID returns the id of the listener.
+func (l *TCP) ID() string {
+	l.RLock()
+	id := l.id
+	l.RUnlock()
+	return id
+}
+
+// Listen starts listening on the listener's network address.
+func (l *TCP) Listen() error {
+	var err error
+	l.listen, err = net.Listen(l.protocol, l.address)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Serve starts waiting for new TCP connections, and calls the connection
+// establishment callback for any received.
+func (l *TCP) Serve(establish EstablishFunc) {
+	l.start.Do(func() {
+		for {
+			select {
+			case <-l.done:
+				return
+
+			default:
+				conn, err := l.listen.Accept()
+				if err != nil {
+					return
+				}
+
+				if atomic.LoadInt64(&l.ending) == 1 {
+					return
+				}
+
+				go establish(l.id, conn, l.config.Auth)
+			}
+		}
+	})
+}
+
+// Close closes the listener and any client connections.
+func (l *TCP) Close(closeClients CloseFunc) {
+	l.Lock()
+	defer l.Unlock()
+
+	l.end.Do(func() {
+		atomic.StoreInt64(&l.ending, 1)
+		close(l.done)
+		closeClients(l.id)
+	})
+
+	if l.listen != nil {
+		err := l.listen.Close()
+		if err != nil {
+			return
+		}
+	}
+}




diff --git a/internal/listeners/tcp_test.go b/internal/listeners/tcp_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..cef458b3ceb885110bd85c842cc6d74ff368b5a4
--- /dev/null
+++ b/internal/listeners/tcp_test.go
@@ -0,0 +1,164 @@
+package listeners
+
+import (
+	"errors"
+	"net"
+	"testing"
+	"time"
+
+	"github.com/mochi-co/mqtt/internal/auth"
+	"github.com/stretchr/testify/require"
+)
+
+const (
+	testPort = ":22222"
+)
+
+func TestNewTCP(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	require.Equal(t, "t1", l.id)
+	require.Equal(t, testPort, l.address)
+	require.NotNil(t, l.end)
+	require.NotNil(t, l.done)
+}
+
+func BenchmarkNewTCP(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		NewTCP("t1", testPort)
+	}
+}
+
+func TestTCPSetConfig(t *testing.T) {
+	l := NewTCP("t1", testPort)
+
+	l.SetConfig(&Config{
+		Auth: new(auth.Allow),
+	})
+	require.NotNil(t, l.config)
+	require.NotNil(t, l.config.Auth)
+	require.Equal(t, new(auth.Allow), l.config.Auth)
+
+	// Switch to disallow on bad config set.
+	l.SetConfig(new(Config))
+	require.NotNil(t, l.config)
+	require.NotNil(t, l.config.Auth)
+	require.Equal(t, new(auth.Disallow), l.config.Auth)
+}
+
+func BenchmarkTCPSetConfig(b *testing.B) {
+	l := NewTCP("t1", testPort)
+	for n := 0; n < b.N; n++ {
+		l.SetConfig(new(Config))
+	}
+}
+
+func TestTCPID(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	require.Equal(t, "t1", l.ID())
+}
+
+func BenchmarkTCPID(b *testing.B) {
+	l := NewTCP("t1", testPort)
+	for n := 0; n < b.N; n++ {
+		l.ID()
+	}
+}
+
+func TestTCPListen(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	err := l.Listen()
+	require.NoError(t, err)
+
+	l2 := NewTCP("t2", testPort)
+	err = l2.Listen()
+	require.Error(t, err)
+	l.listen.Close()
+}
+
+func TestTCPServeAndClose(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	err := l.Listen()
+	require.NoError(t, err)
+
+	o := make(chan bool)
+	go func(o chan bool) {
+		l.Serve(MockEstablisher)
+		o <- true
+	}(o)
+	time.Sleep(time.Millisecond)
+	var closed bool
+	l.Close(func(id string) {
+		closed = true
+	})
+	require.Equal(t, true, closed)
+	<-o
+}
+
+func TestTCPCloseError(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	err := l.Listen()
+	require.NoError(t, err)
+	o := make(chan bool)
+	go func(o chan bool) {
+		l.Serve(MockEstablisher)
+		o <- true
+	}(o)
+
+	time.Sleep(time.Millisecond)
+	l.listen.Close()
+	l.Close(MockCloser)
+	<-o
+}
+
+func TestTCPServeEnd(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	err := l.Listen()
+	require.NoError(t, err)
+
+	l.Close(MockCloser)
+	l.Serve(func(id string, c net.Conn, ac auth.Controller) error {
+		return nil
+	})
+}
+
+func TestTCPEstablishThenError(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	err := l.Listen()
+	require.NoError(t, err)
+
+	o := make(chan bool)
+	established := make(chan bool)
+	go func() {
+		l.Serve(func(id string, c net.Conn, ac auth.Controller) error {
+			established <- true
+			return errors.New("testing") // return an error to exit immediately
+		})
+		o <- true
+	}()
+
+	time.Sleep(time.Millisecond)
+	net.Dial(l.protocol, l.listen.Addr().String())
+	require.Equal(t, true, <-established)
+	l.Close(MockCloser)
+	<-o
+}
+
+func TestTCPEstablishButEnding(t *testing.T) {
+	l := NewTCP("t1", testPort)
+	err := l.Listen()
+	require.NoError(t, err)
+	l.ending = 1
+
+	o := make(chan bool)
+	go func() {
+		l.Serve(func(id string, c net.Conn, ac auth.Controller) error {
+			return nil
+		})
+		o <- true
+	}()
+
+	net.Dial(l.protocol, l.listen.Addr().String())
+
+	time.Sleep(time.Millisecond)
+	<-o
+}




diff --git a/internal/listeners/websocket.go b/internal/listeners/websocket.go
new file mode 100644
index 0000000000000000000000000000000000000000..5690c568490c5151ef94eea7a4fc122dab7ddf62
--- /dev/null
+++ b/internal/listeners/websocket.go
@@ -0,0 +1,3 @@
+package listeners
+
+// https://github.com/gobwas/ws




diff --git a/internal/packets/codec.go b/internal/packets/codec.go
new file mode 100644
index 0000000000000000000000000000000000000000..ed615d5665ba402f8fee7dfc1accd0c9c653882f
--- /dev/null
+++ b/internal/packets/codec.go
@@ -0,0 +1,114 @@
+package packets
+
+import (
+	"encoding/binary"
+	"unicode/utf8"
+	"unsafe"
+)
+
+// bytesToString provides a zero-alloc, no-copy byte to string conversion.
+// via https://github.com/golang/go/issues/25484#issuecomment-391415660
+func bytesToString(bs []byte) string {
+	return *(*string)(unsafe.Pointer(&bs))
+}
+
+// decodeUint16 extracts the value of two bytes from a byte array.
+func decodeUint16(buf []byte, offset int) (uint16, int, error) {
+	if len(buf) < offset+2 {
+		return 0, 0, ErrOffsetUintOutOfRange
+	}
+
+	return binary.BigEndian.Uint16(buf[offset : offset+2]), offset + 2, nil
+}
+
+// decodeString extracts a string from a byte array, beginning at an offset.
+func decodeString(buf []byte, offset int) (string, int, error) {
+	b, n, err := decodeBytes(buf, offset)
+	if err != nil {
+		return "", 0, err
+	}
+
+	return bytesToString(b), n, nil
+}
+
+// decodeBytes extracts a byte array from a byte array, beginning at an offset. Used primarily for message payloads.
+func decodeBytes(buf []byte, offset int) ([]byte, int, error) {
+	length, next, err := decodeUint16(buf, offset)
+	if err != nil {
+		return make([]byte, 0, 0), 0, err
+	}
+
+	if next+int(length) > len(buf) {
+		return make([]byte, 0, 0), 0, ErrOffsetStrOutOfRange
+	}
+
+	if !validUTF8(buf[next : next+int(length)]) {
+		return make([]byte, 0, 0), 0, ErrOffsetStrInvalidUTF8
+	}
+
+	return buf[next : next+int(length)], next + int(length), nil
+}
+
+// decodeByte extracts the value of a byte from a byte array.
+func decodeByte(buf []byte, offset int) (byte, int, error) {
+	if len(buf) <= offset {
+		return 0, 0, ErrOffsetByteOutOfRange
+	}
+	return buf[offset], offset + 1, nil
+}
+
+// decodeByteBool extracts the value of a byte from a byte array and returns a bool.
+func decodeByteBool(buf []byte, offset int) (bool, int, error) {
+	if len(buf) <= offset {
+		return false, 0, ErrOffsetBoolOutOfRange
+	}
+	return 1&buf[offset] > 0, offset + 1, nil
+}
+
+// encodeBool returns a byte instead of a bool.
+func encodeBool(b bool) byte {
+	if b {
+		return 1
+	}
+	return 0
+}
+
+// encodeBytes encodes a byte array to a byte array. Used primarily for message payloads.
+func encodeBytes(val []byte) []byte {
+	// In many circumstances the number of bytes being encoded is small.
+	// Setting the cap to a low amount allows us to account for those with
+	// triggering an alloc on append unless we need to.
+	buf := make([]byte, 2, 32)
+	binary.BigEndian.PutUint16(buf, uint16(len(val)))
+	return append(buf, val...)
+}
+
+// encodeUint16 encodes a uint16 value to a byte array.
+func encodeUint16(val uint16) []byte {
+	buf := make([]byte, 2)
+	binary.BigEndian.PutUint16(buf, val)
+	return buf
+}
+
+// encodeString encodes a string to a byte array.
+func encodeString(val string) []byte {
+	// Like encodeBytes, we set the cap to a small number to avoid
+	// triggering an alloc on append unless we absolutely need to.
+	buf := make([]byte, 2, 32)
+	binary.BigEndian.PutUint16(buf, uint16(len(val)))
+	return append(buf, []byte(val)...)
+}
+
+// validUTF8 checks if the byte array contains valid UTF-8 characters, specifically
+// conforming to the MQTT specification requirements.
+func validUTF8(b []byte) bool {
+	// [MQTT-1.4.0-1] The character data in a UTF-8 encoded string MUST be well-formed UTF-8...
+	if !utf8.Valid(b) {
+		return false
+	}
+
+	// [MQTT-1.4.0-2] A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000...
+	// ...
+	return true
+
+}




diff --git a/internal/packets/codec_test.go b/internal/packets/codec_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2cd0438cc2928b630462dedda5af95504d1442df
--- /dev/null
+++ b/internal/packets/codec_test.go
@@ -0,0 +1,383 @@
+package packets
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestBytesToString(t *testing.T) {
+	b := []byte{'a', 'b', 'c'}
+	require.Equal(t, "abc", bytesToString(b))
+}
+
+func BenchmarkBytesToString(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		bytesToString([]byte{'a', 'b', 'c'})
+	}
+}
+
+func TestDecodeString(t *testing.T) {
+	expect := []struct {
+		rawBytes   []byte
+		result     []string
+		offset     int
+		shouldFail bool
+	}{
+		{
+			offset:   0,
+			rawBytes: []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97},
+			result:   []string{"a/b/c/d", "a"},
+		},
+		{
+			offset: 14,
+			rawBytes: []byte{
+				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
+				'h', 'e', 'y', // Client ID "zen"},
+			},
+			result: []string{"hey"},
+		},
+
+		{
+			offset:   2,
+			rawBytes: []byte{0, 0, 0, 23, 49, 47, 50, 47, 51, 47, 52, 47, 97, 47, 98, 47, 99, 47, 100, 47, 101, 47, 94, 47, 64, 47, 33, 97},
+			result:   []string{"1/2/3/4/a/b/c/d/e/^/@/!", "a"},
+		},
+		{
+			offset:   0,
+			rawBytes: []byte{0, 5, 120, 47, 121, 47, 122, 33, 64, 35, 36, 37, 94, 38},
+			result:   []string{"x/y/z", "!@#$%^&"},
+		},
+		{
+			offset:     0,
+			rawBytes:   []byte{0, 9, 'a', '/', 'b', '/', 'c', '/', 'd', 'z'},
+			result:     []string{"a/b/c/d", "z"},
+			shouldFail: true,
+		},
+		{
+			offset:     5,
+			rawBytes:   []byte{0, 7, 97, 47, 98, 47, 'x'},
+			result:     []string{"a/b/c/d", "x"},
+			shouldFail: true,
+		},
+		{
+			offset:     9,
+			rawBytes:   []byte{0, 7, 97, 47, 98, 47, 'y'},
+			result:     []string{"a/b/c/d", "y"},
+			shouldFail: true,
+		},
+		{
+			offset: 17,
+			rawBytes: []byte{
+				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', 'n', // Client ID "zen"
+				0, 6, // Will Topic - MSB+LSB
+				'l',
+			},
+			result:     []string{"lwt"},
+			shouldFail: true,
+		},
+	}
+
+	for i, wanted := range expect {
+		result, _, err := decodeString(wanted.rawBytes, wanted.offset)
+		if wanted.shouldFail {
+			require.Error(t, err, "Expected error decoding string [i:%d]", i)
+			continue
+		}
+
+		require.NoError(t, err, "Error decoding string [i:%d]", i)
+		require.Equal(t, wanted.result[0], result, "Incorrect decoded value [i:%d]", i)
+	}
+}
+
+func BenchmarkDecodeString(b *testing.B) {
+	in := []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97}
+	for n := 0; n < b.N; n++ {
+		decodeString(in, 0)
+	}
+}
+
+func TestDecodeBytes(t *testing.T) {
+	expect := []struct {
+		rawBytes   []byte
+		result     []uint8
+		next       int
+		offset     int
+		shouldFail bool
+	}{
+		{
+			rawBytes: []byte{0, 4, 77, 81, 84, 84, 4, 194, 0, 50, 0, 36, 49, 53, 52}, // ... truncated connect packet (clean session)
+			result:   []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
+			next:     6,
+			offset:   0,
+		},
+		{
+			rawBytes: []byte{0, 4, 77, 81, 84, 84, 4, 192, 0, 50, 0, 36, 49, 53, 52, 50}, // ... truncated connect packet, only checking start
+			result:   []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
+			next:     6,
+			offset:   0,
+		},
+		{
+			rawBytes:   []byte{0, 4, 77, 81},
+			result:     []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
+			offset:     0,
+			shouldFail: true,
+		},
+		{
+			rawBytes:   []byte{0, 4, 77, 81},
+			result:     []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
+			offset:     8,
+			shouldFail: true,
+		},
+		{
+			rawBytes:   []byte{0, 4, 77, 81},
+			result:     []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
+			offset:     0,
+			shouldFail: true,
+		},
+	}
+
+	for i, wanted := range expect {
+		result, _, err := decodeBytes(wanted.rawBytes, wanted.offset)
+		if wanted.shouldFail {
+			require.Error(t, err, "Expected error decoding bytes [i:%d]", i)
+			continue
+		}
+
+		require.NoError(t, err, "Error decoding bytes [i:%d]", i)
+		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
+	}
+}
+
+func BenchmarkDecodeBytes(b *testing.B) {
+	in := []byte{0, 4, 77, 81, 84, 84, 4, 194, 0, 50, 0, 36, 49, 53, 52}
+	for n := 0; n < b.N; n++ {
+		decodeBytes(in, 0)
+	}
+}
+
+func TestDecodeByte(t *testing.T) {
+	expect := []struct {
+		rawBytes   []byte
+		result     uint8
+		offset     int
+		shouldFail bool
+	}{
+		{
+			rawBytes: []byte{0, 4, 77, 81, 84, 84}, // nonsense slice of bytes
+			result:   uint8(0x00),
+			offset:   0,
+		},
+		{
+			rawBytes: []byte{0, 4, 77, 81, 84, 84},
+			result:   uint8(0x04),
+			offset:   1,
+		},
+		{
+			rawBytes: []byte{0, 4, 77, 81, 84, 84},
+			result:   uint8(0x4d),
+			offset:   2,
+		},
+		{
+			rawBytes: []byte{0, 4, 77, 81, 84, 84},
+			result:   uint8(0x51),
+			offset:   3,
+		},
+		{
+			rawBytes:   []byte{0, 4, 77, 80, 82, 84},
+			result:     uint8(0x00),
+			offset:     8,
+			shouldFail: true,
+		},
+	}
+
+	for i, wanted := range expect {
+		result, offset, err := decodeByte(wanted.rawBytes, wanted.offset)
+		if wanted.shouldFail {
+			require.Error(t, err, "Expected error decoding byte [i:%d]", i)
+			continue
+		}
+
+		require.NoError(t, err, "Error decoding byte [i:%d]", i)
+		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
+		require.Equal(t, i+1, offset, "Incorrect offset value [i:%d]", i)
+	}
+}
+
+func BenchmarkDecodeByte(b *testing.B) {
+	in := []byte{0, 4, 77, 81, 84, 84}
+	for n := 0; n < b.N; n++ {
+		decodeByte(in, 0)
+	}
+}
+
+func TestDecodeUint16(t *testing.T) {
+	expect := []struct {
+		rawBytes   []byte
+		result     uint16
+		offset     int
+		shouldFail bool
+	}{
+		{
+			rawBytes: []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97},
+			result:   uint16(0x07),
+			offset:   0,
+		},
+		{
+			rawBytes: []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97},
+			result:   uint16(0x761),
+			offset:   1,
+		},
+		{
+			rawBytes:   []byte{0, 7, 255, 47},
+			result:     uint16(0x761),
+			offset:     8,
+			shouldFail: true,
+		},
+	}
+
+	for i, wanted := range expect {
+		result, offset, err := decodeUint16(wanted.rawBytes, wanted.offset)
+		if wanted.shouldFail {
+			require.Error(t, err, "Expected error decoding uint16 [i:%d]", i)
+			continue
+		}
+
+		require.NoError(t, err, "Error decoding uint16 [i:%d]", i)
+		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
+		require.Equal(t, i+2, offset, "Incorrect offset value [i:%d]", i)
+	}
+}
+
+func BenchmarkDecodeUint16(b *testing.B) {
+	in := []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97}
+	for n := 0; n < b.N; n++ {
+		decodeUint16(in, 0)
+	}
+}
+
+func TestDecodeByteBool(t *testing.T) {
+	expect := []struct {
+		rawBytes   []byte
+		result     bool
+		offset     int
+		shouldFail bool
+	}{
+		{
+			rawBytes: []byte{0x00, 0x00},
+			result:   false,
+		},
+		{
+			rawBytes: []byte{0x01, 0x00},
+			result:   true,
+		},
+		{
+			rawBytes:   []byte{0x01, 0x00},
+			offset:     5,
+			shouldFail: true,
+		},
+	}
+
+	for i, wanted := range expect {
+		result, offset, err := decodeByteBool(wanted.rawBytes, wanted.offset)
+		if wanted.shouldFail {
+			require.Error(t, err, "Expected error decoding byte bool [i:%d]", i)
+			continue
+		}
+
+		require.NoError(t, err, "Error decoding byte bool [i:%d]", i)
+		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
+		require.Equal(t, 1, offset, "Incorrect offset value [i:%d]", i)
+	}
+}
+
+func BenchmarkDecodeByteBool(b *testing.B) {
+	in := []byte{0x00, 0x00}
+	for n := 0; n < b.N; n++ {
+		decodeByteBool(in, 0)
+	}
+}
+
+func TestEncodeBool(t *testing.T) {
+	result := encodeBool(true)
+	require.Equal(t, byte(1), result, "Incorrect encoded value; not true")
+
+	result = encodeBool(false)
+	require.Equal(t, byte(0), result, "Incorrect encoded value; not false")
+
+	// Check failure.
+	result = encodeBool(false)
+	require.NotEqual(t, byte(1), result, "Expected failure, incorrect encoded value")
+}
+
+func BenchmarkEncodeBool(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		encodeBool(true)
+	}
+}
+
+func TestEncodeBytes(t *testing.T) {
+	result := encodeBytes([]byte("testing"))
+	require.Equal(t, []uint8{0, 7, 116, 101, 115, 116, 105, 110, 103}, result, "Incorrect encoded value")
+
+	result = encodeBytes([]byte("testing"))
+	require.NotEqual(t, []uint8{0, 7, 113, 101, 115, 116, 105, 110, 103}, result, "Expected failure, incorrect encoded value")
+}
+
+func BenchmarkEncodeBytes(b *testing.B) {
+	bb := []byte("testing")
+	for n := 0; n < b.N; n++ {
+		encodeBytes(bb)
+	}
+}
+
+func TestEncodeUint16(t *testing.T) {
+	result := encodeUint16(0)
+	require.Equal(t, []byte{0x00, 0x00}, result, "Incorrect encoded value, 0")
+
+	result = encodeUint16(32767)
+	require.Equal(t, []byte{0x7f, 0xff}, result, "Incorrect encoded value, 32767")
+
+	result = encodeUint16(65535)
+	require.Equal(t, []byte{0xff, 0xff}, result, "Incorrect encoded value, 65535")
+}
+
+func BenchmarkEncodeUint16(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		encodeUint16(32767)
+	}
+}
+
+func TestEncodeString(t *testing.T) {
+	result := encodeString("testing")
+	require.Equal(t, []uint8{0x00, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67}, result, "Incorrect encoded value, testing")
+
+	result = encodeString("")
+	require.Equal(t, []uint8{0x00, 0x00}, result, "Incorrect encoded value, null")
+
+	result = encodeString("a")
+	require.Equal(t, []uint8{0x00, 0x01, 0x61}, result, "Incorrect encoded value, a")
+
+	result = encodeString("b")
+	require.NotEqual(t, []uint8{0x00, 0x00}, result, "Expected failure, incorrect encoded value, b")
+
+}
+
+func BenchmarkEncodeString(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		encodeString("benchmarking")
+	}
+}




diff --git a/internal/packets/fixedheader.go b/internal/packets/fixedheader.go
new file mode 100644
index 0000000000000000000000000000000000000000..eb3c836069915165ef5f46eaa4a306cee78712bc
--- /dev/null
+++ b/internal/packets/fixedheader.go
@@ -0,0 +1,64 @@
+package packets
+
+import (
+	"bytes"
+)
+
+// FixedHeader contains the values of the fixed header portion of the MQTT packet.
+type FixedHeader struct {
+	Type      byte // the type of the packet (PUBLISH, SUBSCRIBE, etc) from bits 7 - 4 (byte 1).
+	Dup       bool // indicates if the packet was already sent at an earlier time.
+	Qos       byte // indicates the quality of service expected.
+	Retain    bool // whether the message should be retained.
+	Remaining int  // the number of remaining bytes in the payload.
+}
+
+// Encode encodes the FixedHeader and returns a bytes buffer.
+func (fh *FixedHeader) Encode(buf *bytes.Buffer) {
+	buf.WriteByte(fh.Type<<4 | encodeBool(fh.Dup)<<3 | fh.Qos<<1 | encodeBool(fh.Retain))
+	encodeLength(buf, fh.Remaining)
+}
+
+// decode extracts the specification bits from the header byte.
+func (fh *FixedHeader) Decode(headerByte byte) error {
+	fh.Type = headerByte >> 4 // Get the message type from the first 4 bytes.
+
+	// @SPEC [MQTT-2.2.2-1]
+	// Where a flag bit is marked as “Reserved” in Table 2.2 - Flag Bits,
+	// it is reserved for future use and MUST be set to the value listed in that table.
+	switch fh.Type {
+	case Publish:
+		fh.Dup = (headerByte>>3)&0x01 > 0 // Extract flags. Check if message is duplicate.
+		fh.Qos = (headerByte >> 1) & 0x03 // Extract QoS flag.
+		fh.Retain = headerByte&0x01 > 0   // Extract retain flag.
+	case Pubrel:
+		fh.Qos = (headerByte >> 1) & 0x03
+	case Subscribe:
+		fh.Qos = (headerByte >> 1) & 0x03
+	case Unsubscribe:
+		fh.Qos = (headerByte >> 1) & 0x03
+	default:
+		// [MQTT-2.2.2-2]
+		// If invalid flags are received, the receiver MUST close the Network Connection.
+		if (headerByte>>3)&0x01 > 0 || (headerByte>>1)&0x03 > 0 || headerByte&0x01 > 0 {
+			return ErrInvalidFlags
+		}
+	}
+
+	return nil
+}
+
+// encodeLength writes length bits for the header.
+func encodeLength(buf *bytes.Buffer, length int) {
+	for {
+		digit := byte(length % 128)
+		length /= 128
+		if length > 0 {
+			digit |= 0x80
+		}
+		buf.WriteByte(digit)
+		if length == 0 {
+			break
+		}
+	}
+}




diff --git a/internal/packets/fixedheader_test.go b/internal/packets/fixedheader_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7ce4e3314e1fd161dc8f85487044f8effb023b92
--- /dev/null
+++ b/internal/packets/fixedheader_test.go
@@ -0,0 +1,220 @@
+package packets
+
+import (
+	"bytes"
+	"math"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+type fixedHeaderTable struct {
+	rawBytes    []byte
+	header      FixedHeader
+	packetError bool
+	flagError   bool
+}
+
+var fixedHeaderExpected = []fixedHeaderTable{
+	{
+		rawBytes: []byte{Connect << 4, 0x00},
+		header:   FixedHeader{Connect, false, 0, false, 0}, // Type byte, Dup bool, Qos byte, Retain bool, Remaining int
+	},
+	{
+		rawBytes: []byte{Connack << 4, 0x00},
+		header:   FixedHeader{Connack, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Publish << 4, 0x00},
+		header:   FixedHeader{Publish, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 1<<1, 0x00},
+		header:   FixedHeader{Publish, false, 1, false, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 1<<1 | 1, 0x00},
+		header:   FixedHeader{Publish, false, 1, true, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 2<<1, 0x00},
+		header:   FixedHeader{Publish, false, 2, false, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 2<<1 | 1, 0x00},
+		header:   FixedHeader{Publish, false, 2, true, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 1<<3, 0x00},
+		header:   FixedHeader{Publish, true, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 1<<3 | 1, 0x00},
+		header:   FixedHeader{Publish, true, 0, true, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 1<<3 | 1<<1 | 1, 0x00},
+		header:   FixedHeader{Publish, true, 1, true, 0},
+	},
+	{
+		rawBytes: []byte{Publish<<4 | 1<<3 | 2<<1 | 1, 0x00},
+		header:   FixedHeader{Publish, true, 2, true, 0},
+	},
+	{
+		rawBytes: []byte{Puback << 4, 0x00},
+		header:   FixedHeader{Puback, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Pubrec << 4, 0x00},
+		header:   FixedHeader{Pubrec, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Pubrel<<4 | 1<<1, 0x00},
+		header:   FixedHeader{Pubrel, false, 1, false, 0},
+	},
+	{
+		rawBytes: []byte{Pubcomp << 4, 0x00},
+		header:   FixedHeader{Pubcomp, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Subscribe<<4 | 1<<1, 0x00},
+		header:   FixedHeader{Subscribe, false, 1, false, 0},
+	},
+	{
+		rawBytes: []byte{Suback << 4, 0x00},
+		header:   FixedHeader{Suback, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Unsubscribe<<4 | 1<<1, 0x00},
+		header:   FixedHeader{Unsubscribe, false, 1, false, 0},
+	},
+	{
+		rawBytes: []byte{Unsuback << 4, 0x00},
+		header:   FixedHeader{Unsuback, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Pingreq << 4, 0x00},
+		header:   FixedHeader{Pingreq, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Pingresp << 4, 0x00},
+		header:   FixedHeader{Pingresp, false, 0, false, 0},
+	},
+	{
+		rawBytes: []byte{Disconnect << 4, 0x00},
+		header:   FixedHeader{Disconnect, false, 0, false, 0},
+	},
+
+	// remaining length
+	{
+		rawBytes: []byte{Publish << 4, 0x0a},
+		header:   FixedHeader{Publish, false, 0, false, 10},
+	},
+	{
+		rawBytes: []byte{Publish << 4, 0x80, 0x04},
+		header:   FixedHeader{Publish, false, 0, false, 512},
+	},
+	{
+		rawBytes: []byte{Publish << 4, 0xd2, 0x07},
+		header:   FixedHeader{Publish, false, 0, false, 978},
+	},
+	{
+		rawBytes: []byte{Publish << 4, 0x86, 0x9d, 0x01},
+		header:   FixedHeader{Publish, false, 0, false, 20102},
+	},
+	{
+		rawBytes:    []byte{Publish << 4, 0xd5, 0x86, 0xf9, 0x9e, 0x01},
+		header:      FixedHeader{Publish, false, 0, false, 333333333},
+		packetError: true,
+	},
+
+	// Invalid flags for packet
+	{
+		rawBytes:  []byte{Connect<<4 | 1<<3, 0x00},
+		header:    FixedHeader{Connect, true, 0, false, 0},
+		flagError: true,
+	},
+	{
+		rawBytes:  []byte{Connect<<4 | 1<<1, 0x00},
+		header:    FixedHeader{Connect, false, 1, false, 0},
+		flagError: true,
+	},
+	{
+		rawBytes:  []byte{Connect<<4 | 1, 0x00},
+		header:    FixedHeader{Connect, false, 0, true, 0},
+		flagError: true,
+	},
+}
+
+func TestFixedHeaderEncode(t *testing.T) {
+	for i, wanted := range fixedHeaderExpected {
+		buf := new(bytes.Buffer)
+		wanted.header.Encode(buf)
+		if wanted.flagError == false {
+			require.Equal(t, len(wanted.rawBytes), len(buf.Bytes()), "Mismatched fixedheader length [i:%d] %v", i, wanted.rawBytes)
+			require.EqualValues(t, wanted.rawBytes, buf.Bytes(), "Mismatched byte values [i:%d] %v", i, wanted.rawBytes)
+		}
+	}
+}
+
+func BenchmarkFixedHeaderEncode(b *testing.B) {
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		fixedHeaderExpected[0].header.Encode(buf)
+	}
+}
+
+func TestFixedHeaderDecode(t *testing.T) {
+	for i, wanted := range fixedHeaderExpected {
+		fh := new(FixedHeader)
+		err := fh.Decode(wanted.rawBytes[0])
+		if wanted.flagError {
+			require.Error(t, err, "Expected error reading fixedheader [i:%d] %v", i, wanted.rawBytes)
+		} else {
+			require.NoError(t, err, "Error reading fixedheader [i:%d] %v", i, wanted.rawBytes)
+			require.Equal(t, wanted.header.Type, fh.Type, "Mismatched fixedheader type [i:%d] %v", i, wanted.rawBytes)
+			require.Equal(t, wanted.header.Dup, fh.Dup, "Mismatched fixedheader dup [i:%d] %v", i, wanted.rawBytes)
+			require.Equal(t, wanted.header.Qos, fh.Qos, "Mismatched fixedheader qos [i:%d] %v", i, wanted.rawBytes)
+			require.Equal(t, wanted.header.Retain, fh.Retain, "Mismatched fixedheader retain [i:%d] %v", i, wanted.rawBytes)
+		}
+	}
+}
+
+func BenchmarkFixedHeaderDecode(b *testing.B) {
+	fh := new(FixedHeader)
+	for n := 0; n < b.N; n++ {
+		err := fh.Decode(fixedHeaderExpected[0].rawBytes[0])
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+func TestEncodeLength(t *testing.T) {
+	tt := []struct {
+		have int
+		want []byte
+	}{
+		{
+			120,
+			[]byte{0x78},
+		},
+		{
+			math.MaxInt64,
+			[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
+		},
+	}
+
+	for i, wanted := range tt {
+		buf := new(bytes.Buffer)
+		encodeLength(buf, wanted.have)
+		require.Equal(t, wanted.want, buf.Bytes(), "Returned bytes should match length [i:%d] %s", i, wanted.have)
+	}
+}
+
+func BenchmarkEncodeLength(b *testing.B) {
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		encodeLength(buf, 120)
+	}
+}




diff --git a/internal/packets/packets.go b/internal/packets/packets.go
new file mode 100644
index 0000000000000000000000000000000000000000..cdedf10c7584012e32ccf6f8ba745097616a51e7
--- /dev/null
+++ b/internal/packets/packets.go
@@ -0,0 +1,672 @@
+package packets
+
+import (
+	"bytes"
+	"errors"
+)
+
+// All of the valid packet types and their packet identifier.
+const (
+	Reserved    byte = iota
+	Connect          // 1
+	Connack          // 2
+	Publish          // 3
+	Puback           // 4
+	Pubrec           // 5
+	Pubrel           // 6
+	Pubcomp          // 7
+	Subscribe        // 8
+	Suback           // 9
+	Unsubscribe      // 10
+	Unsuback         // 11
+	Pingreq          // 12
+	Pingresp         // 13
+	Disconnect       // 14
+
+	Accepted                      byte = 0x00
+	Failed                        byte = 0xFF
+	CodeConnectBadProtocolVersion byte = 0x01
+	CodeConnectBadClientID        byte = 0x02
+	CodeConnectServerUnavailable  byte = 0x03
+	CodeConnectBadAuthValues      byte = 0x04
+	CodeConnectNotAuthorised      byte = 0x05
+	CodeConnectNetworkError       byte = 0xFE
+	CodeConnectProtocolViolation  byte = 0xFF
+	ErrSubAckNetworkError         byte = 0x80
+)
+
+var (
+	// CONNECT
+	ErrMalformedProtocolName    = errors.New("malformed packet: protocol name")
+	ErrMalformedProtocolVersion = errors.New("malformed packet: protocol version")
+	ErrMalformedFlags           = errors.New("malformed packet: flags")
+	ErrMalformedKeepalive       = errors.New("malformed packet: keepalive")
+	ErrMalformedClientID        = errors.New("malformed packet: client id")
+	ErrMalformedWillTopic       = errors.New("malformed packet: will topic")
+	ErrMalformedWillMessage     = errors.New("malformed packet: will message")
+	ErrMalformedUsername        = errors.New("malformed packet: username")
+	ErrMalformedPassword        = errors.New("malformed packet: password")
+
+	// CONNACK
+	ErrMalformedSessionPresent = errors.New("malformed packet: session present")
+	ErrMalformedReturnCode     = errors.New("malformed packet: return code")
+
+	// PUBLISH
+	ErrMalformedTopic    = errors.New("malformed packet: topic name")
+	ErrMalformedPacketID = errors.New("malformed packet: packet id")
+
+	// SUBSCRIBE
+	ErrMalformedQoS = errors.New("malformed packet: qos")
+
+	// PACKETS
+	ErrProtocolViolation        = errors.New("protocol violation")
+	ErrOffsetStrOutOfRange      = errors.New("offset string out of range")
+	ErrOffsetBytesOutOfRange    = errors.New("offset bytes out of range")
+	ErrOffsetByteOutOfRange     = errors.New("offset byte out of range")
+	ErrOffsetBoolOutOfRange     = errors.New("offset bool out of range")
+	ErrOffsetUintOutOfRange     = errors.New("offset uint out of range")
+	ErrOffsetStrInvalidUTF8     = errors.New("offset string invalid utf8")
+	ErrInvalidFlags             = errors.New("invalid flags set for packet")
+	ErrOversizedLengthIndicator = errors.New("protocol violation: oversized length indicator")
+	ErrMissingPacketID          = errors.New("missing packet id")
+	ErrSurplusPacketID          = errors.New("surplus packet id")
+)
+
+// Packet is an MQTT packet. Instead of providing a packet interface and variant
+// packet structs, this is a single concrete packet type to cover all packet
+// types, which allows us to take advantage of various compiler optimizations.
+type Packet struct {
+	FixedHeader FixedHeader
+
+	PacketID uint16
+
+	// Connect
+	ProtocolName     []byte
+	ProtocolVersion  byte
+	CleanSession     bool
+	WillFlag         bool
+	WillQos          byte
+	WillRetain       bool
+	UsernameFlag     bool
+	PasswordFlag     bool
+	ReservedBit      byte
+	Keepalive        uint16
+	ClientIdentifier string
+	WillTopic        string
+	WillMessage      []byte
+	Username         []byte
+	Password         []byte
+
+	// Connack
+	SessionPresent bool
+	ReturnCode     byte
+
+	// Publish
+	TopicName string
+	Payload   []byte
+
+	// Subscribe, Unsubscribe
+	Topics []string
+	Qoss   []byte
+
+	ReturnCodes []byte // Suback
+}
+
+// ConnectEncode encodes a connect packet.
+func (pk *Packet) ConnectEncode(buf *bytes.Buffer) error {
+
+	protoName := encodeBytes(pk.ProtocolName)
+	protoVersion := pk.ProtocolVersion
+	flag := encodeBool(pk.CleanSession)<<1 | encodeBool(pk.WillFlag)<<2 | pk.WillQos<<3 | encodeBool(pk.WillRetain)<<5 | encodeBool(pk.PasswordFlag)<<6 | encodeBool(pk.UsernameFlag)<<7
+	keepalive := encodeUint16(pk.Keepalive)
+	clientID := encodeString(pk.ClientIdentifier)
+
+	var willTopic, willFlag, usernameFlag, passwordFlag []byte
+
+	// If will flag is set, add topic and message.
+	if pk.WillFlag {
+		willTopic = encodeString(pk.WillTopic)
+		willFlag = encodeBytes(pk.WillMessage)
+	}
+
+	// If username flag is set, add username.
+	if pk.UsernameFlag {
+		usernameFlag = encodeBytes(pk.Username)
+	}
+
+	// If password flag is set, add password.
+	if pk.PasswordFlag {
+		passwordFlag = encodeBytes(pk.Password)
+	}
+
+	// Get a length for the connect header. This is not super pretty, but it works.
+	pk.FixedHeader.Remaining =
+		len(protoName) + 1 + 1 + len(keepalive) + len(clientID) +
+			len(willTopic) + len(willFlag) +
+			len(usernameFlag) + len(passwordFlag)
+
+	pk.FixedHeader.Encode(buf)
+
+	// Eschew magic for readability.
+	buf.Write(protoName)
+	buf.WriteByte(protoVersion)
+	buf.WriteByte(flag)
+	buf.Write(keepalive)
+	buf.Write(clientID)
+	buf.Write(willTopic)
+	buf.Write(willFlag)
+	buf.Write(usernameFlag)
+	buf.Write(passwordFlag)
+
+	return nil
+}
+
+// ConnectDecode decodes a connect packet.
+func (pk *Packet) ConnectDecode(buf []byte) error {
+	var offset int
+	var err error
+
+	// Unpack protocol name and version.
+	pk.ProtocolName, offset, err = decodeBytes(buf, 0)
+	if err != nil {
+		return ErrMalformedProtocolName
+	}
+
+	pk.ProtocolVersion, offset, err = decodeByte(buf, offset)
+	if err != nil {
+		return ErrMalformedProtocolVersion
+	}
+	// Unpack flags byte.
+	flags, offset, err := decodeByte(buf, offset)
+	if err != nil {
+		return ErrMalformedFlags
+	}
+	pk.ReservedBit = 1 & flags
+	pk.CleanSession = 1&(flags>>1) > 0
+	pk.WillFlag = 1&(flags>>2) > 0
+	pk.WillQos = 3 & (flags >> 3) // this one is not a bool
+	pk.WillRetain = 1&(flags>>5) > 0
+	pk.PasswordFlag = 1&(flags>>6) > 0
+	pk.UsernameFlag = 1&(flags>>7) > 0
+
+	// Get keepalive interval.
+	pk.Keepalive, offset, err = decodeUint16(buf, offset)
+	if err != nil {
+		return ErrMalformedKeepalive
+	}
+
+	// Get client ID.
+	pk.ClientIdentifier, offset, err = decodeString(buf, offset)
+	if err != nil {
+		return ErrMalformedClientID
+	}
+
+	// Get Last Will and Testament topic and message if applicable.
+	if pk.WillFlag {
+		pk.WillTopic, offset, err = decodeString(buf, offset)
+		if err != nil {
+			return ErrMalformedWillTopic
+		}
+
+		pk.WillMessage, offset, err = decodeBytes(buf, offset)
+		if err != nil {
+			return ErrMalformedWillMessage
+		}
+	}
+
+	// Get username and password if applicable.
+	if pk.UsernameFlag {
+		pk.Username, offset, err = decodeBytes(buf, offset)
+		if err != nil {
+			return ErrMalformedUsername
+		}
+	}
+
+	if pk.PasswordFlag {
+		pk.Password, offset, err = decodeBytes(buf, offset)
+		if err != nil {
+			return ErrMalformedPassword
+		}
+	}
+
+	return nil
+
+}
+
+// ConnectValidate ensures the connect packet is compliant.
+func (pk *Packet) ConnectValidate() (b byte, err error) {
+
+	// End if protocol name is bad.
+	if bytes.Compare(pk.ProtocolName, []byte{'M', 'Q', 'I', 's', 'd', 'p'}) != 0 &&
+		bytes.Compare(pk.ProtocolName, []byte{'M', 'Q', 'T', 'T'}) != 0 {
+		return CodeConnectProtocolViolation, ErrProtocolViolation
+	}
+
+	// End if protocol version is bad.
+	if (bytes.Compare(pk.ProtocolName, []byte{'M', 'Q', 'I', 's', 'd', 'p'}) == 0 && pk.ProtocolVersion != 3) ||
+		(bytes.Compare(pk.ProtocolName, []byte{'M', 'Q', 'T', 'T'}) == 0 && pk.ProtocolVersion != 4) {
+		return CodeConnectBadProtocolVersion, ErrProtocolViolation
+	}
+
+	// End if reserved bit is not 0.
+	if pk.ReservedBit != 0 {
+		return CodeConnectProtocolViolation, ErrProtocolViolation
+	}
+
+	// End if ClientID is too long.
+	if len(pk.ClientIdentifier) > 65535 {
+		return CodeConnectProtocolViolation, ErrProtocolViolation
+	}
+
+	// End if password flag is set without a username.
+	if pk.PasswordFlag && !pk.UsernameFlag {
+		return CodeConnectProtocolViolation, ErrProtocolViolation
+	}
+
+	// End if Username or Password is too long.
+	if len(pk.Username) > 65535 || len(pk.Password) > 65535 {
+		return CodeConnectProtocolViolation, ErrProtocolViolation
+	}
+
+	// End if client id isn't set and clean session is false.
+	if !pk.CleanSession && len(pk.ClientIdentifier) == 0 {
+		return CodeConnectBadClientID, ErrProtocolViolation
+	}
+
+	return Accepted, nil
+}
+
+// ConnackEncode encodes a Connack packet.
+func (pk *Packet) ConnackEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Remaining = 2
+	pk.FixedHeader.Encode(buf)
+	buf.WriteByte(encodeBool(pk.SessionPresent))
+	buf.WriteByte(pk.ReturnCode)
+	return nil
+}
+
+// ConnackDecode decodes a Connack packet.
+func (pk *Packet) ConnackDecode(buf []byte) error {
+	var offset int
+	var err error
+
+	pk.SessionPresent, offset, err = decodeByteBool(buf, 0)
+	if err != nil {
+		return ErrMalformedSessionPresent
+	}
+
+	pk.ReturnCode, offset, err = decodeByte(buf, offset)
+	if err != nil {
+		return ErrMalformedReturnCode
+	}
+
+	return nil
+}
+
+// DisconnectEncode encodes a Disconnect packet.
+func (pk *Packet) DisconnectEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Encode(buf)
+	return nil
+}
+
+// PingreqEncode encodes a Pingreq packet.
+func (pk *Packet) PingreqEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Encode(buf)
+	return nil
+}
+
+// PingrespEncode encodes a Pingresp packet.
+func (pk *Packet) PingrespEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Encode(buf)
+	return nil
+}
+
+// PubackEncode encodes a Puback packet.
+func (pk *Packet) PubackEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Remaining = 2
+	pk.FixedHeader.Encode(buf)
+	buf.Write(encodeUint16(pk.PacketID))
+	return nil
+}
+
+// PubackDecode decodes a Puback packet.
+func (pk *Packet) PubackDecode(buf []byte) error {
+	var err error
+	pk.PacketID, _, err = decodeUint16(buf, 0)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+	return nil
+}
+
+// PubcompEncode encodes a Pubcomp packet.
+func (pk *Packet) PubcompEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Remaining = 2
+	pk.FixedHeader.Encode(buf)
+	buf.Write(encodeUint16(pk.PacketID))
+	return nil
+}
+
+// PubcompDecode decodes a Pubcomp packet.
+func (pk *Packet) PubcompDecode(buf []byte) error {
+	var err error
+	pk.PacketID, _, err = decodeUint16(buf, 0)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+	return nil
+}
+
+// PublishEncode encodes a Publish packet.
+func (pk *Packet) PublishEncode(buf *bytes.Buffer) error {
+	topicName := encodeString(pk.TopicName)
+	var packetID []byte
+
+	// Add PacketID if QOS is set.
+	// [MQTT-2.3.1-5] A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
+	if pk.FixedHeader.Qos > 0 {
+
+		// [MQTT-2.3.1-1] SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+		if pk.PacketID == 0 {
+			return ErrMissingPacketID
+		}
+
+		packetID = encodeUint16(pk.PacketID)
+	}
+
+	pk.FixedHeader.Remaining = len(topicName) + len(packetID) + len(pk.Payload)
+	pk.FixedHeader.Encode(buf)
+	buf.Write(topicName)
+	buf.Write(packetID)
+	buf.Write(pk.Payload)
+
+	return nil
+}
+
+// PublishDecode extracts the data values from the packet.
+func (pk *Packet) PublishDecode(buf []byte) error {
+	var offset int
+	var err error
+
+	pk.TopicName, offset, err = decodeString(buf, 0)
+	if err != nil {
+		return ErrMalformedTopic
+	}
+
+	// If QOS decode Packet ID.
+	if pk.FixedHeader.Qos > 0 {
+		pk.PacketID, offset, err = decodeUint16(buf, offset)
+		if err != nil {
+			return ErrMalformedPacketID
+		}
+	}
+
+	pk.Payload = buf[offset:]
+
+	return nil
+}
+
+// PublishCopy creates a new instance of Publish packet bearing the
+// same payload and destination topic, but with an empty header for
+// inheriting new QoS etc flags.
+func (pk *Packet) PublishCopy() Packet {
+	return Packet{
+		FixedHeader: FixedHeader{
+			Type: Publish,
+		},
+		TopicName: pk.TopicName,
+		Payload:   pk.Payload,
+	}
+}
+
+// PublishValidate validates a publish packet.
+func (pk *Packet) PublishValidate() (byte, error) {
+
+	// @SPEC [MQTT-2.3.1-1]
+	// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+	if pk.FixedHeader.Qos > 0 && pk.PacketID == 0 {
+		return Failed, ErrMissingPacketID
+	}
+
+	// @SPEC [MQTT-2.3.1-5]
+	// A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
+	if pk.FixedHeader.Qos == 0 && pk.PacketID > 0 {
+		return Failed, ErrSurplusPacketID
+	}
+
+	return Accepted, nil
+}
+
+// PubrecEncode encodes a Pubrec packet.
+func (pk *Packet) PubrecEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Remaining = 2
+	pk.FixedHeader.Encode(buf)
+	buf.Write(encodeUint16(pk.PacketID))
+	return nil
+}
+
+// PubrecDecode decodes a Pubrec packet.
+func (pk *Packet) PubrecDecode(buf []byte) error {
+	var err error
+	pk.PacketID, _, err = decodeUint16(buf, 0)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+
+	return nil
+}
+
+// PubrelEncode encodes a Pubrel packet.
+func (pk *Packet) PubrelEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Remaining = 2
+	pk.FixedHeader.Encode(buf)
+	buf.Write(encodeUint16(pk.PacketID))
+	return nil
+}
+
+// PubrelDecode decodes a Pubrel packet.
+func (pk *Packet) PubrelDecode(buf []byte) error {
+	var err error
+	pk.PacketID, _, err = decodeUint16(buf, 0)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+	return nil
+}
+
+// SubackEncode encodes a Suback packet.
+func (pk *Packet) SubackEncode(buf *bytes.Buffer) error {
+	packetID := encodeUint16(pk.PacketID)
+	pk.FixedHeader.Remaining = len(packetID) + len(pk.ReturnCodes) // Set length.
+	pk.FixedHeader.Encode(buf)
+
+	buf.Write(packetID)       // Encode Packet ID.
+	buf.Write(pk.ReturnCodes) // Encode granted QOS flags.
+
+	return nil
+}
+
+// SubackDecode decodes a Suback packet.
+func (pk *Packet) SubackDecode(buf []byte) error {
+	var offset int
+	var err error
+
+	// Get Packet ID.
+	pk.PacketID, offset, err = decodeUint16(buf, offset)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+
+	// Get Granted QOS flags.
+	pk.ReturnCodes = buf[offset:]
+
+	return nil
+}
+
+// SubscribeEncode encodes a Subscribe packet.
+func (pk *Packet) SubscribeEncode(buf *bytes.Buffer) error {
+
+	// Add the Packet ID.
+	// [MQTT-2.3.1-1] SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+	if pk.PacketID == 0 {
+		return ErrMissingPacketID
+	}
+
+	packetID := encodeUint16(pk.PacketID)
+
+	// Count topics lengths and associated QOS flags.
+	var topicsLen int
+	for _, topic := range pk.Topics {
+		topicsLen += len(encodeString(topic)) + 1
+	}
+
+	pk.FixedHeader.Remaining = len(packetID) + topicsLen
+	pk.FixedHeader.Encode(buf)
+	buf.Write(packetID)
+
+	// Add all provided topic names and associated QOS flags.
+	for i, topic := range pk.Topics {
+		buf.Write(encodeString(topic))
+		buf.WriteByte(pk.Qoss[i])
+	}
+
+	return nil
+}
+
+// SubscribeDecode decodes a Subscribe packet.
+func (pk *Packet) SubscribeDecode(buf []byte) error {
+	var offset int
+	var err error
+
+	// Get the Packet ID.
+	pk.PacketID, offset, err = decodeUint16(buf, 0)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+
+	// Keep decoding until there's no space left.
+	for offset < len(buf) {
+
+		// Decode Topic Name.
+		var topic string
+		topic, offset, err = decodeString(buf, offset)
+		if err != nil {
+			return ErrMalformedTopic
+		}
+		pk.Topics = append(pk.Topics, topic)
+
+		// Decode QOS flag.
+		var qos byte
+		qos, offset, err = decodeByte(buf, offset)
+		if err != nil {
+			return ErrMalformedQoS
+		}
+
+		// Ensure QoS byte is within range.
+		if !(qos >= 0 && qos <= 2) {
+			//if !validateQoS(qos) {
+			return ErrMalformedQoS
+		}
+
+		pk.Qoss = append(pk.Qoss, qos)
+	}
+
+	return nil
+}
+
+// SubscribeValidate ensures the packet is compliant.
+func (pk *Packet) SubscribeValidate() (byte, error) {
+	// @SPEC [MQTT-2.3.1-1].
+	// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+	if pk.FixedHeader.Qos > 0 && pk.PacketID == 0 {
+		return Failed, ErrMissingPacketID
+	}
+
+	return Accepted, nil
+}
+
+// UnsubackEncode encodes an Unsuback packet.
+func (pk *Packet) UnsubackEncode(buf *bytes.Buffer) error {
+	pk.FixedHeader.Remaining = 2
+	pk.FixedHeader.Encode(buf)
+	buf.Write(encodeUint16(pk.PacketID))
+	return nil
+}
+
+// UnsubackDecode decodes an Unsuback packet.
+func (pk *Packet) UnsubackDecode(buf []byte) error {
+	var err error
+	pk.PacketID, _, err = decodeUint16(buf, 0)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+	return nil
+}
+
+// UnsubscribeEncode encodes an Unsubscribe packet.
+func (pk *Packet) UnsubscribeEncode(buf *bytes.Buffer) error {
+
+	// Add the Packet ID.
+	// [MQTT-2.3.1-1] SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+	if pk.PacketID == 0 {
+		return ErrMissingPacketID
+	}
+
+	packetID := encodeUint16(pk.PacketID)
+
+	// Count topics lengths.
+	var topicsLen int
+	for _, topic := range pk.Topics {
+		topicsLen += len(encodeString(topic))
+	}
+
+	pk.FixedHeader.Remaining = len(packetID) + topicsLen
+	pk.FixedHeader.Encode(buf)
+	buf.Write(packetID)
+
+	// Add all provided topic names.
+	for _, topic := range pk.Topics {
+		buf.Write(encodeString(topic))
+	}
+
+	return nil
+}
+
+// UnsubscribeDecode decodes an Unsubscribe packet.
+func (pk *Packet) UnsubscribeDecode(buf []byte) error {
+	var offset int
+	var err error
+
+	// Get the Packet ID.
+	pk.PacketID, offset, err = decodeUint16(buf, 0)
+	if err != nil {
+		return ErrMalformedPacketID
+	}
+
+	// Keep decoding until there's no space left.
+	for offset < len(buf) {
+		var t string
+		t, offset, err = decodeString(buf, offset) // Decode Topic Name.
+		if err != nil {
+			return ErrMalformedTopic
+		}
+
+		if len(t) > 0 {
+			pk.Topics = append(pk.Topics, t)
+		}
+	}
+
+	return nil
+
+}
+
+// UnsubscribeValidate validates an Unsubscribe packet.
+func (pk *Packet) UnsubscribeValidate() (byte, error) {
+	// @SPEC [MQTT-2.3.1-1].
+	// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+	if pk.FixedHeader.Qos > 0 && pk.PacketID == 0 {
+		return Failed, ErrMissingPacketID
+	}
+
+	return Accepted, nil
+}




diff --git a/internal/packets/packets_tables_test.go b/internal/packets/packets_tables_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6a5e8cf1767acf59249e92e3162a4913e19deac7
--- /dev/null
+++ b/internal/packets/packets_tables_test.go
@@ -0,0 +1,1416 @@
+package packets
+
+type packetTestData struct {
+	group       string      // group specifies a group that should run the test, blank for all
+	rawBytes    []byte      // the bytes that make the packet
+	actualBytes []byte      // the actual byte array that is created in the event of a byte mutation (eg. MQTT-2.3.1-1 qos/packet id)
+	packet      *Packet     // the packet that is expected
+	desc        string      // a description of the test
+	failFirst   interface{} // expected fail result to be run immediately after the method is called
+	expect      interface{} // 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
+	meta        interface{} // meta conains a metadata value used in testing on a case-by-case basis.
+	code        byte        // code is an expected validation return code
+}
+
+func encodeTestOK(wanted packetTestData) bool {
+	if wanted.rawBytes == nil {
+		return false
+	}
+	if wanted.group != "" && wanted.group != "encode" {
+		return false
+	}
+	return true
+}
+
+func decodeTestOK(wanted packetTestData) bool {
+	if wanted.group != "" && wanted.group != "decode" {
+		return false
+	}
+	return true
+}
+
+var expectedPackets = map[byte][]packetTestData{
+	Connect: {
+		{
+			desc:    "MQTT 3.1",
+			primary: true,
+			rawBytes: []byte{
+				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,
+				},
+				ProtocolName:     []byte("MQIsdp"),
+				ProtocolVersion:  3,
+				CleanSession:     false,
+				Keepalive:        30,
+				ClientIdentifier: "zen",
+			},
+		},
+
+		{
+			desc:    "MQTT 3.1.1",
+			primary: true,
+			rawBytes: []byte{
+				byte(Connect << 4), 16, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				4,     // Protocol Version
+				0,     // Packet Flags
+				0, 60, // Keepalive
+				0, 4, // Client ID - MSB+LSB
+				'z', 'e', 'n', '3', // Client ID "zen"
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connect,
+					Remaining: 16,
+				},
+				ProtocolName:     []byte("MQTT"),
+				ProtocolVersion:  4,
+				CleanSession:     false,
+				Keepalive:        60,
+				ClientIdentifier: "zen3",
+			},
+		},
+		{
+			desc: "MQTT 3.1.1, Clean Session",
+			rawBytes: []byte{
+				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,
+				},
+				ProtocolName:     []byte("MQTT"),
+				ProtocolVersion:  4,
+				CleanSession:     true,
+				Keepalive:        45,
+				ClientIdentifier: "zen",
+			},
+		},
+		{
+			desc: "MQTT 3.1.1, Clean Session, LWT",
+			rawBytes: []byte{
+				byte(Connect << 4), 31, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				4,     // Protocol Version
+				14,    // Packet Flags
+				0, 27, // 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',
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connect,
+					Remaining: 31,
+				},
+				ProtocolName:     []byte("MQTT"),
+				ProtocolVersion:  4,
+				CleanSession:     true,
+				Keepalive:        27,
+				ClientIdentifier: "zen",
+				WillFlag:         true,
+				WillTopic:        "lwt",
+				WillMessage:      []byte("not again"),
+				WillQos:          1,
+			},
+		},
+		{
+			desc: "MQTT 3.1.1, Username, Password",
+			rawBytes: []byte{
+				byte(Connect << 4), 28, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				4,     // Protocol Version
+				194,   // 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,
+				},
+				ProtocolName:     []byte("MQTT"),
+				ProtocolVersion:  4,
+				CleanSession:     true,
+				Keepalive:        20,
+				ClientIdentifier: "zen",
+				UsernameFlag:     true,
+				PasswordFlag:     true,
+				Username:         []byte("mochi"),
+				Password:         []byte(",./;"),
+			},
+		},
+		{
+			desc:    "MQTT 3.1.1, Username, Password, LWT",
+			primary: true,
+			rawBytes: []byte{
+				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,
+				},
+				ProtocolName:     []byte("MQTT"),
+				ProtocolVersion:  4,
+				CleanSession:     true,
+				Keepalive:        120,
+				ClientIdentifier: "zen",
+				UsernameFlag:     true,
+				PasswordFlag:     true,
+				Username:         []byte("mochi"),
+				Password:         []byte(",./;"),
+				WillFlag:         true,
+				WillTopic:        "lwt",
+				WillMessage:      []byte("not again"),
+				WillQos:          1,
+			},
+		},
+
+		// Fail States
+		{
+			desc:      "Malformed Connect - protocol name",
+			group:     "decode",
+			failFirst: ErrMalformedProtocolName,
+			rawBytes: []byte{
+				byte(Connect << 4), 0, // Fixed header
+				0, 7, // Protocol Name - MSB+LSB
+				'M', 'Q', 'I', 's', 'd', // Protocol Name
+			},
+		},
+
+		{
+			desc:      "Malformed Connect - protocol version",
+			group:     "decode",
+			failFirst: ErrMalformedProtocolVersion,
+			rawBytes: []byte{
+				byte(Connect << 4), 0, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+			},
+		},
+
+		{
+			desc:      "Malformed Connect - flags",
+			group:     "decode",
+			failFirst: ErrMalformedFlags,
+			rawBytes: []byte{
+				byte(Connect << 4), 0, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				4, // Protocol Version
+
+			},
+		},
+		{
+			desc:      "Malformed Connect - keepalive",
+			group:     "decode",
+			failFirst: ErrMalformedKeepalive,
+			rawBytes: []byte{
+				byte(Connect << 4), 0, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				4, // Protocol Version
+				0, // Flags
+			},
+		},
+		{
+			desc:      "Malformed Connect - client id",
+			group:     "decode",
+			failFirst: ErrMalformedClientID,
+			rawBytes: []byte{
+				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"
+			},
+		},
+		{
+			desc:      "Malformed Connect - will topic",
+			group:     "decode",
+			failFirst: ErrMalformedWillTopic,
+			rawBytes: []byte{
+				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',
+			},
+		},
+		{
+			desc:      "Malformed Connect - will flag",
+			group:     "decode",
+			failFirst: ErrMalformedWillMessage,
+			rawBytes: []byte{
+				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',
+			},
+		},
+		{
+			desc:      "Malformed Connect - username",
+			group:     "decode",
+			failFirst: ErrMalformedUsername,
+			rawBytes: []byte{
+				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',
+			},
+		},
+		{
+			desc:      "Malformed Connect - password",
+			group:     "decode",
+			failFirst: ErrMalformedPassword,
+			rawBytes: []byte{
+				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
+				',', '.',
+			},
+		},
+
+		// Validation Tests
+		{
+			desc:  "Invalid Protocol Name",
+			group: "validate",
+			code:  CodeConnectProtocolViolation,
+			packet: &Packet{
+				ProtocolName: []byte("stuff"),
+			},
+		},
+		{
+			desc:  "Invalid Protocol Version",
+			group: "validate",
+			code:  CodeConnectBadProtocolVersion,
+			packet: &Packet{
+				ProtocolName:    []byte("MQTT"),
+				ProtocolVersion: 2,
+			},
+		},
+		{
+			desc:  "Invalid Protocol Version",
+			group: "validate",
+			code:  CodeConnectBadProtocolVersion,
+			packet: &Packet{
+				ProtocolName:    []byte("MQIsdp"),
+				ProtocolVersion: 2,
+			},
+		},
+		{
+			desc:  "Reserved bit not 0",
+			group: "validate",
+			code:  CodeConnectProtocolViolation,
+			packet: &Packet{
+				ProtocolName:    []byte("MQTT"),
+				ProtocolVersion: 4,
+				ReservedBit:     1,
+			},
+		},
+		{
+			desc:  "Client ID too long",
+			group: "validate",
+			code:  CodeConnectProtocolViolation,
+			packet: &Packet{
+				ProtocolName:    []byte("MQTT"),
+				ProtocolVersion: 4,
+				ClientIdentifier: func() string {
+					return string(make([]byte, 65536))
+				}(),
+			},
+		},
+		{
+			desc:  "Has Password Flag but no Username flag",
+			group: "validate",
+			code:  CodeConnectProtocolViolation,
+			packet: &Packet{
+				ProtocolName:    []byte("MQTT"),
+				ProtocolVersion: 4,
+				PasswordFlag:    true,
+			},
+		},
+		{
+			desc:  "Username too long",
+			group: "validate",
+			code:  CodeConnectProtocolViolation,
+			packet: &Packet{
+				ProtocolName:    []byte("MQTT"),
+				ProtocolVersion: 4,
+				UsernameFlag:    true,
+				Username: func() []byte {
+					return make([]byte, 65536)
+				}(),
+			},
+		},
+		{
+			desc:  "Password too long",
+			group: "validate",
+			code:  CodeConnectProtocolViolation,
+			packet: &Packet{
+				ProtocolName:    []byte("MQTT"),
+				ProtocolVersion: 4,
+				UsernameFlag:    true,
+				Username:        []byte{},
+				PasswordFlag:    true,
+				Password: func() []byte {
+					return make([]byte, 65536)
+				}(),
+			},
+		},
+		{
+			desc:  "Clean session false and client id not set",
+			group: "validate",
+			code:  CodeConnectBadClientID,
+			packet: &Packet{
+				ProtocolName:    []byte("MQTT"),
+				ProtocolVersion: 4,
+				CleanSession:    false,
+			},
+		},
+
+		// Spec Tests
+		{
+			// @SPEC [MQTT-1.4.0-1]
+			// The character data in a UTF-8 encoded string MUST be well-formed UTF-8
+			// as defined by the Unicode specification [Unicode] and restated in RFC 3629 [RFC 3629].
+			// In particular this data MUST NOT include encodings of code points between U+D800 and U+DFFF.
+			desc:      "Invalid UTF8 string (a) - Code point U+D800.",
+			group:     "decode",
+			failFirst: ErrMalformedClientID,
+			rawBytes: []byte{
+				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
+			},
+		},
+		{
+			desc:      "Invalid UTF8 string (b) - Code point U+DFFF.",
+			group:     "decode",
+			failFirst: ErrMalformedClientID,
+			rawBytes: []byte{
+				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
+			},
+		},
+
+		// @SPEC [MQTT-1.4.0-2]
+		// A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000.
+		{
+			desc:      "Invalid UTF8 string (c) - Code point U+0000.",
+			group:     "decode",
+			failFirst: ErrMalformedClientID,
+			rawBytes: []byte{
+				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
+			},
+		},
+
+		// @ SPEC [MQTT-1.4.0-3]
+		// A UTF-8 encoded sequence 0xEF 0xBB 0xBF is always to be interpreted to mean U+FEFF ("ZERO WIDTH NO-BREAK SPACE")
+		// wherever it appears in a string and MUST NOT be skipped over or stripped off by a packet receiver.
+		{
+			desc: "UTF8 string must not skip or strip code point U+FEFF.",
+			//group: "decode",
+			//failFirst: ErrMalformedClientID,
+			rawBytes: []byte{
+				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,
+				},
+				ProtocolName:     []byte("MQTT"),
+				ProtocolVersion:  4,
+				Keepalive:        20,
+				ClientIdentifier: string([]byte{'e', 'b', 0xEF, 0xBB, 0xBF, 'd'}),
+			},
+		},
+	},
+	Connack: {
+		{
+			desc:    "Accepted, No Session",
+			primary: true,
+			rawBytes: []byte{
+				byte(Connack << 4), 2, // fixed header
+				0, // No existing session
+				Accepted,
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connack,
+					Remaining: 2,
+				},
+				SessionPresent: false,
+				ReturnCode:     Accepted,
+			},
+		},
+		{
+			desc:    "Accepted, Session Exists",
+			primary: true,
+			rawBytes: []byte{
+				byte(Connack << 4), 2, // fixed header
+				1, // Session present
+				Accepted,
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connack,
+					Remaining: 2,
+				},
+				SessionPresent: true,
+				ReturnCode:     Accepted,
+			},
+		},
+		{
+			desc: "Bad Protocol Version",
+			rawBytes: []byte{
+				byte(Connack << 4), 2, // fixed header
+				1, // Session present
+				CodeConnectBadProtocolVersion,
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connack,
+					Remaining: 2,
+				},
+				SessionPresent: true,
+				ReturnCode:     CodeConnectBadProtocolVersion,
+			},
+		},
+		{
+			desc: "Bad Client ID",
+			rawBytes: []byte{
+				byte(Connack << 4), 2, // fixed header
+				1, // Session present
+				CodeConnectBadClientID,
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connack,
+					Remaining: 2,
+				},
+				SessionPresent: true,
+				ReturnCode:     CodeConnectBadClientID,
+			},
+		},
+		{
+			desc: "Server Unavailable",
+			rawBytes: []byte{
+				byte(Connack << 4), 2, // fixed header
+				1, // Session present
+				CodeConnectServerUnavailable,
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connack,
+					Remaining: 2,
+				},
+				SessionPresent: true,
+				ReturnCode:     CodeConnectServerUnavailable,
+			},
+		},
+		{
+			desc: "Bad Username or Password",
+			rawBytes: []byte{
+				byte(Connack << 4), 2, // fixed header
+				1, // Session present
+				CodeConnectBadAuthValues,
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connack,
+					Remaining: 2,
+				},
+				SessionPresent: true,
+				ReturnCode:     CodeConnectBadAuthValues,
+			},
+		},
+		{
+			desc: "Not Authorised",
+			rawBytes: []byte{
+				byte(Connack << 4), 2, // fixed header
+				1, // Session present
+				CodeConnectNotAuthorised,
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connack,
+					Remaining: 2,
+				},
+				SessionPresent: true,
+				ReturnCode:     CodeConnectNotAuthorised,
+			},
+		},
+
+		// Fail States
+		{
+			desc:      "Malformed Connack - session present",
+			group:     "decode",
+			failFirst: ErrMalformedSessionPresent,
+			rawBytes: []byte{
+				byte(Connect << 4), 2, // Fixed header
+			},
+		},
+		{
+			desc:  "Malformed Connack - bad return code",
+			group: "decode",
+			//primary:   true,
+			failFirst: ErrMalformedReturnCode,
+			rawBytes: []byte{
+				byte(Connect << 4), 2, // Fixed header
+				0,
+			},
+		},
+	},
+
+	Publish: {
+		{
+			desc:    "Publish - No payload",
+			primary: true,
+			rawBytes: []byte{
+				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{},
+			},
+		},
+		{
+			desc:    "Publish - basic",
+			primary: true,
+			rawBytes: []byte{
+				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"),
+			},
+		},
+		{
+			desc:    "Publish - QoS:1, Packet ID",
+			primary: true,
+			rawBytes: []byte{
+				byte(Publish<<4) | 2, 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:       1,
+					Remaining: 14,
+				},
+				TopicName: "a/b/c",
+				Payload:   []byte("hello"),
+				PacketID:  7,
+			},
+			meta: byte(2),
+		},
+		{
+			desc:    "Publish - QoS:1, Packet ID, No payload",
+			primary: true,
+			rawBytes: []byte{
+				byte(Publish<<4) | 2, 9, // Fixed header
+				0, 5, // Topic Name - LSB+MSB
+				'y', '/', 'u', '/', 'i', // Topic Name
+				0, 8, // Packet ID - LSB+MSB
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Publish,
+					Qos:       1,
+					Remaining: 9,
+				},
+				TopicName: "y/u/i",
+				PacketID:  8,
+				Payload:   []byte{},
+			},
+			meta: byte(2),
+		},
+		{
+			desc: "Publish - Retain",
+			rawBytes: []byte{
+				byte(Publish<<4) | 1, 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,
+					Retain: true,
+				},
+				TopicName: "a/b",
+				Payload:   []byte("hello"),
+			},
+			meta: byte(1),
+		},
+		{
+			desc: "Publish - Dup",
+			rawBytes: []byte{
+				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"),
+			},
+			meta: byte(8),
+		},
+
+		// Fail States
+		{
+			desc:      "Malformed Publish - topic name",
+			group:     "decode",
+			failFirst: ErrMalformedTopic,
+			rawBytes: []byte{
+				byte(Publish << 4), 7, // Fixed header
+				0, 5, // Topic Name - LSB+MSB
+				'a', '/',
+				0, 11, // Packet ID - LSB+MSB
+			},
+		},
+
+		{
+			desc:      "Malformed Publish - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Publish<<4) | 2, 7, // Fixed header
+				0, 5, // Topic Name - LSB+MSB
+				'x', '/', 'y', '/', 'z', // Topic Name
+				0, // Packet ID - LSB+MSB
+			},
+		},
+
+		// Copy tests
+		{
+			desc:  "Publish - basic copyable",
+			group: "copy",
+			rawBytes: []byte{
+				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
+		{
+			// @SPEC [MQTT-2.3.1-5]
+			// A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
+			desc:  "[MQTT-2.3.1-5] 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{
+				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{
+				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"),
+			},
+		},
+		{
+			// @SPEC [MQTT-2.3.1-1].
+			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+			desc:   "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
+			group:  "encode",
+			expect: ErrMissingPacketID,
+			code:   Failed,
+			rawBytes: []byte{
+				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,
+			},
+			meta: byte(2),
+		},
+		/*
+			{
+				// @SPEC [MQTT-2.3.1-1].
+				// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+				desc:  "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
+				group: "validate",
+				//primary: true,
+				expect: ErrMissingPacketID,
+				code:   Failed,
+				rawBytes: []byte{
+					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,
+				},
+				meta: byte(2),
+			},
+
+		*/
+
+		// Validation Tests
+		{
+			// @SPEC [MQTT-2.3.1-5]
+			desc:   "[MQTT-2.3.1-5] Packet ID must be 0 if QoS is 0 (b)",
+			group:  "validate",
+			expect: ErrSurplusPacketID,
+			code:   Failed,
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Publish,
+					Remaining: 12,
+					Qos:       0,
+				},
+				TopicName: "a/b/c",
+				Payload:   []byte("hello"),
+				PacketID:  3,
+			},
+		},
+		{
+			// @SPEC [MQTT-2.3.1-1].
+			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+			desc:   "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
+			group:  "validate",
+			expect: ErrMissingPacketID,
+			code:   Failed,
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type: Publish,
+					Qos:  1,
+				},
+				PacketID: 0,
+			},
+		},
+	},
+
+	Puback: {
+		{
+			desc:    "Puback",
+			primary: true,
+			rawBytes: []byte{
+				byte(Puback << 4), 2, // Fixed header
+				0, 11, // Packet ID - LSB+MSB
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Puback,
+					Remaining: 2,
+				},
+				PacketID: 11,
+			},
+		},
+
+		// Fail states
+		{
+			desc:      "Malformed Puback - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Puback << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+	},
+	Pubrec: {
+		{
+			desc:    "Pubrec",
+			primary: true,
+			rawBytes: []byte{
+				byte(Pubrec << 4), 2, // Fixed header
+				0, 12, // Packet ID - LSB+MSB
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Pubrec,
+					Remaining: 2,
+				},
+				PacketID: 12,
+			},
+		},
+
+		// Fail states
+		{
+			desc:      "Malformed Pubrec - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Pubrec << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+	},
+	Pubrel: {
+		{
+			desc:    "Pubrel",
+			primary: true,
+			rawBytes: []byte{
+				byte(Pubrel<<4) | 2, 2, // Fixed header
+				0, 12, // Packet ID - LSB+MSB
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Pubrel,
+					Remaining: 2,
+					Qos:       1,
+				},
+				PacketID: 12,
+			},
+			meta: byte(2),
+		},
+
+		// Fail states
+		{
+			desc:      "Malformed Pubrel - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Pubrel << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+	},
+	Pubcomp: {
+		{
+			desc:    "Pubcomp",
+			primary: true,
+			rawBytes: []byte{
+				byte(Pubcomp << 4), 2, // Fixed header
+				0, 14, // Packet ID - LSB+MSB
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Pubcomp,
+					Remaining: 2,
+				},
+				PacketID: 14,
+			},
+		},
+
+		// Fail states
+		{
+			desc:      "Malformed Pubcomp - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Pubcomp << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+	},
+	Subscribe: {
+		{
+			desc:    "Subscribe",
+			primary: true,
+			rawBytes: []byte{
+				byte(Subscribe << 4), 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,
+				},
+				PacketID: 15,
+				Topics: []string{
+					"a/b",
+					"d/e/f/g/h/i",
+					"x/y/z",
+				},
+				Qoss: []byte{0, 1, 2},
+			},
+		},
+
+		// Fail states
+		{
+			desc:      "Malformed Subscribe - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Subscribe << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+		{
+			desc:      "Malformed Subscribe - topic",
+			group:     "decode",
+			failFirst: ErrMalformedTopic,
+			rawBytes: []byte{
+				byte(Subscribe << 4), 2, // Fixed header
+				0, 21, // Packet ID - LSB+MSB
+
+				0, 3, // Topic Name - LSB+MSB
+				'a', '/',
+			},
+		},
+		{
+			desc:      "Malformed Subscribe - qos",
+			group:     "decode",
+			failFirst: ErrMalformedQoS,
+			rawBytes: []byte{
+				byte(Subscribe << 4), 2, // Fixed header
+				0, 22, // Packet ID - LSB+MSB
+
+				0, 3, // Topic Name - LSB+MSB
+				'j', '/', 'b', // Topic Name
+
+			},
+		},
+		{
+			desc:      "Malformed Subscribe - qos out of range",
+			group:     "decode",
+			failFirst: ErrMalformedQoS,
+			rawBytes: []byte{
+				byte(Subscribe << 4), 2, // Fixed header
+				0, 22, // Packet ID - LSB+MSB
+
+				0, 3, // Topic Name - LSB+MSB
+				'c', '/', 'd', // Topic Name
+				5, // QoS
+
+			},
+		},
+
+		// Validation
+		{
+			// @SPEC [MQTT-2.3.1-1].
+			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+			desc:   "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
+			group:  "validate",
+			expect: ErrMissingPacketID,
+			code:   Failed,
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type: Subscribe,
+					Qos:  1,
+				},
+				PacketID: 0,
+			},
+		},
+
+		// Spec tests
+		{
+			// @SPEC [MQTT-2.3.1-1].
+			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+			desc:   "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
+			group:  "encode",
+			code:   Failed,
+			expect: ErrMissingPacketID,
+			rawBytes: []byte{
+				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,
+				},
+				Topics: []string{
+					"a/b/c",
+				},
+				Qoss:     []byte{1},
+				PacketID: 0,
+			},
+			meta: byte(2),
+		},
+	},
+	Suback: {
+		{
+			desc:    "Suback",
+			primary: true,
+			rawBytes: []byte{
+				byte(Suback << 4), 6, // Fixed header
+				0, 17, // 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:    17,
+				ReturnCodes: []byte{0, 1, 2, 0x80},
+			},
+		},
+
+		// Fail states
+		{
+			desc:      "Malformed Suback - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Subscribe << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+	},
+
+	Unsubscribe: {
+		{
+			desc:    "Unsubscribe",
+			primary: true,
+			rawBytes: []byte{
+				byte(Unsubscribe << 4), 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,
+				},
+				PacketID: 35,
+				Topics: []string{
+					"a/b",
+					"d/e/f/g/h/i",
+					"x/y/z",
+				},
+			},
+		},
+		// Fail states
+		{
+			desc:      "Malformed Unsubscribe - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Unsubscribe << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+		{
+			desc:      "Malformed Unsubscribe - topic",
+			group:     "decode",
+			failFirst: ErrMalformedTopic,
+			rawBytes: []byte{
+				byte(Unsubscribe << 4), 2, // Fixed header
+				0, 21, // Packet ID - LSB+MSB
+				0, 3, // Topic Name - LSB+MSB
+				'a', '/',
+			},
+		},
+
+		// Validation
+		{
+			// @SPEC [MQTT-2.3.1-1].
+			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+			desc:   "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
+			group:  "validate",
+			expect: ErrMissingPacketID,
+			code:   Failed,
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type: Unsubscribe,
+					Qos:  1,
+				},
+				PacketID: 0,
+			},
+		},
+
+		// Spec tests
+		{
+			// @SPEC [MQTT-2.3.1-1].
+			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
+			desc:   "[MQTT-2.3.1-1] Unsubscribe No Packet ID with QOS > 0",
+			group:  "encode",
+			code:   Failed,
+			expect: ErrMissingPacketID,
+			rawBytes: []byte{
+				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,
+				},
+				Topics: []string{
+					"a/b/c",
+				},
+				PacketID: 0,
+			},
+			meta: byte(2),
+		},
+	},
+	Unsuback: {
+		{
+			desc:    "Unsuback",
+			primary: true,
+			rawBytes: []byte{
+				byte(Unsuback << 4), 2, // Fixed header
+				0, 37, // Packet ID - LSB+MSB
+
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Unsuback,
+					Remaining: 2,
+				},
+				PacketID: 37,
+			},
+		},
+
+		// Fail states
+		{
+			desc:      "Malformed Unsuback - Packet ID",
+			group:     "decode",
+			failFirst: ErrMalformedPacketID,
+			rawBytes: []byte{
+				byte(Unsuback << 4), 2, // Fixed header
+				0, // Packet ID - LSB+MSB
+			},
+		},
+	},
+
+	Pingreq: {
+		{
+			desc:    "Ping request",
+			primary: true,
+			rawBytes: []byte{
+				byte(Pingreq << 4), 0, // fixed header
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Pingreq,
+					Remaining: 0,
+				},
+			},
+		},
+	},
+	Pingresp: {
+		{
+			desc:    "Ping response",
+			primary: true,
+			rawBytes: []byte{
+				byte(Pingresp << 4), 0, // fixed header
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Pingresp,
+					Remaining: 0,
+				},
+			},
+		},
+	},
+
+	Disconnect: {
+		{
+			desc:    "Disconnect",
+			primary: true,
+			rawBytes: []byte{
+				byte(Disconnect << 4), 0, // fixed header
+			},
+			packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Disconnect,
+					Remaining: 0,
+				},
+			},
+		},
+	},
+}




diff --git a/internal/packets/packets_test.go b/internal/packets/packets_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..aa5d7b5c1057a06284bf9ba7a81847599d49b479
--- /dev/null
+++ b/internal/packets/packets_test.go
@@ -0,0 +1,1082 @@
+package packets
+
+import (
+	"bytes"
+	"testing"
+
+	"github.com/jinzhu/copier"
+	"github.com/stretchr/testify/require"
+)
+
+func TestConnectEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Connect)
+	for i, wanted := range expectedPackets[Connect] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(1), Connect, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Connect, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		pk.ConnectEncode(buf)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		require.Equal(t, byte(Connect<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+
+		ok, _ := pk.ConnectValidate()
+		require.Equal(t, byte(Accepted), ok, "Connect packet didn't validate - %v", ok)
+
+		require.Equal(t, wanted.packet.FixedHeader.Type, pk.FixedHeader.Type, "Mismatched packet fixed header type [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched packet fixed header dup [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched packet fixed header qos [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched packet fixed header retain [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.ProtocolVersion, pk.ProtocolVersion, "Mismatched packet protocol version [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.ProtocolName, pk.ProtocolName, "Mismatched packet protocol name [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.CleanSession, pk.CleanSession, "Mismatched packet cleansession [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.ClientIdentifier, pk.ClientIdentifier, "Mismatched packet client id [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Keepalive, pk.Keepalive, "Mismatched keepalive value [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.UsernameFlag, pk.UsernameFlag, "Mismatched packet username flag [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Username, pk.Username, "Mismatched packet username [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PasswordFlag, pk.PasswordFlag, "Mismatched packet password flag [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Password, pk.Password, "Mismatched packet password [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.WillFlag, pk.WillFlag, "Mismatched packet will flag [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillTopic, pk.WillTopic, "Mismatched packet will topic [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillMessage, pk.WillMessage, "Mismatched packet will message [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillQos, pk.WillQos, "Mismatched packet will qos [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillRetain, pk.WillRetain, "Mismatched packet will retain [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkConnectEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Connect][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.ConnectEncode(buf)
+	}
+}
+
+func TestConnectDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Connect)
+	for i, wanted := range expectedPackets[Connect] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(1), Connect, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		require.Equal(t, true, (len(wanted.rawBytes) > 2), "Insufficent bytes in packet [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Connect}}
+		err := pk.ConnectDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.FixedHeader.Type, pk.FixedHeader.Type, "Mismatched packet fixed header type [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched packet fixed header dup [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched packet fixed header qos [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched packet fixed header retain [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.ProtocolVersion, pk.ProtocolVersion, "Mismatched packet protocol version [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.ProtocolName, pk.ProtocolName, "Mismatched packet protocol name [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.CleanSession, pk.CleanSession, "Mismatched packet cleansession [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.ClientIdentifier, pk.ClientIdentifier, "Mismatched packet client id [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Keepalive, pk.Keepalive, "Mismatched keepalive value [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.UsernameFlag, pk.UsernameFlag, "Mismatched packet username flag [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Username, pk.Username, "Mismatched packet username [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PasswordFlag, pk.PasswordFlag, "Mismatched packet password flag [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Password, pk.Password, "Mismatched packet password [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.WillFlag, pk.WillFlag, "Mismatched packet will flag [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillTopic, pk.WillTopic, "Mismatched packet will topic [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillMessage, pk.WillMessage, "Mismatched packet will message [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillQos, pk.WillQos, "Mismatched packet will qos [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.WillRetain, pk.WillRetain, "Mismatched packet will retain [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkConnectDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Connect}}
+	pk.FixedHeader.Decode(expectedPackets[Connect][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.ConnectDecode(expectedPackets[Connect][0].rawBytes[2:])
+	}
+}
+
+func TestConnectValidate(t *testing.T) {
+	require.Contains(t, expectedPackets, Connect)
+	for i, wanted := range expectedPackets[Connect] {
+		if wanted.group == "validate" {
+			pk := wanted.packet
+			ok, _ := pk.ConnectValidate()
+			require.Equal(t, wanted.code, ok, "Connect packet didn't validate [i:%d] %s", i, wanted.desc)
+		}
+	}
+}
+
+func TestConnackEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Connack)
+	for i, wanted := range expectedPackets[Connack] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(2), Connack, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Connack, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.ConnackEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		require.Equal(t, byte(Connack<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.ReturnCode, pk.ReturnCode, "Mismatched return code [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.SessionPresent, pk.SessionPresent, "Mismatched session present bool [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkConnackEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Connack][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.ConnackEncode(buf)
+	}
+}
+
+func TestConnackDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Connack)
+	for i, wanted := range expectedPackets[Connack] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(2), Connack, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Connack}}
+		err := pk.ConnackDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.ReturnCode, pk.ReturnCode, "Mismatched return code [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.SessionPresent, pk.SessionPresent, "Mismatched session present bool [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkConnackDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Connack}}
+	pk.FixedHeader.Decode(expectedPackets[Connack][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.ConnackDecode(expectedPackets[Connack][0].rawBytes[2:])
+	}
+}
+
+func TestDisconnectEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Disconnect)
+	for i, wanted := range expectedPackets[Disconnect] {
+		require.Equal(t, uint8(14), Disconnect, "Incorrect Packet Type [i:%d]", i)
+
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Disconnect, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d]", i)
+
+		buf := new(bytes.Buffer)
+		err := pk.DisconnectEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d]", i)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d]", i)
+	}
+}
+
+func BenchmarkDisconnectEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Disconnect][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.DisconnectEncode(buf)
+	}
+}
+
+func TestPingreqEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pingreq)
+	for i, wanted := range expectedPackets[Pingreq] {
+		require.Equal(t, uint8(12), Pingreq, "Incorrect Packet Type [i:%d]", i)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Pingreq, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d]", i)
+
+		buf := new(bytes.Buffer)
+		err := pk.PingreqEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d]", i)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d]", i)
+	}
+}
+
+func BenchmarkPingreqEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Pingreq][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.PingreqEncode(buf)
+	}
+}
+
+func TestPingrespEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pingresp)
+	for i, wanted := range expectedPackets[Pingresp] {
+		require.Equal(t, uint8(13), Pingresp, "Incorrect Packet Type [i:%d]", i)
+
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Pingresp, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d]", i)
+
+		buf := new(bytes.Buffer)
+		err := pk.PingrespEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d]", i)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d]", i)
+	}
+}
+
+func BenchmarkPingrespEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Pingresp][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.PingrespEncode(buf)
+	}
+}
+
+func TestPubackEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Puback)
+	for i, wanted := range expectedPackets[Puback] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(4), Puback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Puback, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.PubackEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		require.Equal(t, byte(Puback<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkPubackEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Puback][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.PubackEncode(buf)
+	}
+}
+
+func TestPubackDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Puback)
+	for i, wanted := range expectedPackets[Puback] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(4), Puback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Puback}}
+		err := pk.PubackDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkPubackDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Puback}}
+	pk.FixedHeader.Decode(expectedPackets[Puback][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.PubackDecode(expectedPackets[Puback][0].rawBytes[2:])
+	}
+}
+
+func TestPubcompEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pubcomp)
+	for i, wanted := range expectedPackets[Pubcomp] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(7), Pubcomp, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Pubcomp, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.PubcompEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		require.Equal(t, byte(Pubcomp<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkPubcompEncode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Pubcomp}}
+	copier.Copy(pk, expectedPackets[Pubcomp][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.PubcompEncode(buf)
+	}
+}
+
+func TestPubcompDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pubcomp)
+	for i, wanted := range expectedPackets[Pubcomp] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(7), Pubcomp, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Pubcomp}}
+		err := pk.PubcompDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkPubcompDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Pubcomp}}
+	pk.FixedHeader.Decode(expectedPackets[Pubcomp][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.PubcompDecode(expectedPackets[Pubcomp][0].rawBytes[2:])
+	}
+}
+
+func TestPublishEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Publish)
+	for i, wanted := range expectedPackets[Publish] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(3), Publish, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Publish, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.PublishEncode(buf)
+		encoded := buf.Bytes()
+
+		if wanted.expect != nil {
+			require.Error(t, err, "Expected error writing buffer [i:%d] %s", i, wanted.desc)
+		} else {
+
+			// If actualBytes is set, compare mutated version of byte string instead (to avoid length mismatches, etc).
+			if len(wanted.actualBytes) > 0 {
+				wanted.rawBytes = wanted.actualBytes
+			}
+
+			require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+			if wanted.meta != nil {
+				require.Equal(t, byte(Publish<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+			} else {
+				require.Equal(t, byte(Publish<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+			}
+
+			require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched QOS [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched Dup [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched Retain [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+			require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		}
+	}
+}
+
+func BenchmarkPublishEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Publish][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.PublishEncode(buf)
+	}
+}
+
+func TestPublishDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Publish)
+	for i, wanted := range expectedPackets[Publish] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(3), Publish, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Publish}}
+		pk.FixedHeader.Decode(wanted.rawBytes[0])
+
+		err := pk.PublishDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected fh error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched QOS [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched Dup [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched Retain [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+
+	}
+}
+
+func BenchmarkPublishDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Publish}}
+	pk.FixedHeader.Decode(expectedPackets[Publish][1].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.PublishDecode(expectedPackets[Publish][1].rawBytes[2:])
+	}
+}
+
+func TestPublishCopy(t *testing.T) {
+	require.Contains(t, expectedPackets, Publish)
+	for i, wanted := range expectedPackets[Publish] {
+		if wanted.group == "copy" {
+
+			pk := &Packet{FixedHeader: FixedHeader{Type: Publish}}
+			err := pk.PublishDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+			require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+
+			copied := pk.PublishCopy()
+
+			require.Equal(t, byte(0), copied.FixedHeader.Qos, "Mismatched QOS [i:%d] %s", i, wanted.desc)
+			require.Equal(t, false, copied.FixedHeader.Dup, "Mismatched Dup [i:%d] %s", i, wanted.desc)
+			require.Equal(t, false, copied.FixedHeader.Retain, "Mismatched Retain [i:%d] %s", i, wanted.desc)
+
+			require.Equal(t, pk.Payload, copied.Payload, "Mismatched Payload [i:%d] %s", i, wanted.desc)
+			require.Equal(t, pk.TopicName, copied.TopicName, "Mismatched Topic Name [i:%d] %s", i, wanted.desc)
+
+		}
+	}
+}
+
+func BenchmarkPublishCopy(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Publish}}
+	pk.FixedHeader.Decode(expectedPackets[Publish][1].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.PublishCopy()
+	}
+}
+
+func TestPublishValidate(t *testing.T) {
+	require.Contains(t, expectedPackets, Publish)
+	for i, wanted := range expectedPackets[Publish] {
+		if wanted.group == "validate" || i == 0 {
+			pk := wanted.packet
+			ok, err := pk.PublishValidate()
+
+			if i == 0 {
+				require.NoError(t, err, "Publish should have validated - error incorrect [i:%d] %s", i, wanted.desc)
+				require.Equal(t, Accepted, ok, "Publish should have validated - code incorrect [i:%d] %s", i, wanted.desc)
+			} else {
+				require.Equal(t, Failed, ok, "Publish packet didn't validate - code incorrect [i:%d] %s", i, wanted.desc)
+				if err != nil {
+					require.Equal(t, wanted.expect, err, "Publish packet didn't validate - error incorrect [i:%d] %s", i, wanted.desc)
+				}
+			}
+		}
+	}
+}
+
+func BenchmarkPublishValidate(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Publish}}
+	pk.FixedHeader.Decode(expectedPackets[Publish][1].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		_, err := pk.PublishValidate()
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+func TestPubrecEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pubrec)
+	for i, wanted := range expectedPackets[Pubrec] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(5), Pubrec, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Pubrec, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.PubrecEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		require.Equal(t, byte(Pubrec<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+
+	}
+}
+
+func BenchmarkPubrecEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Pubrec][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.PubrecEncode(buf)
+	}
+}
+
+func TestPubrecDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pubrec)
+	for i, wanted := range expectedPackets[Pubrec] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(5), Pubrec, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Pubrec}}
+		err := pk.PubrecDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkPubrecDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Pubrec}}
+	pk.FixedHeader.Decode(expectedPackets[Pubrec][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.PubrecDecode(expectedPackets[Pubrec][0].rawBytes[2:])
+	}
+}
+
+func TestPubrelEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pubrel)
+	for i, wanted := range expectedPackets[Pubrel] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(6), Pubrel, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Pubrel, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.PubrelEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		if wanted.meta != nil {
+			require.Equal(t, byte(Pubrel<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+		} else {
+			require.Equal(t, byte(Pubrel<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+		}
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkPubrelEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Pubrel][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.PubrelEncode(buf)
+	}
+}
+
+func TestPubrelDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Pubrel)
+	for i, wanted := range expectedPackets[Pubrel] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(6), Pubrel, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Pubrel, Qos: 1}}
+		err := pk.PubrelDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkPubrelDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Pubrel, Qos: 1}}
+	pk.FixedHeader.Decode(expectedPackets[Pubrel][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.PubrelDecode(expectedPackets[Pubrel][0].rawBytes[2:])
+	}
+}
+
+func TestSubackEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Suback)
+	for i, wanted := range expectedPackets[Suback] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(9), Suback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Suback, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.SubackEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		if wanted.meta != nil {
+			require.Equal(t, byte(Suback<<4)|wanted.meta.(byte), encoded[0], "Mismatched mod fixed header packets [i:%d] %s", i, wanted.desc)
+		} else {
+			require.Equal(t, byte(Suback<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
+		}
+
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.ReturnCodes, pk.ReturnCodes, "Mismatched Return Codes [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkSubackEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Suback][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.SubackEncode(buf)
+	}
+}
+
+func TestSubackDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Suback)
+	for i, wanted := range expectedPackets[Suback] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(9), Suback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Suback}}
+		err := pk.SubackDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.ReturnCodes, pk.ReturnCodes, "Mismatched Return Codes [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkSubackDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Suback}}
+	pk.FixedHeader.Decode(expectedPackets[Suback][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.SubackDecode(expectedPackets[Suback][0].rawBytes[2:])
+	}
+}
+
+func TestSubscribeEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Subscribe)
+	for i, wanted := range expectedPackets[Subscribe] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(8), Subscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Subscribe, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.SubscribeEncode(buf)
+		encoded := buf.Bytes()
+
+		if wanted.expect != nil {
+			require.Error(t, err, "Expected error writing buffer [i:%d] %s", i, wanted.desc)
+		} else {
+			require.NoError(t, err, "Error writing buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+			if wanted.meta != nil {
+				require.Equal(t, byte(Subscribe<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+			} else {
+				require.Equal(t, byte(Subscribe<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+			}
+
+			require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.Qoss, pk.Qoss, "Mismatched Qoss slice [i:%d] %s", i, wanted.desc)
+		}
+	}
+}
+
+func BenchmarkSubscribeEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Subscribe][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.SubscribeEncode(buf)
+	}
+}
+
+func TestSubscribeDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Subscribe)
+	for i, wanted := range expectedPackets[Subscribe] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(8), Subscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Subscribe, Qos: 1}}
+		err := pk.SubscribeDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Qoss, pk.Qoss, "Mismatched Qoss slice [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkSubscribeDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Subscribe, Qos: 1}}
+	pk.FixedHeader.Decode(expectedPackets[Subscribe][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.SubscribeDecode(expectedPackets[Subscribe][0].rawBytes[2:])
+	}
+}
+
+func TestSubscribeValidate(t *testing.T) {
+	require.Contains(t, expectedPackets, Subscribe)
+	for i, wanted := range expectedPackets[Subscribe] {
+		if wanted.group == "validate" || i == 0 {
+			pk := wanted.packet
+			ok, err := pk.SubscribeValidate()
+
+			if i == 0 {
+				require.NoError(t, err, "Subscribe should have validated - error incorrect [i:%d] %s", i, wanted.desc)
+				require.Equal(t, Accepted, ok, "Subscribe should have validated - code incorrect [i:%d] %s", i, wanted.desc)
+			} else {
+				require.Equal(t, Failed, ok, "Subscribe packet didn't validate - code incorrect [i:%d] %s", i, wanted.desc)
+				if err != nil {
+					require.Equal(t, wanted.expect, err, "Subscribe packet didn't validate - error incorrect [i:%d] %s", i, wanted.desc)
+				}
+			}
+		}
+	}
+}
+
+func BenchmarkSubscribeValidate(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Subscribe, Qos: 1}}
+	pk.FixedHeader.Decode(expectedPackets[Subscribe][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.SubscribeValidate()
+	}
+}
+
+func TestUnsubackEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Unsuback)
+	for i, wanted := range expectedPackets[Unsuback] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(11), Unsuback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Unsuback, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.UnsubackEncode(buf)
+		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
+		encoded := buf.Bytes()
+
+		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+		if wanted.meta != nil {
+			require.Equal(t, byte(Unsuback<<4)|wanted.meta.(byte), encoded[0], "Mismatched mod fixed header packets [i:%d] %s", i, wanted.desc)
+		} else {
+			require.Equal(t, byte(Unsuback<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
+		}
+
+		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkUnsubackEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Unsuback][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.UnsubackEncode(buf)
+	}
+}
+
+func TestUnsubackDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Unsuback)
+	for i, wanted := range expectedPackets[Unsuback] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(11), Unsuback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Unsuback}}
+		err := pk.UnsubackDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkUnsubackDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Unsuback}}
+	pk.FixedHeader.Decode(expectedPackets[Unsuback][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.UnsubackDecode(expectedPackets[Unsuback][0].rawBytes[2:])
+	}
+}
+
+func TestUnsubscribeEncode(t *testing.T) {
+	require.Contains(t, expectedPackets, Unsubscribe)
+	for i, wanted := range expectedPackets[Unsubscribe] {
+		if !encodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(10), Unsubscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+		pk := new(Packet)
+		copier.Copy(pk, wanted.packet)
+
+		require.Equal(t, Unsubscribe, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
+
+		buf := new(bytes.Buffer)
+		err := pk.UnsubscribeEncode(buf)
+		encoded := buf.Bytes()
+		if wanted.expect != nil {
+			require.Error(t, err, "Expected error writing buffer [i:%d] %s", i, wanted.desc)
+		} else {
+			require.NoError(t, err, "Error writing buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
+			if wanted.meta != nil {
+				require.Equal(t, byte(Unsubscribe<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+			} else {
+				require.Equal(t, byte(Unsubscribe<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
+			}
+
+			require.NoError(t, err, "Error writing buffer [i:%d] %s", i, wanted.desc)
+			require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
+
+			require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.packet.Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
+		}
+	}
+}
+
+func BenchmarkUnsubscribeEncode(b *testing.B) {
+	pk := new(Packet)
+	copier.Copy(pk, expectedPackets[Unsubscribe][0].packet)
+
+	buf := new(bytes.Buffer)
+	for n := 0; n < b.N; n++ {
+		pk.UnsubscribeEncode(buf)
+	}
+}
+
+func TestUnsubscribeDecode(t *testing.T) {
+	require.Contains(t, expectedPackets, Unsubscribe)
+	for i, wanted := range expectedPackets[Unsubscribe] {
+		if !decodeTestOK(wanted) {
+			continue
+		}
+
+		require.Equal(t, uint8(10), Unsubscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
+
+		pk := &Packet{FixedHeader: FixedHeader{Type: Unsubscribe, Qos: 1}}
+		err := pk.UnsubscribeDecode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
+		if wanted.failFirst != nil {
+			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
+			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
+			continue
+		}
+
+		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
+		require.Equal(t, wanted.packet.Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
+	}
+}
+
+func BenchmarkUnsubscribeDecode(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Unsubscribe, Qos: 1}}
+	pk.FixedHeader.Decode(expectedPackets[Unsubscribe][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.UnsubscribeDecode(expectedPackets[Unsubscribe][0].rawBytes[2:])
+	}
+}
+
+func TestUnsubscribeValidate(t *testing.T) {
+	require.Contains(t, expectedPackets, Unsubscribe)
+	for i, wanted := range expectedPackets[Unsubscribe] {
+		if wanted.group == "validate" || i == 0 {
+			pk := wanted.packet
+			ok, err := pk.UnsubscribeValidate()
+			if i == 0 {
+				require.NoError(t, err, "Unsubscribe should have validated - error incorrect [i:%d] %s", i, wanted.desc)
+				require.Equal(t, Accepted, ok, "Unsubscribe should have validated - code incorrect [i:%d] %s", i, wanted.desc)
+			} else {
+				require.Equal(t, Failed, ok, "Unsubscribe packet didn't validate - code incorrect [i:%d] %s", i, wanted.desc)
+				if err != nil {
+					require.Equal(t, wanted.expect, err, "Unsubscribe packet didn't validate - error incorrect [i:%d] %s", i, wanted.desc)
+				}
+			}
+		}
+	}
+}
+
+func BenchmarkUnsubscribeValidate(b *testing.B) {
+	pk := &Packet{FixedHeader: FixedHeader{Type: Unsubscribe, Qos: 1}}
+	pk.FixedHeader.Decode(expectedPackets[Unsubscribe][0].rawBytes[0])
+
+	for n := 0; n < b.N; n++ {
+		pk.UnsubscribeValidate()
+	}
+}




diff --git a/internal/topics/trie.go b/internal/topics/trie.go
new file mode 100644
index 0000000000000000000000000000000000000000..f039f3d2f29409a598ead515af8ac5d82325ce5a
--- /dev/null
+++ b/internal/topics/trie.go
@@ -0,0 +1,305 @@
+package topics
+
+import (
+	"strings"
+	"sync"
+
+	"github.com/mochi-co/mqtt/internal/packets"
+)
+
+// ReLeaf is a dev function for showing the trie leafs.
+/*
+func ReLeaf(m string, leaf *Leaf, d int) {
+	for k, v := range leaf.Leaves {
+		fmt.Println(m, d, strings.Repeat("  ", d), k)
+		ReLeaf(m, v, d+1)
+	}
+}
+*/
+
+// Subscriptions is a map of subscriptions keyed on client.
+type Subscriptions map[string]byte
+
+// Index is a prefix/trie tree containing topic subscribers and retained messages.
+type Index struct {
+	mu   sync.RWMutex // a mutex for locking the whole index.
+	Root *Leaf        // a leaf containing a message and more leaves.
+}
+
+// New returns a pointer to a new instance of Index.
+func New() *Index {
+	return &Index{
+		Root: &Leaf{
+			Leaves:  make(map[string]*Leaf),
+			Clients: make(map[string]byte),
+		},
+	}
+}
+
+// RetainMessage saves a message payload to the end of a topic branch.
+func (x *Index) RetainMessage(msg packets.Packet) {
+	x.mu.Lock()
+	defer x.mu.Unlock()
+	if len(msg.Payload) > 0 {
+		n := x.poperate(msg.TopicName)
+		n.Message = msg
+	} else {
+		x.unpoperate(msg.TopicName, "", true)
+	}
+}
+
+// Subscribe creates a subscription filter for a client.
+func (x *Index) Subscribe(filter, client string, qos byte) {
+	x.mu.Lock()
+	defer x.mu.Unlock()
+	n := x.poperate(filter)
+	n.Clients[client] = qos
+	n.Filter = filter
+}
+
+// Unsubscribe removes a subscription filter for a client. Returns true if an
+// unsubscribe action sucessful.
+func (x *Index) Unsubscribe(filter, client string) bool {
+	x.mu.Lock()
+	defer x.mu.Unlock()
+	return x.unpoperate(filter, client, false)
+}
+
+// unpoperate steps backward through a trie sequence and removes any orphaned
+// nodes. If a client id is specified, it will unsubscribe a client. If message
+// is true, it will delete a retained message.
+func (x *Index) unpoperate(filter string, client string, message bool) bool {
+	var d int // Walk to end leaf.
+	var particle string
+	var hasNext = true
+	e := x.Root
+	for hasNext {
+		particle, hasNext = isolateParticle(filter, d)
+		d++
+		e, _ = e.Leaves[particle]
+
+		// If the topic part doesn't exist in the tree, there's nothing
+		// left to do.
+		if e == nil {
+			return false
+		}
+	}
+
+	// Step backward removing client and orphaned leaves.
+	var key string
+	var orphaned bool
+	var end = true
+	for e.Parent != nil {
+		key = e.Key
+
+		// Wipe the client from this leaf if it's the filter end.
+		if end {
+			if client != "" {
+				delete(e.Clients, client)
+			}
+			if message {
+				e.Message = packets.Packet{}
+			}
+			end = false
+		}
+
+		// If this leaf is empty, note it as orphaned.
+		orphaned = len(e.Clients) == 0 && len(e.Leaves) == 0 && !e.Message.FixedHeader.Retain
+
+		// Traverse up the branch.
+		e = e.Parent
+
+		// If the leaf we just came from was empty, delete it.
+		if orphaned {
+			delete(e.Leaves, key)
+		}
+	}
+
+	return true
+
+}
+
+// poperate iterates and populates through a topic/filter path, instantiating
+// leaves as it goes and returning the final leaf in the branch.
+// poperate is a more enjoyable word than iterpop.
+func (x *Index) poperate(topic string) *Leaf {
+	var d int
+	var particle string
+	var hasNext = true
+	n := x.Root
+	for hasNext {
+		particle, hasNext = isolateParticle(topic, d)
+		d++
+
+		child, _ := n.Leaves[particle]
+		if child == nil {
+			child = &Leaf{
+				Key:     particle,
+				Parent:  n,
+				Leaves:  make(map[string]*Leaf),
+				Clients: make(map[string]byte),
+			}
+			n.Leaves[particle] = child
+		}
+		n = child
+	}
+
+	return n
+}
+
+// Subscribers returns a map of clients who are subscribed to matching filters.
+func (x *Index) Subscribers(topic string) Subscriptions {
+	x.mu.RLock()
+	defer x.mu.RUnlock()
+	return x.Root.scanSubscribers(topic, 0, make(Subscriptions))
+}
+
+// Messages returns a slice of retained topic messages which match a filter.
+func (x *Index) Messages(filter string) []packets.Packet {
+	// ReLeaf("messages", x.Root, 0)
+	x.mu.RLock()
+	defer x.mu.RUnlock()
+	return x.Root.scanMessages(filter, 0, make([]packets.Packet, 0, 32))
+}
+
+// Leaf is a child node on the tree.
+type Leaf struct {
+	Key     string           // the key that was used to create the leaf.
+	Parent  *Leaf            // a pointer to the parent node for the leaf.
+	Leaves  map[string]*Leaf // a map of child nodes, keyed on particle id.
+	Clients map[string]byte  // a map of client ids subscribed to the topic.
+	Filter  string           // the path of the topic filter being matched.
+	Message packets.Packet   // a message which has been retained for a specific topic.
+}
+
+// scanSubscribers recursively steps through a branch of leaves finding clients who
+// have subscription filters matching a topic, and their highest QoS byte.
+func (l *Leaf) scanSubscribers(topic string, d int, clients Subscriptions) Subscriptions {
+	part, hasNext := isolateParticle(topic, d)
+
+	// For either the topic part, a +, or a #, follow the branch.
+	for _, particle := range []string{part, "+", "#"} {
+		if child, ok := l.Leaves[particle]; ok {
+
+			// We're only interested in getting clients from the final
+			// element in the topic, or those with wildhashes.
+			if !hasNext || particle == "#" {
+
+				// Capture the highest QOS byte for any client with a filter
+				// matching the topic.
+				for client, qos := range child.Clients {
+					if ex, ok := clients[client]; !ok || ex < qos {
+						clients[client] = qos
+					}
+				}
+
+				// Make sure we also capture any client who are listening
+				// to this topic via path/#
+				if !hasNext {
+					if extra, ok := child.Leaves["#"]; ok {
+						for client, qos := range extra.Clients {
+							if ex, ok := clients[client]; !ok || ex < qos {
+								clients[client] = qos
+							}
+						}
+					}
+				}
+			}
+
+			// If this branch has hit a wildhash, just return immediately.
+			if particle == "#" {
+				return clients
+			} else if hasNext {
+				clients = child.scanSubscribers(topic, d+1, clients)
+			}
+		}
+	}
+
+	return clients
+}
+
+// scanMessages recursively steps through a branch of leaves finding retained messages
+// that match a topic filter. Setting `d` to -1 will enable wildhash mode, and will
+// recursively check ALL child leaves in every subsequent branch.
+func (l *Leaf) scanMessages(filter string, d int, messages []packets.Packet) []packets.Packet {
+
+	// If a wildhash mode has been set, continue recursively checking through all
+	// child leaves regardless of their particle key.
+	if d == -1 {
+		for _, child := range l.Leaves {
+			if child.Message.FixedHeader.Retain {
+				messages = append(messages, child.Message)
+			}
+			messages = child.scanMessages(filter, -1, messages)
+		}
+		return messages
+	}
+
+	// Otherwise, we'll get the particle for d in the filter.
+	particle, hasNext := isolateParticle(filter, d)
+
+	// If there's no more particles after this one, then take the messages from
+	// these topics.
+	if !hasNext {
+
+		// Wildcards and Wildhashes must be checked first, otherwise they
+		// may be detected as standard particles, and not act properly.
+		if particle == "+" || particle == "#" {
+			// Otherwise, if it's a wildcard or wildhash, get messages from all
+			// the child leaves. This wildhash captures messages on the actual
+			// wildhash position, whereas the d == -1 block collects subsequent
+			// messages further down the branch.
+			for _, child := range l.Leaves {
+				if child.Message.FixedHeader.Retain {
+					messages = append(messages, child.Message)
+				}
+			}
+		} else if child, ok := l.Leaves[particle]; ok {
+			if child.Message.FixedHeader.Retain {
+				messages = append(messages, child.Message)
+			}
+		}
+
+	} else {
+
+		// If it's not the last particle, branch out to the next leaves, scanning
+		// all available if it's a wildcard, or just one if it's a specific particle.
+		if particle == "+" {
+			for _, child := range l.Leaves {
+				messages = child.scanMessages(filter, d+1, messages)
+			}
+		} else if child, ok := l.Leaves[particle]; ok {
+			messages = child.scanMessages(filter, d+1, messages)
+		}
+	}
+
+	// If the particle was a wildhash, scan all the child leaves setting the
+	// d value to wildhash mode.
+	if particle == "#" {
+		for _, child := range l.Leaves {
+			messages = child.scanMessages(filter, -1, messages)
+		}
+	}
+
+	return messages
+}
+
+// isolateParticle extracts a particle between d / and d+1 / without allocations.
+func isolateParticle(filter string, d int) (particle string, hasNext bool) {
+	var next, end int
+	for i := 0; end > -1 && i <= d; i++ {
+		end = strings.IndexRune(filter, '/')
+		if d > -1 && i == d && end > -1 {
+			hasNext = true
+			particle = filter[next:end]
+		} else if end > -1 {
+			hasNext = false
+			filter = filter[end+1:]
+		} else {
+			hasNext = false
+			particle = filter[next:]
+		}
+	}
+
+	return
+}




diff --git a/internal/topics/trie_test.go b/internal/topics/trie_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b9fc4821d47bd65599dca752539933ee79729b46
--- /dev/null
+++ b/internal/topics/trie_test.go
@@ -0,0 +1,406 @@
+package topics
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/mochi-co/mqtt/internal/packets"
+)
+
+func TestNew(t *testing.T) {
+	index := New()
+	require.NotNil(t, index)
+	require.NotNil(t, index.Root)
+}
+
+func BenchmarkNew(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		New()
+	}
+}
+
+func TestPoperate(t *testing.T) {
+	index := New()
+	child := index.poperate("path/to/my/mqtt")
+	require.Equal(t, "mqtt", child.Key)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
+
+	child = index.poperate("a/b/c/d/e")
+	require.Equal(t, "e", child.Key)
+	child = index.poperate("a/b/c/c/a")
+	require.Equal(t, "a", child.Key)
+}
+
+func BenchmarkPoperate(b *testing.B) {
+	index := New()
+	for n := 0; n < b.N; n++ {
+		index.poperate("path/to/my/mqtt")
+	}
+}
+
+func TestUnpoperate(t *testing.T) {
+	index := New()
+	index.Subscribe("path/to/my/mqtt", "client-1", 0)
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
+
+	index.Subscribe("path/to/another/mqtt", "client-1", 0)
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients, "client-1")
+
+	pk := packets.Packet{TopicName: "path/to/retained/message", Payload: []byte{'h', 'e', 'l', 'l', 'o'}}
+	index.RetainMessage(pk)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["retained"].Leaves["message"])
+	require.Equal(t, pk, index.Root.Leaves["path"].Leaves["to"].Leaves["retained"].Leaves["message"].Message)
+
+	pk2 := packets.Packet{TopicName: "path/to/my/mqtt", Payload: []byte{'s', 'h', 'a', 'r', 'e', 'd'}}
+	index.RetainMessage(pk2)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
+	require.Equal(t, pk2, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message)
+
+	index.unpoperate("path/to/my/mqtt", "", true) // delete retained
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
+	require.Equal(t, false, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message.FixedHeader.Retain)
+
+	index.unpoperate("path/to/my/mqtt", "client-1", false) // unsubscribe client
+	require.Nil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"])
+
+	index.unpoperate("path/to/retained/message", "", true) // delete retained
+	require.NotContains(t, index.Root.Leaves["path"].Leaves["to"].Leaves, "my")
+
+	//require.Empty(t, index.Root.Leaves["path"])
+
+}
+
+func BenchmarkUnpoperate(b *testing.B) {
+	//index := New()
+	for n := 0; n < b.N; n++ {
+		//		index.poperate("path/to/my/mqtt")
+	}
+}
+
+func TestRetainMessage(t *testing.T) {
+	pk := packets.Packet{TopicName: "path/to/my/mqtt", Payload: []byte{'h', 'e', 'l', 'l', 'o'}}
+	pk2 := packets.Packet{TopicName: "path/to/another/mqtt", Payload: []byte{'h', 'e', 'l', 'l', 'o'}}
+
+	index := New()
+	index.RetainMessage(pk)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
+	require.Equal(t, pk, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message)
+
+	index.Subscribe("path/to/another/mqtt", "client-1", 0)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients["client-1"])
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"])
+
+	index.RetainMessage(pk2)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"])
+	require.Equal(t, pk2, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Message)
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients, "client-1")
+
+	// Delete retained
+	pk3 := packets.Packet{TopicName: "path/to/another/mqtt", Payload: []byte{}}
+	index.RetainMessage(pk3)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
+	require.Equal(t, pk, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message)
+	require.Equal(t, false, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Message.FixedHeader.Retain)
+}
+
+func BenchmarkRetainMessage(b *testing.B) {
+	index := New()
+	pk := packets.Packet{TopicName: "path/to/another/mqtt"}
+	for n := 0; n < b.N; n++ {
+		index.RetainMessage(pk)
+	}
+}
+
+func TestSubscribeOK(t *testing.T) {
+	index := New()
+	index.Subscribe("path/to/my/mqtt", "client-1", 0)
+	index.Subscribe("path/to/my/mqtt", "client-2", 0)
+	index.Subscribe("path/to/another/mqtt", "client-1", 0)
+	index.Subscribe("path/+", "client-2", 0)
+	index.Subscribe("#", "client-3", 0)
+
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
+	require.Equal(t, "path/to/my/mqtt", index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Filter)
+	require.Equal(t, "mqtt", index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Key)
+	require.Equal(t, index.Root.Leaves["path"], index.Root.Leaves["path"].Leaves["to"].Parent)
+	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-2")
+
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients, "client-1")
+	require.Contains(t, index.Root.Leaves["path"].Leaves["+"].Clients, "client-2")
+	require.Contains(t, index.Root.Leaves["#"].Clients, "client-3")
+}
+
+func BenchmarkSubscribe(b *testing.B) {
+	index := New()
+	for n := 0; n < b.N; n++ {
+		index.Subscribe("path/to/mqtt/basic", "client-1", 0)
+	}
+}
+
+func TestUnsubscribeA(t *testing.T) {
+	index := New()
+	index.Subscribe("path/to/my/mqtt", "client-1", 0)
+	index.Subscribe("path/to/+/mqtt", "client-1", 0)
+	index.Subscribe("path/to/stuff", "client-1", 0)
+	index.Subscribe("path/to/stuff", "client-2", 0)
+	index.Subscribe("#", "client-3", 0)
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["+"].Leaves["mqtt"].Clients, "client-1")
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-1")
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-2")
+	require.Contains(t, index.Root.Leaves["#"].Clients, "client-3")
+
+	ok := index.Unsubscribe("path/to/my/mqtt", "client-1")
+
+	require.Equal(t, true, ok)
+	require.Nil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"])
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["+"].Leaves["mqtt"].Clients, "client-1")
+
+	ok = index.Unsubscribe("path/to/stuff", "client-1")
+	require.Equal(t, true, ok)
+	require.NotContains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-1")
+	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-2")
+	require.Contains(t, index.Root.Leaves["#"].Clients, "client-3")
+
+	ok = index.Unsubscribe("fdasfdas/dfsfads/sa", "client-1")
+	require.Equal(t, false, ok)
+
+}
+
+func TestUnsubscribeCascade(t *testing.T) {
+	index := New()
+	index.Subscribe("a/b/c", "client-1", 0)
+	index.Subscribe("a/b/c/e/e", "client-1", 0)
+
+	ok := index.Unsubscribe("a/b/c/e/e", "client-1")
+	require.Equal(t, true, ok)
+	require.NotEmpty(t, index.Root.Leaves)
+	require.Contains(t, index.Root.Leaves["a"].Leaves["b"].Leaves["c"].Clients, "client-1")
+}
+
+// This benchmark is Unsubscribe-Subscribe
+func BenchmarkUnsubscribe(b *testing.B) {
+	index := New()
+
+	for n := 0; n < b.N; n++ {
+		index.Subscribe("path/to/my/mqtt", "client-1", 0)
+		index.Unsubscribe("path/to/mqtt/basic", "client-1")
+	}
+}
+
+func TestSubscribersFind(t *testing.T) {
+	tt := []struct {
+		filter string
+		topic  string
+		len    int
+	}{
+		{
+			filter: "a",
+			topic:  "a",
+			len:    1,
+		},
+		{
+			filter: "a/",
+			topic:  "a",
+			len:    0,
+		},
+		{
+			filter: "a/",
+			topic:  "a/",
+			len:    1,
+		},
+		{
+			filter: "path/to/my/mqtt",
+			topic:  "path/to/my/mqtt",
+			len:    1,
+		},
+		{
+			filter: "path/to/+/mqtt",
+			topic:  "path/to/my/mqtt",
+			len:    1,
+		},
+		{
+			filter: "+/to/+/mqtt",
+			topic:  "path/to/my/mqtt",
+			len:    1,
+		},
+		{
+			filter: "#",
+			topic:  "path/to/my/mqtt",
+			len:    1,
+		},
+		{
+			filter: "+/+/+/+",
+			topic:  "path/to/my/mqtt",
+			len:    1,
+		},
+		{
+			filter: "+/+/+/#",
+			topic:  "path/to/my/mqtt",
+			len:    1,
+		},
+		{
+			filter: "zen/#",
+			topic:  "zen",
+			len:    1,
+		},
+		{
+			filter: "+/+/#",
+			topic:  "path/to/my/mqtt",
+			len:    1,
+		},
+		{
+			filter: "path/to/",
+			topic:  "path/to/my/mqtt",
+			len:    0,
+		},
+		{
+			filter: "#/stuff",
+			topic:  "path/to/my/mqtt",
+			len:    0,
+		},
+	}
+
+	for i, check := range tt {
+		index := New()
+		index.Subscribe(check.filter, "client-1", 0)
+		clients := index.Subscribers(check.topic)
+		//spew.Dump(clients)
+		require.Equal(t, check.len, len(clients), "Unexpected clients len at %d %s %s", i, check.filter, check.topic)
+	}
+
+}
+
+func BenchmarkSubscribers(b *testing.B) {
+	index := New()
+	index.Subscribe("path/to/my/mqtt", "client-1", 0)
+	index.Subscribe("path/to/+/mqtt", "client-1", 0)
+	index.Subscribe("something/things/stuff/+", "client-1", 0)
+	index.Subscribe("path/to/stuff", "client-2", 0)
+	index.Subscribe("#", "client-3", 0)
+
+	for n := 0; n < b.N; n++ {
+		index.Subscribers("path/to/testing/mqtt")
+	}
+}
+
+func TestIsolateParticle(t *testing.T) {
+	particle, hasNext := isolateParticle("path/to/my/mqtt", 0)
+	require.Equal(t, "path", particle)
+	require.Equal(t, true, hasNext)
+	particle, hasNext = isolateParticle("path/to/my/mqtt", 1)
+	require.Equal(t, "to", particle)
+	require.Equal(t, true, hasNext)
+	particle, hasNext = isolateParticle("path/to/my/mqtt", 2)
+	require.Equal(t, "my", particle)
+	require.Equal(t, true, hasNext)
+	particle, hasNext = isolateParticle("path/to/my/mqtt", 3)
+	require.Equal(t, "mqtt", particle)
+	require.Equal(t, false, hasNext)
+
+	particle, hasNext = isolateParticle("/path/", 0)
+	require.Equal(t, "", particle)
+	require.Equal(t, true, hasNext)
+	particle, hasNext = isolateParticle("/path/", 1)
+	require.Equal(t, "path", particle)
+	require.Equal(t, true, hasNext)
+	particle, hasNext = isolateParticle("/path/", 2)
+	require.Equal(t, "", particle)
+	require.Equal(t, false, hasNext)
+
+	particle, hasNext = isolateParticle("a/b/c/+/+", 3)
+	require.Equal(t, "+", particle)
+	require.Equal(t, true, hasNext)
+	particle, hasNext = isolateParticle("a/b/c/+/+", 4)
+	require.Equal(t, "+", particle)
+	require.Equal(t, false, hasNext)
+}
+
+func BenchmarkIsolateParticle(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		isolateParticle("path/to/my/mqtt", 3)
+	}
+}
+
+func TestMessagesPattern(t *testing.T) {
+	tt := []struct {
+		packet packets.Packet
+		filter string
+		len    int
+	}{
+		{
+			packets.Packet{TopicName: "a/b/c/d", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"a/b/c/d",
+			1,
+		},
+		{
+			packets.Packet{TopicName: "a/b/c/e", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"a/+/c/+",
+			2,
+		},
+		{
+			packets.Packet{TopicName: "a/b/d/f", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"+/+/+/+",
+			3,
+		},
+		{
+			packets.Packet{TopicName: "q/w/e/r/t/y", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"q/w/e/#",
+			1,
+		},
+		{
+			packets.Packet{TopicName: "q/w/x/r/t/x", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"q/#",
+			2,
+		},
+		{
+			packets.Packet{TopicName: "asd", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"asd",
+			1,
+		},
+		{
+			packets.Packet{TopicName: "asd/fgh/jkl", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"#",
+			8,
+		},
+		{
+			packets.Packet{TopicName: "stuff/asdadsa/dsfdsafdsadfsa/dsfdsf/sdsadas", Payload: []byte{'h', 'e', 'l', 'l', 'o'}, FixedHeader: packets.FixedHeader{Retain: true}},
+			"stuff/#/things", // indexer will ignore trailing /things
+			1,
+		},
+	}
+	index := New()
+	for _, check := range tt {
+		index.RetainMessage(check.packet)
+	}
+
+	for i, check := range tt {
+		messages := index.Messages(check.filter)
+		require.Equal(t, check.len, len(messages), "Unexpected messages len at %d %s %s", i, check.filter, check.packet.TopicName)
+	}
+}
+
+func TestMessagesFind(t *testing.T) {
+	index := New()
+	index.RetainMessage(packets.Packet{TopicName: "a/a", Payload: []byte{'a'}, FixedHeader: packets.FixedHeader{Retain: true}})
+	index.RetainMessage(packets.Packet{TopicName: "a/b", Payload: []byte{'b'}, FixedHeader: packets.FixedHeader{Retain: true}})
+	messages := index.Messages("a/a")
+	require.Equal(t, 1, len(messages))
+
+	messages = index.Messages("a/+")
+	require.Equal(t, 2, len(messages))
+}
+
+func BenchmarkMessages(b *testing.B) {
+	index := New()
+	index.RetainMessage(packets.Packet{TopicName: "path/to/my/mqtt"})
+	index.RetainMessage(packets.Packet{TopicName: "path/to/another/mqtt"})
+	index.RetainMessage(packets.Packet{TopicName: "path/a/some/mqtt"})
+	index.RetainMessage(packets.Packet{TopicName: "what/is"})
+	index.RetainMessage(packets.Packet{TopicName: "q/w/e/r/t/y"})
+
+	for n := 0; n < b.N; n++ {
+		index.Messages("path/to/+/mqtt")
+	}
+}




diff --git a/listeners/config.go b/listeners/config.go
deleted file mode 100644
index a06e1bef10cec184b546682ad4769f5403864984..0000000000000000000000000000000000000000
--- a/listeners/config.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package listeners
-
-import (
-	"github.com/mochi-co/mqtt/auth"
-)
-
-// Config contains configuration values for a listener.
-type Config struct {
-
-	// Auth is an authentication controller for the listener, containing methods
-	// for Connect auth and topic ACL.
-	Auth auth.Controller
-
-	// TLS contains the TLS certficates and settings for the connection.
-	TLS *TLS
-}
-
-// TLS contains the TLS certificates and settings for the listener connection.
-type TLS struct {
-}




diff --git a/listeners/listeners.go b/listeners/listeners.go
deleted file mode 100644
index 5f15d502e6e4dd045941283ab3ebb24b6f8dd44b..0000000000000000000000000000000000000000
--- a/listeners/listeners.go
+++ /dev/null
@@ -1,241 +0,0 @@
-package listeners
-
-import (
-	"net"
-	"sync"
-
-	"github.com/mochi-co/mqtt/auth"
-)
-
-// EstablishFunc is a callback function for establishing new clients.
-type EstablishFunc func(id string, c net.Conn, ac auth.Controller) error
-
-// CloseFunc is a callback function for closing all listener clients.
-type CloseFunc func(id string)
-
-// Listener is an interface for network listeners. A network listener listens
-// for incoming client connections and adds them to the server.
-type Listener interface {
-
-	// SetConfig sets the Config values for the listener.
-	SetConfig(*Config)
-
-	// Listen starts listening on the network address.
-	Listen() error
-
-	// Serve starts the listener waiting for new connections. The method
-	// takes a callbacks for establishing new clients.
-	Serve(EstablishFunc)
-
-	// ID returns the ID of the listener.
-	ID() string
-
-	// Close closes all connections for a listener and stops listening. The method
-	// takes a callback for closing all client connections before ending.
-	Close(CloseFunc)
-}
-
-// Listeners contains the network listeners for the broker.
-type Listeners struct {
-	sync.RWMutex
-
-	// wg is a waitgroup that waits for all listeners to finish.
-	wg sync.WaitGroup
-
-	// internal is a map of active listeners.
-	internal map[string]Listener
-
-	// Errors is a channel of errors sent by listeners.
-	//Errors chan error
-}
-
-// NewListeners returns a new instance of Listeners.
-func NewListeners() Listeners {
-	return Listeners{
-		internal: map[string]Listener{},
-	}
-}
-
-// Add adds a new listener to the listeners map, keyed on id.
-func (l *Listeners) Add(val Listener) {
-	l.Lock()
-	l.internal[val.ID()] = val
-	l.Unlock()
-}
-
-// Get returns the value of a listener if it exists.
-func (l *Listeners) Get(id string) (Listener, bool) {
-	l.RLock()
-	val, ok := l.internal[id]
-	l.RUnlock()
-	return val, ok
-}
-
-// Len returns the length of the listeners map.
-func (l *Listeners) Len() int {
-	l.RLock()
-	val := len(l.internal)
-	l.RUnlock()
-	return val
-}
-
-// Delete removes a listener from the internal map.
-func (l *Listeners) Delete(id string) {
-	l.Lock()
-	delete(l.internal, id)
-	l.Unlock()
-}
-
-// Serve starts a listener serving from the internal map.
-func (l *Listeners) Serve(id string, establisher EstablishFunc) error {
-	l.RLock()
-	listener := l.internal[id]
-	l.RUnlock()
-
-	// Start listening on the network address.
-	err := listener.Listen()
-	if err != nil {
-		return err
-	}
-
-	go func(e EstablishFunc) {
-		defer l.wg.Done()
-		l.wg.Add(1)
-		listener.Serve(e)
-
-	}(establisher)
-
-	return nil
-}
-
-// ServeAll starts all listeners serving from the internal map.
-func (l *Listeners) ServeAll(establisher EstablishFunc) error {
-	l.RLock()
-	i := 0
-	ids := make([]string, len(l.internal))
-	for id := range l.internal {
-		ids[i] = id
-		i++
-	}
-	l.RUnlock()
-
-	for _, id := range ids {
-		err := l.Serve(id, establisher)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// Close stops a listener from the internal map.
-func (l *Listeners) Close(id string, closer CloseFunc) {
-	l.RLock()
-	listener := l.internal[id]
-	l.RUnlock()
-	listener.Close(closer)
-}
-
-// CloseAll iterates and closes all registered listeners.
-func (l *Listeners) CloseAll(closer CloseFunc) {
-	l.RLock()
-	i := 0
-	ids := make([]string, len(l.internal))
-	for id := range l.internal {
-		ids[i] = id
-		i++
-	}
-	l.RUnlock()
-
-	for _, id := range ids {
-		l.Close(id, closer)
-	}
-	l.wg.Wait()
-}
-
-// MockCloser is a function signature which can be used in testing.
-func MockCloser(id string) {}
-
-// MockEstablisher is a function signature which can be used in testing.
-func MockEstablisher(c net.Conn, ac auth.Controller) error {
-	return nil
-}
-
-// MockListener is a mock listener for establishing client connections.
-type MockListener struct {
-	sync.RWMutex
-
-	// id is the internal id of the listener.
-	id string
-
-	// Config contains configuration values for the listener.
-	Config *Config
-
-	// address is the address to bind to.
-	address string
-
-	// IsListening indicates that the listener is listening.
-	IsListening bool
-
-	// IsServing indicates that the listener is serving.
-	IsServing bool
-
-	// done is sent when the mock listener should close.
-	done chan bool
-}
-
-// NewMockListener returns a new instance of MockListener
-func NewMockListener(id, address string) *MockListener {
-	return &MockListener{
-		id:      id,
-		address: address,
-		done:    make(chan bool),
-	}
-}
-
-// Serve serves the mock listener.
-func (l *MockListener) Serve(establisher EstablishFunc) {
-	l.Lock()
-	l.IsServing = true
-	l.Unlock()
-DONE:
-	for {
-		select {
-		case <-l.done:
-			break DONE
-		}
-	}
-}
-
-// SetConfig sets the configuration values of the mock listener.
-func (l *MockListener) Listen() error {
-	l.Lock()
-	l.IsListening = true
-	l.Unlock()
-	return nil
-}
-
-// SetConfig sets the configuration values of the mock listener.
-func (l *MockListener) SetConfig(config *Config) {
-	l.Lock()
-	l.Config = config
-	l.Unlock()
-}
-
-// ID returns the id of the mock listener.
-func (l *MockListener) ID() string {
-	l.RLock()
-	id := l.id
-	l.RUnlock()
-	return id
-}
-
-// Close closes the mock listener.
-func (l *MockListener) Close(closer CloseFunc) {
-	l.Lock()
-	defer l.Unlock()
-	l.IsServing = false
-	closer(l.id)
-	close(l.done)
-}




diff --git a/listeners/listeners_test.go b/listeners/listeners_test.go
deleted file mode 100644
index 5b0aecf38c3a166a1dfb07743c285c5eab61db5b..0000000000000000000000000000000000000000
--- a/listeners/listeners_test.go
+++ /dev/null
@@ -1,261 +0,0 @@
-package listeners
-
-import (
-	"net"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/require"
-
-	"github.com/mochi-co/mqtt/auth"
-)
-
-func TestMockEstablisher(t *testing.T) {
-	_, w := net.Pipe()
-	err := MockEstablisher(w, new(auth.Allow))
-	require.NoError(t, err)
-	w.Close()
-}
-
-func TestNewMockListener(t *testing.T) {
-	mocked := NewMockListener("t1", ":1882")
-	require.Equal(t, "t1", mocked.id)
-	require.Equal(t, ":1882", mocked.address)
-}
-
-func TestNewMockListenerListen(t *testing.T) {
-	mocked := NewMockListener("t1", ":1882")
-	require.Equal(t, "t1", mocked.id)
-	require.Equal(t, ":1882", mocked.address)
-
-	require.Equal(t, false, mocked.IsListening)
-	mocked.Listen()
-	require.Equal(t, true, mocked.IsListening)
-}
-
-func TestMockListenerServe(t *testing.T) {
-	mocked := NewMockListener("t1", ":1882")
-	require.Equal(t, false, mocked.IsServing)
-
-	o := make(chan bool)
-	go func(o chan bool) {
-		mocked.Serve(MockEstablisher)
-		o <- true
-	}(o)
-
-	time.Sleep(time.Millisecond) // easy non-channel wait for start of serving
-	require.Equal(t, true, mocked.IsServing)
-
-	var closed bool
-	mocked.Close(func(id string) {
-		closed = true
-	})
-	require.Equal(t, true, closed)
-	<-o
-
-	mocked.Listen()
-}
-
-func TestMockListenerSetConfig(t *testing.T) {
-	mocked := NewMockListener("t1", ":1883")
-	mocked.SetConfig(new(Config))
-	require.NotNil(t, mocked.Config)
-}
-
-func TestMockListenerClose(t *testing.T) {
-	mocked := NewMockListener("t1", ":1882")
-	var closed bool
-	mocked.Close(func(id string) {
-		closed = true
-	})
-	require.Equal(t, true, closed)
-}
-
-func TestNewListeners(t *testing.T) {
-	l := NewListeners()
-	require.NotNil(t, l.internal)
-}
-
-func BenchmarkNewListeners(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		NewListeners()
-	}
-}
-
-func TestAddListener(t *testing.T) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	require.Contains(t, l.internal, "t1")
-}
-
-func BenchmarkAddListener(b *testing.B) {
-	l := NewListeners()
-	mocked := NewMockListener("t1", ":1882")
-	for n := 0; n < b.N; n++ {
-		l.Add(mocked)
-	}
-}
-
-func TestGetListener(t *testing.T) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	l.Add(NewMockListener("t2", ":1882"))
-	require.Contains(t, l.internal, "t1")
-	require.Contains(t, l.internal, "t2")
-
-	g, ok := l.Get("t1")
-	require.Equal(t, true, ok)
-	require.Equal(t, g.ID(), "t1")
-}
-
-func BenchmarkGetListener(b *testing.B) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	for n := 0; n < b.N; n++ {
-		l.Get("t1")
-	}
-}
-
-func TestLenListener(t *testing.T) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	l.Add(NewMockListener("t2", ":1882"))
-	require.Contains(t, l.internal, "t1")
-	require.Contains(t, l.internal, "t2")
-	require.Equal(t, 2, l.Len())
-}
-
-func BenchmarkLenListener(b *testing.B) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	for n := 0; n < b.N; n++ {
-		l.Len()
-	}
-}
-
-func TestDeleteListener(t *testing.T) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	require.Contains(t, l.internal, "t1")
-
-	l.Delete("t1")
-	_, ok := l.Get("t1")
-	require.Equal(t, false, ok)
-	require.Nil(t, l.internal["t1"])
-}
-
-func BenchmarkDeleteListener(b *testing.B) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	for n := 0; n < b.N; n++ {
-		l.Delete("t1")
-	}
-}
-
-func TestServeListener(t *testing.T) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	l.Serve("t1", MockEstablisher)
-	time.Sleep(time.Millisecond)
-	require.Equal(t, true, l.internal["t1"].(*MockListener).IsServing)
-
-	l.Close("t1", MockCloser)
-	require.Equal(t, false, l.internal["t1"].(*MockListener).IsServing)
-}
-
-func BenchmarkServeListener(b *testing.B) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	for n := 0; n < b.N; n++ {
-		l.Serve("t1", MockEstablisher)
-	}
-}
-
-func TestServeAllListeners(t *testing.T) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	l.Add(NewMockListener("t2", ":1882"))
-	l.Add(NewMockListener("t3", ":1882"))
-	l.ServeAll(MockEstablisher)
-	time.Sleep(time.Millisecond)
-
-	require.Equal(t, true, l.internal["t1"].(*MockListener).IsServing)
-	require.Equal(t, true, l.internal["t2"].(*MockListener).IsServing)
-	require.Equal(t, true, l.internal["t3"].(*MockListener).IsServing)
-
-	l.Close("t1", MockCloser)
-	l.Close("t2", MockCloser)
-	l.Close("t3", MockCloser)
-
-	require.Equal(t, false, l.internal["t1"].(*MockListener).IsServing)
-	require.Equal(t, false, l.internal["t2"].(*MockListener).IsServing)
-	require.Equal(t, false, l.internal["t3"].(*MockListener).IsServing)
-}
-
-func BenchmarkServeAllListeners(b *testing.B) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	l.Add(NewMockListener("t2", ":1883"))
-	l.Add(NewMockListener("t3", ":1884"))
-	for n := 0; n < b.N; n++ {
-		l.ServeAll(MockEstablisher)
-	}
-}
-
-func TestCloseListener(t *testing.T) {
-	l := NewListeners()
-	mocked := NewMockListener("t1", ":1882")
-	l.Add(mocked)
-	l.Serve("t1", MockEstablisher)
-	time.Sleep(time.Millisecond)
-	var closed bool
-	l.Close("t1", func(id string) {
-		closed = true
-	})
-	require.Equal(t, true, closed)
-}
-
-func BenchmarkCloseListener(b *testing.B) {
-	l := NewListeners()
-	mocked := NewMockListener("t1", ":1882")
-	l.Add(mocked)
-	l.Serve("t1", MockEstablisher)
-	for n := 0; n < b.N; n++ {
-		l.internal["t1"].(*MockListener).done = make(chan bool)
-		l.Close("t1", MockCloser)
-	}
-}
-
-func TestCloseAllListeners(t *testing.T) {
-	l := NewListeners()
-	l.Add(NewMockListener("t1", ":1882"))
-	l.Add(NewMockListener("t2", ":1882"))
-	l.Add(NewMockListener("t3", ":1882"))
-	l.ServeAll(MockEstablisher)
-	time.Sleep(time.Millisecond)
-	require.Equal(t, true, l.internal["t1"].(*MockListener).IsServing)
-	require.Equal(t, true, l.internal["t2"].(*MockListener).IsServing)
-	require.Equal(t, true, l.internal["t3"].(*MockListener).IsServing)
-
-	closed := make(map[string]bool)
-	l.CloseAll(func(id string) {
-		closed[id] = true
-	})
-	require.Contains(t, closed, "t1")
-	require.Contains(t, closed, "t2")
-	require.Contains(t, closed, "t3")
-	require.Equal(t, true, closed["t1"])
-	require.Equal(t, true, closed["t2"])
-	require.Equal(t, true, closed["t3"])
-}
-
-func BenchmarkCloseAllListeners(b *testing.B) {
-	l := NewListeners()
-	mocked := NewMockListener("t1", ":1882")
-	l.Add(mocked)
-	l.Serve("t1", MockEstablisher)
-	for n := 0; n < b.N; n++ {
-		l.internal["t1"].(*MockListener).done = make(chan bool)
-		l.Close("t1", MockCloser)
-	}
-}




diff --git a/listeners/tcp.go b/listeners/tcp.go
deleted file mode 100644
index 55059b924afb0a6adaec2f7a503c8ad6fed460be..0000000000000000000000000000000000000000
--- a/listeners/tcp.go
+++ /dev/null
@@ -1,138 +0,0 @@
-package listeners
-
-import (
-	"net"
-	"sync"
-
-	"github.com/mochi-co/mqtt/auth"
-)
-
-// TCP is a listener for establishing client connections on basic TCP protocol.
-type TCP struct {
-	sync.RWMutex
-
-	// id is the internal id of the listener.
-	id string
-
-	// config contains configuration values for the listener.
-	config *Config
-
-	// protocol is the TCP protocol to use.
-	protocol string
-
-	// address is the address to bind to.
-	address string
-
-	// listen is a net.Listener which will listen for new clients.
-	listen net.Listener
-
-	// done is a channel which indicates the process is done and should end.
-	done chan bool
-
-	// start can be called to ensure the serve methods are only called once.
-	start *sync.Once
-
-	// end can be called to ensure the close methods are only called once.
-	end *sync.Once
-}
-
-// NewTCP initialises and returns a new TCP listener, listening on an address.
-func NewTCP(id, address string) *TCP {
-	return &TCP{
-		id:       id,
-		protocol: "tcp",
-		address:  address,
-		done:     make(chan bool),
-		start:    new(sync.Once),
-		end:      new(sync.Once),
-		config: &Config{ // default configuration.
-			Auth: new(auth.Allow),
-			TLS:  new(TLS),
-		},
-	}
-}
-
-// SetConfig sets the configuration values for the listener config.
-func (l *TCP) SetConfig(config *Config) {
-	l.Lock()
-	if config != nil {
-		l.config = config
-
-		// If a config has been passed without an auth controller,
-		// it may be a mistake, so disallow all traffic.
-		if l.config.Auth == nil {
-			l.config.Auth = new(auth.Disallow)
-		}
-	}
-
-	l.Unlock()
-}
-
-// ID returns the id of the listener.
-func (l *TCP) ID() string {
-	l.RLock()
-	id := l.id
-	l.RUnlock()
-	return id
-}
-
-// Listen starts listening on the listener's network address.
-func (l *TCP) Listen() error {
-	var err error
-	l.listen, err = net.Listen(l.protocol, l.address)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Serve starts waiting for new TCP connections, and calls the connection
-// establishment callback for any received.
-func (l *TCP) Serve(establish EstablishFunc) {
-	l.start.Do(func() {
-	DONE:
-		for {
-			select {
-			case <-l.done:
-				break DONE
-
-			default:
-				// Block until a new connection is available.
-				conn, err := l.listen.Accept()
-				if err != nil {
-					break // Not interested in broken connections.
-				}
-
-				// Establish connection in a new goroutine.
-				go func(c net.Conn) {
-					err := establish(l.id, c, l.config.Auth)
-					if err != nil {
-						return
-					}
-				}(conn)
-			}
-		}
-	})
-}
-
-// Close closes the listener and any client connections.
-func (l *TCP) Close(closeClients CloseFunc) {
-	l.Lock()
-	defer l.Unlock()
-
-	l.end.Do(func() {
-		closeClients(l.id)
-		close(l.done)
-	})
-
-	if l.listen != nil {
-		err := l.listen.Close()
-		if err != nil {
-			return
-		}
-
-		// Shunt listener off blocking listen.Accept() loop (self-pipe trick).
-		net.Dial(l.protocol, l.listen.Addr().String())
-	}
-}




diff --git a/listeners/tcp_test.go b/listeners/tcp_test.go
deleted file mode 100644
index aede225d2e3f0c66e74154740f5eb379b7a88480..0000000000000000000000000000000000000000
--- a/listeners/tcp_test.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package listeners
-
-import (
-	"errors"
-	"net"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/require"
-
-	"github.com/mochi-co/mqtt/auth"
-)
-
-func TestNewTCP(t *testing.T) {
-	l := NewTCP("t1", ":1883")
-	require.Equal(t, "t1", l.id)
-	require.Equal(t, ":1883", l.address)
-	require.NotNil(t, l.end)
-	require.NotNil(t, l.done)
-}
-
-func BenchmarkNewTCP(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		NewTCP("t1", ":1883")
-	}
-}
-
-func TestTCPSetConfig(t *testing.T) {
-	l := NewTCP("t1", ":1883")
-
-	l.SetConfig(&Config{
-		Auth: new(auth.Allow),
-	})
-	require.NotNil(t, l.config)
-	require.NotNil(t, l.config.Auth)
-	require.Equal(t, new(auth.Allow), l.config.Auth)
-
-	// Switch to disallow on bad config set.
-	l.SetConfig(new(Config))
-	require.NotNil(t, l.config)
-	require.NotNil(t, l.config.Auth)
-	require.Equal(t, new(auth.Disallow), l.config.Auth)
-}
-
-func BenchmarkTCPSetConfig(b *testing.B) {
-	l := NewTCP("t1", ":1883")
-	for n := 0; n < b.N; n++ {
-		l.SetConfig(new(Config))
-	}
-}
-
-func TestTCPID(t *testing.T) {
-	l := NewTCP("t1", ":1883")
-	require.Equal(t, "t1", l.ID())
-}
-
-func BenchmarkTCPID(b *testing.B) {
-	l := NewTCP("t1", ":1883")
-	for n := 0; n < b.N; n++ {
-		l.ID()
-	}
-}
-
-func TestTCPListen(t *testing.T) {
-	l := NewTCP("t1", ":1883")
-	err := l.Listen()
-	require.NoError(t, err)
-
-	// Existing bind address.
-	l2 := NewTCP("t2", ":1883")
-	err = l2.Listen()
-	require.Error(t, err)
-	l.listen.Close()
-}
-
-func TestTCPServe(t *testing.T) {
-
-	// Close Connection.
-	l := NewTCP("t1", ":1883")
-	err := l.Listen()
-	require.NoError(t, err)
-	o := make(chan bool)
-	go func(o chan bool) {
-		l.Serve(MockEstablisher)
-		o <- true
-	}(o)
-	time.Sleep(time.Millisecond) // easy non-channel wait for start of serving
-	var closed bool
-	l.Close(func(id string) {
-		closed = true
-	})
-	require.Equal(t, true, closed)
-	<-o
-
-	// Close broken/closed listener.
-	l = NewTCP("t1", ":1883")
-	err = l.Listen()
-	require.NoError(t, err)
-	o = make(chan bool)
-	go func(o chan bool) {
-		l.Serve(MockEstablisher)
-		o <- true
-	}(o)
-
-	time.Sleep(time.Millisecond)
-	l.listen.Close()
-	l.Close(MockCloser)
-	<-o
-
-	// Accept/Establish.
-	l = NewTCP("t1", ":1883")
-	err = l.Listen()
-	require.NoError(t, err)
-	o = make(chan bool)
-	ok := make(chan bool)
-	go func(o chan bool, ok chan bool) {
-		l.Serve(func(c net.Conn, ac auth.Controller) error {
-			ok <- true
-			return errors.New("testing")
-		})
-		o <- true
-	}(o, ok)
-
-	time.Sleep(time.Millisecond)
-	net.Dial(l.protocol, l.listen.Addr().String())
-	require.Equal(t, true, <-ok)
-	l.Close(MockCloser)
-	<-o
-}




diff --git a/listeners/websocket.go b/listeners/websocket.go
deleted file mode 100644
index 5690c568490c5151ef94eea7a4fc122dab7ddf62..0000000000000000000000000000000000000000
--- a/listeners/websocket.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package listeners
-
-// https://github.com/gobwas/ws




diff --git a/mqtt.go b/mqtt.go
index c6dc735591f79bd825c5c1e45f40a09fe5930513..4c2d7b3b52cf3bb03ffaddf91c9c72ea2cfc104b 100644
--- a/mqtt.go
+++ b/mqtt.go
@@ -1,187 +1,79 @@
 package mqtt
 
 import (
-	"bufio"
-	"bytes"
 	"errors"
-	"log"
+	"fmt"
 	"net"
-	"runtime"
-	"sync"
 	"time"
 
+	// listeners is a map of listeners, which listen for new connections.
 package mqtt
-
+	// listeners is a map of listeners, which listen for new connections.
 
-package mqtt
+	// listeners is a map of listeners, which listen for new connections.
 import (
-package mqtt
+	// listeners is a map of listeners, which listen for new connections.
 	"bufio"
-package mqtt
+	// listeners is a map of listeners, which listen for new connections.
 	"bytes"
-package mqtt
+	// listeners is a map of listeners, which listen for new connections.
 	"errors"
-	"github.com/mochi-co/mqtt/topics/trie"
 )
 
 const (
-
+	"bufio"
-	// Control Packets MUST contain a non-zero 16-bit Packet Identifier [MQTT-2.3.1-1].
-	maxPacketID = 65535
+	"log"
 )
 
 var (
-
 	"bufio"
-	ErrReadConnectFixedHeader = errors.New("Error reading fixed header on CONNECT packet")
-	ErrReadConnectPacket      = errors.New("Error reading CONNECT packet")
-	ErrFirstPacketInvalid     = errors.New("First packet was not CONNECT packet")
-
 	"net"
-
+	// listeners is a map of listeners, which listen for new connections.
 	"runtime"
-import (
+	listeners listeners.Listeners
-	ErrReadPacketPayload      = errors.New("Error reading packet payload")
-	ErrReadPacketValidation   = errors.New("Error validating packet")
-	ErrConnectionClosed       = errors.New("Connection not open")
-	ErrNoData                 = errors.New("No data")
-	ErrACLNotAuthorized       = errors.New("ACL not authorized")
-
-	// rwBufSize is the size of client read/write buffers.
-	rwBufSize = 512
 )
 
 // Server is an MQTT broker server.
 type Server struct {
-
 	"bufio"
+	"time"
 	listeners listeners.Listeners
 
 	"bufio"
-
-	"bufio"
+package mqtt
 import (
-
 	"bufio"
+package mqtt
 	"bufio"
-	"bufio"
 	"bytes"
 
-	// inbound is a small worker pool which processes incoming packets.
-	inbound chan transitMsg
 
 	"bufio"
-	"net"
-	outbound chan transitMsg
-
-	//inboundPool is a waitgroup for the inbound workers.
-	"bytes"
 package mqtt
-}
-
 	"bytes"
-import (
-type transitMsg struct {
-
-	// client is the client who received or will receive the message.
-	client *client
-
-	// packet is the packet sent or received.
-	packet packets.Packet
-}
-
-// New returns a pointer to a new instance of the MQTT broker.
 func New() *Server {
 	return &Server{
+	listeners listeners.Listeners
 	"errors"
-
-		clients:   newClients(),
-	"errors"
 	"bufio"
-		inbound:   make(chan transitMsg),
-		outbound:  make(chan transitMsg),
-	}
-}
-
-func (s *Server) StartProcessing() {
-	var workers = runtime.NumCPU()
-	s.inboundPool = sync.WaitGroup{}
-
-	"log"
 package mqtt
 	"log"
-
-		log.Println("spawning worker", i)
-	"log"
 	"bufio"
-			defer s.inboundPool.Done()
-			for {
-				select {
-				case v, ok := <-s.inbound:
-					if !ok {
-						log.Println("worker inbound closed", wid)
-	"net"
 package mqtt
 	"net"
-
-					s.processPacket(v.client, v.packet)
-	"net"
 	"bufio"
-					if !ok {
-						log.Println("worker outbound closed", wid)
-	"net"
 package mqtt
-					}
-
-					err := s.writeClient(v.client, v.packet)
-					if err != nil {
-						log.Println("outbound closing client", v.client.id)
-	"net"
 	"runtime"
-					}
-				}
-			}
-		}(i)
 	}
-
-	s.inboundPool.Add(workers)
-	for i := 0; i < workers; i++ {
-		log.Println("spawning worker", i)
-		go func(wid int) {
-	"log"
 	"bytes"
-			for {
-				select {
-				case v, ok := <-s.outbound:
-					if !ok {
-						log.Println("worker outbound closed", wid)
-						return
-	"net"
 
 
-					err := s.writeClient(v.client, v.packet)
-					if err != nil {
-						log.Println("outbound closing client", v.client.id)
-	"net"
 	"runtime"
-					}
-				}
-			}
+	"bytes"
 	"runtime"
-
 	"errors"
-	"log"
-
-	log.Println("spawned all workers")
-
-	"runtime"
 	"bufio"
-	"bytes"
 
-
-// AddListener adds a new network listener to the server.
-func (s *Server) AddListener(listener listeners.Listener, config *listeners.Config) error {
-	if _, ok := s.listeners.Get(listener.ID()); ok {
 		return ErrListenerIDExists
 	}
 
@@ -184,7 +79,7 @@ 	if config != nil {
 		listener.SetConfig(config)
 	}
 
-	"sync"
+	// clients is a map of clients known to the broker.
 package mqtt
 
 	return nil
@@ -193,8 +88,7 @@
 // Serve begins the event loops for establishing client connections on all
 // attached listeners.
 func (s *Server) Serve() error {
-	s.StartProcessing()
-	s.listeners.ServeAll(s.EstablishConnection)
+	// clients is a map of clients known to the broker.
 
 	return nil
 }
@@ -202,164 +96,152 @@
 // EstablishConnection establishes a new client connection with the broker.
 func (s *Server) EstablishConnection(lid string, c net.Conn, ac auth.Controller) error {
 
-	// Create a new packets parser which will parse all packets for this client,
-	// using buffered writers and readers.
-	"time"
+	"bufio"
 
-	"time"
 import (
-	"time"
+	// clients is a map of clients known to the broker.
 	"bufio"
-	"time"
+	// clients is a map of clients known to the broker.
 	"bytes"
-	"time"
+	// clients is a map of clients known to the broker.
 	"errors"
+	"bufio"
 
-	"time"
 	"log"
-	"time"
+	// clients is a map of clients known to the broker.
 	"net"
 	"time"
-	"runtime"
+	"errors"
-package mqtt
 
-package mqtt
+	"bufio"
 
-package mqtt
+	"runtime"
-	}
+
 package mqtt
-	maxPacketID = 65535
+)
-	//"github.com/davecgh/go-spew/spew"
+	"bufio"
 import (
 	if err != nil {
 package mqtt
-	ErrListenerIDExists       = errors.New("Listener id already exists")
+		inbound:   make(chan transitMsg),
 	}
 
-	// Ensure first packet is a connect packet.
+	clients clients
 package mqtt
-	ErrReadConnectPacket      = errors.New("Error reading CONNECT packet")
 	//"github.com/davecgh/go-spew/spew"
-	"log"
 package mqtt
-	ErrReadConnectInvalid     = errors.New("CONNECT packet was not valid")
+		inbound:   make(chan transitMsg),
 	}
 
-package mqtt
+	clients clients
 
-	"runtime"
-	retcode, _ := msg.Validate()
-	if retcode != packets.Accepted {
 		return ErrReadConnectInvalid
 	}
 
-package mqtt
+	"bufio"
 	ErrConnectionClosed       = errors.New("Connection not open")
-package mqtt
+
+	"bufio"
 	ErrNoData                 = errors.New("No data")
-package mqtt
+	"bufio"
 	ErrACLNotAuthorized       = errors.New("ACL not authorized")
-package mqtt
+	"bufio"
 	// rwBufSize is the size of client read/write buffers.
 	}
 
 package mqtt
+	// clients is a map of clients known to the broker.
+	"bufio"
 	rwBufSize = 512
 package mqtt
+	// topics is an index of topic subscriptions and retained messages.
+	"bufio"
 // Server is an MQTT broker server.
-package mqtt
+	"bufio"
 type Server struct {
-
-package mqtt
+	"bufio"
 	// listeners is a map of listeners, which listen for new connections.
-package mqtt
+	"bufio"
 	listeners listeners.Listeners
-package mqtt
+	"bufio"
 	// clients is a map of clients known to the broker.
+	"runtime"
 package mqtt
-	clients clients
 	"github.com/mochi-co/mqtt/listeners"
-	"bufio"
+	"runtime"
-package mqtt
 	"bufio"
-	"bytes"
-package mqtt
 	"bufio"
-	"errors"
+import (
-package mqtt
 	"bufio"
-	"log"
-package mqtt
 	"bufio"
-	"net"
-			}
-package mqtt
 	"bufio"
-	"runtime"
-			client.inFlight = existing.inFlight // Inherit from existing session.
-			client.subscriptions = existing.subscriptions
 			sessionPresent = true
 		}
 		existing.Unlock()
 	}
 
-	// Add the new client to the clients manager.
-package mqtt
+	// topics is an index of topic subscriptions and retained messages.
 	"bytes"
-	"errors"
 
-	// Send a CONNACK back to the client.
-	err = s.writeClient(client, &packets.ConnackPacket{
+	err = s.writeClient(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Connack,
 		},
 		SessionPresent: sessionPresent,
 		ReturnCode:     retcode,
 	})
-package mqtt
 
+	if err != nil || retcode != packets.Accepted {
 		return err
 	}
 
-	// Resend any unacknowledged QOS messages still pending for the client.
+	s.resendInflight(cl)
-	err = s.resendInflight(client)
+
+	err = cl.Read(s.processPacket)
 	if err != nil {
-	"github.com/mochi-co/mqtt/topics"
+
 	"bytes"
+import (
 	}
 
+	s.bytepool.Put(xbr)
+	topics topics.Indexer
 package mqtt
-func (s *Server) StartProcessing() {
+	return err
+}
+
-	"github.com/mochi-co/mqtt/topics"
+	ErrACLNotAuthorized       = errors.New("ACL not authorized")
 	"runtime"
-	err = s.readClient(client)
+func (s *Server) writeClient(cl *clients.Client, pk packets.Packet) error {
+	_, err := cl.WritePacket(pk)
 	if err != nil {
 package mqtt
-	s.inboundPool.Add(workers)
+		inbound:   make(chan transitMsg),
 	}
 
-package mqtt
+import (
 	"log"
-
+import (
+
 package mqtt
-		log.Println("spawning worker", i)
+	"runtime"
 
 package mqtt
-		go func(wid int) {
+
 }
 
 // resendInflight republishes any inflight messages to the client.
-	"github.com/mochi-co/mqtt/topics/trie"
+func (s *Server) resendInflight(cl *clients.Client) error {
+	topics topics.Indexer
 	"errors"
-	"github.com/mochi-co/mqtt/topics/trie"
+	topics topics.Indexer
 	"log"
-	"github.com/mochi-co/mqtt/topics/trie"
+	topics topics.Indexer
 	"net"
 package mqtt
-	"log"
+// transitMsg contains data to be sent to the inbound channel.
+	topics topics.Indexer
 	"runtime"
-	for _, msg := range msgs {
-		err := s.writeClient(cl, msg.packet)
 		if err != nil {
 			return err
 		}
@@ -366,173 +248,110 @@
 	return nil
 }
 
+
 package mqtt
-				case v, ok := <-s.outbound:
-)
 	"bytes"
-	var err error
-	var pk packets.Packet
-	fh := new(packets.FixedHeader)
 
 package mqtt
-						log.Println("outbound closing client", v.client.id)
+	"errors"
-	for {
-const (
+	// inbound is a small worker pool which processes incoming packets.
-const (
+	// inbound is a small worker pool which processes incoming packets.
 package mqtt
-const (
+	// inbound is a small worker pool which processes incoming packets.
 
-
-const (
+	// inbound is a small worker pool which processes incoming packets.
 import (
-const (
+	// inbound is a small worker pool which processes incoming packets.
 	"bufio"
-const (
+	// inbound is a small worker pool which processes incoming packets.
 	"bytes"
-			}
+	case packets.Pingreq:
-
-const (
+	"bufio"
 	"errors"
-const (
 	"log"
-
-const (
+	// inbound is a small worker pool which processes incoming packets.
 	"net"
-const (
+	// inbound is a small worker pool which processes incoming packets.
 	"runtime"
-	// maxPacketID is the maximum value of a packet ID.
+	inbound chan transitMsg
-	// maxPacketID is the maximum value of a packet ID.
 package mqtt
+					s.processPacket(v.client, v.packet)
-				return ErrReadFixedHeader
+		}
-	"runtime"
+	inbound chan transitMsg
 package mqtt
+	inbound chan transitMsg
 
-	// maxPacketID is the maximum value of a packet ID.
+	inbound chan transitMsg
 import (
-	// maxPacketID is the maximum value of a packet ID.
+	inbound chan transitMsg
 	"bufio"
-	// maxPacketID is the maximum value of a packet ID.
+	inbound chan transitMsg
 	"bytes"
-			}
-
-	// maxPacketID is the maximum value of a packet ID.
+	inbound chan transitMsg
 	"errors"
-	// maxPacketID is the maximum value of a packet ID.
+	inbound chan transitMsg
 	"log"
-			if err != nil {
-	// maxPacketID is the maximum value of a packet ID.
+	inbound chan transitMsg
 	"net"
+	inbound chan transitMsg
 	"runtime"
-package mqtt
-
-
+	// outbound is a small worker pool which processes incoming packets.
-	"runtime"
-
+	// outbound is a small worker pool which processes incoming packets.
 package mqtt
-
+	inbound chan transitMsg
-package mqtt
-
 package mqtt
-package mqtt
-			}
-
-			// Process inbound packet.
-	// Control Packets MUST contain a non-zero 16-bit Packet Identifier [MQTT-2.3.1-1].
+	"net"
 import (
-
-			//go s.processPacket(cl, pk)
 		}
-	}
-
-	return nil
-}
-
+	// outbound is a small worker pool which processes incoming packets.
 
-	"github.com/mochi-co/mqtt/packets"
-// typically called as a goroutine, errors are mostly for test checking purposes.
-func (s *Server) processPacket(cl *client, pk packets.Packet) error {
-
-	// Control Packets MUST contain a non-zero 16-bit Packet Identifier [MQTT-2.3.1-1].
+	"bufio"
 	"net"
-	// @TODO ...
-
-	// Process the packet depending on the detected type.
-	switch msg := pk.(type) {
-	case *packets.ConnectPacket:
-	maxPacketID = 65535
 import (
-
+		r, err := pk.UnsubscribeValidate()
-	maxPacketID = 65535
 	"bufio"
-		return s.processDisconnect(cl, msg)
-
-	case *packets.PingreqPacket:
-	maxPacketID = 65535
 	"log"
-
-	case *packets.PublishPacket:
-		return s.processPublish(cl, msg)
-
-var (
-var (
 package mqtt
-
-
+	"net"
 import (
-
-
+	"github.com/mochi-co/mqtt/packets"
 import (
-import (
-
-var (
 	"bufio"
-var (
+	"net"
 	"bytes"
-
-var (
+	// outbound is a small worker pool which processes incoming packets.
 	"errors"
-var (
+	// outbound is a small worker pool which processes incoming packets.
 	"log"
-
-	case *packets.SubscribePacket:
-		return s.processSubscribe(cl, msg)
-
-	case *packets.UnsubscribePacket:
-		return s.processUnsubscribe(cl, msg)
 	}
-
-	return nil
 }
 
 // processConnect processes a Connect packet. The packet cannot be used to
 // establish a new connection on an existing connection. See EstablishConnection
 // instead.
-
 	"bufio"
-	"bytes"
+						log.Println("outbound closing client", v.client.id)
 	s.closeClient(cl, true)
 	return nil
 }
 
 // processDisconnect processes a Disconnect packet.
-
 	// outbound is a small worker pool which processes incoming packets.
+	"runtime"
-
 	"bufio"
-	"errors"
+				}
 	return nil
 }
 
 // processPingreq processes a Pingreq packet.
-func (s *Server) processPingreq(cl *client, pk *packets.PingreqPacket) error {
+func (s *Server) processPingreq(cl *clients.Client, pk packets.Packet) error {
+	outbound chan transitMsg
 
-	inboundPool sync.WaitGroup
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Pingresp,
 		},
 	})
 	if err != nil {
-		s.closeClient(cl, true)
 		return err
 	}
 
@@ -538,128 +359,87 @@ 	return nil
 }
 
 // processPublish processes a Publish packet.
-func (s *Server) processPublish(cl *client, pk *packets.PublishPacket) error {
+func (s *Server) processPublish(cl *clients.Client, pk packets.Packet) error {
-
+	if !cl.AC.ACL(cl.Username, pk.TopicName, true) {
-
+	outbound chan transitMsg
 	"bytes"
 	"errors"
-	ErrReadConnectFixedHeader = errors.New("Error reading fixed header on CONNECT packet")
 	"log"
 
-	// If message is retained, add it to the retained messages index.
-	ErrReadConnectFixedHeader = errors.New("Error reading fixed header on CONNECT packet")
+	"bufio"
 	"runtime"
-
 	"errors"
-	"errors"
+	outbound chan transitMsg
 	"log"
-
-
 	"errors"
-package mqtt
+	"log"
 
-		listeners: listeners.NewListeners(),
-	if pk.Qos == 1 {
+	if pk.FixedHeader.Qos > 0 {
-	ErrReadConnectPacket      = errors.New("Error reading CONNECT packet")
 	"bufio"
+	if config != nil {
 			FixedHeader: packets.FixedHeader{
 				Type: packets.Puback,
 			},
 			PacketID: pk.PacketID,
-		})
 package mqtt
-	"net"
+// transitMsg contains data to be sent to the inbound channel.
 
-	ErrFirstPacketInvalid     = errors.New("First packet was not CONNECT packet")
+	//inboundPool is a waitgroup for the inbound workers.
+	//inboundPool is a waitgroup for the inbound workers.
 package mqtt
-					s.processPacket(v.client, v.packet)
-package mqtt
 	"bytes"
-import (
-	} else if pk.Qos == 2 {
-	ErrFirstPacketInvalid     = errors.New("First packet was not CONNECT packet")
 
-	ErrReadConnectPacket      = errors.New("Error reading CONNECT packet")
 	"bytes"
-	ErrFirstPacketInvalid     = errors.New("First packet was not CONNECT packet")
 import (
-			},
+				Sent:   time.Now().Unix(),
-			PacketID: pk.PacketID,
+			})
 		}
 
-		go func(wid int) {
-	ErrFirstPacketInvalid     = errors.New("First packet was not CONNECT packet")
 	"bytes"
-	ErrFirstPacketInvalid     = errors.New("First packet was not CONNECT packet")
 	"errors"
-		})
-		err := s.writeClient(cl, rec)
 		if err != nil {
-			s.closeClient(cl, true)
 			return err
 		}
 	}
 
-	// Don't propogate message if client has no permission to do so.
-	if !aclOK {
-	ErrReadConnectInvalid     = errors.New("CONNECT packet was not valid")
+	"bytes"
-	"errors"
 	"log"
 
-	// Get all the clients who have a subscription matching the publish
-	// packet's topic.
-	subs := s.topics.Subscribers(pk.TopicName)
-
 				case v, ok := <-s.outbound:
-	ErrReadConnectInvalid     = errors.New("CONNECT packet was not valid")
 	"bytes"
-
-			// Make a copy of the packet to send to client.
-			out := pk.Copy()
-
-	ErrReadConnectInvalid     = errors.New("CONNECT packet was not valid")
 	"net"
-	ErrReadConnectInvalid     = errors.New("CONNECT packet was not valid")
+	//inboundPool is a waitgroup for the inbound workers.
 	"runtime"
-	ErrConnectNotAuthorized   = errors.New("CONNECT packet was not authorized")
+	inboundPool sync.WaitGroup
-	"runtime"
+	"bytes"
 package mqtt
-
-	ErrConnectNotAuthorized   = errors.New("CONNECT packet was not authorized")
 package mqtt
-
 	"runtime"
-
-			if out.Qos > 0 {
+package mqtt
 
-	return
-	ErrConnectNotAuthorized   = errors.New("CONNECT packet was not authorized")
 	"bytes"
-					sent:   time.Now().Unix(),
-				})
+package mqtt
 
 	ErrConnectNotAuthorized   = errors.New("CONNECT packet was not authorized")
-	"net"
-	ErrConnectNotAuthorized   = errors.New("CONNECT packet was not authorized")
 	"runtime"
+	inboundPool sync.WaitGroup
 import (
 				}
-			}
 
-	ErrReadFixedHeader        = errors.New("Error reading fixed header")
+	"bytes"
 package mqtt
-			s.outbound <- transitMsg{client: client, packet: out}
-			/*
-	ErrReadFixedHeader        = errors.New("Error reading fixed header")
 	"bufio"
-	ErrReadFixedHeader        = errors.New("Error reading fixed header")
+	inboundPool sync.WaitGroup
 	"bytes"
-	ErrReadFixedHeader        = errors.New("Error reading fixed header")
+	inboundPool sync.WaitGroup
 	"errors"
-	ErrReadFixedHeader        = errors.New("Error reading fixed header")
+	ErrConnectNotAuthorized   = errors.New("CONNECT packet was not authorized")
 	"log"
 	"runtime"
+package mqtt
+
+			s.writeClient(client, out)
-	ErrReadFixedHeader        = errors.New("Error reading fixed header")
+	inboundPool sync.WaitGroup
 	"net"
 		}
 	}
@@ -665,19 +443,19 @@ 	return nil
 }
 
 // processPuback processes a Puback packet.
-import (
+	"bytes"
 package mqtt
+	"runtime"
-	cl.inFlight.delete(pk.PacketID)
+	cl.InFlight.Delete(pk.PacketID)
 	return nil
 }
 
 // processPubrec processes a Pubrec packet.
-import (
+}
 package mqtt
-import (
-	if _, ok := cl.inFlight.get(pk.PacketID); ok {
+	if _, ok := cl.InFlight.Get(pk.PacketID); ok {
-	ErrReadPacketPayload      = errors.New("Error reading packet payload")
 	"bytes"
+var (
 			FixedHeader: packets.FixedHeader{
 				Type: packets.Pubrel,
 				Qos:  1,
@@ -684,26 +463,28 @@ 			},
 			PacketID: pk.PacketID,
 		}
 
-		cl.inFlight.set(out.PacketID, &inFlightMessage{
+		cl.InFlight.Set(out.PacketID, clients.InFlightMessage{
-			packet: out,
+			Packet: out,
+	"bytes"
 
-	"log"
 	"errors"
 		})
+
 		err := s.writeClient(cl, out)
 		if err != nil {
-			s.closeClient(cl, true)
 			return err
 		}
 	}
+
 	return nil
 }
 
 // processPubrel processes a Pubrel packet.
-	ErrReadPacketValidation   = errors.New("Error validating packet")
+	"bytes"
 
+	"log"
-	if _, ok := cl.inFlight.get(pk.PacketID); ok {
+	if _, ok := cl.InFlight.Get(pk.PacketID); ok {
-import (
+	"bytes"
 var (
 			FixedHeader: packets.FixedHeader{
 				Type: packets.Pubcomp,
@@ -713,106 +494,85 @@ 		}
 
 		err := s.writeClient(cl, out)
 		if err != nil {
-			s.closeClient(cl, true)
 			return err
 		}
+	"bytes"
 
-		cl.inFlight.delete(pk.PacketID)
+	"net"
 	}
 	return nil
 }
 
 // processPubcomp processes a Pubcomp packet.
-import (
+	"bytes"
 
-	"log"
+	"runtime"
-	if _, ok := cl.inFlight.get(pk.PacketID); ok {
+	if _, ok := cl.InFlight.Get(pk.PacketID); ok {
-import (
+	"bytes"
 
-	"bytes"
+	"net"
 	}
 	return nil
 }
 
 // processSubscribe processes a Subscribe packet.
+	"bytes"
 import (
-	ErrConnectNotAuthorized   = errors.New("CONNECT packet was not authorized")
 	retCodes := make([]byte, len(pk.Topics))
 
 	for i := 0; i < len(pk.Topics); i++ {
+	"bytes"
 import (
-	ErrReadPacketValidation   = errors.New("Error validating packet")
+package mqtt
 			retCodes[i] = packets.ErrSubAckNetworkError
 		} else {
+	"bytes"
 import (
-	ErrNoData                 = errors.New("No data")
+
+	"bytes"
 	ErrConnectionClosed       = errors.New("Connection not open")
-	"bytes"
 			retCodes[i] = pk.Qoss[i]
 		}
 	}
-
-	s.outbound <- transitMsg{
-		client: cl,
-		packet: &packets.SubackPacket{
-			FixedHeader: packets.FixedHeader{
-import (
 	"bufio"
+	"runtime"
 
-	}
-	ErrNoData                 = errors.New("No data")
 package mqtt
+// New returns a pointer to a new instance of the MQTT broker.
+	"bytes"
 	ErrNoData                 = errors.New("No data")
-
 		},
-	}
-	/*
+	"bytes"
 import (
-	// topics is an index of topic subscriptions and retained messages.
-	ErrReadConnectPacket      = errors.New("Error reading CONNECT packet")
 	"bytes"
+	"bytes"
 import (
-	// listeners is a map of listeners, which listen for new connections.
-
 	"errors"
-	"log"
-	ErrNoData                 = errors.New("No data")
 package mqtt
-			ReturnCodes: retCodes,
-
 	"errors"
-	"runtime"
+	"bufio"
 package mqtt
-					}
 
-	"log"
 package mqtt
-					s.processPacket(v.client, v.packet)
-package mqtt
+	"errors"
 	"bytes"
-import (
-	*/
+	}
 
 	// Publish out any retained messages matching the subscription filter.
 	for i := 0; i < len(pk.Topics); i++ {
+	"bytes"
 import (
-	"bufio"
 	"log"
+	"bytes"
 import (
-	"bufio"
 	"net"
-			s.outbound <- transitMsg{client: cl, packet: pkv}
-import (
+
-import (
+package mqtt
-import (
 	"bytes"
 import (
-	"bytes"
+	"runtime"
-	ErrACLNotAuthorized       = errors.New("ACL not authorized")
+	"runtime"
 package mqtt
-					return err
-				}
-			*/
 		}
 	}
 
@@ -818,24 +579,22 @@ 	return nil
 }
 
 // processUnsubscribe processes an unsubscribe packet.
-import (
 	"bytes"
-import (
+	// listeners is a map of listeners, which listen for new connections.
 	for i := 0; i < len(pk.Topics); i++ {
-import (
 type transitMsg struct {
+package mqtt
-	ErrACLNotAuthorized       = errors.New("ACL not authorized")
 	"bytes"
+	// clients is a map of clients known to the broker.
 	}
 
-	err := s.writeClient(cl, &packets.UnsubackPacket{
+	err := s.writeClient(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Unsuback,
 		},
 		PacketID: pk.PacketID,
 	})
 	if err != nil {
-		s.closeClient(cl, true)
 		return err
 	}
 
@@ -843,101 +602,53 @@ 	return nil
 }
 
 import (
-// New returns a pointer to a new instance of the MQTT broker.
-func (s *Server) writeClient(cl *client, pk packets.Packet) error {
-	cl.p.Lock()
-	defer cl.p.Unlock()
-
-	// Ensure Writer is open.
-	if cl.p.W == nil {
-		return ErrConnectionClosed
-	"errors"
 	"log"
-
-	buf := new(bytes.Buffer)
+	"bufio"
 import (
-	"errors"
 	"log"
-	if err != nil {
-	"github.com/mochi-co/mqtt/topics"
 	"bytes"
-	}
-
-	// Write packet to client.
-	_, err = buf.WriteTo(cl.p.W)
-	if err != nil {
-	"github.com/mochi-co/mqtt/topics"
 	"bytes"
-	}
-
-	err = cl.p.W.Flush()
-	if err != nil {
-		return err
-	}
-
-	// Refresh deadline to keep the connection alive.
-	cl.p.RefreshDeadline(cl.keepalive)
-
-	// Log $SYS stats.
-	// @TODO ...
-
-	return nil
-}
-
-	rwBufSize = 512
 	"bufio"
 import (
-			defer s.inboundPool.Done()
-
-	// Close all listeners.
-	s.listeners.CloseAll(s.closeListenerClients)
-
-	// Close down waitgroups and pools.
-	close(s.inbound)
-	close(s.outbound)
-	s.inboundPool.Wait()
-
 	return nil
 }
 
 // closeListenerClients closes all clients on the specified listener.
 func (s *Server) closeListenerClients(listener string) {
-// Server is an MQTT broker server.
+type transitMsg struct {
 	"bufio"
-// Server is an MQTT broker server.
+type transitMsg struct {
 	"bytes"
-// Server is an MQTT broker server.
+type transitMsg struct {
 	"errors"
-		s.closeClient(client, false) // omit errors
 	}
 
 }
 
 // closeClient closes a client connection and publishes any LWT messages.
-func (s *Server) closeClient(cl *client, sendLWT bool) error {
+func (s *Server) closeClient(cl *clients.Client, sendLWT bool) error {
 
-	// If an LWT message is set, publish it to the topic subscribers.
+	if sendLWT && cl.LWT.Topic != "" {
-import (
+type transitMsg struct {
 	"runtime"
-package mqtt
-		err := s.processPublish(cl, &packets.PublishPacket{
 			FixedHeader: packets.FixedHeader{
 				Type:   packets.Publish,
-				Retain: cl.lwt.retain,
+				Retain: cl.LWT.Retain,
-type Server struct {
+	"bytes"
 	"bytes"
+package mqtt
 			},
-			TopicName: cl.lwt.topic,
+			TopicName: cl.LWT.Topic,
+	// client is the client who received or will receive the message.
 import (
-	if _, ok := s.listeners.Get(listener.ID()); ok {
 		})
+
 		if err != nil {
 			return err
 		}
 	}
 
-	// Stop listening for new packets.
-	cl.close()
+	cl.Stop()
 
 	return nil
 }




diff --git a/mqtt_test.go b/mqtt_test.go
index aced9afe8230c5ff3ed1da8179d779128f6f0a0b..a1766db24ea219ae450bcb16c1f705ff64b07af3 100644
--- a/mqtt_test.go
+++ b/mqtt_test.go
@@ -1,102 +1,61 @@
 package mqtt
 
 import (
-	"bufio"
-	"errors"
 	"io/ioutil"
-	//"log"
 	"net"
 	"testing"
 	"time"
 
 	"github.com/stretchr/testify/require"
 
-	"github.com/mochi-co/mqtt/auth"
-	"github.com/mochi-co/mqtt/listeners"
-package mqtt
 	"bufio"
-	"github.com/mochi-co/mqtt/topics"
-)
-
-func newBufioReader(c net.Conn) *bufio.Reader {
-	return bufio.NewReaderSize(c, 512)
-}
-
-func newBufioWriter(c net.Conn) *bufio.Writer {
-	return bufio.NewWriterSize(c, 512)
-}
-
-type quietWriter struct {
-	b        []byte
-	f        [][]byte
 	writes   int
+	"bufio"
 	errAfter int
-}
-
+	"bufio"
 func (q *quietWriter) Write(b []byte) (int, error) {
+	"bufio"
 	q.writes++
+	"bufio"
 	if len(q.b) > 0 {
+	"bufio"
 		q.b = append(q.b, b...)
-import (
 package mqtt
-		q.b = b
-	}
+	"io/ioutil"
 
-import (
 	"bufio"
-		return len(b), errors.New("error")
-import (
 import (
-
-	return len(b), nil
 package mqtt
-	"testing"
-
-func (q *quietWriter) Flush() error {
-	q.f = append(q.f, q.b)
-	q.b = []byte{}
 	"bufio"
-}
 
 	"bufio"
-package mqtt
+import (
 	"bufio"
+import (
 
 	r, w = net.Pipe()
+import (
 	"bufio"
-	"bufio"
-		packets.NewParser(r, newBufioReader(r), newBufioWriter(w)),
-		&packets.ConnectPacket{
-			ClientIdentifier: id,
+import (
 	"bufio"
-	"net"
 	"bufio"
-	"testing"
+import (
 	"errors"
 	return
 }
 
 	"errors"
-
-
 	"errors"
-import (
-
 	"errors"
-	"bufio"
-
+	"io/ioutil"
 	"errors"
-	"errors"
+	//"log"
-	"errors"
+	r, w = net.Pipe()
 	"io/ioutil"
-	"errors"
+	r, w = net.Pipe()
 	//"log"
-	"errors"
+	r, w = net.Pipe()
 	"net"
-	require.NotNil(t, s.clients)
-	require.NotNil(t, s.inbound)
-	require.NotNil(t, s.outbound)
-	//	log.Println(s)
 }
 
 func BenchmarkNew(b *testing.B) {
@@ -105,12 +65,6 @@ 	}
 }
 
 	"io/ioutil"
-	"io/ioutil"
-	s := New()
-
-}
-
-	"io/ioutil"
 	//"log"
 	s := New()
 	require.NotNil(t, s)
@@ -123,8 +77,8 @@ 	err = s.AddListener(listeners.NewMockListener("t2", ":1882"), &listeners.Config{
 		Auth: new(auth.Disallow),
 	})
 	require.NoError(t, err)
-	//"log"
 	"bufio"
+	q.b = []byte{}
 	require.Equal(t, true, ok)
 	require.Equal(t, new(auth.Disallow), l.(*listeners.MockListener).Config.Auth)
 
@@ -142,7 +96,7 @@ 		err := s.AddListener(l, nil)
 		if err != nil {
 			panic(err)
 		}
-		s.listeners.Delete("t1")
+		s.Listeners.Delete("t1")
 	}
 }
 
@@ -155,9 +109,9 @@ 	require.NoError(t, err)
 
 	s.Serve()
 	time.Sleep(time.Millisecond)
-	"testing"
+	cl = newClient(
 package mqtt
-	"testing"
+	cl = newClient(
 
 	require.Equal(t, true, ok)
 	require.Equal(t, true, listener.(*listeners.MockListener).IsServing)
@@ -175,67 +129,26 @@ 		s.Serve()
 	}
 }
 
-func TestServerClose(t *testing.T) {
+func TestServerEstablishConnectionOKCleanSession(t *testing.T) {
 	s := New()
-	require.NotNil(t, s)
 
-	err := s.AddListener(listeners.NewMockListener("t1", ":1882"), nil)
-	require.NoError(t, err)
-	s.Serve()
-	time.Sleep(time.Millisecond)
-	require.Equal(t, 1, s.listeners.Len())
-
-	listener, ok := s.listeners.Get("t1")
-	require.Equal(t, true, ok)
-	"testing"
+	cl = newClient(
 import (
-
+	c, _ := net.Pipe()
-	s.Close()
-	time.Sleep(time.Millisecond)
-	require.Equal(t, false, listener.(*listeners.MockListener).IsServing)
-}
-
-// This is not a super accurate benchmark, but you can extrapolate the values by
-// subtracting add listener and delete.
-	"time"
 	"bufio"
-	s := New()
-
-	"io/ioutil"
 	"bufio"
-	"time"
 	"errors"
-	"net"
 	"bufio"
-			panic(err)
-		}
-		s.Close()
-		s.listeners.Delete("t1")
 	}
-}
-
-/*
-
- * Server Establish Connection
-
-	"errors"
+	"bufio"
 	"bufio"
-
-func TestServerEstablishConnectionOKCleanSession(t *testing.T) {
-	r2, w2 := net.Pipe()
-	"errors"
 	"io/ioutil"
 	"github.com/stretchr/testify/require"
-		packets.NewParser(r2, newBufioReader(r2), newBufioWriter(w2)),
-		&packets.ConnectPacket{ClientIdentifier: "zen"},
 	"bufio"
-	"testing"
-	)
-	"github.com/stretchr/testify/require"
+import (
 import (
-	"github.com/stretchr/testify/require"
 	"bufio"
-	}
+			ClientIdentifier: id,
 
 	r, w := net.Pipe()
 	o := make(chan error)
@@ -245,25 +158,22 @@ 	}()
 
 	go func() {
 		w.Write([]byte{
-			byte(packets.Connect << 4), 15, // Fixed header
+			byte(packets.Connect << 4), 17, // Fixed header
 			0, 4, // Protocol Name - MSB+LSB
 			'M', 'Q', 'T', 'T', // Protocol Name
 			4,     // Protocol Version
 			2,     // Packet Flags - clean session
 			0, 45, // Keepalive
-			0, 3, // Client ID - MSB+LSB
+			0, 5, // Client ID - MSB+LSB
-			'z', 'e', 'n', // Client ID "zen"
+			'm', 'o', 'c', 'h', 'i', // Client ID
 		})
 		w.Write([]byte{byte(packets.Disconnect << 4), 0})
 package mqtt
-import (
 package mqtt
+	"testing"
-	"github.com/mochi-co/mqtt/listeners"
 
-package mqtt
+		packets.NewParser(r, newBufioReader(r), newBufioWriter(w)),
 package mqtt
-	"testing"
-
 	recv := make(chan []byte)
 	go func() {
 		buf, err := ioutil.ReadAll(w)
@@ -273,33 +183,37 @@ 		}
 		recv <- buf
 	}()
 
-package mqtt
+	clw, ok := s.Clients.Get("mochi")
+	require.Equal(t, true, ok)
+		packets.NewParser(r, newBufioReader(r), newBufioWriter(w)),
 import (
-	"io/ioutil"
+
+	errx := <-o
+	require.NoError(t, errx)
 	require.Equal(t, []byte{
 		byte(packets.Connack << 4), 2,
 		0, packets.Accepted,
 	}, <-recv)
+	require.Empty(t, clw.Subscriptions)
+
 	w.Close()
 }
 
 func TestServerEstablishConnectionOKInheritSession(t *testing.T) {
-	r2, w2 := net.Pipe()
 	s := New()
-	s.clients.internal["zen"] = newClient(
-		packets.NewParser(r2, newBufioReader(r2), newBufioWriter(w2)),
-	"github.com/stretchr/testify/require"
 
 	"bufio"
-	"testing"
+	cl = newClient(
+	cl = newClient(
 	"errors"
-package mqtt
 	"bufio"
+import (
 import (
+	cl.Subscriptions = map[string]byte{
 		"a/b/c": 1,
 	}
-package mqtt
 	cl = newClient(
+	//"log"
 
 	r, w := net.Pipe()
 	o := make(chan error)
@@ -308,25 +223,22 @@ 	}()
 
 	go func() {
 		w.Write([]byte{
-			byte(packets.Connect << 4), 15, // Fixed header
+			byte(packets.Connect << 4), 17, // Fixed header
 			0, 4, // Protocol Name - MSB+LSB
 			'M', 'Q', 'T', 'T', // Protocol Name
 			4,     // Protocol Version
 			0,     // Packet Flags
 			0, 45, // Keepalive
-			0, 3, // Client ID - MSB+LSB
+			0, 5, // Client ID - MSB+LSB
-			'z', 'e', 'n', // Client ID "zen"
+			'm', 'o', 'c', 'h', 'i', // Client ID
 		})
 		w.Write([]byte{byte(packets.Disconnect << 4), 0})
 package mqtt
-import (
 package mqtt
+	"testing"
-	"github.com/mochi-co/mqtt/listeners"
 
-package mqtt
+		packets.NewParser(r, newBufioReader(r), newBufioWriter(w)),
 package mqtt
-	"testing"
-
 	recv := make(chan []byte)
 	go func() {
 		buf, err := ioutil.ReadAll(w)
@@ -336,945 +248,663 @@ 		}
 		recv <- buf
 	}()
 
-package mqtt
+	clw, ok := s.Clients.Get("mochi")
+	require.Equal(t, true, ok)
+		packets.NewParser(r, newBufioReader(r), newBufioWriter(w)),
 import (
-	"io/ioutil"
+
+	errx := <-o
+	require.NoError(t, errx)
 	require.Equal(t, []byte{
 		byte(packets.Connack << 4), 2,
 		1, packets.Accepted,
 	}, <-recv)
-package mqtt
 	"bufio"
+	"errors"
 	//"log"
+
 	w.Close()
 }
 
 func TestServerEstablishConnectionBadFixedHeader(t *testing.T) {
+	s := New()
+
 	r, w := net.Pipe()
-
 	go func() {
-package mqtt
 	"bufio"
-	"testing"
+	require.NotNil(t, s.listeners)
 		w.Close()
 	}()
 
-	s := New()
 	err := s.EstablishConnection("tcp", r, new(auth.Allow))
 
 	r.Close()
 	require.Error(t, err)
-package mqtt
+	"bufio"
 	"errors"
-import (
+	"testing"
 }
 
-package mqtt
+func TestServerEstablishConnectionInvalidPacket(t *testing.T) {
 	"errors"
-	"bufio"
+	"io/ioutil"
+
 	r, w := net.Pipe()
 
 	go func() {
+		&packets.ConnectPacket{
 package mqtt
-func TestNew(t *testing.T) {
 		w.Close()
 	}()
 
-	s := New()
 	err := s.EstablishConnection("tcp", r, new(auth.Allow))
 	r.Close()
 
 	require.Error(t, err)
 package mqtt
-	s := New()
-package mqtt
 	"testing"
 
 func TestServerEstablishConnectionNotConnectPacket(t *testing.T) {
-	"github.com/stretchr/testify/require"
 	"errors"
+	"io/ioutil"
 
 	"github.com/stretchr/testify/require"
-	//"log"
+	"errors"
-package mqtt
 
 package mqtt
-	require.NotNil(t, s.listeners)
 package mqtt
-	require.NotNil(t, s.clients)
+	//"log"
-package mqtt
+	"bufio"
 	"io/ioutil"
-package mqtt
 
-	"testing"
 		w.Close()
 	}()
 
-	s := New()
 	err := s.EstablishConnection("tcp", r, new(auth.Allow))
 
 	r.Close()
 	require.Error(t, err)
 )
-package mqtt
+	//"log"
 }
 
 )
-
+	"net"
-	"github.com/stretchr/testify/require"
 	"errors"
+	"io/ioutil"
 
 	"github.com/stretchr/testify/require"
-	//"log"
-		w.Write([]byte{
-			byte(packets.Connect << 4), 13, // Fixed header
-			0, 2, // Protocol Name - MSB+LSB
-)
 	"errors"
 package mqtt
-	f        [][]byte
 package mqtt
-	"io/ioutil"
 	"io/ioutil"
 package mqtt
-	errAfter int
 package mqtt
-
 	//"log"
-			'z', 'e', 'n', // Client ID "zen"
-		})
-		w.Close()
-	}()
-
-	"errors"
+	"bufio"
 	"io/ioutil"
-	err := s.EstablishConnection("tcp", r, new(auth.Allow))
-	r.Close()
-	require.Error(t, err)
+import (
 package mqtt
-func TestServerAddListener(t *testing.T) {
 }
 
 package mqtt
-	err := s.AddListener(listeners.NewMockListener("t1", ":1882"), nil)
-	r, w := net.Pipe()
-
-package mqtt
 func newBufioReader(c net.Conn) *bufio.Reader {
 		w.Write([]byte{
-package mqtt
+	"bufio"
 	"io/ioutil"
-	"testing"
+	"bufio"
 			0, 4, // Protocol Name - MSB+LSB
 			'M', 'Q', 'T', 'T', // Protocol Name
 			4,     // Protocol Version
 			194,   // Packet Flags
 			0, 20, // Keepalive
-			0, 3, // Client ID - MSB+LSB
+			0, 5, // Client ID - MSB+LSB
-			'z', 'e', 'n', // Client ID "zen"
+			'm', 'o', 'c', 'h', 'i', // Client ID
 			0, 5, // Username MSB+LSB
 			'm', 'o', 'c', 'h', 'i',
 			0, 4, // Password MSB+LSB
 			'a', 'b', 'c', 'd',
 		})
 package mqtt
-	)
-package mqtt
 }
 
+	"bufio"
 	"errors"
-	"io/ioutil"
 package mqtt
-	require.Equal(t, new(auth.Disallow), l.(*listeners.MockListener).Config.Auth)
-
+	recv := make(chan []byte)
 package mqtt
-/*
+package mqtt
 	//"log"
-	"testing"
 package mqtt
-	// Add listener on existing id
+	if q.errAfter == -1 || (q.errAfter > 0 && q.writes > q.errAfter) {
-}
+		if err != nil {
-
+			panic(err)
-func newBufioReader(c net.Conn) *bufio.Reader {
 	"net"
+	"io/ioutil"
 package mqtt
-package mqtt
+import (
 	"errors"
-
 	"github.com/stretchr/testify/require"
-	//"log"
+	"testing"
-package mqtt
 
-			byte(packets.Connect << 4), 15, // Fixed header
+	errx := <-o
-			0, 4, // Protocol Name - MSB+LSB
+	time.Sleep(time.Millisecond)
 package mqtt
+	"errors"
 
-import (
-	"github.com/mochi-co/mqtt/auth"
 	"bufio"
-	"github.com/mochi-co/mqtt/packets"
+	"errors"
 	"errors"
 package mqtt
-	require.Error(t, err)
-	"github.com/mochi-co/mqtt/auth"
+import (
 	//"log"
 package mqtt
-
+import (
 	"net"
-		})
-
-	}()
-
-	"github.com/stretchr/testify/require"
+	"bufio"
 	"io/ioutil"
+	"errors"
 package mqtt
-func newBufioReader(c net.Conn) *bufio.Reader {
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 package mqtt
-	return bufio.NewReaderSize(c, 512)
-	"github.com/stretchr/testify/require"
 	"testing"
 
-	time.Sleep(5 * time.Millisecond)
-
-package mqtt
 	"bufio"
-package mqtt
+func TestProcessor(t *testing.T) {
-	require.Error(t, <-o)
-package mqtt
 	"errors"
-
-}
+	"io/ioutil"
 
 package mqtt
-		err := s.AddListener(l, nil)
-package mqtt
 	"github.com/mochi-co/mqtt/topics"
-
 	o := make(chan error)
 	go func() {
 package mqtt
-	require.Equal(t, ErrListenerIDExists, err)
-package mqtt
 	return bufio.NewReaderSize(c, 512)
 	}()
 
 	go func() {
 		w.Write([]byte{
-			byte(packets.Connect << 4), 15, // Fixed header
+			byte(packets.Connect << 4), 17, // Fixed header
 			0, 4, // Protocol Name - MSB+LSB
 			'M', 'Q', 'T', 'T', // Protocol Name
 			4,     // Protocol Version
 package mqtt
-func TestProcessor(t *testing.T) {
+	writes   int
 			0, 45, // Keepalive
-	"github.com/mochi-co/mqtt/auth"
+			0, 5, // Client ID - MSB+LSB
+		&packets.ConnectPacket{
 	//"log"
 	"github.com/mochi-co/mqtt/auth"
+	"testing"
+		&packets.ConnectPacket{
 	"net"
 package mqtt
-	if len(q.b) > 0 {
-package mqtt
 }
 
+	// Receive the Connack
 	go func() {
 		_, err := ioutil.ReadAll(w)
 		if err != nil {
 			panic(err)
 		}
 package mqtt
-	)
-package mqtt
 }
 
 	return bufio.NewReaderSize(c, 512)
-	"errors"
-	r.Close()
-	return bufio.NewReaderSize(c, 512)
 
 }
 
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 	"io/ioutil"
-	s, _, _, cl := setupClient("zen")
-	cl.p.W = new(quietWriter)
-
-	return bufio.NewReaderSize(c, 512)
 	"testing"
-		packet: &packets.PublishPacket{
-			FixedHeader: packets.FixedHeader{
+	s := New()
-}
 
 package mqtt
-	require.Equal(t, true, listener.(*listeners.MockListener).IsServing)
 package mqtt
-func BenchmarkServerServe(b *testing.B) {
+	"errors"
 package mqtt
-	err := s.AddListener(l, nil)
 package mqtt
-	"testing"
 	"io/ioutil"
 package mqtt
-	"testing"
+package mqtt
 	//"log"
 package mqtt
-	"testing"
+package mqtt
 	"net"
 package mqtt
-	"testing"
+package mqtt
 	"testing"
-		},
 
-	})
-
-func newBufioWriter(c net.Conn) *bufio.Writer {
 package mqtt
-	require.NoError(t, err)
 package mqtt
-import (
 	//"log"
+package mqtt
 func newBufioWriter(c net.Conn) *bufio.Writer {
-
-		0, 5, // Topic Name - LSB+MSB
-func newBufioWriter(c net.Conn) *bufio.Writer {
+	"bufio"
 	"bufio"
+	"net"
+package mqtt
 
-	"errors"
 
-	"io/ioutil"
-	}, cl.p.W.(*quietWriter).f[0])
 package mqtt
-	"testing"
 
+import (
+package mqtt
 
-	"net"
+	"bufio"
 package mqtt
-	"net"
 	//"log"
-
-	"testing"
 package mqtt
-	s.Serve()
-		packet: &packets.PublishPacket{},
 	//"log"
-import (
-
-func newBufioWriter(c net.Conn) *bufio.Writer {
 package mqtt
-	//"log"
+	cl = newClient(
 	"testing"
-}
-
+	"bufio"
 	"errors"
-
+package mqtt
 
+	"testing"
-
 	"github.com/stretchr/testify/require"
+	"testing"
 
+	"bufio"
  */
-
-func TestServerReadClientOK(t *testing.T) {
-	s, r, w, cl := setupClient("zen")
-
-	go func() {
-	"github.com/mochi-co/mqtt/auth"
+	"testing"
-			byte(packets.Publish << 4), 18, // Fixed header
-
 	"github.com/mochi-co/mqtt/topics"
 
-)
-	return bufio.NewWriterSize(c, 512)
+	"bufio"
 	//"log"
 package mqtt
-	if len(q.b) > 0 {
-		close(cl.end)
-	"github.com/stretchr/testify/require"
 	"testing"
 
 	return bufio.NewReaderSize(c, 512)
-	"errors"
-
-	"github.com/stretchr/testify/require"
 	"io/ioutil"
-	"github.com/stretchr/testify/require"
+	"bufio"
 	//"log"
-
 package mqtt
-	"testing"
-	}()
 
-	require.NoError(t, <-o)
-package mqtt
 	"bufio"
-package mqtt
-	"github.com/mochi-co/mqtt/topics"
+	//"log"
 
 package mqtt
-	"testing"
-
-func TestServerReadClientNoConn(t *testing.T) {
-type quietWriter struct {
 package mqtt
+	//"log"
-	cl.p.Conn.Close()
-type quietWriter struct {
+			ClientIdentifier: id,
 import (
-
-	r.Close()
-type quietWriter struct {
+	"net"
 	"bufio"
-	require.Error(t, err)
-type quietWriter struct {
+	"net"
 	"errors"
-}
-
-type quietWriter struct {
+	"net"
 	"io/ioutil"
-	s, r, w, cl := setupClient("zen")
-
-	"github.com/stretchr/testify/require"
+	"bufio"
 	//"log"
-package mqtt
 	"bufio"
-	"testing"
-		w.Close()
 	}()
 
-type quietWriter struct {
 	"bufio"
-package mqtt
+	//"log"
 	"errors"
-
+	"bufio"
 	//"log"
-	"testing"
-	require.Equal(t, ErrReadFixedHeader, err)
+	"io/ioutil"
 }
-
+package mqtt
-func TestServerReadClientDisconnect(t *testing.T) {
-
 package mqtt
-import (
+	"testing"
 
 package mqtt
-func newBufioReader(c net.Conn) *bufio.Reader {
-type quietWriter struct {
 	"testing"
-		w.Close()
+import (
-package mqtt
 }
-
-type quietWriter struct {
 	"bufio"
 package mqtt
-/*
+	"testing"
 	"io/ioutil"
-	"testing"
 }
-
-func TestServerReadClientBadPacketPayload(t *testing.T) {
-	s, r, w, cl := setupClient("zen")
-
-	"github.com/stretchr/testify/require"
 	//"log"
 package mqtt
-func newBufioWriter(c net.Conn) *bufio.Writer {
-			byte(packets.Publish << 4), 7, // Fixed header
-			0, 5, // Topic Name - LSB+MSB
-			'a', '/',
-			0, 11, // Packet ID - LSB+MSB, // malformed packet id.
-	"github.com/mochi-co/mqtt/auth"
 	"testing"
-		w.Close()
+	"net"
-package mqtt
 }
-
-	err := s.readClient(cl)
-	r.Close()
-	//"log"
 	"testing"
-	b        []byte
 	"bufio"
-}
+	"net"
-
+		Sent: time.Now().Unix(),
-
+	//"log"
 import (
-	"errors"
 
-	"github.com/mochi-co/mqtt/listeners"
 
-package mqtt
 package mqtt
-	//"log"
-		w.Write([]byte{
-	b        []byte
 	"io/ioutil"
+	"testing"
 
-func (q *quietWriter) Flush() error {
-			0, 5, // Topic Name - LSB+MSB
-			'a', '/', 'b', '/', 'c', // Topic Name
-	"github.com/mochi-co/mqtt/auth"
 	"testing"
-	"github.com/mochi-co/mqtt/topics"
 package mqtt
-}
-
-type quietWriter struct {
 	"bufio"
 package mqtt
-/*
-	require.Error(t, err)
 
-	q.f = append(q.f, q.b)
 package mqtt
-	"testing"
-
-/*
-
-
 import (
-	"testing"
-
+	//"log"
-	"errors"
 	"bufio"
-
+	err = s.AddListener(listeners.NewMockListener("t1", ":1883"), nil)
-
 	"bufio"
-	return bufio.NewReaderSize(c, 512)
 	//"log"
+	"testing"
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 	"net"
-
 	"bufio"
+	"net"
 package mqtt
-
-
 	"bufio"
+	"net"
 
-
 	"bufio"
+	"net"
 import (
-			Type:      packets.Publish,
+}
 
-		packets.NewParser(r, newBufioReader(r), newBufioWriter(w)),
 		},
+	"bufio"
-
 	"bufio"
-	"io/ioutil"
+			panic(err)
 
-			ClientIdentifier: id,
+	"bufio"
 	//"log"
-import (
-	require.NoError(t, err)
+	"errors"
-
 		},
+	"io/ioutil"
-func newBufioWriter(c net.Conn) *bufio.Writer {
+	//"log"
 import (
 
-	"bufio"
-
 	"bufio"
-	"testing"
+		s.listeners.Delete("t1")
 func newBufioWriter(c net.Conn) *bufio.Writer {
+package mqtt
 	//"log"
+	"testing"
 }
 
 
-	"errors"
+	"bufio"
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 	//"log"
-	f        [][]byte
 package mqtt
-
-
 	"bufio"
-
+	}
-
 	"bufio"
-import (
+func TestServerServe(t *testing.T) {
 
-	return
 		},
+	"testing"
 
-/*
-	//"log"
+	"bufio"
 import (
-	require.Error(t, err)
-}
-
-	writes   int
 import (
-	return bufio.NewReaderSize(c, 512)
 	//"log"
-	s.clients.add(cl)
-
+	//"log"
-	err := s.writeClient(cl, &packets.PublishPacket{
-	f        [][]byte
 import (
-
 	return
 		},
-		PacketID: 0,
+		PacketID: 14,
 	})
-
-	require.Error(t, err)
-package mqtt
+	"io/ioutil"
 	"testing"
 
-	writes   int
 	"bufio"
-	return bufio.NewReaderSize(c, 512)
 	//"log"
 
-func setupClient(id string) (s *Server, r net.Conn, w net.Conn, cl *client) {
-	cl.p.W = nil
-
-	err := s.writeClient(cl, &packets.PublishPacket{})
-	require.Error(t, err)
-	require.Equal(t, ErrConnectionClosed, err)
 package mqtt
-	"testing"
-
-func TestServerWriteClientWriteError(t *testing.T) {
 package mqtt
-	"net"
 	//"log"
-	cl.p.W = &quietWriter{errAfter: -1}
-
 	"bufio"
-package mqtt
-
-	err := s.writeClient(cl, &packets.PublishPacket{})
 	//"log"
-	"testing"
+import (
-}
+		if err != nil {
-
+	"net"
 	"errors"
-
-
-	writes   int
 	"net"
-
+	"io/ioutil"
-	"errors"
 	"bufio"
-
-func TestServerCloseClient(t *testing.T) { // as opposed to client.close
-	return bufio.NewReaderSize(c, 512)
 	//"log"
-
 	"bufio"
-package mqtt
-
-	require.Contains(t, s.clients.internal, "zen")
-
-	errAfter int
+package mqtt
 package mqtt
+	"testing"
-	err := s.closeClient(s.clients.internal["zen"], true)
-	"io/ioutil"
 	"testing"
-	var ok bool
-	errAfter int
+package mqtt
 	"bufio"
+package mqtt
 
-		New()
-import (
+package mqtt
 import (
-	require.Equal(t, false, ok)
-	errAfter int
 	//"log"
+		new(auth.Allow),
 package mqtt
+	"bufio"
 	"testing"
 
-	errAfter int
+	"bufio"
 	"net"
+import (
-	errAfter int
+package mqtt
 	"testing"
 
-	// Add listener with config.
-	s.clients.add(c1)
-	require.Contains(t, s.clients.internal, "zen")
-	c1.lwt = lwt{
-		topic:   "a/b/c",
-func (q *quietWriter) Write(b []byte) (int, error) {
 	"bufio"
-import (
+	"testing"
 import (
-
-func (q *quietWriter) Write(b []byte) (int, error) {
 	"errors"
-func (q *quietWriter) Write(b []byte) (int, error) {
 	"io/ioutil"
-	s.clients.add(c2)
-	require.Contains(t, s.clients.internal, "zen2")
-func (q *quietWriter) Write(b []byte) (int, error) {
+	"bufio"
 	"testing"
-
-	// close the client connection.
-	err := s.closeClient(s.clients.internal["zen"], true)
+	"bufio"
-	"io/ioutil"
+	"bufio"
 	"testing"
+	"errors"
-
-package mqtt
+	"bufio"
 import (
-	//"log"
-		byte(packets.Publish << 4), 12, // Fixed header QoS : 1
-func newBufioWriter(c net.Conn) *bufio.Writer {
 import (
 
-	"bufio"
-		'h', 'e', 'l', 'l', 'o', // Payload
-	}, c2.p.W.(*quietWriter).f[0])
-package mqtt
+	"bufio"
 	"testing"
-
-func TestServerCloseClientLWTWriteError(t *testing.T) { // as opposed to client.close
-
 	"io/ioutil"
-	"testing"
-
 	//"log"
+	"testing"
-func (q *quietWriter) Write(b []byte) (int, error) {
 package mqtt
+	"testing"
 
-	require.NotNil(t, s.inbound)
-
+		new(auth.Allow),
 	//"log"
-
-		topic:   "a/b/c",
-func (q *quietWriter) Write(b []byte) (int, error) {
 	"bufio"
-	}
-
-func (q *quietWriter) Write(b []byte) (int, error) {
+	"net"
 	"errors"
-	q.writes++
 	"bufio"
-	s.clients.add(c2)
-func (q *quietWriter) Write(b []byte) (int, error) {
+	"testing"
 	"net"
-	s.topics.Subscribe("a/b/c", c2.id, 0)
-
-	// close the client connection.
-	err := s.closeClient(s.clients.internal["zen"], true)
 	require.Error(t, err)
 }
 
-	"errors"
 
-
+	time.Sleep(time.Millisecond)
-
+	"bufio"
 			panic(err)
-
-	"errors"
 	"bufio"
-
+func TestServerClose(t *testing.T) {
 
-		}
-	s, _, _, cl := setupClient("zen")
+	r, w = net.Pipe()
 
-		s.listeners.Delete("t1")
-	"io/ioutil"
 	"testing"
-}
 
-	q.writes++
+	"bufio"
 	"net"
-	return bufio.NewReaderSize(c, 512)
 	//"log"
+import (
 	"io/ioutil"
-	"bufio"
-	q.writes++
 	"testing"
-	}
 }
 
 	if len(q.b) > 0 {
-	return bufio.NewReaderSize(c, 512)
 	//"log"
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 	"net"
-
+	"errors"
-
+		new(auth.Allow),
 	"testing"
-package mqtt
 		FixedHeader: packets.FixedHeader{
 	if len(q.b) > 0 {
-
+	"testing"
 		},
 	})
-
+	require.NoError(t, err)
-	"io/ioutil"
+package mqtt
 	"testing"
 
-	"io/ioutil"
+		q.b = append(q.b, b...)
 import (
-	errAfter int
 	"bufio"
+	err = s.AddListener(listeners.NewMockListener("t2", ":1882"), &listeners.Config{
 
-	require.Equal(t, true, listener.(*listeners.MockListener).IsServing)
+package mqtt
 	}
-	require.Equal(t, false, ok)
 package mqtt
-	"testing"
-
-func BenchmarkServerProcessConnect(b *testing.B) {
 package mqtt
-	"net"
 	//"log"
-	pk := &packets.ConnectPacket{
-
 	"bufio"
+	//"log"
 import (
-			Type: packets.Connect,
-	"bufio"
 	"net"
-	}
+	"bufio"
-	for n := 0; n < b.N; n++ {
+			panic(err)
-	if len(q.b) > 0 {
+	"net"
 	"io/ioutil"
-import (
+package mqtt
 import (
+	"errors"
+package mqtt
 }
 
-
+	"bufio"
 	"testing"
-	//"log"
-	s, _, _, cl := setupClient("zen")
-	cl.p.W = new(quietWriter)
-
-
 	"testing"
-	"net"
 		FixedHeader: packets.FixedHeader{
-			Type: packets.Disconnect,
+			Type: packets.Pingreq,
 		},
 	})
 
 	require.NoError(t, err)
-	var ok bool
+	time.Sleep(10 * time.Millisecond)
-	errAfter int
+package mqtt
 	"bufio"
+package mqtt
 
-	require.Equal(t, true, listener.(*listeners.MockListener).IsServing)
-import (
+package mqtt
 import (
+	//"log"
-	errAfter int
+		q.b = append(q.b, b...)
 	"io/ioutil"
 package mqtt
+	return nil
+package mqtt
 	"testing"
 
-import (
+	"errors"
-package mqtt
+	"bufio"
 	"net"
-	//"log"
-	cl.p.W = new(quietWriter)
+	"errors"
 
-	pk := &packets.DisconnectPacket{
+	cl.Stop()
-
 	"bufio"
-import (
-
 func TestServerClose(t *testing.T) {
+
 	"bufio"
-	"net"
 import (
 import (
+	"errors"
-	"io/ioutil"
 	"bufio"
+	"net"
+	//"log"
 import (
-
-	}
+	require.Error(t, err)
 }
 
-import (
+	"errors"
-import (
 package mqtt
-		s.listeners.Delete("t1")
-package mqtt
+	"bufio"
 	"net"
-	"net"
+	"errors"
 
-		q.b = append(q.b, b...)
 	"bufio"
+func TestServerClose(t *testing.T) {
 		FixedHeader: packets.FixedHeader{
 import (
+import (
-	"errors"
+	}
 	"bufio"
-	"net"
-	})
+		},
-	require.NoError(t, err)
+		PacketID: 0,
-	"github.com/mochi-co/mqtt/listeners"
 	//"log"
 import (
-	"io/ioutil"
 
 	//"log"
+	"testing"
 }
 
-import (
+	"errors"
-	//"log"
+
-	s, _, _, cl := setupClient("zen")
+	s, cl1, r1, w1 := setupClient()
-import (
+	"errors"
-	"net"
+	"bufio"
-
-import (
+	"errors"
-	"testing"
+	"errors"
 
-	r, w = net.Pipe()
-import (
+	"errors"
+	"io/ioutil"
 	"errors"
+	//"log"
-	"bufio"
+	)
 	"net"
-	}
+
-	for n := 0; n < b.N; n++ {
+	s.Topics.Subscribe("a/b/+", cl2.ID, 0)
-import (
+	"errors"
 	"time"
-	}
-}
 
-import (
+	"errors"
 	"github.com/stretchr/testify/require"
 package mqtt
-	"net"
+package mqtt
 	//"log"
-	cl.p.W = &quietWriter{errAfter: -1}
+	return
 
-		q.b = append(q.b, b...)
+	"net"
 	"bufio"
-		FixedHeader: packets.FixedHeader{
-		q.b = append(q.b, b...)
+	"net"
 	"errors"
-	"bufio"
 	"net"
+	"io/ioutil"
-	//"log"
+	return
 import (
-	require.Error(t, err)
+package mqtt
 }
 
-import (
+	"errors"
 package mqtt
-
+	"bufio"
-import (
 package mqtt
-import (
-import (
 package mqtt
-	"bufio"
-
 	//"log"
-	c2.p.W = new(quietWriter)
+		buf, err := ioutil.ReadAll(r2)
-
+		if err != nil {
-	s.clients.add(c1)
+			panic(err)
-	s.clients.add(c2)
+		}
-	} else {
 	"errors"
-import (
 )
-import (
 package mqtt
-	//"log"
+}
 
-import (
+	"errors"
 package mqtt
-	"net"
+	//"log"
 		FixedHeader: packets.FixedHeader{
 			Type:   packets.Publish,
 			Qos:    1,
@@ -1274,131 +907,76 @@ 		TopicName: "a/b/c",
 		Payload:   []byte("hello"),
 		PacketID:  12,
 	})
+
 	require.NoError(t, err)
 package mqtt
-func (q *quietWriter) Flush() error {
+			panic(err)
-		byte(packets.Puback << 4), 2, // Fixed header
+	w1.Close()
-		q.b = b
 	"errors"
-	}, c1.p.W.(*quietWriter).f[0])
+}
 
 	require.Equal(t, []byte{
-		byte(packets.Publish<<4 | 2), 14, // Fixed header QoS : 1
+	"errors"
 func newBufioWriter(c net.Conn) *bufio.Writer {
-import (
-		'a', '/', 'b', '/', 'c', // Topic Name
-func newBufioWriter(c net.Conn) *bufio.Writer {
 	"errors"
 
-	"net"
 package mqtt
+	"errors"
 
-	"net"
 
-}
 
-func BenchmarkServerProcessPublish(b *testing.B) {
 package mqtt
-	"net"
+import (
 	//"log"
-	cl.p.W = &quietWriter{}
+	"errors"
 
 import (
-	if len(q.b) > 0 {
-
 	"bufio"
-import (
-			Type: packets.Publish,
+	require.Error(t, err)
 		},
-		TopicName: "a/b/c",
-		Payload:   []byte("hello"),
-	}
-	"io/ioutil"
 	"bufio"
-	}
+	"net"
 package mqtt
-	"net"
 	"bufio"
 	"net"
-	"errors"
-		}
-	}
-}
 
-	}
+	"errors"
 
+	"bufio"
 package mqtt
-		s.listeners.Delete("t1")
+	"testing"
 
-	"testing"
-	err := s.processPacket(cl, &packets.PublishPacket{
+	"errors"
 
-	r, w = net.Pipe()
+	"errors"
-	}
+	"errors"
 import (
-	if q.errAfter == -1 || (q.errAfter > 0 && q.writes > q.errAfter) {
-		},
+	s.Clients.Add(cl1)
 
-		&packets.ConnectPacket{
-		Payload:   []byte("hello"),
-		PacketID:  12,
-	})
-
-	require.Error(t, err)
-}
-
-	}
 	"errors"
-import (
 package mqtt
-import (
-import (
 package mqtt
-	"bufio"
-	c2.p.W = &quietWriter{errAfter: -1}
-
-func (q *quietWriter) Write(b []byte) (int, error) {
 package mqtt
-func (q *quietWriter) Write(b []byte) (int, error) {
+package mqtt
 	//"log"
-	} else {
 	"errors"
-import (
 package mqtt
-	"io/ioutil"
-	require.Nil(t, c1.inFlight.internal[1])
 
-	} else {
 	"net"
-
 	"bufio"
-import (
-			Type: packets.Publish,
+			panic(err)
-	"bufio"
 	"net"
-		TopicName: "a/b/c",
-	}
 	"io/ioutil"
-	})
-	require.Error(t, err)
-
+	"errors"
 package mqtt
-	"testing"
 import (
-func (q *quietWriter) Flush() error {
-import (
 package mqtt
-import (
-import (
 package mqtt
-	"bufio"
+	"testing"
 
-	// Add listener with config.
-	c2.p.W = new(quietWriter)
-
-import (
+	"errors"
 package mqtt
-	"net"
+	//"log"
 		FixedHeader: packets.FixedHeader{
 			Type:   packets.Publish,
 			Qos:    2,
@@ -1404,71 +985,72 @@ 		TopicName: "a/b/c",
 		Payload:   []byte("hello"),
 		PacketID:  12,
 	})
+
 	require.NoError(t, err)
 package mqtt
+			panic(err)
+	w1.Close()
+
+package mqtt
 func (q *quietWriter) Flush() error {
 		byte(packets.Pubrec << 4), 2, // Fixed header
 		0, 12, // Packet ID - LSB+MSB
-import (
+	"errors"
 
-	"io/ioutil"
 
 }
 
-import (
+func TestServerProcessPublishBadACL(t *testing.T) {
 	"bufio"
+			panic(err)
+	cl.AC = new(auth.Disallow)
-	return bufio.NewReaderSize(c, 512)
+	cl = newClient(
 	//"log"
 
+		new(auth.Allow),
 	"testing"
-	err := s.processPacket(cl, &packets.PublishPacket{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Publish,
-			Qos:  2,
 		},
 		TopicName: "a/b/c",
 		Payload:   []byte("hello"),
-		PacketID:  12,
 	})
 
-	//"log"
+	"io/ioutil"
 	"testing"
 }
 
-	if q.errAfter == -1 || (q.errAfter > 0 && q.writes > q.errAfter) {
+	"errors"
 
+	"net"
-import (
 	"bufio"
-import (
-package mqtt
 	"net"
-	"net"
+	"errors"
-import (
 	"bufio"
-	"bufio"
+		s.listeners.Delete("t1")
 
-	err := s.processPacket(cl, &packets.PublishPacket{
+	err := s.processPacket(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Publish,
+			Qos:  1,
 		},
 		TopicName: "a/b/c",
 		Payload:   []byte("hello"),
 	})
 
 	require.Error(t, err)
-	require.Equal(t, ErrACLNotAuthorized, err)
 }
 
 func TestServerProcessPuback(t *testing.T) {
-package mqtt
+	"bufio"
 	"net"
-	//"log"
+	"errors"
+	"errors"
 
-	cl.inFlight.set(11, &inFlightMessage{packet: &packets.PublishPacket{PacketID: 11}, sent: 0})
-	require.NotNil(t, cl.inFlight.internal[11])
+	"testing"
 
-import (
 	"bufio"
+	"testing"
 	"testing"
 		FixedHeader: packets.FixedHeader{
 			Type:      packets.Puback,
@@ -1475,78 +1059,74 @@ 		},
 		PacketID: 11,
 	})
 	require.NoError(t, err)
-import (
+
  * Server
+	require.Equal(t, false, ok)
 }
 
 func TestServerProcessPubrec(t *testing.T) {
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 	//"log"
 package mqtt
-func TestServerServe(t *testing.T) {
-
+	"errors"
 import (
-func TestNew(t *testing.T) {
+package mqtt
 
+package mqtt
 import (
-	s := New()
-	f        [][]byte
 import (
-		return len(b), errors.New("error")
+	"github.com/stretchr/testify/require"
 	//"log"
 	"bufio"
+	})
 	"net"
+	"bufio"
-import (
+	"net"
 	"errors"
 	"net"
-	})
 	"io/ioutil"
-	"testing"
 	"github.com/mochi-co/mqtt/listeners"
-	//"log"
-import (
 	"errors"
-	"testing"
-		0, 12, // Packet ID - LSB+MSB
-	}, cl.p.W.(*quietWriter).f[0])
 package mqtt
-	"testing"
-
-func BenchmarkServerProcessPubrec(b *testing.B) {
 package mqtt
-		s.listeners.Delete("t1")
-	cl.p.W = new(quietWriter)
+	"testing"
 
-	pk := &packets.PubrecPacket{
+	err := s.processPacket(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Pubrec,
 		},
 		PacketID: 12,
-import (
+	//"log"
 import (
-	for n := 0; n < b.N; n++ {
-	return len(b), nil
 
-		if err != nil {
+	require.NoError(t, err)
+package mqtt
 			panic(err)
-		}
-	}
+	"github.com/mochi-co/mqtt/packets"
 package mqtt
-	"testing"
 
+package mqtt
 import (
-	"io/ioutil"
+	//"log"
+	"errors"
 import (
+
+/*
 package mqtt
-		s.listeners.Delete("t1")
+	}, <-recv)
 
+package mqtt
 	"testing"
 
+ * Server
 import (
+		},
 	"errors"
+	cl.Stop()
 	"errors"
+	} else {
 
-	err := s.processPacket(cl, &packets.PubrecPacket{
+	err := s.processPacket(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Pubrec,
 		},
@@ -1554,96 +1135,84 @@ 	require.Error(t, err)
 }
 
 func TestServerProcessPubrel(t *testing.T) {
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 	//"log"
 package mqtt
-func TestServerServe(t *testing.T) {
-
+	"errors"
 import (
-		New()
+	"bufio"
 
+package mqtt
 import (
-func TestProcessor(t *testing.T) {
-	f        [][]byte
 import (
-	return len(b), nil
+	"github.com/stretchr/testify/require"
 	//"log"
 	"bufio"
-	"net"
-		PacketID: 10,
 	})
-	require.NoError(t, err)
+		if err != nil {
-	require.Equal(t, []byte{
+			panic(err)
-import (
+	"net"
 	"io/ioutil"
-	"testing"
-		0, 10, // Packet ID - LSB+MSB
-	}, cl.p.W.(*quietWriter).f[0])
 package mqtt
-	"testing"
-
 import (
-	err = s.AddListener(listeners.NewMockListener("t2", ":1882"), &listeners.Config{
+	"errors"
 package mqtt
-		s.listeners.Delete("t1")
 package mqtt
-func TestServerServe(t *testing.T) {
-
-	cl.inFlight.set(10, &inFlightMessage{packet: &packets.PublishPacket{PacketID: 10}, sent: 0})
+	"testing"
 
-	pk := &packets.PubrelPacket{
+	err := s.processPacket(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Pubrel,
 		},
 		PacketID: 10,
-import (
+	//"log"
 import (
 
 	"io/ioutil"
+	"testing"
+	time.Sleep(10 * time.Millisecond)
+package mqtt
 	"bufio"
+package mqtt
+
+package mqtt
 func (q *quietWriter) Flush() error {
-import (
-	"net"
 	"bufio"
+	require.Equal(t, 1, s.listeners.Len())
-	"net"
+ * Server
 	"errors"
-		}
-	}
+	}, <-recv)
 }
 
+	"errors"
 import (
-	//"log"
+	"io/ioutil"
 	"bufio"
+			panic(err)
-package mqtt
+	"bufio"
 		s.listeners.Delete("t1")
-	cl.p.W = &quietWriter{errAfter: -1}
-
-	return len(b), nil
 	"errors"
+	} else {
 
-	err := s.processPacket(cl, &packets.PubrelPacket{
+	err := s.processPacket(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Pubrel,
 		},
 import (
-	"io/ioutil"
+	"errors"
 	"net"
 	})
 	require.Error(t, err)
 }
 
 func TestServerProcessPubcomp(t *testing.T) {
-package mqtt
+	"bufio"
 	"net"
-	//"log"
+	"errors"
-	cl.p.W = new(quietWriter)
+	cl.InFlight.Set(11, clients.InFlightMessage{Packet: packets.Packet{PacketID: 11}, Sent: 0})
 
-import (
 	"bufio"
-	//"log"
-	require.NotNil(t, cl.inFlight.internal[11])
-
-	err := s.processPacket(cl, &packets.PubcompPacket{
+func TestServerClose(t *testing.T) {
 		FixedHeader: packets.FixedHeader{
 			Type:      packets.Pubcomp,
 			Remaining: 2,
@@ -1650,50 +1220,42 @@ 		},
 		PacketID: 11,
 	})
 	require.NoError(t, err)
-import (
+
  * Server
 
+func TestProcessor(t *testing.T) {
 }
 
+	"errors"
 func (q *quietWriter) Flush() error {
-	"net"
-package mqtt
+	"bufio"
 	"net"
-	//"log"
-	cl.p.W = new(quietWriter)
+	"errors"
 
-import (
 	"bufio"
-	//"log"
-
-func (q *quietWriter) Flush() error {
+	"testing"
 	"testing"
 		FixedHeader: packets.FixedHeader{
 import (
-	// Add listener on existing id
+			panic(err)
 import (
-	return
+	if q.errAfter == -1 || (q.errAfter > 0 && q.writes > q.errAfter) {
 		},
-import (
+
 /*
-import (
+	//"log"
 import (
-	for n := 0; n < b.N; n++ {
+
-		err := s.processPubcomp(cl, pk)
-		if err != nil {
-			panic(err)
-		}
-	}
+	require.Error(t, err)
 }
 
 func TestServerProcessSubscribe(t *testing.T) {
-	return bufio.NewReaderSize(c, 512)
+	"bufio"
 	//"log"
 package mqtt
-func TestServerServe(t *testing.T) {
 
+	"errors"
 	q.f = append(q.f, q.b)
-
 		FixedHeader: packets.FixedHeader{
 			Type:   packets.Publish,
 			Retain: true,
@@ -1700,12 +1262,22 @@ 		},
 		TopicName: "a/b/c",
 		Payload:   []byte("hello"),
 	})
+	"errors"
 import (
-	"net"
+	"testing"
+
+	"github.com/mochi-co/mqtt/listeners"
 import (
-
+	go func() {
+			ClientIdentifier: id,
 import (
 		if err != nil {
+			panic(err)
+		}
+		recv <- buf
+	}()
+
+	err := s.processPacket(cl, packets.Packet{
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Subscribe,
 		},
@@ -1715,86 +1286,118 @@ 		Qoss:     []byte{0, 1},
 	})
 	require.NoError(t, err)
 package mqtt
+			panic(err)
+	w.Close()
+
+package mqtt
 func (q *quietWriter) Flush() error {
 		byte(packets.Suback << 4), 4, // Fixed header
 		0, 10, // Packet ID - LSB+MSB
 		0, // Return Code QoS 0
 		1, // Return Code QoS 1
 
-	//"log"
-
-	require.Equal(t, []byte{
 		byte(packets.Publish<<4 | 1), 12, // Fixed header
 		0, 5, // Topic Name - LSB+MSB
 		'a', '/', 'b', '/', 'c', // Topic Name
 		'h', 'e', 'l', 'l', 'o', // Payload
-	q.b = []byte{}
+	}, <-recv)
 
+	require.Contains(t, cl.Subscriptions, "a/b/c")
+	require.Contains(t, cl.Subscriptions, "d/e/f")
+ */
 
-	q.b = []byte{}
+ */
 import (
-	q.b = []byte{}
+ */
 	"bufio"
-	q.b = []byte{}
+ */
 	"errors"
-import (
+package mqtt
 	"testing"
+
+ */
 	"io/ioutil"
+	s, cl, r, _ := setupClient()
+
+	"errors"
 import (
-		panic(err)
+	"net"
+	f        [][]byte
 import (
-	"testing"
+ */
 	"net"
+		q.b = b
 package mqtt
-	"testing"
+		},
 
+		&packets.ConnectPacket{
 import (
-func TestServerClose(t *testing.T) {
+type quietWriter struct {
-	return bufio.NewReaderSize(c, 512)
 	//"log"
+import (
+	require.Equal(t, 1, len(s.Topics.Messages("a/b/c")))
+
 package mqtt
-func TestServerServe(t *testing.T) {
+	}
-
+	go func() {
 	"bufio"
+	})
-
+	"net"
 	"bufio"
-import (
-import (
 			panic(err)
-	"bufio"
 	"net"
+	"io/ioutil"
+package mqtt
 import (
-	err := s.AddListener(listeners.NewMockListener("t1", ":1882"), nil)
+	"errors"
-	return nil
+package mqtt
 package mqtt
+	"testing"
-	return nil
 
-	}
-	"io/ioutil"
 	"bufio"
+func TestServerClose(t *testing.T) {
+
 	"bufio"
 import (
+import (
 	"net"
+	"errors"
 	"bufio"
+	"net"
+	return len(b), nil
 	"net"
-	"errors"
+import (
 		}
 import (
+		s.listeners.Delete("t1")
+	//"log"
 import (
+	require.Error(t, err)
+
 }
 
 func TestServerProcessSubscribeFailACL(t *testing.T) {
+			ClientIdentifier: id,
 package mqtt
-	"net"
+/*
 	//"log"
+
 package mqtt
+	}
+	go func() {
+		buf, err := ioutil.ReadAll(r)
 	"net"
+	"bufio"
 	"net"
+	"errors"
+		}
+package mqtt
 import (
-	cl = newClient(
+	"errors"
+	}()
 
-	q.f = append(q.f, q.b)
 	"bufio"
+func TestServerClose(t *testing.T) {
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Subscribe,
 		},
@@ -1800,30 +1405,34 @@ 		PacketID: 10,
 		Topics:   []string{"a/b/c", "d/e/f"},
 		Qoss:     []byte{0, 1},
 	})
+
 	require.NoError(t, err)
 package mqtt
+			panic(err)
+	w.Close()
+
+package mqtt
 func (q *quietWriter) Flush() error {
-		byte(packets.Suback << 4), 4, // Fixed header
+		byte(packets.Suback << 4), 4,
+	"errors"
 import (
-	// Add listener with config.
+	"errors"
 		packets.ErrSubAckNetworkError,
 		packets.ErrSubAckNetworkError,
-
+	"github.com/mochi-co/mqtt/packets"
-	//"log"
 
-	"bufio"
+func TestNew(t *testing.T) {
-	"io/ioutil"
-	require.Empty(t, s.topics.Subscribers("d/e/f"))
+	require.Empty(t, s.Topics.Subscribers("d/e/f"))
 }
 
 func TestServerProcessSubscribeWriteError(t *testing.T) {
-package mqtt
+	"bufio"
 	"net"
-	//"log"
+	"errors"
-	cl.p.W = &quietWriter{errAfter: -1}
+	cl.Stop()
 
-	q.f = append(q.f, q.b)
 	"bufio"
+func TestServerClose(t *testing.T) {
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Subscribe,
 		},
@@ -1831,157 +1440,220 @@ 		PacketID: 10,
 		Topics:   []string{"a/b/c", "d/e/f"},
 		Qoss:     []byte{0, 1},
 	})
+
 	require.Error(t, err)
 }
 
+func TestServerProcessUnsubscribeInvalid(t *testing.T) {
 	"bufio"
+			panic(err)
+
+		new(auth.Allow),
 	"testing"
+		FixedHeader: packets.FixedHeader{
+	"bufio"
 package mqtt
-	"net"
 	//"log"
+	}
 	"bufio"
-	"time"
+		},
+	writes   int
 
+	//"log"
 import (
-	"net"
 
-		FixedHeader: packets.FixedHeader{
+	require.Error(t, err)
-import (
 }
-import (
 
+func setupClient(id string) (s *Server, r net.Conn, w net.Conn, cl *client) {
 package mqtt
 	"bufio"
-	"net"
+	err = s.AddListener(listeners.NewMockListener("t2", ":1882"), &listeners.Config{
-
+	"bufio"
 	"bufio"
-	"io/ioutil"
+	//"log"
+func TestNew(t *testing.T) {
 import (
-type quietWriter struct {
+	s.Topics.Subscribe("d/e/f", cl.ID, 1)
+	s.Topics.Subscribe("a/b/+", cl.ID, 2)
+	cl.NoteSubscription("a/b/c", 0)
+func TestNew(t *testing.T) {
 	//"log"
+	cl.NoteSubscription("a/b/+", 2)
+
+	"github.com/mochi-co/mqtt/listeners"
 import (
+	go func() {
+			ClientIdentifier: id,
 import (
+		if err != nil {
 	"net"
+	"errors"
+		}
+package mqtt
 import (
+	"errors"
+	}()
 
-	q.f = append(q.f, q.b)
 	"bufio"
+func TestServerClose(t *testing.T) {
 		FixedHeader: packets.FixedHeader{
-			Type: packets.Subscribe,
+			Type: packets.Unsubscribe,
 		},
 import (
-	"io/ioutil"
+	"errors"
 	"net"
 		Topics:   []string{"a/b/c", "d/e/f"},
+	//"log"
 import (
-		s.listeners.Delete("t1")
-	})
+
-	//"log"
+	"io/ioutil"
 	"testing"
 package mqtt
-	"testing"
+			panic(err)
+	w.Close()
 
-	"bufio"
 package mqtt
-package mqtt
+func (q *quietWriter) Flush() error {
+		byte(packets.Unsuback << 4), 2,
+/*
 package mqtt
-		s.listeners.Delete("t1")
 package mqtt
-func TestServerServe(t *testing.T) {
+	return nil
 
-	f        [][]byte
+	require.NotEmpty(t, s.Topics.Subscribers("a/b/c"))
+func TestNew(t *testing.T) {
 package mqtt
-	"bufio"
+	s := New()
 package mqtt
+	s := New()
 
-func setupClient(id string) (s *Server, r net.Conn, w net.Conn, cl *client) {
+
+	s := New()
 import (
+	s := New()
 	"bufio"
 package mqtt
+	"testing"
+
+	s = New()
 	"bufio"
 	"bufio"
-package mqtt
+	"net"
 	"errors"
+	cl.Stop()
 
 	"bufio"
-)
+func TestServerClose(t *testing.T) {
 		FixedHeader: packets.FixedHeader{
 			Type: packets.Unsubscribe,
 		},
 		PacketID: 12,
 		Topics:   []string{"a/b/c", "d/e/f"},
 	})
-	"io/ioutil"
+
+	//"log"
 	"testing"
 package mqtt
-func (q *quietWriter) Flush() error {
+	"testing"
+
+func TestServerClose(t *testing.T) {
 	"bufio"
-package mqtt
 	"net"
+	"errors"
-		q.b = b
+	s := New()
 	"errors"
-func newBufioWriter(c net.Conn) *bufio.Writer {
+	cl = newClient(
 	//"log"
 
-	return nil
 	"io/ioutil"
+	"net"
-	"bufio"
+	require.NoError(t, err)
+	s.Serve()
+	"testing"
-	//"log"
 	"bufio"
+	"bufio"
 package mqtt
-	"testing"
+
+	"bufio"
 	s = New()
+	require.Equal(t, true, ok)
-package mqtt
 	"testing"
+import (
 
-	s = New()
 package mqtt
+	time.Sleep(time.Millisecond)
 package mqtt
-		s.listeners.Delete("t1")
+package mqtt
 package mqtt
-func TestServerServe(t *testing.T) {
+	"testing"
 
-	pk := &packets.UnsubscribePacket{
+func TestServerCloseClientLWT(t *testing.T) {
-		FixedHeader: packets.FixedHeader{
-func setupClient(id string) (s *Server, r net.Conn, w net.Conn, cl *client) {
+	s := New()
 	//"log"
-	"bufio"
+	s := New()
 	"net"
-import (
 	"errors"
-	"net"
+	require.NoError(t, err)
-	"bufio"
+	require.NotNil(t, s)
+	require.NotNil(t, s)
 package mqtt
 	}
+	s.Clients.Add(cl1)
+
+	)
 	"io/ioutil"
-	"bufio"
-	"bufio"
+	cl2.ID = "mochi2"
+	s.Clients.Add(cl2)
 
-import (
+	s.Topics.Subscribe("a/b/c", cl2.ID, 0)
+
+	ack2 := make(chan []byte)
+	go func() {
+		buf, err := ioutil.ReadAll(r2)
 		if err != nil {
 			panic(err)
 		}
+		ack2 <- buf
+	}()
+
+	require.NotNil(t, s)
 import (
-import (
+	require.NoError(t, err)
+	time.Sleep(time.Millisecond)
+	"errors"
 }
 
+	require.Equal(t, []byte{
+	require.NotNil(t, s)
 	"bufio"
+		0, 5,
+		'a', '/', 'b', '/', 'c',
+		'h', 'e', 'l', 'l', 'o',
+	"errors"
 	f        [][]byte
 package mqtt
+	"testing"
+
+func TestServerCloseClientClosed(t *testing.T) {
+	"bufio"
 	"net"
+	"errors"
+	cl.Listener = "t1"
+	cl.LWT = clients.LWT{
+	require.NotNil(t, s)
 	//"log"
-
+	require.NotNil(t, s)
-	"testing"
-	"bufio"
+	require.NotNil(t, s)
 package mqtt
-	"io/ioutil"
-	f        [][]byte
+import (
 import (
 	"bufio"
-package mqtt
+	"net"
 	//"log"
-		},
+
+	"errors"
 	//"log"
-import (
+	"net"
 	require.Error(t, err)
 }




diff --git a/packets/README.md b/packets/README.md
deleted file mode 100644
index d84d9eafc40379b0a8fa551e3ba2577b64a65701..0000000000000000000000000000000000000000
--- a/packets/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-## MQTT Packets Codec
-
-#### Benchmarks
-```bash
-goos: darwin
-goarch: amd64
-pkg: github.com/mochi-co/mqtt/packets
-BenchmarkNewPacket-4             	16897716	        73.1 ns/op	     144 B/op	       1 allocs/op
-BenchmarkNewParser-4             	 6329433	       163 ns/op	     512 B/op	       1 allocs/op
-BenchmarkRead-4                  	10650950	       115 ns/op	      64 B/op	       1 allocs/op
-
-BenchmarkDecodeString-4          	90128386	        13.6 ns/op	       0 B/op	       0 allocs/op
-BenchmarkDecodeBytes-4           	217574468	         5.41 ns/op	       0 B/op	       0 allocs/op
-BenchmarkDecodeByte-4            	1000000000	         0.343 ns/op	       0 B/op	       0 allocs/op
-BenchmarkDecodeUint16-4          	1000000000	         0.332 ns/op	       0 B/op	       0 allocs/op
-BenchmarkDecodeByteBool-4        	1000000000	         0.340 ns/op	       0 B/op	       0 allocs/op
-BenchmarkEncodeBool-4            	1000000000	         0.337 ns/op	       0 B/op	       0 allocs/op
-BenchmarkEncodeBytes-4           	1000000000	         0.336 ns/op	       0 B/op	       0 allocs/op
-BenchmarkEncodeUint16-4          	1000000000	         0.336 ns/op	       0 B/op	       0 allocs/op
-BenchmarkEncodeString-4          	84589165	        13.0 ns/op	       0 B/op	       0 allocs/op
-BenchmarkConnackEncode-4         	36332194	        39.1 ns/op	      15 B/op	       0 allocs/op
-BenchmarkConnackDecode-4         	40500982	        28.9 ns/op	       0 B/op	       0 allocs/op
-BenchmarkConnackValidate-4       	1000000000	         0.337 ns/op	       0 B/op	       0 allocs/op
-BenchmarkConnectEncode-4         	 8393736	       145 ns/op	      71 B/op	       0 allocs/op
-BenchmarkConnectDecode-4         	16927981	        68.0 ns/op	       0 B/op	       0 allocs/op
-BenchmarkConnectValidate-4       	449974868	         2.70 ns/op	       0 B/op	       0 allocs/op
-BenchmarkDisconnectEncode-4      	79734340	        16.9 ns/op	       6 B/op	       0 allocs/op
-BenchmarkDisconnectDecode-4      	48091165	        25.2 ns/op	       0 B/op	       0 allocs/op
-BenchmarkDisconnectValidate-4    	1000000000	         0.330 ns/op	       0 B/op	       0 allocs/op
-BenchmarkFixedHeaderEncode-4     	83697316	        22.2 ns/op	       6 B/op	       0 allocs/op
-BenchmarkFixedHeaderDecode-4     	178036213	         6.40 ns/op	       0 B/op	       0 allocs/op
-BenchmarkEncodeLength-4          	159687147	        10.4 ns/op	       3 B/op	       0 allocs/op
-BenchmarkRefreshDeadline-4       	10610847	       107 ns/op	       0 B/op	       0 allocs/op
-BenchmarkReset-4                 	24923192	        48.0 ns/op	       0 B/op	       0 allocs/op
-BenchmarkReadFixedHeader-4       	22633785	        53.0 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPingreqEncode-4         	78809928	        18.6 ns/op	       6 B/op	       0 allocs/op
-BenchmarkPingreqDecode-4         	42101839	        29.1 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPingreqValidate-4       	1000000000	         0.500 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPingrespEncode-4        	77277940	        16.4 ns/op	       7 B/op	       0 allocs/op
-BenchmarkPingrespDecode-4        	50585750	        25.1 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPingrespValidate-4      	1000000000	         0.395 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubackEncode-4          	40743432	        35.2 ns/op	      13 B/op	       0 allocs/op
-BenchmarkPubackDecode-4          	43006642	        31.7 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubackValidate-4        	1000000000	         0.386 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubcompEncode-4         	44188626	        45.0 ns/op	      12 B/op	       0 allocs/op
-BenchmarkPubcompDecode-4         	32195416	        36.8 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubcompValidate-4       	1000000000	         0.345 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPublishEncode-4         	18311235	        64.6 ns/op	      31 B/op	       0 allocs/op
-BenchmarkPublishDecode-4         	21433636	        53.2 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPublishCopy-4           	1000000000	         0.358 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPublishValidate-4       	901116214	         1.33 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubrecEncode-4          	40544341	        29.8 ns/op	      13 B/op	       0 allocs/op
-BenchmarkPubrecDecode-4          	40263987	        28.4 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubrecValidate-4        	1000000000	         0.340 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubrelEncode-4          	41809491	        28.9 ns/op	      13 B/op	       0 allocs/op
-BenchmarkPubrelDecode-4          	42216692	        29.5 ns/op	       0 B/op	       0 allocs/op
-BenchmarkPubrelValidate-4        	1000000000	         0.329 ns/op	       0 B/op	       0 allocs/op
-BenchmarkSubackEncode-4          	29528050	        40.0 ns/op	      18 B/op	       0 allocs/op
-BenchmarkSubackDecode-4          	38786544	        31.0 ns/op	       0 B/op	       0 allocs/op
-BenchmarkSubackValidate-4        	1000000000	         0.338 ns/op	       0 B/op	       0 allocs/op
-BenchmarkSubscribeEncode-4       	 7494501	       155 ns/op	      73 B/op	       0 allocs/op
-BenchmarkSubscribeDecode-4       	 2645653	       395 ns/op	     267 B/op	       0 allocs/op
-BenchmarkSubscribeValidate-4     	1000000000	         0.487 ns/op	       0 B/op	       0 allocs/op
-BenchmarkNewFixedHeader-4        	1000000000	         0.343 ns/op	       0 B/op	       0 allocs/op
-BenchmarkUnsubackEncode-4        	42987544	        29.2 ns/op	      12 B/op	       0 allocs/op
-BenchmarkUnsubackDecode-4        	44304354	        27.5 ns/op	       0 B/op	       0 allocs/op
-BenchmarkUnsubackValidate-4      	1000000000	         0.335 ns/op	       0 B/op	       0 allocs/op
-BenchmarkUnsubscribeEncode-4     	 7955698	       148 ns/op	      76 B/op	       0 allocs/op
-BenchmarkUnsubscribeDecode-4     	 3199126	       360 ns/op	     258 B/op	       0 allocs/op
-BenchmarkUnsubscribeValidate-4   	1000000000	         0.456 ns/op	       0 B/op	       0 allocs/op
-PASS
-ok  	github.com/mochi-co/mqtt/packets	71.055s
-
-
-```




diff --git a/packets/codec.go b/packets/codec.go
deleted file mode 100644
index ad1c20c59b8bc0f15882095716ab4ee601e46ca9..0000000000000000000000000000000000000000
--- a/packets/codec.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package packets
-
-import (
-	"encoding/binary"
-	"unicode/utf8"
-	"unsafe"
-)
-
-// byteSlice2String provides a zero-alloc, no-copy byte to string conversion.
-// via https://github.com/golang/go/issues/25484#issuecomment-391415660
-func byteSlice2String(bs []byte) string {
-	return *(*string)(unsafe.Pointer(&bs))
-}
-
-// decodeUint16 extracts the value of two bytes from a byte array.
-func decodeUint16(buf []byte, offset int) (uint16, int, error) {
-	if len(buf) < offset+2 {
-		return 0, 0, ErrOffsetUintOutOfRange
-	}
-
-	return binary.BigEndian.Uint16(buf[offset : offset+2]), offset + 2, nil
-}
-
-// decodeString extracts a string from a byte array, beginning at an offset.
-func decodeString(buf []byte, offset int) (string, int, error) {
-	length, next, err := decodeUint16(buf, offset)
-	if err != nil {
-		return "", 0, err
-	}
-
-	if next+int(length) > len(buf) {
-		return "", 0, ErrOffsetStrOutOfRange
-	}
-
-	if !validUTF8(buf[next : next+int(length)]) {
-		return "", 0, ErrOffsetStrInvalidUTF8
-	}
-
-	return byteSlice2String(buf[next : next+int(length)]), next + int(length), nil
-}
-
-// decodeBytes extracts a byte array from a byte array, beginning at an offset. Used primarily for message payloads.
-func decodeBytes(buf []byte, offset int) ([]byte, int, error) {
-	length, next, err := decodeUint16(buf, offset)
-	if err != nil {
-		return make([]byte, 0, 0), 0, err
-	}
-
-	if next+int(length) > len(buf) {
-		return make([]byte, 0, 0), 0, ErrOffsetStrOutOfRange
-	}
-
-	return buf[next : next+int(length)], next + int(length), nil
-}
-
-// decodeByte extracts the value of a byte from a byte array.
-func decodeByte(buf []byte, offset int) (byte, int, error) {
-	if len(buf) <= offset {
-		return 0, 0, ErrOffsetByteOutOfRange
-	}
-	return buf[offset], offset + 1, nil
-}
-
-// decodeByteBool extracts the value of a byte from a byte array and returns a bool.
-func decodeByteBool(buf []byte, offset int) (bool, int, error) {
-	if len(buf) <= offset {
-		return false, 0, ErrOffsetBoolOutOfRange
-	}
-	return 1&buf[offset] > 0, offset + 1, nil
-}
-
-// encodeBool returns a byte instead of a bool.
-func encodeBool(b bool) byte {
-	if b {
-		return 1
-	}
-	return 0
-}
-
-// encodeBytes encodes a byte array to a byte array. Used primarily for message payloads.
-func encodeBytes(val []byte) []byte {
-	// In many circumstances the number of bytes being encoded is small.
-	// Setting the cap to a low amount allows us to account for those with
-	// triggering an alloc on append unless we need to.
-	buf := make([]byte, 2, 32)
-	binary.BigEndian.PutUint16(buf, uint16(len(val)))
-	return append(buf, val...)
-}
-
-// encodeUint16 encodes a uint16 value to a byte array.
-func encodeUint16(val uint16) []byte {
-	buf := make([]byte, 2)
-	binary.BigEndian.PutUint16(buf, val)
-	return buf
-}
-
-// encodeString encodes a string to a byte array.
-func encodeString(val string) []byte {
-	// Like encodeBytes, we set the cap to a small number to avoid
-	// triggering an alloc on append unless we absolutely need to.
-	buf := make([]byte, 2, 32)
-	binary.BigEndian.PutUint16(buf, uint16(len(val)))
-	return append(buf, []byte(val)...)
-}
-
-// validUTF8 checks if the byte array contains valid UTF-8 characters, specifically
-// conforming to the MQTT specification requirements.
-func validUTF8(b []byte) bool {
-
-	// [MQTT-1.4.0-1] The character data in a UTF-8 encoded string MUST be well-formed UTF-8...
-	if !utf8.Valid(b) {
-		return false
-	}
-
-	// [MQTT-1.4.0-2] A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000...
-	// ...
-	return true
-
-}




diff --git a/packets/codec_test.go b/packets/codec_test.go
deleted file mode 100644
index 586bfa11461ea356e13100b4d5cf04ca241c7b4c..0000000000000000000000000000000000000000
--- a/packets/codec_test.go
+++ /dev/null
@@ -1,372 +0,0 @@
-package packets
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestDecodeString(t *testing.T) {
-	expect := []struct {
-		rawBytes   []byte
-		result     []string
-		offset     int
-		shouldFail bool
-	}{
-		{
-			offset:   0,
-			rawBytes: []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97},
-			result:   []string{"a/b/c/d", "a"},
-		},
-		{
-			offset: 14,
-			rawBytes: []byte{
-				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
-				'h', 'e', 'y', // Client ID "zen"},
-			},
-			result: []string{"hey"},
-		},
-
-		{
-			offset:   2,
-			rawBytes: []byte{0, 0, 0, 23, 49, 47, 50, 47, 51, 47, 52, 47, 97, 47, 98, 47, 99, 47, 100, 47, 101, 47, 94, 47, 64, 47, 33, 97},
-			result:   []string{"1/2/3/4/a/b/c/d/e/^/@/!", "a"},
-		},
-		{
-			offset:   0,
-			rawBytes: []byte{0, 5, 120, 47, 121, 47, 122, 33, 64, 35, 36, 37, 94, 38},
-			result:   []string{"x/y/z", "!@#$%^&"},
-		},
-		{
-			offset:     0,
-			rawBytes:   []byte{0, 9, 'a', '/', 'b', '/', 'c', '/', 'd', 'z'},
-			result:     []string{"a/b/c/d", "z"},
-			shouldFail: true,
-		},
-		{
-			offset:     5,
-			rawBytes:   []byte{0, 7, 97, 47, 98, 47, 'x'},
-			result:     []string{"a/b/c/d", "x"},
-			shouldFail: true,
-		},
-		{
-			offset:     9,
-			rawBytes:   []byte{0, 7, 97, 47, 98, 47, 'y'},
-			result:     []string{"a/b/c/d", "y"},
-			shouldFail: true,
-		},
-		{
-			offset: 17,
-			rawBytes: []byte{
-				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', 'n', // Client ID "zen"
-				0, 6, // Will Topic - MSB+LSB
-				'l',
-			},
-			result:     []string{"lwt"},
-			shouldFail: true,
-		},
-	}
-
-	for i, wanted := range expect {
-		result, _, err := decodeString(wanted.rawBytes, wanted.offset)
-		if wanted.shouldFail {
-			require.Error(t, err, "Expected error decoding string [i:%d]", i)
-			continue
-		}
-
-		require.NoError(t, err, "Error decoding string [i:%d]", i)
-		require.Equal(t, wanted.result[0], result, "Incorrect decoded value [i:%d]", i)
-	}
-}
-
-func BenchmarkDecodeString(b *testing.B) {
-	in := []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97}
-	for n := 0; n < b.N; n++ {
-		decodeString(in, 0)
-	}
-}
-
-func TestDecodeBytes(t *testing.T) {
-	expect := []struct {
-		rawBytes   []byte
-		result     []uint8
-		next       int
-		offset     int
-		shouldFail bool
-	}{
-		{
-			rawBytes: []byte{0, 4, 77, 81, 84, 84, 4, 194, 0, 50, 0, 36, 49, 53, 52}, // ... truncated connect packet (clean session)
-			result:   []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
-			next:     6,
-			offset:   0,
-		},
-		{
-			rawBytes: []byte{0, 4, 77, 81, 84, 84, 4, 192, 0, 50, 0, 36, 49, 53, 52, 50}, // ... truncated connect packet, only checking start
-			result:   []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
-			next:     6,
-			offset:   0,
-		},
-		{
-			rawBytes:   []byte{0, 4, 77, 81},
-			result:     []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
-			offset:     0,
-			shouldFail: true,
-		},
-		{
-			rawBytes:   []byte{0, 4, 77, 81},
-			result:     []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
-			offset:     8,
-			shouldFail: true,
-		},
-		{
-			rawBytes:   []byte{0, 4, 77, 81},
-			result:     []uint8([]byte{0x4d, 0x51, 0x54, 0x54}),
-			offset:     0,
-			shouldFail: true,
-		},
-	}
-
-	for i, wanted := range expect {
-		result, _, err := decodeBytes(wanted.rawBytes, wanted.offset)
-		if wanted.shouldFail {
-			require.Error(t, err, "Expected error decoding bytes [i:%d]", i)
-			continue
-		}
-
-		require.NoError(t, err, "Error decoding bytes [i:%d]", i)
-		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
-	}
-}
-
-func BenchmarkDecodeBytes(b *testing.B) {
-	in := []byte{0, 4, 77, 81, 84, 84, 4, 194, 0, 50, 0, 36, 49, 53, 52}
-	for n := 0; n < b.N; n++ {
-		decodeBytes(in, 0)
-	}
-}
-
-func TestDecodeByte(t *testing.T) {
-	expect := []struct {
-		rawBytes   []byte
-		result     uint8
-		offset     int
-		shouldFail bool
-	}{
-		{
-			rawBytes: []byte{0, 4, 77, 81, 84, 84}, // nonsense slice of bytes
-			result:   uint8(0x00),
-			offset:   0,
-		},
-		{
-			rawBytes: []byte{0, 4, 77, 81, 84, 84},
-			result:   uint8(0x04),
-			offset:   1,
-		},
-		{
-			rawBytes: []byte{0, 4, 77, 81, 84, 84},
-			result:   uint8(0x4d),
-			offset:   2,
-		},
-		{
-			rawBytes: []byte{0, 4, 77, 81, 84, 84},
-			result:   uint8(0x51),
-			offset:   3,
-		},
-		{
-			rawBytes:   []byte{0, 4, 77, 80, 82, 84},
-			result:     uint8(0x00),
-			offset:     8,
-			shouldFail: true,
-		},
-	}
-
-	for i, wanted := range expect {
-		result, offset, err := decodeByte(wanted.rawBytes, wanted.offset)
-		if wanted.shouldFail {
-			require.Error(t, err, "Expected error decoding byte [i:%d]", i)
-			continue
-		}
-
-		require.NoError(t, err, "Error decoding byte [i:%d]", i)
-		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
-		require.Equal(t, i+1, offset, "Incorrect offset value [i:%d]", i)
-	}
-}
-
-func BenchmarkDecodeByte(b *testing.B) {
-	in := []byte{0, 4, 77, 81, 84, 84}
-	for n := 0; n < b.N; n++ {
-		decodeByte(in, 0)
-	}
-}
-
-func TestDecodeUint16(t *testing.T) {
-	expect := []struct {
-		rawBytes   []byte
-		result     uint16
-		offset     int
-		shouldFail bool
-	}{
-		{
-			rawBytes: []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97},
-			result:   uint16(0x07),
-			offset:   0,
-		},
-		{
-			rawBytes: []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97},
-			result:   uint16(0x761),
-			offset:   1,
-		},
-		{
-			rawBytes:   []byte{0, 7, 255, 47},
-			result:     uint16(0x761),
-			offset:     8,
-			shouldFail: true,
-		},
-	}
-
-	for i, wanted := range expect {
-		result, offset, err := decodeUint16(wanted.rawBytes, wanted.offset)
-		if wanted.shouldFail {
-			require.Error(t, err, "Expected error decoding uint16 [i:%d]", i)
-			continue
-		}
-
-		require.NoError(t, err, "Error decoding uint16 [i:%d]", i)
-		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
-		require.Equal(t, i+2, offset, "Incorrect offset value [i:%d]", i)
-	}
-}
-
-func BenchmarkDecodeUint16(b *testing.B) {
-	in := []byte{0, 7, 97, 47, 98, 47, 99, 47, 100, 97}
-	for n := 0; n < b.N; n++ {
-		decodeUint16(in, 0)
-	}
-}
-
-func TestDecodeByteBool(t *testing.T) {
-	expect := []struct {
-		rawBytes   []byte
-		result     bool
-		offset     int
-		shouldFail bool
-	}{
-		{
-			rawBytes: []byte{0x00, 0x00},
-			result:   false,
-		},
-		{
-			rawBytes: []byte{0x01, 0x00},
-			result:   true,
-		},
-		{
-			rawBytes:   []byte{0x01, 0x00},
-			offset:     5,
-			shouldFail: true,
-		},
-	}
-
-	for i, wanted := range expect {
-		result, offset, err := decodeByteBool(wanted.rawBytes, wanted.offset)
-		if wanted.shouldFail {
-			require.Error(t, err, "Expected error decoding byte bool [i:%d]", i)
-			continue
-		}
-
-		require.NoError(t, err, "Error decoding byte bool [i:%d]", i)
-		require.Equal(t, wanted.result, result, "Incorrect decoded value [i:%d]", i)
-		require.Equal(t, 1, offset, "Incorrect offset value [i:%d]", i)
-	}
-}
-
-func BenchmarkDecodeByteBool(b *testing.B) {
-	in := []byte{0x00, 0x00}
-	for n := 0; n < b.N; n++ {
-		decodeByteBool(in, 0)
-	}
-}
-
-func TestEncodeBool(t *testing.T) {
-	result := encodeBool(true)
-	require.Equal(t, byte(1), result, "Incorrect encoded value; not true")
-
-	result = encodeBool(false)
-	require.Equal(t, byte(0), result, "Incorrect encoded value; not false")
-
-	// Check failure.
-	result = encodeBool(false)
-	require.NotEqual(t, byte(1), result, "Expected failure, incorrect encoded value")
-}
-
-func BenchmarkEncodeBool(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		encodeBool(true)
-	}
-}
-
-func TestEncodeBytes(t *testing.T) {
-	result := encodeBytes([]byte("testing"))
-	require.Equal(t, []uint8{0, 7, 116, 101, 115, 116, 105, 110, 103}, result, "Incorrect encoded value")
-
-	result = encodeBytes([]byte("testing"))
-	require.NotEqual(t, []uint8{0, 7, 113, 101, 115, 116, 105, 110, 103}, result, "Expected failure, incorrect encoded value")
-}
-
-func BenchmarkEncodeBytes(b *testing.B) {
-	bb := []byte("testing")
-	for n := 0; n < b.N; n++ {
-		encodeBytes(bb)
-	}
-}
-
-func TestEncodeUint16(t *testing.T) {
-	result := encodeUint16(0)
-	require.Equal(t, []byte{0x00, 0x00}, result, "Incorrect encoded value, 0")
-
-	result = encodeUint16(32767)
-	require.Equal(t, []byte{0x7f, 0xff}, result, "Incorrect encoded value, 32767")
-
-	result = encodeUint16(65535)
-	require.Equal(t, []byte{0xff, 0xff}, result, "Incorrect encoded value, 65535")
-}
-
-func BenchmarkEncodeUint16(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		encodeUint16(32767)
-	}
-}
-
-func TestEncodeString(t *testing.T) {
-	result := encodeString("testing")
-	require.Equal(t, []uint8{0x00, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67}, result, "Incorrect encoded value, testing")
-
-	result = encodeString("")
-	require.Equal(t, []uint8{0x00, 0x00}, result, "Incorrect encoded value, null")
-
-	result = encodeString("a")
-	require.Equal(t, []uint8{0x00, 0x01, 0x61}, result, "Incorrect encoded value, a")
-
-	result = encodeString("b")
-	require.NotEqual(t, []uint8{0x00, 0x00}, result, "Expected failure, incorrect encoded value, b")
-
-}
-
-func BenchmarkEncodeString(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		encodeString("benchmarking")
-	}
-}




diff --git a/packets/connack.go b/packets/connack.go
deleted file mode 100644
index 114a4cc358534169c49a0a2dc2fa21feccb2a10c..0000000000000000000000000000000000000000
--- a/packets/connack.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// ConnackPacket contains the values of an MQTT CONNACK packet.
-type ConnackPacket struct {
-	FixedHeader
-
-	SessionPresent bool
-	ReturnCode     byte
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *ConnackPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.Remaining = 2
-	pk.FixedHeader.encode(buf)
-	buf.WriteByte(encodeBool(pk.SessionPresent))
-	buf.WriteByte(pk.ReturnCode)
-
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *ConnackPacket) Decode(buf []byte) error {
-
-	var offset int
-	var err error
-
-	// Unpack session present flag.
-	pk.SessionPresent, offset, err = decodeByteBool(buf, 0)
-	if err != nil {
-		return ErrMalformedSessionPresent
-	}
-
-	// Unpack return code.
-	pk.ReturnCode, offset, err = decodeByte(buf, offset)
-	if err != nil {
-		return ErrMalformedReturnCode
-	}
-
-	return nil
-
-}
-
-// Validate ensures the packet is compliant.
-func (pk *ConnackPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/connack_test.go b/packets/connack_test.go
deleted file mode 100644
index 28ecdea1259005f641ab54e18f8057edf86b9022..0000000000000000000000000000000000000000
--- a/packets/connack_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestConnackEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Connack)
-	for i, wanted := range expectedPackets[Connack] {
-
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(2), Connack, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(ConnackPacket)
-		copier.Copy(pk, wanted.packet.(*ConnackPacket))
-
-		require.Equal(t, Connack, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Connack, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		require.Equal(t, byte(Connack<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnackPacket).ReturnCode, pk.ReturnCode, "Mismatched return code [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnackPacket).SessionPresent, pk.SessionPresent, "Mismatched session present bool [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkConnackEncode(b *testing.B) {
-	pk := new(ConnackPacket)
-	copier.Copy(pk, expectedPackets[Connack][0].packet.(*ConnackPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestConnackDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Connack)
-	for i, wanted := range expectedPackets[Connack] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(2), Connack, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Connack).(*ConnackPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnackPacket).ReturnCode, pk.ReturnCode, "Mismatched return code [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnackPacket).SessionPresent, pk.SessionPresent, "Mismatched session present bool [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkConnackDecode(b *testing.B) {
-	pk := newPacket(Connack).(*ConnackPacket)
-	pk.FixedHeader.decode(expectedPackets[Connack][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Connack][0].rawBytes[2:])
-	}
-}
-
-func TestConnackValidate(t *testing.T) {
-	pk := newPacket(Connack).(*ConnackPacket)
-	pk.FixedHeader.decode(expectedPackets[Connack][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkConnackValidate(b *testing.B) {
-	pk := newPacket(Connack).(*ConnackPacket)
-	pk.FixedHeader.decode(expectedPackets[Connack][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/connect.go b/packets/connect.go
deleted file mode 100644
index 1b3f93feabe47988deac940c46ecdf46dd378856..0000000000000000000000000000000000000000
--- a/packets/connect.go
+++ /dev/null
@@ -1,189 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// ConnectPacket contains the values of an MQTT CONNECT packet.
-type ConnectPacket struct {
-	FixedHeader
-
-	ProtocolName     string
-	ProtocolVersion  byte
-	CleanSession     bool
-	WillFlag         bool
-	WillQos          byte
-	WillRetain       bool
-	UsernameFlag     bool
-	PasswordFlag     bool
-	ReservedBit      byte
-	Keepalive        uint16
-	ClientIdentifier string
-	WillTopic        string
-	WillMessage      []byte // WillMessage is a payload, so store as byte array.
-	Username         string
-	Password         string
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *ConnectPacket) Encode(buf *bytes.Buffer) error {
-
-	protoName := encodeString(pk.ProtocolName)
-	protoVersion := pk.ProtocolVersion
-	flag := encodeBool(pk.CleanSession)<<1 | encodeBool(pk.WillFlag)<<2 | pk.WillQos<<3 | encodeBool(pk.WillRetain)<<5 | encodeBool(pk.PasswordFlag)<<6 | encodeBool(pk.UsernameFlag)<<7
-	keepalive := encodeUint16(pk.Keepalive)
-	clientID := encodeString(pk.ClientIdentifier)
-
-	var willTopic, willFlag, usernameFlag, passwordFlag []byte
-
-	// If will flag is set, add topic and message.
-	if pk.WillFlag {
-		willTopic = encodeString(pk.WillTopic)
-		willFlag = encodeBytes(pk.WillMessage)
-	}
-
-	// If username flag is set, add username.
-	if pk.UsernameFlag {
-		usernameFlag = encodeString(pk.Username)
-	}
-
-	// If password flag is set, add password.
-	if pk.PasswordFlag {
-		passwordFlag = encodeString(pk.Password)
-	}
-
-	// Get a length for the connect header. This is not super pretty, but it works.
-	pk.FixedHeader.Remaining =
-		len(protoName) + 1 + 1 + len(keepalive) + len(clientID) +
-			len(willTopic) + len(willFlag) +
-			len(usernameFlag) + len(passwordFlag)
-
-	pk.FixedHeader.encode(buf)
-
-	// Eschew magic for readability.
-	buf.Write(protoName)
-	buf.WriteByte(protoVersion)
-	buf.WriteByte(flag)
-	buf.Write(keepalive)
-	buf.Write(clientID)
-	buf.Write(willTopic)
-	buf.Write(willFlag)
-	buf.Write(usernameFlag)
-	buf.Write(passwordFlag)
-
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *ConnectPacket) Decode(buf []byte) error {
-	var offset int
-	var err error
-
-	// Unpack protocol name and version.
-	pk.ProtocolName, offset, err = decodeString(buf, 0)
-	if err != nil {
-		return ErrMalformedProtocolName
-	}
-
-	pk.ProtocolVersion, offset, err = decodeByte(buf, offset)
-	if err != nil {
-		return ErrMalformedProtocolVersion
-	}
-	// Unpack flags byte.
-	flags, offset, err := decodeByte(buf, offset)
-	if err != nil {
-		return ErrMalformedFlags
-	}
-	pk.ReservedBit = 1 & flags
-	pk.CleanSession = 1&(flags>>1) > 0
-	pk.WillFlag = 1&(flags>>2) > 0
-	pk.WillQos = 3 & (flags >> 3) // this one is not a bool
-	pk.WillRetain = 1&(flags>>5) > 0
-	pk.PasswordFlag = 1&(flags>>6) > 0
-	pk.UsernameFlag = 1&(flags>>7) > 0
-
-	// Get keepalive interval.
-	pk.Keepalive, offset, err = decodeUint16(buf, offset)
-	if err != nil {
-		return ErrMalformedKeepalive
-	}
-
-	// Get client ID.
-	pk.ClientIdentifier, offset, err = decodeString(buf, offset)
-	if err != nil {
-		return ErrMalformedClientID
-	}
-
-	// Get Last Will and Testament topic and message if applicable.
-	if pk.WillFlag {
-		pk.WillTopic, offset, err = decodeString(buf, offset)
-		if err != nil {
-			return ErrMalformedWillTopic
-		}
-
-		pk.WillMessage, offset, err = decodeBytes(buf, offset)
-		if err != nil {
-			return ErrMalformedWillMessage
-		}
-	}
-
-	// Get username and password if applicable.
-	if pk.UsernameFlag {
-		pk.Username, offset, err = decodeString(buf, offset)
-		if err != nil {
-			return ErrMalformedUsername
-		}
-	}
-
-	if pk.PasswordFlag {
-		pk.Password, offset, err = decodeString(buf, offset)
-		if err != nil {
-			return ErrMalformedPassword
-		}
-	}
-
-	return nil
-
-}
-
-// Validate ensures the packet is compliant.
-func (pk *ConnectPacket) Validate() (b byte, err error) {
-
-	// End if protocol name is bad.
-	if pk.ProtocolName != "MQIsdp" && pk.ProtocolName != "MQTT" {
-		return CodeConnectProtocolViolation, ErrProtocolViolation
-	}
-
-	// End if protocol version is bad.
-	if (pk.ProtocolName == "MQIsdp" && pk.ProtocolVersion != 3) ||
-		(pk.ProtocolName == "MQTT" && pk.ProtocolVersion != 4) {
-		return CodeConnectBadProtocolVersion, ErrProtocolViolation
-	}
-
-	// End if reserved bit is not 0.
-	if pk.ReservedBit != 0 {
-		return CodeConnectProtocolViolation, ErrProtocolViolation
-	}
-
-	// End if ClientID is too long.
-	if len(pk.ClientIdentifier) > 65535 {
-		return CodeConnectProtocolViolation, ErrProtocolViolation
-	}
-
-	// End if password flag is set without a username.
-	if pk.PasswordFlag && !pk.UsernameFlag {
-		return CodeConnectProtocolViolation, ErrProtocolViolation
-	}
-
-	// End if Username or Password is too long.
-	if len(pk.Username) > 65535 || len(pk.Password) > 65535 {
-		return CodeConnectProtocolViolation, ErrProtocolViolation
-	}
-
-	// End if client id isn't set and clean session is false.
-	if !pk.CleanSession && len(pk.ClientIdentifier) == 0 {
-		return CodeConnectBadClientID, ErrProtocolViolation
-	}
-
-	return Accepted, nil
-}




diff --git a/packets/connect_test.go b/packets/connect_test.go
deleted file mode 100644
index a0ee55adc77ff1014e56e02a17b4e973cb63fb29..0000000000000000000000000000000000000000
--- a/packets/connect_test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestConnectEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Connect)
-	for i, wanted := range expectedPackets[Connect] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(1), Connect, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(ConnectPacket)
-		copier.Copy(pk, wanted.packet.(*ConnectPacket))
-
-		require.Equal(t, Connect, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Connect, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		pk.Encode(buf)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		require.Equal(t, byte(Connect<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-		ok, _ := pk.Validate()
-		require.Equal(t, byte(Accepted), ok, "Connect packet didn't validate - %v", ok)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Type, pk.FixedHeader.Type, "Mismatched packet fixed header type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched packet fixed header dup [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched packet fixed header qos [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched packet fixed header retain [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).ProtocolVersion, pk.ProtocolVersion, "Mismatched packet protocol version [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).ProtocolName, pk.ProtocolName, "Mismatched packet protocol name [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).CleanSession, pk.CleanSession, "Mismatched packet cleansession [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).ClientIdentifier, pk.ClientIdentifier, "Mismatched packet client id [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).Keepalive, pk.Keepalive, "Mismatched keepalive value [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).UsernameFlag, pk.UsernameFlag, "Mismatched packet username flag [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).Username, pk.Username, "Mismatched packet username [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).PasswordFlag, pk.PasswordFlag, "Mismatched packet password flag [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).Password, pk.Password, "Mismatched packet password [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillFlag, pk.WillFlag, "Mismatched packet will flag [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillTopic, pk.WillTopic, "Mismatched packet will topic [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillMessage, pk.WillMessage, "Mismatched packet will message [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillQos, pk.WillQos, "Mismatched packet will qos [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillRetain, pk.WillRetain, "Mismatched packet will retain [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkConnectEncode(b *testing.B) {
-	pk := new(ConnectPacket)
-	copier.Copy(pk, expectedPackets[Connect][0].packet.(*ConnectPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestConnectDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Connect)
-	for i, wanted := range expectedPackets[Connect] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(1), Connect, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, true, (len(wanted.rawBytes) > 2), "Insufficent bytes in packet [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Connect).(*ConnectPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Type, pk.FixedHeader.Type, "Mismatched packet fixed header type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched packet fixed header dup [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched packet fixed header qos [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched packet fixed header retain [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).ProtocolVersion, pk.ProtocolVersion, "Mismatched packet protocol version [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).ProtocolName, pk.ProtocolName, "Mismatched packet protocol name [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).CleanSession, pk.CleanSession, "Mismatched packet cleansession [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).ClientIdentifier, pk.ClientIdentifier, "Mismatched packet client id [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).Keepalive, pk.Keepalive, "Mismatched keepalive value [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).UsernameFlag, pk.UsernameFlag, "Mismatched packet username flag [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).Username, pk.Username, "Mismatched packet username [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).PasswordFlag, pk.PasswordFlag, "Mismatched packet password flag [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).Password, pk.Password, "Mismatched packet password [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillFlag, pk.WillFlag, "Mismatched packet will flag [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillTopic, pk.WillTopic, "Mismatched packet will topic [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillMessage, pk.WillMessage, "Mismatched packet will message [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillQos, pk.WillQos, "Mismatched packet will qos [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*ConnectPacket).WillRetain, pk.WillRetain, "Mismatched packet will retain [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkConnectDecode(b *testing.B) {
-	pk := newPacket(Connect).(*ConnectPacket)
-	pk.FixedHeader.decode(expectedPackets[Connect][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Connect][0].rawBytes[2:])
-	}
-}
-
-func TestConnectValidate(t *testing.T) {
-	require.Contains(t, expectedPackets, Connect)
-	for i, wanted := range expectedPackets[Connect] {
-		if wanted.group == "validate" {
-			pk := wanted.packet.(*ConnectPacket)
-			ok, _ := pk.Validate()
-			require.Equal(t, wanted.code, ok, "Connect packet didn't validate [i:%d] %s", i, wanted.desc)
-		}
-	}
-}
-
-func BenchmarkConnectValidate(b *testing.B) {
-	pk := newPacket(Connect).(*ConnectPacket)
-	pk.FixedHeader.decode(expectedPackets[Connect][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/disconnect.go b/packets/disconnect.go
deleted file mode 100644
index 67e40f4c5c55d3dd66f1ff82c1ea53404706b9b1..0000000000000000000000000000000000000000
--- a/packets/disconnect.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// DisconnectPacket contains the values of an MQTT DISCONNECT packet.
-type DisconnectPacket struct {
-	FixedHeader
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *DisconnectPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.encode(buf)
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *DisconnectPacket) Decode(buf []byte) error {
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *DisconnectPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/disconnect_test.go b/packets/disconnect_test.go
deleted file mode 100644
index 8e56db00f4fdc7b277a41e884824ae2b65847764..0000000000000000000000000000000000000000
--- a/packets/disconnect_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestDisconnectEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Disconnect)
-	for i, wanted := range expectedPackets[Disconnect] {
-		require.Equal(t, uint8(14), Disconnect, "Incorrect Packet Type [i:%d]", i)
-
-		pk := new(DisconnectPacket)
-		copier.Copy(pk, wanted.packet.(*DisconnectPacket))
-
-		require.Equal(t, Disconnect, pk.Type, "Mismatched Packet Type [i:%d]", i)
-		require.Equal(t, Disconnect, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d]", i)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d]", i)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d]", i)
-	}
-}
-
-func BenchmarkDisconnectEncode(b *testing.B) {
-	pk := new(DisconnectPacket)
-	copier.Copy(pk, expectedPackets[Disconnect][0].packet.(*DisconnectPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestDisconnectDecode(t *testing.T) {
-	pk := newPacket(Disconnect).(*DisconnectPacket)
-
-	var b = []byte{}
-	err := pk.Decode(b)
-	require.NoError(t, err, "Error unpacking buffer")
-	require.Empty(t, b)
-}
-
-func BenchmarkDisconnectDecode(b *testing.B) {
-	pk := newPacket(Disconnect).(*DisconnectPacket)
-	pk.FixedHeader.decode(expectedPackets[Disconnect][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Disconnect][0].rawBytes[2:])
-	}
-}
-
-func TestDisconnectValidate(t *testing.T) {
-	pk := newPacket(Disconnect).(*DisconnectPacket)
-	pk.FixedHeader.decode(expectedPackets[Disconnect][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkDisconnectValidate(b *testing.B) {
-	pk := newPacket(Disconnect).(*DisconnectPacket)
-	pk.FixedHeader.decode(expectedPackets[Disconnect][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/errors.go b/packets/errors.go
deleted file mode 100644
index 674246d22f31cec3cfe26033bac38a851e10f107..0000000000000000000000000000000000000000
--- a/packets/errors.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package packets
-
-import (
-	"errors"
-)
-
-const (
-	Accepted byte = 0x00
-	Failed   byte = 0xFF
-
-	CodeConnectBadProtocolVersion byte = 0x01
-	CodeConnectBadClientID        byte = 0x02
-	CodeConnectServerUnavailable  byte = 0x03
-	CodeConnectBadAuthValues      byte = 0x04
-	CodeConnectNotAuthorised      byte = 0x05
-	CodeConnectNetworkError       byte = 0xFE
-	CodeConnectProtocolViolation  byte = 0xFF
-
-	ErrSubAckNetworkError byte = 0x80
-)
-
-var (
-	// CONNECT
-	ErrMalformedProtocolName    = errors.New("malformed packet: protocol name")
-	ErrMalformedProtocolVersion = errors.New("malformed packet: protocol version")
-	ErrMalformedFlags           = errors.New("malformed packet: flags")
-	ErrMalformedKeepalive       = errors.New("malformed packet: keepalive")
-	ErrMalformedClientID        = errors.New("malformed packet: client id")
-	ErrMalformedWillTopic       = errors.New("malformed packet: will topic")
-	ErrMalformedWillMessage     = errors.New("malformed packet: will message")
-	ErrMalformedUsername        = errors.New("malformed packet: username")
-	ErrMalformedPassword        = errors.New("malformed packet: password")
-
-	// CONNACK
-	ErrMalformedSessionPresent = errors.New("malformed packet: session present")
-	ErrMalformedReturnCode     = errors.New("malformed packet: return code")
-
-	// PUBLISH
-	ErrMalformedTopic    = errors.New("malformed packet: topic name")
-	ErrMalformedPacketID = errors.New("malformed packet: packet id")
-
-	// SUBSCRIBE
-	ErrMalformedQoS = errors.New("malformed packet: qos")
-
-	// PACKETS
-	ErrProtocolViolation        = errors.New("protocol violation")
-	ErrOffsetStrOutOfRange      = errors.New("offset string out of range")
-	ErrOffsetBytesOutOfRange    = errors.New("offset bytes out of range")
-	ErrOffsetByteOutOfRange     = errors.New("offset byte out of range")
-	ErrOffsetBoolOutOfRange     = errors.New("offset bool out of range")
-	ErrOffsetUintOutOfRange     = errors.New("offset uint out of range")
-	ErrOffsetStrInvalidUTF8     = errors.New("offset string invalid utf8")
-	ErrInvalidFlags             = errors.New("invalid flags set for packet")
-	ErrOversizedLengthIndicator = errors.New("protocol violation: oversized length indicator")
-	ErrMissingPacketID          = errors.New("missing packet id")
-	ErrSurplusPacketID          = errors.New("surplus packet id")
-)
-
-// validateQoS ensures the QoS byte is within the correct range.
-func validateQoS(qos byte) bool {
-
-	if qos >= 0 && qos <= 2 {
-		return true
-	}
-
-	return false
-}




diff --git a/packets/fixedheader.go b/packets/fixedheader.go
deleted file mode 100644
index 9067411fee78dc30df67eacb4d1df55d3a07845b..0000000000000000000000000000000000000000
--- a/packets/fixedheader.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// FixedHeader contains the values of the fixed header portion of the MQTT packet.
-type FixedHeader struct {
-
-	// Type is the type of the packet (PUBLISH, SUBSCRIBE, etc) from bits 7 - 4 (byte 1).
-	Type byte
-
-	// Dup indicates if the packet is a duplicate.
-	Dup bool
-
-	// Qos byte indicates the quality of service expected.
-	Qos byte
-
-	// Retain indicates whether the message should be retained.
-	Retain bool
-
-	// Remaining is the number of remaining bytes in the payload.
-	Remaining int
-}
-
-// encode encodes the FixedHeader and returns a bytes buffer.
-func (fh *FixedHeader) encode(buf *bytes.Buffer) {
-	buf.WriteByte(fh.Type<<4 | encodeBool(fh.Dup)<<3 | fh.Qos<<1 | encodeBool(fh.Retain))
-	encodeLength(buf, fh.Remaining)
-}
-
-// decode extracts the specification bits from the header byte.
-func (fh *FixedHeader) decode(headerByte byte) error {
-
-	// Get the message type from the first 4 bytes.
-	fh.Type = headerByte >> 4
-
-	// @SPEC [MQTT-2.2.2-1]
-	// Where a flag bit is marked as “Reserved” in Table 2.2 - Flag Bits,
-	// it is reserved for future use and MUST be set to the value listed in that table.
-	switch fh.Type {
-
-	case Publish:
-		fh.Dup = (headerByte>>3)&0x01 > 0 // Extract flags. Check if message is duplicate.
-		fh.Qos = (headerByte >> 1) & 0x03 // Extract QoS flag.
-		fh.Retain = headerByte&0x01 > 0   // Extract retain flag.
-
-	case Pubrel:
-		fh.Qos = (headerByte >> 1) & 0x03
-
-	case Subscribe:
-		fh.Qos = (headerByte >> 1) & 0x03
-
-	case Unsubscribe:
-		fh.Qos = (headerByte >> 1) & 0x03
-
-	default:
-
-		// [MQTT-2.2.2-2]
-		// If invalid flags are received, the receiver MUST close the Network Connection.
-		if (headerByte>>3)&0x01 > 0 || (headerByte>>1)&0x03 > 0 || headerByte&0x01 > 0 {
-			return ErrInvalidFlags
-		}
-	}
-
-	return nil
-
-}
-
-// encodeLength writes length bits for the header.
-func encodeLength(buf *bytes.Buffer, length int) {
-	for {
-		digit := byte(length % 128)
-		length /= 128
-		if length > 0 {
-			digit |= 0x80
-		}
-		buf.WriteByte(digit)
-		if length == 0 {
-			break
-		}
-	}
-}




diff --git a/packets/fixedheader_test.go b/packets/fixedheader_test.go
deleted file mode 100644
index 122593d2ea44545125a8061c544a79e158407de5..0000000000000000000000000000000000000000
--- a/packets/fixedheader_test.go
+++ /dev/null
@@ -1,220 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"math"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-type fixedHeaderTable struct {
-	rawBytes    []byte
-	header      FixedHeader
-	packetError bool
-	flagError   bool
-}
-
-var fixedHeaderExpected = []fixedHeaderTable{
-	{
-		rawBytes: []byte{Connect << 4, 0x00},
-		header:   FixedHeader{Connect, false, 0, false, 0}, // Type byte, Dup bool, Qos byte, Retain bool, Remaining int
-	},
-	{
-		rawBytes: []byte{Connack << 4, 0x00},
-		header:   FixedHeader{Connack, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Publish << 4, 0x00},
-		header:   FixedHeader{Publish, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 1<<1, 0x00},
-		header:   FixedHeader{Publish, false, 1, false, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 1<<1 | 1, 0x00},
-		header:   FixedHeader{Publish, false, 1, true, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 2<<1, 0x00},
-		header:   FixedHeader{Publish, false, 2, false, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 2<<1 | 1, 0x00},
-		header:   FixedHeader{Publish, false, 2, true, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 1<<3, 0x00},
-		header:   FixedHeader{Publish, true, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 1<<3 | 1, 0x00},
-		header:   FixedHeader{Publish, true, 0, true, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 1<<3 | 1<<1 | 1, 0x00},
-		header:   FixedHeader{Publish, true, 1, true, 0},
-	},
-	{
-		rawBytes: []byte{Publish<<4 | 1<<3 | 2<<1 | 1, 0x00},
-		header:   FixedHeader{Publish, true, 2, true, 0},
-	},
-	{
-		rawBytes: []byte{Puback << 4, 0x00},
-		header:   FixedHeader{Puback, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Pubrec << 4, 0x00},
-		header:   FixedHeader{Pubrec, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Pubrel<<4 | 1<<1, 0x00},
-		header:   FixedHeader{Pubrel, false, 1, false, 0},
-	},
-	{
-		rawBytes: []byte{Pubcomp << 4, 0x00},
-		header:   FixedHeader{Pubcomp, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Subscribe<<4 | 1<<1, 0x00},
-		header:   FixedHeader{Subscribe, false, 1, false, 0},
-	},
-	{
-		rawBytes: []byte{Suback << 4, 0x00},
-		header:   FixedHeader{Suback, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Unsubscribe<<4 | 1<<1, 0x00},
-		header:   FixedHeader{Unsubscribe, false, 1, false, 0},
-	},
-	{
-		rawBytes: []byte{Unsuback << 4, 0x00},
-		header:   FixedHeader{Unsuback, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Pingreq << 4, 0x00},
-		header:   FixedHeader{Pingreq, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Pingresp << 4, 0x00},
-		header:   FixedHeader{Pingresp, false, 0, false, 0},
-	},
-	{
-		rawBytes: []byte{Disconnect << 4, 0x00},
-		header:   FixedHeader{Disconnect, false, 0, false, 0},
-	},
-
-	// remaining length
-	{
-		rawBytes: []byte{Publish << 4, 0x0a},
-		header:   FixedHeader{Publish, false, 0, false, 10},
-	},
-	{
-		rawBytes: []byte{Publish << 4, 0x80, 0x04},
-		header:   FixedHeader{Publish, false, 0, false, 512},
-	},
-	{
-		rawBytes: []byte{Publish << 4, 0xd2, 0x07},
-		header:   FixedHeader{Publish, false, 0, false, 978},
-	},
-	{
-		rawBytes: []byte{Publish << 4, 0x86, 0x9d, 0x01},
-		header:   FixedHeader{Publish, false, 0, false, 20102},
-	},
-	{
-		rawBytes:    []byte{Publish << 4, 0xd5, 0x86, 0xf9, 0x9e, 0x01},
-		header:      FixedHeader{Publish, false, 0, false, 333333333},
-		packetError: true,
-	},
-
-	// Invalid flags for packet
-	{
-		rawBytes:  []byte{Connect<<4 | 1<<3, 0x00},
-		header:    FixedHeader{Connect, true, 0, false, 0},
-		flagError: true,
-	},
-	{
-		rawBytes:  []byte{Connect<<4 | 1<<1, 0x00},
-		header:    FixedHeader{Connect, false, 1, false, 0},
-		flagError: true,
-	},
-	{
-		rawBytes:  []byte{Connect<<4 | 1, 0x00},
-		header:    FixedHeader{Connect, false, 0, true, 0},
-		flagError: true,
-	},
-}
-
-func TestFixedHeaderEncode(t *testing.T) {
-	for i, wanted := range fixedHeaderExpected {
-		buf := new(bytes.Buffer)
-		wanted.header.encode(buf)
-		if wanted.flagError == false {
-			require.Equal(t, len(wanted.rawBytes), len(buf.Bytes()), "Mismatched fixedheader length [i:%d] %v", i, wanted.rawBytes)
-			require.EqualValues(t, wanted.rawBytes, buf.Bytes(), "Mismatched byte values [i:%d] %v", i, wanted.rawBytes)
-		}
-	}
-}
-
-func BenchmarkFixedHeaderEncode(b *testing.B) {
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		fixedHeaderExpected[0].header.encode(buf)
-	}
-}
-
-func TestFixedHeaderDecode(t *testing.T) {
-	for i, wanted := range fixedHeaderExpected {
-		fh := new(FixedHeader)
-		err := fh.decode(wanted.rawBytes[0])
-		if wanted.flagError {
-			require.Error(t, err, "Expected error reading fixedheader [i:%d] %v", i, wanted.rawBytes)
-		} else {
-			require.NoError(t, err, "Error reading fixedheader [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Type, fh.Type, "Mismatched fixedheader type [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Dup, fh.Dup, "Mismatched fixedheader dup [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Qos, fh.Qos, "Mismatched fixedheader qos [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Retain, fh.Retain, "Mismatched fixedheader retain [i:%d] %v", i, wanted.rawBytes)
-		}
-	}
-}
-
-func BenchmarkFixedHeaderDecode(b *testing.B) {
-	fh := new(FixedHeader)
-	for n := 0; n < b.N; n++ {
-		err := fh.decode(fixedHeaderExpected[0].rawBytes[0])
-		if err != nil {
-			panic(err)
-		}
-	}
-}
-
-func TestEncodeLength(t *testing.T) {
-	tt := []struct {
-		have int
-		want []byte
-	}{
-		{
-			120,
-			[]byte{0x78},
-		},
-		{
-			math.MaxInt64,
-			[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
-		},
-	}
-
-	for i, wanted := range tt {
-		buf := new(bytes.Buffer)
-		encodeLength(buf, wanted.have)
-		require.Equal(t, wanted.want, buf.Bytes(), "Returned bytes should match length [i:%d] %s", i, wanted.have)
-	}
-}
-
-func BenchmarkEncodeLength(b *testing.B) {
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		encodeLength(buf, 120)
-	}
-}




diff --git a/packets/packets.go b/packets/packets.go
deleted file mode 100644
index f55e420e2ab1ea7e374a1ee44da3054e43d32d64..0000000000000000000000000000000000000000
--- a/packets/packets.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package packets
-
-// All of the valid packet types and their packet identifier.
-const (
-	Reserved    byte = iota
-	Connect          // 1
-	Connack          // 2
-	Publish          // 3
-	Puback           // 4
-	Pubrec           // 5
-	Pubrel           // 6
-	Pubcomp          // 7
-	Subscribe        // 8
-	Suback           // 9
-	Unsubscribe      // 10
-	Unsuback         // 11
-	Pingreq          // 12
-	Pingresp         // 13
-	Disconnect       // 14
-)
-
-// Names is a map that provides human-readable names for the different
-// MQTT packet types based on their ids.
-var Names = map[byte]string{
-	0:  "RESERVED",
-	1:  "CONNECT",
-	2:  "CONNACK",
-	3:  "PUBLISH",
-	4:  "PUBACK",
-	5:  "PUBREC",
-	6:  "PUBREL",
-	7:  "PUBCOMP",
-	8:  "SUBSCRIBE",
-	9:  "SUBACK",
-	10: "UNSUBSCRIBE",
-	11: "UNSUBACK",
-	12: "PINGREQ",
-	13: "PINGRESP",
-	14: "DISCONNECT",
-}
-
-// newPacket returns a packet of a specified packetType.
-// this is a convenience package for testing and shouldn't be used for production
-// code.
-func newPacket(packetType byte) Packet {
-
-	switch packetType {
-	case Connect:
-		return &ConnectPacket{FixedHeader: FixedHeader{Type: Connect}}
-	case Connack:
-		return &ConnackPacket{FixedHeader: FixedHeader{Type: Connack}}
-	case Publish:
-		return &PublishPacket{FixedHeader: FixedHeader{Type: Publish}}
-	case Puback:
-		return &PubackPacket{FixedHeader: FixedHeader{Type: Puback}}
-	case Pubrec:
-		return &PubrecPacket{FixedHeader: FixedHeader{Type: Pubrec}}
-	case Pubrel:
-		return &PubrelPacket{FixedHeader: FixedHeader{Type: Pubrel, Qos: 1}}
-	case Pubcomp:
-		return &PubcompPacket{FixedHeader: FixedHeader{Type: Pubcomp}}
-	case Subscribe:
-		return &SubscribePacket{FixedHeader: FixedHeader{Type: Subscribe, Qos: 1}}
-	case Suback:
-		return &SubackPacket{FixedHeader: FixedHeader{Type: Suback}}
-	case Unsubscribe:
-		return &UnsubscribePacket{FixedHeader: FixedHeader{Type: Unsubscribe, Qos: 1}}
-	case Unsuback:
-		return &UnsubackPacket{FixedHeader: FixedHeader{Type: Unsuback}}
-	case Pingreq:
-		return &PingreqPacket{FixedHeader: FixedHeader{Type: Pingreq}}
-	case Pingresp:
-		return &PingrespPacket{FixedHeader: FixedHeader{Type: Pingresp}}
-	case Disconnect:
-		return &DisconnectPacket{FixedHeader: FixedHeader{Type: Disconnect}}
-	}
-	return nil
-
-}




diff --git a/packets/packets_test.go b/packets/packets_test.go
deleted file mode 100644
index 1b98b8d076daa184092e0f8e1490c3415e153abf..0000000000000000000000000000000000000000
--- a/packets/packets_test.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package packets
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestNewPacket(t *testing.T) {
-	// All the other packet types are handled and tested thorougly by the other tests.
-	// Just check for out of range packet code.
-	pk := newPacket(99)
-	require.Equal(t, nil, pk, "Returned packet should be nil")
-}




diff --git a/packets/parser.go b/packets/parser.go
deleted file mode 100644
index 54e90f8b8edc81fc7315b4d4ac6e70620bad19a9..0000000000000000000000000000000000000000
--- a/packets/parser.go
+++ /dev/null
@@ -1,204 +0,0 @@
-package packets
-
-import (
-	"bufio"
-	"bytes"
-	"encoding/binary"
-	"errors"
-	"io"
-	"net"
-	"sync"
-	"time"
-)
-
-// Packet is the base interface that all MQTT packets must implement.
-type Packet interface {
-
-	// Encode encodes a packet into a byte buffer.
-	Encode(*bytes.Buffer) error
-
-	// Decode decodes a byte array into a packet struct.
-	Decode([]byte) error
-
-	// Validate the packet. Returns a error code and error if not valid.
-	Validate() (byte, error)
-}
-
-// BufWriter is an interface for satisfying a bufio.Writer. This is mainly
-// in place to allow testing.
-type BufWriter interface {
-
-	// Write writes a byte buffer.
-	Write(p []byte) (nn int, err error)
-
-	// Flush flushes the buffer.
-	Flush() error
-}
-
-// Parser is an MQTT packet parser that reads and writes MQTT payloads to a
-// buffered IO stream.
-type Parser struct {
-	sync.RWMutex
-
-	// Conn is the net.Conn used to establish the connection.
-	Conn net.Conn
-
-	// R is a bufio reader for peeking and reading incoming packets.
-	R *bufio.Reader
-
-	// W is a bufio writer for writing outgoing packets.
-	W BufWriter
-
-	// FixedHeader is the fixed header from the last seen packet.
-	FixedHeader FixedHeader
-}
-
-// NewParser returns an instance of Parser for a connection.
-func NewParser(c net.Conn, r *bufio.Reader, w BufWriter) *Parser {
-	return &Parser{
-		Conn: c,
-		R:    r,
-		W:    w,
-	}
-}
-
-// RefreshDeadline refreshes the read/write deadline for the net.Conn connection.
-func (p *Parser) RefreshDeadline(keepalive uint16) {
-	if p.Conn != nil {
-		expiry := time.Duration(keepalive+(keepalive/2)) * time.Second
-		p.Conn.SetDeadline(time.Now().Add(expiry))
-	}
-}
-
-// ReadFixedHeader reads in the values of the next packet's fixed header.
-func (p *Parser) ReadFixedHeader(fh *FixedHeader) error {
-
-	// Peek the maximum message type and flags, and length.
-	peeked, err := p.R.Peek(1)
-	if err != nil {
-		return err
-	}
-
-	// Unpack message type and flags from byte 1.
-	err = fh.decode(peeked[0])
-	if err != nil {
-
-		// @SPEC [MQTT-2.2.2-2]
-		// If invalid flags are received, the receiver MUST close the Network Connection.
-		return ErrInvalidFlags
-	}
-
-	// The remaining length value can be up to 5 bytes. Peek through each byte
-	// looking for continue values, and if found increase the peek. Otherwise
-	// decode the bytes that were legit.
-	//p.fhBuffer = p.fhBuffer[:0]
-	buf := make([]byte, 0, 6)
-	i := 1
-	b := 2
-	for ; b < 6; b++ {
-		peeked, err = p.R.Peek(b)
-		if err != nil {
-			return err
-		}
-
-		// Add the byte to the length bytes slice.
-		buf = append(buf, peeked[i])
-
-		// If it's not a continuation flag, end here.
-		if peeked[i] < 128 {
-			break
-		}
-
-		// If i has reached 4 without a length terminator, throw a protocol violation.
-		i++
-		if i == 4 {
-			return ErrOversizedLengthIndicator
-		}
-	}
-
-	// Calculate and store the remaining length.
-	rem, _ := binary.Uvarint(buf)
-	fh.Remaining = int(rem)
-
-	// Discard the number of used length bytes + first byte.
-	p.R.Discard(b)
-
-	// Set the fixed header in the parser.
-	p.FixedHeader = *fh
-
-	return nil
-}
-
-// Read reads the remaining buffer into an MQTT packet.
-func (p *Parser) Read() (pk Packet, err error) {
-
-	switch p.FixedHeader.Type {
-	case Connect:
-		pk = &ConnectPacket{FixedHeader: p.FixedHeader}
-	case Connack:
-		pk = &ConnackPacket{FixedHeader: p.FixedHeader}
-	case Publish:
-		pk = &PublishPacket{FixedHeader: p.FixedHeader}
-	case Puback:
-		pk = &PubackPacket{FixedHeader: p.FixedHeader}
-	case Pubrec:
-		pk = &PubrecPacket{FixedHeader: p.FixedHeader}
-	case Pubrel:
-		pk = &PubrelPacket{FixedHeader: p.FixedHeader}
-	case Pubcomp:
-		pk = &PubcompPacket{FixedHeader: p.FixedHeader}
-	case Subscribe:
-		pk = &SubscribePacket{FixedHeader: p.FixedHeader}
-	case Suback:
-		pk = &SubackPacket{FixedHeader: p.FixedHeader}
-	case Unsubscribe:
-		pk = &UnsubscribePacket{FixedHeader: p.FixedHeader}
-	case Unsuback:
-		pk = &UnsubackPacket{FixedHeader: p.FixedHeader}
-	case Pingreq:
-		pk = &PingreqPacket{FixedHeader: p.FixedHeader}
-	case Pingresp:
-		pk = &PingrespPacket{FixedHeader: p.FixedHeader}
-	case Disconnect:
-		pk = &DisconnectPacket{FixedHeader: p.FixedHeader}
-	default:
-		return pk, errors.New("No valid packet available; " + string(p.FixedHeader.Type))
-	}
-
-	// Attempt to peek the rest of the packet.
-	peeked := true
-
-	bt, err := p.R.Peek(p.FixedHeader.Remaining)
-	if err != nil {
-
-		// Only try to continue if reading is still possible.
-		if err != bufio.ErrBufferFull {
-			return pk, err
-		}
-
-		// If it didn't work, read the buffer directly, and if that still doesn't
-		// work, then throw an error.
-		peeked = false
-		bt = make([]byte, p.FixedHeader.Remaining)
-		_, err := io.ReadFull(p.R, bt)
-		if err != nil {
-			return pk, err
-		}
-	}
-
-	// If peeking was successful, discard the rest of the packet now it's been read.
-	if peeked {
-		p.R.Discard(p.FixedHeader.Remaining)
-	}
-
-	// Decode the remaining packet values using a fresh copy of the bytes,
-	// otherwise the next packet will change the data of this one.
-	// 🚨 DANGER!! This line is super important. If the bytes being decoded are not
-	// in their own memory space, packets will get corrupted all over the place.
-	err = pk.Decode(append([]byte{}, bt[:]...)) // <--- MUST BE A COPY.
-	if err != nil {
-		return pk, err
-	}
-
-	return
-}




diff --git a/packets/parser_test.go b/packets/parser_test.go
deleted file mode 100644
index 33fc8a3598e71bd4b67b72eda1fc410162689575..0000000000000000000000000000000000000000
--- a/packets/parser_test.go
+++ /dev/null
@@ -1,342 +0,0 @@
-package packets
-
-import (
-	"bufio"
-	"bytes"
-	"io"
-	"net"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/require"
-)
-
-func newBufioReader(c io.Reader) *bufio.Reader {
-	return bufio.NewReaderSize(c, 512)
-}
-
-func newBufioWriter(c io.Writer) *bufio.Writer {
-	return bufio.NewWriterSize(c, 512)
-}
-
-func TestNewParser(t *testing.T) {
-	conn := new(MockNetConn)
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-	require.NotNil(t, p.R)
-}
-
-func BenchmarkNewParser(b *testing.B) {
-	conn := new(MockNetConn)
-	r, w := new(bufio.Reader), new(bufio.Writer)
-
-	for n := 0; n < b.N; n++ {
-		NewParser(conn, r, w)
-	}
-}
-
-func TestRefreshDeadline(t *testing.T) {
-	conn := new(MockNetConn)
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-
-	dl := p.Conn.(*MockNetConn).Deadline
-	p.RefreshDeadline(10)
-
-	require.NotEqual(t, dl, p.Conn.(*MockNetConn).Deadline)
-}
-
-func BenchmarkRefreshDeadline(b *testing.B) {
-	conn := new(MockNetConn)
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-
-	for n := 0; n < b.N; n++ {
-		p.RefreshDeadline(10)
-	}
-}
-
-func TestReadFixedHeader(t *testing.T) {
-	conn := new(MockNetConn)
-
-	// Test null data.
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-	fh := new(FixedHeader)
-	err := p.ReadFixedHeader(fh)
-	require.Error(t, err)
-
-	// Test insufficient peeking.
-	fh = new(FixedHeader)
-	p.R = bufio.NewReader(bytes.NewReader([]byte{Connect << 4}))
-	err = p.ReadFixedHeader(fh)
-	require.Error(t, err)
-
-	// Test expected bytes.
-	for i, wanted := range fixedHeaderExpected {
-		fh := new(FixedHeader)
-		p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-		b := wanted.rawBytes
-		p.R = bufio.NewReader(bytes.NewReader(b))
-
-		err := p.ReadFixedHeader(fh)
-		if wanted.packetError || wanted.flagError {
-			require.Error(t, err, "Expected error [i:%d] %v", i, wanted.rawBytes)
-		} else {
-			require.NoError(t, err, "Error reading fixedheader [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Type, p.FixedHeader.Type, "Mismatched fixedheader type [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Dup, p.FixedHeader.Dup, "Mismatched fixedheader dup [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Qos, p.FixedHeader.Qos, "Mismatched fixedheader qos [i:%d] %v", i, wanted.rawBytes)
-			require.Equal(t, wanted.header.Retain, p.FixedHeader.Retain, "Mismatched fixedheader retain [i:%d] %v", i, wanted.rawBytes)
-		}
-	}
-}
-
-func BenchmarkReadFixedHeader(b *testing.B) {
-	conn := new(MockNetConn)
-	fh := new(FixedHeader)
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-
-	var rn bytes.Reader = *bytes.NewReader(fixedHeaderExpected[0].rawBytes)
-	var rc bytes.Reader
-	for n := 0; n < b.N; n++ {
-		rc = rn
-		p.R.Reset(&rc)
-		err := p.ReadFixedHeader(fh)
-		if err != nil {
-			panic(err)
-		}
-	}
-}
-
-func TestRead(t *testing.T) {
-	conn := new(MockNetConn)
-
-	for code, pt := range expectedPackets {
-		for i, wanted := range pt {
-			if wanted.primary {
-				var fh FixedHeader
-				b := wanted.rawBytes
-				p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-				p.R = bufio.NewReader(bytes.NewReader(b))
-
-				err := p.ReadFixedHeader(&fh)
-				if wanted.failFirst != nil {
-					require.Error(t, err, "Expected error reading fixedheader [i:%d] %s - %s", i, wanted.desc, Names[code])
-				} else {
-					require.NoError(t, err, "Error reading fixedheader [i:%d] %s - %s", i, wanted.desc, Names[code])
-				}
-
-				pko, err := p.Read()
-
-				if wanted.expect != nil {
-					require.Error(t, err, "Expected error reading packet [i:%d] %s - %s", i, wanted.desc, Names[code])
-					if err != nil {
-						require.Equal(t, err, wanted.expect, "Mismatched packet error [i:%d] %s - %s", i, wanted.desc, Names[code])
-					}
-				} else {
-					require.NoError(t, err, "Error reading packet [i:%d] %s - %s", i, wanted.desc, Names[code])
-					require.Equal(t, wanted.packet, pko, "Mismatched packet final [i:%d] %s - %s", i, wanted.desc, Names[code])
-				}
-			}
-		}
-	}
-
-	// Fail decoder
-	var fh FixedHeader
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-	p.R = bufio.NewReader(bytes.NewReader([]byte{
-		byte(Publish << 4), 3, // Fixed header
-		0, 5, // Topic Name - LSB+MSB
-		'a', '/',
-	}))
-	err := p.ReadFixedHeader(&fh)
-	require.NoError(t, err)
-	_, err = p.Read()
-	require.Error(t, err)
-}
-
-func BenchmarkRead(b *testing.B) {
-	conn := new(MockNetConn)
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-
-	p.R = bufio.NewReader(bytes.NewReader(expectedPackets[Publish][1].rawBytes))
-	var fh FixedHeader
-	err := p.ReadFixedHeader(&fh)
-	if err != nil {
-		panic(err)
-	}
-
-	var rn bytes.Reader = *bytes.NewReader(expectedPackets[Publish][1].rawBytes)
-	var rc bytes.Reader
-	for n := 0; n < b.N; n++ {
-		rc = rn
-		p.R.Reset(&rc)
-		p.R.Discard(2)
-		_, err := p.Read()
-		if err != nil {
-			panic(err)
-		}
-	}
-
-}
-
-// This is a super important test. It checks whether or not subsequent packets
-// mutate each other. This happens when you use a single byte buffer for decoding
-// multiple packets.
-func TestReadPacketNoOverwrite(t *testing.T) {
-	pk1 := []byte{
-		byte(Publish << 4), 12, // Fixed header
-		0, 5, // Topic Name - LSB+MSB
-		'a', '/', 'b', '/', 'c', // Topic Name
-		'h', 'e', 'l', 'l', 'o', // Payload
-	}
-
-	pk2 := []byte{
-		byte(Publish << 4), 14, // Fixed header
-		0, 5, // Topic Name - LSB+MSB
-		'x', '/', 'y', '/', 'z', // Topic Name
-		'y', 'a', 'h', 'a', 'l', 'l', 'o', // Payload
-	}
-
-	r, w := net.Pipe()
-	p := NewParser(r, newBufioReader(r), newBufioWriter(w))
-	go func() {
-		w.Write(pk1)
-		w.Write(pk2)
-		w.Close()
-	}()
-
-	var fh FixedHeader
-	err := p.ReadFixedHeader(&fh)
-	require.NoError(t, err)
-	o1, err := p.Read()
-	require.NoError(t, err)
-	require.Equal(t, []byte{'h', 'e', 'l', 'l', 'o'}, o1.(*PublishPacket).Payload)
-	require.Equal(t, []byte{'h', 'e', 'l', 'l', 'o'}, pk1[9:])
-	require.NoError(t, err)
-
-	err = p.ReadFixedHeader(&fh)
-	require.NoError(t, err)
-	o2, err := p.Read()
-	require.NoError(t, err)
-
-	require.Equal(t, []byte{'y', 'a', 'h', 'a', 'l', 'l', 'o'}, o2.(*PublishPacket).Payload)
-	require.Equal(t, []byte{'h', 'e', 'l', 'l', 'o'}, o1.(*PublishPacket).Payload, "o1 payload was mutated")
-}
-
-func TestReadPacketNil(t *testing.T) {
-
-	conn := new(MockNetConn)
-	var fh FixedHeader
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-
-	// Check for un-specified packet.
-	// Create a ping request packet with a false fixedheader type code.
-	pk := newPacket(Pingreq).(*PingreqPacket)
-
-	pk.FixedHeader.Type = 99
-	p.R = bufio.NewReader(bytes.NewReader([]byte{0, 0}))
-
-	err := p.ReadFixedHeader(&fh)
-	_, err = p.Read()
-
-	require.Error(t, err, "Expected error reading packet")
-
-}
-
-func TestReadPacketReadOverflow(t *testing.T) {
-	conn := new(MockNetConn)
-	var fh FixedHeader
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-
-	// Check for un-specified packet.
-	// Create a ping request packet with a false fixedheader type code.
-	pk := newPacket(Pingreq).(*PingreqPacket)
-
-	pk.FixedHeader.Type = 99
-	p.R = bufio.NewReader(bytes.NewReader([]byte{byte(Connect << 4), 0}))
-
-	err := p.ReadFixedHeader(&fh)
-
-	p.FixedHeader.Remaining = 999999 // overflow buffer
-	_, err = p.Read()
-
-	require.Error(t, err, "Expected error reading packet")
-}
-
-func TestReadPacketReadAllFail(t *testing.T) {
-	conn := new(MockNetConn)
-	var fh FixedHeader
-	p := NewParser(conn, newBufioReader(conn), newBufioWriter(conn))
-
-	// Check for un-specified packet.
-	// Create a ping request packet with a false fixedheader type code.
-	pk := newPacket(Pingreq).(*PingreqPacket)
-
-	pk.FixedHeader.Type = 99
-	p.R = bufio.NewReader(bytes.NewReader([]byte{byte(Connect << 4), 0}))
-
-	err := p.ReadFixedHeader(&fh)
-
-	p.FixedHeader.Remaining = 1 // overflow buffer
-	_, err = p.Read()
-
-	require.Error(t, err, "Expected error reading packet")
-}
-
-// MockNetConn satisfies the net.Conn interface.
-type MockNetConn struct {
-	ID       string
-	Deadline time.Time
-}
-
-// Read reads bytes from the net io.reader.
-func (m *MockNetConn) Read(b []byte) (n int, err error) {
-	return 0, nil
-}
-
-// Read writes bytes to the net io.writer.
-func (m *MockNetConn) Write(b []byte) (n int, err error) {
-	return 0, nil
-}
-
-// Close closes the net.Conn connection.
-func (m *MockNetConn) Close() error {
-	return nil
-}
-
-// LocalAddr returns the local address of the request.
-func (m *MockNetConn) LocalAddr() net.Addr {
-	return new(MockNetAddr)
-}
-
-// RemoteAddr returns the remove address of the request.
-func (m *MockNetConn) RemoteAddr() net.Addr {
-	return new(MockNetAddr)
-}
-
-// SetDeadline sets the request deadline.
-func (m *MockNetConn) SetDeadline(t time.Time) error {
-	m.Deadline = t
-	return nil
-}
-
-// SetReadDeadline sets the read deadline.
-func (m *MockNetConn) SetReadDeadline(t time.Time) error {
-	return nil
-}
-
-// SetWriteDeadline sets the write deadline.
-func (m *MockNetConn) SetWriteDeadline(t time.Time) error {
-	return nil
-}
-
-// MockNetAddr satisfies net.Addr interface.
-type MockNetAddr struct{}
-
-// Network returns the network protocol.
-func (m *MockNetAddr) Network() string {
-	return "tcp"
-}
-
-// String returns the network address.
-func (m *MockNetAddr) String() string {
-	return "127.0.0.1"
-}




diff --git a/packets/pingreq.go b/packets/pingreq.go
deleted file mode 100644
index b25e8ae40e9f366ef7ce55970829807a131ca22e..0000000000000000000000000000000000000000
--- a/packets/pingreq.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// PingreqPacket contains the values of an MQTT PINGREQ packet.
-type PingreqPacket struct {
-	FixedHeader
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *PingreqPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.encode(buf)
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *PingreqPacket) Decode(buf []byte) error {
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *PingreqPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/pingreq_test.go b/packets/pingreq_test.go
deleted file mode 100644
index fe8465b2d57b4987685594c8e597159be482bb29..0000000000000000000000000000000000000000
--- a/packets/pingreq_test.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package packets
-
-import (
-	"testing"
-
-	"bytes"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPingreqEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pingreq)
-	for i, wanted := range expectedPackets[Pingreq] {
-
-		require.Equal(t, uint8(12), Pingreq, "Incorrect Packet Type [i:%d]", i)
-
-		pk := new(PingreqPacket)
-		copier.Copy(pk, wanted.packet.(*PingreqPacket))
-
-		require.Equal(t, Pingreq, pk.Type, "Mismatched Packet Type [i:%d]", i)
-		require.Equal(t, Pingreq, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d]", i)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d]", i)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d]", i)
-	}
-}
-
-func BenchmarkPingreqEncode(b *testing.B) {
-	pk := new(PingreqPacket)
-	copier.Copy(pk, expectedPackets[Pingreq][0].packet.(*PingreqPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestPingreqDecode(t *testing.T) {
-	pk := newPacket(Pingreq).(*PingreqPacket)
-
-	var b = []byte{}
-	err := pk.Decode(b)
-	require.NoError(t, err, "Error unpacking buffer")
-	require.Empty(t, b)
-}
-
-func BenchmarkPingreqDecode(b *testing.B) {
-	pk := newPacket(Pingreq).(*PingreqPacket)
-	pk.FixedHeader.decode(expectedPackets[Pingreq][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Pingreq][0].rawBytes[2:])
-	}
-}
-
-func TestPingreqValidate(t *testing.T) {
-	pk := newPacket(Pingreq).(*PingreqPacket)
-	pk.FixedHeader.decode(expectedPackets[Pingreq][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkPingreqValidate(b *testing.B) {
-	pk := newPacket(Pingreq).(*PingreqPacket)
-	pk.FixedHeader.decode(expectedPackets[Pingreq][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/pingresp.go b/packets/pingresp.go
deleted file mode 100644
index 386505480dfa3d702ea49646b017acc122fa5f1a..0000000000000000000000000000000000000000
--- a/packets/pingresp.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// PingrespPacket contains the values of an MQTT PINGRESP packet.
-type PingrespPacket struct {
-	FixedHeader
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *PingrespPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.encode(buf)
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *PingrespPacket) Decode(buf []byte) error {
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *PingrespPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/pingresp_test.go b/packets/pingresp_test.go
deleted file mode 100644
index f3947d54a5e18dd4a16f966161fb4dc96d1c9bee..0000000000000000000000000000000000000000
--- a/packets/pingresp_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPingrespEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pingresp)
-	for i, wanted := range expectedPackets[Pingresp] {
-
-		require.Equal(t, uint8(13), Pingresp, "Incorrect Packet Type [i:%d]", i)
-
-		pk := new(PingrespPacket)
-		copier.Copy(pk, wanted.packet.(*PingrespPacket))
-
-		require.Equal(t, Pingresp, pk.Type, "Mismatched Packet Type [i:%d]", i)
-		require.Equal(t, Pingresp, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d]", i)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d]", i)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d]", i)
-	}
-}
-
-func BenchmarkPingrespEncode(b *testing.B) {
-	pk := new(PingrespPacket)
-	copier.Copy(pk, expectedPackets[Pingresp][0].packet.(*PingrespPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestPingrespDecode(t *testing.T) {
-	pk := newPacket(Pingresp).(*PingrespPacket)
-
-	var b = []byte{}
-	err := pk.Decode(b)
-	require.NoError(t, err, "Error unpacking buffer")
-	require.Empty(t, b)
-}
-
-func BenchmarkPingrespDecode(b *testing.B) {
-	pk := newPacket(Pingresp).(*PingrespPacket)
-	pk.FixedHeader.decode(expectedPackets[Pingresp][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Pingresp][0].rawBytes[2:])
-	}
-}
-
-func TestPingrespValidate(t *testing.T) {
-	pk := newPacket(Pingresp).(*PingrespPacket)
-	pk.FixedHeader.decode(expectedPackets[Pingresp][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkPingrespValidate(b *testing.B) {
-	pk := newPacket(Pingresp).(*PingrespPacket)
-	pk.FixedHeader.decode(expectedPackets[Pingresp][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/puback.go b/packets/puback.go
deleted file mode 100644
index c9b54e88012d68bf5676d93f9fa8f5a4095e8f7c..0000000000000000000000000000000000000000
--- a/packets/puback.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// PubackPacket contains the values of an MQTT PUBACK packet.
-type PubackPacket struct {
-	FixedHeader
-
-	PacketID uint16
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *PubackPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.Remaining = 2
-	pk.FixedHeader.encode(buf)
-	buf.Write(encodeUint16(pk.PacketID))
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *PubackPacket) Decode(buf []byte) error {
-	var err error
-	pk.PacketID, _, err = decodeUint16(buf, 0)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *PubackPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/puback_test.go b/packets/puback_test.go
deleted file mode 100644
index fd77cba363ceded0870c958ebbe5cd72bb30e567..0000000000000000000000000000000000000000
--- a/packets/puback_test.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPubackEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Puback)
-	for i, wanted := range expectedPackets[Puback] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(4), Puback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(PubackPacket)
-		copier.Copy(pk, wanted.packet.(*PubackPacket))
-
-		require.Equal(t, Puback, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Puback, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		require.Equal(t, byte(Puback<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*PubackPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkPubackEncode(b *testing.B) {
-	pk := new(PubackPacket)
-	copier.Copy(pk, expectedPackets[Puback][0].packet.(*PubackPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestPubackDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Puback)
-	for i, wanted := range expectedPackets[Puback] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(4), Puback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Puback).(*PubackPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*PubackPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkPubackDecode(b *testing.B) {
-	pk := newPacket(Puback).(*PubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Puback][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Puback][0].rawBytes[2:])
-	}
-}
-
-func TestPubackValidate(t *testing.T) {
-	pk := newPacket(Puback).(*PubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Puback][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkPubackValidate(b *testing.B) {
-	pk := newPacket(Puback).(*PubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Puback][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/pubcomp.go b/packets/pubcomp.go
deleted file mode 100644
index 298e8281c39e4ad0fdf3395bcc328eb7ceb6bfa5..0000000000000000000000000000000000000000
--- a/packets/pubcomp.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// PubcompPacket contains the values of an MQTT PUBCOMP packet.
-type PubcompPacket struct {
-	FixedHeader
-
-	PacketID uint16
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *PubcompPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.Remaining = 2
-	pk.FixedHeader.encode(buf)
-	buf.Write(encodeUint16(pk.PacketID))
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *PubcompPacket) Decode(buf []byte) error {
-	var err error
-	pk.PacketID, _, err = decodeUint16(buf, 0)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *PubcompPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/pubcomp_test.go b/packets/pubcomp_test.go
deleted file mode 100644
index 7de495ffa9db315c8d1e2db505cb534fa2662d70..0000000000000000000000000000000000000000
--- a/packets/pubcomp_test.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPubcompEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pubcomp)
-	for i, wanted := range expectedPackets[Pubcomp] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(7), Pubcomp, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(PubcompPacket)
-		copier.Copy(pk, wanted.packet.(*PubcompPacket))
-
-		require.Equal(t, Pubcomp, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Pubcomp, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		require.Equal(t, byte(Pubcomp<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*PubcompPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkPubcompEncode(b *testing.B) {
-	pk := new(PubcompPacket)
-	copier.Copy(pk, expectedPackets[Pubcomp][0].packet.(*PubcompPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestPubcompDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pubcomp)
-	for i, wanted := range expectedPackets[Pubcomp] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(7), Pubcomp, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Pubcomp).(*PubcompPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*PubcompPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkPubcompDecode(b *testing.B) {
-	pk := newPacket(Pubcomp).(*PubcompPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubcomp][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Pubcomp][0].rawBytes[2:])
-	}
-}
-
-func TestPubcompValidate(t *testing.T) {
-	pk := newPacket(Pubcomp).(*PubcompPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubcomp][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkPubcompValidate(b *testing.B) {
-	pk := newPacket(Pubcomp).(*PubcompPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubcomp][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/publish.go b/packets/publish.go
deleted file mode 100644
index 92f6956c44e3403a3add39b4608199ad34a798a9..0000000000000000000000000000000000000000
--- a/packets/publish.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// PublishPacket contains the values of an MQTT PUBLISH packet.
-type PublishPacket struct {
-	FixedHeader
-
-	TopicName string
-	PacketID  uint16
-	Payload   []byte
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *PublishPacket) Encode(buf *bytes.Buffer) error {
-	topicName := encodeString(pk.TopicName)
-	var packetID []byte
-
-	// Add PacketID if QOS is set.
-	// [MQTT-2.3.1-5] A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
-	if pk.Qos > 0 {
-
-		// [MQTT-2.3.1-1] SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-		if pk.PacketID == 0 {
-			return ErrMissingPacketID
-		}
-
-		packetID = encodeUint16(pk.PacketID)
-	}
-
-	pk.FixedHeader.Remaining = len(topicName) + len(packetID) + len(pk.Payload)
-	pk.FixedHeader.encode(buf)
-	buf.Write(topicName)
-	buf.Write(packetID)
-	buf.Write(pk.Payload)
-
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *PublishPacket) Decode(buf []byte) error {
-	var offset int
-	var err error
-
-	pk.TopicName, offset, err = decodeString(buf, 0)
-	if err != nil {
-		return ErrMalformedTopic
-	}
-
-	// If QOS decode Packet ID.
-	if pk.Qos > 0 {
-		pk.PacketID, offset, err = decodeUint16(buf, offset)
-		if err != nil {
-			return ErrMalformedPacketID
-		}
-	}
-
-	pk.Payload = buf[offset:]
-
-	return nil
-}
-
-// Copy creates a new instance of PublishPacket bearing the same payload and
-// destination topic, but with an empty header for inheriting new QoS etc flags.
-func (pk *PublishPacket) Copy() *PublishPacket {
-	return &PublishPacket{
-		FixedHeader: FixedHeader{
-			Type: Publish,
-		},
-		TopicName: pk.TopicName,
-		Payload:   pk.Payload,
-	}
-}
-
-// Validate ensures the packet is compliant.
-func (pk *PublishPacket) Validate() (byte, error) {
-
-	// @SPEC [MQTT-2.3.1-1]
-	// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-	if pk.FixedHeader.Qos > 0 && pk.PacketID == 0 {
-		return Failed, ErrMissingPacketID
-	}
-
-	// @SPEC [MQTT-2.3.1-5]
-	// A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
-	if pk.FixedHeader.Qos == 0 && pk.PacketID > 0 {
-		return Failed, ErrSurplusPacketID
-	}
-
-	return Accepted, nil
-}




diff --git a/packets/publish_test.go b/packets/publish_test.go
deleted file mode 100644
index 5ec278ea760893b505e17dbd238b5fb8404e15f4..0000000000000000000000000000000000000000
--- a/packets/publish_test.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPublishEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Publish)
-	for i, wanted := range expectedPackets[Publish] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(3), Publish, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(PublishPacket)
-		copier.Copy(pk, wanted.packet.(*PublishPacket))
-
-		require.Equal(t, Publish, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Publish, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		encoded := buf.Bytes()
-
-		if wanted.expect != nil {
-			require.Error(t, err, "Expected error writing buffer [i:%d] %s", i, wanted.desc)
-		} else {
-
-			// If actualBytes is set, compare mutated version of byte string instead (to avoid length mismatches, etc).
-			if len(wanted.actualBytes) > 0 {
-				wanted.rawBytes = wanted.actualBytes
-			}
-
-			require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-			if wanted.meta != nil {
-				require.Equal(t, byte(Publish<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-			} else {
-				require.Equal(t, byte(Publish<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-			}
-
-			require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*PublishPacket).FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched QOS [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*PublishPacket).FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched Dup [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*PublishPacket).FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched Retain [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*PublishPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-			require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		}
-	}
-}
-
-func BenchmarkPublishEncode(b *testing.B) {
-	pk := new(PublishPacket)
-	copier.Copy(pk, expectedPackets[Publish][0].packet.(*PublishPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestPublishDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Publish)
-	for i, wanted := range expectedPackets[Publish] {
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(3), Publish, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Publish).(*PublishPacket)
-		pk.FixedHeader.decode(wanted.rawBytes[0])
-
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected fh error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*PublishPacket).FixedHeader.Qos, pk.FixedHeader.Qos, "Mismatched QOS [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*PublishPacket).FixedHeader.Dup, pk.FixedHeader.Dup, "Mismatched Dup [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*PublishPacket).FixedHeader.Retain, pk.FixedHeader.Retain, "Mismatched Retain [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*PublishPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-
-	}
-}
-
-func BenchmarkPublishDecode(b *testing.B) {
-	pk := newPacket(Publish).(*PublishPacket)
-	pk.FixedHeader.decode(expectedPackets[Publish][1].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Publish][1].rawBytes[2:])
-	}
-}
-
-func TestPublishCopy(t *testing.T) {
-	require.Contains(t, expectedPackets, Publish)
-	for i, wanted := range expectedPackets[Publish] {
-		if wanted.group == "copy" {
-
-			pk := newPacket(Publish).(*PublishPacket)
-			err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-			require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-			copied := pk.Copy()
-
-			require.Equal(t, byte(0), copied.FixedHeader.Qos, "Mismatched QOS [i:%d] %s", i, wanted.desc)
-			require.Equal(t, false, copied.FixedHeader.Dup, "Mismatched Dup [i:%d] %s", i, wanted.desc)
-			require.Equal(t, false, copied.FixedHeader.Retain, "Mismatched Retain [i:%d] %s", i, wanted.desc)
-
-			require.Equal(t, pk.Payload, copied.Payload, "Mismatched Payload [i:%d] %s", i, wanted.desc)
-			require.Equal(t, pk.TopicName, copied.TopicName, "Mismatched Topic Name [i:%d] %s", i, wanted.desc)
-
-		}
-	}
-}
-
-func BenchmarkPublishCopy(b *testing.B) {
-	pk := newPacket(Publish).(*PublishPacket)
-	pk.FixedHeader.decode(expectedPackets[Publish][1].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Copy()
-	}
-}
-
-func TestPublishValidate(t *testing.T) {
-	require.Contains(t, expectedPackets, Publish)
-	for i, wanted := range expectedPackets[Publish] {
-		if wanted.group == "validate" || i == 0 {
-			pk := wanted.packet.(*PublishPacket)
-			ok, err := pk.Validate()
-
-			if i == 0 {
-				require.NoError(t, err, "Publish should have validated - error incorrect [i:%d] %s", i, wanted.desc)
-				require.Equal(t, Accepted, ok, "Publish should have validated - code incorrect [i:%d] %s", i, wanted.desc)
-			} else {
-				require.Equal(t, Failed, ok, "Publish packet didn't validate - code incorrect [i:%d] %s", i, wanted.desc)
-				if err != nil {
-					require.Equal(t, wanted.expect, err, "Publish packet didn't validate - error incorrect [i:%d] %s", i, wanted.desc)
-				}
-			}
-		}
-	}
-}
-
-func BenchmarkPublishValidate(b *testing.B) {
-	pk := newPacket(Publish).(*PublishPacket)
-	pk.FixedHeader.decode(expectedPackets[Publish][1].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		_, err := pk.Validate()
-		if err != nil {
-			panic(err)
-		}
-	}
-}




diff --git a/packets/pubrec.go b/packets/pubrec.go
deleted file mode 100644
index c84e73454a605b8a2d88dd3cf273c8a7778a6c96..0000000000000000000000000000000000000000
--- a/packets/pubrec.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// PubrecPacket contains the values of an MQTT PUBREC packet.
-type PubrecPacket struct {
-	FixedHeader
-
-	PacketID uint16
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *PubrecPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.Remaining = 2
-	pk.FixedHeader.encode(buf)
-	buf.Write(encodeUint16(pk.PacketID))
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *PubrecPacket) Decode(buf []byte) error {
-
-	var err error
-
-	pk.PacketID, _, err = decodeUint16(buf, 0)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *PubrecPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/pubrec_test.go b/packets/pubrec_test.go
deleted file mode 100644
index b3d7a8802145756ee393c3ebb1ac0e5d8ced71eb..0000000000000000000000000000000000000000
--- a/packets/pubrec_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPubrecEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pubrec)
-	for i, wanted := range expectedPackets[Pubrec] {
-
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(5), Pubrec, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(PubrecPacket)
-		copier.Copy(pk, wanted.packet.(*PubrecPacket))
-
-		require.Equal(t, Pubrec, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Pubrec, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		require.Equal(t, byte(Pubrec<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*PubrecPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-
-	}
-}
-
-func BenchmarkPubrecEncode(b *testing.B) {
-	pk := new(PubrecPacket)
-	copier.Copy(pk, expectedPackets[Pubrec][0].packet.(*PubrecPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestPubrecDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pubrec)
-	for i, wanted := range expectedPackets[Pubrec] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(5), Pubrec, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Pubrec).(*PubrecPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*PubrecPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-
-}
-
-func BenchmarkPubrecDecode(b *testing.B) {
-	pk := newPacket(Pubrec).(*PubrecPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubrec][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Pubrec][0].rawBytes[2:])
-	}
-}
-
-func TestPubrecValidate(t *testing.T) {
-	pk := newPacket(Pubrec).(*PubrecPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubrec][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkPubrecValidate(b *testing.B) {
-	pk := newPacket(Pubrec).(*PubrecPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubrec][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/pubrel.go b/packets/pubrel.go
deleted file mode 100644
index ccd3e2364e9d424fabc2011b77f8b16ddf606152..0000000000000000000000000000000000000000
--- a/packets/pubrel.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// PubrelPacket contains the values of an MQTT PUBREL packet.
-type PubrelPacket struct {
-	FixedHeader
-
-	PacketID uint16
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *PubrelPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.Remaining = 2
-	pk.FixedHeader.encode(buf)
-	buf.Write(encodeUint16(pk.PacketID))
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *PubrelPacket) Decode(buf []byte) error {
-	var err error
-	pk.PacketID, _, err = decodeUint16(buf, 0)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *PubrelPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/pubrel_test.go b/packets/pubrel_test.go
deleted file mode 100644
index 86ba4a7f31757250f603c8fea3a95b09ab2c0368..0000000000000000000000000000000000000000
--- a/packets/pubrel_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestPubrelEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pubrel)
-	for i, wanted := range expectedPackets[Pubrel] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(6), Pubrel, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(PubrelPacket)
-		copier.Copy(pk, wanted.packet.(*PubrelPacket))
-
-		require.Equal(t, Pubrel, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Pubrel, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		//require.Equal(t, byte(Pubrel<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		if wanted.meta != nil {
-			require.Equal(t, byte(Pubrel<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-		} else {
-			require.Equal(t, byte(Pubrel<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-		}
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*PubrelPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkPubrelEncode(b *testing.B) {
-	pk := new(PubrelPacket)
-	copier.Copy(pk, expectedPackets[Pubrel][0].packet.(*PubrelPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestPubrelDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Pubrel)
-	for i, wanted := range expectedPackets[Pubrel] {
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(6), Pubrel, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Pubrel).(*PubrelPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*PubrelPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkPubrelDecode(b *testing.B) {
-	pk := newPacket(Pubrel).(*PubrelPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubrel][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Pubrel][0].rawBytes[2:])
-	}
-}
-
-func TestPubrelValidate(t *testing.T) {
-	pk := newPacket(Pubrel).(*PubrelPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubrel][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkPubrelValidate(b *testing.B) {
-	pk := newPacket(Pubrel).(*PubrelPacket)
-	pk.FixedHeader.decode(expectedPackets[Pubrel][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/spec_test.go b/packets/spec_test.go
deleted file mode 100644
index a15e6da1b6abe5b0a264385eaf587ec598d83dae..0000000000000000000000000000000000000000
--- a/packets/spec_test.go
+++ /dev/null
@@ -1,546 +0,0 @@
-package packets
-
-/*
-
-
-
-## Packets Funcs
-
-# MQTT 3.1.1
-[MQTT-1.4.0-1] The character data in a UTF-8 encoded string MUST be well-formed UTF-8 as defined by the Unicode specification [Unicode] and restated in RFC 3629 [RFC 3629]. In particular this data MUST NOT include encodings of code points between U+D800 and U+DFFF. If a receiver (Server or Client) receives a Control Packet containing ill-formed UTF-8 it MUST close the Network Connection
-[MQTT-1.4.0-2] A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000. If a receiver (Server or Client) receives a Control Packet containing U+0000 it MUST close the Network Connection.
-[MQTT-1.4.0-3] A UTF-8 encoded sequence 0xEF 0xBB 0xBF is always to be interpreted to mean U+FEFF ("ZERO WIDTH NO-BREAK SPACE") wherever it appears in a string and MUST NOT be skipped over or stripped off by a packet receiver.
-[MQTT-2.2.2-1] Where a flag bit is marked as “Reserved” in Table 2.2 - Flag Bits, it is reserved for future use and MUST be set to the value listed in that table
-[MQTT-2.2.2-2] If invalid flags are received, the receiver MUST close the Network Connection.
-[MQTT-2.3.1-1] SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-[MQTT-2.3.1-5] A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
-
-
-## TODO
-
-## Broker tests
-// [MQTT-2.3.1-4] The same conditions [MQTT-2.3.1-3] apply to a Server when it sends a PUBLISH with QoS >0.
-// [MQTT-2.3.1-2] Each time a Client sends a new packet of one of these types it MUST assign it a currently unused Packet Identifier.
-// [MQTT-2.3.1-3] If a Client re-sends a particular Control Packet, then it MUST use the same Packet Identifier in subsequent re-sends of that packet. The Packet Identifier becomes available for reuse after the Client has processed the corresponding acknowledgement packet. In the case of a QoS 1 PUBLISH this is the corresponding PUBACK; in the case of QO2 it is PUBCOMP. For SUBSCRIBE or UNSUBSCRIBE it is the corresponding SUBACK or UNSUBACK.
-// [MQTT-2.3.1-6] A PUBACK, PUBREC or PUBREL Packet MUST contain the same Packet Identifier as the PUBLISH Packet that was originally sent.
-
-[MQTT-2.3.1-7]
-
-Similarly to [MQTT-2.3.1-6], SUBACK and UNSUBACK MUST contain the Packet Identifier that was used in the corresponding SUBSCRIBE and UNSUBSCRIBE Packet respectively
-
-[MQTT-3.1.0-1]
-
-After a Network Connection is established by a Client to a Server, the first Packet sent from the Client to the Server MUST be a CONNECT Packet.
-
-[MQTT-3.1.0-2]
-
-The Server MUST process a second CONNECT Packet sent from a Client as a protocol violation and disconnect the Client.
-
-[MQTT-3.1.2-1].
-
-If the protocol name is incorrect the Server MAY disconnect the Client, or it MAY continue processing the CONNECT packet in accordance with some other specification. In the latter case, the Server MUST NOT continue to process the CONNECT packet in line with this specification
-
-[MQTT-3.1.2-2]
-
-The Server MUST respond to the CONNECT Packet with a CONNACK return code 0x01 (unacceptable protocol level) and then disconnect the Client if the Protocol Level is not supported by the Server.
-
-[MQTT-3.1.2-3]
-
-The Server MUST validate that the reserved flag in the CONNECT Control Packet is set to zero and disconnect the Client if it is not zero.
-
-[MQTT-3.1.2-4]
-
-If CleanSession is set to 0, the Server MUST resume communications with the Client based on state from the current Session (as identified by the Client identifier). If there is no Session associated with the Client identifier the Server MUST create a new Session. The Client and Server MUST store the Session after the Client and Server are disconnected.
-
-[MQTT-3.1.2-5]
-
-After the disconnection of a Session that had CleanSession set to 0, the Server MUST store further QoS 1 and QoS 2 messages that match any subscriptions that the client had at the time of disconnection as part of the Session state.
-
-[MQTT-3.1.2-6]
-
-If CleanSession is set to 1, the Client and Server MUST discard any previous Session and start a new one. This Session lasts as long as the Network Connection. State data associated with this Session MUST NOT be reused in any subsequent Session
-
-[MQTT-3.1.2.7]
-
-Retained messages do not form part of the Session state in the Server, they MUST NOT be deleted when the Session ends.
-
-[MQTT-3.1.2-8]
-
-If the Will Flag is set to 1 this indicates that, if the Connect request is accepted,a Will Message MUST be stored on the Server and associated with the Network Connection. The Will Message MUST be published when the Network Connection is subsequently closed unless the Will Message has been deleted by the Server on receipt of a DISCONNECT Packet
-
-[MQTT-3.1.2-9]
-
-If the Will Flag is set to 1, the Will QoS and Will Retain fields in the Connect Flags will be used by the Server, and the Will Topic and Will Message fields MUST be present in the payload.
-
-[MQTT-3.1.2-10]
-
-The Will Message MUST be removed from the stored Session state in the Server once it has been published or the Server has received a DISCONNECT packet from the Client.
-
-[MQTT-3.1.2-11]
-
-If the Will Flag is set to 0 the Will QoS and Will Retain fields in the Connect Flags MUST be set to zero and the Will Topic and Will Message fields MUST NOT be present in the payload
-
-[MQTT-3.1.2-12]
-
-If the Will Flag is set to 0, a Will Message MUST NOT be published when this Network Connection ends.
-
-[MQTT-3.1.2-13]
-
-If the Will Flag is set to 0, then the Will QoS MUST be set to 0 (0x00).
-
-[MQTT-3.1.2-14]
-
-If the Will Flag is set to 1, the value of Will QoS can be 0 (0x00), 1 (0x01), or 2 (0x02). It MUST NOT be 3 (0x03).
-
-[MQTT-3.1.2-15]
-
-If the Will Flag is set to 0, then the Will Retain Flag MUST be set to 0.
-
-[MQTT-3.1.2-16]
-
-If the Will Flag is set to 1 and If Will Retain is set to 0, the Server MUST publish the Will Message as a non-retained message.
-
-[MQTT-3.1.2-17]
-
-If the Will Flag is set to 1 and If Will Retain is set to 1, the Server MUST publish the Will Message as a retained message.
-
-
-
-[MQTT-3.1.2-18]
-
-If the User Name Flag is set to 0, a user name MUST NOT be present in the payload.
-
-[MQTT-3.1.2-19]
-
-If the User Name Flag is set to 1, a user name MUST be present in the payload.
-
-[MQTT-3.1.2-20]
-
-If the Password Flag is set to 0, a password MUST NOT be present in the payload.
-
-[MQTT-3.1.2-21]
-
-If the Password Flag is set to 1, a password MUST be present in the payload.
-
-[MQTT-3.1.2-22]
-
-If the User Name Flag is set to 0 then the Password Flag MUST be set to 0.
-
-[MQTT-3.1.2-23]
-
-It is the responsibility of the Client to ensure that the interval between Control Packets being sent does not exceed the Keep Alive value .In the absence of sending any other Control Packets, the Client MUST send a PINGREQ Packet.
-
-[MQTT-3.1.2-24]
-
-If the Keep Alive value is non-zero and the Server does not receive a Control Packet from the Client within one and a half times the Keep Alive time period, it MUST disconnect the Network Connection to the Client as if the network had failed.
-
-[MQTT-3.1.3-1]
-
-These fields, if present, MUST appear in the order Client Identifier, Will Topic, Will Message, User Name, Password.
-
-[MQTT-3.1.3-2]
-
-The ClientId MUST be used by Clients and by Servers to identify state that they hold relating to this MQTT connection between the Client and the Server
-
-[MQTT-3.1.3-3]
-
-The Client Identifier (ClientId) MUST be present and MUST be the first field in the CONNECT packet payload.
-
-[MQTT-3.1.3-4]
-
-The ClientId MUST be a UTF-8 encoded string as defined in Section 1.5.3..
-
-[MQTT-3.1.3-5]
-
-The Server MUST allow ClientIds which are between 1 and 23 UTF-8 encoded bytes in length, and that contain only the characters
-
-"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
-
-[MQTT-3.1.3-6]
-
-A Server MAY allow a Client to supply a ClientId that has a length of zero bytes. However if it does so the Server MUST treat this as a special case and assign a unique ClientId to that Client. It MUST then process the CONNECT packet as if the Client had provided that unique ClientId.
-
-[MQTT-3.1.3-7]
-
-If the Client supplies a zero-byte ClientId, the Client MUST also set CleanSession to 1.
-
-[MQTT-3.1.3-8]
-
-If the Client supplies a zero-byte ClientId with CleanSession set to 0, the Server MUST respond to the CONNECT Packet with a CONNACK return code 0x02 (Identifier rejected) and then close the Network Connection.
-
-[MQTT-3.1.3-9]
-
-If the Server rejects the ClientId it MUST respond to the CONNECT Packet with a CONNACK return code 0x02 (Identifier rejected) and then close the Network Connection.
-
-[MQTT-3.1.3-10]
-
-The WillTopic MUST be a UTF-8 encoded string as defined in Section ‎1.5.3.
-
-[MQTT-3.1.3-11]
-
-User Name MUST be a UTF-8 encoded string as defined in Section ‎1.5.3.
-
-[MQTT-3.1.4-1]
-
-The Server MUST validate that the CONNECT Packet conforms to section 3.1 and close the Network Connection without sending a CONNACK if it does not conform.
-
-[MQTT-3.1.4-2]
-
-If the ClientId represents a Client already connected to the Server then the Server MUST disconnect the existing Client.
-
-[MQTT-3.1.4-3]
-
-If CONNECT validation is successful the Server MUST perform the processing of CleanSession MUST that is described in section 3.1.2.4.
-
-[MQTT-3.1.4-4]
-
-If CONNECT validation is successful the Server MUST acknowledge the CONNECT Packet with a CONNACK Packet containing a zero return code
-
-[MQTT-3.1.4-5]
-
-If the Server rejects the CONNECT, it MUST NOT process any data sent by the Client after the CONNECT Packet.
-
-[MQTT-3.2.0-1]
-
-The first packet sent from the Server to the Client MUST be a CONNACK Packet.
-
-[MQTT-3.2.2-1]
-
-If the Server accepts a connection with CleanSession set to 1, the Server MUST set Session Present to 0 in the CONNACK packet in addition to setting a zero return code in the CONNACK packet.
-
-[MQTT-3.2.2-2]
-
-If the Server accepts a connection with CleanSession set to 0, the value set in Session Present depends on whether the Server already has stored Session state for the supplied client ID. If the Server has stored Session state, it MUST set Session Present to 1 in the CONNACK packet.
-
-[MQTT-3.2.2-3]
-
-If the Server does not have stored Session state, it MUST set Session Present to 0 in the CONNACK packet. This is in addition to setting a zero return code in the CONNACK packet.
-
-[MQTT-3.2.2-4]
-
-If a server sends a CONNACK packet containing a non-zero return code it MUST set Session Present to 0.
-
-[MQTT-3.2.2-5]
-
-If a server sends a CONNACK packet containing a non-zero return code it MUST then close the Network Connection.
-
-[MQTT-3.2.2-6]
-
-If none of the return codes listed in Table 3.1 – Connect Return code valuesare deemed applicable, then the Server MUST close the Network Connection without sending a CONNACK.
-
-[MQTT-3.3.2-1]
-
-The Topic Name MUST be present as the first field in the PUBLISH Packet Variable header. It MUST be a UTF-8 encoded string.
-
-[MQTT-3.3.2-2]
-
-The Topic Name in the PUBLISH Packet MUST NOT contain wildcard characters.
-
-[MQTT-3.3.1-1]
-
-The DUP flag MUST be set to 1 by the Client or Server when it attempts to re-deliver a PUBLISH Packet.
-
-[MQTT-3.3.1-2]
-
-The DUP flag MUST be set to 0 for all QoS 0 messages
-
-[MQTT-3.3.1-3]
-
-The value of the DUP flag from an incoming PUBLISH packet is not propagated when the PUBLISH Packet is sent to subscribers by the Server. The DUP flag in the outgoing PUBLISH packet is set independently to the incoming PUBLISH packet, its value MUST be determined solely by whether the outgoing PUBLISH packet is a retransmission.
-
-[MQTT-3.3.1-4]
-
-A PUBLISH Packet MUST NOT have both QoS bits set to 1. If a Server or Client receives a PUBLISH Packet which has both QoS bits set to 1 it MUST close the Network Connection
-
-[MQTT-3.3.1-5]
-
-If the RETAIN flag is set to 1, in a PUBLISH Packet sent by a Client to a Server, the Server MUST store the Application Message and its QoS, so that it can be delivered to future subscribers whose subscriptions match its topic name.
-
-[MQTT-3.3.1-6]
-
-When a new subscription is established, the last retained message, if any, on each matching topic name MUST be sent to the subscriber.
-
-[MQTT-3.3.1-7]
-
-If the Server receives a QoS 0 message with the RETAIN flag set to 1 it MUST discard any message previously retained for that topic. It SHOULD store the new QoS 0 message as the new retained message for that topic, but MAY choose to discard it at any time - if this happens there will be no retained message for that topic.
-
-[MQTT-3.3.1-8]
-
-When sending a PUBLISH Packet to a Client the Server MUST set the RETAIN flag to 1 if a message is sent as a result of a new subscription being made by a Client.
-
-[MQTT-3.3.1-9]
-
-It MUST set the RETAIN flag to 0 when a PUBLISH Packet is sent to a Client because it matches an established subscription regardless of how the flag was set in the message it received
-
-[MQTT-3.3.1-10]
-
-A PUBLISH Packet with a RETAIN flag set to 1 and a payload containing zero bytes will be processed as normal by the Server and sent to Clients with a subscription matching the topic name. Additionally any existing retained message with the same topic name MUST be removed and any future subscribers for the topic will not receive a retained message.
-
-[MQTT-3.3.1-11]
-
-A zero byte retained message MUST NOT be stored as a retained message on the Server.
-
-[MQTT-3.3.1-12]
-
-If the RETAIN flag is 0, in a PUBLISH Packet sent by a Client to a Server, the Server MUST NOT store the message and MUST NOT remove or replace any existing retained message.
-
-[MQTT-3-8.3-4]
-
-The Server MUST treat a SUBSCRIBE packet as malformed and close the Network Connection if any of Reserved bits in the payload are non-zero, or QoS is not 0,1 or 2.
-
-[MQTT-3.8.4-1]
-
-When the Server receives a SUBSCRIBE Packet from a Client, the Server MUST respond with a SUBACK Packet.
-
-[MQTT-3.8.4-2]
-
-The SUBACK Packet MUST have the same Packet Identifier as the SUBSCRIBE Packet.
-
-[MQTT-3.8.4-3]
-
-A subscribe request which contains a Topic Filter that is identical to an existing Subscription’s Topic Filter completely replaces that existing Subscription with a new Subscription. The Topic Filter in the new Subscription will be identical to that in the previous Subscription, although its maximum QoS value could be different. Any existing retained messages matching the Topic Filter are re-sent, but the flow of publications is not interrupted.
-
-[MQTT-3.8.4-4]
-
-If a Server receives a SUBSCRIBE packet that contains multiple Topic Filters it MUST handle that packet as if it had received a sequence of multiple SUBSCRIBE packets, except that it combines their responses into a single SUBACK response.
-
-[MQTT-3.8.4-5]
-
-The SUBACK Packet sent by the Server to the Client MUST contain a return code for each Topic Filter/QoS pair. This return code MUST either show the maximum QoS that was granted for that Subscription or indicate that the subscription failed.
-
-[MQTT-3.8.4-6]
-
-The Server might grant a lower maximum QoS than the subscriber requested. The QoS of Payload Messages sent in response to a Subscription MUST be the minimum of the QoS of the originally published message and the maximum QoS granted by the Server. The server is permitted to send duplicate copies of a message to a subscriber in the case where the original message was published with QoS 1 and the maximum QoS granted was QoS 0.
-
-[MQTT-3.9.3-1]
-
-The order of return codes in the SUBACK Packet MUST match the order of Topic Filters in the SUBSCRIBE Packet.
-
-[MQTT-3.9.3-2]
-
-SUBACK return codes other than 0x00, 0x01, 0x02 and 0x80 are reserved and MUST NOT be used.
-
-[MQTT-3.10.1-1]
-
-Bits 3,2,1 and 0 of the fixed header of the UNSUBSCRIBE Control Packet are reserved and MUST be set to 0,0,1 and 0 respectively. The Server MUST treat any other value as malformed and close the Network Connection.
-
-[MQTT-3.10.3-1
-
-The Topic Filters in an UNSUBSCRIBE packet MUST be UTF-8 encoded strings as defined in Section 1.5.3, packed contiguously
-
-[MQTT-3.10.3-2]
-
-The Payload of an UNSUBSCRIBE packet MUST contain at least one Topic Filter. An UNSUBSCRIBE packet with no payload is a protocol violation.
-
-[MQTT-3.10.4-1]
-
-The Topic Filter (whether containing a wild-card or not) supplied in an UNSUBSCRIBE packet MUST be compared byte-for-byte with the current set of Topic Filters held by the Server for the Client. If any filter matches exactly then it is deleted, otherwise no additional processing occurs.
-
-[MQTT-3.10.4-2]
-
-The Server sends an UNSUBACK Packet to the Client in response to an UNSUBSCRIBE Packet, The Server MUST stop adding any new messages for delivery to the Client.
-
-[MQTT-3.10.4-3]
-
-The Server sends an UNSUBACK Packet to the Client in response to an UNSUBSCRIBE Packet, The Server MUST complete the delivery of any QoS 1 or QoS 2 messages which it has started to send to the Client.
-
-[MQTT-3.10.4-4]
-
-The Server sends an UNSUBACK Packet to the Client in response to an UNSUBSCRIBE Packet, The Server MUST send an UNSUBACK packet. The UNSUBACK Packet MUST have the same Packet Identifier as the UNSUBSCRIBE Packet.
-
-[MQTT-3.10.4-5]
-
-Even where no Topic Filters are deleted, the Server MUST respond with an UNSUBACK.
-
-[MQTT-3.10.4-6]
-
-If a Server receives an UNSUBSCRIBE packet that contains multiple Topic Filters it MUST handle that packet as if it had received a sequence of multiple UNSUBSCRIBE packets, except that it sends just one UNSUBACK response.
-
-[MQTT-3.12.4-1]
-
-The Server MUST send a PINGRESP Packet in response to a PINGREQ packet.
-
-[MQTT-3.14.1-1]
-
-The Server MUST validate that reserved bits are set to zero in DISCONNECT Control Packet, and disconnect the Client if they are not zero.
-
-[MQTT-3.14.4-1]
-
-After sending a DISCONNECT Packet the Client MUST close the Network Connection.
-
-[MQTT-3.14.4-2]
-
-After sending a DISCONNECT Packet the Client MUST NOT send any more Control Packets on that Network Connection.
-
-[MQTT-3.14.4-3]
-
-On receipt of DISCONNECT the Server MUST discard any Will Message associated with the current connection without publishing it, as described in Section 3.1.2.5
-
-[MQTT-4.1.0.1]
-
-The Client and Server MUST store Session state for the entire duration of the Session.
-
-[MQTT-4.1.0-2]
-
-A Session MUST last at least as long it has an active Network Connection.
-
-[MQTT-4.3.1.1]
-
-
-
-In the QoS 0 delivery protocol, the Sender
-
-·         MUST send a PUBLISH packet with QoS=0, DUP=0.
-
-[MQTT-4.3.2.1]
-
-
-
-In the QoS 1 delivery protocol, the Sender
-
-·         MUST assign an unused Packet Identifier each time it has a new Application Message to publish.
-
-·         MUST send a PUBLISH Packet containing this Packet Identifier with QoS=1, DUP=0.
-
-·         MUST treat the PUBLISH Packet as "unacknowledged" until it has received the corresponding PUBACK packet from the receiver. See Section 4.4 for a discussion of unacknowledged messages.
-
-[MQTT-4.3.2.2]
-
-
-
-In the QoS 1 delivery protocol, the Receiver
-
-MUST respond with a PUBACK Packet containing the Packet Identifier from the incoming PUBLISH Packet, having accepted ownership of the Application Message
-After it has sent a PUBACK Packet the Receiver MUST treat any incoming PUBLISH packet that contains the same Packet Identifier as being a new publication, irrespective of the setting of its DUP flag.
-[MQTT-4.3.3-1]
-
-
-
-In the QoS 2 delivery protocol, the Sender
-
-MUST assign an unused Packet Identifier when it has a new Application Message to publish.
-MUST send a PUBLISH packet containing this Packet Identifier with QoS=2, DUP=0.
-MUST treat the PUBLISH packet as "unacknowledged" until it has received the corresponding PUBREC packet from the receiver. See Section 4.4 for a discussion of unacknowledged messages.
-MUST send a PUBREL packet when it receives a PUBREC packet from the receiver. This PUBREL packet MUST contain the same Packet Identifier as the original PUBLISH packet.
-MUST treat the PUBREL packet as "unacknowledged" until it has received the corresponding PUBCOMP packet from the receiver.
-MUST NOT re-send the PUBLISH once it has sent the corresponding PUBREL packet.
-
-
-[MQTT-4.3.3-2]
-
-
-
-In the QoS 2 delivery protocol, the Receiver
-
-MUST respond with a PUBREC containing the Packet Identifier from the incoming PUBLISH Packet, having accepted ownership of the Application Message.
-Until it has received the corresponding PUBREL packet, the Receiver MUST acknowledge any subsequent PUBLISH packet with the same Packet Identifier by sending a PUBREC. It MUST NOT cause duplicate messages to be delivered to any onward recipients in this case.
-MUST respond to a PUBREL packet by sending a PUBCOMP packet containing the same Packet Identifier as the PUBREL.
-After it has sent a PUBCOMP, the receiver MUST treat any subsequent PUBLISH packet that contains that Packet Identifier as being a new publication.
-[MQTT-4.4.0-1]
-
-When a Client reconnects with CleanSession set to 0, both the Client and Server MUST re-send any unacknowledged PUBLISH Packets (where QoS > 0) and PUBREL Packets using their original Packet Identifiers.
-
-[MQTT-4.5.0-1]
-
-When a Server takes ownership of an incoming Application Message it MUST add it to the Session state of those clients that have matching Subscriptions. Matching rules are defined in Section ‎4.7.
-
-[MQTT-4.5.0-2]
-
-The Client MUST acknowledge any Publish Packet it receives according to the applicable QoS rules regardless of whether it elects to process the Application Message that it contains.
-
-[MQTT-4.6.0-1]
-
-When it re-sends any PUBLISH packets, it MUST re-send them in the order in which the original PUBLISH packets were sent (this applies to QoS 1 and QoS 2 messages).
-
-[MQTT-4.6.0-2]
-
-Client MUST send PUBACK packets in the order in which the corresponding PUBLISH packets were received (QoS 1 messages).
-
-[MQTT-4.6.0-3]
-
-Client MUST send PUBREC packets in the order in which the corresponding PUBLISH packets were received (QoS 2 messages).
-
-[MQTT-4.6.0-4]
-
-Client MUST send PUBREL packets in the order in which the corresponding PUBREC packets were received (QoS 2 messages).
-
-[MQTT-4.6.0-5]
-
-A Server MUST by default treat each Topic as an "Ordered Topic". It MAY provide an administrative or other mechanism to allow one or more Topics to be treated as an "Unordered Topic".
-
-[MQTT-4.6.0-6]
-
-When a Server processes a message that has been published to an Ordered Topic, it MUST follow the rules listed above when delivering messages to each of its subscribers. In addition it MUST send PUBLISH packets to consumers (for the same Topic and QoS) in the order that they were received from any given Client.
-
-[MQTT-4.7.1-1]
-
-The wildcard characters can be used in Topic Filters, but MUST NOT be used within a Topic Name.
-
-[MQTT-4.7.1-2]
-
-The multi-level wildcard character MUST be specified either on its own or following a topic level separator. In either case it MUST be the last character specified in the Topic Filter.
-
-[MQTT-4.7.1-3]
-
-The single-level wildcard can be used at any level in the Topic Filter, including first and last levels. Where it is used it MUST occupy an entire level of the filter.
-
-[MQTT-4.7.2-1]
-
-The Server MUST NOT match Topic Filters starting with a wildcard character (# or +) with Topic Names beginning with a $ character.
-
-[MQTT-4.7.3-1]
-
-All Topic Names and Topic Filters MUST be at least one character long.
-
-[MQTT-4.7.3-2]
-
-Topic Names and Topic Filters MUST NOT include the null character (Unicode U+0000).
-
-[MQTT-4.7.3-3]
-
-Topic Names and Topic Filters are UTF-8 encoded strings, they MUST NOT encode to more than 65535 bytes.
-
-[MQTT-4.7.3-4]
-
-When it performs subscription matching the Server MUST NOT perform any normalization of Topic Names or Topic Filters, or any modification or substitution of unrecognized characters
-
-[MQTT-4.8.0-1]
-
-Unless stated otherwise, if either the Server or Client encounters a protocol violation, it MUST close the Network Connection on which it received that Control Packet which caused the protocol violation.
-
-[MQTT-4.8.0-2]
-
-If the Client or Server encounters a Transient Error while processing an inbound Control Packet it MUST close the Network Connection on which it received that Control Packet.
-
-[MQTT-6.0.0.1]
-
-MQTT Control Packets MUST be sent in WebSocket binary data frames. If any other type of data frame is received the recipient MUST close the Network Connection.
-
-[MQTT-6.0.0.2]
-
-A single WebSocket data frame can contain multiple or partial MQTT Control Packets. The receiver MUST NOT assume that MQTT Control Packets are aligned on WebSocket frame boundaries].
-
-[MQTT-6.0.0.3]
-
-The client MUST include “mqtt” in the list of WebSocket Sub Protocols it offers.
-
-[MQTT-6.0.0.4]
-
-The WebSocket Sub Protocol name selected and returned by the server MUST be “mqtt”.
-
-[MQTT-7.0.0-1]
-
-A Server that both accepts inbound connections and establishes outbound connections to other Servers MUST conform as both an MQTT Client and MQTT Server.
-
-[MQTT-7.0.0-2]
-
-Conformant implementations MUST NOT require the use of any extensions defined outside of this specification in order to interoperate with any other conformant implementation.
-
-[MQTT-7.1.1-1]
-
-A conformant Server MUST support the use of one or more underlying transport protocols that provide an ordered, lossless, stream of bytes from the Client to Server and Server to Client.
-
-[MQTT-7.1.2-1]
-
-A conformant Client MUST support the use of one or more underlying transport protocols that provide an ordered, lossless, stream of bytes from the Client to Server and Server to Client.
-*/




diff --git a/packets/suback.go b/packets/suback.go
deleted file mode 100644
index 5ffc63116928b1d85405a4bfe822f05d5c67590d..0000000000000000000000000000000000000000
--- a/packets/suback.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// SubackPacket contains the values of an MQTT SUBACK packet.
-type SubackPacket struct {
-	FixedHeader
-
-	PacketID    uint16
-	ReturnCodes []byte
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *SubackPacket) Encode(buf *bytes.Buffer) error {
-
-	packetID := encodeUint16(pk.PacketID)
-	pk.FixedHeader.Remaining = len(packetID) + len(pk.ReturnCodes) // Set length.
-	pk.FixedHeader.encode(buf)
-
-	buf.Write(packetID)       // Encode Packet ID.
-	buf.Write(pk.ReturnCodes) // Encode granted QOS flags.
-
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *SubackPacket) Decode(buf []byte) error {
-	var offset int
-	var err error
-
-	// Get Packet ID.
-	pk.PacketID, offset, err = decodeUint16(buf, offset)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-
-	// Get Granted QOS flags.
-	pk.ReturnCodes = buf[offset:]
-
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *SubackPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/suback_test.go b/packets/suback_test.go
deleted file mode 100644
index 11bae9d06a71d4acbf828967abd9b99179a18673..0000000000000000000000000000000000000000
--- a/packets/suback_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestSubackEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Suback)
-	for i, wanted := range expectedPackets[Suback] {
-
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(9), Suback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(SubackPacket)
-		copier.Copy(pk, wanted.packet.(*SubackPacket))
-
-		require.Equal(t, Suback, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Suback, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		if wanted.meta != nil {
-			require.Equal(t, byte(Suback<<4)|wanted.meta.(byte), encoded[0], "Mismatched mod fixed header packets [i:%d] %s", i, wanted.desc)
-		} else {
-			require.Equal(t, byte(Suback<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		}
-
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*SubackPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*SubackPacket).ReturnCodes, pk.ReturnCodes, "Mismatched Return Codes [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkSubackEncode(b *testing.B) {
-	pk := new(SubackPacket)
-	copier.Copy(pk, expectedPackets[Suback][0].packet.(*SubackPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestSubackDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Suback)
-	for i, wanted := range expectedPackets[Suback] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(9), Suback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Suback).(*SubackPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*SubackPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*SubackPacket).ReturnCodes, pk.ReturnCodes, "Mismatched Return Codes [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkSubackDecode(b *testing.B) {
-	pk := newPacket(Suback).(*SubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Suback][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Suback][0].rawBytes[2:])
-	}
-}
-
-func TestSubackValidate(t *testing.T) {
-	pk := newPacket(Suback).(*SubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Suback][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkSubackValidate(b *testing.B) {
-	pk := newPacket(Suback).(*SubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Suback][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/subscribe.go b/packets/subscribe.go
deleted file mode 100644
index d2ce96cb16a73a2a6ceb405309763c670cc9477b..0000000000000000000000000000000000000000
--- a/packets/subscribe.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// SubscribePacket contains the values of an MQTT SUBSCRIBE packet.
-type SubscribePacket struct {
-	FixedHeader
-
-	PacketID uint16
-	Topics   []string
-	Qoss     []byte
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *SubscribePacket) Encode(buf *bytes.Buffer) error {
-
-	// Add the Packet ID.
-	// [MQTT-2.3.1-1] SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-	if pk.PacketID == 0 {
-		return ErrMissingPacketID
-	}
-
-	packetID := encodeUint16(pk.PacketID)
-
-	// Count topics lengths and associated QOS flags.
-	var topicsLen int
-	for _, topic := range pk.Topics {
-		topicsLen += len(encodeString(topic)) + 1
-	}
-
-	pk.FixedHeader.Remaining = len(packetID) + topicsLen
-	pk.FixedHeader.encode(buf)
-	buf.Write(packetID)
-
-	// Add all provided topic names and associated QOS flags.
-	for i, topic := range pk.Topics {
-		buf.Write(encodeString(topic))
-		buf.WriteByte(pk.Qoss[i])
-	}
-
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *SubscribePacket) Decode(buf []byte) error {
-	var offset int
-	var err error
-
-	// Get the Packet ID.
-	pk.PacketID, offset, err = decodeUint16(buf, 0)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-
-	// Keep decoding until there's no space left.
-	for offset < len(buf) {
-
-		// Decode Topic Name.
-		var topic string
-		topic, offset, err = decodeString(buf, offset)
-		if err != nil {
-			return ErrMalformedTopic
-		}
-		pk.Topics = append(pk.Topics, topic)
-
-		// Decode QOS flag.
-		var qos byte
-		qos, offset, err = decodeByte(buf, offset)
-		if err != nil {
-			return ErrMalformedQoS
-		}
-
-		if !validateQoS(qos) {
-			return ErrMalformedQoS
-		}
-
-		pk.Qoss = append(pk.Qoss, qos)
-	}
-
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *SubscribePacket) Validate() (byte, error) {
-
-	// @SPEC [MQTT-2.3.1-1].
-	// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-	if pk.FixedHeader.Qos > 0 && pk.PacketID == 0 {
-		return Failed, ErrMissingPacketID
-	}
-
-	return Accepted, nil
-}




diff --git a/packets/subscribe_test.go b/packets/subscribe_test.go
deleted file mode 100644
index d981a3f963347d6a8c3b9a08d3afd7b84c7040f7..0000000000000000000000000000000000000000
--- a/packets/subscribe_test.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestSubscribeEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Subscribe)
-	for i, wanted := range expectedPackets[Subscribe] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(8), Subscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(SubscribePacket)
-		copier.Copy(pk, wanted.packet.(*SubscribePacket))
-
-		require.Equal(t, Subscribe, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Subscribe, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		encoded := buf.Bytes()
-
-		if wanted.expect != nil {
-			require.Error(t, err, "Expected error writing buffer [i:%d] %s", i, wanted.desc)
-		} else {
-			require.NoError(t, err, "Error writing buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-			if wanted.meta != nil {
-				require.Equal(t, byte(Subscribe<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-			} else {
-				require.Equal(t, byte(Subscribe<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-			}
-
-			require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*SubscribePacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*SubscribePacket).Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*SubscribePacket).Qoss, pk.Qoss, "Mismatched Qoss slice [i:%d] %s", i, wanted.desc)
-		}
-	}
-}
-
-func BenchmarkSubscribeEncode(b *testing.B) {
-	pk := new(SubscribePacket)
-	copier.Copy(pk, expectedPackets[Subscribe][0].packet.(*SubscribePacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestSubscribeDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Subscribe)
-	for i, wanted := range expectedPackets[Subscribe] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(8), Subscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Subscribe).(*SubscribePacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*SubscribePacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*SubscribePacket).Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*SubscribePacket).Qoss, pk.Qoss, "Mismatched Qoss slice [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkSubscribeDecode(b *testing.B) {
-	pk := newPacket(Subscribe).(*SubscribePacket)
-	pk.FixedHeader.decode(expectedPackets[Subscribe][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Subscribe][0].rawBytes[2:])
-	}
-}
-
-func TestSubscribeValidate(t *testing.T) {
-	/*pk := newPacket(Subscribe).(*SubscribePacket)
-	pk.FixedHeader.decode(expectedPackets[Subscribe][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-	*/
-	require.Contains(t, expectedPackets, Subscribe)
-	for i, wanted := range expectedPackets[Subscribe] {
-		if wanted.group == "validate" || i == 0 {
-			pk := wanted.packet.(*SubscribePacket)
-			ok, err := pk.Validate()
-
-			if i == 0 {
-				require.NoError(t, err, "Subscribe should have validated - error incorrect [i:%d] %s", i, wanted.desc)
-				require.Equal(t, Accepted, ok, "Subscribe should have validated - code incorrect [i:%d] %s", i, wanted.desc)
-			} else {
-				require.Equal(t, Failed, ok, "Subscribe packet didn't validate - code incorrect [i:%d] %s", i, wanted.desc)
-				if err != nil {
-					require.Equal(t, wanted.expect, err, "Subscribe packet didn't validate - error incorrect [i:%d] %s", i, wanted.desc)
-				}
-			}
-		}
-	}
-}
-
-func BenchmarkSubscribeValidate(b *testing.B) {
-	pk := newPacket(Subscribe).(*SubscribePacket)
-	pk.FixedHeader.decode(expectedPackets[Subscribe][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/tables_test.go b/packets/tables_test.go
deleted file mode 100644
index ec54d356bb1a29fe21adb18acc5bc1a2cfab893e..0000000000000000000000000000000000000000
--- a/packets/tables_test.go
+++ /dev/null
@@ -1,1417 +0,0 @@
-package packets
-
-type packetTestData struct {
-	group       string      // group specifies a group that should run the test, blank for all
-	rawBytes    []byte      // the bytes that make the packet
-	actualBytes []byte      // the actual byte array that is created in the event of a byte mutation (eg. MQTT-2.3.1-1 qos/packet id)
-	packet      Packet      // the packet that is expected
-	desc        string      // a description of the test
-	failFirst   interface{} // expected fail result to be run immediately after the method is called
-	expect      interface{} // 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
-	meta        interface{} // meta conains a metadata value used in testing on a case-by-case basis.
-	code        byte        // code is an expected validation return code
-}
-
-func encodeTestOK(wanted packetTestData) bool {
-	if wanted.rawBytes == nil {
-		return false
-	}
-	if wanted.group != "" && wanted.group != "encode" {
-		return false
-	}
-	return true
-}
-
-func decodeTestOK(wanted packetTestData) bool {
-	if wanted.group != "" && wanted.group != "decode" {
-		return false
-	}
-	return true
-}
-
-var expectedPackets = map[byte][]packetTestData{
-	Connect: {
-		{
-			desc:    "MQTT 3.1",
-			primary: true,
-			rawBytes: []byte{
-				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: &ConnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connect,
-					Remaining: 17,
-				},
-				ProtocolName:     "MQIsdp",
-				ProtocolVersion:  3,
-				CleanSession:     false,
-				Keepalive:        30,
-				ClientIdentifier: "zen",
-			},
-		},
-
-		{
-			desc:    "MQTT 3.1.1",
-			primary: true,
-			rawBytes: []byte{
-				byte(Connect << 4), 16, // Fixed header
-				0, 4, // Protocol Name - MSB+LSB
-				'M', 'Q', 'T', 'T', // Protocol Name
-				4,     // Protocol Version
-				0,     // Packet Flags
-				0, 60, // Keepalive
-				0, 4, // Client ID - MSB+LSB
-				'z', 'e', 'n', '3', // Client ID "zen"
-			},
-			packet: &ConnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connect,
-					Remaining: 16,
-				},
-				ProtocolName:     "MQTT",
-				ProtocolVersion:  4,
-				CleanSession:     false,
-				Keepalive:        60,
-				ClientIdentifier: "zen3",
-			},
-		},
-		{
-			desc: "MQTT 3.1.1, Clean Session",
-			rawBytes: []byte{
-				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: &ConnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connect,
-					Remaining: 15,
-				},
-				ProtocolName:     "MQTT",
-				ProtocolVersion:  4,
-				CleanSession:     true,
-				Keepalive:        45,
-				ClientIdentifier: "zen",
-			},
-		},
-		{
-			desc: "MQTT 3.1.1, Clean Session, LWT",
-			rawBytes: []byte{
-				byte(Connect << 4), 31, // Fixed header
-				0, 4, // Protocol Name - MSB+LSB
-				'M', 'Q', 'T', 'T', // Protocol Name
-				4,     // Protocol Version
-				14,    // Packet Flags
-				0, 27, // 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',
-			},
-			packet: &ConnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connect,
-					Remaining: 31,
-				},
-				ProtocolName:     "MQTT",
-				ProtocolVersion:  4,
-				CleanSession:     true,
-				Keepalive:        27,
-				ClientIdentifier: "zen",
-				WillFlag:         true,
-				WillTopic:        "lwt",
-				WillMessage:      []byte("not again"),
-				WillQos:          1,
-			},
-		},
-		{
-			desc: "MQTT 3.1.1, Username, Password",
-			rawBytes: []byte{
-				byte(Connect << 4), 28, // Fixed header
-				0, 4, // Protocol Name - MSB+LSB
-				'M', 'Q', 'T', 'T', // Protocol Name
-				4,     // Protocol Version
-				194,   // 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: &ConnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connect,
-					Remaining: 28,
-				},
-				ProtocolName:     "MQTT",
-				ProtocolVersion:  4,
-				CleanSession:     true,
-				Keepalive:        20,
-				ClientIdentifier: "zen",
-				UsernameFlag:     true,
-				PasswordFlag:     true,
-				Username:         "mochi",
-				Password:         ",./;",
-			},
-		},
-		{
-			desc:    "MQTT 3.1.1, Username, Password, LWT",
-			primary: true,
-			rawBytes: []byte{
-				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: &ConnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connect,
-					Remaining: 44,
-				},
-				ProtocolName:     "MQTT",
-				ProtocolVersion:  4,
-				CleanSession:     true,
-				Keepalive:        120,
-				ClientIdentifier: "zen",
-				UsernameFlag:     true,
-				PasswordFlag:     true,
-				Username:         "mochi",
-				Password:         ",./;",
-				WillFlag:         true,
-				WillTopic:        "lwt",
-				WillMessage:      []byte("not again"),
-				WillQos:          1,
-			},
-		},
-
-		// Fail States
-		{
-			desc:      "Malformed Connect - protocol name",
-			group:     "decode",
-			failFirst: ErrMalformedProtocolName,
-			rawBytes: []byte{
-				byte(Connect << 4), 0, // Fixed header
-				0, 7, // Protocol Name - MSB+LSB
-				'M', 'Q', 'I', 's', 'd', // Protocol Name
-			},
-		},
-
-		{
-			desc:      "Malformed Connect - protocol version",
-			group:     "decode",
-			failFirst: ErrMalformedProtocolVersion,
-			rawBytes: []byte{
-				byte(Connect << 4), 0, // Fixed header
-				0, 4, // Protocol Name - MSB+LSB
-				'M', 'Q', 'T', 'T', // Protocol Name
-			},
-		},
-
-		{
-			desc:      "Malformed Connect - flags",
-			group:     "decode",
-			failFirst: ErrMalformedFlags,
-			rawBytes: []byte{
-				byte(Connect << 4), 0, // Fixed header
-				0, 4, // Protocol Name - MSB+LSB
-				'M', 'Q', 'T', 'T', // Protocol Name
-				4, // Protocol Version
-
-			},
-		},
-		{
-			desc:      "Malformed Connect - keepalive",
-			group:     "decode",
-			failFirst: ErrMalformedKeepalive,
-			rawBytes: []byte{
-				byte(Connect << 4), 0, // Fixed header
-				0, 4, // Protocol Name - MSB+LSB
-				'M', 'Q', 'T', 'T', // Protocol Name
-				4, // Protocol Version
-				0, // Flags
-			},
-		},
-		{
-			desc:      "Malformed Connect - client id",
-			group:     "decode",
-			failFirst: ErrMalformedClientID,
-			rawBytes: []byte{
-				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"
-			},
-		},
-		{
-			desc:      "Malformed Connect - will topic",
-			group:     "decode",
-			failFirst: ErrMalformedWillTopic,
-			rawBytes: []byte{
-				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',
-			},
-		},
-		{
-			desc:      "Malformed Connect - will flag",
-			group:     "decode",
-			failFirst: ErrMalformedWillMessage,
-			rawBytes: []byte{
-				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',
-			},
-		},
-		{
-			desc:      "Malformed Connect - username",
-			group:     "decode",
-			failFirst: ErrMalformedUsername,
-			rawBytes: []byte{
-				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',
-			},
-		},
-		{
-			desc:      "Malformed Connect - password",
-			group:     "decode",
-			failFirst: ErrMalformedPassword,
-			rawBytes: []byte{
-				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
-				',', '.',
-			},
-		},
-
-		// Validation Tests
-		{
-			desc:  "Invalid Protocol Name",
-			group: "validate",
-			code:  CodeConnectProtocolViolation,
-			packet: &ConnectPacket{
-				ProtocolName: "stuff",
-			},
-		},
-		{
-			desc:  "Invalid Protocol Version",
-			group: "validate",
-			code:  CodeConnectBadProtocolVersion,
-			packet: &ConnectPacket{
-				ProtocolName:    "MQTT",
-				ProtocolVersion: 2,
-			},
-		},
-		{
-			desc:  "Invalid Protocol Version",
-			group: "validate",
-			code:  CodeConnectBadProtocolVersion,
-			packet: &ConnectPacket{
-				ProtocolName:    "MQIsdp",
-				ProtocolVersion: 2,
-			},
-		},
-		{
-			desc:  "Reserved bit not 0",
-			group: "validate",
-			code:  CodeConnectProtocolViolation,
-			packet: &ConnectPacket{
-				ProtocolName:    "MQTT",
-				ProtocolVersion: 4,
-				ReservedBit:     1,
-			},
-		},
-		{
-			desc:  "Client ID too long",
-			group: "validate",
-			code:  CodeConnectProtocolViolation,
-			packet: &ConnectPacket{
-				ProtocolName:    "MQTT",
-				ProtocolVersion: 4,
-				ClientIdentifier: func() string {
-					return string(make([]byte, 65536))
-				}(),
-			},
-		},
-		{
-			desc:  "Has Password Flag but no Username flag",
-			group: "validate",
-			code:  CodeConnectProtocolViolation,
-			packet: &ConnectPacket{
-				ProtocolName:    "MQTT",
-				ProtocolVersion: 4,
-				PasswordFlag:    true,
-			},
-		},
-		{
-			desc:  "Username too long",
-			group: "validate",
-			code:  CodeConnectProtocolViolation,
-			packet: &ConnectPacket{
-				ProtocolName:    "MQTT",
-				ProtocolVersion: 4,
-				UsernameFlag:    true,
-				Username: func() string {
-					return string(make([]byte, 65536))
-				}(),
-			},
-		},
-		{
-			desc:  "Password too long",
-			group: "validate",
-			code:  CodeConnectProtocolViolation,
-			packet: &ConnectPacket{
-				ProtocolName:    "MQTT",
-				ProtocolVersion: 4,
-				UsernameFlag:    true,
-				Username:        "",
-				PasswordFlag:    true,
-				Password: func() string {
-					return string(make([]byte, 65536))
-				}(),
-			},
-		},
-		{
-			desc:  "Clean session false and client id not set",
-			group: "validate",
-			code:  CodeConnectBadClientID,
-			packet: &ConnectPacket{
-				ProtocolName:     "MQTT",
-				ProtocolVersion:  4,
-				CleanSession:     false,
-				ClientIdentifier: "",
-			},
-		},
-
-		// Spec Tests
-		{
-			// @SPEC [MQTT-1.4.0-1]
-			// The character data in a UTF-8 encoded string MUST be well-formed UTF-8
-			// as defined by the Unicode specification [Unicode] and restated in RFC 3629 [RFC 3629].
-			// In particular this data MUST NOT include encodings of code points between U+D800 and U+DFFF.
-			desc:      "Invalid UTF8 string (a) - Code point U+D800.",
-			group:     "decode",
-			failFirst: ErrMalformedClientID,
-			rawBytes: []byte{
-				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
-			},
-		},
-		{
-			desc:      "Invalid UTF8 string (b) - Code point U+DFFF.",
-			group:     "decode",
-			failFirst: ErrMalformedClientID,
-			rawBytes: []byte{
-				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
-			},
-		},
-
-		// @SPEC [MQTT-1.4.0-2]
-		// A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000.
-		{
-			desc:      "Invalid UTF8 string (c) - Code point U+0000.",
-			group:     "decode",
-			failFirst: ErrMalformedClientID,
-			rawBytes: []byte{
-				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
-			},
-		},
-
-		// @ SPEC [MQTT-1.4.0-3]
-		// A UTF-8 encoded sequence 0xEF 0xBB 0xBF is always to be interpreted to mean U+FEFF ("ZERO WIDTH NO-BREAK SPACE")
-		// wherever it appears in a string and MUST NOT be skipped over or stripped off by a packet receiver.
-		{
-			desc: "UTF8 string must not skip or strip code point U+FEFF.",
-			//group: "decode",
-			//failFirst: ErrMalformedClientID,
-			rawBytes: []byte{
-				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: &ConnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connect,
-					Remaining: 16,
-				},
-				ProtocolName:     "MQTT",
-				ProtocolVersion:  4,
-				Keepalive:        20,
-				ClientIdentifier: "eb" + string([]byte{0xEF, 0xBB, 0xBF}) + "d",
-			},
-		},
-	},
-	Connack: {
-		{
-			desc:    "Accepted, No Session",
-			primary: true,
-			rawBytes: []byte{
-				byte(Connack << 4), 2, // fixed header
-				0, // No existing session
-				Accepted,
-			},
-			packet: &ConnackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connack,
-					Remaining: 2,
-				},
-				SessionPresent: false,
-				ReturnCode:     Accepted,
-			},
-		},
-		{
-			desc:    "Accepted, Session Exists",
-			primary: true,
-			rawBytes: []byte{
-				byte(Connack << 4), 2, // fixed header
-				1, // Session present
-				Accepted,
-			},
-			packet: &ConnackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connack,
-					Remaining: 2,
-				},
-				SessionPresent: true,
-				ReturnCode:     Accepted,
-			},
-		},
-		{
-			desc: "Bad Protocol Version",
-			rawBytes: []byte{
-				byte(Connack << 4), 2, // fixed header
-				1, // Session present
-				CodeConnectBadProtocolVersion,
-			},
-			packet: &ConnackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connack,
-					Remaining: 2,
-				},
-				SessionPresent: true,
-				ReturnCode:     CodeConnectBadProtocolVersion,
-			},
-		},
-		{
-			desc: "Bad Client ID",
-			rawBytes: []byte{
-				byte(Connack << 4), 2, // fixed header
-				1, // Session present
-				CodeConnectBadClientID,
-			},
-			packet: &ConnackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connack,
-					Remaining: 2,
-				},
-				SessionPresent: true,
-				ReturnCode:     CodeConnectBadClientID,
-			},
-		},
-		{
-			desc: "Server Unavailable",
-			rawBytes: []byte{
-				byte(Connack << 4), 2, // fixed header
-				1, // Session present
-				CodeConnectServerUnavailable,
-			},
-			packet: &ConnackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connack,
-					Remaining: 2,
-				},
-				SessionPresent: true,
-				ReturnCode:     CodeConnectServerUnavailable,
-			},
-		},
-		{
-			desc: "Bad Username or Password",
-			rawBytes: []byte{
-				byte(Connack << 4), 2, // fixed header
-				1, // Session present
-				CodeConnectBadAuthValues,
-			},
-			packet: &ConnackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connack,
-					Remaining: 2,
-				},
-				SessionPresent: true,
-				ReturnCode:     CodeConnectBadAuthValues,
-			},
-		},
-		{
-			desc: "Not Authorised",
-			rawBytes: []byte{
-				byte(Connack << 4), 2, // fixed header
-				1, // Session present
-				CodeConnectNotAuthorised,
-			},
-			packet: &ConnackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Connack,
-					Remaining: 2,
-				},
-				SessionPresent: true,
-				ReturnCode:     CodeConnectNotAuthorised,
-			},
-		},
-
-		// Fail States
-		{
-			desc:      "Malformed Connack - session present",
-			group:     "decode",
-			failFirst: ErrMalformedSessionPresent,
-			rawBytes: []byte{
-				byte(Connect << 4), 2, // Fixed header
-			},
-		},
-		{
-			desc:  "Malformed Connack - bad return code",
-			group: "decode",
-			//primary:   true,
-			failFirst: ErrMalformedReturnCode,
-			rawBytes: []byte{
-				byte(Connect << 4), 2, // Fixed header
-				0,
-			},
-		},
-	},
-
-	Publish: {
-		{
-			desc:    "Publish - No payload",
-			primary: true,
-			rawBytes: []byte{
-				byte(Publish << 4), 7, // Fixed header
-				0, 5, // Topic Name - LSB+MSB
-				'a', '/', 'b', '/', 'c', // Topic Name
-			},
-			packet: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:      Publish,
-					Remaining: 7,
-				},
-				TopicName: "a/b/c",
-				Payload:   []byte{},
-			},
-		},
-		{
-			desc:    "Publish - basic",
-			primary: true,
-			rawBytes: []byte{
-				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: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:      Publish,
-					Remaining: 18,
-				},
-				TopicName: "a/b/c",
-				Payload:   []byte("hello mochi"),
-			},
-		},
-		{
-			desc:    "Publish - QoS:1, Packet ID",
-			primary: true,
-			rawBytes: []byte{
-				byte(Publish<<4) | 2, 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: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:      Publish,
-					Qos:       1,
-					Remaining: 14,
-				},
-				TopicName: "a/b/c",
-				Payload:   []byte("hello"),
-				PacketID:  7,
-			},
-			meta: byte(2),
-		},
-		{
-			desc:    "Publish - QoS:1, Packet ID, No payload",
-			primary: true,
-			rawBytes: []byte{
-				byte(Publish<<4) | 2, 9, // Fixed header
-				0, 5, // Topic Name - LSB+MSB
-				'y', '/', 'u', '/', 'i', // Topic Name
-				0, 8, // Packet ID - LSB+MSB
-			},
-			packet: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:      Publish,
-					Qos:       1,
-					Remaining: 9,
-				},
-				TopicName: "y/u/i",
-				PacketID:  8,
-				Payload:   []byte{},
-			},
-			meta: byte(2),
-		},
-		{
-			desc: "Publish - Retain",
-			rawBytes: []byte{
-				byte(Publish<<4) | 1, 10, // Fixed header
-				0, 3, // Topic Name - LSB+MSB
-				'a', '/', 'b', // Topic Name
-				'h', 'e', 'l', 'l', 'o', // Payload
-			},
-			packet: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:   Publish,
-					Retain: true,
-				},
-				TopicName: "a/b",
-				Payload:   []byte("hello"),
-			},
-			meta: byte(1),
-		},
-		{
-			desc: "Publish - Dup",
-			rawBytes: []byte{
-				byte(Publish<<4) | 8, 10, // Fixed header
-				0, 3, // Topic Name - LSB+MSB
-				'a', '/', 'b', // Topic Name
-				'h', 'e', 'l', 'l', 'o', // Payload
-			},
-			packet: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type: Publish,
-					Dup:  true,
-				},
-				TopicName: "a/b",
-				Payload:   []byte("hello"),
-			},
-			meta: byte(8),
-		},
-
-		// Fail States
-		{
-			desc:      "Malformed Publish - topic name",
-			group:     "decode",
-			failFirst: ErrMalformedTopic,
-			rawBytes: []byte{
-				byte(Publish << 4), 7, // Fixed header
-				0, 5, // Topic Name - LSB+MSB
-				'a', '/',
-				0, 11, // Packet ID - LSB+MSB
-			},
-		},
-
-		{
-			desc:      "Malformed Publish - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Publish<<4) | 2, 7, // Fixed header
-				0, 5, // Topic Name - LSB+MSB
-				'x', '/', 'y', '/', 'z', // Topic Name
-				0, // Packet ID - LSB+MSB
-			},
-		},
-
-		// Copy tests
-		{
-			desc:  "Publish - basic copyable",
-			group: "copy",
-			rawBytes: []byte{
-				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: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:   Publish,
-					Dup:    true,
-					Retain: true,
-					Qos:    1,
-				},
-				TopicName: "z/e/n",
-				Payload:   []byte("mochi mochi"),
-			},
-		},
-
-		// Spec tests
-		{
-			// @SPEC [MQTT-2.3.1-5]
-			// A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
-			desc:  "[MQTT-2.3.1-5] 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{
-				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{
-				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: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:      Publish,
-					Remaining: 12,
-				},
-				TopicName: "a/b/c",
-				Payload:   []byte("hello"),
-			},
-		},
-		{
-			// @SPEC [MQTT-2.3.1-1].
-			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-			desc:   "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
-			group:  "encode",
-			expect: ErrMissingPacketID,
-			code:   Failed,
-			rawBytes: []byte{
-				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: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type: Publish,
-					Qos:  1,
-				},
-				TopicName: "a/b/c",
-				Payload:   []byte("hello"),
-				PacketID:  0,
-			},
-			meta: byte(2),
-		},
-		/*
-			{
-				// @SPEC [MQTT-2.3.1-1].
-				// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-				desc:  "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
-				group: "validate",
-				//primary: true,
-				expect: ErrMissingPacketID,
-				code:   Failed,
-				rawBytes: []byte{
-					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: &PublishPacket{
-					FixedHeader: FixedHeader{
-						Type: Publish,
-						Qos:  1,
-					},
-					TopicName: "a/b/c",
-					Payload:   []byte("hello"),
-					PacketID:  0,
-				},
-				meta: byte(2),
-			},
-
-		*/
-
-		// Validation Tests
-		{
-			// @SPEC [MQTT-2.3.1-5]
-			desc:   "[MQTT-2.3.1-5] Packet ID must be 0 if QoS is 0 (b)",
-			group:  "validate",
-			expect: ErrSurplusPacketID,
-			code:   Failed,
-			packet: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type:      Publish,
-					Remaining: 12,
-					Qos:       0,
-				},
-				TopicName: "a/b/c",
-				Payload:   []byte("hello"),
-				PacketID:  3,
-			},
-		},
-		{
-			// @SPEC [MQTT-2.3.1-1].
-			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-			desc:   "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
-			group:  "validate",
-			expect: ErrMissingPacketID,
-			code:   Failed,
-			packet: &PublishPacket{
-				FixedHeader: FixedHeader{
-					Type: Publish,
-					Qos:  1,
-				},
-				PacketID: 0,
-			},
-		},
-	},
-
-	Puback: {
-		{
-			desc:    "Puback",
-			primary: true,
-			rawBytes: []byte{
-				byte(Puback << 4), 2, // Fixed header
-				0, 11, // Packet ID - LSB+MSB
-			},
-			packet: &PubackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Puback,
-					Remaining: 2,
-				},
-				PacketID: 11,
-			},
-		},
-
-		// Fail states
-		{
-			desc:      "Malformed Puback - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Puback << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-	},
-	Pubrec: {
-		{
-			desc:    "Pubrec",
-			primary: true,
-			rawBytes: []byte{
-				byte(Pubrec << 4), 2, // Fixed header
-				0, 12, // Packet ID - LSB+MSB
-			},
-			packet: &PubrecPacket{
-				FixedHeader: FixedHeader{
-					Type:      Pubrec,
-					Remaining: 2,
-				},
-				PacketID: 12,
-			},
-		},
-
-		// Fail states
-		{
-			desc:      "Malformed Pubrec - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Pubrec << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-	},
-	Pubrel: {
-		{
-			desc:    "Pubrel",
-			primary: true,
-			rawBytes: []byte{
-				byte(Pubrel<<4) | 2, 2, // Fixed header
-				0, 12, // Packet ID - LSB+MSB
-			},
-			packet: &PubrelPacket{
-				FixedHeader: FixedHeader{
-					Type:      Pubrel,
-					Remaining: 2,
-					Qos:       1,
-				},
-				PacketID: 12,
-			},
-			meta: byte(2),
-		},
-
-		// Fail states
-		{
-			desc:      "Malformed Pubrel - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Pubrel << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-	},
-	Pubcomp: {
-		{
-			desc:    "Pubcomp",
-			primary: true,
-			rawBytes: []byte{
-				byte(Pubcomp << 4), 2, // Fixed header
-				0, 14, // Packet ID - LSB+MSB
-			},
-			packet: &PubcompPacket{
-				FixedHeader: FixedHeader{
-					Type:      Pubcomp,
-					Remaining: 2,
-				},
-				PacketID: 14,
-			},
-		},
-
-		// Fail states
-		{
-			desc:      "Malformed Pubcomp - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Pubcomp << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-	},
-	Subscribe: {
-		{
-			desc:    "Subscribe",
-			primary: true,
-			rawBytes: []byte{
-				byte(Subscribe << 4), 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: &SubscribePacket{
-				FixedHeader: FixedHeader{
-					Type:      Subscribe,
-					Remaining: 30,
-				},
-				PacketID: 15,
-				Topics: []string{
-					"a/b",
-					"d/e/f/g/h/i",
-					"x/y/z",
-				},
-				Qoss: []byte{0, 1, 2},
-			},
-		},
-
-		// Fail states
-		{
-			desc:      "Malformed Subscribe - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Subscribe << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-		{
-			desc:      "Malformed Subscribe - topic",
-			group:     "decode",
-			failFirst: ErrMalformedTopic,
-			rawBytes: []byte{
-				byte(Subscribe << 4), 2, // Fixed header
-				0, 21, // Packet ID - LSB+MSB
-
-				0, 3, // Topic Name - LSB+MSB
-				'a', '/',
-			},
-		},
-		{
-			desc:      "Malformed Subscribe - qos",
-			group:     "decode",
-			failFirst: ErrMalformedQoS,
-			rawBytes: []byte{
-				byte(Subscribe << 4), 2, // Fixed header
-				0, 22, // Packet ID - LSB+MSB
-
-				0, 3, // Topic Name - LSB+MSB
-				'j', '/', 'b', // Topic Name
-
-			},
-		},
-		{
-			desc:      "Malformed Subscribe - qos out of range",
-			group:     "decode",
-			failFirst: ErrMalformedQoS,
-			rawBytes: []byte{
-				byte(Subscribe << 4), 2, // Fixed header
-				0, 22, // Packet ID - LSB+MSB
-
-				0, 3, // Topic Name - LSB+MSB
-				'c', '/', 'd', // Topic Name
-				5, // QoS
-
-			},
-		},
-
-		// Validation
-		{
-			// @SPEC [MQTT-2.3.1-1].
-			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-			desc:   "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
-			group:  "validate",
-			expect: ErrMissingPacketID,
-			code:   Failed,
-			packet: &SubscribePacket{
-				FixedHeader: FixedHeader{
-					Type: Subscribe,
-					Qos:  1,
-				},
-				PacketID: 0,
-			},
-		},
-
-		// Spec tests
-		{
-			// @SPEC [MQTT-2.3.1-1].
-			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-			desc:   "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
-			group:  "encode",
-			code:   Failed,
-			expect: ErrMissingPacketID,
-			rawBytes: []byte{
-				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: &SubscribePacket{
-				FixedHeader: FixedHeader{
-					Type:      Subscribe,
-					Qos:       1,
-					Remaining: 10,
-				},
-				Topics: []string{
-					"a/b/c",
-				},
-				Qoss:     []byte{1},
-				PacketID: 0,
-			},
-			meta: byte(2),
-		},
-	},
-	Suback: {
-		{
-			desc:    "Suback",
-			primary: true,
-			rawBytes: []byte{
-				byte(Suback << 4), 6, // Fixed header
-				0, 17, // Packet ID - LSB+MSB
-				0,    // Return Code QoS 0
-				1,    // Return Code QoS 1
-				2,    // Return Code QoS 2
-				0x80, // Return Code fail
-			},
-			packet: &SubackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Suback,
-					Remaining: 6,
-				},
-				PacketID:    17,
-				ReturnCodes: []byte{0, 1, 2, 0x80},
-			},
-		},
-
-		// Fail states
-		{
-			desc:      "Malformed Suback - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Subscribe << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-	},
-
-	Unsubscribe: {
-		{
-			desc:    "Unsubscribe",
-			primary: true,
-			rawBytes: []byte{
-				byte(Unsubscribe << 4), 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: &UnsubscribePacket{
-				FixedHeader: FixedHeader{
-					Type:      Unsubscribe,
-					Remaining: 27,
-				},
-				PacketID: 35,
-				Topics: []string{
-					"a/b",
-					"d/e/f/g/h/i",
-					"x/y/z",
-				},
-			},
-		},
-		// Fail states
-		{
-			desc:      "Malformed Unsubscribe - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Unsubscribe << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-		{
-			desc:      "Malformed Unsubscribe - topic",
-			group:     "decode",
-			failFirst: ErrMalformedTopic,
-			rawBytes: []byte{
-				byte(Unsubscribe << 4), 2, // Fixed header
-				0, 21, // Packet ID - LSB+MSB
-				0, 3, // Topic Name - LSB+MSB
-				'a', '/',
-			},
-		},
-
-		// Validation
-		{
-			// @SPEC [MQTT-2.3.1-1].
-			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-			desc:   "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
-			group:  "validate",
-			expect: ErrMissingPacketID,
-			code:   Failed,
-			packet: &UnsubscribePacket{
-				FixedHeader: FixedHeader{
-					Type: Unsubscribe,
-					Qos:  1,
-				},
-				PacketID: 0,
-			},
-		},
-
-		// Spec tests
-		{
-			// @SPEC [MQTT-2.3.1-1].
-			// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-			desc:   "[MQTT-2.3.1-1] Unsubscribe No Packet ID with QOS > 0",
-			group:  "encode",
-			code:   Failed,
-			expect: ErrMissingPacketID,
-			rawBytes: []byte{
-				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: &UnsubscribePacket{
-				FixedHeader: FixedHeader{
-					Type:      Unsubscribe,
-					Qos:       1,
-					Remaining: 9,
-				},
-				Topics: []string{
-					"a/b/c",
-				},
-				PacketID: 0,
-			},
-			meta: byte(2),
-		},
-	},
-	Unsuback: {
-		{
-			desc:    "Unsuback",
-			primary: true,
-			rawBytes: []byte{
-				byte(Unsuback << 4), 2, // Fixed header
-				0, 37, // Packet ID - LSB+MSB
-
-			},
-			packet: &UnsubackPacket{
-				FixedHeader: FixedHeader{
-					Type:      Unsuback,
-					Remaining: 2,
-				},
-				PacketID: 37,
-			},
-		},
-
-		// Fail states
-		{
-			desc:      "Malformed Unsuback - Packet ID",
-			group:     "decode",
-			failFirst: ErrMalformedPacketID,
-			rawBytes: []byte{
-				byte(Unsuback << 4), 2, // Fixed header
-				0, // Packet ID - LSB+MSB
-			},
-		},
-	},
-
-	Pingreq: {
-		{
-			desc:    "Ping request",
-			primary: true,
-			rawBytes: []byte{
-				byte(Pingreq << 4), 0, // fixed header
-			},
-			packet: &PingreqPacket{
-				FixedHeader: FixedHeader{
-					Type:      Pingreq,
-					Remaining: 0,
-				},
-			},
-		},
-	},
-	Pingresp: {
-		{
-			desc:    "Ping response",
-			primary: true,
-			rawBytes: []byte{
-				byte(Pingresp << 4), 0, // fixed header
-			},
-			packet: &PingrespPacket{
-				FixedHeader: FixedHeader{
-					Type:      Pingresp,
-					Remaining: 0,
-				},
-			},
-		},
-	},
-
-	Disconnect: {
-		{
-			desc:    "Disconnect",
-			primary: true,
-			rawBytes: []byte{
-				byte(Disconnect << 4), 0, // fixed header
-			},
-			packet: &DisconnectPacket{
-				FixedHeader: FixedHeader{
-					Type:      Disconnect,
-					Remaining: 0,
-				},
-			},
-		},
-	},
-}




diff --git a/packets/unsuback.go b/packets/unsuback.go
deleted file mode 100644
index 62a8f389f65fa01ff516129ff5b8b3329a5973c3..0000000000000000000000000000000000000000
--- a/packets/unsuback.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// UnsubackPacket contains the values of an MQTT UNSUBACK packet.
-type UnsubackPacket struct {
-	FixedHeader
-
-	PacketID uint16
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *UnsubackPacket) Encode(buf *bytes.Buffer) error {
-	pk.FixedHeader.Remaining = 2
-	pk.FixedHeader.encode(buf)
-	buf.Write(encodeUint16(pk.PacketID))
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *UnsubackPacket) Decode(buf []byte) error {
-	var err error
-	pk.PacketID, _, err = decodeUint16(buf, 0)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-	return nil
-}
-
-// Validate ensures the packet is compliant.
-func (pk *UnsubackPacket) Validate() (byte, error) {
-	return Accepted, nil
-}




diff --git a/packets/unsuback_test.go b/packets/unsuback_test.go
deleted file mode 100644
index 27defb707ccee7e7c1ff4a0732ed8a5e505dbadd..0000000000000000000000000000000000000000
--- a/packets/unsuback_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestUnsubackEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Unsuback)
-	for i, wanted := range expectedPackets[Unsuback] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(11), Unsuback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(UnsubackPacket)
-		copier.Copy(pk, wanted.packet.(*UnsubackPacket))
-
-		require.Equal(t, Unsuback, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Unsuback, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		require.NoError(t, err, "Expected no error writing buffer [i:%d] %s", i, wanted.desc)
-		encoded := buf.Bytes()
-
-		require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-		if wanted.meta != nil {
-			require.Equal(t, byte(Unsuback<<4)|wanted.meta.(byte), encoded[0], "Mismatched mod fixed header packets [i:%d] %s", i, wanted.desc)
-		} else {
-			require.Equal(t, byte(Unsuback<<4), encoded[0], "Mismatched fixed header packets [i:%d] %s", i, wanted.desc)
-		}
-
-		require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*UnsubackPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkUnsubackEncode(b *testing.B) {
-	pk := new(UnsubackPacket)
-	copier.Copy(pk, expectedPackets[Unsuback][0].packet.(*UnsubackPacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestUnsubackDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Unsuback)
-	for i, wanted := range expectedPackets[Unsuback] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(11), Unsuback, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Unsuback).(*UnsubackPacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*UnsubackPacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkUnsubackDecode(b *testing.B) {
-	pk := newPacket(Unsuback).(*UnsubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Unsuback][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Unsuback][0].rawBytes[2:])
-	}
-}
-
-func TestUnsubackValidate(t *testing.T) {
-	pk := newPacket(Unsuback).(*UnsubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Unsuback][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-
-}
-
-func BenchmarkUnsubackValidate(b *testing.B) {
-	pk := newPacket(Unsuback).(*UnsubackPacket)
-	pk.FixedHeader.decode(expectedPackets[Unsuback][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/packets/unsubscribe.go b/packets/unsubscribe.go
deleted file mode 100644
index 6e6124eeee66c3fe29b9d42e1109b1e59fb72e93..0000000000000000000000000000000000000000
--- a/packets/unsubscribe.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package packets
-
-import (
-	"bytes"
-)
-
-// UnsubscribePacket contains the values of an MQTT UNSUBSCRIBE packet.
-type UnsubscribePacket struct {
-	FixedHeader
-
-	PacketID uint16
-	Topics   []string
-}
-
-// Encode encodes and writes the packet data values to the buffer.
-func (pk *UnsubscribePacket) Encode(buf *bytes.Buffer) error {
-
-	// Add the Packet ID.
-	// [MQTT-2.3.1-1] SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-	if pk.PacketID == 0 {
-		return ErrMissingPacketID
-	}
-
-	packetID := encodeUint16(pk.PacketID)
-
-	// Count topics lengths.
-	var topicsLen int
-	for _, topic := range pk.Topics {
-		topicsLen += len(encodeString(topic))
-	}
-
-	pk.FixedHeader.Remaining = len(packetID) + topicsLen
-	pk.FixedHeader.encode(buf)
-	buf.Write(packetID)
-
-	// Add all provided topic names.
-	for _, topic := range pk.Topics {
-		buf.Write(encodeString(topic))
-	}
-
-	return nil
-}
-
-// Decode extracts the data values from the packet.
-func (pk *UnsubscribePacket) Decode(buf []byte) error {
-
-	var offset int
-	var err error
-
-	// Get the Packet ID.
-	pk.PacketID, offset, err = decodeUint16(buf, 0)
-	if err != nil {
-		return ErrMalformedPacketID
-	}
-
-	// Keep decoding until there's no space left.
-	for offset < len(buf) {
-
-		// Decode Topic Name.
-		var t string
-		t, offset, err = decodeString(buf, offset)
-		if err != nil {
-			return ErrMalformedTopic
-		}
-
-		if t != "" {
-			pk.Topics = append(pk.Topics, t)
-		}
-
-	}
-
-	return nil
-
-}
-
-// Validate ensures the packet is compliant.
-func (pk *UnsubscribePacket) Validate() (byte, error) {
-
-	// @SPEC [MQTT-2.3.1-1].
-	// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
-	if pk.FixedHeader.Qos > 0 && pk.PacketID == 0 {
-		return Failed, ErrMissingPacketID
-	}
-
-	return Accepted, nil
-}




diff --git a/packets/unsubscribe_test.go b/packets/unsubscribe_test.go
deleted file mode 100644
index ce3453d68bdc9500d9fd965815fe428ace4045d8..0000000000000000000000000000000000000000
--- a/packets/unsubscribe_test.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package packets
-
-import (
-	"bytes"
-	"testing"
-
-	"github.com/jinzhu/copier"
-	"github.com/stretchr/testify/require"
-)
-
-func TestUnsubscribeEncode(t *testing.T) {
-	require.Contains(t, expectedPackets, Unsubscribe)
-	for i, wanted := range expectedPackets[Unsubscribe] {
-		if !encodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(10), Unsubscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-		pk := new(UnsubscribePacket)
-		copier.Copy(pk, wanted.packet.(*UnsubscribePacket))
-
-		require.Equal(t, Unsubscribe, pk.Type, "Mismatched Packet Type [i:%d] %s", i, wanted.desc)
-		require.Equal(t, Unsubscribe, pk.FixedHeader.Type, "Mismatched FixedHeader Type [i:%d] %s", i, wanted.desc)
-
-		buf := new(bytes.Buffer)
-		err := pk.Encode(buf)
-		encoded := buf.Bytes()
-
-		if wanted.expect != nil {
-			require.Error(t, err, "Expected error writing buffer [i:%d] %s", i, wanted.desc)
-		} else {
-			require.NoError(t, err, "Error writing buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, len(wanted.rawBytes), len(encoded), "Mismatched packet length [i:%d] %s", i, wanted.desc)
-			if wanted.meta != nil {
-				require.Equal(t, byte(Unsubscribe<<4)|wanted.meta.(byte), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-			} else {
-				require.Equal(t, byte(Unsubscribe<<4), encoded[0], "Mismatched fixed header bytes [i:%d] %s", i, wanted.desc)
-			}
-
-			require.NoError(t, err, "Error writing buffer [i:%d] %s", i, wanted.desc)
-			require.EqualValues(t, wanted.rawBytes, encoded, "Mismatched byte values [i:%d] %s", i, wanted.desc)
-
-			require.Equal(t, wanted.packet.(*UnsubscribePacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.packet.(*UnsubscribePacket).Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
-		}
-	}
-}
-
-func BenchmarkUnsubscribeEncode(b *testing.B) {
-	pk := new(UnsubscribePacket)
-	copier.Copy(pk, expectedPackets[Unsubscribe][0].packet.(*UnsubscribePacket))
-
-	buf := new(bytes.Buffer)
-	for n := 0; n < b.N; n++ {
-		pk.Encode(buf)
-	}
-}
-
-func TestUnsubscribeDecode(t *testing.T) {
-	require.Contains(t, expectedPackets, Unsubscribe)
-	for i, wanted := range expectedPackets[Unsubscribe] {
-
-		if !decodeTestOK(wanted) {
-			continue
-		}
-
-		require.Equal(t, uint8(10), Unsubscribe, "Incorrect Packet Type [i:%d] %s", i, wanted.desc)
-
-		pk := newPacket(Unsubscribe).(*UnsubscribePacket)
-		err := pk.Decode(wanted.rawBytes[2:]) // Unpack skips fixedheader.
-		if wanted.failFirst != nil {
-			require.Error(t, err, "Expected error unpacking buffer [i:%d] %s", i, wanted.desc)
-			require.Equal(t, wanted.failFirst, err, "Expected fail state; %v [i:%d] %s", err.Error(), i, wanted.desc)
-			continue
-		}
-
-		require.NoError(t, err, "Error unpacking buffer [i:%d] %s", i, wanted.desc)
-
-		require.Equal(t, wanted.packet.(*UnsubscribePacket).PacketID, pk.PacketID, "Mismatched Packet ID [i:%d] %s", i, wanted.desc)
-		require.Equal(t, wanted.packet.(*UnsubscribePacket).Topics, pk.Topics, "Mismatched Topics slice [i:%d] %s", i, wanted.desc)
-	}
-}
-
-func BenchmarkUnsubscribeDecode(b *testing.B) {
-	pk := newPacket(Unsubscribe).(*UnsubscribePacket)
-	pk.FixedHeader.decode(expectedPackets[Unsubscribe][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Decode(expectedPackets[Unsubscribe][0].rawBytes[2:])
-	}
-}
-
-func TestUnsubscribeValidate(t *testing.T) {
-	/*pk := newPacket(Unsubscribe).(*UnsubscribePacket)
-	pk.FixedHeader.decode(expectedPackets[Unsubscribe][0].rawBytes[0])
-
-	b, err := pk.Validate()
-	require.NoError(t, err)
-	require.Equal(t, Accepted, b)
-	*/
-	require.Contains(t, expectedPackets, Unsubscribe)
-	for i, wanted := range expectedPackets[Unsubscribe] {
-		if wanted.group == "validate" || i == 0 {
-			pk := wanted.packet.(*UnsubscribePacket)
-			ok, err := pk.Validate()
-			if i == 0 {
-				require.NoError(t, err, "Unsubscribe should have validated - error incorrect [i:%d] %s", i, wanted.desc)
-				require.Equal(t, Accepted, ok, "Unsubscribe should have validated - code incorrect [i:%d] %s", i, wanted.desc)
-			} else {
-				require.Equal(t, Failed, ok, "Unsubscribe packet didn't validate - code incorrect [i:%d] %s", i, wanted.desc)
-				if err != nil {
-					require.Equal(t, wanted.expect, err, "Unsubscribe packet didn't validate - error incorrect [i:%d] %s", i, wanted.desc)
-				}
-			}
-		}
-	}
-
-}
-
-func BenchmarkUnsubscribeValidate(b *testing.B) {
-	pk := newPacket(Unsubscribe).(*UnsubscribePacket)
-	pk.FixedHeader.decode(expectedPackets[Unsubscribe][0].rawBytes[0])
-
-	for n := 0; n < b.N; n++ {
-		pk.Validate()
-	}
-}




diff --git a/pools/bufio.go b/pools/bufio.go
deleted file mode 100644
index 31d775306966999c72d29e5eb45ad4d964094524..0000000000000000000000000000000000000000
--- a/pools/bufio.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package pools
-
-/*
-import (
-	"bufio"
-	"io"
-	"sync"
-)
-
-// BufioReadersPool is a pool of bufio.Reader.
-type BufioReadersPool struct {
-	pool sync.Pool
-}
-
-// NewBufioReadersPool returns a sync.pool of bufio.Reader.
-func NewBufioReadersPool(size int) BufioReadersPool {
-	return BufioReadersPool{
-		pool: sync.Pool{
-			New: func() interface{} {
-				return bufio.NewReaderSize(nil, size)
-			},
-		},
-	}
-}
-
-// Get returns a pooled bufio.Reader.
-func (b BufioReadersPool) Get(r io.Reader) *bufio.Reader {
-	buf := b.pool.Get().(*bufio.Reader)
-	buf.Reset(r)
-	return buf
-}
-
-// Put puts the bufio.Reader back into the pool.
-func (b BufioReadersPool) Put(x *bufio.Reader) {
-	x.Reset(nil)
-	b.pool.Put(x)
-}
-
-// BufioWritersPool is a pool of bufio.Writer.
-type BufioWritersPool struct {
-	pool sync.Pool
-}
-
-// NewBufioWritersPool returns a sync.pool of bufio.Writer.
-func NewBufioWritersPool(size int) BufioWritersPool {
-	return BufioWritersPool{
-		pool: sync.Pool{
-			New: func() interface{} {
-				return bufio.NewWriterSize(nil, size)
-			},
-		},
-	}
-}
-
-// Get returns a pooled bufio.Writer.
-func (b BufioWritersPool) Get(r io.Writer) *bufio.Writer {
-	buf := b.pool.Get().(*bufio.Writer)
-	buf.Reset(r)
-	return buf
-}
-
-// Put puts the bufio.Writer back into the pool.
-func (b BufioWritersPool) Put(x *bufio.Writer) {
-	x.Reset(nil)
-	b.pool.Put(x)
-}
-*/




diff --git a/pools/bufio_test.go b/pools/bufio_test.go
deleted file mode 100644
index c3e0a6b12e9fc06ffd4a883095450a6a2ceddc9e..0000000000000000000000000000000000000000
--- a/pools/bufio_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package pools
-
-/*
-import (
-	"bufio"
-	"bytes"
-	"strings"
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestNewBufioReadersPool(t *testing.T) {
-	bpool := NewBufioReadersPool(16)
-	require.NotNil(t, bpool.pool)
-}
-
-func BenchmarkNewBufioReadersPool(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		NewBufioReadersPool(16)
-	}
-}
-
-func TestNewBufioReadersGetPut(t *testing.T) {
-	bpool := NewBufioReadersPool(16)
-	r := bufio.NewReader(strings.NewReader("mochi"))
-	out := make([]byte, 5)
-	buf := bpool.Get(r)
-	n, err := buf.Read(out)
-	require.Equal(t, 5, n)
-	require.NoError(t, err)
-
-	require.Equal(t, []byte{'m', 'o', 'c', 'h', 'i'}, out)
-
-	bpool.Put(buf)
-	require.Panics(t, func() {
-		buf.ReadByte()
-	})
-}
-
-func BenchmarkBufioReadersGet(b *testing.B) {
-	bpool := NewBufioReadersPool(16)
-	r := bufio.NewReader(strings.NewReader("mochi"))
-
-	for n := 0; n < b.N; n++ {
-		bpool.Get(r)
-	}
-}
-
-func BenchmarkBufioReadersPut(b *testing.B) {
-	bpool := NewBufioReadersPool(16)
-	r := bufio.NewReader(strings.NewReader("mochi"))
-	buf := bpool.Get(r)
-	for n := 0; n < b.N; n++ {
-		bpool.Put(buf)
-	}
-}
-
-func TestNewBufioWritersPool(t *testing.T) {
-	bpool := NewBufioWritersPool(16)
-	require.NotNil(t, bpool.pool)
-}
-
-func BenchmarkNewBufioWritersPool(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		NewBufioWritersPool(16)
-	}
-}
-
-func TestNewBufioWritersGetPut(t *testing.T) {
-	payload := []byte{'m', 'o', 'c', 'h', 'i'}
-
-	bpool := NewBufioWritersPool(16)
-
-	buf := new(bytes.Buffer)
-	bw := bufio.NewWriter(buf)
-	require.NotNil(t, bw)
-	w := bpool.Get(bw)
-	require.NotNil(t, w)
-
-	n, err := w.Write(payload)
-	require.NoError(t, err)
-	require.Equal(t, 5, n)
-
-	w.Flush()
-	bw.Flush()
-	require.Equal(t, payload, buf.Bytes())
-
-	bpool.Put(w)
-	_, err = w.Write(payload)
-	require.NoError(t, err)
-	require.Panics(t, func() {
-		w.Flush()
-	})
-}
-
-func BenchmarkBufioWritersGet(b *testing.B) {
-	bpool := NewBufioWritersPool(16)
-	buf := new(bytes.Buffer)
-	bw := bufio.NewWriter(buf)
-
-	for n := 0; n < b.N; n++ {
-		bpool.Get(bw)
-	}
-}
-
-func BenchmarkBufioWritersPut(b *testing.B) {
-	bpool := NewBufioWritersPool(16)
-	w := bpool.Get(bufio.NewWriter(new(bytes.Buffer)))
-
-	for n := 0; n < b.N; n++ {
-		bpool.Put(w)
-	}
-}
-*/




diff --git a/pools/bytesbuffer.go b/pools/bytesbuffer.go
deleted file mode 100644
index e804a7849369cda8270cfa6d1b233aa7500faaa8..0000000000000000000000000000000000000000
--- a/pools/bytesbuffer.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package pools
-
-import (
-	"bytes"
-	"sync"
-)
-
-// BytesBuffersPool is a pool of bytes.Buffer.
-type BytesBuffersPool struct {
-	pool sync.Pool
-}
-
-// NewBytesBuffersPool returns a sync.pool of bytes.Buffer.
-func NewBytesBuffersPool() BytesBuffersPool {
-	return BytesBuffersPool{
-		pool: sync.Pool{
-			New: func() interface{} {
-				return new(bytes.Buffer)
-			},
-		},
-	}
-}
-
-// Get returns a pooled bytes.Buffer.
-func (b BytesBuffersPool) Get() *bytes.Buffer {
-	return b.pool.Get().(*bytes.Buffer)
-}
-
-// Put puts the bytes.Buffer back into the pool.
-func (b BytesBuffersPool) Put(x *bytes.Buffer) {
-	x.Reset()
-	b.pool.Put(x)
-}




diff --git a/pools/bytesbuffer_test.go b/pools/bytesbuffer_test.go
deleted file mode 100644
index 765ebbae30b5802d50e1f5a7e2e08a8beb972cc0..0000000000000000000000000000000000000000
--- a/pools/bytesbuffer_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package pools
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestNewBytesBuffersPool(t *testing.T) {
-	bpool := NewBytesBuffersPool()
-	require.NotNil(t, bpool.pool)
-}
-
-func BenchmarkNewBytesBuffersPool(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		NewBytesBuffersPool()
-	}
-}
-
-func TestNewBytesBuffersPoolGet(t *testing.T) {
-	bpool := NewBytesBuffersPool()
-	buf := bpool.Get()
-
-	buf.WriteString("mochi")
-	require.Equal(t, []byte{'m', 'o', 'c', 'h', 'i'}, buf.Bytes())
-}
-
-func BenchmarkBytesBuffersPoolGet(b *testing.B) {
-	bpool := NewBytesBuffersPool()
-	for n := 0; n < b.N; n++ {
-		bpool.Get()
-	}
-}
-
-func TestNewBytesBuffersPoolPut(t *testing.T) {
-	bpool := NewBytesBuffersPool()
-	buf := bpool.Get()
-
-	buf.WriteString("mochi")
-	require.Equal(t, []byte{'m', 'o', 'c', 'h', 'i'}, buf.Bytes())
-
-	bpool.Put(buf)
-	require.Equal(t, 0, buf.Len())
-}
-
-func BenchmarkBytesBuffersPoolPut(b *testing.B) {
-	bpool := NewBytesBuffersPool()
-	buf := bpool.Get()
-	for n := 0; n < b.N; n++ {
-		bpool.Put(buf)
-	}
-}




diff --git a/topics/topics.go b/topics/topics.go
deleted file mode 100644
index 1265ef5f41ef26c58afddc6ca361dff86a3dc438..0000000000000000000000000000000000000000
--- a/topics/topics.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package topics
-
-import (
-	"github.com/mochi-co/mqtt/packets"
-)
-
-// Indexer indexes filter subscriptions and retained messages.
-type Indexer interface {
-
-	// Subscribers returns the clients with filters matching a topic.
-	Subscribers(topic string) Subscriptions
-
-	// Subscribe adds a new filter subscription for a client.
-	Subscribe(filter string, client string, qos byte)
-
-	// Unsubscribe removes a filter subscription for a client.
-	Unsubscribe(filter string, client string) bool
-
-	// Messages returns message payloads retained for topics matching a filter.
-	Messages(topic string) []*packets.PublishPacket
-
-	// Retain retains a message payload for a topic.
-	RetainMessage(packet *packets.PublishPacket)
-}
-
-// Subscriptions is a map of subscriptions keyed on client.
-type Subscriptions map[string]byte




diff --git a/topics/trie/trie.go b/topics/trie/trie.go
deleted file mode 100644
index a1fa400c2536065cbe1d6d57dc57b145c67805c2..0000000000000000000000000000000000000000
--- a/topics/trie/trie.go
+++ /dev/null
@@ -1,323 +0,0 @@
-package trie
-
-import (
-	"fmt"
-	"strings"
-	//	"sync"
-	sync "github.com/sasha-s/go-deadlock"
-
-	//"github.com/davecgh/go-spew/spew"
-
-	"github.com/mochi-co/mqtt/packets"
-	"github.com/mochi-co/mqtt/topics"
-)
-
-func ReLeaf(m string, leaf *Leaf, d int) {
-	for k, v := range leaf.Leaves {
-		fmt.Println(m, d, strings.Repeat("  ", d), k)
-		ReLeaf(m, v, d+1)
-	}
-}
-
-// Index is a prefix/trie tree containing topic subscribers and retained messages.
-type Index struct {
-	mu sync.RWMutex
-	//	sync.RWMutex
-	Root *Leaf
-}
-
-// New returns a pointer to a new instance of Index.
-func New() *Index {
-	return &Index{
-		Root: &Leaf{
-			Leaves:  make(map[string]*Leaf),
-			Clients: make(map[string]byte),
-		},
-	}
-}
-
-// RetainMessage saves a message payload to the end of a topic branch.
-func (x *Index) RetainMessage(msg *packets.PublishPacket) {
-	x.mu.Lock()
-	defer x.mu.Unlock()
-	if len(msg.Payload) > 0 {
-		n := x.poperate(msg.TopicName)
-		n.Message = msg
-	} else {
-		x.unpoperate(msg.TopicName, "", true)
-	}
-	//spew.Dump(x.Root)
-}
-
-// Subscribe creates a subscription filter for a client.
-func (x *Index) Subscribe(filter, client string, qos byte) {
-	x.mu.Lock()
-	defer x.mu.Unlock()
-	n := x.poperate(filter)
-	n.Clients[client] = qos
-	n.Filter = filter
-	//ReLeaf("sub", x.Root, 0)
-}
-
-// Unsubscribe removes a subscription filter for a client. Returns true if an
-// unsubscribe action sucessful.
-func (x *Index) Unsubscribe(filter, client string) bool {
-	x.mu.Lock()
-	defer x.mu.Unlock()
-	return x.unpoperate(filter, client, false)
-}
-
-// unpoperate steps backward through a trie sequence and removes any orphaned
-// nodes. If a client id is specified, it will unsubscribe a client. If message
-// is true, it will delete a retained message.
-func (x *Index) unpoperate(filter string, client string, message bool) bool {
-
-	// Walk to end leaf.
-	var d int
-	var particle string
-	var hasNext = true
-	e := x.Root
-	for hasNext {
-		particle, hasNext = isolateParticle(filter, d)
-		d++
-		e, _ = e.Leaves[particle]
-
-		// If the topic part doesn't exist in the tree, there's nothing
-		// left to do.
-		if e == nil {
-			return false
-		}
-	}
-
-	// Step backward removing client and orphaned leaves.
-	var key string
-	var orphaned bool
-	var end = true
-	for e.Parent != nil {
-		key = e.Key
-
-		// Wipe the client from this leaf if it's the filter end.
-		if end {
-			if client != "" {
-				delete(e.Clients, client)
-			}
-			if message {
-				e.Message = nil
-			}
-			end = false
-		}
-
-		// If this leaf is empty, note it as orphaned.
-		orphaned = len(e.Clients) == 0 && len(e.Leaves) == 0 && e.Message == nil
-
-		// Traverse up the branch.
-		e = e.Parent
-
-		// If the leaf we just came from was empty, delete it.
-		if orphaned {
-			delete(e.Leaves, key)
-		}
-	}
-
-	return true
-
-}
-
-// poperate iterates and populates through a topic/filter path, instantiating
-// leaves as it goes and returning the final leaf in the branch.
-// poperate is a more enjoyable word than iterpop.
-func (x *Index) poperate(topic string) *Leaf {
-	var d int
-	var particle string
-	var hasNext = true
-	n := x.Root
-	for hasNext {
-		particle, hasNext = isolateParticle(topic, d)
-		d++
-
-		child, _ := n.Leaves[particle]
-		if child == nil {
-			child = &Leaf{
-				Key:     particle,
-				Parent:  n,
-				Leaves:  make(map[string]*Leaf),
-				Clients: make(map[string]byte),
-			}
-			n.Leaves[particle] = child
-		}
-		n = child
-	}
-
-	return n
-}
-
-// Subscribers returns a map of clients who are subscribed to matching filters.
-func (x *Index) Subscribers(topic string) topics.Subscriptions {
-	x.mu.RLock()
-	defer x.mu.RUnlock()
-	return x.Root.scanSubscribers(topic, 0, make(topics.Subscriptions))
-}
-
-// Messages returns a slice of retained topic messages which match a filter.
-func (x *Index) Messages(filter string) []*packets.PublishPacket {
-	// ReLeaf("messages", x.Root, 0)
-	x.mu.RLock()
-	defer x.mu.RUnlock()
-	return x.Root.scanMessages(filter, 0, make([]*packets.PublishPacket, 0, 32))
-}
-
-// Leaf is a child node on the tree.
-type Leaf struct {
-
-	// Key contains the key that was used to create the leaf.
-	Key string
-
-	// Parent is a pointer to the parent node for the leaf.
-	Parent *Leaf
-
-	// Leafs is a map of child nodes, keyed on particle id.
-	Leaves map[string]*Leaf
-
-	// Clients is a map of client ids subscribed to the topic.
-	Clients map[string]byte
-
-	// Filter is the path of the topic filter being matched.
-	Filter string
-
-	// Message is a message which has been retained for a specific topic.
-	Message *packets.PublishPacket
-}
-
-// scanSubscribers recursively steps through a branch of leaves finding clients who
-// have subscription filters matching a topic, and their highest QoS byte.
-func (l *Leaf) scanSubscribers(topic string, d int, clients topics.Subscriptions) topics.Subscriptions {
-	part, hasNext := isolateParticle(topic, d)
-
-	// For either the topic part, a +, or a #, follow the branch.
-	for _, particle := range []string{part, "+", "#"} {
-		if child, ok := l.Leaves[particle]; ok {
-
-			// We're only interested in getting clients from the final
-			// element in the topic, or those with wildhashes.
-			if !hasNext || particle == "#" {
-
-				// Capture the highest QOS byte for any client with a filter
-				// matching the topic.
-				for client, qos := range child.Clients {
-					if ex, ok := clients[client]; !ok || ex < qos {
-						clients[client] = qos
-					}
-				}
-
-				// Make sure we also capture any client who are listening
-				// to this topic via path/#
-				if !hasNext {
-					if extra, ok := child.Leaves["#"]; ok {
-						for client, qos := range extra.Clients {
-							if ex, ok := clients[client]; !ok || ex < qos {
-								clients[client] = qos
-							}
-						}
-					}
-				}
-			}
-
-			// If this branch has hit a wildhash, just return immediately.
-			if particle == "#" {
-				return clients
-			} else if hasNext {
-				// Otherwise continue down the branch.
-				clients = child.scanSubscribers(topic, d+1, clients)
-			}
-		}
-	}
-
-	return clients
-}
-
-// scanMessages recursively steps through a branch of leaves finding retained messages
-// that match a topic filter. Setting `d` to -1 will enable wildhash mode, and will
-// recursively check ALL child leaves in every subsequent branch.
-func (l *Leaf) scanMessages(filter string, d int, messages []*packets.PublishPacket) []*packets.PublishPacket {
-
-	// If a wildhash mode has been set, continue recursively checking through all
-	// child leaves regardless of their particle key.
-	if d == -1 {
-		for _, child := range l.Leaves {
-			if child.Message != nil {
-				messages = append(messages, child.Message)
-			}
-			messages = child.scanMessages(filter, -1, messages)
-		}
-		return messages
-	}
-
-	// Otherwise, we'll get the particle for d in the filter.
-	particle, hasNext := isolateParticle(filter, d)
-
-	// If there's no more particles after this one, then take the messages from
-	// these topics.
-	if !hasNext {
-
-		// Wildcards and Wildhashes must be checked first, otherwise they
-		// may be detected as standard particles, and not act properly.
-		if particle == "+" || particle == "#" {
-			// Otherwise, if it's a wildcard or wildhash, get messages from all
-			// the child leaves. This wildhash captures messages on the actual
-			// wildhash position, whereas the d == -1 block collects subsequent
-			// messages further down the branch.
-			for _, child := range l.Leaves {
-				if child.Message != nil {
-					messages = append(messages, child.Message)
-				}
-			}
-		} else if child, ok := l.Leaves[particle]; ok {
-			// If it's a specific particle, we only need the single message.
-			if child.Message != nil {
-				messages = append(messages, child.Message)
-			}
-		}
-
-	} else {
-
-		// If it's not the last particle, branch out to the next leaves, scanning
-		// all available if it's a wildcard, or just one if it's a specific particle.
-		if particle == "+" {
-			for _, child := range l.Leaves {
-				messages = child.scanMessages(filter, d+1, messages)
-			}
-		} else if child, ok := l.Leaves[particle]; ok {
-			messages = child.scanMessages(filter, d+1, messages)
-		}
-	}
-
-	// If the particle was a wildhash, scan all the child leaves setting the
-	// d value to wildhash mode.
-	if particle == "#" {
-		for _, child := range l.Leaves {
-			messages = child.scanMessages(filter, -1, messages)
-		}
-	}
-
-	return messages
-}
-
-// isolateParticle extracts a particle between d / and d+1 / without allocations.
-func isolateParticle(filter string, d int) (particle string, hasNext bool) {
-	var next, end int
-	for i := 0; end > -1 && i <= d; i++ {
-		end = strings.IndexRune(filter, '/')
-		if d > -1 && i == d && end > -1 {
-			hasNext = true
-			particle = filter[next:end]
-		} else if end > -1 {
-			hasNext = false
-			filter = filter[end+1:]
-		} else {
-			hasNext = false
-			particle = filter[next:]
-		}
-	}
-
-	return
-}




diff --git a/topics/trie/trie_test.go b/topics/trie/trie_test.go
deleted file mode 100644
index cc46a225a71a8cd1e9fd4b991e07e662d43b6728..0000000000000000000000000000000000000000
--- a/topics/trie/trie_test.go
+++ /dev/null
@@ -1,411 +0,0 @@
-package trie
-
-import (
-	"testing"
-
-	//"github.com/davecgh/go-spew/spew"
-
-	"github.com/stretchr/testify/require"
-
-	"github.com/mochi-co/mqtt/packets"
-)
-
-func TestNew(t *testing.T) {
-	index := New()
-	require.NotNil(t, index)
-	require.NotNil(t, index.Root)
-}
-
-func BenchmarkNew(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		New()
-	}
-}
-
-func TestPoperate(t *testing.T) {
-	index := New()
-	child := index.poperate("path/to/my/mqtt")
-	require.Equal(t, "mqtt", child.Key)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
-
-	child = index.poperate("a/b/c/d/e")
-	require.Equal(t, "e", child.Key)
-	child = index.poperate("a/b/c/c/a")
-	require.Equal(t, "a", child.Key)
-}
-
-func BenchmarkPoperate(b *testing.B) {
-	index := New()
-	for n := 0; n < b.N; n++ {
-		index.poperate("path/to/my/mqtt")
-	}
-}
-
-func TestUnpoperate(t *testing.T) {
-	index := New()
-	index.Subscribe("path/to/my/mqtt", "client-1", 0)
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
-
-	index.Subscribe("path/to/another/mqtt", "client-1", 0)
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients, "client-1")
-
-	pk := &packets.PublishPacket{TopicName: "path/to/retained/message", Payload: []byte{'h', 'e', 'l', 'l', 'o'}}
-	index.RetainMessage(pk)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["retained"].Leaves["message"])
-	require.Equal(t, pk, index.Root.Leaves["path"].Leaves["to"].Leaves["retained"].Leaves["message"].Message)
-
-	pk2 := &packets.PublishPacket{TopicName: "path/to/my/mqtt", Payload: []byte{'s', 'h', 'a', 'r', 'e', 'd'}}
-	index.RetainMessage(pk2)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
-	require.Equal(t, pk2, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message)
-
-	index.unpoperate("path/to/my/mqtt", "", true) // delete retained
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
-	require.Nil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message)
-
-	index.unpoperate("path/to/my/mqtt", "client-1", false) // unsubscribe client
-	require.Nil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"])
-
-	index.unpoperate("path/to/retained/message", "", true) // delete retained
-	require.NotContains(t, index.Root.Leaves["path"].Leaves["to"].Leaves, "my")
-
-	//require.Empty(t, index.Root.Leaves["path"])
-
-}
-
-func BenchmarkUnpoperate(b *testing.B) {
-	//index := New()
-	for n := 0; n < b.N; n++ {
-		//		index.poperate("path/to/my/mqtt")
-	}
-}
-
-func TestRetainMessage(t *testing.T) {
-	pk := &packets.PublishPacket{TopicName: "path/to/my/mqtt", Payload: []byte{'h', 'e', 'l', 'l', 'o'}}
-	pk2 := &packets.PublishPacket{TopicName: "path/to/another/mqtt", Payload: []byte{'h', 'e', 'l', 'l', 'o'}}
-
-	index := New()
-	index.RetainMessage(pk)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
-	require.Equal(t, pk, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message)
-
-	index.Subscribe("path/to/another/mqtt", "client-1", 0)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients["client-1"])
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"])
-
-	index.RetainMessage(pk2)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"])
-	require.Equal(t, pk2, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Message)
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients, "client-1")
-
-	// Delete retained
-	pk3 := &packets.PublishPacket{TopicName: "path/to/another/mqtt", Payload: []byte{}}
-	index.RetainMessage(pk3)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"])
-	require.Equal(t, pk, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Message)
-	require.Nil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Message)
-
-}
-
-func BenchmarkRetainMessage(b *testing.B) {
-	index := New()
-	pk := &packets.PublishPacket{TopicName: "path/to/another/mqtt"}
-	for n := 0; n < b.N; n++ {
-		index.RetainMessage(pk)
-	}
-}
-
-func TestSubscribeOK(t *testing.T) {
-	index := New()
-	index.Subscribe("path/to/my/mqtt", "client-1", 0)
-	index.Subscribe("path/to/my/mqtt", "client-2", 0)
-	index.Subscribe("path/to/another/mqtt", "client-1", 0)
-	index.Subscribe("path/+", "client-2", 0)
-	index.Subscribe("#", "client-3", 0)
-
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
-	require.Equal(t, "path/to/my/mqtt", index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Filter)
-	require.Equal(t, "mqtt", index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Key)
-	require.Equal(t, index.Root.Leaves["path"], index.Root.Leaves["path"].Leaves["to"].Parent)
-	require.NotNil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-2")
-
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["another"].Leaves["mqtt"].Clients, "client-1")
-	require.Contains(t, index.Root.Leaves["path"].Leaves["+"].Clients, "client-2")
-	require.Contains(t, index.Root.Leaves["#"].Clients, "client-3")
-}
-
-func BenchmarkSubscribe(b *testing.B) {
-	index := New()
-	for n := 0; n < b.N; n++ {
-		index.Subscribe("path/to/mqtt/basic", "client-1", 0)
-	}
-}
-
-func TestUnsubscribeA(t *testing.T) {
-	index := New()
-	index.Subscribe("path/to/my/mqtt", "client-1", 0)
-	index.Subscribe("path/to/+/mqtt", "client-1", 0)
-	index.Subscribe("path/to/stuff", "client-1", 0)
-	index.Subscribe("path/to/stuff", "client-2", 0)
-	index.Subscribe("#", "client-3", 0)
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"].Leaves["mqtt"].Clients, "client-1")
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["+"].Leaves["mqtt"].Clients, "client-1")
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-1")
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-2")
-	require.Contains(t, index.Root.Leaves["#"].Clients, "client-3")
-
-	ok := index.Unsubscribe("path/to/my/mqtt", "client-1")
-
-	require.Equal(t, true, ok)
-	require.Nil(t, index.Root.Leaves["path"].Leaves["to"].Leaves["my"])
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["+"].Leaves["mqtt"].Clients, "client-1")
-
-	ok = index.Unsubscribe("path/to/stuff", "client-1")
-	require.Equal(t, true, ok)
-	require.NotContains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-1")
-	require.Contains(t, index.Root.Leaves["path"].Leaves["to"].Leaves["stuff"].Clients, "client-2")
-	require.Contains(t, index.Root.Leaves["#"].Clients, "client-3")
-
-	ok = index.Unsubscribe("fdasfdas/dfsfads/sa", "client-1")
-	require.Equal(t, false, ok)
-
-}
-
-func TestUnsubscribeCascade(t *testing.T) {
-	index := New()
-	index.Subscribe("a/b/c", "client-1", 0)
-	index.Subscribe("a/b/c/e/e", "client-1", 0)
-
-	ok := index.Unsubscribe("a/b/c/e/e", "client-1")
-	require.Equal(t, true, ok)
-	require.NotEmpty(t, index.Root.Leaves)
-	require.Contains(t, index.Root.Leaves["a"].Leaves["b"].Leaves["c"].Clients, "client-1")
-}
-
-// This benchmark is Unsubscribe-Subscribe
-func BenchmarkUnsubscribe(b *testing.B) {
-	index := New()
-
-	for n := 0; n < b.N; n++ {
-		index.Subscribe("path/to/my/mqtt", "client-1", 0)
-		index.Unsubscribe("path/to/mqtt/basic", "client-1")
-	}
-}
-
-func TestSubscribersFind(t *testing.T) {
-	tt := []struct {
-		filter string
-		topic  string
-		len    int
-	}{
-		{
-			filter: "a",
-			topic:  "a",
-			len:    1,
-		},
-		{
-			filter: "a/",
-			topic:  "a",
-			len:    0,
-		},
-		{
-			filter: "a/",
-			topic:  "a/",
-			len:    1,
-		},
-		{
-			filter: "path/to/my/mqtt",
-			topic:  "path/to/my/mqtt",
-			len:    1,
-		},
-		{
-			filter: "path/to/+/mqtt",
-			topic:  "path/to/my/mqtt",
-			len:    1,
-		},
-		{
-			filter: "+/to/+/mqtt",
-			topic:  "path/to/my/mqtt",
-			len:    1,
-		},
-		{
-			filter: "#",
-			topic:  "path/to/my/mqtt",
-			len:    1,
-		},
-		{
-			filter: "+/+/+/+",
-			topic:  "path/to/my/mqtt",
-			len:    1,
-		},
-		{
-			filter: "+/+/+/#",
-			topic:  "path/to/my/mqtt",
-			len:    1,
-		},
-		{
-			filter: "zen/#",
-			topic:  "zen",
-			len:    1,
-		},
-		{
-			filter: "+/+/#",
-			topic:  "path/to/my/mqtt",
-			len:    1,
-		},
-		{
-			filter: "path/to/",
-			topic:  "path/to/my/mqtt",
-			len:    0,
-		},
-		{
-			filter: "#/stuff",
-			topic:  "path/to/my/mqtt",
-			len:    0,
-		},
-	}
-
-	for i, check := range tt {
-		index := New()
-		index.Subscribe(check.filter, "client-1", 0)
-		clients := index.Subscribers(check.topic)
-		//spew.Dump(clients)
-		require.Equal(t, check.len, len(clients), "Unexpected clients len at %d %s %s", i, check.filter, check.topic)
-	}
-
-}
-
-func BenchmarkSubscribers(b *testing.B) {
-	index := New()
-	index.Subscribe("path/to/my/mqtt", "client-1", 0)
-	index.Subscribe("path/to/+/mqtt", "client-1", 0)
-	index.Subscribe("something/things/stuff/+", "client-1", 0)
-	index.Subscribe("path/to/stuff", "client-2", 0)
-	index.Subscribe("#", "client-3", 0)
-
-	for n := 0; n < b.N; n++ {
-		index.Subscribers("path/to/testing/mqtt")
-	}
-}
-
-func TestIsolateParticle(t *testing.T) {
-	particle, hasNext := isolateParticle("path/to/my/mqtt", 0)
-	require.Equal(t, "path", particle)
-	require.Equal(t, true, hasNext)
-	particle, hasNext = isolateParticle("path/to/my/mqtt", 1)
-	require.Equal(t, "to", particle)
-	require.Equal(t, true, hasNext)
-	particle, hasNext = isolateParticle("path/to/my/mqtt", 2)
-	require.Equal(t, "my", particle)
-	require.Equal(t, true, hasNext)
-	particle, hasNext = isolateParticle("path/to/my/mqtt", 3)
-	require.Equal(t, "mqtt", particle)
-	require.Equal(t, false, hasNext)
-
-	particle, hasNext = isolateParticle("/path/", 0)
-	require.Equal(t, "", particle)
-	require.Equal(t, true, hasNext)
-	particle, hasNext = isolateParticle("/path/", 1)
-	require.Equal(t, "path", particle)
-	require.Equal(t, true, hasNext)
-	particle, hasNext = isolateParticle("/path/", 2)
-	require.Equal(t, "", particle)
-	require.Equal(t, false, hasNext)
-
-	particle, hasNext = isolateParticle("a/b/c/+/+", 3)
-	require.Equal(t, "+", particle)
-	require.Equal(t, true, hasNext)
-	particle, hasNext = isolateParticle("a/b/c/+/+", 4)
-	require.Equal(t, "+", particle)
-	require.Equal(t, false, hasNext)
-}
-
-func BenchmarkIsolateParticle(b *testing.B) {
-	for n := 0; n < b.N; n++ {
-		isolateParticle("path/to/my/mqtt", 3)
-	}
-}
-
-func TestMessagesPattern(t *testing.T) {
-
-	tt := []struct {
-		packet *packets.PublishPacket
-		filter string
-		len    int
-	}{
-		{
-			&packets.PublishPacket{TopicName: "a/b/c/d", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"a/b/c/d",
-			1,
-		},
-		{
-			&packets.PublishPacket{TopicName: "a/b/c/e", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"a/+/c/+",
-			2,
-		},
-		{
-			&packets.PublishPacket{TopicName: "a/b/d/f", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"+/+/+/+",
-			3,
-		},
-		{
-			&packets.PublishPacket{TopicName: "q/w/e/r/t/y", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"q/w/e/#",
-			1,
-		},
-		{
-			&packets.PublishPacket{TopicName: "q/w/x/r/t/x", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"q/#",
-			2,
-		},
-		{
-			&packets.PublishPacket{TopicName: "asd", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"asd",
-			1,
-		},
-		{
-			&packets.PublishPacket{TopicName: "asd/fgh/jkl", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"#",
-			8,
-		},
-		{
-			&packets.PublishPacket{TopicName: "stuff/asdadsa/dsfdsafdsadfsa/dsfdsf/sdsadas", Payload: []byte{'h', 'e', 'l', 'l', 'o'}},
-			"stuff/#/things", // indexer will ignore trailing /things
-			1,
-		},
-	}
-	index := New()
-
-	for _, check := range tt {
-		index.RetainMessage(check.packet)
-	}
-
-	for i, check := range tt {
-		messages := index.Messages(check.filter)
-		require.Equal(t, check.len, len(messages), "Unexpected messages len at %d %s %s", i, check.filter, check.packet.TopicName)
-	}
-}
-
-func TestMessagesFind(t *testing.T) {
-	index := New()
-	index.RetainMessage(&packets.PublishPacket{TopicName: "a/a", Payload: []byte{'a'}})
-	index.RetainMessage(&packets.PublishPacket{TopicName: "a/b", Payload: []byte{'b'}})
-	messages := index.Messages("a/a")
-	require.Equal(t, 1, len(messages))
-
-	messages = index.Messages("a/+")
-	require.Equal(t, 2, len(messages))
-}
-
-func BenchmarkMessages(b *testing.B) {
-	index := New()
-	index.RetainMessage(&packets.PublishPacket{TopicName: "path/to/my/mqtt"})
-	index.RetainMessage(&packets.PublishPacket{TopicName: "path/to/another/mqtt"})
-	index.RetainMessage(&packets.PublishPacket{TopicName: "path/a/some/mqtt"})
-	index.RetainMessage(&packets.PublishPacket{TopicName: "what/is"})
-	index.RetainMessage(&packets.PublishPacket{TopicName: "q/w/e/r/t/y"})
-
-	for n := 0; n < b.N; n++ {
-		index.Messages("path/to/+/mqtt")
-	}
-}