aboutsummaryrefslogtreecommitdiff
path: root/src/smtp_server.rs
diff options
context:
space:
mode:
authorMroik <mroik@delayed.space>2026-04-12 21:16:17 +0200
committerMroik <mroik@delayed.space>2026-04-13 06:56:12 +0200
commit784bf87d6fbf59194412c1dafeb56b3ed3946106 (patch)
tree0384213401f21c25996f8cbcff46126a2d04232e /src/smtp_server.rs
parente3854173d720fbbc4f98a7939ac667fcac007ec1 (diff)
Remove extra String alloc improving memory usage
Signed-off-by: Mroik <mroik@delayed.space>
Diffstat (limited to 'src/smtp_server.rs')
-rw-r--r--src/smtp_server.rs62
1 files changed, 30 insertions, 32 deletions
diff --git a/src/smtp_server.rs b/src/smtp_server.rs
index 9e313f4..83a1093 100644
--- a/src/smtp_server.rs
+++ b/src/smtp_server.rs
@@ -10,18 +10,18 @@ use tokio::{
use crate::model::Mail;
-const SERVER_NAME: &str = "";
-
pub struct SmtpServer {
listener: TcpListener,
running: bool,
+ server_name: String,
}
impl SmtpServer {
- pub async fn new(ip: [u8; 4], port: u16) -> Result<Self> {
+ pub async fn new(ip: [u8; 4], port: u16, server_name: &str) -> Result<Self> {
Ok(Self {
listener: TcpListener::bind((IpAddr::from(ip), port)).await?,
running: false,
+ server_name: String::from(server_name),
})
}
@@ -46,6 +46,7 @@ impl SmtpServer {
};
let session = SessionHandler {
addr,
+ server_name: self.server_name.clone(),
tx_processor: tx_processor.clone(),
state: SessionState::default(),
client_host: String::new(),
@@ -61,6 +62,7 @@ impl SmtpServer {
struct SessionHandler {
addr: SocketAddr,
+ server_name: String,
tx_processor: Sender<Mail>,
state: SessionState,
client_host: String,
@@ -76,11 +78,7 @@ impl SessionHandler {
let mut buffer = String::new();
writer
- .write_all(
- Reply::Ready(String::from(SERVER_NAME))
- .to_string()
- .as_bytes(),
- )
+ .write_all(Reply::Ready(&self.server_name).to_string().as_bytes())
.await?;
loop {
@@ -129,7 +127,7 @@ impl SessionHandler {
Ok(())
}
- async fn apply(&mut self, command: Command) -> Result<Reply> {
+ async fn apply(&mut self, command: Command<'_>) -> Result<Reply<'_>> {
match &command {
Command::HELO(hostname) => self.helo(hostname).await,
Command::MAIL(from) => self.mail(from).await,
@@ -140,17 +138,17 @@ impl SessionHandler {
}
/// HELO resets buffers
- async fn helo(&mut self, hostname: &str) -> Result<Reply> {
+ async fn helo(&mut self, hostname: &str) -> Result<Reply<'_>> {
self.client_host = String::from(hostname);
self.state = SessionState::Normal;
self.from = None;
self.to.clear();
- Ok(Reply::Completed(String::from(SERVER_NAME)))
+ Ok(Reply::Completed(&self.server_name))
}
/// TODO: Validate email address
/// MAIL resets buffers
- async fn mail(&mut self, from: &str) -> Result<Reply> {
+ async fn mail(&mut self, from: &str) -> Result<Reply<'_>> {
if from.is_empty() {
return Ok(Reply::InvalidParameter);
}
@@ -159,12 +157,12 @@ impl SessionHandler {
self.to.clear();
self.state = SessionState::MailTransaction;
- Ok(Reply::Completed(String::from("Ok")))
+ Ok(Reply::Completed("Ok"))
}
/// TODO: Validate email addresses
/// Only after having started a mail transaction
- async fn rcpt(&mut self, to: &str) -> Result<Reply> {
+ async fn rcpt(&mut self, to: &str) -> Result<Reply<'_>> {
if self.state != SessionState::MailTransaction {
return Ok(Reply::BadSequence);
}
@@ -174,11 +172,11 @@ impl SessionHandler {
}
self.to.push(String::from(to));
- Ok(Reply::Completed(String::from("Ok")))
+ Ok(Reply::Completed("Ok"))
}
/// Only after having started a mail transaction
- async fn start_data(&mut self) -> std::result::Result<Reply, anyhow::Error> {
+ async fn start_data(&mut self) -> Result<Reply<'_>> {
if self.state != SessionState::MailTransaction {
return Ok(Reply::BadSequence);
}
@@ -193,7 +191,7 @@ impl SessionHandler {
Ok(Reply::StartMailInput)
}
- async fn process_mail(&mut self) -> Result<Reply> {
+ async fn process_mail(&mut self) -> Result<Reply<'_>> {
if self.from.is_none() || self.to.is_empty() || self.data.trim().is_empty() {
return Ok(Reply::InvalidCommand);
}
@@ -211,10 +209,10 @@ impl SessionHandler {
self.to.clear();
self.data.clear();
- Ok(Reply::Completed(String::from("Ok")))
+ Ok(Reply::Completed("Ok"))
}
- async fn quit(&self) -> Result<Reply> {
+ async fn quit(&self) -> Result<Reply<'_>> {
Ok(Reply::EndTransmission)
}
}
@@ -229,9 +227,9 @@ enum SessionState {
}
#[derive(PartialEq)]
-enum Reply {
- Ready(String),
- Completed(String),
+enum Reply<'a> {
+ Ready(&'a str),
+ Completed(&'a str),
StartMailInput,
InvalidCommand,
InvalidParameter,
@@ -239,7 +237,7 @@ enum Reply {
EndTransmission,
}
-impl std::fmt::Display for Reply {
+impl std::fmt::Display for Reply<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Reply::Ready(hostname) => write!(f, "220 {}\r\n", hostname),
@@ -253,20 +251,20 @@ impl std::fmt::Display for Reply {
}
}
-enum Command {
- HELO(String),
- MAIL(String),
- RCPT(String),
+enum Command<'a> {
+ HELO(&'a str),
+ MAIL(&'a str),
+ RCPT(&'a str),
DATA,
QUIT,
}
-impl TryFrom<&str> for Command {
+impl<'a> TryFrom<&'a str> for Command<'a> {
type Error = anyhow::Error;
- fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ fn try_from(value: &'a str) -> std::result::Result<Self, Self::Error> {
if value.len() >= 5 && value.to_lowercase().starts_with("helo ") {
- return Ok(Command::HELO(String::from(&value[5..])));
+ return Ok(Command::HELO(&value[5..]));
}
if value.len() >= 11
@@ -274,13 +272,13 @@ impl TryFrom<&str> for Command {
&& value.contains('>')
{
let from = &value[11..value.find('>').unwrap()];
- return Ok(Command::MAIL(String::from(from)));
+ return Ok(Command::MAIL(from));
}
if value.len() >= 9 && value.to_lowercase().starts_with("rcpt to:<") && value.contains('>')
{
let to = &value[9..value.find('>').unwrap()];
- return Ok(Command::RCPT(String::from(to)));
+ return Ok(Command::RCPT(to));
}
if value.to_lowercase().starts_with("data") {
XMR address: 854DmXNrxULU3ZFJVs4Wc8PFhbq29RhqHhY8W6cdWrtFN3qmooKyyeYPcDzZTNRxphhJ5UzASQfAdEMwSteVqymk28aLhqj