use std::io;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::time::{timeout, Duration};
pub struct RawTestClient {
stream: TcpStream,
}
impl RawTestClient {
pub async fn connect(addr: &str) -> io::Result<Self> {
let stream = TcpStream::connect(addr).await?;
Ok(Self { stream })
}
pub async fn send_raw(&mut self, bytes: Vec<u8>) -> io::Result<()> {
self.stream.write_all(&bytes).await?;
self.stream.flush().await?;
Ok(())
}
pub async fn receive_raw(&mut self, max_size: usize, timeout_ms: u64) -> io::Result<Vec<u8>> {
let read_future = async {
let mut buffer = vec![0u8; max_size];
let n = self.stream.read(&mut buffer).await?;
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::ConnectionReset,
"Connection closed by broker",
));
}
buffer.truncate(n);
Ok(buffer)
};
match timeout(Duration::from_millis(timeout_ms), read_future).await {
Ok(result) => result,
Err(_) => Err(io::Error::new(
io::ErrorKind::TimedOut,
format!("Read timed out after {} ms", timeout_ms),
)),
}
}
pub async fn send_expect_disconnect(
&mut self,
bytes: Vec<u8>,
timeout_ms: u64,
) -> io::Result<()> {
self.send_raw(bytes).await?;
let result = timeout(Duration::from_millis(timeout_ms), async {
loop {
match self.receive_raw(1, 100).await {
Err(e) if e.kind() == io::ErrorKind::ConnectionReset => {
return Ok::<(), io::Error>(());
}
Err(e) if e.kind() == io::ErrorKind::TimedOut => {
continue;
}
Err(e) => return Err(e),
Ok(_) => {
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
}
})
.await;
match result {
Ok(Ok(())) => Ok(()),
Ok(Err(e)) => Err(e),
Err(_) => Err(io::Error::new(
io::ErrorKind::TimedOut,
format!("Broker did not disconnect within {} ms", timeout_ms),
)),
}
}
pub async fn send_and_receive(
&mut self,
bytes: Vec<u8>,
max_response_size: usize,
timeout_ms: u64,
) -> io::Result<Vec<u8>> {
self.send_raw(bytes).await?;
self.receive_raw(max_response_size, timeout_ms).await
}
pub async fn is_connected(&mut self) -> bool {
let mut buf = [0u8; 1];
matches!(
timeout(Duration::from_millis(10), self.stream.peek(&mut buf)).await,
Ok(Ok(_))
)
}
pub async fn close(mut self) -> io::Result<()> {
self.stream.shutdown().await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
#[ignore] async fn test_raw_client_connect() {
let result = RawTestClient::connect("broker.emqx.io:1883").await;
assert!(result.is_ok());
}
#[tokio::test]
#[ignore] async fn test_raw_client_send_receive() {
use crate::mqtt_serde::control_packet::MqttPacket;
use crate::mqtt_serde::mqttv3::connectv3::MqttConnect as MqttConnect3;
let mut client = RawTestClient::connect("broker.emqx.io:1883").await.unwrap();
let connect = MqttConnect3::new(
"raw_test_client".to_string(),
60, true, );
let connect_bytes = MqttPacket::Connect3(connect).to_bytes().unwrap();
client.send_raw(connect_bytes).await.unwrap();
let connack = client.receive_raw(8192, 5000).await.unwrap();
assert!(connack.len() >= 4); assert_eq!(connack[0] & 0xF0, 0x20);
client.send_raw(vec![0xC0, 0x00]).await.unwrap();
let response = client.receive_raw(8192, 5000).await.unwrap();
assert_eq!(response, vec![0xD0, 0x00]);
}
}