Repository: bytebeamio/rumqtt (language: rust)

Issue:
Implement v5 support with current `EventLoop`
In the v5 module, we have significantly modified the architecture to solve a certain few problems, we believe this process has bred a new set of problems. Now we seek to undo these problems by re-implementing v5 support with the same old `EventLoop::poll` way of doing things. This issue will be used to track progress and current v5 implementation will be shelved/moved into a separate branch for future reference when we pursue the restructure wholeheartedly.

- [x] Create [restructure branch](https://github.com/bytebeamio/rumqtt/tree/restructure), with current state of v5, freeze it for future reference
- [ ] Rewrite v5 module with new v5 structs from rumqttd(without properties/topic aliasing)
- [ ] Test and verify v5 support as implemented


Discussion / hints:

Fix patch:
diff --git a/rumqttc/examples/async_manual_acks_v5.rs b/rumqttc/examples/async_manual_acks_v5.rs
index dc920333f..f7efdcb9c 100644
--- a/rumqttc/examples/async_manual_acks_v5.rs
+++ b/rumqttc/examples/async_manual_acks_v5.rs
@@ -1,18 +1,20 @@
 #![allow(dead_code, unused_imports)]
+use rumqttc::v5::mqttbytes::v5::Packet;
+use rumqttc::v5::mqttbytes::QoS;
 use tokio::{task, time};
 
-use rumqttc::v5::{AsyncClient, MqttOptions, Notifier, Packet, QoS};
+use rumqttc::v5::{AsyncClient, Event, EventLoop, MqttOptions};
 use std::error::Error;
 use std::time::Duration;
 
-async fn create_conn() -> (AsyncClient, Notifier) {
+fn create_conn() -> (AsyncClient, EventLoop) {
     let mut mqttoptions = MqttOptions::new("test-1", "localhost", 1883);
     mqttoptions
         .set_keep_alive(Duration::from_secs(5))
         .set_manual_acks(true)
         .set_clean_session(false);
 
-    AsyncClient::connect(mqttoptions, 10).await
+    AsyncClient::new(mqttoptions, 10)
 }
 
 #[tokio::main(worker_threads = 1)]
@@ -21,7 +23,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
     pretty_env_logger::init();
 
     // create mqtt connection with clean_session = false and manual_acks = true
-    let (client, mut notifier) = create_conn().await;
+    let (client, mut eventloop) = create_conn();
 
     // subscribe example topic
     client
@@ -36,17 +38,21 @@ async fn main() -> Result<(), Box<dyn Error>> {
     });
 
     // get subscribed messages without acking
-    for event in notifier.iter() {
+    while let Ok(event) = eventloop.poll().await {
         println!("{:?}", event);
     }
 
     // create new broker connection
-    let (client, mut notifier) = create_conn().await;
+    let (client, mut eventloop) = create_conn();
 
-    for event in notifier.iter() {
+    while let Ok(event) = eventloop.poll().await {
         println!("{:?}", event);
 
-        if let Packet::Publish(publish) = event {
+        if let Event::Incoming(packet) = event {
+            let publish = match *packet {
+                Packet::Publish(publish, _) => publish,
+                _ => continue,
+            };
             // this time we will ack incoming publishes.
             // Its important not to block notifier as this can cause deadlock.
             let c = client.clone();
diff --git a/rumqttc/examples/asyncpubsub_v5.rs b/rumqttc/examples/asyncpubsub_v5.rs
index 75aaad18d..960159bab 100644
--- a/rumqttc/examples/asyncpubsub_v5.rs
+++ b/rumqttc/examples/asyncpubsub_v5.rs
@@ -1,6 +1,7 @@
+use rumqttc::v5::mqttbytes::QoS;
 use tokio::{task, time};
 
-use rumqttc::v5::{AsyncClient, MqttOptions, QoS};
+use rumqttc::v5::{AsyncClient, MqttOptions};
 use std::error::Error;
 use std::time::Duration;
 
@@ -12,13 +13,13 @@ async fn main() -> Result<(), Box<dyn Error>> {
     let mut mqttoptions = MqttOptions::new("test-1", "localhost", 1883);
     mqttoptions.set_keep_alive(Duration::from_secs(5));
 
-    let (client, mut notifier) = AsyncClient::connect(mqttoptions, 10).await;
+    let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10);
     task::spawn(async move {
         requests(client).await;
         time::sleep(Duration::from_secs(3)).await;
     });
 
-    for event in notifier.iter() {
+    while let Ok(event) = eventloop.poll().await {
         println!("{:?}", event);
     }
 
diff --git a/rumqttc/examples/syncpubsub_v5.rs b/rumqttc/examples/syncpubsub_v5.rs
index 62bc7fcd4..862341e2d 100644
--- a/rumqttc/examples/syncpubsub_v5.rs
+++ b/rumqttc/examples/syncpubsub_v5.rs
@@ -1,4 +1,5 @@
-use rumqttc::v5::{Client, LastWill, MqttOptions, QoS};
+use rumqttc::v5::mqttbytes::{LastWill, QoS};
+use rumqttc::v5::{Client, MqttOptions};
 use std::thread;
 use std::time::Duration;
 
@@ -11,7 +12,7 @@ fn main() {
         .set_keep_alive(Duration::from_secs(5))
         .set_last_will(will);
 
-    let (client, mut connection) = Client::connect(mqttoptions, 10);
+    let (client, mut connection) = Client::new(mqttoptions, 10);
     thread::spawn(move || publish(client));
 
     for (i, notification) in connection.iter().enumerate() {
diff --git a/rumqttc/src/eventloop.rs b/rumqttc/src/eventloop.rs
index e80db27bf..b38315621 100644
--- a/rumqttc/src/eventloop.rs
+++ b/rumqttc/src/eventloop.rs
@@ -241,7 +241,7 @@ async fn network_connect(options: &MqttOptions) -> Result<Network, ConnectionErr
         }
         #[cfg(feature = "use-rustls")]
         Transport::Tls(tls_config) => {
-            let socket = tls::tls_connect(options, &tls_config).await?;
+            let socket = tls::tls_connect(&options.broker_addr, options.port, &tls_config).await?;
             Network::new(socket, options.max_incoming_packet_size)
         }
         #[cfg(unix)]
diff --git a/rumqttc/src/tls.rs b/rumqttc/src/tls.rs
index 0d453f6fb..cfd45b1b3 100644
--- a/rumqttc/src/tls.rs
+++ b/rumqttc/src/tls.rs
@@ -7,7 +7,7 @@ use tokio_rustls::rustls::{
 use tokio_rustls::webpki;
 use tokio_rustls::{client::TlsStream, TlsConnector};
 
-use crate::{Key, MqttOptions, TlsConfiguration};
+use crate::{Key, TlsConfiguration};
 
 use std::convert::TryFrom;
 use std::io;
@@ -124,11 +124,10 @@ pub async fn tls_connector(tls_config: &TlsConfiguration) -> Result<TlsConnector
 }
 
 pub async fn tls_connect(
-    options: &MqttOptions,
+    addr: &str,
+    port: u16,
     tls_config: &TlsConfiguration,
 ) -> Result<TlsStream<TcpStream>, Error> {
-    let addr = options.broker_addr.as_str();
-    let port = options.port;
     let connector = tls_connector(tls_config).await?;
     let domain = ServerName::try_from(addr)?;
     let tcp = TcpStream::connect((addr, port)).await?;
diff --git a/rumqttc/src/v5/client.rs b/rumqttc/src/v5/client.rs
new file mode 100644
index 000000000..a2cadfb9b
--- /dev/null
+++ b/rumqttc/src/v5/client.rs
@@ -0,0 +1,488 @@
+//! This module offers a high level synchronous and asynchronous abstraction to
+//! async eventloop.
+use std::time::Duration;
+
+use super::mqttbytes::{Filter, PubAck, PubRec, Publish, QoS, Subscribe, Unsubscribe};
+use super::{ConnectionError, Event, EventLoop, MqttOptions, Request};
+use crate::valid_topic;
+
+use bytes::Bytes;
+use flume::{SendError, Sender, TrySendError};
+use futures::FutureExt;
+use tokio::runtime::{self, Runtime};
+use tokio::time::timeout;
+
+/// Client Error
+#[derive(Debug, thiserror::Error)]
+pub enum ClientError {
+    #[error("Failed to send mqtt requests to eventloop")]
+    Request(Request),
+    #[error("Failed to send mqtt requests to eventloop")]
+    TryRequest(Request),
+}
+
+impl From<SendError<Request>> for ClientError {
+    fn from(e: SendError<Request>) -> Self {
+        Self::Request(e.into_inner())
+    }
+}
+
+impl From<TrySendError<Request>> for ClientError {
+    fn from(e: TrySendError<Request>) -> Self {
+        Self::TryRequest(e.into_inner())
+    }
+}
+
+/// An asynchronous client, communicates with MQTT `EventLoop`.
+///
+/// This is cloneable and can be used to asynchronously [`publish`](`AsyncClient::publish`),
+/// [`subscribe`](`AsyncClient::subscribe`) through the `EventLoop`, which is to be polled parallelly.
+///
+/// **NOTE**: The `EventLoop` must be regularly polled in order to send, receive and process packets
+/// from the broker, i.e. move ahead.
+#[derive(Clone, Debug)]
+pub struct AsyncClient {
+    request_tx: Sender<Request>,
+}
+
+impl AsyncClient {
+    /// Create a new `AsyncClient`.
+    ///
+    /// `cap` specifies the capacity of the bounded async channel.
+    pub fn new(options: MqttOptions, cap: usize) -> (AsyncClient, EventLoop) {
+        let eventloop = EventLoop::new(options, cap);
+        let request_tx = eventloop.requests_tx.clone();
+
+        let client = AsyncClient { request_tx };
+
+        (client, eventloop)
+    }
+
+    /// Create a new `AsyncClient` from a pair of async channel `Sender`s. This is mostly useful for
+    /// creating a test instance.
+    pub fn from_senders(request_tx: Sender<Request>) -> AsyncClient {
+        AsyncClient { request_tx }
+    }
+
+    /// Sends a MQTT Publish to the `EventLoop`.
+    pub async fn publish<S, P>(
+        &self,
+        topic: S,
+        qos: QoS,
+        retain: bool,
+        payload: P,
+    ) -> Result<(), ClientError>
+    where
+        S: Into<String>,
+        P: Into<Bytes>,
+    {
+        let topic = topic.into();
+        let mut publish = Publish::new(&topic, qos, payload);
+        publish.retain = retain;
+        let publish = Request::Publish(publish);
+        if !valid_topic(&topic) {
+            return Err(ClientError::Request(publish));
+        }
+        self.request_tx.send_async(publish).await?;
+        Ok(())
+    }
+
+    /// Attempts to send a MQTT Publish to the `EventLoop`.
+    pub fn try_publish<S, P>(
+        &self,
+        topic: S,
+        qos: QoS,
+        retain: bool,
+        payload: P,
+    ) -> Result<(), ClientError>
+    where
+        S: Into<String>,
+        P: Into<Bytes>,
+    {
+        let topic = topic.into();
+        let mut publish = Publish::new(&topic, qos, payload);
+        publish.retain = retain;
+        let publish = Request::Publish(publish);
+        if !valid_topic(&topic) {
+            return Err(ClientError::TryRequest(publish));
+        }
+        self.request_tx.try_send(publish)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT PubAck to the `EventLoop`. Only needed in if `manual_acks` flag is set.
+    pub async fn ack(&self, publish: &Publish) -> Result<(), ClientError> {
+        let ack = get_ack_req(publish);
+
+        if let Some(ack) = ack {
+            self.request_tx.send_async(ack).await?;
+        }
+        Ok(())
+    }
+
+    /// Attempts to send a MQTT PubAck to the `EventLoop`. Only needed in if `manual_acks` flag is set.
+    pub fn try_ack(&self, publish: &Publish) -> Result<(), ClientError> {
+        let ack = get_ack_req(publish);
+        if let Some(ack) = ack {
+            self.request_tx.try_send(ack)?;
+        }
+        Ok(())
+    }
+
+    /// Sends a MQTT Publish to the `EventLoop`
+    pub async fn publish_bytes<S>(
+        &self,
+        topic: S,
+        qos: QoS,
+        retain: bool,
+        payload: Bytes,
+    ) -> Result<(), ClientError>
+    where
+        S: Into<String>,
+    {
+        let topic = topic.into();
+        let mut publish = Publish::new(&topic, qos, payload);
+        publish.retain = retain;
+        let publish = Request::Publish(publish);
+        if !valid_topic(&topic) {
+            return Err(ClientError::TryRequest(publish));
+        }
+        self.request_tx.send_async(publish).await?;
+        Ok(())
+    }
+
+    /// Sends a MQTT Subscribe to the `EventLoop`
+    pub async fn subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<(), ClientError> {
+        let filter = Filter::new(topic, qos);
+        let subscribe = Subscribe::new(filter);
+        let request = Request::Subscribe(subscribe);
+        self.request_tx.send_async(request).await?;
+        Ok(())
+    }
+
+    /// Attempts to send a MQTT Subscribe to the `EventLoop`
+    pub fn try_subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<(), ClientError> {
+        let filter = Filter::new(topic, qos);
+        let subscribe = Subscribe::new(filter);
+        let request = Request::Subscribe(subscribe);
+        self.request_tx.try_send(request)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT Subscribe for multiple topics to the `EventLoop`
+    pub async fn subscribe_many<T>(&self, topics: T) -> Result<(), ClientError>
+    where
+        T: IntoIterator<Item = Filter>,
+    {
+        let subscribe = Subscribe::new_many(topics);
+        let request = Request::Subscribe(subscribe);
+        self.request_tx.send_async(request).await?;
+        Ok(())
+    }
+
+    /// Attempts to send a MQTT Subscribe for multiple topics to the `EventLoop`
+    pub fn try_subscribe_many<T>(&self, topics: T) -> Result<(), ClientError>
+    where
+        T: IntoIterator<Item = Filter>,
+    {
+        let subscribe = Subscribe::new_many(topics);
+        let request = Request::Subscribe(subscribe);
+        self.request_tx.try_send(request)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT Unsubscribe to the `EventLoop`
+    pub async fn unsubscribe<S: Into<String>>(&self, topic: S) -> Result<(), ClientError> {
+        let unsubscribe = Unsubscribe::new(topic);
+        let request = Request::Unsubscribe(unsubscribe);
+        self.request_tx.send_async(request).await?;
+        Ok(())
+    }
+
+    /// Attempts to send a MQTT Unsubscribe to the `EventLoop`
+    pub fn try_unsubscribe<S: Into<String>>(&self, topic: S) -> Result<(), ClientError> {
+        let unsubscribe = Unsubscribe::new(topic);
+        let request = Request::Unsubscribe(unsubscribe);
+        self.request_tx.try_send(request)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT disconnect to the `EventLoop`
+    pub async fn disconnect(&self) -> Result<(), ClientError> {
+        let request = Request::Disconnect;
+        self.request_tx.send_async(request).await?;
+        Ok(())
+    }
+
+    /// Attempts to send a MQTT disconnect to the `EventLoop`
+    pub fn try_disconnect(&self) -> Result<(), ClientError> {
+        let request = Request::Disconnect;
+        self.request_tx.try_send(request)?;
+        Ok(())
+    }
+}
+
+fn get_ack_req(publish: &Publish) -> Option<Request> {
+    let ack = match publish.qos {
+        QoS::AtMostOnce => return None,
+        QoS::AtLeastOnce => Request::PubAck(PubAck::new(publish.pkid)),
+        QoS::ExactlyOnce => Request::PubRec(PubRec::new(publish.pkid)),
+    };
+    Some(ack)
+}
+
+/// A synchronous client, communicates with MQTT `EventLoop`.
+///
+/// This is cloneable and can be used to synchronously [`publish`](`AsyncClient::publish`),
+/// [`subscribe`](`AsyncClient::subscribe`) through the `EventLoop`/`Connection`, which is to be polled in parallel
+/// by iterating over the object returned by [`Connection.iter()`](Connection::iter) in a separate thread.
+///
+/// **NOTE**: The `EventLoop`/`Connection` must be regularly polled(`.next()` in case of `Connection`) in order
+/// to send, receive and process packets from the broker, i.e. move ahead.
+///
+/// An asynchronous channel handle can also be extracted if necessary.
+#[derive(Clone)]
+pub struct Client {
+    client: AsyncClient,
+}
+
+impl Client {
+    /// Create a new `Client`
+    ///
+    /// `cap` specifies the capacity of the bounded async channel.
+    pub fn new(options: MqttOptions, cap: usize) -> (Client, Connection) {
+        let (client, eventloop) = AsyncClient::new(options, cap);
+        let client = Client { client };
+        let runtime = runtime::Builder::new_current_thread()
+            .enable_all()
+            .build()
+            .unwrap();
+
+        let connection = Connection::new(eventloop, runtime);
+        (client, connection)
+    }
+
+    /// Sends a MQTT Publish to the `EventLoop`
+    pub fn publish<S, P>(
+        &self,
+        topic: S,
+        qos: QoS,
+        retain: bool,
+        payload: P,
+    ) -> Result<(), ClientError>
+    where
+        S: Into<String>,
+        P: Into<Bytes>,
+    {
+        pollster::block_on(self.client.publish(topic, qos, retain, payload))?;
+        Ok(())
+    }
+
+    pub fn try_publish<S, P>(
+        &self,
+        topic: S,
+        qos: QoS,
+        retain: bool,
+        payload: P,
+    ) -> Result<(), ClientError>
+    where
+        S: Into<String>,
+        P: Into<Bytes>,
+    {
+        self.client.try_publish(topic, qos, retain, payload)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT PubAck to the `EventLoop`. Only needed in if `manual_acks` flag is set.
+    pub fn ack(&self, publish: &Publish) -> Result<(), ClientError> {
+        pollster::block_on(self.client.ack(publish))?;
+        Ok(())
+    }
+
+    /// Sends a MQTT PubAck to the `EventLoop`. Only needed in if `manual_acks` flag is set.
+    pub fn try_ack(&self, publish: &Publish) -> Result<(), ClientError> {
+        self.client.try_ack(publish)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT Subscribe to the `EventLoop`
+    pub fn subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<(), ClientError> {
+        pollster::block_on(self.client.subscribe(topic, qos))?;
+        Ok(())
+    }
+
+    /// Sends a MQTT Subscribe to the `EventLoop`
+    pub fn try_subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<(), ClientError> {
+        self.client.try_subscribe(topic, qos)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT Subscribe for multiple topics to the `EventLoop`
+    pub fn subscribe_many<T>(&self, topics: T) -> Result<(), ClientError>
+    where
+        T: IntoIterator<Item = Filter>,
+    {
+        pollster::block_on(self.client.subscribe_many(topics))
+    }
+
+    pub fn try_subscribe_many<T>(&self, topics: T) -> Result<(), ClientError>
+    where
+        T: IntoIterator<Item = Filter>,
+    {
+        self.client.try_subscribe_many(topics)
+    }
+
+    /// Sends a MQTT Unsubscribe to the `EventLoop`
+    pub fn unsubscribe<S: Into<String>>(&self, topic: S) -> Result<(), ClientError> {
+        pollster::block_on(self.client.unsubscribe(topic))?;
+        Ok(())
+    }
+
+    /// Sends a MQTT Unsubscribe to the `EventLoop`
+    pub fn try_unsubscribe<S: Into<String>>(&self, topic: S) -> Result<(), ClientError> {
+        self.client.try_unsubscribe(topic)?;
+        Ok(())
+    }
+
+    /// Sends a MQTT disconnect to the `EventLoop`
+    pub fn disconnect(&self) -> Result<(), ClientError> {
+        pollster::block_on(self.client.disconnect())?;
+        Ok(())
+    }
+
+    /// Sends a MQTT disconnect to the `EventLoop`
+    pub fn try_disconnect(&self) -> Result<(), ClientError> {
+        self.client.try_disconnect()?;
+        Ok(())
+    }
+}
+
+/// Error type returned by [`Connection::recv`]
+#[derive(Debug, Eq, PartialEq)]
+pub struct RecvError;
+
+/// Error type returned by [`Connection::try_recv`]
+#[derive(Debug, Eq, PartialEq)]
+pub enum TryRecvError {
+    /// User has closed requests channel
+    Disconnected,
+    /// Did not resolve
+    Empty,
+}
+
+/// Error type returned by [`Connection::recv_timeout`]
+#[derive(Debug, Eq, PartialEq)]
+pub enum RecvTimeoutError {
+    /// User has closed requests channel
+    Disconnected,
+    /// Recv request timedout
+    Timeout,
+}
+
+///  MQTT connection. Maintains all the necessary state
+pub struct Connection {
+    pub eventloop: EventLoop,
+    runtime: Runtime,
+}
+impl Connection {
+    fn new(eventloop: EventLoop, runtime: Runtime) -> Connection {
+        Connection { eventloop, runtime }
+    }
+
+    /// Returns an iterator over this connection. Iterating over this is all that's
+    /// necessary to make connection progress and maintain a robust connection.
+    /// Just continuing to loop will reconnect
+    /// **NOTE** Don't block this while iterating
+    // ideally this should be named iter_mut because it requires a mutable reference
+    // Also we can implement IntoIter for this to make it easy to iterate over it
+    #[must_use = "Connection should be iterated over a loop to make progress"]
+    pub fn iter(&mut self) -> Iter<'_> {
+        Iter { connection: self }
+    }
+
+    /// Attempt to fetch an incoming [`Event`] on the [`EvenLoop`], returning an error
+    /// if all clients/users have closed requests channel.
+    ///
+    /// [`EvenLoop`]: super::EventLoop
+    pub fn recv(&mut self) -> Result<Result<Event, ConnectionError>, RecvError> {
+        let f = self.eventloop.poll();
+        let event = self.runtime.block_on(f);
+
+        resolve_event(event).ok_or(RecvError)
+    }
+
+    /// Attempt to fetch an incoming [`Event`] on the [`EvenLoop`], returning an error
+    /// if none immediately present or all clients/users have closed requests channel.
+    ///
+    /// [`EvenLoop`]: super::EventLoop
+    pub fn try_recv(&mut self) -> Result<Result<Event, ConnectionError>, TryRecvError> {
+        let f = self.eventloop.poll();
+        let event = f.now_or_never().ok_or(TryRecvError::Empty)?;
+
+        resolve_event(event).ok_or(TryRecvError::Disconnected)
+    }
+
+    /// Attempt to fetch an incoming [`Event`] on the [`EvenLoop`], returning an error
+    /// if all clients/users have closed requests channel or the timeout has expired.
+    ///
+    /// [`EvenLoop`]: super::EventLoop
+    pub fn recv_timeout(
+        &mut self,
+        duration: Duration,
+    ) -> Result<Result<Event, ConnectionError>, RecvTimeoutError> {
+        let f = timeout(duration, self.eventloop.poll());
+        let event = self
+            .runtime
+            .block_on(f)
+            .map_err(|_| RecvTimeoutError::Timeout)?;
+
+        resolve_event(event).ok_or(RecvTimeoutError::Disconnected)
+    }
+}
+
+fn resolve_event(event: Result<Event, ConnectionError>) -> Option<Result<Event, ConnectionError>> {
+    match event {
+        Ok(v) => Some(Ok(v)),
+        // closing of request channel should stop the iterator
+        Err(ConnectionError::RequestsDone) => {
+            trace!("Done with requests");
+            None
+        }
+        Err(e) => Some(Err(e)),
+    }
+}
+
+/// Iterator which polls the `EventLoop` for connection progress
+pub struct Iter<'a> {
+    connection: &'a mut Connection,
+}
+
+impl Iterator for Iter<'_> {
+    type Item = Result<Event, ConnectionError>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.connection.recv().ok()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::v5::mqttbytes::LastWill;
+
+    use super::*;
+
+    #[test]
+    fn calling_iter_twice_on_connection_shouldnt_panic() {
+        use std::time::Duration;
+
+        let mut mqttoptions = MqttOptions::new("test-1", "localhost", 1883);
+        let will = LastWill::new("hello/world", "good bye", QoS::AtMostOnce, false);
+        mqttoptions
+            .set_keep_alive(Duration::from_secs(5))
+            .set_last_will(will);
+
+        let (_, mut connection) = Client::new(mqttoptions, 10);
+        let _ = connection.iter();
+        let _ = connection.iter();
+    }
+}
diff --git a/rumqttc/src/v5/client/asyncclient.rs b/rumqttc/src/v5/client/asyncclient.rs
deleted file mode 100644
index 0ce24ceb1..000000000
--- a/rumqttc/src/v5/client/asyncclient.rs
+++ /dev/null
@@ -1,402 +0,0 @@
-use std::{
-    collections::VecDeque,
-    sync::{Arc, Mutex},
-};
-
-use bytes::Bytes;
-use flume::{SendError, Sender, TrySendError};
-
-use crate::v5::{
-    client::get_ack_req,
-    outgoing_buf::OutgoingBuf,
-    packet::{Publish, Subscribe, SubscribeFilter, Unsubscribe},
-    ClientError, ConnectionError, EventLoop, MqttOptions, Notifier, QoS, Request,
-    SubscribeProperties, UnsubscribeProperties,
-};
-
-/// `AsyncClient` to communicate with an MQTT `Eventloop`
-/// This is cloneable and can be used to asynchronously Publish, Subscribe.
-#[derive(Clone, Debug)]
-pub struct AsyncClient {
-    pub(crate) outgoing_buf: Arc<Mutex<OutgoingBuf>>,
-    pub(crate) request_tx: Sender<()>,
-}
-
-impl AsyncClient {
-    /// Create a new `AsyncClient`
-    pub fn new(options: MqttOptions, cap: usize) -> (AsyncClient, EventLoop) {
-        let eventloop = EventLoop::new(options, cap);
-        let outgoing_buf = eventloop.state.outgoing_buf.clone();
-        let request_tx = eventloop.handle();
-
-        let client = AsyncClient {
-            outgoing_buf,
-            request_tx,
-        };
-
-        (client, eventloop)
-    }
-
-    /// Create an `AsyncClient` and it's associated [`Notifier`]
-    pub async fn connect(options: MqttOptions, cap: usize) -> (AsyncClient, Notifier) {
-        let (client, mut eventloop) = AsyncClient::new(options, cap);
-        let incoming_buf = eventloop.state.incoming_buf.clone();
-        let disconnected = eventloop.state.disconnected.clone();
-        let incoming_buf_cache = VecDeque::with_capacity(cap);
-        let notifier = Notifier::new(incoming_buf, incoming_buf_cache, disconnected);
-
-        tokio::spawn(async move {
-            loop {
-                // TODO: maybe do something like retries for some specific errors? or maybe give user
-                // options to configure these retries?
-                let event = eventloop.poll().await;
-                if let Err(e) = &event {
-                    println!("{}", e);
-                }
-
-                if let Err(ConnectionError::Disconnected) = event {
-                    break;
-                }
-            }
-        });
-
-        (client, notifier)
-    }
-
-    fn make_request(&self, mut request: Request, increment: bool) -> Result<u16, ClientError> {
-        let mut request_buf = self.outgoing_buf.lock().unwrap();
-        if request_buf.buf.len() == request_buf.capacity {
-            return Err(ClientError::RequestsFull);
-        }
-
-        let pkid = if increment {
-            let pkid = request_buf.increment_pkid();
-            set_pkid(&mut request, pkid);
-            pkid
-        } else {
-            0
-        };
-        request_buf.buf.push_back(request);
-
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Publish to the eventloop
-    pub async fn publish<S, V>(
-        &self,
-        topic: S,
-        qos: QoS,
-        retain: bool,
-        payload: V,
-    ) -> Result<u16, ClientError>
-    where
-        S: Into<String>,
-        V: Into<Vec<u8>>,
-    {
-        let mut publish = Publish::new(topic, qos, payload);
-        publish.retain = retain;
-        let pkid = self.make_request(Request::Publish(publish), qos != QoS::AtMostOnce)?;
-        self.notify_async().await?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Publish to the eventloop
-    pub fn try_publish<S, V>(
-        &self,
-        topic: S,
-        qos: QoS,
-        retain: bool,
-        payload: V,
-    ) -> Result<u16, ClientError>
-    where
-        S: Into<String>,
-        V: Into<Vec<u8>>,
-    {
-        let mut publish = Publish::new(topic, qos, payload);
-        publish.retain = retain;
-        let pkid = self.make_request(Request::Publish(publish), qos != QoS::AtMostOnce)?;
-        self.try_notify()?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT PubAck to the eventloop. Only needed in if `manual_acks` flag is set.
-    pub async fn ack(&self, publish: &Publish) -> Result<(), ClientError> {
-        if let Some(ack) = get_ack_req(publish.qos, publish.pkid) {
-            self.make_request(ack, false)?;
-            self.notify_async().await?;
-        }
-        Ok(())
-    }
-
-    /// Sends a MQTT PubAck to the eventloop. Only needed in if `manual_acks` flag is set.
-    pub fn try_ack(&self, publish: &Publish) -> Result<(), ClientError> {
-        if let Some(ack) = get_ack_req(publish.qos, publish.pkid) {
-            self.make_request(ack, false)?;
-            self.try_notify()?;
-        }
-        Ok(())
-    }
-
-    /// Sends a MQTT Publish to the eventloop
-    pub async fn publish_bytes<S>(
-        &self,
-        topic: S,
-        qos: QoS,
-        retain: bool,
-        payload: Bytes,
-    ) -> Result<u16, ClientError>
-    where
-        S: Into<String>,
-    {
-        let mut publish = Publish::from_bytes(topic, qos, payload);
-        publish.retain = retain;
-        let pkid = self.make_request(Request::Publish(publish), qos != QoS::AtMostOnce)?;
-        self.notify_async().await?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Subscribe to the eventloop
-    pub async fn subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<u16, ClientError> {
-        self.subscribe_with(topic, qos, None).await
-    }
-
-    /// Sends a MQTT Subscribe to the eventloop with options
-    pub async fn subscribe_with<S: Into<String>, P: Into<Option<SubscribeProperties>>>(
-        &self,
-        topic: S,
-        qos: QoS,
-        props: P,
-    ) -> Result<u16, ClientError> {
-        let mut subscribe = Subscribe::new(topic.into(), qos);
-        subscribe.properties = props.into();
-        let pkid = {
-            let mut request_buf = self.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            subscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Subscribe(subscribe));
-            pkid
-        };
-        self.notify_async().await?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Subscribe to the eventloop
-    pub fn try_subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<u16, ClientError> {
-        self.try_subscribe_with(topic, qos, None)
-    }
-
-    /// Sends a MQTT Subscribe to the eventloop
-    pub fn try_subscribe_with<S: Into<String>, P: Into<Option<SubscribeProperties>>>(
-        &self,
-        topic: S,
-        qos: QoS,
-        props: P,
-    ) -> Result<u16, ClientError> {
-        let mut subscribe = Subscribe::new(topic.into(), qos);
-        subscribe.properties = props.into();
-        let pkid = {
-            let mut request_buf = self.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            subscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Subscribe(subscribe));
-            pkid
-        };
-        self.try_notify()?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Subscribe for multiple topics to the eventloop
-    pub async fn subscribe_many<T>(&self, topics: T) -> Result<u16, ClientError>
-    where
-        T: IntoIterator<Item = SubscribeFilter>,
-    {
-        self.subscribe_with_many(None, topics).await
-    }
-
-    /// Sends a MQTT Subscribe for multiple topics to the eventloop, with properties
-    pub async fn subscribe_with_many<P: Into<Option<SubscribeProperties>>, T>(
-        &self,
-        props: P,
-        topics: T,
-    ) -> Result<u16, ClientError>
-    where
-        T: IntoIterator<Item = SubscribeFilter>,
-    {
-        let mut subscribe = Subscribe::new_many(topics)?;
-        subscribe.properties = props.into();
-        let pkid = {
-            let mut request_buf = self.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            subscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Subscribe(subscribe));
-            pkid
-        };
-        self.notify_async().await?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Subscribe for multiple topics to the eventloop
-    pub fn try_subscribe_many<T>(&self, topics: T) -> Result<u16, ClientError>
-    where
-        T: IntoIterator<Item = SubscribeFilter>,
-    {
-        self.try_subscribe_with_many(None, topics)
-    }
-
-    /// Sends a MQTT Subscribe for multiple topics to the eventloop
-    pub fn try_subscribe_with_many<P, T>(&self, props: P, topics: T) -> Result<u16, ClientError>
-    where
-        P: Into<Option<SubscribeProperties>>,
-        T: IntoIterator<Item = SubscribeFilter>,
-    {
-        let mut subscribe = Subscribe::new_many(topics)?;
-        subscribe.properties = props.into();
-        let pkid = {
-            let mut request_buf = self.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            subscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Subscribe(subscribe));
-            pkid
-        };
-        self.try_notify()?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Unsubscribe to the eventloop
-    pub async fn unsubscribe<S: Into<String>>(&self, topic: S) -> Result<u16, ClientError> {
-        self.unsubscribe_with(topic, None).await
-    }
-
-    /// Sends a MQTT Unsubscribe to the eventloop, with properties
-    pub async fn unsubscribe_with<S: Into<String>, P: Into<Option<UnsubscribeProperties>>>(
-        &self,
-        topic: S,
-        props: P,
-    ) -> Result<u16, ClientError> {
-        let mut unsubscribe = Unsubscribe::new(topic.into());
-        unsubscribe.properties = props.into();
-        let pkid = {
-            let mut request_buf = self.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            unsubscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Unsubscribe(unsubscribe));
-            pkid
-        };
-        self.notify_async().await?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Unsubscribe to the eventloop
-    pub fn try_unsubscribe<S: Into<String>>(&self, topic: S) -> Result<u16, ClientError> {
-        self.try_unsubscribe_with(topic, None)
-    }
-
-    /// Sends a MQTT Unsubscribe to the eventloop, with properties
-    pub fn try_unsubscribe_with<S: Into<String>, P: Into<Option<UnsubscribeProperties>>>(
-        &self,
-        topic: S,
-        props: P,
-    ) -> Result<u16, ClientError> {
-        let mut unsubscribe = Unsubscribe::new(topic.into());
-        unsubscribe.properties = props.into();
-        let pkid = {
-            let mut request_buf = self.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            unsubscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Unsubscribe(unsubscribe));
-            pkid
-        };
-        self.try_notify()?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT disconnect to the eventloop
-    #[inline]
-    pub async fn disconnect(&self) -> Result<(), ClientError> {
-        self.make_request(Request::Disconnect, false)?;
-        self.notify_async().await
-    }
-
-    /// Sends a MQTT disconnect to the eventloop
-    #[inline]
-    pub fn try_disconnect(&self) -> Result<(), ClientError> {
-        self.make_request(Request::Disconnect, false)?;
-        self.try_notify()
-    }
-
-    #[inline]
-    async fn notify_async(&self) -> Result<(), ClientError> {
-        if let Err(SendError(_)) = self.request_tx.send_async(()).await {
-            return Err(ClientError::EventloopClosed);
-        };
-        Ok(())
-    }
-
-    #[inline]
-    pub(crate) fn notify(&self) -> Result<(), ClientError> {
-        if let Err(SendError(_)) = self.request_tx.send(()) {
-            return Err(ClientError::EventloopClosed);
-        };
-        Ok(())
-    }
-
-    #[inline]
-    fn try_notify(&self) -> Result<(), ClientError> {
-        if let Err(TrySendError::Disconnected(_)) = self.request_tx.try_send(()) {
-            return Err(ClientError::EventloopClosed);
-        }
-        Ok(())
-    }
-}
-
-#[inline]
-pub fn set_pkid(request: &mut Request, pkid: u16) {
-    match request {
-        Request::Publish(p) => {
-            p.pkid = pkid;
-        }
-        Request::PubAck(p) => {
-            p.pkid = pkid;
-        }
-        Request::PubRec(p) => {
-            p.pkid = pkid;
-        }
-        Request::PubComp(p) => {
-            p.pkid = pkid;
-        }
-        Request::PubRel(p) => {
-            p.pkid = pkid;
-        }
-        Request::Subscribe(p) => {
-            p.pkid = pkid;
-        }
-        Request::SubAck(p) => {
-            p.pkid = pkid;
-        }
-        Request::Unsubscribe(p) => {
-            p.pkid = pkid;
-        }
-        Request::UnsubAck(p) => {
-            p.pkid = pkid;
-        }
-        _ => {}
-    }
-}
diff --git a/rumqttc/src/v5/client/mod.rs b/rumqttc/src/v5/client/mod.rs
deleted file mode 100644
index 661f26905..000000000
--- a/rumqttc/src/v5/client/mod.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-//! This module offers a high level synchronous and asynchronous abstraction to
-//! async eventloop.
-use crate::v5::{packet::*, ConnectionError, EventLoop, Request};
-
-use std::mem;
-use tokio::runtime::{self, Runtime};
-
-mod asyncclient;
-pub use asyncclient::AsyncClient;
-mod syncclient;
-pub use syncclient::Client;
-
-/// Client Error
-#[derive(Debug, thiserror::Error)]
-pub enum ClientError {
-    #[error("Failed to send mqtt request to eventloop, the evenloop has been closed")]
-    EventloopClosed,
-    #[error("Failed to send mqtt request to evenloop, to requests buffer is full right now")]
-    RequestsFull,
-    #[error("Serialization error")]
-    Mqtt5(#[from] Error),
-}
-
-fn get_ack_req(qos: QoS, pkid: u16) -> Option<Request> {
-    let ack = match qos {
-        QoS::AtMostOnce => return None,
-        QoS::AtLeastOnce => Request::PubAck(PubAck::new(pkid)),
-        QoS::ExactlyOnce => Request::PubRec(PubRec::new(pkid)),
-    };
-    Some(ack)
-}
-
-///  MQTT connection. Maintains all the necessary state
-pub struct Connection {
-    pub eventloop: EventLoop,
-    runtime: Option<Runtime>,
-}
-
-impl Connection {
-    fn new(eventloop: EventLoop, runtime: Runtime) -> Connection {
-        Connection {
-            eventloop,
-            runtime: Some(runtime),
-        }
-    }
-
-    /// Returns an iterator over this connection. Iterating over this is all that's
-    /// necessary to make connection progress and maintain a robust connection.
-    /// Just continuing to loop will reconnect
-    /// **NOTE** Don't block this while iterating
-    #[must_use = "Connection should be iterated over a loop to make progress"]
-    pub fn iter(&mut self) -> Iter {
-        let runtime = self.runtime.take().unwrap();
-        Iter {
-            connection: self,
-            runtime,
-        }
-    }
-}
-
-/// Iterator which polls the eventloop for connection progress
-pub struct Iter<'a> {
-    connection: &'a mut Connection,
-    runtime: runtime::Runtime,
-}
-
-impl<'a> Iterator for Iter<'a> {
-    type Item = Result<(), ConnectionError>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let f = self.connection.eventloop.poll();
-        match self.runtime.block_on(f) {
-            Ok(_) => Some(Ok(())),
-            // closing of request channel should stop the iterator
-            Err(ConnectionError::RequestsDone) => {
-                trace!("Done with requests");
-                None
-            }
-            Err(e) => Some(Err(e)),
-        }
-    }
-}
-
-impl<'a> Drop for Iter<'a> {
-    fn drop(&mut self) {
-        // TODO: Don't create new runtime in drop
-        let runtime = runtime::Builder::new_current_thread().build().unwrap();
-        self.connection.runtime = Some(mem::replace(&mut self.runtime, runtime));
-    }
-}
diff --git a/rumqttc/src/v5/client/syncclient.rs b/rumqttc/src/v5/client/syncclient.rs
deleted file mode 100644
index 12f143717..000000000
--- a/rumqttc/src/v5/client/syncclient.rs
+++ /dev/null
@@ -1,207 +0,0 @@
-use std::collections::VecDeque;
-
-use tokio::runtime;
-
-use crate::v5::{
-    client::get_ack_req,
-    packet::{Publish, Subscribe, SubscribeFilter, Unsubscribe},
-    AsyncClient, ClientError, Connection, ConnectionError, MqttOptions, Notifier, QoS, Request,
-};
-
-/// `Client` to communicate with MQTT eventloop `Connection`.
-///
-/// Client is cloneable and can be used to synchronously Publish, Subscribe.
-/// Asynchronous channel handle can also be extracted if necessary
-pub struct Client {
-    client: AsyncClient,
-}
-
-impl Client {
-    /// Create a new `Client`
-    pub fn new(options: MqttOptions, cap: usize) -> (Client, Connection) {
-        let (client, eventloop) = AsyncClient::new(options, cap);
-        let client = Client { client };
-        let runtime = runtime::Builder::new_current_thread()
-            .enable_all()
-            .build()
-            .unwrap();
-
-        let connection = Connection::new(eventloop, runtime);
-        (client, connection)
-    }
-
-    /// Create a `Client` and it's associated [`Notifier`]
-    pub fn connect(options: MqttOptions, cap: usize) -> (Client, Notifier) {
-        let (client, mut connection) = Client::new(options, cap);
-        let incoming_buf = connection.eventloop.state.incoming_buf.clone();
-        let disconnected = connection.eventloop.state.disconnected.clone();
-        let incoming_buf_cache = VecDeque::with_capacity(cap);
-        let notifier = Notifier::new(incoming_buf, incoming_buf_cache, disconnected);
-
-        std::thread::spawn(move || {
-            for event in connection.iter() {
-                // TODO: maybe do something like retries for some specific errors? or maybe give user
-                // options to configure these retries?
-                if let Err(e) = &event {
-                    println!("{}", e);
-                }
-
-                if let Err(ConnectionError::Disconnected) = event {
-                    break;
-                }
-            }
-        });
-
-        (client, notifier)
-    }
-
-    /// Sends a MQTT Publish to the eventloop
-    pub fn publish<S, V>(
-        &self,
-        topic: S,
-        qos: QoS,
-        retain: bool,
-        payload: V,
-    ) -> Result<u16, ClientError>
-    where
-        S: Into<String>,
-        V: Into<Vec<u8>>,
-    {
-        let mut publish = Publish::new(topic, qos, payload);
-        publish.retain = retain;
-        let pkid = if qos != QoS::AtMostOnce {
-            let mut request_buf = self.client.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            publish.pkid = pkid;
-            request_buf.buf.push_back(Request::Publish(publish));
-            pkid
-        } else {
-            0
-        };
-        self.client.notify()?;
-        Ok(pkid)
-    }
-
-    pub fn try_publish<S, V>(
-        &self,
-        topic: S,
-        qos: QoS,
-        retain: bool,
-        payload: V,
-    ) -> Result<u16, ClientError>
-    where
-        S: Into<String>,
-        V: Into<Vec<u8>>,
-    {
-        self.client.try_publish(topic, qos, retain, payload)
-    }
-
-    /// Sends a MQTT PubAck to the eventloop. Only needed in if `manual_acks` flag is set.
-    pub fn ack(&self, publish: &Publish) -> Result<(), ClientError> {
-        if let Some(ack) = get_ack_req(publish.qos, publish.pkid) {
-            let mut request_buf = self.client.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            request_buf.buf.push_back(ack);
-            self.client.notify()?;
-        }
-        Ok(())
-    }
-
-    /// Sends a MQTT PubAck to the eventloop. Only needed in if `manual_acks` flag is set.
-    pub fn try_ack(&self, publish: &Publish) -> Result<(), ClientError> {
-        self.client.try_ack(publish)
-    }
-
-    /// Sends a MQTT Subscribe to the eventloop
-    pub fn subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<u16, ClientError> {
-        let mut subscribe = Subscribe::new(topic.into(), qos);
-        let pkid = if qos != QoS::AtMostOnce {
-            let mut request_buf = self.client.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            subscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Subscribe(subscribe));
-            pkid
-        } else {
-            0
-        };
-        self.client.notify()?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Subscribe to the eventloop
-    pub fn try_subscribe<S: Into<String>>(&self, topic: S, qos: QoS) -> Result<u16, ClientError> {
-        self.client.try_subscribe(topic, qos)
-    }
-
-    /// Sends a MQTT Subscribe for multiple topics to the eventloop
-    pub fn subscribe_many<T>(&self, topics: T) -> Result<u16, ClientError>
-    where
-        T: IntoIterator<Item = SubscribeFilter>,
-    {
-        let mut subscribe = Subscribe::new_many(topics)?;
-        let pkid = {
-            let mut request_buf = self.client.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            subscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Subscribe(subscribe));
-            pkid
-        };
-        self.client.notify()?;
-        Ok(pkid)
-    }
-
-    pub fn try_subscribe_many<T>(&self, topics: T) -> Result<u16, ClientError>
-    where
-        T: IntoIterator<Item = SubscribeFilter>,
-    {
-        self.client.try_subscribe_many(topics)
-    }
-
-    /// Sends a MQTT Unsubscribe to the eventloop
-    pub fn unsubscribe<S: Into<String>>(&self, topic: S) -> Result<u16, ClientError> {
-        let mut unsubscribe = Unsubscribe::new(topic.into());
-        let pkid = {
-            let mut request_buf = self.client.outgoing_buf.lock().unwrap();
-            if request_buf.buf.len() == request_buf.capacity {
-                return Err(ClientError::RequestsFull);
-            }
-            let pkid = request_buf.increment_pkid();
-            unsubscribe.pkid = pkid;
-            request_buf.buf.push_back(Request::Unsubscribe(unsubscribe));
-            pkid
-        };
-        self.client.notify()?;
-        Ok(pkid)
-    }
-
-    /// Sends a MQTT Unsubscribe to the eventloop
-    pub fn try_unsubscribe<S: Into<String>>(&self, topic: S) -> Result<u16, ClientError> {
-        self.client.try_unsubscribe(topic)
-    }
-
-    /// Sends a MQTT disconnect to the eventloop
-    pub fn disconnect(&self) -> Result<(), ClientError> {
-        let mut request_buf = self.client.outgoing_buf.lock().unwrap();
-        if request_buf.buf.len() == request_buf.capacity {
-            return Err(ClientError::RequestsFull);
-        }
-        request_buf.buf.push_back(Request::Disconnect);
-        self.client.notify()
-    }
-
-    /// Sends a MQTT disconnect to the eventloop
-    pub fn try_disconnect(&self) -> Result<(), ClientError> {
-        self.client.try_disconnect()
-    }
-}
diff --git a/rumqttc/src/v5/eventloop.rs b/rumqttc/src/v5/eventloop.rs
index a52377778..86b0d505f 100644
--- a/rumqttc/src/v5/eventloop.rs
+++ b/rumqttc/src/v5/eventloop.rs
@@ -1,9 +1,8 @@
+use super::framed::Network;
+use super::mqttbytes::{v5::*, *};
+use super::{Incoming, MqttOptions, MqttState, Outgoing, Request, StateError, Transport};
 #[cfg(feature = "use-rustls")]
-use crate::v5::tls;
-use crate::v5::{
-    framed::Network, outgoing_buf::OutgoingBuf, packet::*, Incoming, MqttOptions, MqttState,
-    Packet, Request, StateError, Transport,
-};
+use crate::tls;
 
 #[cfg(feature = "websocket")]
 use async_tungstenite::tokio::connect_async;
@@ -18,16 +17,14 @@ use tokio::time::{self, error::Elapsed, Instant, Sleep};
 #[cfg(feature = "websocket")]
 use ws_stream_tungstenite::WsStream;
 
+use std::io;
 #[cfg(unix)]
 use std::path::Path;
-use std::{
-    collections::VecDeque,
-    io,
-    pin::Pin,
-    sync::{Arc, Mutex},
-    time::Duration,
-    vec::IntoIter,
-};
+use std::pin::Pin;
+use std::time::Duration;
+use std::vec::IntoIter;
+
+use super::mqttbytes::ConnectReturnCode;
 
 /// Critical errors during eventloop polling
 #[derive(Debug, thiserror::Error)]
@@ -36,23 +33,23 @@ pub enum ConnectionError {
     MqttState(#[from] StateError),
     #[error("Timeout")]
     Timeout(#[from] Elapsed),
-    #[error("Packet parsing error: {0}")]
-    Mqtt5Bytes(Error),
+    #[cfg(feature = "websocket")]
+    #[error("Websocket: {0}")]
+    Websocket(#[from] async_tungstenite::tungstenite::error::Error),
+    #[cfg(feature = "websocket")]
+    #[error("Websocket Connect: {0}")]
+    WsConnect(#[from] http::Error),
     #[cfg(feature = "use-rustls")]
-    #[error("Network: {0}")]
-    Network(#[from] tls::Error),
+    #[error("TLS: {0}")]
+    Tls(#[from] tls::Error),
     #[error("I/O: {0}")]
     Io(#[from] io::Error),
     #[error("Connection refused, return code: {0:?}")]
     ConnectionRefused(ConnectReturnCode),
     #[error("Expected ConnAck packet, received: {0:?}")]
     NotConnAck(Box<Packet>),
-    #[error("Stream done")]
-    StreamDone,
     #[error("Requests done")]
     RequestsDone,
-    #[error("MQTT connection has been disconnected")]
-    Disconnected,
 }
 
 /// Eventloop with all the state of a connection
@@ -61,18 +58,23 @@ pub struct EventLoop {
     pub options: MqttOptions,
     /// Current state of the connection
     pub state: MqttState,
-    outgoing_buf: Arc<Mutex<OutgoingBuf>>,
-    outgoing_buf_cache: VecDeque<Request>,
     /// Request stream
-    pub incoming_rx: Receiver<()>,
+    requests_rx: Receiver<Request>,
     /// Requests handle to send requests
-    pub incoming_tx: Sender<()>,
+    pub(crate) requests_tx: Sender<Request>,
     /// Pending packets from last session
     pub pending: IntoIter<Request>,
     /// Network connection to the broker
-    pub(crate) network: Option<Network>,
+    network: Option<Network>,
     /// Keep alive time
-    pub(crate) keepalive_timeout: Option<Pin<Box<Sleep>>>,
+    keepalive_timeout: Option<Pin<Box<Sleep>>>,
+}
+
+/// Events which can be yielded by the event loop
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Event {
+    Incoming(Box<Incoming>),
+    Outgoing(Outgoing),
 }
 
 impl EventLoop {
@@ -81,33 +83,23 @@ impl EventLoop {
     /// When connection encounters critical errors (like auth failure), user has a choice to
     /// access and update `options`, `state` and `requests`.
     pub fn new(options: MqttOptions, cap: usize) -> EventLoop {
-        let (incoming_tx, incoming_rx) = bounded(1);
+        let (requests_tx, requests_rx) = bounded(cap);
         let pending = Vec::new();
         let pending = pending.into_iter();
         let max_inflight = options.inflight;
         let manual_acks = options.manual_acks;
-        let state = MqttState::new(max_inflight, manual_acks, cap);
-        let outgoing_buf = state.outgoing_buf.clone();
 
         EventLoop {
             options,
-            state,
-            outgoing_buf,
-            outgoing_buf_cache: VecDeque::with_capacity(cap),
-            incoming_tx,
-            incoming_rx,
+            state: MqttState::new(max_inflight, manual_acks),
+            requests_tx,
+            requests_rx,
             pending,
             network: None,
             keepalive_timeout: None,
         }
     }
 
-    /// Returns a handle to communicate with this eventloop
-    #[inline]
-    pub fn handle(&self) -> Sender<()> {
-        self.incoming_tx.clone()
-    }
-
     fn clean(&mut self) {
         self.network = None;
         self.keepalive_timeout = None;
@@ -119,39 +111,33 @@ impl EventLoop {
     /// the broker. Continuing to poll will reconnect to the broker if there is
     /// a disconnection.
     /// **NOTE** Don't block this while iterating
-    pub async fn poll(&mut self) -> Result<(), ConnectionError> {
-        // TODO: Enable user to reconnect
-        if self.state.is_disconnected() {
-            return Err(ConnectionError::Disconnected);
-        }
-
+    pub async fn poll(&mut self) -> Result<Event, ConnectionError> {
         if self.network.is_none() {
             let (network, connack) = time::timeout(
                 Duration::from_secs(self.options.connection_timeout()),
                 connect(&self.options),
             )
             .await??;
-
-            self.state.handle_incoming_packet(connack)?;
             self.network = Some(network);
 
             if self.keepalive_timeout.is_none() {
                 self.keepalive_timeout = Some(Box::pin(time::sleep(self.options.keep_alive)));
             }
 
-            return Ok(());
+            return Ok(Event::Incoming(Box::new(connack)));
         }
 
-        if let Err(e) = self.select().await {
-            self.clean();
-            return Err(e);
+        match self.select().await {
+            Ok(v) => Ok(v),
+            Err(e) => {
+                self.clean();
+                Err(e)
+            }
         }
-
-        Ok(())
     }
 
     /// Select on network and requests and generate keepalive pings when necessary
-    async fn select(&mut self) -> Result<(), ConnectionError> {
+    async fn select(&mut self) -> Result<Event, ConnectionError> {
         let network = self.network.as_mut().unwrap();
         // let await_acks = self.state.await_acks;
         let inflight_full = self.state.inflight >= self.options.inflight;
@@ -159,76 +145,69 @@ impl EventLoop {
         let pending = self.pending.len() > 0;
         let collision = self.state.collision.is_some();
 
-        // this loop is necessary as self.request_buf might be empty, in which case it is possible
-        // for self.state.events to be empty, and so popping off from it might return None. If None
-        // is returned, we select again.
-        loop {
-            select! {
-                // Pull a bunch of packets from network, reply in bunch and yield the first item
-                o = network.readb(&mut self.state) => {
-                    o?;
-                    // flush all the acks and return first incoming packet
-                    network.flush(&mut self.state.write).await?;
-                    return Ok(());
-                },
-                // Pull next request from user requests channel.
-                // If conditions in the below branch are for flow control. We read next user
-                // request only when inflight messages are < configured inflight and there are no
-                // collisions while handling previous outgoing requests.
-                //
-                // Flow control is based on ack count. If inflight packet count in the buffer is
-                // less than max_inflight setting, next outgoing request will progress. For this to
-                // work correctly, broker should ack in sequence (a lot of brokers won't)
-                //
-                // E.g If max inflight = 5, user requests will be blocked when inflight queue looks
-                // like this                       -> [1, 2, 3, 4, 5].
-                // If broker acking 2 instead of 1 -> [1, x, 3, 4, 5].
-                // This pulls next user request. But because max packet id = max_inflight, next
-                // user request's packet id will roll to 1. This replaces existing packet id 1.
-                // Resulting in a collision
-                //
-                // Eventloop can stop receiving outgoing user requests when previous outgoing
-                // request collided. I.e collision state. Collision state will be cleared only
-                // when correct ack is received
-                // Full inflight queue will look like -> [1a, 2, 3, 4, 5].
-                // If 3 is acked instead of 1 first   -> [1a, 2, x, 4, 5].
-                // After collision with pkid 1        -> [1b ,2, x, 4, 5].
-                // 1a is saved to state and event loop is set to collision mode stopping new
-                // outgoing requests (along with 1b).
-                o = self.incoming_rx.recv_async(), if !inflight_full && !pending && !collision => match o {
-                    Ok(_request_notif) => {
-                        // swapping to avoid blocking the mutex
-                        std::mem::swap(&mut self.outgoing_buf_cache, &mut self.outgoing_buf.lock().unwrap().buf);
-                        if self.outgoing_buf_cache.is_empty() {
-                            continue;
-                        }
-                        for request in self.outgoing_buf_cache.drain(..) {
-                            self.state.handle_outgoing_packet(request)?;
-                        }
-                        network.flush(&mut self.state.write).await?;
-                        // remaining events in the self.state.events will be taken out in next call
-                        // to poll() even before the select! is used.
-                        return Ok(())
-                    }
-                    Err(_) => return Err(ConnectionError::RequestsDone),
-                },
-                // Handle the next pending packet from previous session. Disable
-                // this branch when done with all the pending packets
-                Some(request) = next_pending(throttle, &mut self.pending), if pending => {
+        // Read buffered events from previous polls before calling a new poll
+        if let Some(event) = self.state.events.pop_front() {
+            return Ok(event);
+        }
+
+        // this loop is necessary since self.incoming.pop_front() might return None. In that case,
+        // instead of returning a None event, we try again.
+        select! {
+            // Pull a bunch of packets from network, reply in bunch and yield the first item
+            o = network.readb(&mut self.state) => {
+                o?;
+                // flush all the acks and return first incoming packet
+                network.flush(&mut self.state.write).await?;
+                Ok(self.state.events.pop_front().unwrap())
+            },
+            // Pull next request from user requests channel.
+            // If conditions in the below branch are for flow control. We read next user
+            // user request only when inflight messages are < configured inflight and there
+            // are no collisions while handling previous outgoing requests.
+            //
+            // Flow control is based on ack count. If inflight packet count in the buffer is
+            // less than max_inflight setting, next outgoing request will progress. For this
+            // to work correctly, broker should ack in sequence (a lot of brokers won't)
+            //
+            // E.g If max inflight = 5, user requests will be blocked when inflight queue
+            // looks like this                 -> [1, 2, 3, 4, 5].
+            // If broker acking 2 instead of 1 -> [1, x, 3, 4, 5].
+            // This pulls next user request. But because max packet id = max_inflight, next
+            // user request's packet id will roll to 1. This replaces existing packet id 1.
+            // Resulting in a collision
+            //
+            // Eventloop can stop receiving outgoing user requests when previous outgoing
+            // request collided. I.e collision state. Collision state will be cleared only
+            // when correct ack is received
+            // Full inflight queue will look like -> [1a, 2, 3, 4, 5].
+            // If 3 is acked instead of 1 first   -> [1a, 2, x, 4, 5].
+            // After collision with pkid 1        -> [1b ,2, x, 4, 5].
+            // 1a is saved to state and event loop is set to collision mode stopping new
+            // outgoing requests (along with 1b).
+            o = self.requests_rx.recv_async(), if !inflight_full && !pending && !collision => match o {
+                Ok(request) => {
                     self.state.handle_outgoing_packet(request)?;
                     network.flush(&mut self.state.write).await?;
-                    return Ok(())
-                },
-                // We generate pings irrespective of network activity. This keeps the ping logic
-                // simple. We can change this behavior in future if necessary (to prevent extra pings)
-                _ = self.keepalive_timeout.as_mut().unwrap() => {
-                    let timeout = self.keepalive_timeout.as_mut().unwrap();
-                    timeout.as_mut().reset(Instant::now() + self.options.keep_alive);
-
-                    self.state.handle_outgoing_packet(Request::PingReq)?;
-                    network.flush(&mut self.state.write).await?;
-                    return Ok(())
+                    Ok(self.state.events.pop_front().unwrap())
                 }
+                Err(_) => Err(ConnectionError::RequestsDone),
+            },
+            // Handle the next pending packet from previous session. Disable
+            // this branch when done with all the pending packets
+            Some(request) = next_pending(throttle, &mut self.pending), if pending => {
+                self.state.handle_outgoing_packet(request)?;
+                network.flush(&mut self.state.write).await?;
+                Ok(self.state.events.pop_front().unwrap())
+            },
+            // We generate pings irrespective of network activity. This keeps the ping logic
+            // simple. We can change this behavior in future if necessary (to prevent extra pings)
+            _ = self.keepalive_timeout.as_mut().unwrap() => {
+                let timeout = self.keepalive_timeout.as_mut().unwrap();
+                timeout.as_mut().reset(Instant::now() + self.options.keep_alive);
+
+                self.state.handle_outgoing_packet(Request::PingReq)?;
+                network.flush(&mut self.state.write).await?;
+                Ok(self.state.events.pop_front().unwrap())
             }
         }
     }
@@ -264,7 +243,7 @@ async fn network_connect(options: &MqttOptions) -> Result<Network, ConnectionErr
         }
         #[cfg(feature = "use-rustls")]
         Transport::Tls(tls_config) => {
-            let socket = tls::tls_connect(options, &tls_config).await?;
+            let socket = tls::tls_connect(&options.broker_addr, options.port, &tls_config).await?;
             Network::new(socket, options.max_incoming_packet_size)
         }
         #[cfg(unix)]
@@ -279,12 +258,9 @@ async fn network_connect(options: &MqttOptions) -> Result<Network, ConnectionErr
                 .method(http::Method::GET)
                 .uri(options.broker_addr.as_str())
                 .header("Sec-WebSocket-Protocol", "mqttv3.1")
-                .body(())
-                .unwrap();
+                .body(())?;
 
-            let (socket, _) = connect_async(request)
-                .await
-                .map_err(|e| io::Error::new(io::ErrorKind::ConnectionRefused, e))?;
+            let (socket, _) = connect_async(request).await?;
 
             Network::new(WsStream::new(socket), options.max_incoming_packet_size)
         }
@@ -294,14 +270,11 @@ async fn network_connect(options: &MqttOptions) -> Result<Network, ConnectionErr
                 .method(http::Method::GET)
                 .uri(options.broker_addr.as_str())
                 .header("Sec-WebSocket-Protocol", "mqttv3.1")
-                .body(())
-                .unwrap();
+                .body(())?;
 
             let connector = tls::tls_connector(&tls_config).await?;
 
-            let (socket, _) = connect_async_with_tls_connector(request, Some(connector))
-                .await
-                .map_err(|e| io::Error::new(io::ErrorKind::ConnectionRefused, e))?;
+            let (socket, _) = connect_async_with_tls_connector(request, Some(connector)).await?;
 
             Network::new(WsStream::new(socket), options.max_incoming_packet_size)
         }
@@ -316,20 +289,15 @@ async fn mqtt_connect(
 ) -> Result<Incoming, ConnectionError> {
     let keep_alive = options.keep_alive().as_secs() as u16;
     let clean_session = options.clean_session();
-    let last_will = options.last_will();
+    let client_id = options.client_id();
 
-    let mut connect = Connect::new(options.client_id());
-    connect.keep_alive = keep_alive;
-    connect.clean_session = clean_session;
-    connect.last_will = last_will;
-
-    if let Some((username, password)) = options.credentials() {
-        let login = Login::new(username, password);
-        connect.login = Some(login);
-    }
-
-    // mqtt connection with timeout
+    let connect = Connect {
+        keep_alive,
+        client_id,
+        clean_session,
+    };
 
+    // send mqtt connect packet
     network.connect(connect).await?;
 
     // validate connack
diff --git a/rumqttc/src/v5/framed.rs b/rumqttc/src/v5/framed.rs
index 684694d5a..cb6371193 100644
--- a/rumqttc/src/v5/framed.rs
+++ b/rumqttc/src/v5/framed.rs
@@ -1,7 +1,9 @@
 use bytes::BytesMut;
 use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
 
-use crate::v5::{packet::*, Incoming, MqttState, StateError};
+use super::mqttbytes::v5::Packet;
+use super::mqttbytes::{self, Connect};
+use super::{Incoming, MqttState, StateError};
 use std::io;
 
 /// Network transforms packets <-> frames efficiently. It takes
@@ -57,9 +59,9 @@ impl Network {
 
     pub async fn read(&mut self) -> io::Result<Incoming> {
         loop {
-            let required = match read(&mut self.read, self.max_incoming_size) {
+            let required = match Packet::read(&mut self.read, self.max_incoming_size) {
                 Ok(packet) => return Ok(packet),
-                Err(Error::InsufficientBytes(required)) => required,
+                Err(mqttbytes::Error::InsufficientBytes(required)) => required,
                 Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string())),
             };
 
@@ -74,7 +76,7 @@ impl Network {
     pub async fn readb(&mut self, state: &mut MqttState) -> Result<(), StateError> {
         let mut count = 0;
         loop {
-            match read(&mut self.read, self.max_incoming_size) {
+            match Packet::read(&mut self.read, self.max_incoming_size) {
                 Ok(packet) => {
                     state.handle_incoming_packet(packet)?;
 
@@ -84,9 +86,9 @@ impl Network {
                     }
                 }
                 // If some packets are already framed, return those
-                Err(Error::InsufficientBytes(_)) if count > 0 => return Ok(()),
+                Err(mqttbytes::Error::InsufficientBytes(_)) if count > 0 => return Ok(()),
                 // Wait for more bytes until a frame can be created
-                Err(Error::InsufficientBytes(required)) => {
+                Err(mqttbytes::Error::InsufficientBytes(required)) => {
                     self.read_bytes(required).await?;
                 }
                 Err(e) => return Err(StateError::Deserialization(e)),
@@ -96,7 +98,7 @@ impl Network {
 
     pub async fn connect(&mut self, connect: Connect) -> io::Result<usize> {
         let mut write = BytesMut::new();
-        let len = match connect.write(&mut write) {
+        let len = match Packet::Connect(connect, None, None, None, None).write(&mut write) {
             Ok(size) => size,
             Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string())),
         };
diff --git a/rumqttc/src/v5/mod.rs b/rumqttc/src/v5/mod.rs
index b5ecb79c1..f2d0dfbd2 100644
--- a/rumqttc/src/v5/mod.rs
+++ b/rumqttc/src/v5/mod.rs
@@ -1,36 +1,25 @@
-#[cfg(feature = "use-rustls")]
-use std::sync::Arc;
-use std::{
-    fmt::{self, Debug, Formatter},
-    time::Duration,
-};
+use std::fmt::{self, Debug, Formatter};
+use std::time::Duration;
 
 mod client;
 mod eventloop;
 mod framed;
-mod notifier;
-mod outgoing_buf;
-#[allow(clippy::all)]
-mod packet;
+pub mod mqttbytes;
 mod state;
-#[cfg(feature = "use-rustls")]
-mod tls;
 
+#[cfg(feature = "use-rustls")]
+pub use crate::tls::Error as TlsError;
+use crate::Transport;
 pub use client::{AsyncClient, Client, ClientError, Connection, Iter};
-pub use eventloop::{ConnectionError, EventLoop};
-pub use flume::{SendError, Sender, TrySendError};
-pub use notifier::{Notifier, TryRecvError};
-pub use packet::*;
+pub use eventloop::{ConnectionError, Event, EventLoop};
 pub use state::{MqttState, StateError};
-#[cfg(feature = "use-rustls")]
-pub use tls::Error;
-#[cfg(feature = "use-rustls")]
-pub use tokio_rustls;
-#[cfg(feature = "use-rustls")]
-use tokio_rustls::rustls::ClientConfig;
+
+use mqttbytes::{v5::*, *};
 
 pub type Incoming = Packet;
 
+use crate::Outgoing;
+
 /// Requests by the client to mqtt event loop. Request are
 /// handled one by one.
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -49,141 +38,10 @@ pub enum Request {
     Disconnect,
 }
 
-/// Key type for TLS authentication
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum Key {
-    RSA(Vec<u8>),
-    ECC(Vec<u8>),
-}
-
-impl From<Publish> for Request {
-    fn from(publish: Publish) -> Request {
-        Request::Publish(publish)
-    }
-}
-
-impl From<Subscribe> for Request {
-    fn from(subscribe: Subscribe) -> Request {
-        Request::Subscribe(subscribe)
-    }
-}
-
-impl From<Unsubscribe> for Request {
-    fn from(unsubscribe: Unsubscribe) -> Request {
-        Request::Unsubscribe(unsubscribe)
-    }
-}
-
-#[derive(Clone)]
-pub enum Transport {
-    Tcp,
-    #[cfg(feature = "use-rustls")]
-    Tls(TlsConfiguration),
-    #[cfg(unix)]
-    Unix,
-    #[cfg(feature = "websocket")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "websocket")))]
-    Ws,
-    #[cfg(all(feature = "use-rustls", feature = "websocket"))]
-    #[cfg_attr(docsrs, doc(cfg(all(feature = "use-rustls", feature = "websocket"))))]
-    Wss(TlsConfiguration),
-}
-
-impl Default for Transport {
-    fn default() -> Self {
-        Self::tcp()
-    }
-}
-
-impl Transport {
-    /// Use regular tcp as transport (default)
-    pub fn tcp() -> Self {
-        Self::Tcp
-    }
-
-    /// Use secure tcp with tls as transport
-    #[cfg(feature = "use-rustls")]
-    pub fn tls(
-        ca: Vec<u8>,
-        client_auth: Option<(Vec<u8>, Key)>,
-        alpn: Option<Vec<Vec<u8>>>,
-    ) -> Self {
-        let config = TlsConfiguration::Simple {
-            ca,
-            alpn,
-            client_auth,
-        };
-
-        Self::tls_with_config(config)
-    }
-
-    #[cfg(feature = "use-rustls")]
-    pub fn tls_with_config(tls_config: TlsConfiguration) -> Self {
-        Self::Tls(tls_config)
-    }
-
-    #[cfg(unix)]
-    pub fn unix() -> Self {
-        Self::Unix
-    }
-
-    /// Use websockets as transport
-    #[cfg(feature = "websocket")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "websocket")))]
-    pub fn ws() -> Self {
-        Self::Ws
-    }
-
-    /// Use secure websockets with tls as transport
-    #[cfg(all(feature = "use-rustls", feature = "websocket"))]
-    #[cfg_attr(docsrs, doc(cfg(all(feature = "use-rustls", feature = "websocket"))))]
-    pub fn wss(
-        ca: Vec<u8>,
-        client_auth: Option<(Vec<u8>, Key)>,
-        alpn: Option<Vec<Vec<u8>>>,
-    ) -> Self {
-        let config = TlsConfiguration::Simple {
-            ca,
-            client_auth,
-            alpn,
-        };
-
-        Self::wss_with_config(config)
-    }
-
-    #[cfg(all(feature = "use-rustls", feature = "websocket"))]
-    #[cfg_attr(docsrs, doc(cfg(all(feature = "use-rustls", feature = "websocket"))))]
-    pub fn wss_with_config(tls_config: TlsConfiguration) -> Self {
-        Self::Wss(tls_config)
-    }
-}
-
-#[derive(Clone)]
-#[cfg(feature = "use-rustls")]
-pub enum TlsConfiguration {
-    Simple {
-        /// connection method
-        ca: Vec<u8>,
-        /// alpn settings
-        alpn: Option<Vec<Vec<u8>>>,
-        /// tls client_authentication
-        client_auth: Option<(Vec<u8>, Key)>,
-    },
-    /// Injected rustls ClientConfig for TLS, to allow more customisation.
-    Rustls(Arc<ClientConfig>),
-}
-
-#[cfg(feature = "use-rustls")]
-impl From<ClientConfig> for TlsConfiguration {
-    fn from(config: ClientConfig) -> Self {
-        TlsConfiguration::Rustls(Arc::new(config))
-    }
-}
-
 // TODO: Should all the options be exposed as public? Drawback
 // would be loosing the ability to panic when the user options
 // are wrong (e.g empty client id) or aggressive (keep alive time)
-/// Options to configure the behaviour of mqtt connection
+/// Options to configure the behaviour of MQTT connection
 #[derive(Clone)]
 pub struct MqttOptions {
     /// broker address that you want to connect to
@@ -226,7 +84,21 @@ pub struct MqttOptions {
 }
 
 impl MqttOptions {
-    /// New mqtt options
+    /// Create an [`MqttOptions`] object that contains default values for all settings other than
+    /// - id: A string to identify the device connecting to a broker
+    /// - host: The broker's domain name or IP address
+    /// - port: The port number on which broker must be listening for incoming connections
+    ///
+    /// ```
+    /// # use rumqttc::MqttOptions;
+    /// let options = MqttOptions::new("123", "localhost", 1883);
+    /// ```
+    /// NOTE: you are not allowed to use an id that starts with a whitespace or is empty.
+    /// for example, the following code would panic:
+    /// ```should_panic
+    /// # use rumqttc::MqttOptions;
+    /// let options = MqttOptions::new("", "localhost", 1883);
+    /// ```
     pub fn new<S: Into<String>, T: Into<String>>(id: S, host: T, port: u16) -> MqttOptions {
         let id = id.into();
         if id.starts_with(' ') || id.is_empty() {
@@ -253,6 +125,42 @@ impl MqttOptions {
         }
     }
 
+    #[cfg(feature = "url")]
+    /// Creates an [`MqttOptions`] object by parsing provided string with the [url] crate's
+    /// [`Url::parse(url)`](url::Url::parse) method and is only enabled when run using the "url" feature.
+    ///
+    /// ```
+    /// # use rumqttc::MqttOptions;
+    /// let options = MqttOptions::parse_url("mqtt://example.com:1883?client_id=123").unwrap();
+    /// ```
+    ///
+    /// **NOTE:** A url must be prefixed with one of either `tcp://`, `mqtt://`, `ssl://`,`mqtts://`,
+    /// `ws://` or `wss://` to denote the protocol for establishing a connection with the broker.
+    ///
+    /// **NOTE:** Encrypted connections(i.e. `mqtts://`, `ssl://`, `wss://`) by default use the
+    /// system's root certificates. To configure with custom certificates, one may use the
+    /// [`set_transport`](MqttOptions::set_transport) method.
+    ///
+    /// ```ignore
+    /// # use rumqttc::{MqttOptions, Transport};
+    /// # use tokio_rustls::rustls::ClientConfig;
+    /// # let root_cert_store = rustls::RootCertStore::empty();
+    /// # let client_config = ClientConfig::builder()
+    /// #    .with_safe_defaults()
+    /// #    .with_root_certificates(root_cert_store)
+    /// #    .with_no_client_auth();
+    /// let mut options = MqttOptions::parse_url("mqtts://example.com?client_id=123").unwrap();
+    /// options.set_transport(Transport::tls_with_config(client_config.into()));
+    /// ```
+    pub fn parse_url<S: Into<String>>(url: S) -> Result<MqttOptions, OptionError> {
+        use std::convert::TryFrom;
+
+        let url = url::Url::parse(&url.into())?;
+        let options = MqttOptions::try_from(url)?;
+
+        Ok(options)
+    }
+
     /// Broker address
     pub fn broker_address(&self) -> (String, u16) {
         (self.broker_addr.clone(), self.port)
@@ -279,7 +187,7 @@ impl MqttOptions {
     /// Set number of seconds after which client should ping the broker
     /// if there is no other data exchange
     pub fn set_keep_alive(&mut self, duration: Duration) -> &mut Self {
-        assert!(duration.as_secs() >= 5, "Keep alives should be >= 5  secs");
+        assert!(duration.as_secs() >= 5, "Keep alives should be >= 5 secs");
 
         self.keep_alive = duration;
         self
@@ -362,9 +270,7 @@ impl MqttOptions {
 
     /// Set number of concurrent in flight messages
     pub fn set_inflight(&mut self, inflight: u16) -> &mut Self {
-        if inflight == 0 {
-            panic!("zero in flight is not allowed");
-        }
+        assert!(inflight != 0, "zero in flight is not allowed");
 
         self.inflight = inflight;
         self
@@ -436,6 +342,9 @@ pub enum OptionError {
 
     #[error("Unknown option: {0}")]
     Unknown(String),
+
+    #[error("Couldn't parse option from url: {0}")]
+    Parse(#[from] url::ParseError),
 }
 
 #[cfg(feature = "url")]
@@ -445,14 +354,19 @@ impl std::convert::TryFrom<url::Url> for MqttOptions {
     fn try_from(url: url::Url) -> Result<Self, Self::Error> {
         use std::collections::HashMap;
 
-        let broker_addr = url.host_str().unwrap_or_default().to_owned();
+        let host = url.host_str().unwrap_or_default().to_owned();
 
         let (transport, default_port) = match url.scheme() {
             // Encrypted connections are supported, but require explicit TLS configuration. We fall
             // back to the unencrypted transport layer, so that `set_transport` can be used to
             // configure the encrypted transport layer with the provided TLS configuration.
-            "mqtts" | "ssl" => (Transport::Tcp, 8883),
+            #[cfg(feature = "use-rustls")]
+            "mqtts" | "ssl" => (Transport::tls_with_default_config(), 8883),
             "mqtt" | "tcp" => (Transport::Tcp, 1883),
+            #[cfg(feature = "websocket")]
+            "ws" => (Transport::Ws, 8000),
+            #[cfg(all(feature = "use-rustls", feature = "websocket"))]
+            "wss" => (Transport::wss_with_default_config(), 8000),
             _ => return Err(OptionError::Scheme),
         };
 
@@ -460,26 +374,31 @@ impl std::convert::TryFrom<url::Url> for MqttOptions {
 
         let mut queries = url.query_pairs().collect::<HashMap<_, _>>();
 
-        let keep_alive = Duration::from_secs(
-            queries
-                .remove("keep_alive_secs")
-                .map(|v| v.parse::<u64>().map_err(|_| OptionError::KeepAlive))
-                .transpose()?
-                .unwrap_or(60),
-        );
-
-        let client_id = queries
+        let id = queries
             .remove("client_id")
             .ok_or(OptionError::ClientId)?
             .into_owned();
 
-        let clean_session = queries
+        let mut options = MqttOptions::new(id, host, port);
+        options.set_transport(transport);
+
+        if let Some(keep_alive) = queries
+            .remove("keep_alive_secs")
+            .map(|v| v.parse::<u64>().map_err(|_| OptionError::KeepAlive))
+            .transpose()?
+        {
+            options.set_keep_alive(Duration::from_secs(keep_alive));
+        }
+
+        if let Some(clean_session) = queries
             .remove("clean_session")
             .map(|v| v.parse::<bool>().map_err(|_| OptionError::CleanSession))
             .transpose()?
-            .unwrap_or(true);
+        {
+            options.set_clean_session(clean_session);
+        }
 
-        let credentials = {
+        if let Some((username, password)) = {
             match url.username() {
                 "" => None,
                 username => Some((
@@ -487,83 +406,77 @@ impl std::convert::TryFrom<url::Url> for MqttOptions {
                     url.password().unwrap_or_default().to_owned(),
                 )),
             }
-        };
-
-        let max_incoming_packet_size = queries
-            .remove("max_incoming_packet_size_bytes")
-            .map(|v| {
-                v.parse::<usize>()
-                    .map_err(|_| OptionError::MaxIncomingPacketSize)
-            })
-            .transpose()?
-            .unwrap_or(10 * 1024);
+        } {
+            options.set_credentials(username, password);
+        }
 
-        let max_outgoing_packet_size = queries
-            .remove("max_outgoing_packet_size_bytes")
-            .map(|v| {
-                v.parse::<usize>()
-                    .map_err(|_| OptionError::MaxOutgoingPacketSize)
-            })
-            .transpose()?
-            .unwrap_or(10 * 1024);
+        if let (Some(incoming), Some(outgoing)) = (
+            queries
+                .remove("max_incoming_packet_size_bytes")
+                .map(|v| {
+                    v.parse::<usize>()
+                        .map_err(|_| OptionError::MaxIncomingPacketSize)
+                })
+                .transpose()?,
+            queries
+                .remove("max_outgoing_packet_size_bytes")
+                .map(|v| {
+                    v.parse::<usize>()
+                        .map_err(|_| OptionError::MaxOutgoingPacketSize)
+                })
+                .transpose()?,
+        ) {
+            options.set_max_packet_size(incoming, outgoing);
+        }
 
-        let request_channel_capacity = queries
+        if let Some(request_channel_capacity) = queries
             .remove("request_channel_capacity_num")
             .map(|v| {
                 v.parse::<usize>()
                     .map_err(|_| OptionError::RequestChannelCapacity)
             })
             .transpose()?
-            .unwrap_or(10);
+        {
+            options.request_channel_capacity = request_channel_capacity;
+        }
 
-        let max_request_batch = queries
+        if let Some(max_request_batch) = queries
             .remove("max_request_batch_num")
             .map(|v| v.parse::<usize>().map_err(|_| OptionError::MaxRequestBatch))
             .transpose()?
-            .unwrap_or(0);
+        {
+            options.max_request_batch = max_request_batch;
+        }
 
-        let pending_throttle = Duration::from_micros(
-            queries
-                .remove("pending_throttle_usecs")
-                .map(|v| v.parse::<u64>().map_err(|_| OptionError::PendingThrottle))
-                .transpose()?
-                .unwrap_or(0),
-        );
+        if let Some(pending_throttle) = queries
+            .remove("pending_throttle_usecs")
+            .map(|v| v.parse::<u64>().map_err(|_| OptionError::PendingThrottle))
+            .transpose()?
+        {
+            options.set_pending_throttle(Duration::from_micros(pending_throttle));
+        }
 
-        let inflight = queries
+        if let Some(inflight) = queries
             .remove("inflight_num")
             .map(|v| v.parse::<u16>().map_err(|_| OptionError::Inflight))
             .transpose()?
-            .unwrap_or(100);
+        {
+            options.set_inflight(inflight);
+        }
 
-        let conn_timeout = queries
+        if let Some(conn_timeout) = queries
             .remove("conn_timeout_secs")
             .map(|v| v.parse::<u64>().map_err(|_| OptionError::ConnTimeout))
             .transpose()?
-            .unwrap_or(5);
+        {
+            options.set_connection_timeout(conn_timeout);
+        }
 
         if let Some((opt, _)) = queries.into_iter().next() {
             return Err(OptionError::Unknown(opt.into_owned()));
         }
 
-        Ok(Self {
-            broker_addr,
-            port,
-            transport,
-            keep_alive,
-            clean_session,
-            client_id,
-            credentials,
-            max_incoming_packet_size,
-            max_outgoing_packet_size,
-            request_channel_capacity,
-            max_request_batch,
-            pending_throttle,
-            inflight,
-            last_will: None,
-            conn_timeout,
-            manual_acks: false,
-        })
+        Ok(options)
     }
 }
 
@@ -603,15 +516,16 @@ mod test {
     #[test]
     #[cfg(all(feature = "use-rustls", feature = "websocket"))]
     fn no_scheme() {
-        let mut _mqtt_opts = MqttOptions::new("client_a", "a3f8czas.iot.eu-west-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MyCreds%2F20201001%2Feu-west-1%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20201001T130812Z&X-Amz-Expires=7200&X-Amz-Signature=9ae09b49896f44270f2707551581953e6cac71a4ccf34c7c3415555be751b2d1&X-Amz-SignedHeaders=host", 443);
+        use crate::{TlsConfiguration, Transport};
+        let mut mqttoptions = MqttOptions::new("client_a", "a3f8czas.iot.eu-west-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MyCreds%2F20201001%2Feu-west-1%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20201001T130812Z&X-Amz-Expires=7200&X-Amz-Signature=9ae09b49896f44270f2707551581953e6cac71a4ccf34c7c3415555be751b2d1&X-Amz-SignedHeaders=host", 443);
 
-        _mqtt_opts.set_transport(crate::v5::Transport::wss(Vec::from("Test CA"), None, None));
+        mqttoptions.set_transport(Transport::wss(Vec::from("Test CA"), None, None));
 
-        if let crate::v5::Transport::Wss(TlsConfiguration::Simple {
+        if let Transport::Wss(TlsConfiguration::Simple {
             ca,
             client_auth,
             alpn,
-        }) = _mqtt_opts.transport
+        }) = mqttoptions.transport
         {
             assert_eq!(ca, Vec::from("Test CA"));
             assert_eq!(client_auth, None);
@@ -620,17 +534,14 @@ mod test {
             panic!("Unexpected transport!");
         }
 
-        assert_eq!(_mqtt_opts.broker_addr, "a3f8czas.iot.eu-west-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MyCreds%2F20201001%2Feu-west-1%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20201001T130812Z&X-Amz-Expires=7200&X-Amz-Signature=9ae09b49896f44270f2707551581953e6cac71a4ccf34c7c3415555be751b2d1&X-Amz-SignedHeaders=host");
+        assert_eq!(mqttoptions.broker_addr, "a3f8czas.iot.eu-west-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MyCreds%2F20201001%2Feu-west-1%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20201001T130812Z&X-Amz-Expires=7200&X-Amz-Signature=9ae09b49896f44270f2707551581953e6cac71a4ccf34c7c3415555be751b2d1&X-Amz-SignedHeaders=host");
     }
 
     #[test]
     #[cfg(feature = "url")]
     fn from_url() {
-        use std::convert::TryInto;
-        use std::str::FromStr;
-
         fn opt(s: &str) -> Result<MqttOptions, OptionError> {
-            url::Url::from_str(s).expect("valid url").try_into()
+            MqttOptions::parse_url(s)
         }
         fn ok(s: &str) -> MqttOptions {
             opt(s).expect("valid options")
diff --git a/rumqttc/src/v5/mqttbytes/mod.rs b/rumqttc/src/v5/mqttbytes/mod.rs
new file mode 100644
index 000000000..193008f70
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/mod.rs
@@ -0,0 +1,784 @@
+use std::{str::Utf8Error, vec};
+
+/// This module is the place where all the protocol specifics gets abstracted
+/// out and creates a structures which are common across protocols. Since,
+/// MQTT is the core protocol that this broker supports, a lot of structs closely
+/// map to what MQTT specifies in its protocol
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+pub mod v5;
+
+//--------------------------- Connect packet -------------------------------
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PingReq;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PingResp;
+
+/// Connection packet initiated by the client
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Connect {
+    /// Mqtt keep alive time
+    pub keep_alive: u16,
+    /// Client Id
+    pub client_id: String,
+    /// Clean session. Asks the broker to clear previous state
+    pub clean_session: bool,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ConnectProperties {
+    /// Expiry interval property after loosing connection
+    pub session_expiry_interval: Option<u32>,
+    /// Maximum simultaneous packets
+    pub receive_maximum: Option<u16>,
+    /// Maximum packet size
+    pub max_packet_size: Option<u32>,
+    /// Maximum mapping integer for a topic
+    pub topic_alias_max: Option<u16>,
+    pub request_response_info: Option<u8>,
+    pub request_problem_info: Option<u8>,
+    /// List of user properties
+    pub user_properties: Vec<(String, String)>,
+    /// Method of authentication
+    pub authentication_method: Option<String>,
+    /// Authentication data
+    pub authentication_data: Option<Bytes>,
+}
+
+/// LastWill that broker forwards on behalf of the client
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LastWill {
+    pub topic: Bytes,
+    pub message: Bytes,
+    pub qos: QoS,
+    pub retain: bool,
+}
+
+impl LastWill {
+    pub fn new(
+        topic: impl Into<String>,
+        payload: impl Into<Vec<u8>>,
+        qos: QoS,
+        retain: bool,
+    ) -> LastWill {
+        let topic = Bytes::copy_from_slice(topic.into().as_bytes());
+        LastWill {
+            topic,
+            message: Bytes::from(payload.into()),
+            qos,
+            retain,
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LastWillProperties {
+    pub delay_interval: Option<u32>,
+    pub payload_format_indicator: Option<u8>,
+    pub message_expiry_interval: Option<u32>,
+    pub content_type: Option<String>,
+    pub response_topic: Option<String>,
+    pub correlation_data: Option<Bytes>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Login {
+    pub username: String,
+    pub password: String,
+}
+
+//--------------------------- ConnectAck packet -------------------------------
+
+/// Return code in connack
+// This contains return codes for both MQTT v311 and v5
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ConnectReturnCode {
+    Success,
+    RefusedProtocolVersion,
+    BadClientId,
+    ServiceUnavailable,
+    UnspecifiedError,
+    MalformedPacket,
+    ProtocolError,
+    ImplementationSpecificError,
+    UnsupportedProtocolVersion,
+    ClientIdentifierNotValid,
+    BadUserNamePassword,
+    NotAuthorized,
+    ServerUnavailable,
+    ServerBusy,
+    Banned,
+    BadAuthenticationMethod,
+    TopicNameInvalid,
+    PacketTooLarge,
+    QuotaExceeded,
+    PayloadFormatInvalid,
+    RetainNotSupported,
+    QoSNotSupported,
+    UseAnotherServer,
+    ServerMoved,
+    ConnectionRateExceeded,
+}
+
+/// Acknowledgement to connect packet
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ConnAck {
+    pub session_present: bool,
+    pub code: ConnectReturnCode,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ConnAckProperties {
+    pub session_expiry_interval: Option<u32>,
+    pub receive_max: Option<u16>,
+    pub max_qos: Option<u8>,
+    pub retain_available: Option<u8>,
+    pub max_packet_size: Option<u32>,
+    pub assigned_client_identifier: Option<String>,
+    pub topic_alias_max: Option<u16>,
+    pub reason_string: Option<String>,
+    pub user_properties: Vec<(String, String)>,
+    pub wildcard_subscription_available: Option<u8>,
+    pub subscription_identifiers_available: Option<u8>,
+    pub shared_subscription_available: Option<u8>,
+    pub server_keep_alive: Option<u16>,
+    pub response_information: Option<String>,
+    pub server_reference: Option<String>,
+    pub authentication_method: Option<String>,
+    pub authentication_data: Option<Bytes>,
+}
+
+//--------------------------- Publish packet -------------------------------
+
+/// Publish packet
+#[derive(Clone, Debug, PartialEq, Eq, Default)]
+pub struct Publish {
+    pub dup: bool,
+    pub qos: QoS,
+    pub retain: bool,
+    pub topic: Bytes,
+    pub pkid: u16,
+    pub payload: Bytes,
+}
+
+impl Publish {
+    pub fn new<T: Into<String>, P: Into<Bytes>>(topic: T, qos: QoS, payload: P) -> Self {
+        let topic = Bytes::copy_from_slice(topic.into().as_bytes());
+        Self {
+            qos,
+            topic,
+            payload: payload.into(),
+            ..Default::default()
+        }
+    }
+
+    /// Approximate length for meter
+    pub fn len(&self) -> usize {
+        let len = 2 + self.topic.len() + self.payload.len();
+        match self.qos == QoS::AtMostOnce {
+            true => len,
+            false => len + 2,
+        }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// Serialization which is independent of MQTT
+    pub fn serialize(&self) -> Bytes {
+        let mut o = BytesMut::with_capacity(self.len() + 5);
+        let dup = self.dup as u8;
+        let qos = self.qos as u8;
+        let retain = self.retain as u8;
+        o.put_u8(0b0011_0000 | retain | qos << 1 | dup << 3);
+        o.put_u16(self.pkid);
+        o.put_u16(self.topic.len() as u16);
+        o.extend_from_slice(&self.topic[..]);
+
+        // TODO: Change segments to take Buf to prevent this copying
+        o.extend_from_slice(&self.payload[..]);
+        o.freeze()
+    }
+
+    /// Serialization which is independent of MQTT
+    pub fn deserialize(mut o: Bytes) -> Publish {
+        let header = o.get_u8();
+        let qos_num = (header & 0b0110) >> 1;
+        let qos = qos(qos_num).unwrap_or(QoS::AtMostOnce);
+        let dup = (header & 0b1000) != 0;
+        let retain = (header & 0b0001) != 0;
+
+        let pkid = o.get_u16();
+        let topic_len = o.get_u16();
+        let topic = o.split_to(topic_len as usize);
+        let payload = o;
+        Publish {
+            dup,
+            qos,
+            retain,
+            topic,
+            pkid,
+            payload,
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PublishProperties {
+    pub payload_format_indicator: Option<u8>,
+    pub message_expiry_interval: Option<u32>,
+    pub topic_alias: Option<u16>,
+    pub response_topic: Option<String>,
+    pub correlation_data: Option<Bytes>,
+    pub user_properties: Vec<(String, String)>,
+    pub subscription_identifiers: Vec<usize>,
+    pub content_type: Option<String>,
+}
+
+//--------------------------- PublishAck packet -------------------------------
+
+/// Return code in puback
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PubAckReason {
+    Success,
+    NoMatchingSubscribers,
+    UnspecifiedError,
+    ImplementationSpecificError,
+    NotAuthorized,
+    TopicNameInvalid,
+    PacketIdentifierInUse,
+    QuotaExceeded,
+    PayloadFormatInvalid,
+}
+
+/// Acknowledgement to QoS1 publish
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubAck {
+    pub pkid: u16,
+    pub reason: PubAckReason,
+}
+
+impl PubAck {
+    pub fn new(pkid: u16) -> Self {
+        Self {
+            pkid,
+            reason: PubAckReason::Success,
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubAckProperties {
+    pub reason_string: Option<String>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+//--------------------------- Subscribe packet -------------------------------
+
+/// Subscription packet
+#[derive(Clone, Debug, PartialEq, Eq, Default)]
+pub struct Subscribe {
+    pub pkid: u16,
+    pub filters: Vec<Filter>,
+}
+
+impl Subscribe {
+    pub fn new(filter: Filter) -> Self {
+        Self {
+            filters: vec![filter],
+            ..Default::default()
+        }
+    }
+
+    pub fn new_many<F>(filters: F) -> Self
+    where
+        F: IntoIterator<Item = Filter>,
+    {
+        Self {
+            filters: filters.into_iter().collect(),
+            ..Default::default()
+        }
+    }
+}
+
+///  Subscription filter
+#[derive(Clone, Debug, PartialEq, Eq, Default)]
+pub struct Filter {
+    pub path: String,
+    pub qos: QoS,
+    pub nolocal: bool,
+    pub preserve_retain: bool,
+    pub retain_forward_rule: RetainForwardRule,
+}
+
+impl Filter {
+    pub fn new<T: Into<String>>(topic: T, qos: QoS) -> Self {
+        Self {
+            path: topic.into(),
+            qos,
+            ..Default::default()
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum RetainForwardRule {
+    OnEverySubscribe,
+    OnNewSubscribe,
+    Never,
+}
+
+impl Default for RetainForwardRule {
+    fn default() -> Self {
+        Self::Never
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SubscribeProperties {
+    pub id: Option<usize>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+//--------------------------- SubscribeAck packet -------------------------------
+
+/// Acknowledgement to subscribe
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SubAck {
+    pub pkid: u16,
+    pub return_codes: Vec<SubscribeReasonCode>,
+}
+
+impl SubAck {
+    pub fn len(&self) -> usize {
+        2 + self.return_codes.len()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum SubscribeReasonCode {
+    QoS0,
+    QoS1,
+    QoS2,
+    Success(QoS),
+    Failure,
+    Unspecified,
+    ImplementationSpecific,
+    NotAuthorized,
+    TopicFilterInvalid,
+    PkidInUse,
+    QuotaExceeded,
+    SharedSubscriptionsNotSupported,
+    SubscriptionIdNotSupported,
+    WildcardSubscriptionsNotSupported,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SubAckProperties {
+    pub reason_string: Option<String>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+//--------------------------- Unsubscribe packet -------------------------------
+
+/// Unsubscribe packet
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
+pub struct Unsubscribe {
+    pub pkid: u16,
+    pub filters: Vec<String>,
+}
+
+impl Unsubscribe {
+    pub fn new<S: Into<String>>(filter: S) -> Self {
+        Self {
+            filters: vec![filter.into()],
+            ..Default::default()
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct UnsubscribeProperties {
+    pub user_properties: Vec<(String, String)>,
+}
+//--------------------------- UnsubscribeAck packet -------------------------------
+
+/// Acknowledgement to unsubscribe
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct UnsubAck {
+    pub pkid: u16,
+    pub reasons: Vec<UnsubAckReason>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum UnsubAckReason {
+    Success,
+    NoSubscriptionExisted,
+    UnspecifiedError,
+    ImplementationSpecificError,
+    NotAuthorized,
+    TopicFilterInvalid,
+    PacketIdentifierInUse,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct UnsubAckProperties {
+    pub reason_string: Option<String>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+//--------------------------- Disconnect packet -------------------------------
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum DisconnectReasonCode {
+    /// Close the connection normally. Do not send the Will Message.
+    NormalDisconnection,
+    /// The Client wishes to disconnect but requires that the Server also publishes its Will Message.
+    DisconnectWithWillMessage,
+    /// The Connection is closed but the sender either does not wish to reveal the reason, or none of the other Reason Codes apply.
+    UnspecifiedError,
+    /// The received packet does not conform to this specification.
+    MalformedPacket,
+    /// An unexpected or out of order packet was received.
+    ProtocolError,
+    /// The packet received is valid but cannot be processed by this implementation.
+    ImplementationSpecificError,
+    /// The request is not authorized.
+    NotAuthorized,
+    /// The Server is busy and cannot continue processing requests from this Client.
+    ServerBusy,
+    /// The Server is shutting down.
+    ServerShuttingDown,
+    /// The Connection is closed because no packet has been received for 1.5 times the Keepalive time.
+    KeepAliveTimeout,
+    /// Another Connection using the same ClientID has connected causing this Connection to be closed.
+    SessionTakenOver,
+    /// The Topic Filter is correctly formed, but is not accepted by this Sever.
+    TopicFilterInvalid,
+    /// The Topic Name is correctly formed, but is not accepted by this Client or Server.
+    TopicNameInvalid,
+    /// The Client or Server has received more than Receive Maximum publication for which it has not sent PUBACK or PUBCOMP.
+    ReceiveMaximumExceeded,
+    /// The Client or Server has received a PUBLISH packet containing a Topic Alias which is greater than the Maximum Topic Alias it sent in the CONNECT or CONNACK packet.
+    TopicAliasInvalid,
+    /// The packet size is greater than Maximum Packet Size for this Client or Server.
+    PacketTooLarge,
+    /// The received data rate is too high.
+    MessageRateTooHigh,
+    /// An implementation or administrative imposed limit has been exceeded.
+    QuotaExceeded,
+    /// The Connection is closed due to an administrative action.
+    AdministrativeAction,
+    /// The payload format does not match the one specified by the Payload Format Indicator.
+    PayloadFormatInvalid,
+    /// The Server has does not support retained messages.
+    RetainNotSupported,
+    /// The Client specified a QoS greater than the QoS specified in a Maximum QoS in the CONNACK.
+    QoSNotSupported,
+    /// The Client should temporarily change its Server.
+    UseAnotherServer,
+    /// The Server is moved and the Client should permanently change its server location.
+    ServerMoved,
+    /// The Server does not support Shared Subscriptions.
+    SharedSubscriptionNotSupported,
+    /// This connection is closed because the connection rate is too high.
+    ConnectionRateExceeded,
+    /// The maximum connection time authorized for this connection has been exceeded.
+    MaximumConnectTime,
+    /// The Server does not support Subscription Identifiers; the subscription is not accepted.
+    SubscriptionIdentifiersNotSupported,
+    /// The Server does not support Wildcard subscription; the subscription is not accepted.
+    WildcardSubscriptionsNotSupported,
+}
+
+//--------------------------- Ping packet -------------------------------
+
+// struct Ping;
+// struct PingResponse;
+
+//------------------------------------------------------------------------
+
+//--------------------------- PubRec packet -------------------------------
+
+/// Return code in connack
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum PubRecReason {
+    Success,
+    NoMatchingSubscribers,
+    UnspecifiedError,
+    ImplementationSpecificError,
+    NotAuthorized,
+    TopicNameInvalid,
+    PacketIdentifierInUse,
+    QuotaExceeded,
+    PayloadFormatInvalid,
+}
+
+/// Acknowledgement to QoS1 publish
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubRec {
+    pub pkid: u16,
+    pub reason: PubRecReason,
+}
+
+impl PubRec {
+    pub fn new(pkid: u16) -> Self {
+        Self {
+            pkid,
+            reason: PubRecReason::Success,
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubRecProperties {
+    pub reason_string: Option<String>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+//------------------------------------------------------------------------
+
+//--------------------------- PubComp packet -------------------------------
+
+/// Return code in connack
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum PubCompReason {
+    Success,
+    PacketIdentifierNotFound,
+}
+
+/// QoS2 Assured publish complete, in response to PUBREL packet
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubComp {
+    pub pkid: u16,
+    pub reason: PubCompReason,
+}
+
+impl PubComp {
+    pub fn new(pkid: u16) -> Self {
+        Self {
+            pkid,
+            reason: PubCompReason::Success,
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubCompProperties {
+    pub reason_string: Option<String>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+//------------------------------------------------------------------------
+
+//--------------------------- PubRel packet -------------------------------
+
+/// Return code in connack
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum PubRelReason {
+    Success,
+    PacketIdentifierNotFound,
+}
+
+/// QoS2 Publish release, in response to PUBREC packet
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubRel {
+    pub pkid: u16,
+    pub reason: PubRelReason,
+}
+
+impl PubRel {
+    pub fn new(pkid: u16) -> Self {
+        Self {
+            pkid,
+            reason: PubRelReason::Success,
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PubRelProperties {
+    pub reason_string: Option<String>,
+    pub user_properties: Vec<(String, String)>,
+}
+
+//------------------------------------------------------------------------
+
+/// Quality of service
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
+#[allow(clippy::enum_variant_names)]
+pub enum QoS {
+    AtMostOnce = 0,
+    AtLeastOnce = 1,
+    ExactlyOnce = 2,
+}
+
+impl Default for QoS {
+    fn default() -> Self {
+        Self::AtMostOnce
+    }
+}
+
+/// Maps a number to QoS
+pub fn qos(num: u8) -> Option<QoS> {
+    match num {
+        0 => Some(QoS::AtMostOnce),
+        1 => Some(QoS::AtLeastOnce),
+        2 => Some(QoS::ExactlyOnce),
+        _ => None,
+    }
+}
+
+/// Checks if a topic or topic filter has wildcards
+pub fn has_wildcards(s: &str) -> bool {
+    s.contains('+') || s.contains('#')
+}
+
+/// Checks if a topic is valid
+pub fn valid_topic(topic: &str) -> bool {
+    if topic.contains('+') {
+        return false;
+    }
+
+    if topic.contains('#') {
+        return false;
+    }
+
+    true
+}
+
+/// Checks if the filter is valid
+///
+/// https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106
+pub fn valid_filter(filter: &str) -> bool {
+    if filter.is_empty() {
+        return false;
+    }
+
+    let hirerarchy = filter.split('/').collect::<Vec<&str>>();
+    if let Some((last, remaining)) = hirerarchy.split_last() {
+        // # is not allowed in filer except as a last entry
+        // invalid: sport/tennis#/player
+        // invalid: sport/tennis/#/ranking
+        for entry in remaining.iter() {
+            if entry.contains('#') {
+                return false;
+            }
+        }
+
+        // only single '#" is allowed in last entry
+        // invalid: sport/tennis#
+        if last.len() != 1 && last.contains('#') {
+            return false;
+        }
+    }
+
+    true
+}
+
+/// Checks if topic matches a filter. topic and filter validation isn't done here.
+///
+/// **NOTE**: 'topic' is a misnomer in the arg. this can also be used to match 2 wild subscriptions
+/// **NOTE**: make sure a topic is validated during a publish and filter is validated
+/// during a subscribe
+pub fn matches(topic: &str, filter: &str) -> bool {
+    if !topic.is_empty() && topic[..1].contains('$') {
+        return false;
+    }
+
+    let mut topics = topic.split('/');
+    let mut filters = filter.split('/');
+
+    for f in filters.by_ref() {
+        // "#" being the last element is validated by the broker with 'valid_filter'
+        if f == "#" {
+            return true;
+        }
+
+        // filter still has remaining elements
+        // filter = a/b/c/# should match topci = a/b/c
+        // filter = a/b/c/d should not match topic = a/b/c
+        let top = topics.next();
+        match top {
+            Some(t) if t == "#" => return false,
+            Some(_) if f == "+" => continue,
+            Some(t) if f != t => return false,
+            Some(_) => continue,
+            None => return false,
+        }
+    }
+
+    // topic has remaining elements and filter's last element isn't "#"
+    if topics.next().is_some() {
+        return false;
+    }
+
+    true
+}
+
+/// Error during serialization and deserialization
+#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
+pub enum Error {
+    #[error("Invalid return code received as response for connect = {0}")]
+    InvalidConnectReturnCode(u8),
+    #[error("Invalid reason = {0}")]
+    InvalidReason(u8),
+    #[error("Invalid reason = {0}")]
+    InvalidRemainingLength(usize),
+    #[error("Invalid protocol used")]
+    InvalidProtocol,
+    #[error("Invalid protocol level")]
+    InvalidProtocolLevel(u8),
+    #[error("Invalid packet format")]
+    IncorrectPacketFormat,
+    #[error("Invalid packet type = {0}")]
+    InvalidPacketType(u8),
+    #[error("Invalid retain forward rule = {0}")]
+    InvalidRetainForwardRule(u8),
+    #[error("Invalid QoS level = {0}")]
+    InvalidQoS(u8),
+    #[error("Invalid subscribe reason code = {0}")]
+    InvalidSubscribeReasonCode(u8),
+    #[error("Packet received has id Zero")]
+    PacketIdZero,
+    #[error("Empty Subscription")]
+    EmptySubscription,
+    #[error("Subscription had id Zero")]
+    SubscriptionIdZero,
+    #[error("Payload size is incorrect")]
+    PayloadSizeIncorrect,
+    #[error("Payload is too long")]
+    PayloadTooLong,
+    #[error("Payload size has been exceeded by {0} bytes")]
+    PayloadSizeLimitExceeded(usize),
+    #[error("Payload is required")]
+    PayloadRequired,
+    #[error("Payload is required = {0}")]
+    PayloadNotUtf8(#[from] Utf8Error),
+    #[error("Topic not utf-8")]
+    TopicNotUtf8,
+    #[error("Promised boundary crossed, contains {0} bytes")]
+    BoundaryCrossed(usize),
+    #[error("Packet is malformed")]
+    MalformedPacket,
+    #[error("Remaining length is malformed")]
+    MalformedRemainingLength,
+    #[error("Invalid property type = {0}")]
+    InvalidPropertyType(u8),
+    /// More bytes required to frame packet. Argument
+    /// implies minimum additional bytes required to
+    /// proceed further
+    #[error("Insufficient number of bytes to frame packet, {0} more bytes required")]
+    InsufficientBytes(usize),
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/connack.rs b/rumqttc/src/v5/mqttbytes/v5/connack.rs
new file mode 100644
index 000000000..8317a6f51
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/connack.rs
@@ -0,0 +1,422 @@
+use super::*;
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+fn len(_: &ConnAck, properties: &Option<ConnAckProperties>) -> usize {
+    let mut len = 1  // session present
+                + 1; // code
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(ConnAck, Option<ConnAckProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+
+    let flags = read_u8(&mut bytes)?;
+    let return_code = read_u8(&mut bytes)?;
+    let properties = properties::read(&mut bytes)?;
+
+    let session_present = (flags & 0x01) == 1;
+    let code = connect_return(return_code)?;
+    let connack = ConnAck {
+        session_present,
+        code,
+    };
+
+    Ok((connack, properties))
+}
+
+pub fn write(
+    connack: &ConnAck,
+    properties: &Option<ConnAckProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    let len = len(connack, properties);
+    buffer.put_u8(0x20);
+
+    let count = write_remaining_length(buffer, len)?;
+    buffer.put_u8(connack.session_present as u8);
+    buffer.put_u8(connect_code(connack.code));
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    Ok(1 + count + len)
+}
+
+mod properties {
+    use super::*;
+
+    pub fn len(properties: &ConnAckProperties) -> usize {
+        let mut len = 0;
+
+        if properties.session_expiry_interval.is_some() {
+            len += 1 + 4;
+        }
+
+        if properties.receive_max.is_some() {
+            len += 1 + 2;
+        }
+
+        if properties.max_qos.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.retain_available.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.max_packet_size.is_some() {
+            len += 1 + 4;
+        }
+
+        if let Some(id) = &properties.assigned_client_identifier {
+            len += 1 + 2 + id.len();
+        }
+
+        if properties.topic_alias_max.is_some() {
+            len += 1 + 2;
+        }
+
+        if let Some(reason) = &properties.reason_string {
+            len += 1 + 2 + reason.len();
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        if properties.wildcard_subscription_available.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.subscription_identifiers_available.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.shared_subscription_available.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.server_keep_alive.is_some() {
+            len += 1 + 2;
+        }
+
+        if let Some(info) = &properties.response_information {
+            len += 1 + 2 + info.len();
+        }
+
+        if let Some(reference) = &properties.server_reference {
+            len += 1 + 2 + reference.len();
+        }
+
+        if let Some(authentication_method) = &properties.authentication_method {
+            len += 1 + 2 + authentication_method.len();
+        }
+
+        if let Some(authentication_data) = &properties.authentication_data {
+            len += 1 + 2 + authentication_data.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<ConnAckProperties>, Error> {
+        let mut session_expiry_interval = None;
+        let mut receive_max = None;
+        let mut max_qos = None;
+        let mut retain_available = None;
+        let mut max_packet_size = None;
+        let mut assigned_client_identifier = None;
+        let mut topic_alias_max = None;
+        let mut reason_string = None;
+        let mut user_properties = Vec::new();
+        let mut wildcard_subscription_available = None;
+        let mut subscription_identifiers_available = None;
+        let mut shared_subscription_available = None;
+        let mut server_keep_alive = None;
+        let mut response_information = None;
+        let mut server_reference = None;
+        let mut authentication_method = None;
+        let mut authentication_data = None;
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::SessionExpiryInterval => {
+                    session_expiry_interval = Some(read_u32(bytes)?);
+                    cursor += 4;
+                }
+                PropertyType::ReceiveMaximum => {
+                    receive_max = Some(read_u16(bytes)?);
+                    cursor += 2;
+                }
+                PropertyType::MaximumQos => {
+                    max_qos = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::RetainAvailable => {
+                    retain_available = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::AssignedClientIdentifier => {
+                    let id = read_mqtt_string(bytes)?;
+                    cursor += 2 + id.len();
+                    assigned_client_identifier = Some(id);
+                }
+                PropertyType::MaximumPacketSize => {
+                    max_packet_size = Some(read_u32(bytes)?);
+                    cursor += 4;
+                }
+                PropertyType::TopicAliasMaximum => {
+                    topic_alias_max = Some(read_u16(bytes)?);
+                    cursor += 2;
+                }
+                PropertyType::ReasonString => {
+                    let reason = read_mqtt_string(bytes)?;
+                    cursor += 2 + reason.len();
+                    reason_string = Some(reason);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                PropertyType::WildcardSubscriptionAvailable => {
+                    wildcard_subscription_available = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::SubscriptionIdentifierAvailable => {
+                    subscription_identifiers_available = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::SharedSubscriptionAvailable => {
+                    shared_subscription_available = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::ServerKeepAlive => {
+                    server_keep_alive = Some(read_u16(bytes)?);
+                    cursor += 2;
+                }
+                PropertyType::ResponseInformation => {
+                    let info = read_mqtt_string(bytes)?;
+                    cursor += 2 + info.len();
+                    response_information = Some(info);
+                }
+                PropertyType::ServerReference => {
+                    let reference = read_mqtt_string(bytes)?;
+                    cursor += 2 + reference.len();
+                    server_reference = Some(reference);
+                }
+                PropertyType::AuthenticationMethod => {
+                    let method = read_mqtt_string(bytes)?;
+                    cursor += 2 + method.len();
+                    authentication_method = Some(method);
+                }
+                PropertyType::AuthenticationData => {
+                    let data = read_mqtt_bytes(bytes)?;
+                    cursor += 2 + data.len();
+                    authentication_data = Some(data);
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(ConnAckProperties {
+            session_expiry_interval,
+            receive_max,
+            max_qos,
+            retain_available,
+            max_packet_size,
+            assigned_client_identifier,
+            topic_alias_max,
+            reason_string,
+            user_properties,
+            wildcard_subscription_available,
+            subscription_identifiers_available,
+            shared_subscription_available,
+            server_keep_alive,
+            response_information,
+            server_reference,
+            authentication_method,
+            authentication_data,
+        }))
+    }
+
+    pub fn write(properties: &ConnAckProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(session_expiry_interval) = properties.session_expiry_interval {
+            buffer.put_u8(PropertyType::SessionExpiryInterval as u8);
+            buffer.put_u32(session_expiry_interval);
+        }
+
+        if let Some(receive_maximum) = properties.receive_max {
+            buffer.put_u8(PropertyType::ReceiveMaximum as u8);
+            buffer.put_u16(receive_maximum);
+        }
+
+        if let Some(qos) = properties.max_qos {
+            buffer.put_u8(PropertyType::MaximumQos as u8);
+            buffer.put_u8(qos);
+        }
+
+        if let Some(retain_available) = properties.retain_available {
+            buffer.put_u8(PropertyType::RetainAvailable as u8);
+            buffer.put_u8(retain_available);
+        }
+
+        if let Some(max_packet_size) = properties.max_packet_size {
+            buffer.put_u8(PropertyType::MaximumPacketSize as u8);
+            buffer.put_u32(max_packet_size);
+        }
+
+        if let Some(id) = &properties.assigned_client_identifier {
+            buffer.put_u8(PropertyType::AssignedClientIdentifier as u8);
+            write_mqtt_string(buffer, id);
+        }
+
+        if let Some(topic_alias_max) = properties.topic_alias_max {
+            buffer.put_u8(PropertyType::TopicAliasMaximum as u8);
+            buffer.put_u16(topic_alias_max);
+        }
+
+        if let Some(reason) = &properties.reason_string {
+            buffer.put_u8(PropertyType::ReasonString as u8);
+            write_mqtt_string(buffer, reason);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        if let Some(w) = properties.wildcard_subscription_available {
+            buffer.put_u8(PropertyType::WildcardSubscriptionAvailable as u8);
+            buffer.put_u8(w);
+        }
+
+        if let Some(s) = properties.subscription_identifiers_available {
+            buffer.put_u8(PropertyType::SubscriptionIdentifierAvailable as u8);
+            buffer.put_u8(s);
+        }
+
+        if let Some(s) = properties.shared_subscription_available {
+            buffer.put_u8(PropertyType::SharedSubscriptionAvailable as u8);
+            buffer.put_u8(s);
+        }
+
+        if let Some(keep_alive) = properties.server_keep_alive {
+            buffer.put_u8(PropertyType::ServerKeepAlive as u8);
+            buffer.put_u16(keep_alive);
+        }
+
+        if let Some(info) = &properties.response_information {
+            buffer.put_u8(PropertyType::ResponseInformation as u8);
+            write_mqtt_string(buffer, info);
+        }
+
+        if let Some(reference) = &properties.server_reference {
+            buffer.put_u8(PropertyType::ServerReference as u8);
+            write_mqtt_string(buffer, reference);
+        }
+
+        if let Some(authentication_method) = &properties.authentication_method {
+            buffer.put_u8(PropertyType::AuthenticationMethod as u8);
+            write_mqtt_string(buffer, authentication_method);
+        }
+
+        if let Some(authentication_data) = &properties.authentication_data {
+            buffer.put_u8(PropertyType::AuthenticationData as u8);
+            write_mqtt_bytes(buffer, authentication_data);
+        }
+
+        Ok(())
+    }
+}
+
+/// Connection return code type
+fn connect_return(num: u8) -> Result<ConnectReturnCode, Error> {
+    let code = match num {
+        0 => ConnectReturnCode::Success,
+        128 => ConnectReturnCode::UnspecifiedError,
+        129 => ConnectReturnCode::MalformedPacket,
+        130 => ConnectReturnCode::ProtocolError,
+        131 => ConnectReturnCode::ImplementationSpecificError,
+        132 => ConnectReturnCode::UnsupportedProtocolVersion,
+        133 => ConnectReturnCode::ClientIdentifierNotValid,
+        134 => ConnectReturnCode::BadUserNamePassword,
+        135 => ConnectReturnCode::NotAuthorized,
+        136 => ConnectReturnCode::ServerUnavailable,
+        137 => ConnectReturnCode::ServerBusy,
+        138 => ConnectReturnCode::Banned,
+        140 => ConnectReturnCode::BadAuthenticationMethod,
+        144 => ConnectReturnCode::TopicNameInvalid,
+        149 => ConnectReturnCode::PacketTooLarge,
+        151 => ConnectReturnCode::QuotaExceeded,
+        153 => ConnectReturnCode::PayloadFormatInvalid,
+        154 => ConnectReturnCode::RetainNotSupported,
+        155 => ConnectReturnCode::QoSNotSupported,
+        156 => ConnectReturnCode::UseAnotherServer,
+        157 => ConnectReturnCode::ServerMoved,
+        159 => ConnectReturnCode::ConnectionRateExceeded,
+        num => return Err(Error::InvalidConnectReturnCode(num)),
+    };
+
+    Ok(code)
+}
+
+fn connect_code(return_code: ConnectReturnCode) -> u8 {
+    match return_code {
+        ConnectReturnCode::Success => 0,
+        ConnectReturnCode::UnspecifiedError => 128,
+        ConnectReturnCode::MalformedPacket => 129,
+        ConnectReturnCode::ProtocolError => 130,
+        ConnectReturnCode::ImplementationSpecificError => 131,
+        ConnectReturnCode::UnsupportedProtocolVersion => 132,
+        ConnectReturnCode::ClientIdentifierNotValid => 133,
+        ConnectReturnCode::BadUserNamePassword => 134,
+        ConnectReturnCode::NotAuthorized => 135,
+        ConnectReturnCode::ServerUnavailable => 136,
+        ConnectReturnCode::ServerBusy => 137,
+        ConnectReturnCode::Banned => 138,
+        ConnectReturnCode::BadAuthenticationMethod => 140,
+        ConnectReturnCode::TopicNameInvalid => 144,
+        ConnectReturnCode::PacketTooLarge => 149,
+        ConnectReturnCode::QuotaExceeded => 151,
+        ConnectReturnCode::PayloadFormatInvalid => 153,
+        ConnectReturnCode::RetainNotSupported => 154,
+        ConnectReturnCode::QoSNotSupported => 155,
+        ConnectReturnCode::UseAnotherServer => 156,
+        ConnectReturnCode::ServerMoved => 157,
+        ConnectReturnCode::ConnectionRateExceeded => 159,
+        _ => unreachable!(),
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/connect.rs b/rumqttc/src/v5/mqttbytes/v5/connect.rs
new file mode 100644
index 000000000..e8b27b815
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/connect.rs
@@ -0,0 +1,588 @@
+use super::Connect;
+
+use super::*;
+use bytes::{Buf, Bytes};
+
+#[allow(clippy::type_complexity)]
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<
+    (
+        Connect,
+        Option<ConnectProperties>,
+        Option<LastWill>,
+        Option<LastWillProperties>,
+        Option<Login>,
+    ),
+    Error,
+> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+
+    // Variable header
+    let protocol_name = read_mqtt_string(&mut bytes)?;
+    let protocol_level = read_u8(&mut bytes)?;
+    if protocol_name != "MQTT" {
+        return Err(Error::InvalidProtocol);
+    }
+
+    if protocol_level != 5 {
+        return Err(Error::InvalidProtocolLevel(protocol_level));
+    }
+
+    let connect_flags = read_u8(&mut bytes)?;
+    let clean_session = (connect_flags & 0b10) != 0;
+    let keep_alive = read_u16(&mut bytes)?;
+
+    let properties = properties::read(&mut bytes)?;
+
+    let client_id = read_mqtt_string(&mut bytes)?;
+    let (will, willproperties) = will::read(connect_flags, &mut bytes)?;
+    let login = login::read(connect_flags, &mut bytes)?;
+
+    let connect = Connect {
+        keep_alive,
+        client_id,
+        clean_session,
+    };
+
+    Ok((connect, properties, will, willproperties, login))
+}
+
+pub fn write(
+    connect: &Connect,
+    will: &Option<LastWill>,
+    will_properties: &Option<LastWillProperties>,
+    l: &Option<Login>,
+    properties: &Option<ConnectProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    let len = {
+        let mut len = 2 + "MQTT".len() // protocol name
+                        + 1            // protocol version
+                        + 1            // connect flags
+                        + 2; // keep alive
+
+        if let Some(p) = properties {
+            let properties_len = properties::len(p);
+            let properties_len_len = len_len(properties_len);
+            len += properties_len_len + properties_len;
+        } else {
+            // just 1 byte representing 0 len
+            len += 1;
+        }
+
+        len += 2 + connect.client_id.len();
+
+        // last will len
+        if let Some(w) = will {
+            len += will::len(w, will_properties);
+        }
+
+        // username and password len
+        if let Some(l) = l {
+            len += login::len(l);
+        }
+
+        len
+    };
+
+    buffer.put_u8(0b0001_0000);
+    let count = write_remaining_length(buffer, len)?;
+    write_mqtt_string(buffer, "MQTT");
+
+    buffer.put_u8(0x05);
+    let flags_index = 1 + count + 2 + 4 + 1;
+
+    let mut connect_flags = 0;
+    if connect.clean_session {
+        connect_flags |= 0x02;
+    }
+
+    buffer.put_u8(connect_flags);
+    buffer.put_u16(connect.keep_alive);
+
+    match properties {
+        Some(p) => properties::write(p, buffer)?,
+        None => {
+            write_remaining_length(buffer, 0)?;
+        }
+    };
+
+    write_mqtt_string(buffer, &connect.client_id);
+
+    if let Some(w) = will {
+        connect_flags |= will::write(w, will_properties, buffer)?;
+    }
+
+    if let Some(l) = l {
+        connect_flags |= login::write(l, buffer);
+    }
+
+    // update connect flags
+    buffer[flags_index] = connect_flags;
+    Ok(len)
+}
+
+mod will {
+    use super::*;
+
+    pub fn len(will: &LastWill, properties: &Option<LastWillProperties>) -> usize {
+        let mut len = 0;
+
+        if let Some(p) = properties {
+            let properties_len = willproperties::len(p);
+            let properties_len_len = len_len(properties_len);
+            len += properties_len_len + properties_len;
+        } else {
+            // just 1 byte representing 0 len
+            len += 1;
+        }
+
+        len += 2 + will.topic.len() + 2 + will.message.len();
+        len
+    }
+
+    pub fn read(
+        connect_flags: u8,
+        bytes: &mut Bytes,
+    ) -> Result<(Option<LastWill>, Option<LastWillProperties>), Error> {
+        let o = match connect_flags & 0b100 {
+            0 if (connect_flags & 0b0011_1000) != 0 => {
+                return Err(Error::IncorrectPacketFormat);
+            }
+            0 => (None, None),
+            _ => {
+                // Properties in variable header
+                let properties = willproperties::read(bytes)?;
+
+                let will_topic = read_mqtt_bytes(bytes)?;
+                let will_message = read_mqtt_bytes(bytes)?;
+                let qos_num = (connect_flags & 0b11000) >> 3;
+                let will_qos = qos(qos_num).ok_or(Error::InvalidQoS(qos_num))?;
+                let will = Some(LastWill {
+                    topic: will_topic,
+                    message: will_message,
+                    qos: will_qos,
+                    retain: (connect_flags & 0b0010_0000) != 0,
+                });
+
+                (will, properties)
+            }
+        };
+
+        Ok(o)
+    }
+
+    pub fn write(
+        will: &LastWill,
+        properties: &Option<LastWillProperties>,
+        buffer: &mut BytesMut,
+    ) -> Result<u8, Error> {
+        let mut connect_flags = 0;
+
+        connect_flags |= 0x04 | (will.qos as u8) << 3;
+        if will.retain {
+            connect_flags |= 0x20;
+        }
+
+        if let Some(p) = properties {
+            willproperties::write(p, buffer)?;
+        } else {
+            write_remaining_length(buffer, 0)?;
+        }
+
+        write_mqtt_bytes(buffer, &will.topic);
+        write_mqtt_bytes(buffer, &will.message);
+        Ok(connect_flags)
+    }
+}
+
+mod willproperties {
+    use super::*;
+
+    pub fn len(properties: &LastWillProperties) -> usize {
+        let mut len = 0;
+
+        if properties.delay_interval.is_some() {
+            len += 1 + 4;
+        }
+
+        if properties.payload_format_indicator.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.message_expiry_interval.is_some() {
+            len += 1 + 4;
+        }
+
+        if let Some(typ) = &properties.content_type {
+            len += 1 + 2 + typ.len()
+        }
+
+        if let Some(topic) = &properties.response_topic {
+            len += 1 + 2 + topic.len()
+        }
+
+        if let Some(data) = &properties.correlation_data {
+            len += 1 + 2 + data.len()
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<LastWillProperties>, Error> {
+        let mut delay_interval = None;
+        let mut payload_format_indicator = None;
+        let mut message_expiry_interval = None;
+        let mut content_type = None;
+        let mut response_topic = None;
+        let mut correlation_data = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::WillDelayInterval => {
+                    delay_interval = Some(read_u32(bytes)?);
+                    cursor += 4;
+                }
+                PropertyType::PayloadFormatIndicator => {
+                    payload_format_indicator = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::MessageExpiryInterval => {
+                    message_expiry_interval = Some(read_u32(bytes)?);
+                    cursor += 4;
+                }
+                PropertyType::ContentType => {
+                    let typ = read_mqtt_string(bytes)?;
+                    cursor += 2 + typ.len();
+                    content_type = Some(typ);
+                }
+                PropertyType::ResponseTopic => {
+                    let topic = read_mqtt_string(bytes)?;
+                    cursor += 2 + topic.len();
+                    response_topic = Some(topic);
+                }
+                PropertyType::CorrelationData => {
+                    let data = read_mqtt_bytes(bytes)?;
+                    cursor += 2 + data.len();
+                    correlation_data = Some(data);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(LastWillProperties {
+            delay_interval,
+            payload_format_indicator,
+            message_expiry_interval,
+            content_type,
+            response_topic,
+            correlation_data,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &LastWillProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(delay_interval) = properties.delay_interval {
+            buffer.put_u8(PropertyType::WillDelayInterval as u8);
+            buffer.put_u32(delay_interval);
+        }
+
+        if let Some(payload_format_indicator) = properties.payload_format_indicator {
+            buffer.put_u8(PropertyType::PayloadFormatIndicator as u8);
+            buffer.put_u8(payload_format_indicator);
+        }
+
+        if let Some(message_expiry_interval) = properties.message_expiry_interval {
+            buffer.put_u8(PropertyType::MessageExpiryInterval as u8);
+            buffer.put_u32(message_expiry_interval);
+        }
+
+        if let Some(typ) = &properties.content_type {
+            buffer.put_u8(PropertyType::ContentType as u8);
+            write_mqtt_string(buffer, typ);
+        }
+
+        if let Some(topic) = &properties.response_topic {
+            buffer.put_u8(PropertyType::ResponseTopic as u8);
+            write_mqtt_string(buffer, topic);
+        }
+
+        if let Some(data) = &properties.correlation_data {
+            buffer.put_u8(PropertyType::CorrelationData as u8);
+            write_mqtt_bytes(buffer, data);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
+
+mod login {
+    use super::*;
+
+    // pub fn new<U: Into<String>, P: Into<String>>(u: U, p: P) -> Login {
+    //     Login {
+    //         username: u.into(),
+    //         password: p.into(),
+    //     }
+    // }
+
+    pub fn read(connect_flags: u8, bytes: &mut Bytes) -> Result<Option<Login>, Error> {
+        let username = match connect_flags & 0b1000_0000 {
+            0 => String::new(),
+            _ => read_mqtt_string(bytes)?,
+        };
+
+        let password = match connect_flags & 0b0100_0000 {
+            0 => String::new(),
+            _ => read_mqtt_string(bytes)?,
+        };
+
+        if username.is_empty() && password.is_empty() {
+            Ok(None)
+        } else {
+            Ok(Some(Login { username, password }))
+        }
+    }
+
+    pub fn len(login: &Login) -> usize {
+        let mut len = 0;
+
+        if !login.username.is_empty() {
+            len += 2 + login.username.len();
+        }
+
+        if !login.password.is_empty() {
+            len += 2 + login.password.len();
+        }
+
+        len
+    }
+
+    pub fn write(login: &Login, buffer: &mut BytesMut) -> u8 {
+        let mut connect_flags = 0;
+        if !login.username.is_empty() {
+            connect_flags |= 0x80;
+            write_mqtt_string(buffer, &login.username);
+        }
+
+        if !login.password.is_empty() {
+            connect_flags |= 0x40;
+            write_mqtt_string(buffer, &login.password);
+        }
+
+        connect_flags
+    }
+}
+
+mod properties {
+    use super::*;
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<ConnectProperties>, Error> {
+        let mut session_expiry_interval = None;
+        let mut receive_maximum = None;
+        let mut max_packet_size = None;
+        let mut topic_alias_max = None;
+        let mut request_response_info = None;
+        let mut request_problem_info = None;
+        let mut user_properties = Vec::new();
+        let mut authentication_method = None;
+        let mut authentication_data = None;
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+            match property(prop)? {
+                PropertyType::SessionExpiryInterval => {
+                    session_expiry_interval = Some(read_u32(bytes)?);
+                    cursor += 4;
+                }
+                PropertyType::ReceiveMaximum => {
+                    receive_maximum = Some(read_u16(bytes)?);
+                    cursor += 2;
+                }
+                PropertyType::MaximumPacketSize => {
+                    max_packet_size = Some(read_u32(bytes)?);
+                    cursor += 4;
+                }
+                PropertyType::TopicAliasMaximum => {
+                    topic_alias_max = Some(read_u16(bytes)?);
+                    cursor += 2;
+                }
+                PropertyType::RequestResponseInformation => {
+                    request_response_info = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::RequestProblemInformation => {
+                    request_problem_info = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                PropertyType::AuthenticationMethod => {
+                    let method = read_mqtt_string(bytes)?;
+                    cursor += 2 + method.len();
+                    authentication_method = Some(method);
+                }
+                PropertyType::AuthenticationData => {
+                    let data = read_mqtt_bytes(bytes)?;
+                    cursor += 2 + data.len();
+                    authentication_data = Some(data);
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(ConnectProperties {
+            session_expiry_interval,
+            receive_maximum,
+            max_packet_size,
+            topic_alias_max,
+            request_response_info,
+            request_problem_info,
+            user_properties,
+            authentication_method,
+            authentication_data,
+        }))
+    }
+
+    pub fn len(properties: &ConnectProperties) -> usize {
+        let mut len = 0;
+
+        if properties.session_expiry_interval.is_some() {
+            len += 1 + 4;
+        }
+
+        if properties.receive_maximum.is_some() {
+            len += 1 + 2;
+        }
+
+        if properties.max_packet_size.is_some() {
+            len += 1 + 4;
+        }
+
+        if properties.topic_alias_max.is_some() {
+            len += 1 + 2;
+        }
+
+        if properties.request_response_info.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.request_problem_info.is_some() {
+            len += 1 + 1;
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        if let Some(authentication_method) = &properties.authentication_method {
+            len += 1 + 2 + authentication_method.len();
+        }
+
+        if let Some(authentication_data) = &properties.authentication_data {
+            len += 1 + 2 + authentication_data.len();
+        }
+
+        len
+    }
+
+    pub fn write(properties: &ConnectProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(session_expiry_interval) = properties.session_expiry_interval {
+            buffer.put_u8(PropertyType::SessionExpiryInterval as u8);
+            buffer.put_u32(session_expiry_interval);
+        }
+
+        if let Some(receive_maximum) = properties.receive_maximum {
+            buffer.put_u8(PropertyType::ReceiveMaximum as u8);
+            buffer.put_u16(receive_maximum);
+        }
+
+        if let Some(max_packet_size) = properties.max_packet_size {
+            buffer.put_u8(PropertyType::MaximumPacketSize as u8);
+            buffer.put_u32(max_packet_size);
+        }
+
+        if let Some(topic_alias_max) = properties.topic_alias_max {
+            buffer.put_u8(PropertyType::TopicAliasMaximum as u8);
+            buffer.put_u16(topic_alias_max);
+        }
+
+        if let Some(request_response_info) = properties.request_response_info {
+            buffer.put_u8(PropertyType::RequestResponseInformation as u8);
+            buffer.put_u8(request_response_info);
+        }
+
+        if let Some(request_problem_info) = properties.request_problem_info {
+            buffer.put_u8(PropertyType::RequestProblemInformation as u8);
+            buffer.put_u8(request_problem_info);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        if let Some(authentication_method) = &properties.authentication_method {
+            buffer.put_u8(PropertyType::AuthenticationMethod as u8);
+            write_mqtt_string(buffer, authentication_method);
+        }
+
+        if let Some(authentication_data) = &properties.authentication_data {
+            buffer.put_u8(PropertyType::AuthenticationData as u8);
+            write_mqtt_bytes(buffer, authentication_data);
+        }
+
+        Ok(())
+    }
+}
diff --git a/rumqttc/src/v5/packet/disconnect.rs b/rumqttc/src/v5/mqttbytes/v5/disconnect.rs
similarity index 94%
rename from rumqttc/src/v5/packet/disconnect.rs
rename to rumqttc/src/v5/mqttbytes/v5/disconnect.rs
index 03d03894f..cab44a1cd 100644
--- a/rumqttc/src/v5/packet/disconnect.rs
+++ b/rumqttc/src/v5/mqttbytes/v5/disconnect.rs
@@ -135,14 +135,14 @@ pub struct Disconnect {
 }
 
 impl DisconnectProperties {
-    pub fn new() -> Self {
-        Self {
-            session_expiry_interval: None,
-            reason_string: None,
-            user_properties: Vec::new(),
-            server_reference: None,
-        }
-    }
+    // pub fn new() -> Self {
+    //     Self {
+    //         session_expiry_interval: None,
+    //         reason_string: None,
+    //         user_properties: Vec::new(),
+    //         server_reference: None,
+    //     }
+    // }
 
     fn len(&self) -> usize {
         let mut length = 0;
@@ -166,7 +166,7 @@ impl DisconnectProperties {
         length
     }
 
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<Self>, Error> {
+    pub fn extract(bytes: &mut Bytes) -> Result<Option<Self>, Error> {
         let (properties_len_len, properties_len) = length(bytes.iter())?;
 
         bytes.advance(properties_len_len);
@@ -184,27 +184,27 @@ impl DisconnectProperties {
 
         // read until cursor reaches property length. properties_len = 0 will skip this loop
         while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
+            let prop = read_u8(bytes)?;
             cursor += 1;
 
             match property(prop)? {
                 PropertyType::SessionExpiryInterval => {
-                    session_expiry_interval = Some(read_u32(&mut bytes)?);
+                    session_expiry_interval = Some(read_u32(bytes)?);
                     cursor += 4;
                 }
                 PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
+                    let reason = read_mqtt_string(bytes)?;
                     cursor += 2 + reason.len();
                     reason_string = Some(reason);
                 }
                 PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
                     cursor += 2 + key.len() + 2 + value.len();
                     user_properties.push((key, value));
                 }
                 PropertyType::ServerReference => {
-                    let reference = read_mqtt_string(&mut bytes)?;
+                    let reference = read_mqtt_string(bytes)?;
                     cursor += 2 + reference.len();
                     server_reference = Some(reference);
                 }
@@ -275,7 +275,7 @@ impl Disconnect {
             let properties_len_len = len_len(properties_len);
             length += properties_len_len + properties_len;
         } else {
-            length += 1; // Disconnect Reason Code
+            length += 1;
         }
 
         length
@@ -333,6 +333,12 @@ impl Disconnect {
     }
 }
 
+impl Default for Disconnect {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 #[cfg(test)]
 mod test {
     use bytes::BytesMut;
diff --git a/rumqttc/src/v5/packet/mod.rs b/rumqttc/src/v5/mqttbytes/v5/mod.rs
similarity index 67%
rename from rumqttc/src/v5/packet/mod.rs
rename to rumqttc/src/v5/mqttbytes/v5/mod.rs
index 4badb3a69..0cf63a352 100644
--- a/rumqttc/src/v5/packet/mod.rs
+++ b/rumqttc/src/v5/mqttbytes/v5/mod.rs
@@ -1,5 +1,8 @@
 use std::slice::Iter;
 
+use self::{disconnect::Disconnect, ping::pingreq};
+
+use super::*;
 use bytes::{Buf, BufMut, Bytes, BytesMut};
 
 mod connack;
@@ -16,39 +19,130 @@ mod subscribe;
 mod unsuback;
 mod unsubscribe;
 
-pub use connack::*;
-pub use connect::*;
-pub use disconnect::*;
-pub use ping::*;
-pub use puback::*;
-pub use pubcomp::*;
-pub use publish::*;
-pub use pubrec::*;
-pub use pubrel::*;
-pub use suback::*;
-pub use subscribe::*;
-pub use unsuback::*;
-pub use unsubscribe::*;
-
-/// Encapsulates all MQTT packet types
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Packet {
-    Connect(Connect),
+    Connect(
+        Connect,
+        Option<ConnectProperties>,
+        Option<LastWill>,
+        Option<LastWillProperties>,
+        Option<Login>,
+    ),
     ConnAck(ConnAck),
-    Publish(Publish),
-    PubAck(PubAck),
-    PubRec(PubRec),
-    PubRel(PubRel),
-    PubComp(PubComp),
-    Subscribe(Subscribe),
-    SubAck(SubAck),
+    Publish(Publish, Option<PublishProperties>),
+    PubAck(PubAck, Option<PubAckProperties>),
+    PingReq(PingReq),
+    PingResp(PingResp),
+    Subscribe(Subscribe, Option<SubscribeProperties>),
+    SubAck(SubAck, Option<SubAckProperties>),
+    PubRec(PubRec, Option<PubRecProperties>),
+    PubRel(PubRel, Option<PubRelProperties>),
+    PubComp(PubComp, Option<PubCompProperties>),
     Unsubscribe(Unsubscribe),
     UnsubAck(UnsubAck),
-    PingReq,
-    PingResp,
     Disconnect(Disconnect),
 }
 
+impl Packet {
+    /// Reads a stream of bytes and extracts next MQTT packet out of it
+    pub fn read(stream: &mut BytesMut, max_size: usize) -> Result<Packet, Error> {
+        let fixed_header = check(stream.iter(), max_size)?;
+
+        // Test with a stream with exactly the size to check border panics
+        let packet = stream.split_to(fixed_header.frame_length());
+        let packet_type = fixed_header.packet_type()?;
+
+        if fixed_header.remaining_len == 0 {
+            // no payload packets, Disconnect still has a bit more info
+            return match packet_type {
+                PacketType::PingReq => Ok(Packet::PingReq(PingReq)),
+                PacketType::PingResp => Ok(Packet::PingResp(PingResp)),
+                _ => Err(Error::PayloadRequired),
+            };
+        }
+
+        let packet = packet.freeze();
+        let packet = match packet_type {
+            PacketType::Connect => {
+                let (connect, properties, will, willproperties, login) =
+                    connect::read(fixed_header, packet)?;
+                Packet::Connect(connect, properties, will, willproperties, login)
+            }
+            PacketType::Publish => {
+                let (publish, properties) = publish::read(fixed_header, packet)?;
+                Packet::Publish(publish, properties)
+            }
+            PacketType::Subscribe => {
+                let (subscribe, properties) = subscribe::read(fixed_header, packet)?;
+                Packet::Subscribe(subscribe, properties)
+            }
+            PacketType::Unsubscribe => {
+                let (unsubscribe, _) = unsubscribe::read(fixed_header, packet)?;
+                Packet::Unsubscribe(unsubscribe)
+            }
+            PacketType::ConnAck => {
+                let (connack, _) = connack::read(fixed_header, packet)?;
+                Packet::ConnAck(connack)
+            }
+            PacketType::PubAck => {
+                let (puback, properties) = puback::read(fixed_header, packet)?;
+                Packet::PubAck(puback, properties)
+            }
+            PacketType::PubRec => {
+                let (pubrec, properties) = pubrec::read(fixed_header, packet)?;
+                Packet::PubRec(pubrec, properties)
+            }
+            PacketType::PubRel => {
+                let (pubrel, properties) = pubrel::read(fixed_header, packet)?;
+                Packet::PubRel(pubrel, properties)
+            }
+            PacketType::PubComp => {
+                let (pubcomp, properties) = pubcomp::read(fixed_header, packet)?;
+                Packet::PubComp(pubcomp, properties)
+            }
+            PacketType::SubAck => {
+                let (suback, properties) = suback::read(fixed_header, packet)?;
+                Packet::SubAck(suback, properties)
+            }
+            PacketType::UnsubAck => {
+                let (unsuback, _) = unsuback::read(fixed_header, packet)?;
+                Packet::UnsubAck(unsuback)
+            }
+            PacketType::PingReq => Packet::PingReq(PingReq),
+            PacketType::PingResp => Packet::PingResp(PingResp),
+            PacketType::Disconnect => {
+                let disconnect = Disconnect::read(fixed_header, packet)?;
+                Packet::Disconnect(disconnect)
+            }
+        };
+
+        Ok(packet)
+    }
+
+    pub fn write(&self, write: &mut BytesMut) -> Result<usize, Error> {
+        match self {
+            Self::Publish(publish, properties) => publish::write(publish, properties, write),
+            Self::Subscribe(subscription, properties) => {
+                subscribe::write(subscription, properties, write)
+            }
+            Self::Unsubscribe(unsubscribe) => unsubscribe::write(unsubscribe, &None, write),
+            Self::ConnAck(ack) => connack::write(ack, &None, write),
+            Self::PubAck(ack, properties) => puback::write(ack, properties, write),
+            Self::SubAck(ack, properties) => suback::write(ack, properties, write),
+            Self::UnsubAck(unsuback) => unsuback::write(unsuback, &None, write),
+            Self::PubRec(pubrec, properties) => pubrec::write(pubrec, properties, write),
+            Self::PubRel(pubrel, properties) => pubrel::write(pubrel, properties, write),
+            Self::PubComp(pubcomp, properties) => pubcomp::write(pubcomp, properties, write),
+            Self::Connect(connect, properties, will, will_properties, login) => {
+                connect::write(connect, will, will_properties, login, properties, write)
+            }
+            Self::PingReq(_) => pingreq::write(write),
+            Self::PingResp(_) => ping::pingresp::write(write),
+            Self::Disconnect(disconnect) => disconnect.write(write),
+        }
+    }
+}
+
 /// MQTT packet type
 #[repr(u8)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -101,74 +195,6 @@ enum PropertyType {
     SharedSubscriptionAvailable = 42,
 }
 
-/// Error during serialization and deserialization
-#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
-pub enum Error {
-    #[error("Expected Connect, received: {0:?}")]
-    NotConnect(PacketType),
-    #[error("Unexpected Connect")]
-    UnexpectedConnect,
-    #[error("Invalid Connect return code: {0}")]
-    InvalidConnectReturnCode(u8),
-    #[error("Invalid protocol")]
-    InvalidProtocol,
-    #[error("Invalid protocol level: {0}")]
-    InvalidProtocolLevel(u8),
-    #[error("Incorrect packet format")]
-    IncorrectPacketFormat,
-    #[error("Invalid packet type: {0}")]
-    InvalidPacketType(u8),
-    #[error("Invalid property type: {0}")]
-    InvalidPropertyType(u8),
-    #[error("Invalid QoS level: {0}")]
-    InvalidQoS(u8),
-    #[error("Invalid retain forward rule: {0}")]
-    InvalidRetainForwardRule(u8),
-    #[error("Invalid subscribe reason code: {0}")]
-    InvalidSubscribeReasonCode(u8),
-    #[error("Packet id Zero")]
-    PacketIdZero,
-    #[error("Payload size is incorrect")]
-    PayloadSizeIncorrect,
-    #[error("payload is too long")]
-    PayloadTooLong,
-    #[error("payload size limit exceeded: {0}")]
-    PayloadSizeLimitExceeded(usize),
-    #[error("Payload required")]
-    PayloadRequired,
-    #[error("Topic is not UTF-8")]
-    TopicNotUtf8,
-    #[error("Promised boundary crossed: {0}")]
-    BoundaryCrossed(usize),
-    #[error("Malformed packet")]
-    MalformedPacket,
-    #[error("Malformed remaining length")]
-    MalformedRemainingLength,
-    #[error("A Subscribe packet must contain atleast one filter")]
-    EmptySubscription,
-    /// More bytes required to frame packet. Argument
-    /// implies minimum additional bytes required to
-    /// proceed further
-    #[error("At least {0} more bytes required to frame packet")]
-    InsufficientBytes(usize),
-}
-
-/// Protocol type
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Protocol {
-    V4,
-    V5,
-}
-
-/// Quality of service
-#[repr(u8)]
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
-pub enum QoS {
-    AtMostOnce = 0,
-    AtLeastOnce = 1,
-    ExactlyOnce = 2,
-}
-
 /// Packet type from a byte
 ///
 /// ```ignore
@@ -350,44 +376,6 @@ fn length(stream: Iter<u8>) -> Result<(usize, usize), Error> {
     Ok((len_len, len))
 }
 
-/// Reads a stream of bytes and extracts next MQTT packet out of it
-pub fn read(stream: &mut BytesMut, max_size: usize) -> Result<Packet, Error> {
-    let fixed_header = check(stream.iter(), max_size)?;
-
-    // Test with a stream with exactly the size to check border panics
-    let packet = stream.split_to(fixed_header.frame_length());
-    let packet_type = fixed_header.packet_type()?;
-
-    if fixed_header.remaining_len == 0 {
-        // no payload packets
-        return match packet_type {
-            PacketType::PingReq => Ok(Packet::PingReq),
-            PacketType::PingResp => Ok(Packet::PingResp),
-            _ => Err(Error::PayloadRequired),
-        };
-    }
-
-    let packet = packet.freeze();
-    let packet = match packet_type {
-        PacketType::Connect => Packet::Connect(Connect::read(fixed_header, packet)?),
-        PacketType::ConnAck => Packet::ConnAck(ConnAck::read(fixed_header, packet)?),
-        PacketType::Publish => Packet::Publish(Publish::read(fixed_header, packet)?),
-        PacketType::PubAck => Packet::PubAck(PubAck::read(fixed_header, packet)?),
-        PacketType::PubRec => Packet::PubRec(PubRec::read(fixed_header, packet)?),
-        PacketType::PubRel => Packet::PubRel(PubRel::read(fixed_header, packet)?),
-        PacketType::PubComp => Packet::PubComp(PubComp::read(fixed_header, packet)?),
-        PacketType::Subscribe => Packet::Subscribe(Subscribe::read(fixed_header, packet)?),
-        PacketType::SubAck => Packet::SubAck(SubAck::read(fixed_header, packet)?),
-        PacketType::Unsubscribe => Packet::Unsubscribe(Unsubscribe::read(fixed_header, packet)?),
-        PacketType::UnsubAck => Packet::UnsubAck(UnsubAck::read(fixed_header, packet)?),
-        PacketType::PingReq => Packet::PingReq,
-        PacketType::PingResp => Packet::PingResp,
-        PacketType::Disconnect => Packet::Disconnect(Disconnect::read(fixed_header, packet)?),
-    };
-
-    Ok(packet)
-}
-
 /// Reads a series of bytes with a length from a byte stream
 fn read_mqtt_bytes(stream: &mut Bytes) -> Result<Bytes, Error> {
     let len = read_u16(stream)? as usize;
@@ -461,16 +449,6 @@ fn len_len(len: usize) -> usize {
     }
 }
 
-/// Maps a number to QoS
-pub fn qos(num: u8) -> Result<QoS, Error> {
-    match num {
-        0 => Ok(QoS::AtMostOnce),
-        1 => Ok(QoS::AtLeastOnce),
-        2 => Ok(QoS::ExactlyOnce),
-        qos => Err(Error::InvalidQoS(qos)),
-    }
-}
-
 /// After collecting enough bytes to frame a packet (packet's frame())
 /// , It's possible that content itself in the stream is wrong. Like expected
 /// packet id or qos not being present. In cases where `read_mqtt_string` or
diff --git a/rumqttc/src/v5/mqttbytes/v5/ping.rs b/rumqttc/src/v5/mqttbytes/v5/ping.rs
new file mode 100644
index 000000000..78c3d8453
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/ping.rs
@@ -0,0 +1,20 @@
+use super::*;
+use bytes::{BufMut, BytesMut};
+
+pub mod pingreq {
+    use super::*;
+
+    pub fn write(payload: &mut BytesMut) -> Result<usize, Error> {
+        payload.put_slice(&[0xC0, 0x00]);
+        Ok(2)
+    }
+}
+
+pub mod pingresp {
+    use super::*;
+
+    pub fn write(payload: &mut BytesMut) -> Result<usize, Error> {
+        payload.put_slice(&[0xD0, 0x00]);
+        Ok(2)
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/puback.rs b/rumqttc/src/v5/mqttbytes/v5/puback.rs
new file mode 100644
index 000000000..f744a2fbe
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/puback.rs
@@ -0,0 +1,195 @@
+use super::*;
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+fn len(puback: &PubAck, properties: &Option<PubAckProperties>) -> usize {
+    let mut len = 2 + 1; // pkid + reason
+
+    // If there are no properties, sending reason code is optional
+    if puback.reason == PubAckReason::Success && properties.is_none() {
+        return 2;
+    }
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        // just 1 byte representing 0 len properties
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(PubAck, Option<PubAckProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+    let pkid = read_u16(&mut bytes)?;
+
+    // No reason code or properties if remaining length == 2
+    if fixed_header.remaining_len == 2 {
+        return Ok((
+            PubAck {
+                pkid,
+                reason: PubAckReason::Success,
+            },
+            None,
+        ));
+    }
+
+    // No properties len or properties if remaining len > 2 but < 4
+    let ack_reason = read_u8(&mut bytes)?;
+    if fixed_header.remaining_len < 4 {
+        return Ok((
+            PubAck {
+                pkid,
+                reason: reason(ack_reason)?,
+            },
+            None,
+        ));
+    }
+
+    let puback = PubAck {
+        pkid,
+        reason: reason(ack_reason)?,
+    };
+
+    let properties = properties::read(&mut bytes)?;
+    Ok((puback, properties))
+}
+
+pub fn write(
+    puback: &PubAck,
+    properties: &Option<PubAckProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    let len = len(puback, properties);
+    buffer.put_u8(0x40);
+
+    let count = write_remaining_length(buffer, len)?;
+    buffer.put_u16(puback.pkid);
+
+    // Reason code is optional with success if there are no properties
+    if puback.reason == PubAckReason::Success && properties.is_none() {
+        return Ok(4);
+    }
+
+    buffer.put_u8(code(puback.reason));
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    Ok(1 + count + len)
+}
+
+mod properties {
+    use super::*;
+
+    pub fn len(properties: &PubAckProperties) -> usize {
+        let mut len = 0;
+
+        if let Some(reason) = &properties.reason_string {
+            len += 1 + 2 + reason.len();
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<PubAckProperties>, Error> {
+        let mut reason_string = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::ReasonString => {
+                    let reason = read_mqtt_string(bytes)?;
+                    cursor += 2 + reason.len();
+                    reason_string = Some(reason);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(PubAckProperties {
+            reason_string,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &PubAckProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(reason) = &properties.reason_string {
+            buffer.put_u8(PropertyType::ReasonString as u8);
+            write_mqtt_string(buffer, reason);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
+
+/// Connection return code type
+fn reason(num: u8) -> Result<PubAckReason, Error> {
+    let code = match num {
+        0 => PubAckReason::Success,
+        16 => PubAckReason::NoMatchingSubscribers,
+        128 => PubAckReason::UnspecifiedError,
+        131 => PubAckReason::ImplementationSpecificError,
+        135 => PubAckReason::NotAuthorized,
+        144 => PubAckReason::TopicNameInvalid,
+        145 => PubAckReason::PacketIdentifierInUse,
+        151 => PubAckReason::QuotaExceeded,
+        153 => PubAckReason::PayloadFormatInvalid,
+        num => return Err(Error::InvalidConnectReturnCode(num)),
+    };
+
+    Ok(code)
+}
+
+// TODO: Is typecasting significantly faster than functions?
+fn code(reason: PubAckReason) -> u8 {
+    match reason {
+        PubAckReason::Success => 0,
+        PubAckReason::NoMatchingSubscribers => 16,
+        PubAckReason::UnspecifiedError => 128,
+        PubAckReason::ImplementationSpecificError => 131,
+        PubAckReason::NotAuthorized => 135,
+        PubAckReason::TopicNameInvalid => 144,
+        PubAckReason::PacketIdentifierInUse => 145,
+        PubAckReason::QuotaExceeded => 151,
+        PubAckReason::PayloadFormatInvalid => 153,
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/pubcomp.rs b/rumqttc/src/v5/mqttbytes/v5/pubcomp.rs
new file mode 100644
index 000000000..f353d3d65
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/pubcomp.rs
@@ -0,0 +1,177 @@
+use super::*;
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+fn len(pubcomp: &PubComp, properties: &Option<PubCompProperties>) -> usize {
+    let mut len = 2 + 1; // pkid + reason
+
+    // The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success)
+    // and there are no Properties. In this case the PUBCOMP has a Remaining Length of 2.
+    // ref: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901154
+    if pubcomp.reason == PubCompReason::Success && properties.is_none() {
+        return 2;
+    }
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(PubComp, Option<PubCompProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+    let pkid = read_u16(&mut bytes)?;
+
+    if fixed_header.remaining_len == 2 {
+        return Ok((
+            PubComp {
+                pkid,
+                reason: PubCompReason::Success,
+            },
+            None,
+        ));
+    }
+
+    let ack_reason = read_u8(&mut bytes)?;
+    if fixed_header.remaining_len < 4 {
+        return Ok((
+            PubComp {
+                pkid,
+                reason: reason(ack_reason)?,
+            },
+            None,
+        ));
+    }
+
+    let puback = PubComp {
+        pkid,
+        reason: reason(ack_reason)?,
+    };
+
+    let properties = properties::read(&mut bytes)?;
+    Ok((puback, properties))
+}
+
+pub fn write(
+    pubcomp: &PubComp,
+    properties: &Option<PubCompProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    let len = len(pubcomp, properties);
+    buffer.put_u8(0x70);
+    let count = write_remaining_length(buffer, len)?;
+    buffer.put_u16(pubcomp.pkid);
+
+    // If there are no properties during success, sending reason code is optional
+    if pubcomp.reason == PubCompReason::Success && properties.is_none() {
+        return Ok(4);
+    }
+
+    buffer.put_u8(code(pubcomp.reason));
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    Ok(1 + count + len)
+}
+
+mod properties {
+    use super::*;
+    pub fn len(properties: &PubCompProperties) -> usize {
+        let mut len = 0;
+
+        if let Some(reason) = &properties.reason_string {
+            len += 1 + 2 + reason.len();
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<PubCompProperties>, Error> {
+        let mut reason_string = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::ReasonString => {
+                    let reason = read_mqtt_string(bytes)?;
+                    cursor += 2 + reason.len();
+                    reason_string = Some(reason);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(PubCompProperties {
+            reason_string,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &PubCompProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(reason) = &properties.reason_string {
+            buffer.put_u8(PropertyType::ReasonString as u8);
+            write_mqtt_string(buffer, reason);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
+/// Connection return code type
+fn reason(num: u8) -> Result<PubCompReason, Error> {
+    let code = match num {
+        0 => PubCompReason::Success,
+        146 => PubCompReason::PacketIdentifierNotFound,
+        num => return Err(Error::InvalidConnectReturnCode(num)),
+    };
+
+    Ok(code)
+}
+
+fn code(reason: PubCompReason) -> u8 {
+    match reason {
+        PubCompReason::Success => 0,
+        PubCompReason::PacketIdentifierNotFound => 146,
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/publish.rs b/rumqttc/src/v5/mqttbytes/v5/publish.rs
new file mode 100644
index 000000000..f656e9b39
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/publish.rs
@@ -0,0 +1,260 @@
+use super::*;
+use bytes::{Buf, Bytes};
+
+pub fn len(publish: &Publish, properties: &Option<PublishProperties>) -> usize {
+    let mut len = 2 + publish.topic.len();
+    if publish.qos != QoS::AtMostOnce && publish.pkid != 0 {
+        len += 2;
+    }
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        // just 1 byte representing 0 len
+        len += 1;
+    }
+
+    len += publish.payload.len();
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(Publish, Option<PublishProperties>), Error> {
+    let qos_num = (fixed_header.byte1 & 0b0110) >> 1;
+    let qos = qos(qos_num).ok_or(Error::InvalidQoS(qos_num))?;
+    let dup = (fixed_header.byte1 & 0b1000) != 0;
+    let retain = (fixed_header.byte1 & 0b0001) != 0;
+
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+    let topic = read_mqtt_bytes(&mut bytes)?;
+
+    // Packet identifier exists where QoS > 0
+    let pkid = match qos {
+        QoS::AtMostOnce => 0,
+        QoS::AtLeastOnce | QoS::ExactlyOnce => read_u16(&mut bytes)?,
+    };
+
+    if qos != QoS::AtMostOnce && pkid == 0 {
+        return Err(Error::PacketIdZero);
+    }
+
+    let properties = properties::read(&mut bytes)?;
+    let publish = Publish {
+        dup,
+        retain,
+        qos,
+        pkid,
+        topic,
+        payload: bytes,
+    };
+
+    Ok((publish, properties))
+}
+
+pub fn write(
+    publish: &Publish,
+    properties: &Option<PublishProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    let len = len(publish, properties);
+
+    let dup = publish.dup as u8;
+    let qos = publish.qos as u8;
+    let retain = publish.retain as u8;
+    buffer.put_u8(0b0011_0000 | retain | qos << 1 | dup << 3);
+
+    let count = write_remaining_length(buffer, len)?;
+    write_mqtt_bytes(buffer, &publish.topic);
+
+    if publish.qos != QoS::AtMostOnce {
+        let pkid = publish.pkid;
+        if pkid == 0 {
+            return Err(Error::PacketIdZero);
+        }
+
+        buffer.put_u16(pkid);
+    }
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    buffer.extend_from_slice(&publish.payload);
+
+    Ok(1 + count + len)
+}
+
+mod properties {
+    use super::*;
+
+    pub fn len(properties: &PublishProperties) -> usize {
+        let mut len = 0;
+
+        if properties.payload_format_indicator.is_some() {
+            len += 1 + 1;
+        }
+
+        if properties.message_expiry_interval.is_some() {
+            len += 1 + 4;
+        }
+
+        if properties.topic_alias.is_some() {
+            len += 1 + 2;
+        }
+
+        if let Some(topic) = &properties.response_topic {
+            len += 1 + 2 + topic.len()
+        }
+
+        if let Some(data) = &properties.correlation_data {
+            len += 1 + 2 + data.len()
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        for id in properties.subscription_identifiers.iter() {
+            len += 1 + len_len(*id);
+        }
+
+        if let Some(typ) = &properties.content_type {
+            len += 1 + 2 + typ.len()
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<PublishProperties>, Error> {
+        let mut payload_format_indicator = None;
+        let mut message_expiry_interval = None;
+        let mut topic_alias = None;
+        let mut response_topic = None;
+        let mut correlation_data = None;
+        let mut user_properties = Vec::new();
+        let mut subscription_identifiers = Vec::new();
+        let mut content_type = None;
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::PayloadFormatIndicator => {
+                    payload_format_indicator = Some(read_u8(bytes)?);
+                    cursor += 1;
+                }
+                PropertyType::MessageExpiryInterval => {
+                    message_expiry_interval = Some(read_u32(bytes)?);
+                    cursor += 4;
+                }
+                PropertyType::TopicAlias => {
+                    topic_alias = Some(read_u16(bytes)?);
+                    cursor += 2;
+                }
+                PropertyType::ResponseTopic => {
+                    let topic = read_mqtt_string(bytes)?;
+                    cursor += 2 + topic.len();
+                    response_topic = Some(topic);
+                }
+                PropertyType::CorrelationData => {
+                    let data = read_mqtt_bytes(bytes)?;
+                    cursor += 2 + data.len();
+                    correlation_data = Some(data);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                PropertyType::SubscriptionIdentifier => {
+                    let (id_len, id) = length(bytes.iter())?;
+                    cursor += 1 + id_len;
+                    bytes.advance(id_len);
+                    subscription_identifiers.push(id);
+                }
+                PropertyType::ContentType => {
+                    let typ = read_mqtt_string(bytes)?;
+                    cursor += 2 + typ.len();
+                    content_type = Some(typ);
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(PublishProperties {
+            payload_format_indicator,
+            message_expiry_interval,
+            topic_alias,
+            response_topic,
+            correlation_data,
+            user_properties,
+            subscription_identifiers,
+            content_type,
+        }))
+    }
+
+    pub fn write(properties: &PublishProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(payload_format_indicator) = properties.payload_format_indicator {
+            buffer.put_u8(PropertyType::PayloadFormatIndicator as u8);
+            buffer.put_u8(payload_format_indicator);
+        }
+
+        if let Some(message_expiry_interval) = properties.message_expiry_interval {
+            buffer.put_u8(PropertyType::MessageExpiryInterval as u8);
+            buffer.put_u32(message_expiry_interval);
+        }
+
+        if let Some(topic_alias) = properties.topic_alias {
+            buffer.put_u8(PropertyType::TopicAlias as u8);
+            buffer.put_u16(topic_alias);
+        }
+
+        if let Some(topic) = &properties.response_topic {
+            buffer.put_u8(PropertyType::ResponseTopic as u8);
+            write_mqtt_string(buffer, topic);
+        }
+
+        if let Some(data) = &properties.correlation_data {
+            buffer.put_u8(PropertyType::CorrelationData as u8);
+            write_mqtt_bytes(buffer, data);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        for id in properties.subscription_identifiers.iter() {
+            buffer.put_u8(PropertyType::SubscriptionIdentifier as u8);
+            write_remaining_length(buffer, *id)?;
+        }
+
+        if let Some(typ) = &properties.content_type {
+            buffer.put_u8(PropertyType::ContentType as u8);
+            write_mqtt_string(buffer, typ);
+        }
+
+        Ok(())
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/pubrec.rs b/rumqttc/src/v5/mqttbytes/v5/pubrec.rs
new file mode 100644
index 000000000..7202ea687
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/pubrec.rs
@@ -0,0 +1,191 @@
+use super::*;
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+fn len(pubrec: &PubRec, properties: &Option<PubRecProperties>) -> usize {
+    let mut len = 2 + 1; // pkid + reason
+
+    // The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success)
+    // and there are no Properties. In this case the PUBREC has a Remaining Length of 2.
+    // As mentioned in: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901134
+    if pubrec.reason == PubRecReason::Success && properties.is_none() {
+        return 2;
+    }
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        len += 1
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(PubRec, Option<PubRecProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+    let pkid = read_u16(&mut bytes)?;
+    if fixed_header.remaining_len == 2 {
+        return Ok((
+            PubRec {
+                pkid,
+                reason: PubRecReason::Success,
+            },
+            None,
+        ));
+    }
+
+    let ack_reason = read_u8(&mut bytes)?;
+    if fixed_header.remaining_len < 4 {
+        return Ok((
+            PubRec {
+                pkid,
+                reason: reason(ack_reason)?,
+            },
+            None,
+        ));
+    }
+
+    let puback = PubRec {
+        pkid,
+        reason: reason(ack_reason)?,
+    };
+    let properties = properties::read(&mut bytes)?;
+
+    Ok((puback, properties))
+}
+
+pub fn write(
+    pubrec: &PubRec,
+    properties: &Option<PubRecProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    let len = len(pubrec, properties);
+    buffer.put_u8(0x50);
+    let count = write_remaining_length(buffer, len)?;
+    buffer.put_u16(pubrec.pkid);
+
+    // If there are no properties during success, sending reason code is optional
+    if pubrec.reason == PubRecReason::Success && properties.is_none() {
+        return Ok(4);
+    }
+
+    buffer.put_u8(code(pubrec.reason));
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    Ok(1 + count + len)
+}
+
+mod properties {
+    use super::*;
+
+    pub fn len(properties: &PubRecProperties) -> usize {
+        let mut len = 0;
+
+        if let Some(reason) = &properties.reason_string {
+            len += 1 + 2 + reason.len();
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<PubRecProperties>, Error> {
+        let mut reason_string = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::ReasonString => {
+                    let reason = read_mqtt_string(bytes)?;
+                    cursor += 2 + reason.len();
+                    reason_string = Some(reason);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(PubRecProperties {
+            reason_string,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &PubRecProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(reason) = &properties.reason_string {
+            buffer.put_u8(PropertyType::ReasonString as u8);
+            write_mqtt_string(buffer, reason);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
+/// Connection return code type
+fn reason(num: u8) -> Result<PubRecReason, Error> {
+    let code = match num {
+        0 => PubRecReason::Success,
+        16 => PubRecReason::NoMatchingSubscribers,
+        128 => PubRecReason::UnspecifiedError,
+        131 => PubRecReason::ImplementationSpecificError,
+        135 => PubRecReason::NotAuthorized,
+        144 => PubRecReason::TopicNameInvalid,
+        145 => PubRecReason::PacketIdentifierInUse,
+        151 => PubRecReason::QuotaExceeded,
+        153 => PubRecReason::PayloadFormatInvalid,
+        num => return Err(Error::InvalidConnectReturnCode(num)),
+    };
+
+    Ok(code)
+}
+
+fn code(reason: PubRecReason) -> u8 {
+    match reason {
+        PubRecReason::Success => 0,
+        PubRecReason::NoMatchingSubscribers => 16,
+        PubRecReason::UnspecifiedError => 128,
+        PubRecReason::ImplementationSpecificError => 131,
+        PubRecReason::NotAuthorized => 135,
+        PubRecReason::TopicNameInvalid => 144,
+        PubRecReason::PacketIdentifierInUse => 145,
+        PubRecReason::QuotaExceeded => 151,
+        PubRecReason::PayloadFormatInvalid => 153,
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/pubrel.rs b/rumqttc/src/v5/mqttbytes/v5/pubrel.rs
new file mode 100644
index 000000000..ec48d65ef
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/pubrel.rs
@@ -0,0 +1,177 @@
+use super::*;
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+fn len(pubrel: &PubRel, properties: &Option<PubRelProperties>) -> usize {
+    let mut len = 2 + 1; // pkid + reason
+
+    // The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success)
+    // and there are no Properties. In this case the PUBREL has a Remaining Length of 2.
+    // As mentioned in: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
+    if pubrel.reason == PubRelReason::Success && properties.is_none() {
+        return 2;
+    }
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(PubRel, Option<PubRelProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+    let pkid = read_u16(&mut bytes)?;
+    if fixed_header.remaining_len == 2 {
+        return Ok((
+            PubRel {
+                pkid,
+                reason: PubRelReason::Success,
+            },
+            None,
+        ));
+    }
+
+    let ack_reason = read_u8(&mut bytes)?;
+    if fixed_header.remaining_len < 4 {
+        return Ok((
+            PubRel {
+                pkid,
+                reason: reason(ack_reason)?,
+            },
+            None,
+        ));
+    }
+
+    let puback = PubRel {
+        pkid,
+        reason: reason(ack_reason)?,
+    };
+
+    let properties = properties::read(&mut bytes)?;
+    Ok((puback, properties))
+}
+
+pub fn write(
+    pubrel: &PubRel,
+    properties: &Option<PubRelProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    let len = len(pubrel, properties);
+    buffer.put_u8(0x62);
+    let count = write_remaining_length(buffer, len)?;
+    buffer.put_u16(pubrel.pkid);
+
+    // If there are no properties during success, sending reason code is optional
+    if pubrel.reason == PubRelReason::Success && properties.is_none() {
+        return Ok(4);
+    }
+
+    buffer.put_u8(code(pubrel.reason));
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    Ok(1 + count + len)
+}
+
+mod properties {
+    use super::*;
+
+    pub fn len(properties: &PubRelProperties) -> usize {
+        let mut len = 0;
+
+        if let Some(reason) = &properties.reason_string {
+            len += 1 + 2 + reason.len();
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<PubRelProperties>, Error> {
+        let mut reason_string = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::ReasonString => {
+                    let reason = read_mqtt_string(bytes)?;
+                    cursor += 2 + reason.len();
+                    reason_string = Some(reason);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(PubRelProperties {
+            reason_string,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &PubRelProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(reason) = &properties.reason_string {
+            buffer.put_u8(PropertyType::ReasonString as u8);
+            write_mqtt_string(buffer, reason);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
+/// Connection return code type
+fn reason(num: u8) -> Result<PubRelReason, Error> {
+    let code = match num {
+        0 => PubRelReason::Success,
+        146 => PubRelReason::PacketIdentifierNotFound,
+        num => return Err(Error::InvalidConnectReturnCode(num)),
+    };
+
+    Ok(code)
+}
+
+fn code(reason: PubRelReason) -> u8 {
+    match reason {
+        PubRelReason::Success => 0,
+        PubRelReason::PacketIdentifierNotFound => 146,
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/suback.rs b/rumqttc/src/v5/mqttbytes/v5/suback.rs
new file mode 100644
index 000000000..54db23168
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/suback.rs
@@ -0,0 +1,178 @@
+use super::*;
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+pub fn len(suback: &SubAck, properties: &Option<SubAckProperties>) -> usize {
+    let mut len = 2 + suback.return_codes.len();
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        // just 1 byte representing 0 len
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(SubAck, Option<SubAckProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+
+    let pkid = read_u16(&mut bytes)?;
+    let properties = properties::read(&mut bytes)?;
+
+    if !bytes.has_remaining() {
+        return Err(Error::MalformedPacket);
+    }
+
+    let mut return_codes = Vec::new();
+    while bytes.has_remaining() {
+        let return_code = read_u8(&mut bytes)?;
+        return_codes.push(reason(return_code)?);
+    }
+
+    let suback = SubAck { pkid, return_codes };
+
+    Ok((suback, properties))
+}
+
+pub fn write(
+    suback: &SubAck,
+    properties: &Option<SubAckProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    buffer.put_u8(0x90);
+    let remaining_len = len(suback, properties);
+    let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
+
+    buffer.put_u16(suback.pkid);
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    let p: Vec<u8> = suback.return_codes.iter().map(|&c| code(c)).collect();
+
+    buffer.extend_from_slice(&p);
+    Ok(1 + remaining_len_bytes + remaining_len)
+}
+
+mod properties {
+    use super::*;
+
+    pub fn len(properties: &SubAckProperties) -> usize {
+        let mut len = 0;
+
+        if let Some(reason) = &properties.reason_string {
+            len += 1 + 2 + reason.len();
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<SubAckProperties>, Error> {
+        let mut reason_string = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::ReasonString => {
+                    let reason = read_mqtt_string(bytes)?;
+                    cursor += 2 + reason.len();
+                    reason_string = Some(reason);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(SubAckProperties {
+            reason_string,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &SubAckProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(reason) = &properties.reason_string {
+            buffer.put_u8(PropertyType::ReasonString as u8);
+            write_mqtt_string(buffer, reason);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
+
+fn reason(code: u8) -> Result<SubscribeReasonCode, Error> {
+    let v = match code {
+        0 => SubscribeReasonCode::QoS0,
+        1 => SubscribeReasonCode::QoS1,
+        2 => SubscribeReasonCode::QoS2,
+        128 => SubscribeReasonCode::Unspecified,
+        131 => SubscribeReasonCode::ImplementationSpecific,
+        135 => SubscribeReasonCode::NotAuthorized,
+        143 => SubscribeReasonCode::TopicFilterInvalid,
+        145 => SubscribeReasonCode::PkidInUse,
+        151 => SubscribeReasonCode::QuotaExceeded,
+        158 => SubscribeReasonCode::SharedSubscriptionsNotSupported,
+        161 => SubscribeReasonCode::SubscriptionIdNotSupported,
+        162 => SubscribeReasonCode::WildcardSubscriptionsNotSupported,
+        v => return Err(Error::InvalidSubscribeReasonCode(v)),
+    };
+
+    Ok(v)
+}
+
+fn code(value: SubscribeReasonCode) -> u8 {
+    match value {
+        SubscribeReasonCode::Success(qos) => qos as u8,
+        SubscribeReasonCode::Failure => 0x80,
+        SubscribeReasonCode::QoS0 => 0,
+        SubscribeReasonCode::QoS1 => 1,
+        SubscribeReasonCode::QoS2 => 2,
+        SubscribeReasonCode::Unspecified => 128,
+        SubscribeReasonCode::ImplementationSpecific => 131,
+        SubscribeReasonCode::NotAuthorized => 135,
+        SubscribeReasonCode::TopicFilterInvalid => 143,
+        SubscribeReasonCode::PkidInUse => 145,
+        SubscribeReasonCode::QuotaExceeded => 151,
+        SubscribeReasonCode::SharedSubscriptionsNotSupported => 158,
+        SubscribeReasonCode::SubscriptionIdNotSupported => 161,
+        SubscribeReasonCode::WildcardSubscriptionsNotSupported => 162,
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/subscribe.rs b/rumqttc/src/v5/mqttbytes/v5/subscribe.rs
new file mode 100644
index 000000000..3b4c7a5fa
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/subscribe.rs
@@ -0,0 +1,194 @@
+use super::*;
+use bytes::{Buf, Bytes};
+
+pub fn len(subscribe: &Subscribe, properties: &Option<SubscribeProperties>) -> usize {
+    let mut len = 2 + subscribe.filters.iter().fold(0, |s, t| s + filter::len(t));
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        // just 1 byte representing 0 len
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(Subscribe, Option<SubscribeProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+
+    let pkid = read_u16(&mut bytes)?;
+    let properties = properties::read(&mut bytes)?;
+
+    // variable header size = 2 (packet identifier)
+    let filters = filter::read(&mut bytes)?;
+
+    match filters.len() {
+        0 => Err(Error::EmptySubscription),
+        _ => Ok((Subscribe { pkid, filters }, properties)),
+    }
+}
+
+pub fn write(
+    subscribe: &Subscribe,
+    properties: &Option<SubscribeProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    // write packet type
+    buffer.put_u8(0x82);
+
+    // write remaining length
+    let remaining_len = len(subscribe, properties);
+    let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
+
+    // write packet id
+    buffer.put_u16(subscribe.pkid);
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    // write filters
+    for f in subscribe.filters.iter() {
+        filter::write(f, buffer);
+    }
+
+    Ok(1 + remaining_len_bytes + remaining_len)
+}
+
+mod filter {
+    use super::*;
+
+    pub fn len(filter: &Filter) -> usize {
+        // filter len + filter + options
+        2 + filter.path.len() + 1
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Vec<Filter>, Error> {
+        // variable header size = 2 (packet identifier)
+        let mut filters = Vec::new();
+
+        while bytes.has_remaining() {
+            let path = read_mqtt_string(bytes)?;
+            let options = read_u8(bytes)?;
+            let requested_qos = options & 0b0000_0011;
+
+            let nolocal = options >> 2 & 0b0000_0001;
+            let nolocal = nolocal != 0;
+
+            let preserve_retain = options >> 3 & 0b0000_0001;
+            let preserve_retain = preserve_retain != 0;
+
+            let retain_forward_rule = (options >> 4) & 0b0000_0011;
+            let retain_forward_rule = match retain_forward_rule {
+                0 => RetainForwardRule::OnEverySubscribe,
+                1 => RetainForwardRule::OnNewSubscribe,
+                2 => RetainForwardRule::Never,
+                r => return Err(Error::InvalidRetainForwardRule(r)),
+            };
+
+            filters.push(Filter {
+                path,
+                qos: qos(requested_qos).ok_or(Error::InvalidQoS(requested_qos))?,
+                nolocal,
+                preserve_retain,
+                retain_forward_rule,
+            });
+        }
+
+        Ok(filters)
+    }
+
+    pub fn write(filter: &Filter, buffer: &mut BytesMut) {
+        let mut options = 0;
+        options |= filter.qos as u8;
+
+        write_mqtt_string(buffer, filter.path.as_str());
+        buffer.put_u8(options);
+    }
+}
+
+mod properties {
+    use super::*;
+
+    pub fn len(properties: &SubscribeProperties) -> usize {
+        let mut len = 0;
+
+        if let Some(id) = &properties.id {
+            len += 1 + len_len(*id);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<SubscribeProperties>, Error> {
+        let mut id = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::SubscriptionIdentifier => {
+                    let (id_len, sub_id) = length(bytes.iter())?;
+                    // TODO: Validate 1 +. Tests are working either way
+                    cursor += 1 + id_len;
+                    bytes.advance(id_len);
+                    id = Some(sub_id)
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(SubscribeProperties {
+            id,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &SubscribeProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(id) = &properties.id {
+            buffer.put_u8(PropertyType::SubscriptionIdentifier as u8);
+            write_remaining_length(buffer, *id)?;
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/unsuback.rs b/rumqttc/src/v5/mqttbytes/v5/unsuback.rs
new file mode 100644
index 000000000..a6b1aa03c
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/unsuback.rs
@@ -0,0 +1,165 @@
+use super::*;
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+
+pub fn len(unsuback: &UnsubAck, properties: &Option<UnsubAckProperties>) -> usize {
+    let mut len = 2 + unsuback.reasons.len();
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        // just 1 byte representing 0 len
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(UnsubAck, Option<UnsubAckProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+
+    let pkid = read_u16(&mut bytes)?;
+    let properties = properties::read(&mut bytes)?;
+
+    if !bytes.has_remaining() {
+        return Err(Error::MalformedPacket);
+    }
+
+    let mut reasons = Vec::new();
+    while bytes.has_remaining() {
+        let r = read_u8(&mut bytes)?;
+        reasons.push(reason(r)?);
+    }
+
+    let unsuback = UnsubAck { pkid, reasons };
+
+    Ok((unsuback, properties))
+}
+
+pub fn write(
+    unsuback: &UnsubAck,
+    properties: &Option<UnsubAckProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    buffer.put_u8(0xB0);
+    let remaining_len = len(unsuback, properties);
+    let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
+
+    buffer.put_u16(unsuback.pkid);
+
+    if let Some(p) = &properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    let p: Vec<u8> = unsuback.reasons.iter().map(|&c| code(c)).collect();
+    buffer.extend_from_slice(&p);
+    Ok(1 + remaining_len_bytes + remaining_len)
+}
+
+mod properties {
+    use super::*;
+    pub fn len(properties: &UnsubAckProperties) -> usize {
+        let mut len = 0;
+
+        if let Some(reason) = &properties.reason_string {
+            len += 1 + 2 + reason.len();
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<UnsubAckProperties>, Error> {
+        let mut reason_string = None;
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::ReasonString => {
+                    let reason = read_mqtt_string(bytes)?;
+                    cursor += 2 + reason.len();
+                    reason_string = Some(reason);
+                }
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(UnsubAckProperties {
+            reason_string,
+            user_properties,
+        }))
+    }
+
+    pub fn write(properties: &UnsubAckProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        if let Some(reason) = &properties.reason_string {
+            buffer.put_u8(PropertyType::ReasonString as u8);
+            write_mqtt_string(buffer, reason);
+        }
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
+
+/// Connection return code type
+fn reason(num: u8) -> Result<UnsubAckReason, Error> {
+    let code = match num {
+        0x00 => UnsubAckReason::Success,
+        0x11 => UnsubAckReason::NoSubscriptionExisted,
+        0x80 => UnsubAckReason::UnspecifiedError,
+        0x83 => UnsubAckReason::ImplementationSpecificError,
+        0x87 => UnsubAckReason::NotAuthorized,
+        0x8F => UnsubAckReason::TopicFilterInvalid,
+        0x91 => UnsubAckReason::PacketIdentifierInUse,
+        num => return Err(Error::InvalidSubscribeReasonCode(num)),
+    };
+
+    Ok(code)
+}
+
+fn code(reason: UnsubAckReason) -> u8 {
+    match reason {
+        UnsubAckReason::Success => 0x00,
+        UnsubAckReason::NoSubscriptionExisted => 0x11,
+        UnsubAckReason::UnspecifiedError => 0x80,
+        UnsubAckReason::ImplementationSpecificError => 0x83,
+        UnsubAckReason::NotAuthorized => 0x87,
+        UnsubAckReason::TopicFilterInvalid => 0x8F,
+        UnsubAckReason::PacketIdentifierInUse => 0x91,
+    }
+}
diff --git a/rumqttc/src/v5/mqttbytes/v5/unsubscribe.rs b/rumqttc/src/v5/mqttbytes/v5/unsubscribe.rs
new file mode 100644
index 000000000..4c356a591
--- /dev/null
+++ b/rumqttc/src/v5/mqttbytes/v5/unsubscribe.rs
@@ -0,0 +1,123 @@
+use super::*;
+use bytes::{Buf, Bytes};
+
+pub fn len(unsubscribe: &Unsubscribe, properties: &Option<UnsubscribeProperties>) -> usize {
+    // Packet id + length of filters (unlike subscribe, this just a string.
+    // Hence 2 is prefixed for len per filter)
+    let mut len = 2 + unsubscribe.filters.iter().fold(0, |s, t| 2 + s + t.len());
+
+    if let Some(p) = properties {
+        let properties_len = properties::len(p);
+        let properties_len_len = len_len(properties_len);
+        len += properties_len_len + properties_len;
+    } else {
+        // just 1 byte representing 0 len
+        len += 1;
+    }
+
+    len
+}
+
+pub fn read(
+    fixed_header: FixedHeader,
+    mut bytes: Bytes,
+) -> Result<(Unsubscribe, Option<UnsubscribeProperties>), Error> {
+    let variable_header_index = fixed_header.fixed_header_len;
+    bytes.advance(variable_header_index);
+
+    let pkid = read_u16(&mut bytes)?;
+    let properties = properties::read(&mut bytes)?;
+
+    let mut filters = Vec::with_capacity(1);
+    while bytes.has_remaining() {
+        let filter = read_mqtt_string(&mut bytes)?;
+        filters.push(filter);
+    }
+
+    let unsubscribe = Unsubscribe { pkid, filters };
+    Ok((unsubscribe, properties))
+}
+
+pub fn write(
+    unsubscribe: &Unsubscribe,
+    properties: &Option<UnsubscribeProperties>,
+    buffer: &mut BytesMut,
+) -> Result<usize, Error> {
+    buffer.put_u8(0xA2);
+
+    // write remaining length
+    let remaining_len = len(unsubscribe, properties);
+    let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
+
+    // write packet id
+    buffer.put_u16(unsubscribe.pkid);
+
+    if let Some(p) = properties {
+        properties::write(p, buffer)?;
+    } else {
+        write_remaining_length(buffer, 0)?;
+    }
+
+    // write filters
+    for filter in unsubscribe.filters.iter() {
+        write_mqtt_string(buffer, filter);
+    }
+
+    Ok(1 + remaining_len_bytes + remaining_len)
+}
+
+mod properties {
+    use super::*;
+    pub fn len(properties: &UnsubscribeProperties) -> usize {
+        let mut len = 0;
+
+        for (key, value) in properties.user_properties.iter() {
+            len += 1 + 2 + key.len() + 2 + value.len();
+        }
+
+        len
+    }
+
+    pub fn read(bytes: &mut Bytes) -> Result<Option<UnsubscribeProperties>, Error> {
+        let mut user_properties = Vec::new();
+
+        let (properties_len_len, properties_len) = length(bytes.iter())?;
+        bytes.advance(properties_len_len);
+
+        if properties_len == 0 {
+            return Ok(None);
+        }
+
+        let mut cursor = 0;
+        // read until cursor reaches property length. properties_len = 0 will skip this loop
+        while cursor < properties_len {
+            let prop = read_u8(bytes)?;
+            cursor += 1;
+
+            match property(prop)? {
+                PropertyType::UserProperty => {
+                    let key = read_mqtt_string(bytes)?;
+                    let value = read_mqtt_string(bytes)?;
+                    cursor += 2 + key.len() + 2 + value.len();
+                    user_properties.push((key, value));
+                }
+                _ => return Err(Error::InvalidPropertyType(prop)),
+            }
+        }
+
+        Ok(Some(UnsubscribeProperties { user_properties }))
+    }
+
+    pub fn write(properties: &UnsubscribeProperties, buffer: &mut BytesMut) -> Result<(), Error> {
+        let len = len(properties);
+        write_remaining_length(buffer, len)?;
+
+        for (key, value) in properties.user_properties.iter() {
+            buffer.put_u8(PropertyType::UserProperty as u8);
+            write_mqtt_string(buffer, key);
+            write_mqtt_string(buffer, value);
+        }
+
+        Ok(())
+    }
+}
diff --git a/rumqttc/src/v5/notifier.rs b/rumqttc/src/v5/notifier.rs
deleted file mode 100644
index 0bd9e5ff4..000000000
--- a/rumqttc/src/v5/notifier.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use std::collections::VecDeque;
-use std::mem;
-use std::sync::{Arc, Mutex, RwLock};
-
-use crate::v5::Incoming;
-
-/// Error type to denote if Notifier failed to resolve due to empty buffer or due to disconnection
-#[derive(Debug, Eq, PartialEq)]
-pub enum TryRecvError {
-    /// [`EventLoop`] is disconnected from broker
-    ///
-    /// [`EventLoop`]: super::EventLoop
-    Disconnected,
-    /// [`EventLoop`] is connected to broker
-    ///
-    /// [`EventLoop`]: super::EventLoop
-    Waiting,
-}
-
-/// Interface to receive incoming [`Packet`]s from the [`EventLoop`]
-///
-/// [`Packet`]: super::Packet
-/// [`EventLoop`]: super::EventLoop
-#[derive(Debug)]
-pub struct Notifier {
-    incoming_buf: Arc<Mutex<VecDeque<Incoming>>>,
-    incoming_buf_cache: VecDeque<Incoming>,
-    disconnected: Arc<RwLock<bool>>,
-}
-
-impl Notifier {
-    #[inline]
-    pub(crate) fn new(
-        incoming_buf: Arc<Mutex<VecDeque<Incoming>>>,
-        incoming_buf_cache: VecDeque<Incoming>,
-        disconnected: Arc<RwLock<bool>>,
-    ) -> Self {
-        Self {
-            incoming_buf,
-            incoming_buf_cache,
-            disconnected,
-        }
-    }
-
-    #[inline]
-    pub fn is_disconnected(&self) -> bool {
-        *self.disconnected.read().unwrap()
-    }
-
-    #[inline]
-    pub fn iter(&mut self) -> NotifierIter<'_> {
-        NotifierIter(self)
-    }
-
-    #[inline]
-    /// Returns None immediately even if not disconnected, use when expecting non-blocking interface
-    pub fn try_recv(&mut self) -> Result<Incoming, TryRecvError> {
-        let next = match self.incoming_buf_cache.pop_front() {
-            None => {
-                let mut incoming_buf = self.incoming_buf.lock().unwrap();
-                if incoming_buf.is_empty() {
-                    None
-                } else {
-                    mem::swap(&mut self.incoming_buf_cache, &mut *incoming_buf);
-                    drop(incoming_buf);
-                    self.incoming_buf_cache.pop_front()
-                }
-            }
-            val => val,
-        };
-
-        match next {
-            Some(p) => Ok(p),
-            None if self.is_disconnected() => Err(TryRecvError::Disconnected),
-            None => Err(TryRecvError::Waiting),
-        }
-    }
-}
-
-impl Iterator for Notifier {
-    type Item = Incoming;
-
-    #[inline]
-    /// Return None only if disconnected, else block and retry till resolves as Some
-    fn next(&mut self) -> Option<Incoming> {
-        loop {
-            match self.try_recv() {
-                Ok(p) => return Some(p),
-                Err(TryRecvError::Disconnected) => return None,
-                _ => {}
-            }
-        }
-    }
-}
-
-pub struct NotifierIter<'a>(&'a mut Notifier);
-
-impl<'a> Iterator for NotifierIter<'a> {
-    type Item = Incoming;
-
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use std::{
-        collections::VecDeque,
-        sync::{Arc, Mutex, RwLock},
-    };
-
-    use super::{Notifier, TryRecvError};
-
-    #[test]
-    fn when_disconnected() {
-        let mut notifier = Notifier::new(
-            Arc::new(Mutex::new(VecDeque::new())),
-            VecDeque::new(),
-            Arc::new(RwLock::new(true)),
-        );
-
-        assert_eq!(notifier.try_recv(), Err(TryRecvError::Disconnected));
-
-        assert_eq!(notifier.next(), None);
-    }
-}
diff --git a/rumqttc/src/v5/outgoing_buf.rs b/rumqttc/src/v5/outgoing_buf.rs
deleted file mode 100644
index d036dd0c8..000000000
--- a/rumqttc/src/v5/outgoing_buf.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use std::{
-    collections::VecDeque,
-    sync::{Arc, Mutex},
-};
-
-use crate::v5::Request;
-
-#[derive(Debug)]
-pub struct OutgoingBuf {
-    pub(crate) buf: VecDeque<Request>,
-    pub(crate) pkid_counter: u16,
-    pub(crate) capacity: usize,
-}
-
-impl OutgoingBuf {
-    #[inline]
-    pub fn new(cap: usize) -> Arc<Mutex<Self>> {
-        Arc::new(Mutex::new(Self {
-            buf: VecDeque::with_capacity(cap),
-            pkid_counter: 0,
-            capacity: cap,
-        }))
-    }
-
-    #[inline]
-    pub fn increment_pkid(&mut self) -> u16 {
-        self.pkid_counter = if self.pkid_counter == self.capacity as u16 {
-            1
-        } else {
-            self.pkid_counter + 1
-        };
-        self.pkid_counter
-    }
-}
diff --git a/rumqttc/src/v5/packet/connack.rs b/rumqttc/src/v5/packet/connack.rs
deleted file mode 100644
index a944277a8..000000000
--- a/rumqttc/src/v5/packet/connack.rs
+++ /dev/null
@@ -1,557 +0,0 @@
-use super::*;
-use bytes::{Buf, BufMut, Bytes, BytesMut};
-
-/// Return code in connack
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[repr(u8)]
-pub enum ConnectReturnCode {
-    Success = 0,
-    UnspecifiedError = 128,
-    MalformedPacket = 129,
-    ProtocolError = 130,
-    ImplementationSpecificError = 131,
-    UnsupportedProtocolVersion = 132,
-    ClientIdentifierNotValid = 133,
-    BadUserNamePassword = 134,
-    NotAuthorized = 135,
-    ServerUnavailable = 136,
-    ServerBusy = 137,
-    Banned = 138,
-    BadAuthenticationMethod = 140,
-    TopicNameInvalid = 144,
-    PacketTooLarge = 149,
-    QuotaExceeded = 151,
-    PayloadFormatInvalid = 153,
-    RetainNotSupported = 154,
-    QoSNotSupported = 155,
-    UseAnotherServer = 156,
-    ServerMoved = 157,
-    ConnectionRateExceeded = 159,
-}
-
-/// Acknowledgement to connect packet
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ConnAck {
-    pub session_present: bool,
-    pub code: ConnectReturnCode,
-    pub properties: Option<ConnAckProperties>,
-}
-
-impl ConnAck {
-    pub fn new(code: ConnectReturnCode, session_present: bool) -> ConnAck {
-        ConnAck {
-            code,
-            session_present,
-            properties: None,
-        }
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 1  // session present
-                    + 1; // code
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-
-        let flags = read_u8(&mut bytes)?;
-        let return_code = read_u8(&mut bytes)?;
-
-        let session_present = (flags & 0x01) == 1;
-        let code = connect_return(return_code)?;
-        let connack = ConnAck {
-            session_present,
-            code,
-            properties: ConnAckProperties::extract(&mut bytes)?,
-        };
-
-        Ok(connack)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        let len = self.len();
-        buffer.put_u8(0x20);
-
-        let count = write_remaining_length(buffer, len)?;
-        buffer.put_u8(self.session_present as u8);
-        buffer.put_u8(self.code as u8);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        Ok(1 + count + len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ConnAckProperties {
-    pub session_expiry_interval: Option<u32>,
-    pub receive_max: Option<u16>,
-    pub max_qos: Option<u8>,
-    pub retain_available: Option<u8>,
-    pub max_packet_size: Option<u32>,
-    pub assigned_client_identifier: Option<String>,
-    pub topic_alias_max: Option<u16>,
-    pub reason_string: Option<String>,
-    pub user_properties: Vec<(String, String)>,
-    pub wildcard_subscription_available: Option<u8>,
-    pub subscription_identifiers_available: Option<u8>,
-    pub shared_subscription_available: Option<u8>,
-    pub server_keep_alive: Option<u16>,
-    pub response_information: Option<String>,
-    pub server_reference: Option<String>,
-    pub authentication_method: Option<String>,
-    pub authentication_data: Option<Bytes>,
-}
-
-impl ConnAckProperties {
-    pub fn new() -> ConnAckProperties {
-        ConnAckProperties {
-            session_expiry_interval: None,
-            receive_max: None,
-            max_qos: None,
-            retain_available: None,
-            max_packet_size: None,
-            assigned_client_identifier: None,
-            topic_alias_max: None,
-            reason_string: None,
-            user_properties: Vec::new(),
-            wildcard_subscription_available: None,
-            subscription_identifiers_available: None,
-            shared_subscription_available: None,
-            server_keep_alive: None,
-            response_information: None,
-            server_reference: None,
-            authentication_method: None,
-            authentication_data: None,
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(_) = &self.session_expiry_interval {
-            len += 1 + 4;
-        }
-
-        if let Some(_) = &self.receive_max {
-            len += 1 + 2;
-        }
-
-        if let Some(_) = &self.max_qos {
-            len += 1 + 1;
-        }
-
-        if let Some(_) = &self.retain_available {
-            len += 1 + 1;
-        }
-
-        if let Some(_) = &self.max_packet_size {
-            len += 1 + 4;
-        }
-
-        if let Some(id) = &self.assigned_client_identifier {
-            len += 1 + 2 + id.len();
-        }
-
-        if let Some(_) = &self.topic_alias_max {
-            len += 1 + 2;
-        }
-
-        if let Some(reason) = &self.reason_string {
-            len += 1 + 2 + reason.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        if let Some(_) = &self.wildcard_subscription_available {
-            len += 1 + 1;
-        }
-
-        if let Some(_) = &self.subscription_identifiers_available {
-            len += 1 + 1;
-        }
-
-        if let Some(_) = &self.shared_subscription_available {
-            len += 1 + 1;
-        }
-
-        if let Some(_) = &self.server_keep_alive {
-            len += 1 + 2;
-        }
-
-        if let Some(info) = &self.response_information {
-            len += 1 + 2 + info.len();
-        }
-
-        if let Some(reference) = &self.server_reference {
-            len += 1 + 2 + reference.len();
-        }
-
-        if let Some(authentication_method) = &self.authentication_method {
-            len += 1 + 2 + authentication_method.len();
-        }
-
-        if let Some(authentication_data) = &self.authentication_data {
-            len += 1 + 2 + authentication_data.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<ConnAckProperties>, Error> {
-        let mut session_expiry_interval = None;
-        let mut receive_max = None;
-        let mut max_qos = None;
-        let mut retain_available = None;
-        let mut max_packet_size = None;
-        let mut assigned_client_identifier = None;
-        let mut topic_alias_max = None;
-        let mut reason_string = None;
-        let mut user_properties = Vec::new();
-        let mut wildcard_subscription_available = None;
-        let mut subscription_identifiers_available = None;
-        let mut shared_subscription_available = None;
-        let mut server_keep_alive = None;
-        let mut response_information = None;
-        let mut server_reference = None;
-        let mut authentication_method = None;
-        let mut authentication_data = None;
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::SessionExpiryInterval => {
-                    session_expiry_interval = Some(read_u32(&mut bytes)?);
-                    cursor += 4;
-                }
-                PropertyType::ReceiveMaximum => {
-                    receive_max = Some(read_u16(&mut bytes)?);
-                    cursor += 2;
-                }
-                PropertyType::MaximumQos => {
-                    max_qos = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::RetainAvailable => {
-                    retain_available = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::AssignedClientIdentifier => {
-                    let id = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + id.len();
-                    assigned_client_identifier = Some(id);
-                }
-                PropertyType::MaximumPacketSize => {
-                    max_packet_size = Some(read_u32(&mut bytes)?);
-                    cursor += 4;
-                }
-                PropertyType::TopicAliasMaximum => {
-                    topic_alias_max = Some(read_u16(&mut bytes)?);
-                    cursor += 2;
-                }
-                PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reason.len();
-                    reason_string = Some(reason);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                PropertyType::WildcardSubscriptionAvailable => {
-                    wildcard_subscription_available = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::SubscriptionIdentifierAvailable => {
-                    subscription_identifiers_available = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::SharedSubscriptionAvailable => {
-                    shared_subscription_available = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::ServerKeepAlive => {
-                    server_keep_alive = Some(read_u16(&mut bytes)?);
-                    cursor += 2;
-                }
-                PropertyType::ResponseInformation => {
-                    let info = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + info.len();
-                    response_information = Some(info);
-                }
-                PropertyType::ServerReference => {
-                    let reference = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reference.len();
-                    server_reference = Some(reference);
-                }
-                PropertyType::AuthenticationMethod => {
-                    let method = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + method.len();
-                    authentication_method = Some(method);
-                }
-                PropertyType::AuthenticationData => {
-                    let data = read_mqtt_bytes(&mut bytes)?;
-                    cursor += 2 + data.len();
-                    authentication_data = Some(data);
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(ConnAckProperties {
-            session_expiry_interval,
-            receive_max,
-            max_qos,
-            retain_available,
-            max_packet_size,
-            assigned_client_identifier,
-            topic_alias_max,
-            reason_string,
-            user_properties,
-            wildcard_subscription_available,
-            subscription_identifiers_available,
-            shared_subscription_available,
-            server_keep_alive,
-            response_information,
-            server_reference,
-            authentication_method,
-            authentication_data,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(session_expiry_interval) = self.session_expiry_interval {
-            buffer.put_u8(PropertyType::SessionExpiryInterval as u8);
-            buffer.put_u32(session_expiry_interval);
-        }
-
-        if let Some(receive_maximum) = self.receive_max {
-            buffer.put_u8(PropertyType::ReceiveMaximum as u8);
-            buffer.put_u16(receive_maximum);
-        }
-
-        if let Some(qos) = self.max_qos {
-            buffer.put_u8(PropertyType::MaximumQos as u8);
-            buffer.put_u8(qos);
-        }
-
-        if let Some(retain_available) = self.retain_available {
-            buffer.put_u8(PropertyType::RetainAvailable as u8);
-            buffer.put_u8(retain_available);
-        }
-
-        if let Some(max_packet_size) = self.max_packet_size {
-            buffer.put_u8(PropertyType::MaximumPacketSize as u8);
-            buffer.put_u32(max_packet_size);
-        }
-
-        if let Some(id) = &self.assigned_client_identifier {
-            buffer.put_u8(PropertyType::AssignedClientIdentifier as u8);
-            write_mqtt_string(buffer, id);
-        }
-
-        if let Some(topic_alias_max) = self.topic_alias_max {
-            buffer.put_u8(PropertyType::TopicAliasMaximum as u8);
-            buffer.put_u16(topic_alias_max);
-        }
-
-        if let Some(reason) = &self.reason_string {
-            buffer.put_u8(PropertyType::ReasonString as u8);
-            write_mqtt_string(buffer, reason);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        if let Some(w) = self.wildcard_subscription_available {
-            buffer.put_u8(PropertyType::WildcardSubscriptionAvailable as u8);
-            buffer.put_u8(w);
-        }
-
-        if let Some(s) = self.subscription_identifiers_available {
-            buffer.put_u8(PropertyType::SubscriptionIdentifierAvailable as u8);
-            buffer.put_u8(s);
-        }
-
-        if let Some(s) = self.shared_subscription_available {
-            buffer.put_u8(PropertyType::SharedSubscriptionAvailable as u8);
-            buffer.put_u8(s);
-        }
-
-        if let Some(keep_alive) = self.server_keep_alive {
-            buffer.put_u8(PropertyType::ServerKeepAlive as u8);
-            buffer.put_u16(keep_alive);
-        }
-
-        if let Some(info) = &self.response_information {
-            buffer.put_u8(PropertyType::ResponseInformation as u8);
-            write_mqtt_string(buffer, info);
-        }
-
-        if let Some(reference) = &self.server_reference {
-            buffer.put_u8(PropertyType::ServerReference as u8);
-            write_mqtt_string(buffer, reference);
-        }
-
-        if let Some(authentication_method) = &self.authentication_method {
-            buffer.put_u8(PropertyType::AuthenticationMethod as u8);
-            write_mqtt_string(buffer, authentication_method);
-        }
-
-        if let Some(authentication_data) = &self.authentication_data {
-            buffer.put_u8(PropertyType::AuthenticationData as u8);
-            write_mqtt_bytes(buffer, authentication_data);
-        }
-
-        Ok(())
-    }
-}
-
-/// Connection return code type
-fn connect_return(num: u8) -> Result<ConnectReturnCode, Error> {
-    let code = match num {
-        0 => ConnectReturnCode::Success,
-        128 => ConnectReturnCode::UnspecifiedError,
-        129 => ConnectReturnCode::MalformedPacket,
-        130 => ConnectReturnCode::ProtocolError,
-        131 => ConnectReturnCode::ImplementationSpecificError,
-        132 => ConnectReturnCode::UnsupportedProtocolVersion,
-        133 => ConnectReturnCode::ClientIdentifierNotValid,
-        134 => ConnectReturnCode::BadUserNamePassword,
-        135 => ConnectReturnCode::NotAuthorized,
-        136 => ConnectReturnCode::ServerUnavailable,
-        137 => ConnectReturnCode::ServerBusy,
-        138 => ConnectReturnCode::Banned,
-        140 => ConnectReturnCode::BadAuthenticationMethod,
-        144 => ConnectReturnCode::TopicNameInvalid,
-        149 => ConnectReturnCode::PacketTooLarge,
-        151 => ConnectReturnCode::QuotaExceeded,
-        153 => ConnectReturnCode::PayloadFormatInvalid,
-        154 => ConnectReturnCode::RetainNotSupported,
-        155 => ConnectReturnCode::QoSNotSupported,
-        156 => ConnectReturnCode::UseAnotherServer,
-        157 => ConnectReturnCode::ServerMoved,
-        159 => ConnectReturnCode::ConnectionRateExceeded,
-        num => return Err(Error::InvalidConnectReturnCode(num)),
-    };
-
-    Ok(code)
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::{Bytes, BytesMut};
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> ConnAck {
-        let properties = ConnAckProperties {
-            session_expiry_interval: Some(1234),
-            receive_max: Some(432),
-            max_qos: Some(2),
-            retain_available: Some(1),
-            max_packet_size: Some(100),
-            assigned_client_identifier: Some("test".to_owned()),
-            topic_alias_max: Some(456),
-            reason_string: Some("test".to_owned()),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-            wildcard_subscription_available: Some(1),
-            subscription_identifiers_available: Some(1),
-            shared_subscription_available: Some(0),
-            server_keep_alive: Some(1234),
-            response_information: Some("test".to_owned()),
-            server_reference: Some("test".to_owned()),
-            authentication_method: Some("test".to_owned()),
-            authentication_data: Some(Bytes::from(vec![1, 2, 3, 4])),
-        };
-
-        ConnAck {
-            session_present: false,
-            code: ConnectReturnCode::Success,
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x20, // Packet type
-            0x57, // Remaining length
-            0x00, 0x00, // Session, code
-            0x54, // Properties length
-            0x11, 0x00, 0x00, 0x04, 0xd2, // Session expiry interval
-            0x21, 0x01, 0xb0, // Receive maximum
-            0x24, 0x02, // Maximum qos
-            0x25, 0x01, // Retain available
-            0x27, 0x00, 0x00, 0x00, 0x64, // Maximum packet size
-            0x12, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // Assigned client identifier
-            0x22, 0x01, 0xc8, // Topic alias max
-            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // Reason string
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-            0x28, 0x01, // wildcard_subscription_available
-            0x29, 0x01, // subscription_identifiers_available
-            0x2a, 0x00, // shared_subscription_available
-            0x13, 0x04, 0xd2, // server keep_alive
-            0x1a, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // response_information
-            0x1c, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // server reference
-            0x15, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // authentication method
-            0x16, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, // authentication data
-        ]
-    }
-
-    #[test]
-    fn connack_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let connack_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let connack = ConnAck::read(fixed_header, connack_bytes).unwrap();
-
-        assert_eq!(connack, sample());
-    }
-
-    #[test]
-    fn connack_encoding_works() {
-        let connack = sample();
-        let mut buf = BytesMut::new();
-        connack.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/connect.rs b/rumqttc/src/v5/packet/connect.rs
deleted file mode 100644
index e943f4d3a..000000000
--- a/rumqttc/src/v5/packet/connect.rs
+++ /dev/null
@@ -1,920 +0,0 @@
-use super::*;
-use bytes::{Buf, Bytes};
-
-/// Connection packet initiated by the client
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Connect {
-    /// Mqtt protocol version
-    pub protocol: Protocol,
-    /// Mqtt keep alive time
-    pub keep_alive: u16,
-    /// Client Id
-    pub client_id: String,
-    /// Clean session. Asks the broker to clear previous state
-    pub clean_session: bool,
-    /// Will that broker needs to publish when the client disconnects
-    pub last_will: Option<LastWill>,
-    /// Login credentials
-    pub login: Option<Login>,
-    /// Properties
-    pub properties: Option<ConnectProperties>,
-}
-
-impl Connect {
-    pub fn new<S: Into<String>>(id: S) -> Connect {
-        Connect {
-            protocol: Protocol::V5,
-            keep_alive: 10,
-            properties: None,
-            client_id: id.into(),
-            clean_session: true,
-            last_will: None,
-            login: None,
-        }
-    }
-
-    pub fn set_login<U: Into<String>, P: Into<String>>(&mut self, u: U, p: P) -> &mut Connect {
-        let login = Login {
-            username: u.into(),
-            password: p.into(),
-        };
-
-        self.login = Some(login);
-        self
-    }
-
-    pub fn len(&self) -> usize {
-        let mut len = 2 + "MQTT".len() // protocol name
-                        + 1            // protocol version
-                        + 1            // connect flags
-                        + 2; // keep alive
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            // just 1 byte representing 0 len
-            len += 1;
-        }
-
-        len += 2 + self.client_id.len();
-
-        // last will len
-        if let Some(last_will) = &self.last_will {
-            len += last_will.len();
-        }
-
-        // username and password len
-        if let Some(login) = &self.login {
-            len += login.len();
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Connect, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-
-        // Variable header
-        let protocol_name = read_mqtt_string(&mut bytes)?;
-        let protocol_level = read_u8(&mut bytes)?;
-        if protocol_name != "MQTT" {
-            return Err(Error::InvalidProtocol);
-        }
-
-        let protocol = match protocol_level {
-            4 => Protocol::V4,
-            5 => Protocol::V5,
-            num => return Err(Error::InvalidProtocolLevel(num)),
-        };
-
-        let connect_flags = read_u8(&mut bytes)?;
-        let clean_session = (connect_flags & 0b10) != 0;
-        let keep_alive = read_u16(&mut bytes)?;
-
-        // Properties in variable header
-        let properties = match protocol {
-            Protocol::V5 => ConnectProperties::read(&mut bytes)?,
-            Protocol::V4 => None,
-        };
-
-        let client_id = read_mqtt_string(&mut bytes)?;
-        let last_will = LastWill::read(connect_flags, &mut bytes)?;
-        let login = Login::read(connect_flags, &mut bytes)?;
-
-        let connect = Connect {
-            protocol,
-            keep_alive,
-            client_id,
-            clean_session,
-            last_will,
-            login,
-            properties,
-        };
-
-        Ok(connect)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        let len = self.len();
-        buffer.put_u8(0b0001_0000);
-        let count = write_remaining_length(buffer, len)?;
-        write_mqtt_string(buffer, "MQTT");
-
-        match self.protocol {
-            Protocol::V4 => buffer.put_u8(0x04),
-            Protocol::V5 => buffer.put_u8(0x05),
-        }
-
-        let flags_index = 1 + count + 2 + 4 + 1;
-
-        let mut connect_flags = 0;
-        if self.clean_session {
-            connect_flags |= 0x02;
-        }
-
-        buffer.put_u8(connect_flags);
-        buffer.put_u16(self.keep_alive);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        write_mqtt_string(buffer, &self.client_id);
-
-        if let Some(last_will) = &self.last_will {
-            connect_flags |= last_will.write(buffer)?;
-        }
-
-        if let Some(login) = &self.login {
-            connect_flags |= login.write(buffer);
-        }
-
-        // update connect flags
-        buffer[flags_index] = connect_flags;
-        Ok(len)
-    }
-}
-
-/// LastWill that broker forwards on behalf of the client
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct LastWill {
-    pub topic: String,
-    pub message: Bytes,
-    pub qos: QoS,
-    pub retain: bool,
-    pub properties: Option<WillProperties>,
-}
-
-impl LastWill {
-    pub fn new(
-        topic: impl Into<String>,
-        payload: impl Into<Vec<u8>>,
-        qos: QoS,
-        retain: bool,
-    ) -> LastWill {
-        LastWill {
-            topic: topic.into(),
-            message: Bytes::from(payload.into()),
-            qos,
-            retain,
-            properties: None,
-        }
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            // just 1 byte representing 0 len
-            len += 1;
-        }
-
-        len += 2 + self.topic.len() + 2 + self.message.len();
-        len
-    }
-
-    fn read(connect_flags: u8, mut bytes: &mut Bytes) -> Result<Option<LastWill>, Error> {
-        let last_will = match connect_flags & 0b100 {
-            0 if (connect_flags & 0b0011_1000) != 0 => {
-                return Err(Error::IncorrectPacketFormat);
-            }
-            0 => None,
-            _ => {
-                // Properties in variable header
-                let properties = WillProperties::read(&mut bytes)?;
-
-                let will_topic = read_mqtt_string(&mut bytes)?;
-                let will_message = read_mqtt_bytes(&mut bytes)?;
-                let will_qos = qos((connect_flags & 0b11000) >> 3)?;
-                Some(LastWill {
-                    topic: will_topic,
-                    message: will_message,
-                    qos: will_qos,
-                    retain: (connect_flags & 0b0010_0000) != 0,
-                    properties,
-                })
-            }
-        };
-
-        Ok(last_will)
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<u8, Error> {
-        let mut connect_flags = 0;
-
-        connect_flags |= 0x04 | (self.qos as u8) << 3;
-        if self.retain {
-            connect_flags |= 0x20;
-        }
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        write_mqtt_string(buffer, &self.topic);
-        write_mqtt_bytes(buffer, &self.message);
-        Ok(connect_flags)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct WillProperties {
-    pub delay_interval: Option<u32>,
-    pub payload_format_indicator: Option<u8>,
-    pub message_expiry_interval: Option<u32>,
-    pub content_type: Option<String>,
-    pub response_topic: Option<String>,
-    pub correlation_data: Option<Bytes>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl WillProperties {
-    fn len(&self) -> usize {
-        let mut len = 0;
-
-        if self.delay_interval.is_some() {
-            len += 1 + 4;
-        }
-
-        if self.payload_format_indicator.is_some() {
-            len += 1 + 1;
-        }
-
-        if self.message_expiry_interval.is_some() {
-            len += 1 + 4;
-        }
-
-        if let Some(typ) = &self.content_type {
-            len += 1 + 2 + typ.len();
-        }
-
-        if let Some(topic) = &self.response_topic {
-            len += 1 + 2 + topic.len();
-        }
-
-        if let Some(data) = &self.correlation_data {
-            len += 1 + 2 + data.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    fn read(mut bytes: &mut Bytes) -> Result<Option<WillProperties>, Error> {
-        let mut delay_interval = None;
-        let mut payload_format_indicator = None;
-        let mut message_expiry_interval = None;
-        let mut content_type = None;
-        let mut response_topic = None;
-        let mut correlation_data = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::WillDelayInterval => {
-                    delay_interval = Some(read_u32(&mut bytes)?);
-                    cursor += 4;
-                }
-                PropertyType::PayloadFormatIndicator => {
-                    payload_format_indicator = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::MessageExpiryInterval => {
-                    message_expiry_interval = Some(read_u32(&mut bytes)?);
-                    cursor += 4;
-                }
-                PropertyType::ContentType => {
-                    let typ = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + typ.len();
-                    content_type = Some(typ);
-                }
-                PropertyType::ResponseTopic => {
-                    let topic = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + topic.len();
-                    response_topic = Some(topic);
-                }
-                PropertyType::CorrelationData => {
-                    let data = read_mqtt_bytes(&mut bytes)?;
-                    cursor += 2 + data.len();
-                    correlation_data = Some(data);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(WillProperties {
-            delay_interval,
-            payload_format_indicator,
-            message_expiry_interval,
-            content_type,
-            response_topic,
-            correlation_data,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(delay_interval) = self.delay_interval {
-            buffer.put_u8(PropertyType::WillDelayInterval as u8);
-            buffer.put_u32(delay_interval);
-        }
-
-        if let Some(payload_format_indicator) = self.payload_format_indicator {
-            buffer.put_u8(PropertyType::PayloadFormatIndicator as u8);
-            buffer.put_u8(payload_format_indicator);
-        }
-
-        if let Some(message_expiry_interval) = self.message_expiry_interval {
-            buffer.put_u8(PropertyType::MessageExpiryInterval as u8);
-            buffer.put_u32(message_expiry_interval);
-        }
-
-        if let Some(typ) = &self.content_type {
-            buffer.put_u8(PropertyType::ContentType as u8);
-            write_mqtt_string(buffer, typ);
-        }
-
-        if let Some(topic) = &self.response_topic {
-            buffer.put_u8(PropertyType::ResponseTopic as u8);
-            write_mqtt_string(buffer, topic);
-        }
-
-        if let Some(data) = &self.correlation_data {
-            buffer.put_u8(PropertyType::CorrelationData as u8);
-            write_mqtt_bytes(buffer, data);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Login {
-    pub username: String,
-    pub password: String,
-}
-
-impl Login {
-    pub fn new<U: Into<String>, P: Into<String>>(u: U, p: P) -> Login {
-        Login {
-            username: u.into(),
-            password: p.into(),
-        }
-    }
-
-    fn read(connect_flags: u8, mut bytes: &mut Bytes) -> Result<Option<Login>, Error> {
-        let username = match connect_flags & 0b1000_0000 {
-            0 => String::new(),
-            _ => read_mqtt_string(&mut bytes)?,
-        };
-
-        let password = match connect_flags & 0b0100_0000 {
-            0 => String::new(),
-            _ => read_mqtt_string(&mut bytes)?,
-        };
-
-        if username.is_empty() && password.is_empty() {
-            Ok(None)
-        } else {
-            Ok(Some(Login { username, password }))
-        }
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 0;
-
-        if !self.username.is_empty() {
-            len += 2 + self.username.len();
-        }
-
-        if !self.password.is_empty() {
-            len += 2 + self.password.len();
-        }
-
-        len
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> u8 {
-        let mut connect_flags = 0;
-        if !self.username.is_empty() {
-            connect_flags |= 0x80;
-            write_mqtt_string(buffer, &self.username);
-        }
-
-        if !self.password.is_empty() {
-            connect_flags |= 0x40;
-            write_mqtt_string(buffer, &self.password);
-        }
-
-        connect_flags
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ConnectProperties {
-    /// Expiry interval property after loosing connection
-    pub session_expiry_interval: Option<u32>,
-    /// Maximum simultaneous packets
-    pub receive_maximum: Option<u16>,
-    /// Maximum packet size
-    pub max_packet_size: Option<u32>,
-    /// Maximum mapping integer for a topic
-    pub topic_alias_max: Option<u16>,
-    pub request_response_info: Option<u8>,
-    pub request_problem_info: Option<u8>,
-    /// List of user properties
-    pub user_properties: Vec<(String, String)>,
-    /// Method of authentication
-    pub authentication_method: Option<String>,
-    /// Authentication data
-    pub authentication_data: Option<Bytes>,
-}
-
-impl ConnectProperties {
-    fn _new() -> ConnectProperties {
-        ConnectProperties {
-            session_expiry_interval: None,
-            receive_maximum: None,
-            max_packet_size: None,
-            topic_alias_max: None,
-            request_response_info: None,
-            request_problem_info: None,
-            user_properties: Vec::new(),
-            authentication_method: None,
-            authentication_data: None,
-        }
-    }
-
-    fn read(mut bytes: &mut Bytes) -> Result<Option<ConnectProperties>, Error> {
-        let mut session_expiry_interval = None;
-        let mut receive_maximum = None;
-        let mut max_packet_size = None;
-        let mut topic_alias_max = None;
-        let mut request_response_info = None;
-        let mut request_problem_info = None;
-        let mut user_properties = Vec::new();
-        let mut authentication_method = None;
-        let mut authentication_data = None;
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-            match property(prop)? {
-                PropertyType::SessionExpiryInterval => {
-                    session_expiry_interval = Some(read_u32(&mut bytes)?);
-                    cursor += 4;
-                }
-                PropertyType::ReceiveMaximum => {
-                    receive_maximum = Some(read_u16(&mut bytes)?);
-                    cursor += 2;
-                }
-                PropertyType::MaximumPacketSize => {
-                    max_packet_size = Some(read_u32(&mut bytes)?);
-                    cursor += 4;
-                }
-                PropertyType::TopicAliasMaximum => {
-                    topic_alias_max = Some(read_u16(&mut bytes)?);
-                    cursor += 2;
-                }
-                PropertyType::RequestResponseInformation => {
-                    request_response_info = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::RequestProblemInformation => {
-                    request_problem_info = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                PropertyType::AuthenticationMethod => {
-                    let method = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + method.len();
-                    authentication_method = Some(method);
-                }
-                PropertyType::AuthenticationData => {
-                    let data = read_mqtt_bytes(&mut bytes)?;
-                    cursor += 2 + data.len();
-                    authentication_data = Some(data);
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(ConnectProperties {
-            session_expiry_interval,
-            receive_maximum,
-            max_packet_size,
-            topic_alias_max,
-            request_response_info,
-            request_problem_info,
-            user_properties,
-            authentication_method,
-            authentication_data,
-        }))
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 0;
-
-        if self.session_expiry_interval.is_some() {
-            len += 1 + 4;
-        }
-
-        if self.receive_maximum.is_some() {
-            len += 1 + 2;
-        }
-
-        if self.max_packet_size.is_some() {
-            len += 1 + 4;
-        }
-
-        if self.topic_alias_max.is_some() {
-            len += 1 + 2;
-        }
-
-        if self.request_response_info.is_some() {
-            len += 1 + 1;
-        }
-
-        if self.request_problem_info.is_some() {
-            len += 1 + 1;
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        if let Some(authentication_method) = &self.authentication_method {
-            len += 1 + 2 + authentication_method.len();
-        }
-
-        if let Some(authentication_data) = &self.authentication_data {
-            len += 1 + 2 + authentication_data.len();
-        }
-
-        len
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(session_expiry_interval) = self.session_expiry_interval {
-            buffer.put_u8(PropertyType::SessionExpiryInterval as u8);
-            buffer.put_u32(session_expiry_interval);
-        }
-
-        if let Some(receive_maximum) = self.receive_maximum {
-            buffer.put_u8(PropertyType::ReceiveMaximum as u8);
-            buffer.put_u16(receive_maximum);
-        }
-
-        if let Some(max_packet_size) = self.max_packet_size {
-            buffer.put_u8(PropertyType::MaximumPacketSize as u8);
-            buffer.put_u32(max_packet_size);
-        }
-
-        if let Some(topic_alias_max) = self.topic_alias_max {
-            buffer.put_u8(PropertyType::TopicAliasMaximum as u8);
-            buffer.put_u16(topic_alias_max);
-        }
-
-        if let Some(request_response_info) = self.request_response_info {
-            buffer.put_u8(PropertyType::RequestResponseInformation as u8);
-            buffer.put_u8(request_response_info);
-        }
-
-        if let Some(request_problem_info) = self.request_problem_info {
-            buffer.put_u8(PropertyType::RequestProblemInformation as u8);
-            buffer.put_u8(request_problem_info);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        if let Some(authentication_method) = &self.authentication_method {
-            buffer.put_u8(PropertyType::AuthenticationMethod as u8);
-            write_mqtt_string(buffer, authentication_method);
-        }
-
-        if let Some(authentication_data) = &self.authentication_data {
-            buffer.put_u8(PropertyType::AuthenticationData as u8);
-            write_mqtt_bytes(buffer, authentication_data);
-        }
-
-        Ok(())
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    fn sample() -> Connect {
-        let connect_properties = ConnectProperties {
-            session_expiry_interval: Some(1234),
-            receive_maximum: Some(432),
-            max_packet_size: Some(100),
-            topic_alias_max: Some(456),
-            request_response_info: Some(1),
-            request_problem_info: Some(1),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-            authentication_method: Some("test".to_owned()),
-            authentication_data: Some(Bytes::from(vec![1, 2, 3, 4])),
-        };
-
-        let will_properties = WillProperties {
-            delay_interval: Some(1234),
-            payload_format_indicator: Some(0),
-            message_expiry_interval: Some(4321),
-            content_type: Some("test".to_owned()),
-            response_topic: Some("topic".to_owned()),
-            correlation_data: Some(Bytes::from(vec![1, 2, 3, 4])),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        let will = LastWill {
-            topic: "mydevice/status".to_string(),
-            message: Bytes::from(vec![b'd', b'e', b'a', b'd']),
-            qos: QoS::AtMostOnce,
-            retain: false,
-            properties: Some(will_properties),
-        };
-
-        let login = Login {
-            username: "matteo".to_string(),
-            password: "collina".to_string(),
-        };
-
-        Connect {
-            protocol: Protocol::V5,
-            keep_alive: 0,
-            properties: Some(connect_properties),
-            client_id: "my-device".to_string(),
-            clean_session: true,
-            last_will: Some(will),
-            login: Some(login),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x10, // packet type
-            0x9d, // remaining len
-            0x01, // remaining len
-            0x00, 0x04, // 4
-            0x4d, // M
-            0x51, // Q
-            0x54, // T
-            0x54, // T
-            0x05, // Level
-            0xc6, // connect flags
-            0x00, 0x00, // keep alive
-            0x2f, // properties len
-            0x11, 0x00, 0x00, 0x04, 0xd2, // session expiry interval
-            0x21, 0x01, 0xb0, // receive_maximum
-            0x27, 0x00, 0x00, 0x00, 0x64, // max packet size
-            0x22, 0x01, 0xc8, // topic_alias_max
-            0x19, 0x01, // request_response_info
-            0x17, 0x01, // request_problem_info
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user
-            0x15, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // authentication_method
-            0x16, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, // authentication_data
-            0x00, 0x09, 0x6d, 0x79, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, // client id
-            0x2f, // will properties len
-            0x18, 0x00, 0x00, 0x04, 0xd2, // will delay interval
-            0x01, 0x00, // payload format indicator
-            0x02, 0x00, 0x00, 0x10, 0xe1, // message expiry interval
-            0x03, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // content type
-            0x08, 0x00, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, // response topic
-            0x09, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, // correlation_data
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // will user properties
-            0x00, 0x0f, 0x6d, 0x79, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61,
-            0x74, 0x75, 0x73, // will topic
-            0x00, 0x04, 0x64, 0x65, 0x61, 0x64, // will payload
-            0x00, 0x06, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x6f, // username
-            0x00, 0x07, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x61, // password
-        ]
-    }
-
-    #[test]
-    fn connect1_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let connect_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let connect = Connect::read(fixed_header, connect_bytes).unwrap();
-        assert_eq!(connect, sample());
-    }
-
-    #[test]
-    fn connect1_encoding_works() {
-        let connect = sample();
-        let mut buf = BytesMut::new();
-        connect.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample_bytes());
-    }
-
-    fn sample2() -> Connect {
-        Connect {
-            protocol: Protocol::V5,
-            keep_alive: 10,
-            properties: None,
-            client_id: "hackathonmqtt5test".to_owned(),
-            clean_session: true,
-            last_will: None,
-            login: None,
-        }
-    }
-
-    fn sample2_bytes() -> Vec<u8> {
-        vec![
-            0x10, // packet type
-            0x1f, 0x00, // remaining len
-            0x04, // 4
-            0x4d, 0x51, 0x54, 0x54, // MQTT
-            0x05, // level
-            0x02, // connect flags
-            0x00, 0x0a, // keep alive
-            0x00, 0x00, 0x12, 0x68, 0x61, 0x63, 0x6b, 0x61, 0x74, 0x68, 0x6f, 0x6e, 0x6d, 0x71,
-            0x74, 0x74, 0x35, 0x74, 0x65, 0x73, 0x74, // payload
-            0x10, 0x11, 0x12, // extra bytes in the stream
-        ]
-    }
-
-    #[test]
-    fn connect2_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample2_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let connect_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let connect = Connect::read(fixed_header, connect_bytes).unwrap();
-        assert_eq!(connect, sample2());
-    }
-
-    #[test]
-    fn connect2_encoding_works() {
-        let connect = sample2();
-        let mut buf = BytesMut::new();
-        connect.write(&mut buf).unwrap();
-
-        let expected = sample2_bytes();
-        assert_eq!(&buf[..], &expected[0..(expected.len() - 3)]);
-    }
-
-    fn sample3() -> Connect {
-        let connect_properties = ConnectProperties {
-            session_expiry_interval: Some(1234),
-            receive_maximum: Some(432),
-            max_packet_size: Some(100),
-            topic_alias_max: Some(456),
-            request_response_info: Some(1),
-            request_problem_info: Some(1),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-            authentication_method: Some("test".to_owned()),
-            authentication_data: Some(Bytes::from(vec![1, 2, 3, 4])),
-        };
-
-        let will = LastWill {
-            topic: "mydevice/status".to_string(),
-            message: Bytes::from(vec![b'd', b'e', b'a', b'd']),
-            qos: QoS::AtMostOnce,
-            retain: false,
-            properties: None,
-        };
-
-        let login = Login {
-            username: "matteo".to_string(),
-            password: "collina".to_string(),
-        };
-
-        Connect {
-            protocol: Protocol::V5,
-            keep_alive: 0,
-            properties: Some(connect_properties),
-            client_id: "my-device".to_string(),
-            clean_session: true,
-            last_will: Some(will),
-            login: Some(login),
-        }
-    }
-
-    fn sample3_bytes() -> Vec<u8> {
-        vec![
-            0x10, 0x6e, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x05, 0xc6, 0x00, 0x00, 0x2f, 0x11,
-            0x00, 0x00, 0x04, 0xd2, 0x21, 0x01, 0xb0, 0x27, 0x00, 0x00, 0x00, 0x64, 0x22, 0x01,
-            0xc8, 0x19, 0x01, 0x17, 0x01, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04,
-            0x74, 0x65, 0x73, 0x74, 0x15, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x16, 0x00, 0x04,
-            0x01, 0x02, 0x03, 0x04, 0x00, 0x09, 0x6d, 0x79, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63,
-            0x65, 0x00, 0x00, 0x0f, 0x6d, 0x79, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73,
-            0x74, 0x61, 0x74, 0x75, 0x73, 0x00, 0x04, 0x64, 0x65, 0x61, 0x64, 0x00, 0x06, 0x6d,
-            0x61, 0x74, 0x74, 0x65, 0x6f, 0x00, 0x07, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x61,
-        ]
-    }
-
-    #[test]
-    fn connect3_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample3_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let connect_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let connect = Connect::read(fixed_header, connect_bytes).unwrap();
-        assert_eq!(connect, sample3());
-    }
-
-    #[test]
-    fn connect3_encoding_works() {
-        let connect = sample3();
-        let mut buf = BytesMut::new();
-        connect.write(&mut buf).unwrap();
-
-        let expected = sample3_bytes();
-        assert_eq!(&buf[..], &expected[0..(expected.len())]);
-    }
-
-    #[test]
-    fn missing_properties_are_encoded() {}
-}
diff --git a/rumqttc/src/v5/packet/ping.rs b/rumqttc/src/v5/packet/ping.rs
deleted file mode 100644
index 1072029bd..000000000
--- a/rumqttc/src/v5/packet/ping.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use super::*;
-use bytes::{BufMut, BytesMut};
-
-pub struct PingReq;
-
-impl PingReq {
-    pub fn write(&self, payload: &mut BytesMut) -> Result<usize, Error> {
-        payload.put_slice(&[0xC0, 0x00]);
-        Ok(2)
-    }
-}
-
-pub struct PingResp;
-
-impl PingResp {
-    pub fn write(&self, payload: &mut BytesMut) -> Result<usize, Error> {
-        payload.put_slice(&[0xD0, 0x00]);
-        Ok(2)
-    }
-}
diff --git a/rumqttc/src/v5/packet/puback.rs b/rumqttc/src/v5/packet/puback.rs
deleted file mode 100644
index 37db5473f..000000000
--- a/rumqttc/src/v5/packet/puback.rs
+++ /dev/null
@@ -1,325 +0,0 @@
-use super::*;
-use bytes::{Buf, BufMut, Bytes, BytesMut};
-
-/// Return code in connack
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[repr(u8)]
-pub enum PubAckReason {
-    Success = 0,
-    NoMatchingSubscribers = 16,
-    UnspecifiedError = 128,
-    ImplementationSpecificError = 131,
-    NotAuthorized = 135,
-    TopicNameInvalid = 144,
-    PacketIdentifierInUse = 145,
-    QuotaExceeded = 151,
-    PayloadFormatInvalid = 153,
-}
-
-/// Acknowledgement to QoS1 publish
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubAck {
-    pub pkid: u16,
-    pub reason: PubAckReason,
-    pub properties: Option<PubAckProperties>,
-}
-
-impl PubAck {
-    pub fn new(pkid: u16) -> PubAck {
-        PubAck {
-            pkid,
-            reason: PubAckReason::Success,
-            properties: None,
-        }
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 2 + 1; // pkid + reason
-
-        // If there are no properties, sending reason code is optional
-        if self.reason == PubAckReason::Success && self.properties.is_none() {
-            return 2;
-        }
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            len += 1
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-        let pkid = read_u16(&mut bytes)?;
-
-        // No reason code or properties if remaining length == 2
-        if fixed_header.remaining_len == 2 {
-            return Ok(PubAck {
-                pkid,
-                reason: PubAckReason::Success,
-                properties: None,
-            });
-        }
-
-        // No properties len or properties if remaining len > 2 but < 4
-        let ack_reason = read_u8(&mut bytes)?;
-        if fixed_header.remaining_len < 4 {
-            return Ok(PubAck {
-                pkid,
-                reason: reason(ack_reason)?,
-                properties: None,
-            });
-        }
-
-        let puback = PubAck {
-            pkid,
-            reason: reason(ack_reason)?,
-            properties: PubAckProperties::extract(&mut bytes)?,
-        };
-
-        Ok(puback)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        let len = self.len();
-        buffer.put_u8(0x40);
-
-        let count = write_remaining_length(buffer, len)?;
-        buffer.put_u16(self.pkid);
-
-        // Reason code is optional with success if there are no properties
-        if self.reason == PubAckReason::Success && self.properties.is_none() {
-            return Ok(4);
-        }
-
-        buffer.put_u8(self.reason as u8);
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        Ok(1 + count + len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubAckProperties {
-    pub reason_string: Option<String>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl PubAckProperties {
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(reason) = &self.reason_string {
-            len += 1 + 2 + reason.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<PubAckProperties>, Error> {
-        let mut reason_string = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reason.len();
-                    reason_string = Some(reason);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(PubAckProperties {
-            reason_string,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(reason) = &self.reason_string {
-            buffer.put_u8(PropertyType::ReasonString as u8);
-            write_mqtt_string(buffer, reason);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-/// Connection return code type
-fn reason(num: u8) -> Result<PubAckReason, Error> {
-    let code = match num {
-        0 => PubAckReason::Success,
-        16 => PubAckReason::NoMatchingSubscribers,
-        128 => PubAckReason::UnspecifiedError,
-        131 => PubAckReason::ImplementationSpecificError,
-        135 => PubAckReason::NotAuthorized,
-        144 => PubAckReason::TopicNameInvalid,
-        145 => PubAckReason::PacketIdentifierInUse,
-        151 => PubAckReason::QuotaExceeded,
-        153 => PubAckReason::PayloadFormatInvalid,
-        num => return Err(Error::InvalidConnectReturnCode(num)),
-    };
-
-    Ok(code)
-}
-
-#[cfg(v5)]
-#[cfg(test)]
-mod test {
-    use super::*;
-    use alloc::vec;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> PubAck {
-        let properties = PubAckProperties {
-            reason_string: Some("test".to_owned()),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        PubAck {
-            pkid: 42,
-            reason: PubAckReason::NoMatchingSubscribers,
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x40, // payload type
-            0x18, // remaining length
-            0x00, 0x2a, // packet id
-            0x10, // reason
-            0x14, // properties len
-            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // reason_string
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-        ]
-    }
-
-    #[test]
-    fn puback_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let puback_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let puback = PubAck::read(fixed_header, puback_bytes).unwrap();
-        assert_eq!(puback, sample());
-    }
-
-    #[test]
-    fn puback_encoding_works() {
-        let puback = sample();
-        let mut buf = BytesMut::new();
-        puback.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample_bytes());
-    }
-
-    fn sample2() -> PubAck {
-        PubAck {
-            pkid: 42,
-            reason: PubAckReason::NoMatchingSubscribers,
-            properties: None,
-        }
-    }
-
-    fn sample2_bytes() -> Vec<u8> {
-        vec![0x40, 0x03, 0x00, 0x2a, 0x10]
-    }
-
-    #[test]
-    fn puback2_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample2_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let puback_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let puback = PubAck::read(fixed_header, puback_bytes).unwrap();
-        assert_eq!(puback, sample2());
-    }
-
-    #[test]
-    fn puback2_encoding_works() {
-        let puback = sample2();
-        let mut buf = BytesMut::new();
-
-        puback.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample2_bytes());
-    }
-
-    fn sample3() -> PubAck {
-        PubAck {
-            pkid: 42,
-            reason: PubAckReason::Success,
-            properties: None,
-        }
-    }
-
-    fn sample3_bytes() -> Vec<u8> {
-        vec![0x40, 0x02, 0x00, 0x2a]
-    }
-
-    #[test]
-    fn puback3_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample3_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let puback_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let puback = PubAck::read(fixed_header, puback_bytes).unwrap();
-        assert_eq!(puback, sample3());
-    }
-
-    #[test]
-    fn puback3_encoding_works() {
-        let puback = sample3();
-        let mut buf = BytesMut::new();
-
-        puback.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample3_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/pubcomp.rs b/rumqttc/src/v5/packet/pubcomp.rs
deleted file mode 100644
index a75416f29..000000000
--- a/rumqttc/src/v5/packet/pubcomp.rs
+++ /dev/null
@@ -1,241 +0,0 @@
-use super::*;
-use bytes::{Buf, BufMut, Bytes, BytesMut};
-
-/// Return code in connack
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[repr(u8)]
-pub enum PubCompReason {
-    Success = 0,
-    PacketIdentifierNotFound = 146,
-}
-
-/// Acknowledgement to QoS1 publish
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubComp {
-    pub pkid: u16,
-    pub reason: PubCompReason,
-    pub properties: Option<PubCompProperties>,
-}
-
-impl PubComp {
-    pub fn new(pkid: u16) -> PubComp {
-        PubComp {
-            pkid,
-            reason: PubCompReason::Success,
-            properties: None,
-        }
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 2 + 1; // pkid + reason
-
-        // If there are no properties during success, sending reason code is optional
-        if self.reason == PubCompReason::Success && self.properties.is_none() {
-            return 2;
-        }
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-        let pkid = read_u16(&mut bytes)?;
-
-        if fixed_header.remaining_len == 2 {
-            return Ok(PubComp {
-                pkid,
-                reason: PubCompReason::Success,
-                properties: None,
-            });
-        }
-
-        let ack_reason = read_u8(&mut bytes)?;
-        if fixed_header.remaining_len < 4 {
-            return Ok(PubComp {
-                pkid,
-                reason: reason(ack_reason)?,
-                properties: None,
-            });
-        }
-
-        let puback = PubComp {
-            pkid,
-            reason: reason(ack_reason)?,
-            properties: PubCompProperties::extract(&mut bytes)?,
-        };
-
-        Ok(puback)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        let len = self.len();
-        buffer.put_u8(0x70);
-        let count = write_remaining_length(buffer, len)?;
-        buffer.put_u16(self.pkid);
-
-        // If there are no properties during success, sending reason code is optional
-        if self.reason == PubCompReason::Success && self.properties.is_none() {
-            return Ok(4);
-        }
-
-        buffer.put_u8(self.reason as u8);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        Ok(1 + count + len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubCompProperties {
-    pub reason_string: Option<String>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl PubCompProperties {
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(reason) = &self.reason_string {
-            len += 1 + 2 + reason.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<PubCompProperties>, Error> {
-        let mut reason_string = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reason.len();
-                    reason_string = Some(reason);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(PubCompProperties {
-            reason_string,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(reason) = &self.reason_string {
-            buffer.put_u8(PropertyType::ReasonString as u8);
-            write_mqtt_string(buffer, reason);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-/// Connection return code type
-fn reason(num: u8) -> Result<PubCompReason, Error> {
-    let code = match num {
-        0 => PubCompReason::Success,
-        146 => PubCompReason::PacketIdentifierNotFound,
-        num => return Err(Error::InvalidConnectReturnCode(num)),
-    };
-
-    Ok(code)
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> PubComp {
-        let properties = PubCompProperties {
-            reason_string: Some("test".to_owned()),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        PubComp {
-            pkid: 42,
-            reason: PubCompReason::PacketIdentifierNotFound,
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x70, // payload type
-            0x18, // remaining length
-            0x00, 0x2a, // packet id
-            0x92, // reason
-            0x14, // properties len
-            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // reason_string
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-        ]
-    }
-
-    #[test]
-    fn pubcomp_parsing_works_correctly() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let pubcomp_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let pubcomp = PubComp::read(fixed_header, pubcomp_bytes).unwrap();
-        assert_eq!(pubcomp, sample());
-    }
-
-    #[test]
-    fn pubcomp_encoding_works_correctly() {
-        let pubcomp = sample();
-        let mut buf = BytesMut::new();
-        pubcomp.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/publish.rs b/rumqttc/src/v5/packet/publish.rs
deleted file mode 100644
index 443fa3513..000000000
--- a/rumqttc/src/v5/packet/publish.rs
+++ /dev/null
@@ -1,389 +0,0 @@
-use super::*;
-use bytes::{Buf, Bytes};
-use core::fmt;
-
-/// Publish packet
-#[derive(Clone, PartialEq, Eq)]
-pub struct Publish {
-    pub dup: bool,
-    pub qos: QoS,
-    pub retain: bool,
-    pub topic: String,
-    pub pkid: u16,
-    pub properties: Option<PublishProperties>,
-    pub payload: Bytes,
-}
-
-impl Publish {
-    pub fn new<S: Into<String>, P: Into<Vec<u8>>>(topic: S, qos: QoS, payload: P) -> Publish {
-        Publish {
-            dup: false,
-            qos,
-            retain: false,
-            pkid: 0,
-            topic: topic.into(),
-            properties: None,
-            payload: Bytes::from(payload.into()),
-        }
-    }
-
-    pub fn from_bytes<S: Into<String>>(topic: S, qos: QoS, payload: Bytes) -> Publish {
-        Publish {
-            dup: false,
-            qos,
-            retain: false,
-            pkid: 0,
-            topic: topic.into(),
-            properties: None,
-            payload,
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        let mut len = 2 + self.topic.len();
-        if self.qos != QoS::AtMostOnce && self.pkid != 0 {
-            len += 2;
-        }
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            // just 1 byte representing 0 len
-            len += 1;
-        }
-
-        len += self.payload.len();
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let qos = qos((fixed_header.byte1 & 0b0110) >> 1)?;
-        let dup = (fixed_header.byte1 & 0b1000) != 0;
-        let retain = (fixed_header.byte1 & 0b0001) != 0;
-
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-        let topic = read_mqtt_string(&mut bytes)?;
-
-        // Packet identifier exists where QoS > 0
-        let pkid = match qos {
-            QoS::AtMostOnce => 0,
-            QoS::AtLeastOnce | QoS::ExactlyOnce => read_u16(&mut bytes)?,
-        };
-
-        if qos != QoS::AtMostOnce && pkid == 0 {
-            return Err(Error::PacketIdZero);
-        }
-
-        let publish = Publish {
-            dup,
-            retain,
-            qos,
-            pkid,
-            topic,
-            properties: PublishProperties::extract(&mut bytes)?,
-            payload: bytes,
-        };
-
-        Ok(publish)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        let len = self.len();
-
-        let dup = self.dup as u8;
-        let qos = self.qos as u8;
-        let retain = self.retain as u8;
-        buffer.put_u8(0b0011_0000 | retain | qos << 1 | dup << 3);
-
-        let count = write_remaining_length(buffer, len)?;
-        write_mqtt_string(buffer, self.topic.as_str());
-
-        if self.qos != QoS::AtMostOnce {
-            let pkid = self.pkid;
-            if pkid == 0 {
-                return Err(Error::PacketIdZero);
-            }
-
-            buffer.put_u16(pkid);
-        }
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        buffer.extend_from_slice(&self.payload);
-
-        Ok(1 + count + len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PublishProperties {
-    pub payload_format_indicator: Option<u8>,
-    pub message_expiry_interval: Option<u32>,
-    pub topic_alias: Option<u16>,
-    pub response_topic: Option<String>,
-    pub correlation_data: Option<Bytes>,
-    pub user_properties: Vec<(String, String)>,
-    pub subscription_identifiers: Vec<usize>,
-    pub content_type: Option<String>,
-}
-
-impl PublishProperties {
-    fn len(&self) -> usize {
-        let mut len = 0;
-
-        if self.payload_format_indicator.is_some() {
-            len += 1 + 1;
-        }
-
-        if self.message_expiry_interval.is_some() {
-            len += 1 + 4;
-        }
-
-        if self.topic_alias.is_some() {
-            len += 1 + 2;
-        }
-
-        if let Some(topic) = &self.response_topic {
-            len += 1 + 2 + topic.len();
-        }
-
-        if let Some(data) = &self.correlation_data {
-            len += 1 + 2 + data.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        for id in self.subscription_identifiers.iter() {
-            len += 1 + len_len(*id);
-        }
-
-        if let Some(typ) = &self.content_type {
-            len += 1 + 2 + typ.len();
-        }
-
-        len
-    }
-
-    fn extract(mut bytes: &mut Bytes) -> Result<Option<PublishProperties>, Error> {
-        let mut payload_format_indicator = None;
-        let mut message_expiry_interval = None;
-        let mut topic_alias = None;
-        let mut response_topic = None;
-        let mut correlation_data = None;
-        let mut user_properties = Vec::new();
-        let mut subscription_identifiers = Vec::new();
-        let mut content_type = None;
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::PayloadFormatIndicator => {
-                    payload_format_indicator = Some(read_u8(&mut bytes)?);
-                    cursor += 1;
-                }
-                PropertyType::MessageExpiryInterval => {
-                    message_expiry_interval = Some(read_u32(&mut bytes)?);
-                    cursor += 4;
-                }
-                PropertyType::TopicAlias => {
-                    topic_alias = Some(read_u16(&mut bytes)?);
-                    cursor += 2;
-                }
-                PropertyType::ResponseTopic => {
-                    let topic = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + topic.len();
-                    response_topic = Some(topic);
-                }
-                PropertyType::CorrelationData => {
-                    let data = read_mqtt_bytes(&mut bytes)?;
-                    cursor += 2 + data.len();
-                    correlation_data = Some(data);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                PropertyType::SubscriptionIdentifier => {
-                    let (id_len, id) = length(bytes.iter())?;
-                    cursor += 1 + id_len;
-                    bytes.advance(id_len);
-                    subscription_identifiers.push(id);
-                }
-                PropertyType::ContentType => {
-                    let typ = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + typ.len();
-                    content_type = Some(typ);
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(PublishProperties {
-            payload_format_indicator,
-            message_expiry_interval,
-            topic_alias,
-            response_topic,
-            correlation_data,
-            user_properties,
-            subscription_identifiers,
-            content_type,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(payload_format_indicator) = self.payload_format_indicator {
-            buffer.put_u8(PropertyType::PayloadFormatIndicator as u8);
-            buffer.put_u8(payload_format_indicator);
-        }
-
-        if let Some(message_expiry_interval) = self.message_expiry_interval {
-            buffer.put_u8(PropertyType::MessageExpiryInterval as u8);
-            buffer.put_u32(message_expiry_interval);
-        }
-
-        if let Some(topic_alias) = self.topic_alias {
-            buffer.put_u8(PropertyType::TopicAlias as u8);
-            buffer.put_u16(topic_alias);
-        }
-
-        if let Some(topic) = &self.response_topic {
-            buffer.put_u8(PropertyType::ResponseTopic as u8);
-            write_mqtt_string(buffer, topic);
-        }
-
-        if let Some(data) = &self.correlation_data {
-            buffer.put_u8(PropertyType::CorrelationData as u8);
-            write_mqtt_bytes(buffer, data);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        for id in self.subscription_identifiers.iter() {
-            buffer.put_u8(PropertyType::SubscriptionIdentifier as u8);
-            write_remaining_length(buffer, *id)?;
-        }
-
-        if let Some(typ) = &self.content_type {
-            buffer.put_u8(PropertyType::ContentType as u8);
-            write_mqtt_string(buffer, typ);
-        }
-
-        Ok(())
-    }
-}
-
-impl fmt::Debug for Publish {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Topic = {}, Qos = {:?}, Retain = {}, Pkid = {:?}, Payload Size = {}",
-            self.topic,
-            self.qos,
-            self.retain,
-            self.pkid,
-            self.payload.len()
-        )
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::{Bytes, BytesMut};
-    use pretty_assertions::assert_eq;
-
-    fn sample_v5() -> Publish {
-        let publish_properties = PublishProperties {
-            payload_format_indicator: Some(1),
-            message_expiry_interval: Some(4321),
-            topic_alias: Some(100),
-            response_topic: Some("topic".to_owned()),
-            correlation_data: Some(Bytes::from(vec![1, 2, 3, 4])),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-            subscription_identifiers: vec![120, 121],
-            content_type: Some("test".to_owned()),
-        };
-
-        Publish {
-            dup: false,
-            qos: QoS::ExactlyOnce,
-            retain: false,
-            topic: "test".to_string(),
-            pkid: 42,
-            properties: Some(publish_properties),
-            payload: Bytes::from(vec![b't', b'e', b's', b't']),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x34, // payload type
-            0x3e, // remaining len
-            0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // topic name
-            0x00, 0x2a, // pkid
-            0x31, // properties len
-            0x01, 0x01, // payload format indicator
-            0x02, 0x00, 0x00, 0x10, 0xe1, // message_expiry_interval
-            0x23, 0x00, 0x64, // topic alias
-            0x08, 0x00, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, // response topic
-            0x09, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, // correlation_data
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-            0x0b, 0x78, // subscription_identifier
-            0x0b, 0x79, // subscription_identifier
-            0x03, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // content_type
-            0x74, 0x65, 0x73, 0x74, // payload
-        ]
-    }
-
-    #[test]
-    fn publish_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let publish_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let publish = Publish::read(fixed_header, publish_bytes).unwrap();
-        assert_eq!(publish, sample_v5());
-    }
-
-    #[test]
-    fn publish_encoding_works() {
-        let publish = sample_v5();
-        let mut buf = BytesMut::new();
-        publish.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample_bytes());
-    }
-
-    #[test]
-    fn missing_properties_are_encoded() {}
-}
diff --git a/rumqttc/src/v5/packet/pubrec.rs b/rumqttc/src/v5/packet/pubrec.rs
deleted file mode 100644
index 286b22c7c..000000000
--- a/rumqttc/src/v5/packet/pubrec.rs
+++ /dev/null
@@ -1,254 +0,0 @@
-use super::*;
-use bytes::{Buf, BufMut, Bytes, BytesMut};
-
-/// Return code in connack
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[repr(u8)]
-pub enum PubRecReason {
-    Success = 0,
-    NoMatchingSubscribers = 16,
-    UnspecifiedError = 128,
-    ImplementationSpecificError = 131,
-    NotAuthorized = 135,
-    TopicNameInvalid = 144,
-    PacketIdentifierInUse = 145,
-    QuotaExceeded = 151,
-    PayloadFormatInvalid = 153,
-}
-
-/// Acknowledgement to QoS1 publish
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubRec {
-    pub pkid: u16,
-    pub reason: PubRecReason,
-    pub properties: Option<PubRecProperties>,
-}
-
-impl PubRec {
-    pub fn new(pkid: u16) -> PubRec {
-        PubRec {
-            pkid,
-            reason: PubRecReason::Success,
-            properties: None,
-        }
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 2 + 1; // pkid + reason
-
-        // If there are no properties during success, sending reason code is optional
-        if self.reason == PubRecReason::Success && self.properties.is_none() {
-            return 2;
-        }
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-        let pkid = read_u16(&mut bytes)?;
-        if fixed_header.remaining_len == 2 {
-            return Ok(PubRec {
-                pkid,
-                reason: PubRecReason::Success,
-                properties: None,
-            });
-        }
-
-        let ack_reason = read_u8(&mut bytes)?;
-        if fixed_header.remaining_len < 4 {
-            return Ok(PubRec {
-                pkid,
-                reason: reason(ack_reason)?,
-                properties: None,
-            });
-        }
-
-        let puback = PubRec {
-            pkid,
-            reason: reason(ack_reason)?,
-            properties: PubRecProperties::extract(&mut bytes)?,
-        };
-
-        Ok(puback)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        let len = self.len();
-        buffer.put_u8(0x50);
-        let count = write_remaining_length(buffer, len)?;
-        buffer.put_u16(self.pkid);
-
-        // If there are no properties during success, sending reason code is optional
-        if self.reason == PubRecReason::Success && self.properties.is_none() {
-            return Ok(4);
-        }
-
-        buffer.put_u8(self.reason as u8);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        Ok(1 + count + len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubRecProperties {
-    pub reason_string: Option<String>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl PubRecProperties {
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(reason) = &self.reason_string {
-            len += 1 + 2 + reason.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<PubRecProperties>, Error> {
-        let mut reason_string = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reason.len();
-                    reason_string = Some(reason);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(PubRecProperties {
-            reason_string,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(reason) = &self.reason_string {
-            buffer.put_u8(PropertyType::ReasonString as u8);
-            write_mqtt_string(buffer, reason);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-/// Connection return code type
-fn reason(num: u8) -> Result<PubRecReason, Error> {
-    let code = match num {
-        0 => PubRecReason::Success,
-        16 => PubRecReason::NoMatchingSubscribers,
-        128 => PubRecReason::UnspecifiedError,
-        131 => PubRecReason::ImplementationSpecificError,
-        135 => PubRecReason::NotAuthorized,
-        144 => PubRecReason::TopicNameInvalid,
-        145 => PubRecReason::PacketIdentifierInUse,
-        151 => PubRecReason::QuotaExceeded,
-        153 => PubRecReason::PayloadFormatInvalid,
-        num => return Err(Error::InvalidConnectReturnCode(num)),
-    };
-
-    Ok(code)
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> PubRec {
-        let properties = PubRecProperties {
-            reason_string: Some("test".to_owned()),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        PubRec {
-            pkid: 42,
-            reason: PubRecReason::NoMatchingSubscribers,
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x50, // payload type
-            0x18, // remaining length
-            0x00, 0x2a, // packet id
-            0x10, // reason
-            0x14, // properties len
-            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // reason_string
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-        ]
-    }
-
-    #[test]
-    fn pubrec_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let pubrec_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let pubrec = PubRec::read(fixed_header, pubrec_bytes).unwrap();
-        assert_eq!(pubrec, sample());
-    }
-
-    #[test]
-    fn pubrec_encoding_works() {
-        let pubrec = sample();
-        let mut buf = BytesMut::new();
-        pubrec.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/pubrel.rs b/rumqttc/src/v5/packet/pubrel.rs
deleted file mode 100644
index 79f070181..000000000
--- a/rumqttc/src/v5/packet/pubrel.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use super::*;
-use bytes::{Buf, BufMut, Bytes, BytesMut};
-
-/// Return code in connack
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[repr(u8)]
-pub enum PubRelReason {
-    Success = 0,
-    PacketIdentifierNotFound = 146,
-}
-
-/// Acknowledgement to QoS1 publish
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubRel {
-    pub pkid: u16,
-    pub reason: PubRelReason,
-    pub properties: Option<PubRelProperties>,
-}
-
-impl PubRel {
-    pub fn new(pkid: u16) -> PubRel {
-        PubRel {
-            pkid,
-            reason: PubRelReason::Success,
-            properties: None,
-        }
-    }
-
-    fn len(&self) -> usize {
-        let mut len = 2 + 1; // pkid + reason
-
-        // If there are no properties during success, sending reason code is optional
-        if self.reason == PubRelReason::Success && self.properties.is_none() {
-            return 2;
-        }
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-        let pkid = read_u16(&mut bytes)?;
-        if fixed_header.remaining_len == 2 {
-            return Ok(PubRel {
-                pkid,
-                reason: PubRelReason::Success,
-                properties: None,
-            });
-        }
-
-        let ack_reason = read_u8(&mut bytes)?;
-        if fixed_header.remaining_len < 4 {
-            return Ok(PubRel {
-                pkid,
-                reason: reason(ack_reason)?,
-                properties: None,
-            });
-        }
-
-        let puback = PubRel {
-            pkid,
-            reason: reason(ack_reason)?,
-            properties: PubRelProperties::extract(&mut bytes)?,
-        };
-
-        Ok(puback)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        let len = self.len();
-        buffer.put_u8(0x62);
-        let count = write_remaining_length(buffer, len)?;
-        buffer.put_u16(self.pkid);
-
-        // If there are no properties during success, sending reason code is optional
-        if self.reason == PubRelReason::Success && self.properties.is_none() {
-            return Ok(4);
-        }
-
-        buffer.put_u8(self.reason as u8);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        Ok(1 + count + len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PubRelProperties {
-    pub reason_string: Option<String>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl PubRelProperties {
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(reason) = &self.reason_string {
-            len += 1 + 2 + reason.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<PubRelProperties>, Error> {
-        let mut reason_string = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reason.len();
-                    reason_string = Some(reason);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(PubRelProperties {
-            reason_string,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(reason) = &self.reason_string {
-            buffer.put_u8(PropertyType::ReasonString as u8);
-            write_mqtt_string(buffer, reason);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-/// Connection return code type
-fn reason(num: u8) -> Result<PubRelReason, Error> {
-    let code = match num {
-        0 => PubRelReason::Success,
-        146 => PubRelReason::PacketIdentifierNotFound,
-        num => return Err(Error::InvalidConnectReturnCode(num)),
-    };
-
-    Ok(code)
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> PubRel {
-        let properties = PubRelProperties {
-            reason_string: Some("test".to_owned()),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        PubRel {
-            pkid: 42,
-            reason: PubRelReason::PacketIdentifierNotFound,
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x62, // payload type
-            0x18, // remaining length
-            0x00, 0x2a, // packet id
-            0x92, // reason
-            0x14, // properties len
-            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // reason_string
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-        ]
-    }
-
-    #[test]
-    fn pubrel_parsing_works() {
-        let mut stream = bytes::BytesMut::new();
-        let packetstream = &sample_bytes();
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let pubrel_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let pubrel = PubRel::read(fixed_header, pubrel_bytes).unwrap();
-        assert_eq!(pubrel, sample());
-    }
-
-    #[test]
-    fn pubrel_encoding_works() {
-        let pubrel = sample();
-        let mut buf = BytesMut::new();
-        pubrel.write(&mut buf).unwrap();
-        assert_eq!(&buf[..], sample_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/suback.rs b/rumqttc/src/v5/packet/suback.rs
deleted file mode 100644
index fe2081c6e..000000000
--- a/rumqttc/src/v5/packet/suback.rs
+++ /dev/null
@@ -1,259 +0,0 @@
-use super::*;
-use bytes::{Buf, BufMut, Bytes, BytesMut};
-use std::convert::{TryFrom, TryInto};
-
-/// Acknowledgement to subscribe
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SubAck {
-    pub pkid: u16,
-    pub return_codes: Vec<SubscribeReasonCode>,
-    pub properties: Option<SubAckProperties>,
-}
-
-impl SubAck {
-    pub fn new(pkid: u16, return_codes: Vec<SubscribeReasonCode>) -> SubAck {
-        SubAck {
-            pkid,
-            return_codes,
-            properties: None,
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        let mut len = 2 + self.return_codes.len();
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            // just 1 byte representing 0 len
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-
-        let pkid = read_u16(&mut bytes)?;
-        let properties = SubAckProperties::extract(&mut bytes)?;
-
-        if !bytes.has_remaining() {
-            return Err(Error::MalformedPacket);
-        }
-
-        let mut return_codes = Vec::new();
-        while bytes.has_remaining() {
-            let return_code = read_u8(&mut bytes)?;
-            return_codes.push(return_code.try_into()?);
-        }
-
-        let suback = SubAck {
-            pkid,
-            return_codes,
-            properties,
-        };
-
-        Ok(suback)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        buffer.put_u8(0x90);
-        let remaining_len = self.len();
-        let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
-
-        buffer.put_u16(self.pkid);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        let p: Vec<u8> = self.return_codes.iter().map(|code| *code as u8).collect();
-        buffer.extend_from_slice(&p);
-        Ok(1 + remaining_len_bytes + remaining_len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SubAckProperties {
-    pub reason_string: Option<String>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl SubAckProperties {
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(reason) = &self.reason_string {
-            len += 1 + 2 + reason.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<SubAckProperties>, Error> {
-        let mut reason_string = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reason.len();
-                    reason_string = Some(reason);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(SubAckProperties {
-            reason_string,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(reason) = &self.reason_string {
-            buffer.put_u8(PropertyType::ReasonString as u8);
-            write_mqtt_string(buffer, reason);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-
-#[repr(u8)]
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum SubscribeReasonCode {
-    QoS0 = 0,
-    QoS1 = 1,
-    QoS2 = 2,
-    Unspecified = 128,
-    ImplementationSpecific = 131,
-    NotAuthorized = 135,
-    TopicFilterInvalid = 143,
-    PkidInUse = 145,
-    QuotaExceeded = 151,
-    SharedSubscriptionsNotSupported = 158,
-    SubscriptionIdNotSupported = 161,
-    WildcardSubscriptionsNotSupported = 162,
-}
-
-impl TryFrom<u8> for SubscribeReasonCode {
-    type Error = super::Error;
-
-    fn try_from(value: u8) -> Result<Self, Self::Error> {
-        let v = match value {
-            0 => SubscribeReasonCode::QoS0,
-            1 => SubscribeReasonCode::QoS1,
-            2 => SubscribeReasonCode::QoS2,
-            128 => SubscribeReasonCode::Unspecified,
-            131 => SubscribeReasonCode::ImplementationSpecific,
-            135 => SubscribeReasonCode::NotAuthorized,
-            143 => SubscribeReasonCode::TopicFilterInvalid,
-            145 => SubscribeReasonCode::PkidInUse,
-            151 => SubscribeReasonCode::QuotaExceeded,
-            158 => SubscribeReasonCode::SharedSubscriptionsNotSupported,
-            161 => SubscribeReasonCode::SubscriptionIdNotSupported,
-            162 => SubscribeReasonCode::WildcardSubscriptionsNotSupported,
-            v => return Err(super::Error::InvalidSubscribeReasonCode(v)),
-        };
-
-        Ok(v)
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> SubAck {
-        let properties = SubAckProperties {
-            reason_string: Some("test".to_owned()),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        SubAck {
-            pkid: 42,
-            return_codes: vec![
-                SubscribeReasonCode::QoS0,
-                SubscribeReasonCode::QoS1,
-                SubscribeReasonCode::QoS2,
-                SubscribeReasonCode::Unspecified,
-            ],
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x90, // packet type
-            0x1b, // remaining len
-            0x00, 0x2a, // pkid
-            0x14, // properties len
-            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74,
-            0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // user properties
-            0x00, 0x01, 0x02, 0x80, // return codes
-        ]
-    }
-
-    #[test]
-    fn suback_parsing_works() {
-        let mut stream = BytesMut::new();
-        let packetstream = &sample_bytes();
-
-        stream.extend_from_slice(&packetstream[..]);
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let suback_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let suback = SubAck::read(fixed_header, suback_bytes).unwrap();
-        assert_eq!(suback, sample());
-    }
-
-    #[test]
-    fn suback_encoding_works() {
-        let publish = sample();
-        let mut buf = BytesMut::new();
-        publish.write(&mut buf).unwrap();
-
-        // println!("{:X?}", buf);
-        // println!("{:#04X?}", &buf[..]);
-        assert_eq!(&buf[..], sample_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/subscribe.rs b/rumqttc/src/v5/packet/subscribe.rs
deleted file mode 100644
index f3e5a01f1..000000000
--- a/rumqttc/src/v5/packet/subscribe.rs
+++ /dev/null
@@ -1,422 +0,0 @@
-use super::*;
-use bytes::{Buf, Bytes};
-use core::fmt;
-
-/// Subscription packet
-#[derive(Clone, PartialEq, Eq)]
-pub struct Subscribe {
-    pub pkid: u16,
-    pub filters: Vec<SubscribeFilter>,
-    pub properties: Option<SubscribeProperties>,
-}
-
-impl Subscribe {
-    pub fn new<S: Into<String>>(path: S, qos: QoS) -> Subscribe {
-        let filter = SubscribeFilter {
-            path: path.into(),
-            qos,
-            nolocal: false,
-            preserve_retain: false,
-            retain_forward_rule: RetainForwardRule::OnEverySubscribe,
-        };
-
-        Subscribe {
-            pkid: 0,
-            filters: vec![filter],
-            properties: None,
-        }
-    }
-
-    pub fn new_many<T>(topics: T) -> Result<Subscribe, Error>
-    where
-        T: IntoIterator<Item = SubscribeFilter>,
-    {
-        let filters: Vec<SubscribeFilter> = topics.into_iter().collect();
-
-        match filters.len() {
-            0 => Err(Error::EmptySubscription),
-            _ => Ok(Subscribe {
-                pkid: 0,
-                filters,
-                properties: None,
-            }),
-        }
-    }
-
-    pub fn add(&mut self, path: String, qos: QoS) -> &mut Self {
-        let filter = SubscribeFilter {
-            path,
-            qos,
-            nolocal: false,
-            preserve_retain: false,
-            retain_forward_rule: RetainForwardRule::OnEverySubscribe,
-        };
-
-        self.filters.push(filter);
-        self
-    }
-
-    pub fn len(&self) -> usize {
-        let mut len = 2 + self.filters.iter().fold(0, |s, t| s + t.len());
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            // just 1 byte representing 0 len
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-
-        let pkid = read_u16(&mut bytes)?;
-        let properties = SubscribeProperties::extract(&mut bytes)?;
-
-        // variable header size = 2 (packet identifier)
-        let mut filters = Vec::new();
-
-        while bytes.has_remaining() {
-            let path = read_mqtt_string(&mut bytes)?;
-            let options = read_u8(&mut bytes)?;
-            let requested_qos = options & 0b0000_0011;
-
-            let nolocal = options >> 2 & 0b0000_0001;
-            let nolocal = nolocal != 0;
-
-            let preserve_retain = options >> 3 & 0b0000_0001;
-            let preserve_retain = preserve_retain != 0;
-
-            let retain_forward_rule = (options >> 4) & 0b0000_0011;
-            let retain_forward_rule = match retain_forward_rule {
-                0 => RetainForwardRule::OnEverySubscribe,
-                1 => RetainForwardRule::OnNewSubscribe,
-                2 => RetainForwardRule::Never,
-                r => return Err(Error::InvalidRetainForwardRule(r)),
-            };
-
-            filters.push(SubscribeFilter {
-                path,
-                qos: qos(requested_qos)?,
-                nolocal,
-                preserve_retain,
-                retain_forward_rule,
-            });
-        }
-
-        match filters.len() {
-            0 => Err(Error::EmptySubscription),
-            _ => Ok(Subscribe {
-                pkid,
-                filters,
-                properties,
-            }),
-        }
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        // write packet type
-        buffer.put_u8(0x82);
-
-        // write remaining length
-        let remaining_len = self.len();
-        let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
-
-        // write packet id
-        buffer.put_u16(self.pkid);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        // write filters
-        for filter in self.filters.iter() {
-            filter.write(buffer);
-        }
-
-        Ok(1 + remaining_len_bytes + remaining_len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SubscribeProperties {
-    pub id: Option<usize>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl SubscribeProperties {
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(id) = &self.id {
-            len += 1 + len_len(*id);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<SubscribeProperties>, Error> {
-        let mut id = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::SubscriptionIdentifier => {
-                    let (id_len, sub_id) = length(bytes.iter())?;
-                    // TODO: Validate 1 +. Tests are working either way
-                    cursor += 1 + id_len;
-                    bytes.advance(id_len);
-                    id = Some(sub_id);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(SubscribeProperties {
-            id,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(id) = &self.id {
-            buffer.put_u8(PropertyType::SubscriptionIdentifier as u8);
-            write_remaining_length(buffer, *id)?;
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-
-///  Subscription filter
-#[derive(Clone, PartialEq, Eq)]
-pub struct SubscribeFilter {
-    pub path: String,
-    pub qos: QoS,
-    pub nolocal: bool,
-    pub preserve_retain: bool,
-    pub retain_forward_rule: RetainForwardRule,
-}
-
-impl SubscribeFilter {
-    pub fn new(path: String, qos: QoS) -> SubscribeFilter {
-        SubscribeFilter {
-            path,
-            qos,
-            nolocal: false,
-            preserve_retain: false,
-            retain_forward_rule: RetainForwardRule::OnEverySubscribe,
-        }
-    }
-
-    pub fn set_nolocal(&mut self, flag: bool) -> &mut Self {
-        self.nolocal = flag;
-        self
-    }
-
-    pub fn set_preserve_retain(&mut self, flag: bool) -> &mut Self {
-        self.preserve_retain = flag;
-        self
-    }
-
-    pub fn set_retain_forward_rule(&mut self, rule: RetainForwardRule) -> &mut Self {
-        self.retain_forward_rule = rule;
-        self
-    }
-
-    pub fn len(&self) -> usize {
-        // filter len + filter + options
-        2 + self.path.len() + 1
-    }
-
-    fn write(&self, buffer: &mut BytesMut) {
-        let mut options = 0;
-        options |= self.qos as u8;
-
-        if self.nolocal {
-            options |= 1 << 2;
-        }
-
-        if self.preserve_retain {
-            options |= 1 << 3;
-        }
-
-        match self.retain_forward_rule {
-            RetainForwardRule::OnEverySubscribe => options |= 0 << 4,
-            RetainForwardRule::OnNewSubscribe => options |= 1 << 4,
-            RetainForwardRule::Never => options |= 2 << 4,
-        }
-
-        write_mqtt_string(buffer, self.path.as_str());
-        buffer.put_u8(options);
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum RetainForwardRule {
-    OnEverySubscribe,
-    OnNewSubscribe,
-    Never,
-}
-
-impl fmt::Debug for Subscribe {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Filters = {:?}, Packet id = {:?}",
-            self.filters, self.pkid
-        )
-    }
-}
-
-impl fmt::Debug for SubscribeFilter {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Filter = {}, Qos = {:?}, Nolocal = {}, Preserve retain = {}, Forward rule = {:?}",
-            self.path, self.qos, self.nolocal, self.preserve_retain, self.retain_forward_rule
-        )
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> Subscribe {
-        let subscribe_properties = SubscribeProperties {
-            id: Some(100),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        let mut filter = SubscribeFilter::new("hello".to_owned(), QoS::AtLeastOnce);
-        filter
-            .set_nolocal(true)
-            .set_preserve_retain(true)
-            .set_retain_forward_rule(RetainForwardRule::Never);
-
-        Subscribe {
-            pkid: 42,
-            filters: vec![filter],
-            properties: Some(subscribe_properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0x82, // packet type
-            0x1a, // remaining length
-            0x00, 0x2a, // pkid
-            0x0f, // properties len
-            0x0b, 0x64, // subscription identifier
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-            0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, // filter
-            0x2d, // options
-        ]
-    }
-
-    #[test]
-    fn subscribe_parsing_works() {
-        let mut stream = BytesMut::new();
-        let packetstream = &sample_bytes();
-
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let subscribe_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let subscribe = Subscribe::read(fixed_header, subscribe_bytes).unwrap();
-        assert_eq!(subscribe, sample());
-    }
-
-    #[test]
-    fn subscribe_encoding_works() {
-        let publish = sample();
-        let mut buf = BytesMut::new();
-        publish.write(&mut buf).unwrap();
-
-        // println!("{:X?}", buf);
-        // println!("{:#04X?}", &buf[..]);
-        assert_eq!(&buf[..], sample_bytes());
-    }
-
-    fn sample2() -> Subscribe {
-        let filter = SubscribeFilter::new("hello/world".to_owned(), QoS::AtLeastOnce);
-        Subscribe {
-            pkid: 42,
-            filters: vec![filter],
-            properties: None,
-        }
-    }
-
-    fn sample2_bytes() -> Vec<u8> {
-        vec![
-            0x82, 0x11, 0x00, 0x2a, 0x00, 0x00, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2f, 0x77,
-            0x6f, 0x72, 0x6c, 0x64, 0x01,
-        ]
-    }
-
-    #[test]
-    fn subscribe2_parsing_works() {
-        let mut stream = BytesMut::new();
-        let packetstream = &sample2_bytes();
-
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let subscribe_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let subscribe = Subscribe::read(fixed_header, subscribe_bytes).unwrap();
-        assert_eq!(subscribe, sample2());
-    }
-
-    #[test]
-    fn subscribe2_encoding_works() {
-        let publish = sample2();
-        let mut buf = BytesMut::new();
-        publish.write(&mut buf).unwrap();
-
-        // println!("{:X?}", buf);
-        // println!("{:#04X?}", &buf[..]);
-        assert_eq!(&buf[..], sample2_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/unsuback.rs b/rumqttc/src/v5/packet/unsuback.rs
deleted file mode 100644
index ae2aa88d5..000000000
--- a/rumqttc/src/v5/packet/unsuback.rs
+++ /dev/null
@@ -1,245 +0,0 @@
-use super::*;
-use bytes::{Buf, BufMut, Bytes, BytesMut};
-
-//// Return code in connack
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[repr(u8)]
-pub enum UnsubAckReason {
-    Success = 0x00,
-    NoSubscriptionExisted = 0x11,
-    UnspecifiedError = 0x80,
-    ImplementationSpecificError = 0x83,
-    NotAuthorized = 0x87,
-    TopicFilterInvalid = 0x8F,
-    PacketIdentifierInUse = 0x91,
-}
-
-/// Acknowledgement to unsubscribe
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct UnsubAck {
-    pub pkid: u16,
-    pub reasons: Vec<UnsubAckReason>,
-    pub properties: Option<UnsubAckProperties>,
-}
-
-impl UnsubAck {
-    pub fn new(pkid: u16) -> UnsubAck {
-        UnsubAck {
-            pkid,
-            reasons: Vec::new(),
-            properties: None,
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        let mut len = 2 + self.reasons.len();
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            // just 1 byte representing 0 len
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-
-        let pkid = read_u16(&mut bytes)?;
-        let properties = UnsubAckProperties::extract(&mut bytes)?;
-
-        if !bytes.has_remaining() {
-            return Err(Error::MalformedPacket);
-        }
-
-        let mut reasons = Vec::new();
-        while bytes.has_remaining() {
-            let r = read_u8(&mut bytes)?;
-            reasons.push(reason(r)?);
-        }
-
-        let unsuback = UnsubAck {
-            pkid,
-            reasons,
-            properties,
-        };
-
-        Ok(unsuback)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        buffer.put_u8(0xB0);
-        let remaining_len = self.len();
-        let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
-
-        buffer.put_u16(self.pkid);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        let p: Vec<u8> = self.reasons.iter().map(|code| *code as u8).collect();
-        buffer.extend_from_slice(&p);
-        Ok(1 + remaining_len_bytes + remaining_len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct UnsubAckProperties {
-    pub reason_string: Option<String>,
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl UnsubAckProperties {
-    pub fn len(&self) -> usize {
-        let mut len = 0;
-
-        if let Some(reason) = &self.reason_string {
-            len += 1 + 2 + reason.len();
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    pub fn extract(mut bytes: &mut Bytes) -> Result<Option<UnsubAckProperties>, Error> {
-        let mut reason_string = None;
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::ReasonString => {
-                    let reason = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + reason.len();
-                    reason_string = Some(reason);
-                }
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(UnsubAckProperties {
-            reason_string,
-            user_properties,
-        }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        if let Some(reason) = &self.reason_string {
-            buffer.put_u8(PropertyType::ReasonString as u8);
-            write_mqtt_string(buffer, reason);
-        }
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-
-/// Connection return code type
-fn reason(num: u8) -> Result<UnsubAckReason, Error> {
-    let code = match num {
-        0x00 => UnsubAckReason::Success,
-        0x11 => UnsubAckReason::NoSubscriptionExisted,
-        0x80 => UnsubAckReason::UnspecifiedError,
-        0x83 => UnsubAckReason::ImplementationSpecificError,
-        0x87 => UnsubAckReason::NotAuthorized,
-        0x8F => UnsubAckReason::TopicFilterInvalid,
-        0x91 => UnsubAckReason::PacketIdentifierInUse,
-        num => return Err(Error::InvalidSubscribeReasonCode(num)),
-    };
-
-    Ok(code)
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> UnsubAck {
-        let properties = UnsubAckProperties {
-            reason_string: Some("test".to_owned()),
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        UnsubAck {
-            pkid: 10,
-            reasons: vec![
-                UnsubAckReason::NotAuthorized,
-                UnsubAckReason::TopicFilterInvalid,
-            ],
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0xb0, // packet type
-            0x19, // remaining len
-            0x00, 0x0a, // pkid
-            0x14, // properties len
-            0x1f, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, // reason string
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-            0x87, 0x8f, // reasons
-        ]
-    }
-
-    #[test]
-    fn unsuback_parsing_works() {
-        let mut stream = BytesMut::new();
-        let packetstream = &sample_bytes();
-
-        stream.extend_from_slice(&packetstream[..]);
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let unsuback_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let unsuback = UnsubAck::read(fixed_header, unsuback_bytes).unwrap();
-        assert_eq!(unsuback, sample());
-    }
-
-    #[test]
-    fn unsuback_encoding_works() {
-        let publish = sample();
-        let mut buf = BytesMut::new();
-        publish.write(&mut buf).unwrap();
-
-        // println!("{:X?}", buf);
-        // println!("{:#04X?}", &buf[..]);
-        assert_eq!(&buf[..], sample_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/packet/unsubscribe.rs b/rumqttc/src/v5/packet/unsubscribe.rs
deleted file mode 100644
index c43d1aa56..000000000
--- a/rumqttc/src/v5/packet/unsubscribe.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-use super::*;
-use bytes::{Buf, Bytes};
-
-/// Unsubscribe packet
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Unsubscribe {
-    pub pkid: u16,
-    pub filters: Vec<String>,
-    pub properties: Option<UnsubscribeProperties>,
-}
-
-impl Unsubscribe {
-    pub fn new<S: Into<String>>(topic: S) -> Unsubscribe {
-        Unsubscribe {
-            pkid: 0,
-            filters: vec![topic.into()],
-            properties: None,
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        // Packet id + length of filters (unlike subscribe, this just a string.
-        // Hence 2 is prefixed for len per filter)
-        let mut len = 2 + self.filters.iter().fold(0, |s, t| 2 + s + t.len());
-
-        if let Some(properties) = &self.properties {
-            let properties_len = properties.len();
-            let properties_len_len = len_len(properties_len);
-            len += properties_len_len + properties_len;
-        } else {
-            // just 1 byte representing 0 len
-            len += 1;
-        }
-
-        len
-    }
-
-    pub fn read(fixed_header: FixedHeader, mut bytes: Bytes) -> Result<Self, Error> {
-        let variable_header_index = fixed_header.fixed_header_len;
-        bytes.advance(variable_header_index);
-
-        let pkid = read_u16(&mut bytes)?;
-        dbg!(pkid);
-        let properties = UnsubscribeProperties::extract(&mut bytes)?;
-
-        let mut filters = Vec::with_capacity(1);
-        while bytes.has_remaining() {
-            let filter = read_mqtt_string(&mut bytes)?;
-            filters.push(filter);
-        }
-
-        let unsubscribe = Unsubscribe {
-            pkid,
-            filters,
-            properties,
-        };
-        Ok(unsubscribe)
-    }
-
-    pub fn write(&self, buffer: &mut BytesMut) -> Result<usize, Error> {
-        buffer.put_u8(0xA2);
-
-        // write remaining length
-        let remaining_len = self.len();
-        let remaining_len_bytes = write_remaining_length(buffer, remaining_len)?;
-
-        // write packet id
-        buffer.put_u16(self.pkid);
-
-        if let Some(properties) = &self.properties {
-            properties.write(buffer)?;
-        } else {
-            write_remaining_length(buffer, 0)?;
-        }
-
-        // write filters
-        for filter in self.filters.iter() {
-            write_mqtt_string(buffer, filter);
-        }
-
-        Ok(1 + remaining_len_bytes + remaining_len)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct UnsubscribeProperties {
-    pub user_properties: Vec<(String, String)>,
-}
-
-impl UnsubscribeProperties {
-    fn len(&self) -> usize {
-        let mut len = 0;
-
-        for (key, value) in self.user_properties.iter() {
-            len += 1 + 2 + key.len() + 2 + value.len();
-        }
-
-        len
-    }
-
-    fn extract(mut bytes: &mut Bytes) -> Result<Option<UnsubscribeProperties>, Error> {
-        let mut user_properties = Vec::new();
-
-        let (properties_len_len, properties_len) = length(bytes.iter())?;
-        bytes.advance(properties_len_len);
-
-        if properties_len == 0 {
-            return Ok(None);
-        }
-
-        let mut cursor = 0;
-        // read until cursor reaches property length. properties_len = 0 will skip this loop
-        while cursor < properties_len {
-            let prop = read_u8(&mut bytes)?;
-            cursor += 1;
-
-            match property(prop)? {
-                PropertyType::UserProperty => {
-                    let key = read_mqtt_string(&mut bytes)?;
-                    let value = read_mqtt_string(&mut bytes)?;
-                    cursor += 2 + key.len() + 2 + value.len();
-                    user_properties.push((key, value));
-                }
-                _ => return Err(Error::InvalidPropertyType(prop)),
-            }
-        }
-
-        Ok(Some(UnsubscribeProperties { user_properties }))
-    }
-
-    fn write(&self, buffer: &mut BytesMut) -> Result<(), Error> {
-        let len = self.len();
-        write_remaining_length(buffer, len)?;
-
-        for (key, value) in self.user_properties.iter() {
-            buffer.put_u8(PropertyType::UserProperty as u8);
-            write_mqtt_string(buffer, key);
-            write_mqtt_string(buffer, value);
-        }
-
-        Ok(())
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use bytes::BytesMut;
-    use pretty_assertions::assert_eq;
-
-    fn sample() -> Unsubscribe {
-        let properties = UnsubscribeProperties {
-            user_properties: vec![("test".to_owned(), "test".to_owned())],
-        };
-
-        Unsubscribe {
-            pkid: 10,
-            filters: vec!["hello".to_owned(), "world".to_owned()],
-            properties: Some(properties),
-        }
-    }
-
-    fn sample_bytes() -> Vec<u8> {
-        vec![
-            0xa2, // packet type
-            0x1e, // remaining len
-            0x00, 0x0a, // pkid
-            0x0d, // properties len
-            0x26, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x04, 0x74, 0x65, 0x73,
-            0x74, // user properties
-            0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, // filter 1
-            0x00, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, // filter 2
-        ]
-    }
-
-    #[test]
-    fn unsubscribe_parsing_works() {
-        let mut stream = BytesMut::new();
-        let packetstream = &sample_bytes();
-
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let unsubscribe_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let unsubscribe = Unsubscribe::read(fixed_header, unsubscribe_bytes).unwrap();
-        assert_eq!(unsubscribe, sample());
-    }
-
-    #[test]
-    fn subscribe_encoding_works() {
-        let publish = sample();
-        let mut buf = BytesMut::new();
-        publish.write(&mut buf).unwrap();
-
-        println!("{:X?}", buf);
-        println!("{:#04X?}", &buf[..]);
-        assert_eq!(&buf[..], sample_bytes());
-    }
-
-    fn sample2() -> Unsubscribe {
-        Unsubscribe {
-            pkid: 10,
-            filters: vec!["hello".to_owned()],
-            properties: None,
-        }
-    }
-
-    fn sample2_bytes() -> Vec<u8> {
-        vec![
-            0xa2, 0x0a, 0x00, 0x0a, 0x00, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
-        ]
-    }
-
-    #[test]
-    fn subscribe2_parsing_works() {
-        let mut stream = BytesMut::new();
-        let packetstream = &sample2_bytes();
-
-        stream.extend_from_slice(&packetstream[..]);
-
-        let fixed_header = parse_fixed_header(stream.iter()).unwrap();
-        let subscribe_bytes = stream.split_to(fixed_header.frame_length()).freeze();
-        let subscribe = Unsubscribe::read(fixed_header, subscribe_bytes).unwrap();
-        assert_eq!(subscribe, sample2());
-    }
-
-    #[test]
-    fn subscribe2_encoding_works() {
-        let publish = sample2();
-        let mut buf = BytesMut::new();
-        publish.write(&mut buf).unwrap();
-
-        // println!("{:X?}", buf);
-        // println!("{:#04X?}", &buf[..]);
-        assert_eq!(&buf[..], sample2_bytes());
-    }
-}
diff --git a/rumqttc/src/v5/state.rs b/rumqttc/src/v5/state.rs
index b4ff64154..919362ea8 100644
--- a/rumqttc/src/v5/state.rs
+++ b/rumqttc/src/v5/state.rs
@@ -1,28 +1,29 @@
-use std::{
-    collections::VecDeque,
-    io, mem,
-    sync::{Arc, Mutex, RwLock},
-    time::Instant,
+use crate::v5::mqttbytes::PingReq;
+
+use super::mqttbytes::v5::Packet;
+use super::mqttbytes::{
+    self, ConnectReturnCode, PubAck, PubComp, PubRec, PubRel, Publish, QoS, Subscribe, Unsubscribe,
 };
+use super::{Event, Incoming, Outgoing, Request};
 
 use bytes::BytesMut;
-
-use crate::v5::{outgoing_buf::OutgoingBuf, packet::*, Incoming, Request};
+use std::collections::VecDeque;
+use std::{io, time::Instant};
 
 /// Errors during state handling
 #[derive(Debug, thiserror::Error)]
 pub enum StateError {
     /// Io Error while state is passed to network
-    #[error("Io error {0:?}")]
+    #[error("Io error: {0:?}")]
     Io(#[from] io::Error),
     /// Broker's error reply to client's connect packet
-    #[error("Connect return code `{0:?}`")]
+    #[error("Connect return code: `{0:?}`")]
     Connect(ConnectReturnCode),
     /// Invalid state for a given operation
     #[error("Invalid state for a given operation")]
     InvalidState,
     /// Received a packet (ack) which isn't asked for
-    #[error("Received unsolicited ack pkid {0}")]
+    #[error("Received unsolicited ack pkid: {0}")]
     Unsolicited(u16),
     /// Last pingreq isn't acked
     #[error("Last pingreq isn't acked")]
@@ -32,16 +33,10 @@ pub enum StateError {
     WrongPacket,
     #[error("Timeout while waiting to resolve collision")]
     CollisionTimeout,
-    #[error("Mqtt serialization/deserialization error")]
-    Deserialization(Error),
-    #[error("Couldn't get write lock")]
-    WriteLock,
-}
-
-impl From<Error> for StateError {
-    fn from(e: Error) -> StateError {
-        StateError::Deserialization(e)
-    }
+    #[error("A Subscribe packet must contain atleast one filter")]
+    EmptySubscription,
+    #[error("Mqtt serialization/deserialization error: {0}")]
+    Deserialization(#[from] mqttbytes::Error),
 }
 
 /// State of the mqtt connection.
@@ -62,8 +57,12 @@ pub struct MqttState {
     last_incoming: Instant,
     /// Last outgoing packet time
     last_outgoing: Instant,
+    /// Packet id of the last outgoing packet
+    pub(crate) last_pkid: u16,
     /// Number of outgoing inflight publishes
     pub(crate) inflight: u16,
+    /// Maximum number of allowed inflight
+    pub(crate) max_inflight: u16,
     /// Outgoing QoS 1, 2 publishes which aren't acked yet
     pub(crate) outgoing_pub: Vec<Option<Publish>>,
     /// Packet ids of released QoS 2 publishes
@@ -72,37 +71,36 @@ pub struct MqttState {
     pub(crate) incoming_pub: Vec<Option<u16>>,
     /// Last collision due to broker not acking in order
     pub collision: Option<Publish>,
+    /// Buffered incoming packets
+    pub events: VecDeque<Event>,
     /// Write buffer
     pub write: BytesMut,
     /// Indicates if acknowledgements should be send immediately
     pub manual_acks: bool,
-    pub(crate) incoming_buf: Arc<Mutex<VecDeque<Incoming>>>,
-    pub(crate) outgoing_buf: Arc<Mutex<OutgoingBuf>>,
-    pub(crate) disconnected: Arc<RwLock<bool>>,
 }
 
 impl MqttState {
     /// Creates new mqtt state. Same state should be used during a
     /// connection for persistent sessions while new state should
     /// instantiated for clean sessions
-    pub fn new(max_inflight: u16, manual_acks: bool, cap: usize) -> Self {
+    pub fn new(max_inflight: u16, manual_acks: bool) -> Self {
         MqttState {
             await_pingresp: false,
             collision_ping_count: 0,
             last_incoming: Instant::now(),
             last_outgoing: Instant::now(),
+            last_pkid: 0,
             inflight: 0,
+            max_inflight,
             // index 0 is wasted as 0 is not a valid packet id
             outgoing_pub: vec![None; max_inflight as usize + 1],
             outgoing_rel: vec![None; max_inflight as usize + 1],
             incoming_pub: vec![None; std::u16::MAX as usize + 1],
             collision: None,
             // TODO: Optimize these sizes later
+            events: VecDeque::with_capacity(100),
             write: BytesMut::with_capacity(10 * 1024),
             manual_acks,
-            incoming_buf: Arc::new(Mutex::new(VecDeque::with_capacity(cap))),
-            outgoing_buf: OutgoingBuf::new(max_inflight as usize),
-            disconnected: Arc::new(RwLock::new(false)),
         }
     }
 
@@ -136,21 +134,10 @@ impl MqttState {
         pending
     }
 
-    #[inline]
     pub fn inflight(&self) -> u16 {
         self.inflight
     }
 
-    #[inline]
-    pub fn cur_pkid(&self) -> u16 {
-        self.outgoing_buf.lock().unwrap().pkid_counter
-    }
-
-    #[inline]
-    pub fn is_disconnected(&self) -> bool {
-        *self.disconnected.read().unwrap()
-    }
-
     /// Consolidates handling of all outgoing mqtt packet logic. Returns a packet which should
     /// be put on to the network by the eventloop
     pub fn handle_outgoing_packet(&mut self, request: Request) -> Result<(), StateError> {
@@ -176,15 +163,14 @@ impl MqttState {
     /// be forwarded to user and Pubck packet will be written to network
     pub fn handle_incoming_packet(&mut self, packet: Incoming) -> Result<(), StateError> {
         let out = match &packet {
-            Incoming::PingResp => self.handle_incoming_pingresp(),
-            Incoming::Publish(publish) => self.handle_incoming_publish(publish),
-            Incoming::SubAck(_suback) => self.handle_incoming_suback(),
-            Incoming::UnsubAck(_unsuback) => self.handle_incoming_unsuback(),
-            Incoming::PubAck(puback) => self.handle_incoming_puback(puback),
-            Incoming::PubRec(pubrec) => self.handle_incoming_pubrec(pubrec),
-            Incoming::PubRel(pubrel) => self.handle_incoming_pubrel(pubrel),
-            Incoming::PubComp(pubcomp) => self.handle_incoming_pubcomp(pubcomp),
-            Incoming::ConnAck(_) => Ok(()),
+            Incoming::PingResp(_) => self.handle_incoming_pingresp(),
+            Incoming::Publish(publish, _) => self.handle_incoming_publish(publish),
+            Incoming::SubAck(..) => self.handle_incoming_suback(),
+            Incoming::UnsubAck(_) => self.handle_incoming_unsuback(),
+            Incoming::PubAck(puback, _) => self.handle_incoming_puback(puback),
+            Incoming::PubRec(pubrec, _) => self.handle_incoming_pubrec(pubrec),
+            Incoming::PubRel(pubrel, _) => self.handle_incoming_pubrel(pubrel),
+            Incoming::PubComp(pubcomp, _) => self.handle_incoming_pubcomp(pubcomp),
             _ => {
                 error!("Invalid incoming packet = {:?}", packet);
                 return Err(StateError::WrongPacket);
@@ -192,17 +178,15 @@ impl MqttState {
         };
 
         out?;
-        self.incoming_buf.lock().unwrap().push_back(packet);
+        self.events.push_back(Event::Incoming(Box::new(packet)));
         self.last_incoming = Instant::now();
         Ok(())
     }
 
-    #[inline]
     fn handle_incoming_suback(&mut self) -> Result<(), StateError> {
         Ok(())
     }
 
-    #[inline]
     fn handle_incoming_unsuback(&mut self) -> Result<(), StateError> {
         Ok(())
     }
@@ -210,32 +194,36 @@ impl MqttState {
     /// Results in a publish notification in all the QoS cases. Replys with an ack
     /// in case of QoS1 and Replys rec in case of QoS while also storing the message
     fn handle_incoming_publish(&mut self, publish: &Publish) -> Result<(), StateError> {
-        match publish.qos {
-            QoS::AtLeastOnce if !self.manual_acks => {
-                let puback = PubAck::new(publish.pkid);
-                self.outgoing_puback(puback)?
+        let qos = publish.qos;
+
+        match qos {
+            QoS::AtMostOnce => Ok(()),
+            QoS::AtLeastOnce => {
+                if !self.manual_acks {
+                    let puback = PubAck::new(publish.pkid);
+                    self.outgoing_puback(puback)?;
+                }
+                Ok(())
             }
             QoS::ExactlyOnce => {
                 let pkid = publish.pkid;
-                let incoming = self
-                    .incoming_pub
-                    .get_mut(pkid as usize)
-                    .ok_or(StateError::Unsolicited(pkid))?;
-                *incoming = Some(pkid);
+                self.incoming_pub[pkid as usize] = Some(pkid);
 
                 if !self.manual_acks {
                     let pubrec = PubRec::new(pkid);
                     self.outgoing_pubrec(pubrec)?;
                 }
+                Ok(())
             }
-            _ => {}
         }
-
-        Ok(())
     }
 
     fn handle_incoming_puback(&mut self, puback: &PubAck) -> Result<(), StateError> {
-        let v = match mem::replace(&mut self.outgoing_pub[puback.pkid as usize], None) {
+        let publish = self
+            .outgoing_pub
+            .get_mut(puback.pkid as usize)
+            .ok_or(StateError::Unsolicited(puback.pkid))?;
+        let v = match publish.take() {
             Some(_) => {
                 self.inflight -= 1;
                 Ok(())
@@ -250,7 +238,10 @@ impl MqttState {
             self.outgoing_pub[publish.pkid as usize] = Some(publish.clone());
             self.inflight += 1;
 
-            publish.write(&mut self.write)?;
+            let pkid = publish.pkid;
+            Packet::Publish(publish, None).write(&mut self.write)?;
+            let event = Event::Outgoing(Outgoing::Publish(pkid));
+            self.events.push_back(event);
             self.collision_ping_count = 0;
         }
 
@@ -258,11 +249,18 @@ impl MqttState {
     }
 
     fn handle_incoming_pubrec(&mut self, pubrec: &PubRec) -> Result<(), StateError> {
-        match mem::replace(&mut self.outgoing_pub[pubrec.pkid as usize], None) {
+        let publish = self
+            .outgoing_pub
+            .get_mut(pubrec.pkid as usize)
+            .ok_or(StateError::Unsolicited(pubrec.pkid))?;
+        match publish.take() {
             Some(_) => {
                 // NOTE: Inflight - 1 for qos2 in comp
                 self.outgoing_rel[pubrec.pkid as usize] = Some(pubrec.pkid);
-                PubRel::new(pubrec.pkid).write(&mut self.write)?;
+                Packet::PubRel(PubRel::new(pubrec.pkid), None).write(&mut self.write)?;
+
+                let event = Event::Outgoing(Outgoing::PubRel(pubrec.pkid));
+                self.events.push_back(event);
                 Ok(())
             }
             None => {
@@ -273,9 +271,15 @@ impl MqttState {
     }
 
     fn handle_incoming_pubrel(&mut self, pubrel: &PubRel) -> Result<(), StateError> {
-        match mem::replace(&mut self.incoming_pub[pubrel.pkid as usize], None) {
+        let publish = self
+            .incoming_pub
+            .get_mut(pubrel.pkid as usize)
+            .ok_or(StateError::Unsolicited(pubrel.pkid))?;
+        match publish.take() {
             Some(_) => {
-                PubComp::new(pubrel.pkid).write(&mut self.write)?;
+                Packet::PubComp(PubComp::new(pubrel.pkid), None).write(&mut self.write)?;
+                let event = Event::Outgoing(Outgoing::PubComp(pubrel.pkid));
+                self.events.push_back(event);
                 Ok(())
             }
             None => {
@@ -287,11 +291,18 @@ impl MqttState {
 
     fn handle_incoming_pubcomp(&mut self, pubcomp: &PubComp) -> Result<(), StateError> {
         if let Some(publish) = self.check_collision(pubcomp.pkid) {
-            publish.write(&mut self.write)?;
+            let pkid = publish.pkid;
+            Packet::Publish(publish, None).write(&mut self.write)?;
+            let event = Event::Outgoing(Outgoing::Publish(pkid));
+            self.events.push_back(event);
             self.collision_ping_count = 0;
         }
 
-        match mem::replace(&mut self.outgoing_rel[pubcomp.pkid as usize], None) {
+        let pubrel = self
+            .outgoing_rel
+            .get_mut(pubcomp.pkid as usize)
+            .ok_or(StateError::Unsolicited(pubcomp.pkid))?;
+        match pubrel.take() {
             Some(_) => {
                 self.inflight -= 1;
                 Ok(())
@@ -303,7 +314,6 @@ impl MqttState {
         }
     }
 
-    #[inline]
     fn handle_incoming_pingresp(&mut self) -> Result<(), StateError> {
         self.await_pingresp = false;
         Ok(())
@@ -311,18 +321,23 @@ impl MqttState {
 
     /// Adds next packet identifier to QoS 1 and 2 publish packets and returns
     /// it buy wrapping publish in packet
-    fn outgoing_publish(&mut self, publish: Publish) -> Result<(), StateError> {
+    fn outgoing_publish(&mut self, mut publish: Publish) -> Result<(), StateError> {
         if publish.qos != QoS::AtMostOnce {
-            // client should set proper pkid
+            if publish.pkid == 0 {
+                publish.pkid = self.next_pkid();
+            }
+
             let pkid = publish.pkid;
             if self
                 .outgoing_pub
                 .get(publish.pkid as usize)
-                .unwrap()
+                .ok_or(StateError::Unsolicited(publish.pkid))?
                 .is_some()
             {
                 info!("Collision on packet id = {:?}", publish.pkid);
                 self.collision = Some(publish);
+                let event = Event::Outgoing(Outgoing::AwaitAck(pkid));
+                self.events.push_back(event);
                 return Ok(());
             }
 
@@ -334,33 +349,42 @@ impl MqttState {
 
         debug!(
             "Publish. Topic = {}, Pkid = {:?}, Payload Size = {:?}",
-            publish.topic,
+            String::from_utf8(publish.topic.to_vec()).unwrap(),
             publish.pkid,
             publish.payload.len()
         );
 
-        publish.write(&mut self.write)?;
+        let pkid = publish.pkid;
+        Packet::Publish(publish, None).write(&mut self.write)?;
+        let event = Event::Outgoing(Outgoing::Publish(pkid));
+        self.events.push_back(event);
         Ok(())
     }
 
-    #[inline]
     fn outgoing_pubrel(&mut self, pubrel: PubRel) -> Result<(), StateError> {
         let pubrel = self.save_pubrel(pubrel)?;
 
         debug!("Pubrel. Pkid = {}", pubrel.pkid);
-        PubRel::new(pubrel.pkid).write(&mut self.write)?;
+        Packet::PubRel(PubRel::new(pubrel.pkid), None).write(&mut self.write)?;
+
+        let event = Event::Outgoing(Outgoing::PubRel(pubrel.pkid));
+        self.events.push_back(event);
         Ok(())
     }
 
-    #[inline]
     fn outgoing_puback(&mut self, puback: PubAck) -> Result<(), StateError> {
-        puback.write(&mut self.write)?;
+        let pkid = puback.pkid;
+        Packet::PubAck(puback, None).write(&mut self.write)?;
+        let event = Event::Outgoing(Outgoing::PubAck(pkid));
+        self.events.push_back(event);
         Ok(())
     }
 
-    #[inline]
     fn outgoing_pubrec(&mut self, pubrec: PubRec) -> Result<(), StateError> {
-        pubrec.write(&mut self.write)?;
+        let pkid = pubrec.pkid;
+        Packet::PubRec(pubrec, None).write(&mut self.write)?;
+        let event = Event::Outgoing(Outgoing::PubRec(pkid));
+        self.events.push_back(event);
         Ok(())
     }
 
@@ -393,48 +417,57 @@ impl MqttState {
             elapsed_out.as_millis()
         );
 
-        PingReq.write(&mut self.write)?;
+        Packet::PingReq(PingReq).write(&mut self.write)?;
+        let event = Event::Outgoing(Outgoing::PingReq);
+        self.events.push_back(event);
         Ok(())
     }
 
-    #[inline]
-    fn outgoing_subscribe(&mut self, subscription: Subscribe) -> Result<(), StateError> {
-        // client should set correct pkid
+    fn outgoing_subscribe(&mut self, mut subscription: Subscribe) -> Result<(), StateError> {
+        if subscription.filters.is_empty() {
+            return Err(StateError::EmptySubscription);
+        }
+
+        let pkid = self.next_pkid();
+        subscription.pkid = pkid;
+
         debug!(
             "Subscribe. Topics = {:?}, Pkid = {:?}",
             subscription.filters, subscription.pkid
         );
 
-        subscription.write(&mut self.write)?;
+        let pkid = subscription.pkid;
+        Packet::Subscribe(subscription, None).write(&mut self.write)?;
+        let event = Event::Outgoing(Outgoing::Subscribe(pkid));
+        self.events.push_back(event);
         Ok(())
     }
 
-    #[inline]
-    fn outgoing_unsubscribe(&mut self, unsub: Unsubscribe) -> Result<(), StateError> {
+    fn outgoing_unsubscribe(&mut self, mut unsub: Unsubscribe) -> Result<(), StateError> {
+        let pkid = self.next_pkid();
+        unsub.pkid = pkid;
+
         debug!(
             "Unsubscribe. Topics = {:?}, Pkid = {:?}",
             unsub.filters, unsub.pkid
         );
 
-        unsub.write(&mut self.write)?;
+        let pkid = unsub.pkid;
+        Packet::Unsubscribe(unsub).write(&mut self.write)?;
+        let event = Event::Outgoing(Outgoing::Unsubscribe(pkid));
+        self.events.push_back(event);
         Ok(())
     }
 
-    #[inline]
     fn outgoing_disconnect(&mut self) -> Result<(), StateError> {
         debug!("Disconnect");
 
-        let mut disconnected = self
-            .disconnected
-            .write()
-            .map_err(|_| StateError::WriteLock)?;
-        *disconnected = true;
-
-        Disconnect::new().write(&mut self.write)?;
+        Packet::Disconnect(Default::default()).write(&mut self.write)?;
+        let event = Event::Outgoing(Outgoing::Disconnect);
+        self.events.push_back(event);
         Ok(())
     }
 
-    #[inline]
     fn check_collision(&mut self, pkid: u16) -> Option<Publish> {
         if let Some(publish) = &self.collision {
             if publish.pkid == pkid {
@@ -445,42 +478,48 @@ impl MqttState {
         None
     }
 
-    #[inline]
-    fn save_pubrel(&mut self, pubrel: PubRel) -> Result<PubRel, StateError> {
-        // pubrel's pkid should already be set correct
+    fn save_pubrel(&mut self, mut pubrel: PubRel) -> Result<PubRel, StateError> {
+        let pubrel = match pubrel.pkid {
+            // consider PacketIdentifier(0) as uninitialized packets
+            0 => {
+                pubrel.pkid = self.next_pkid();
+                pubrel
+            }
+            _ => pubrel,
+        };
+
         self.outgoing_rel[pubrel.pkid as usize] = Some(pubrel.pkid);
+        self.inflight += 1;
         Ok(pubrel)
     }
 
-    #[inline]
-    pub fn increment_pkid(&self) -> u16 {
-        self.outgoing_buf.lock().unwrap().increment_pkid()
-    }
-
-    ///// http://stackoverflow.com/questions/11115364/mqtt-messageid-practical-implementation
-    ///// Packet ids are incremented till maximum set inflight messages and reset to 1 after that.
-    /////
-    //fn next_pkid(&mut self) -> u16 {
-    //    let next_pkid = self.last_pkid + 1;
+    /// http://stackoverflow.com/questions/11115364/mqtt-messageid-practical-implementation
+    /// Packet ids are incremented till maximum set inflight messages and reset to 1 after that.
+    ///
+    fn next_pkid(&mut self) -> u16 {
+        let next_pkid = self.last_pkid + 1;
 
-    //    // When next packet id is at the edge of inflight queue,
-    //    // set await flag. This instructs eventloop to stop
-    //    // processing requests until all the inflight publishes
-    //    // are acked
-    //    if next_pkid == self.max_inflight {
-    //        self.last_pkid = 0;
-    //        return next_pkid;
-    //    }
+        // When next packet id is at the edge of inflight queue,
+        // set await flag. This instructs eventloop to stop
+        // processing requests until all the inflight publishes
+        // are acked
+        if next_pkid == self.max_inflight {
+            self.last_pkid = 0;
+            return next_pkid;
+        }
 
-    //    self.last_pkid = next_pkid;
-    //    next_pkid
-    //}
+        self.last_pkid = next_pkid;
+        next_pkid
+    }
 }
 
 #[cfg(test)]
 mod test {
+    use crate::v5::mqttbytes::v5::Packet;
+
+    use super::mqttbytes::*;
+    use super::{Event, Incoming, Outgoing, Request};
     use super::{MqttState, StateError};
-    use crate::v5::{packet::*, Incoming, MqttOptions, Request};
 
     fn build_outgoing_publish(qos: QoS) -> Publish {
         let topic = "hello/world".to_owned();
@@ -502,15 +541,15 @@ mod test {
     }
 
     fn build_mqttstate() -> MqttState {
-        MqttState::new(100, false, 100)
+        MqttState::new(100, false)
     }
 
     #[test]
     fn next_pkid_increments_as_expected() {
-        let mqtt = build_mqttstate();
+        let mut mqtt = build_mqttstate();
 
         for i in 1..=100 {
-            let pkid = mqtt.increment_pkid();
+            let pkid = mqtt.next_pkid();
 
             // loops between 0-99. % 100 == 0 implies border
             let expected = i % 100;
@@ -527,46 +566,37 @@ mod test {
         let mut mqtt = build_mqttstate();
 
         // QoS0 Publish
-        let mut publish = build_outgoing_publish(QoS::AtMostOnce);
-        publish.pkid = 1;
+        let publish = build_outgoing_publish(QoS::AtMostOnce);
 
         // QoS 0 publish shouldn't be saved in queue
         mqtt.outgoing_publish(publish).unwrap();
-        assert_eq!(mqtt.cur_pkid(), 0);
+        assert_eq!(mqtt.last_pkid, 0);
         assert_eq!(mqtt.inflight, 0);
 
         // QoS1 Publish
-        let mut publish = build_outgoing_publish(QoS::AtLeastOnce);
-        publish.pkid = 2;
+        let publish = build_outgoing_publish(QoS::AtLeastOnce);
 
         // Packet id should be set and publish should be saved in queue
         mqtt.outgoing_publish(publish.clone()).unwrap();
-        // cur_pkid == 0 as there is no client to update it
-        assert_eq!(mqtt.cur_pkid(), 0);
+        assert_eq!(mqtt.last_pkid, 1);
         assert_eq!(mqtt.inflight, 1);
 
         // Packet id should be incremented and publish should be saved in queue
-        publish.pkid = 3;
         mqtt.outgoing_publish(publish).unwrap();
-        // cur_pkid == 0 as there is no client to update it
-        assert_eq!(mqtt.cur_pkid(), 0);
+        assert_eq!(mqtt.last_pkid, 2);
         assert_eq!(mqtt.inflight, 2);
 
         // QoS1 Publish
-        let mut publish = build_outgoing_publish(QoS::ExactlyOnce);
-        publish.pkid = 4;
+        let publish = build_outgoing_publish(QoS::ExactlyOnce);
 
         // Packet id should be set and publish should be saved in queue
         mqtt.outgoing_publish(publish.clone()).unwrap();
-        // cur_pkid == 0 as there is no client to update it
-        assert_eq!(mqtt.cur_pkid(), 0);
+        assert_eq!(mqtt.last_pkid, 3);
         assert_eq!(mqtt.inflight, 3);
 
-        publish.pkid = 5;
         // Packet id should be incremented and publish should be saved in queue
         mqtt.outgoing_publish(publish).unwrap();
-        // cur_pkid == 0 as there is no client to update it
-        assert_eq!(mqtt.cur_pkid(), 0);
+        assert_eq!(mqtt.last_pkid, 4);
         assert_eq!(mqtt.inflight, 4);
     }
 
@@ -601,6 +631,18 @@ mod test {
         mqtt.handle_incoming_publish(&publish1).unwrap();
         mqtt.handle_incoming_publish(&publish2).unwrap();
         mqtt.handle_incoming_publish(&publish3).unwrap();
+
+        if let Event::Outgoing(Outgoing::PubAck(pkid)) = mqtt.events[0] {
+            assert_eq!(pkid, 2);
+        } else {
+            panic!("missing puback");
+        }
+
+        if let Event::Outgoing(Outgoing::PubRec(pkid)) = mqtt.events[1] {
+            assert_eq!(pkid, 3);
+        } else {
+            panic!("missing PubRec");
+        }
     }
 
     #[test]
@@ -620,7 +662,7 @@ mod test {
         let pkid = mqtt.incoming_pub[3].unwrap();
         assert_eq!(pkid, 3);
 
-        assert!(mqtt.incoming_buf.lock().unwrap().is_empty());
+        assert!(mqtt.events.is_empty());
     }
 
     #[test]
@@ -629,9 +671,9 @@ mod test {
         let publish = build_incoming_publish(QoS::ExactlyOnce, 1);
 
         mqtt.handle_incoming_publish(&publish).unwrap();
-        let packet = read(&mut mqtt.write, 10 * 1024).unwrap();
+        let packet = Packet::read(&mut mqtt.write, 10 * 1024).unwrap();
         match packet {
-            Packet::PubRec(pubrec) => assert_eq!(pubrec.pkid, 1),
+            Packet::PubRec(pubrec, _) => assert_eq!(pubrec.pkid, 1),
             _ => panic!("Invalid network request: {:?}", packet),
         }
     }
@@ -640,10 +682,8 @@ mod test {
     fn incoming_puback_should_remove_correct_publish_from_queue() {
         let mut mqtt = build_mqttstate();
 
-        let mut publish1 = build_outgoing_publish(QoS::AtLeastOnce);
-        let mut publish2 = build_outgoing_publish(QoS::ExactlyOnce);
-        publish1.pkid = 1;
-        publish2.pkid = 2;
+        let publish1 = build_outgoing_publish(QoS::AtLeastOnce);
+        let publish2 = build_outgoing_publish(QoS::ExactlyOnce);
 
         mqtt.outgoing_publish(publish1).unwrap();
         mqtt.outgoing_publish(publish2).unwrap();
@@ -659,14 +699,24 @@ mod test {
         assert!(mqtt.outgoing_pub[2].is_none());
     }
 
+    #[test]
+    fn incoming_puback_with_pkid_greater_than_max_inflight_should_be_handled_gracefully() {
+        let mut mqtt = build_mqttstate();
+
+        let got = mqtt.handle_incoming_puback(&PubAck::new(101)).unwrap_err();
+
+        match got {
+            StateError::Unsolicited(pkid) => assert_eq!(pkid, 101),
+            e => panic!("Unexpected error: {}", e),
+        }
+    }
+
     #[test]
     fn incoming_pubrec_should_release_publish_from_queue_and_add_relid_to_rel_queue() {
         let mut mqtt = build_mqttstate();
 
-        let mut publish1 = build_outgoing_publish(QoS::AtLeastOnce);
-        let mut publish2 = build_outgoing_publish(QoS::ExactlyOnce);
-        publish1.pkid = 1;
-        publish2.pkid = 2;
+        let publish1 = build_outgoing_publish(QoS::AtLeastOnce);
+        let publish2 = build_outgoing_publish(QoS::ExactlyOnce);
 
         let _publish_out = mqtt.outgoing_publish(publish1);
         let _publish_out = mqtt.outgoing_publish(publish2);
@@ -686,19 +736,18 @@ mod test {
     fn incoming_pubrec_should_send_release_to_network_and_nothing_to_user() {
         let mut mqtt = build_mqttstate();
 
-        let mut publish = build_outgoing_publish(QoS::ExactlyOnce);
-        publish.pkid = 1;
+        let publish = build_outgoing_publish(QoS::ExactlyOnce);
         mqtt.outgoing_publish(publish).unwrap();
-        let packet = read(&mut mqtt.write, 10 * 1024).unwrap();
+        let packet = Packet::read(&mut mqtt.write, 10 * 1024).unwrap();
         match packet {
-            Packet::Publish(publish) => assert_eq!(publish.pkid, 1),
+            Packet::Publish(publish, _) => assert_eq!(publish.pkid, 1),
             packet => panic!("Invalid network request: {:?}", packet),
         }
 
         mqtt.handle_incoming_pubrec(&PubRec::new(1)).unwrap();
-        let packet = read(&mut mqtt.write, 10 * 1024).unwrap();
+        let packet = Packet::read(&mut mqtt.write, 10 * 1024).unwrap();
         match packet {
-            Packet::PubRel(pubrel) => assert_eq!(pubrel.pkid, 1),
+            Packet::PubRel(pubrel, _) => assert_eq!(pubrel.pkid, 1),
             packet => panic!("Invalid network request: {:?}", packet),
         }
     }
@@ -709,16 +758,16 @@ mod test {
         let publish = build_incoming_publish(QoS::ExactlyOnce, 1);
 
         mqtt.handle_incoming_publish(&publish).unwrap();
-        let packet = read(&mut mqtt.write, 10 * 1024).unwrap();
+        let packet = Packet::read(&mut mqtt.write, 10 * 1024).unwrap();
         match packet {
-            Packet::PubRec(pubrec) => assert_eq!(pubrec.pkid, 1),
+            Packet::PubRec(pubrec, _) => assert_eq!(pubrec.pkid, 1),
             packet => panic!("Invalid network request: {:?}", packet),
         }
 
         mqtt.handle_incoming_pubrel(&PubRel::new(1)).unwrap();
-        let packet = read(&mut mqtt.write, 10 * 1024).unwrap();
+        let packet = Packet::read(&mut mqtt.write, 10 * 1024).unwrap();
         match packet {
-            Packet::PubComp(pubcomp) => assert_eq!(pubcomp.pkid, 1),
+            Packet::PubComp(pubcomp, _) => assert_eq!(pubcomp.pkid, 1),
             packet => panic!("Invalid network request: {:?}", packet),
         }
     }
@@ -726,8 +775,7 @@ mod test {
     #[test]
     fn incoming_pubcomp_should_release_correct_pkid_from_release_queue() {
         let mut mqtt = build_mqttstate();
-        let mut publish = build_outgoing_publish(QoS::ExactlyOnce);
-        publish.pkid = 1;
+        let publish = build_outgoing_publish(QoS::ExactlyOnce);
 
         mqtt.outgoing_publish(publish).unwrap();
         mqtt.handle_incoming_pubrec(&PubRec::new(1)).unwrap();
@@ -739,16 +787,13 @@ mod test {
     #[test]
     fn outgoing_ping_handle_should_throw_errors_for_no_pingresp() {
         let mut mqtt = build_mqttstate();
-        let mut opts = MqttOptions::new("test", "localhost", 1883);
-        opts.set_keep_alive(std::time::Duration::from_secs(10));
         mqtt.outgoing_ping().unwrap();
 
         // network activity other than pingresp
-        let mut publish = build_outgoing_publish(QoS::AtLeastOnce);
-        publish.pkid = 1;
+        let publish = build_outgoing_publish(QoS::AtLeastOnce);
         mqtt.handle_outgoing_packet(Request::Publish(publish))
             .unwrap();
-        mqtt.handle_incoming_packet(Incoming::PubAck(PubAck::new(1)))
+        mqtt.handle_incoming_packet(Incoming::PubAck(PubAck::new(1), None))
             .unwrap();
 
         // should throw error because we didn't get pingresp for previous ping
@@ -763,12 +808,10 @@ mod test {
     fn outgoing_ping_handle_should_succeed_if_pingresp_is_received() {
         let mut mqtt = build_mqttstate();
 
-        let mut opts = MqttOptions::new("test", "localhost", 1883);
-        opts.set_keep_alive(std::time::Duration::from_secs(10));
-
         // should ping
         mqtt.outgoing_ping().unwrap();
-        mqtt.handle_incoming_packet(Incoming::PingResp).unwrap();
+        mqtt.handle_incoming_packet(Incoming::PingResp(PingResp))
+            .unwrap();
 
         // should ping
         mqtt.outgoing_ping().unwrap();
diff --git a/rumqttc/src/v5/tls.rs b/rumqttc/src/v5/tls.rs
deleted file mode 100644
index 3936b2ca8..000000000
--- a/rumqttc/src/v5/tls.rs
+++ /dev/null
@@ -1,130 +0,0 @@
-use tokio::net::TcpStream;
-use tokio_rustls::rustls;
-use tokio_rustls::rustls::client::InvalidDnsNameError;
-use tokio_rustls::rustls::{
-    Certificate, ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerName,
-};
-use tokio_rustls::webpki;
-use tokio_rustls::{client::TlsStream, TlsConnector};
-
-use crate::v5::{Key, MqttOptions, TlsConfiguration};
-
-use std::convert::TryFrom;
-use std::io;
-use std::io::{BufReader, Cursor};
-use std::net::AddrParseError;
-use std::sync::Arc;
-
-#[derive(Debug, thiserror::Error)]
-pub enum Error {
-    #[error("Addr")]
-    Addr(#[from] AddrParseError),
-    #[error("I/O")]
-    Io(#[from] io::Error),
-    #[error("Web Pki")]
-    WebPki(#[from] webpki::Error),
-    #[error("DNS name")]
-    DNSName(#[from] InvalidDnsNameError),
-    #[error("TLS error")]
-    TLS(#[from] rustls::Error),
-    #[error("No valid cert in chain")]
-    NoValidCertInChain,
-}
-
-// The cert handling functions return unit right now, this is a shortcut
-impl From<()> for Error {
-    fn from(_: ()) -> Self {
-        Error::NoValidCertInChain
-    }
-}
-
-pub async fn tls_connector(tls_config: &TlsConfiguration) -> Result<TlsConnector, Error> {
-    let config = match tls_config {
-        TlsConfiguration::Simple {
-            ca,
-            alpn,
-            client_auth,
-        } => {
-            // Add ca to root store if the connection is TLS
-            let mut root_cert_store = RootCertStore::empty();
-            let certs = rustls_pemfile::certs(&mut BufReader::new(Cursor::new(ca)))?;
-
-            let trust_anchors = certs.iter().map_while(|cert| {
-                if let Ok(ta) = webpki::TrustAnchor::try_from_cert_der(&cert[..]) {
-                    Some(OwnedTrustAnchor::from_subject_spki_name_constraints(
-                        ta.subject,
-                        ta.spki,
-                        ta.name_constraints,
-                    ))
-                } else {
-                    None
-                }
-            });
-
-            root_cert_store.add_server_trust_anchors(trust_anchors);
-
-            if root_cert_store.is_empty() {
-                return Err(Error::NoValidCertInChain);
-            }
-
-            let config = ClientConfig::builder()
-                .with_safe_defaults()
-                .with_root_certificates(root_cert_store);
-
-            // Add der encoded client cert and key
-            let mut config = if let Some(client) = client_auth.as_ref() {
-                let certs =
-                    rustls_pemfile::certs(&mut BufReader::new(Cursor::new(client.0.clone())))?;
-                // load appropriate Key as per the user request. The underlying signature algorithm
-                // of key generation determines the Signature Algorithm during the TLS Handskahe.
-                let read_keys = match &client.1 {
-                    Key::RSA(k) => rustls_pemfile::rsa_private_keys(&mut BufReader::new(
-                        Cursor::new(k.clone()),
-                    )),
-                    Key::ECC(k) => rustls_pemfile::pkcs8_private_keys(&mut BufReader::new(
-                        Cursor::new(k.clone()),
-                    )),
-                };
-                let keys = match read_keys {
-                    Ok(v) => v,
-                    Err(_e) => return Err(Error::NoValidCertInChain),
-                };
-
-                // Get the first key. Error if it's not valid
-                let key = match keys.first() {
-                    Some(k) => k.clone(),
-                    None => return Err(Error::NoValidCertInChain),
-                };
-
-                let certs = certs.into_iter().map(Certificate).collect();
-
-                config.with_single_cert(certs, PrivateKey(key))?
-            } else {
-                config.with_no_client_auth()
-            };
-
-            // Set ALPN
-            if let Some(alpn) = alpn.as_ref() {
-                config.alpn_protocols.extend_from_slice(alpn);
-            }
-
-            Arc::new(config)
-        }
-        TlsConfiguration::Rustls(tls_client_config) => tls_client_config.clone(),
-    };
-
-    Ok(TlsConnector::from(config))
-}
-
-pub async fn tls_connect(
-    options: &MqttOptions,
-    tls_config: &TlsConfiguration,
-) -> Result<TlsStream<TcpStream>, Error> {
-    let addr = options.broker_addr.as_str();
-    let port = options.port;
-    let connector = tls_connector(tls_config).await?;
-    let domain = ServerName::try_from(addr)?;
-    let tcp = TcpStream::connect((addr, port)).await?;
-    let tls = connector.connect(domain, tcp).await?;
-    Ok(tls)
-}


Test patch: