#[cfg(feature = "quic")]
use quinn_proto::{ClientConfig, Connection, ConnectionHandle, Endpoint, EndpointConfig, StreamId};
use std::collections::VecDeque;
#[cfg(feature = "quic")]
use std::sync::Arc;
use std::time::{Duration, Instant};
use crate::mqtt_serde::control_packet::MqttPacket;
use crate::mqtt_serde::mqttv3::{
connectv3, disconnectv3, pingreqv3, pubrelv3, subscribev3, unsubscribev3,
};
use crate::mqtt_serde::mqttv5::{
authv5, common::properties::Property, connectv5, disconnectv5, pingreqv5, pubackv5::MqttPubAck,
pubcompv5::MqttPubComp, publishv5::MqttPublish, pubrecv5::MqttPubRec, pubrelv5::MqttPubRel,
subscribev5, unsubscribev5,
};
use crate::mqtt_serde::parser::stream::MqttParser;
use crate::mqtt_session::ClientSession;
use crate::priority_queue::PriorityQueue;
use super::client::{
ConnectionResult, PingResult, PublishResult, SubscribeResult, UnsubscribeResult,
};
use super::commands::{PublishCommand, SubscribeCommand, UnsubscribeCommand};
use super::error::MqttClientError;
use super::inflight::InflightQueue;
use super::opts::MqttClientOptions;
pub type MqttMessage = MqttPublish;
#[derive(Debug, serde::Serialize)]
pub enum MqttEvent {
Connected(ConnectionResult),
Disconnected(Option<u8>),
Published(PublishResult),
Subscribed(SubscribeResult),
Unsubscribed(UnsubscribeResult),
MessageReceived(MqttMessage),
PingResponse(PingResult),
Error(MqttClientError),
ReconnectNeeded,
ReconnectScheduled {
attempt: u32,
delay: Duration,
},
}
pub struct MqttEngine {
options: MqttClientOptions,
session: Option<ClientSession>,
priority_queue: PriorityQueue<u8, MqttPacket>,
is_connected: bool,
last_packet_sent: Instant,
last_packet_received: Instant,
parser: MqttParser,
outgoing_buffer: VecDeque<Vec<u8>>,
inflight_queue: InflightQueue,
events: Vec<MqttEvent>,
reconnect_attempts: u32,
next_reconnect_at: Option<Instant>,
reconnect_base_delay: Duration,
reconnect_max_delay: Duration,
max_reconnect_attempts: u32,
}
impl MqttEngine {
pub fn new(options: MqttClientOptions) -> Self {
let mqtt_version = options.mqtt_version;
let parser = MqttParser::new(16384, mqtt_version);
let retransmission_timeout = Duration::from_millis(options.retransmission_timeout_ms);
let reconnect_base_delay = Duration::from_millis(options.reconnect_base_delay_ms);
let reconnect_max_delay = Duration::from_millis(options.reconnect_max_delay_ms);
let max_reconnect_attempts = options.max_reconnect_attempts;
Self {
inflight_queue: InflightQueue::new(
options.receive_maximum,
options.mqtt_version,
retransmission_timeout,
),
session: None,
priority_queue: PriorityQueue::new(1000),
is_connected: false,
last_packet_sent: Instant::now(),
last_packet_received: Instant::now(),
parser,
outgoing_buffer: VecDeque::new(),
events: Vec::new(),
reconnect_attempts: 0,
next_reconnect_at: None,
reconnect_base_delay,
reconnect_max_delay,
max_reconnect_attempts,
options,
}
}
pub fn take_events(&mut self) -> Vec<MqttEvent> {
std::mem::take(&mut self.events)
}
pub fn options(&self) -> &MqttClientOptions {
&self.options
}
pub fn is_connected(&self) -> bool {
self.is_connected
}
pub fn handle_connection_lost(&mut self) {
self.is_connected = false;
}
pub fn schedule_reconnect(&mut self, now: Instant) {
if self.max_reconnect_attempts > 0 && self.reconnect_attempts >= self.max_reconnect_attempts
{
self.next_reconnect_at = None;
return;
}
let exponent = self.reconnect_attempts.min(10);
let multiplier = 1u64 << exponent;
let delay_ms = self
.reconnect_base_delay
.as_millis()
.saturating_mul(multiplier as u128);
let delay_ms = delay_ms.min(self.reconnect_max_delay.as_millis());
let delay = Duration::from_millis(delay_ms as u64);
self.next_reconnect_at = Some(now + delay);
self.reconnect_attempts += 1;
self.events.push(MqttEvent::ReconnectScheduled {
attempt: self.reconnect_attempts,
delay,
});
}
pub fn reset_reconnect_state(&mut self) {
self.reconnect_attempts = 0;
self.next_reconnect_at = None;
}
pub fn handle_incoming(&mut self, data: &[u8]) -> Vec<MqttEvent> {
self.parser.feed(data);
loop {
if self.events.len() >= self.options.max_event_count {
break;
}
match self.parser.next_packet() {
Ok(Some(packet)) => {
self.last_packet_received = Instant::now();
let packet_events = self.handle_packet(packet);
self.events.extend(packet_events);
}
Ok(None) => break,
Err(e) => {
self.events.push(MqttEvent::Error(MqttClientError::from(e)));
break;
}
}
}
self.process_queue();
self.take_events()
}
pub fn mqtt_version(&self) -> u8 {
self.options.mqtt_version
}
pub fn handle_tick(&mut self, now: Instant) -> Vec<MqttEvent> {
if !self.is_connected {
if let Some(reconnect_at) = self.next_reconnect_at {
if now >= reconnect_at {
self.events.push(MqttEvent::ReconnectNeeded);
self.next_reconnect_at = None;
}
}
return self.take_events();
}
let keep_alive = Duration::from_secs(self.options.keep_alive as u64);
if keep_alive > Duration::ZERO && now.duration_since(self.last_packet_sent) >= keep_alive {
self.send_ping();
self.last_packet_sent = now;
}
if keep_alive > Duration::ZERO
&& now.duration_since(self.last_packet_received)
>= keep_alive * self.options.ping_timeout_multiplier
{
self.events.push(MqttEvent::ReconnectNeeded);
self.handle_connection_lost();
self.schedule_reconnect(now);
return self.take_events();
}
let retrans_events = self.handle_retransmissions(now);
self.events.extend(retrans_events);
self.take_events()
}
fn handle_retransmissions(&mut self, now: Instant) -> Vec<MqttEvent> {
let events = Vec::new();
let expired = self.inflight_queue.get_expired(now);
for mut packet in expired {
packet.set_dup(true);
if let Ok(bytes) = packet.to_bytes() {
self.outgoing_buffer.push_back(bytes);
self.last_packet_sent = now;
}
}
events
}
pub fn next_tick_at(&self) -> Option<Instant> {
if !self.is_connected {
return self.next_reconnect_at;
}
let mut next = None;
let keep_alive = Duration::from_secs(self.options.keep_alive as u64);
if keep_alive > Duration::ZERO {
let ping_deadline = self.last_packet_sent + keep_alive;
next = Some(ping_deadline);
}
if keep_alive > Duration::ZERO {
let timeout = keep_alive * self.options.ping_timeout_multiplier;
let timeout_deadline = self.last_packet_received + timeout;
if next.is_none() || timeout_deadline < next.unwrap() {
next = Some(timeout_deadline);
}
}
if let Some(resend_at) = self.inflight_queue.next_expiration() {
if next.is_none() || resend_at < next.unwrap() {
next = Some(resend_at);
}
}
next
}
pub fn take_outgoing(&mut self) -> Vec<u8> {
let mut all_bytes = Vec::new();
while let Some(packet) = self.outgoing_buffer.pop_front() {
all_bytes.extend(packet);
}
all_bytes
}
pub fn connect(&mut self) {
if self.is_connected {
return;
}
if self.session.is_none() {
self.session = Some(ClientSession::new());
}
let packet = if self.options.mqtt_version == 5 {
let connect = connectv5::MqttConnect::new(
self.options.client_id.clone(),
self.options.username.clone(),
self.options.password.clone(),
None, self.options.keep_alive,
self.options.clean_start,
Vec::new(), );
MqttPacket::Connect5(connect)
} else {
let connect = connectv3::MqttConnect::new(
self.options.client_id.clone(),
self.options.keep_alive,
self.options.clean_start,
);
MqttPacket::Connect3(connect)
};
let _ = self.enqueue_packet(packet);
}
pub fn publish(&mut self, mut command: PublishCommand) -> Result<Option<u16>, MqttClientError> {
let pid = if command.qos > 0 {
if let Some(pid) = command.packet_id {
Some(pid)
} else {
let pid = self.next_packet_id()?;
command.packet_id = Some(pid);
Some(pid)
}
} else {
None
};
let packet = if self.options.mqtt_version == 5 {
MqttPacket::Publish5(command.to_mqtt_publish())
} else {
MqttPacket::Publish3(command.to_mqttv3_publish())
};
if let Some(_pid) = pid {
}
self.priority_queue.enqueue(command.priority, packet);
self.process_queue();
Ok(pid)
}
pub fn subscribe(&mut self, mut command: SubscribeCommand) -> Result<u16, MqttClientError> {
let pid = if let Some(pid) = command.packet_id {
pid
} else {
let pid = self.next_packet_id()?;
command.packet_id = Some(pid);
pid
};
let packet = if self.options.mqtt_version == 5 {
MqttPacket::Subscribe5(subscribev5::MqttSubscribe::new(
pid,
command.subscriptions,
command.properties,
))
} else {
let v3_subs = command
.subscriptions
.into_iter()
.map(|s| subscribev3::SubscriptionTopic {
topic_filter: s.topic_filter,
qos: s.qos,
})
.collect();
MqttPacket::Subscribe3(subscribev3::MqttSubscribe::new(pid, v3_subs))
};
self.inflight_queue.push(pid, packet.clone(), 1)?;
self.enqueue_packet(packet)?;
Ok(pid)
}
pub fn unsubscribe(&mut self, mut command: UnsubscribeCommand) -> Result<u16, MqttClientError> {
let pid = if let Some(pid) = command.packet_id {
pid
} else {
let pid = self.next_packet_id()?;
command.packet_id = Some(pid);
pid
};
let packet = if self.options.mqtt_version == 5 {
MqttPacket::Unsubscribe5(unsubscribev5::MqttUnsubscribe::new(
pid,
command.topics.clone(),
command.properties,
))
} else {
MqttPacket::Unsubscribe3(unsubscribev3::MqttUnsubscribe::new(pid, command.topics))
};
self.inflight_queue.push(pid, packet.clone(), 1)?;
self.enqueue_packet(packet)?;
Ok(pid)
}
pub fn disconnect(&mut self) {
if !self.is_connected {
return;
}
let packet = if self.options.mqtt_version == 5 {
MqttPacket::Disconnect5(disconnectv5::MqttDisconnect::new(0, Vec::new()))
} else {
MqttPacket::Disconnect3(disconnectv3::MqttDisconnect::new())
};
let _ = self.enqueue_packet(packet);
self.is_connected = false;
}
pub fn auth(&mut self, reason_code: u8, properties: Vec<Property>) {
if self.options.mqtt_version == 5 {
let auth = authv5::MqttAuth::new(reason_code, properties);
let _ = self.enqueue_packet(MqttPacket::Auth(auth));
}
}
fn handle_packet(&mut self, packet: MqttPacket) -> Vec<MqttEvent> {
let mut events = Vec::new();
match packet {
MqttPacket::ConnAck5(ack) => {
self.is_connected = ack.reason_code == 0;
if self.is_connected {
self.reset_reconnect_state();
}
events.push(MqttEvent::Connected(ConnectionResult {
reason_code: ack.reason_code,
session_present: ack.session_present,
properties: ack.properties.clone(),
}));
if let Some(props) = &ack.properties {
for prop in props {
if let Property::ReceiveMaximum(max) = prop {
self.inflight_queue.update_receive_maximum(*max);
break;
}
}
}
if ack.session_present {
let pending = self.inflight_queue.get_all_for_reconnect();
for mut packet in pending {
packet.set_dup(true);
if let Ok(bytes) = packet.to_bytes() {
self.outgoing_buffer.push_back(bytes);
}
}
}
}
MqttPacket::ConnAck3(ack) => {
self.is_connected = ack.return_code == 0;
if self.is_connected {
self.reset_reconnect_state();
}
events.push(MqttEvent::Connected(ConnectionResult {
reason_code: ack.return_code,
session_present: ack.session_present,
properties: None,
}));
if ack.session_present {
let pending = self.inflight_queue.get_all_for_reconnect();
for mut packet in pending {
packet.set_dup(true);
if let Ok(bytes) = packet.to_bytes() {
self.outgoing_buffer.push_back(bytes);
}
}
}
}
MqttPacket::PubAck5(ack) => {
if let Some(entry) = self.inflight_queue.acknowledge(ack.packet_id) {
events.push(MqttEvent::Published(PublishResult {
packet_id: Some(ack.packet_id),
reason_code: Some(ack.reason_code),
properties: Some(ack.properties),
qos: entry.qos,
}));
}
}
MqttPacket::PubAck3(ack) => {
if let Some(entry) = self.inflight_queue.acknowledge(ack.message_id) {
events.push(MqttEvent::Published(PublishResult {
packet_id: Some(ack.message_id),
reason_code: Some(0), properties: None,
qos: entry.qos,
}));
}
}
MqttPacket::Publish5(p) => {
let qos = p.qos;
let pid = p.packet_id;
events.push(MqttEvent::MessageReceived(p));
if qos == 1 {
if let Some(pid) = pid {
let ack = MqttPacket::PubAck5(MqttPubAck::new(pid, 0, Vec::new()));
let _ = self.enqueue_packet(ack);
}
} else if qos == 2 {
if let Some(pid) = pid {
let rec = MqttPacket::PubRec5(MqttPubRec::new(pid, 0, Vec::new()));
let _ = self.enqueue_packet(rec);
}
}
}
MqttPacket::Publish3(p) => {
let qos = p.qos;
let pid = p.message_id;
let p5 = MqttPublish::new_with_prop(
qos,
p.topic_name.clone(),
pid,
p.payload.clone(),
p.retain,
p.dup,
Vec::new(),
);
events.push(MqttEvent::MessageReceived(p5));
if qos == 1 {
if let Some(pid) = pid {
let ack = MqttPacket::PubAck3(
crate::mqtt_serde::mqttv3::puback::MqttPubAck::new(pid),
);
let _ = self.enqueue_packet(ack);
}
} else if qos == 2 {
if let Some(pid) = pid {
let rec = MqttPacket::PubRec3(
crate::mqtt_serde::mqttv3::pubrec::MqttPubRec::new(pid),
);
let _ = self.enqueue_packet(rec);
}
}
}
MqttPacket::SubAck5(ack) => {
self.inflight_queue.acknowledge(ack.packet_id);
events.push(MqttEvent::Subscribed(SubscribeResult {
packet_id: ack.packet_id,
reason_codes: ack.reason_codes,
properties: ack.properties,
}));
}
MqttPacket::SubAck3(ack) => {
self.inflight_queue.acknowledge(ack.message_id);
events.push(MqttEvent::Subscribed(SubscribeResult {
packet_id: ack.message_id,
reason_codes: ack.return_codes,
properties: Vec::new(),
}));
}
MqttPacket::UnsubAck5(ack) => {
self.inflight_queue.acknowledge(ack.packet_id);
events.push(MqttEvent::Unsubscribed(UnsubscribeResult {
packet_id: ack.packet_id,
reason_codes: ack.reason_codes,
properties: ack.properties,
}));
}
MqttPacket::UnsubAck3(ack) => {
self.inflight_queue.acknowledge(ack.message_id);
events.push(MqttEvent::Unsubscribed(UnsubscribeResult {
packet_id: ack.message_id,
reason_codes: Vec::new(),
properties: Vec::new(),
}));
}
MqttPacket::PingResp5(_) | MqttPacket::PingResp3(_) => {
events.push(MqttEvent::PingResponse(PingResult { success: true }));
}
MqttPacket::PubRec5(rec) => {
if let Some(mut entry) = self.inflight_queue.acknowledge(rec.packet_id) {
let rel = MqttPacket::PubRel5(MqttPubRel::new(rec.packet_id, 0, Vec::new()));
entry.packet = rel.clone();
entry.sent_at = Instant::now();
let _ = self.inflight_queue.push(entry.packet_id, entry.packet, 2);
let _ = self.enqueue_packet(rel);
}
}
MqttPacket::PubRec3(rec) => {
if let Some(mut entry) = self.inflight_queue.acknowledge(rec.message_id) {
let rel = MqttPacket::PubRel3(pubrelv3::MqttPubRel::new(rec.message_id));
entry.packet = rel.clone();
entry.sent_at = Instant::now();
let _ = self.inflight_queue.push(entry.packet_id, entry.packet, 2);
let _ = self.enqueue_packet(rel);
}
}
MqttPacket::PubRel5(rel) => {
let comp = MqttPacket::PubComp5(MqttPubComp::new(rel.packet_id, 0, Vec::new()));
let _ = self.enqueue_packet(comp);
}
MqttPacket::PubRel3(rel) => {
let comp = MqttPacket::PubComp3(
crate::mqtt_serde::mqttv3::pubcomp::MqttPubComp::new(rel.message_id),
);
let _ = self.enqueue_packet(comp);
}
MqttPacket::PubComp5(comp) => {
if let Some(entry) = self.inflight_queue.acknowledge(comp.packet_id) {
events.push(MqttEvent::Published(PublishResult {
packet_id: Some(comp.packet_id),
reason_code: Some(comp.reason_code),
properties: Some(comp.properties),
qos: entry.qos,
}));
}
}
MqttPacket::PubComp3(comp) => {
if let Some(entry) = self.inflight_queue.acknowledge(comp.message_id) {
events.push(MqttEvent::Published(PublishResult {
packet_id: Some(comp.message_id),
reason_code: Some(0),
properties: None,
qos: entry.qos,
}));
}
}
MqttPacket::Disconnect5(d) => {
self.is_connected = false;
events.push(MqttEvent::Disconnected(Some(d.reason_code)));
}
_ => {}
}
events
}
pub fn send_ping(&mut self) {
let packet = if self.options.mqtt_version == 5 {
MqttPacket::PingReq5(pingreqv5::MqttPingReq::new())
} else {
MqttPacket::PingReq3(pingreqv3::MqttPingReq::new())
};
let _ = self.enqueue_packet(packet);
}
pub fn enqueue_packet(&mut self, packet: MqttPacket) -> Result<(), MqttClientError> {
if self.outgoing_buffer.len() >= self.options.max_outgoing_packet_count {
return Err(MqttClientError::BufferFull {
buffer_type: "outgoing".to_string(),
capacity: self.options.max_outgoing_packet_count,
});
}
match packet.to_bytes() {
Ok(bytes) => {
self.outgoing_buffer.push_back(bytes);
self.last_packet_sent = Instant::now();
Ok(())
}
Err(e) => Err(MqttClientError::from(e)),
}
}
fn process_queue(&mut self) {
if !self.is_connected {
return;
}
while self.outgoing_buffer.len() < self.options.max_outgoing_packet_count {
if let Some((_priority, packet)) = self.priority_queue.peek() {
let is_publish =
matches!(packet, MqttPacket::Publish5(_) | MqttPacket::Publish3(_));
if is_publish && !self.inflight_queue.can_push_publish() {
break;
}
match packet.to_bytes() {
Ok(bytes) => {
if let Some((_, packet)) = self.priority_queue.dequeue() {
match &packet {
MqttPacket::Publish5(p) if p.qos > 0 => {
let pid = p.packet_id.unwrap();
let _ = self.inflight_queue.push(pid, packet.clone(), p.qos);
}
MqttPacket::Publish3(p) if p.qos > 0 => {
let pid = p.message_id.unwrap();
let _ = self.inflight_queue.push(pid, packet.clone(), p.qos);
}
_ => {}
}
self.outgoing_buffer.push_back(bytes);
self.last_packet_sent = Instant::now();
}
}
Err(e) => {
if let Some((_, _)) = self.priority_queue.dequeue() {
self.events.push(MqttEvent::Error(MqttClientError::from(e)));
}
}
}
} else {
break;
}
}
}
pub fn next_packet_id(&mut self) -> Result<u16, MqttClientError> {
let session = self
.session
.as_mut()
.ok_or(MqttClientError::ProtocolViolation {
message: "No session available for packet ID allocation".into(),
})?;
Ok(session.next_packet_id())
}
}
#[cfg(feature = "quic")]
pub struct QuicMqttEngine {
mqtt_engine: MqttEngine,
endpoint: Endpoint,
connection: Option<Connection>,
connection_handle: Option<ConnectionHandle>,
mqtt_stream: Option<StreamId>,
outgoing_datagrams: VecDeque<(std::net::SocketAddr, Vec<u8>)>,
}
#[cfg(feature = "quic")]
impl QuicMqttEngine {
pub fn new(options: MqttClientOptions) -> Result<Self, MqttClientError> {
let mqtt_engine = MqttEngine::new(options);
let endpoint_config = EndpointConfig::default();
let endpoint = Endpoint::new(Arc::new(endpoint_config), None, true, None);
Ok(Self {
mqtt_engine,
endpoint,
connection: None,
connection_handle: None,
mqtt_stream: None,
outgoing_datagrams: VecDeque::new(),
})
}
pub fn connect(
&mut self,
server_addr: std::net::SocketAddr,
server_name: &str,
mut crypto_config: rustls::ClientConfig,
now: Instant,
) -> Result<(), MqttClientError> {
if crypto_config.alpn_protocols.is_empty() {
crypto_config.alpn_protocols = vec![b"mqtt".to_vec()];
}
let mut client_config = ClientConfig::new(Arc::new(
quinn_proto::crypto::rustls::QuicClientConfig::try_from(crypto_config).map_err(
|e| MqttClientError::InternalError {
message: format!("Failed to create QUIC client config: {}", e),
},
)?,
));
let mut transport = quinn_proto::TransportConfig::default();
transport.datagram_receive_buffer_size(None);
client_config.transport_config(Arc::new(transport));
let (ch, conn) = self
.endpoint
.connect(now, client_config, server_addr, server_name)
.map_err(|e| MqttClientError::InternalError {
message: format!("Failed to create QUIC connection: {}", e),
})?;
self.connection = Some(conn);
self.connection_handle = Some(ch);
Ok(())
}
pub fn handle_datagram(
&mut self,
data: Vec<u8>,
remote_addr: std::net::SocketAddr,
now: Instant,
) {
use bytes::BytesMut;
use quinn_proto::DatagramEvent;
let mut buf = Vec::new();
let bytes = BytesMut::from(&data[..]);
let result = self
.endpoint
.handle(now, remote_addr, None, None, bytes, &mut buf);
if !buf.is_empty() {
self.outgoing_datagrams.push_back((remote_addr, buf));
}
if let Some(event) = result {
match event {
DatagramEvent::NewConnection(_incoming) => {
}
DatagramEvent::ConnectionEvent(ch, event) => {
if Some(ch) == self.connection_handle {
if let Some(conn) = &mut self.connection {
conn.handle_event(event);
}
}
}
DatagramEvent::Response(_transmit) => {
}
}
}
}
pub fn handle_tick(&mut self, now: Instant) -> Vec<MqttEvent> {
let mut mqtt_events = Vec::new();
if let Some(conn) = &mut self.connection {
conn.handle_timeout(now);
while let Some(event) = conn.poll() {
match event {
quinn_proto::Event::Stream(_stream_id) => {
}
quinn_proto::Event::Connected => {
if self.mqtt_stream.is_none() {
if let Some(stream_id) = conn.streams().open(quinn_proto::Dir::Bi) {
self.mqtt_stream = Some(stream_id);
self.mqtt_engine.connect();
}
}
}
quinn_proto::Event::ConnectionLost { .. } => {
self.mqtt_engine.handle_connection_lost();
mqtt_events.push(MqttEvent::Disconnected(None));
}
_ => {}
}
}
if let Some(stream_id) = self.mqtt_stream {
let mut stream = conn.recv_stream(stream_id);
let read_result = stream.read(true);
if let Ok(mut chunks) = read_result {
while let Ok(Some(chunk)) = chunks.next(16384) {
mqtt_events.extend(self.mqtt_engine.handle_incoming(&chunk.bytes));
}
}
}
let outgoing_bytes = self.mqtt_engine.take_outgoing();
if !outgoing_bytes.is_empty() {
if let Some(stream_id) = self.mqtt_stream {
let mut stream = conn.send_stream(stream_id);
let _ = stream.write(&outgoing_bytes);
}
}
let mut buf = Vec::new();
while let Some(transmit) = conn.poll_transmit(now, 1, &mut buf) {
self.outgoing_datagrams
.push_back((transmit.destination, buf.clone()));
buf.clear();
}
}
let tick_events = self.mqtt_engine.handle_tick(now);
mqtt_events.extend(tick_events);
mqtt_events
}
pub fn take_outgoing_datagrams(&mut self) -> VecDeque<(std::net::SocketAddr, Vec<u8>)> {
std::mem::take(&mut self.outgoing_datagrams)
}
pub fn take_events(&mut self) -> Vec<MqttEvent> {
self.mqtt_engine.take_events()
}
pub fn publish(&mut self, command: PublishCommand) -> Result<Option<u16>, MqttClientError> {
self.mqtt_engine.publish(command)
}
pub fn subscribe(&mut self, command: SubscribeCommand) -> Result<u16, MqttClientError> {
self.mqtt_engine.subscribe(command)
}
pub fn unsubscribe(&mut self, command: UnsubscribeCommand) -> Result<u16, MqttClientError> {
self.mqtt_engine.unsubscribe(command)
}
pub fn disconnect(&mut self) {
self.mqtt_engine.disconnect();
}
pub fn is_connected(&self) -> bool {
self.mqtt_engine.is_connected()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mqtt_client::opts::MqttClientOptions;
use crate::mqtt_serde::mqttv5::pingreqv5;
#[test]
fn test_outgoing_buffer_limit() {
let options = MqttClientOptions::builder()
.max_outgoing_packet_count(2)
.build();
let mut engine = MqttEngine::new(options);
let packet = MqttPacket::PingReq5(pingreqv5::MqttPingReq::new());
assert!(engine.enqueue_packet(packet.clone()).is_ok());
assert_eq!(engine.outgoing_buffer.len(), 1);
assert!(engine.enqueue_packet(packet.clone()).is_ok());
assert_eq!(engine.outgoing_buffer.len(), 2);
let result = engine.enqueue_packet(packet.clone());
assert!(result.is_err());
match result {
Err(MqttClientError::BufferFull {
buffer_type,
capacity,
}) => {
assert_eq!(buffer_type, "outgoing");
assert_eq!(capacity, 2);
}
_ => panic!("Expected BufferFull error"),
}
let _ = engine.take_outgoing();
assert_eq!(engine.outgoing_buffer.len(), 0);
assert!(engine.enqueue_packet(packet).is_ok());
}
#[test]
fn test_event_buffer_limit() {
let options = MqttClientOptions::builder().max_event_count(1).build();
let mut engine = MqttEngine::new(options);
let data = vec![0xD0, 0x00, 0xD0, 0x00];
let events = engine.handle_incoming(&data);
assert_eq!(events.len(), 1);
match events[0] {
MqttEvent::PingResponse(_) => {}
_ => panic!("Expected PingResponse"),
}
let events2 = engine.handle_incoming(&[]);
assert_eq!(events2.len(), 1);
match events2[0] {
MqttEvent::PingResponse(_) => {}
_ => panic!("Expected second PingResponse"),
}
}
}