use super::property::{property_decode, property_decode_non_zero, property_encode};
use super::property::{property_len, Property, PropertyFrame};
use crate::codec::util::{
decode_byte, decode_bytes, decode_string, decode_variable_integer, encode_bytes, encode_string,
encode_variable_integer,
};
use crate::protocol::common::{connect, ConnectHeader};
use crate::protocol::common::{ConnectFrame, WillFrame};
use crate::protocol::util::len_bytes;
use crate::protocol::{util, Credentials, Protocol, QoS};
use crate::Error;
use bit_field::BitField;
use bytes::{Buf, Bytes, BytesMut};
use std::ops::RangeInclusive;
use std::time::Duration;
const WILL_FLAG: usize = 2;
const WILL_QOS: RangeInclusive<usize> = 3..=4;
const WILL_RETAIN: usize = 5;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct ConnectProperties {
pub session_expiry_interval: Option<Duration>,
pub receive_maximum: Option<u16>,
pub maximum_packet_size: Option<u32>,
pub topic_alias_maximum: Option<u16>,
pub request_response_info: Option<bool>,
pub request_problem_info: Option<bool>,
pub user_properties: Vec<(String, String)>,
pub auth_method: Option<String>,
pub auth_data: Option<Bytes>,
}
impl PropertyFrame for ConnectProperties {
fn encoded_len(&self) -> usize {
let mut len = 0;
len += property_len!(&self.session_expiry_interval);
len += property_len!(&self.receive_maximum);
len += property_len!(&self.maximum_packet_size);
len += property_len!(&self.topic_alias_maximum);
len += property_len!(&self.request_response_info);
len += property_len!(&self.request_problem_info);
len += property_len!(&self.user_properties);
len += property_len!(&self.auth_method);
len += property_len!(&self.auth_data);
len
}
fn encode(&self, buf: &mut BytesMut) {
property_encode!(
&self.session_expiry_interval,
Property::SessionExpiryInterval,
buf
);
property_encode!(&self.receive_maximum, Property::ReceiveMaximum, buf);
property_encode!(&self.maximum_packet_size, Property::MaximumPacketSize, buf);
property_encode!(&self.topic_alias_maximum, Property::TopicAliasMaximum, buf);
property_encode!(
&self.request_response_info,
Property::RequestResponseInformation,
buf
);
property_encode!(
&self.request_problem_info,
Property::RequestProblemInformation,
buf
);
property_encode!(&self.user_properties, Property::UserProp, buf);
property_encode!(&self.auth_method, Property::AuthenticationMethod, buf);
property_encode!(&self.auth_data, Property::AuthenticationData, buf);
}
fn decode(buf: &mut Bytes) -> Result<Option<Self>, Error> {
if buf.is_empty() {
return Ok(None);
}
let mut properties = ConnectProperties::default();
while buf.has_remaining() {
let property: Property = decode_byte(buf)?.try_into()?;
match property {
Property::SessionExpiryInterval => {
property_decode!(&mut properties.session_expiry_interval, buf);
}
Property::ReceiveMaximum => {
property_decode_non_zero!(&mut properties.receive_maximum, buf);
}
Property::MaximumPacketSize => {
property_decode_non_zero!(&mut properties.maximum_packet_size, buf);
}
Property::TopicAliasMaximum => {
property_decode!(&mut properties.topic_alias_maximum, buf);
}
Property::RequestResponseInformation => {
property_decode!(&mut properties.request_response_info, buf);
}
Property::RequestProblemInformation => {
property_decode!(&mut properties.request_problem_info, buf);
}
Property::UserProp => {
property_decode!(&mut properties.user_properties, buf);
}
Property::AuthenticationMethod => {
property_decode!(&mut properties.auth_method, buf);
}
Property::AuthenticationData => {
property_decode!(&mut properties.auth_data, buf);
}
_ => return Err(Error::PropertyMismatch),
};
}
if properties.auth_data.is_some() && properties.auth_method.is_none() {
return Err(Error::ProtocolError);
}
Ok(Some(properties))
}
}
impl ConnectFrame for ConnectHeader<ConnectProperties> {
fn encoded_len(&self) -> usize {
let properties_len = self
.properties
.as_ref()
.map(|properties| properties.encoded_len())
.unwrap_or(0);
properties_len + len_bytes(properties_len) + self.primary_encoded_len()
}
fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
self.primary_encode(buf);
let properties_len = self
.properties
.as_ref()
.map(|properties| properties.encoded_len())
.unwrap_or(0) as u32;
encode_variable_integer(buf, properties_len)?;
if let Some(properties) = self.properties.as_ref() {
properties.encode(buf);
}
Ok(())
}
fn decode(buf: &mut Bytes) -> Result<Self, Error> {
let mut header = Self::primary_decode(buf)?;
let properties_len = decode_variable_integer(buf)? as usize;
if buf.len() < properties_len + len_bytes(properties_len) {
return Err(Error::MalformedPacket);
}
buf.advance(len_bytes(properties_len));
let mut properties_buf = buf.split_to(properties_len);
header.properties = ConnectProperties::decode(&mut properties_buf)?;
Ok(header)
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WillProperties {
pub delay_interval: Option<Duration>,
pub payload_format_indicator: Option<u8>,
pub message_expiry_interval: Option<Duration>,
pub content_type: Option<String>,
pub response_topic: Option<String>,
pub correlation_data: Option<Bytes>,
pub user_properties: Vec<(String, String)>,
}
impl PropertyFrame for WillProperties {
fn encoded_len(&self) -> usize {
let mut len = 0;
len += property_len!(&self.delay_interval);
len += property_len!(&self.payload_format_indicator);
len += property_len!(&self.message_expiry_interval);
len += property_len!(&self.content_type);
len += property_len!(&self.response_topic);
len += property_len!(&self.correlation_data);
len += property_len!(&self.user_properties);
len
}
fn encode(&self, buf: &mut BytesMut) {
property_encode!(&self.delay_interval, Property::WillDelayInterval, buf);
property_encode!(
&self.payload_format_indicator,
Property::PayloadFormatIndicator,
buf
);
property_encode!(
&self.message_expiry_interval,
Property::MessageExpiryInterval,
buf
);
property_encode!(&self.content_type, Property::ContentType, buf);
property_encode!(&self.response_topic, Property::ResponseTopic, buf);
property_encode!(&self.correlation_data, Property::CorrelationData, buf);
property_encode!(&self.user_properties, Property::UserProp, buf);
}
fn decode(buf: &mut Bytes) -> Result<Option<Self>, Error> {
if buf.is_empty() {
return Ok(None);
}
let mut properties = WillProperties::default();
while buf.has_remaining() {
let property: Property = decode_byte(buf)?.try_into()?;
match property {
Property::WillDelayInterval => {
property_decode!(&mut properties.delay_interval, buf);
}
Property::PayloadFormatIndicator => {
property_decode!(&mut properties.payload_format_indicator, buf);
if let Some(value) = properties.payload_format_indicator
&& value != 0
&& value != 1
{
return Err(Error::ProtocolError);
}
}
Property::MessageExpiryInterval => {
property_decode!(&mut properties.message_expiry_interval, buf);
}
Property::ContentType => {
property_decode!(&mut properties.content_type, buf);
}
Property::ResponseTopic => {
property_decode!(&mut properties.response_topic, buf);
}
Property::CorrelationData => {
property_decode!(&mut properties.correlation_data, buf);
}
Property::UserProp => {
property_decode!(&mut properties.user_properties, buf);
}
_ => return Err(Error::PropertyMismatch),
}
}
Ok(Some(properties))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Will {
pub properties: Option<WillProperties>,
pub topic: String,
pub payload: Bytes,
pub qos: QoS,
pub retain: bool,
}
impl Will {
pub fn new<T: Into<String>>(
properties: Option<WillProperties>,
topic: T,
payload: Bytes,
qos: QoS,
retain: bool,
) -> Self {
let topic = topic.into();
if !util::is_valid_topic_name(&topic) {
panic!("Invalid topic name: '{}'", topic);
}
Will {
properties,
topic,
payload,
qos,
retain,
}
}
}
impl WillFrame for Will {
fn encoded_len(&self) -> usize {
let properties_len = self
.properties
.as_ref()
.map(|properties| properties.encoded_len())
.unwrap_or(0);
2 + self.topic.len() + 2 + self.payload.len() + len_bytes(properties_len) + properties_len
}
fn update_flags(&self, flags: &mut u8) {
flags.set_bit(WILL_FLAG, true);
flags.set_bits(WILL_QOS, self.qos as u8);
flags.set_bit(WILL_RETAIN, self.retain);
}
fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
let properties_len = self
.properties
.as_ref()
.map(|properties| properties.encoded_len())
.unwrap_or(0) as u32;
encode_variable_integer(buf, properties_len)?;
if let Some(properties) = self.properties.as_ref() {
properties.encode(buf);
}
encode_string(buf, &self.topic);
encode_bytes(buf, &self.payload);
Ok(())
}
fn decode(buf: &mut Bytes, flags: u8) -> Result<Option<Self>, Error> {
if !flags.get_bit(WILL_FLAG) {
return Ok(None);
}
let properties_len = decode_variable_integer(buf)? as usize;
if buf.len() < properties_len + len_bytes(properties_len) {
return Err(Error::MalformedPacket);
}
buf.advance(len_bytes(properties_len));
let mut properties_buf = buf.split_to(properties_len);
let properties = WillProperties::decode(&mut properties_buf)?;
let qos = flags.get_bits(WILL_QOS).try_into()?;
let retain = flags.get_bit(WILL_RETAIN);
let topic = decode_string(buf)?;
if !util::is_valid_topic_name(&topic) {
return Err(Error::InvalidTopicName(topic));
}
let payload = decode_bytes(buf)?;
Ok(Some(Will {
properties,
topic,
payload,
qos,
retain,
}))
}
}
connect!(Connect<ConnectProperties, Will>, Protocol::V5);
impl Connect {
pub fn with_properties<S: Into<String>>(
client_id: S,
auth: Option<Credentials>,
will: Option<Will>,
properties: ConnectProperties,
keep_alive: Duration,
clean_session: bool,
) -> Self {
Self::from_scratch(
client_id,
auth,
will,
Some(properties),
keep_alive,
clean_session,
)
}
pub fn properties(&self) -> Option<ConnectProperties> {
self.header.properties.clone()
}
}