Skip to main content

rusmes_loadtest/protocols/
smtp.rs

1//! SMTP client for load testing
2
3use anyhow::{Context, Result};
4use std::time::Duration;
5use tokio::io::{AsyncReadExt, AsyncWriteExt};
6use tokio::net::TcpStream;
7use tokio::time::timeout;
8
9/// SMTP client for load testing
10pub struct SmtpClient;
11
12impl SmtpClient {
13    /// Send a message via SMTP
14    pub async fn send_message(host: &str, port: u16, message: &str) -> Result<usize> {
15        let addr = format!("{}:{}", host, port);
16
17        let connect_timeout = Duration::from_secs(5);
18        let stream = timeout(connect_timeout, TcpStream::connect(&addr))
19            .await
20            .context("Connection timeout")?
21            .context("Failed to connect")?;
22
23        let mut stream = stream;
24        let mut buffer = vec![0u8; 4096];
25
26        // Read greeting
27        let n = timeout(Duration::from_secs(5), stream.read(&mut buffer))
28            .await
29            .context("Read timeout")?
30            .context("Failed to read greeting")?;
31
32        let mut bytes_received = n;
33
34        // EHLO
35        stream.write_all(b"EHLO loadtest.example.com\r\n").await?;
36        let n = stream.read(&mut buffer).await?;
37        bytes_received += n;
38
39        // MAIL FROM
40        stream
41            .write_all(b"MAIL FROM:<loadtest@example.com>\r\n")
42            .await?;
43        let n = stream.read(&mut buffer).await?;
44        bytes_received += n;
45
46        // RCPT TO
47        stream.write_all(b"RCPT TO:<user@example.com>\r\n").await?;
48        let n = stream.read(&mut buffer).await?;
49        bytes_received += n;
50
51        // DATA
52        stream.write_all(b"DATA\r\n").await?;
53        let n = stream.read(&mut buffer).await?;
54        bytes_received += n;
55
56        // Message
57        stream.write_all(message.as_bytes()).await?;
58        stream.write_all(b"\r\n.\r\n").await?;
59        let n = stream.read(&mut buffer).await?;
60        bytes_received += n;
61
62        // QUIT
63        stream.write_all(b"QUIT\r\n").await?;
64        let _ = stream.read(&mut buffer).await;
65
66        Ok(bytes_received)
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[tokio::test]
75    async fn test_smtp_client_timeout() {
76        // Test connection timeout to non-existent host
77        let result = SmtpClient::send_message("192.0.2.1", 25, "test").await;
78        assert!(result.is_err());
79    }
80}