Client state refactoring

This commit is contained in:
Alexis Mousset
2015-02-28 18:30:45 +01:00
parent 4bd27f2ca4
commit 159bc9c0b5

View File

@@ -34,22 +34,34 @@ pub mod connecter;
pub mod stream;
/// Represents the configuration of a client
#[derive(Clone,Debug)]
#[derive(Debug)]
pub struct Configuration {
/// Maximum connection reuse
///
/// Zero means no limitation
pub connection_reuse_count_limit: usize,
pub connection_reuse_count_limit: u16,
/// Enable connection reuse
pub enable_connection_reuse: bool,
/// Maximum recipients
pub destination_recipient_limit: usize,
/// Maximum line length
pub line_length_limit: usize,
pub line_length_limit: u16,
/// Name sent during HELO or EHLO
pub hello_name: String,
}
/// Represents the state of a client
#[derive(Debug)]
pub struct State {
/// Transaction state, to check the sequence of commands
pub transaction_state: TransactionState,
/// Panic state
pub panic: bool,
/// Connection reuse counter
pub connection_reuse_count: u16,
/// Current message id
pub current_message: Option<Uuid>,
}
/// Structure that implements the SMTP client
pub struct Client<S = TcpStream> {
/// TCP stream between client and server
@@ -60,14 +72,8 @@ pub struct Client<S = TcpStream> {
/// Information about the server
/// Value is None before HELO/EHLO
server_info: Option<ServerInfo>,
/// Transaction state, to check the sequence of commands
state: TransactionState,
/// Panic state
panic: bool,
/// Connection reuse counter
connection_reuse_count: usize,
/// Current message id
current_message: Option<Uuid>,
/// Client variable states
state: State,
/// Configuration of the client
configuration: Configuration,
}
@@ -83,8 +89,8 @@ macro_rules! try_smtp (
macro_rules! close_and_return_err (
($err: expr, $client: ident) => ({
if !$client.panic {
$client.panic = true;
if !$client.state.panic {
$client.state.panic = true;
$client.close();
}
return Err(FromError::from_error($err))
@@ -93,7 +99,7 @@ macro_rules! close_and_return_err (
macro_rules! check_command_sequence (
($command: ident $client: ident) => ({
if !$client.state.is_allowed(&$command) {
if !$client.state.transaction_state.is_allowed(&$command) {
close_and_return_err!(
Response{code: 503, message: Some("Bad sequence of commands".to_string())}, $client
);
@@ -110,17 +116,18 @@ impl<S = TcpStream> Client<S> {
stream: None,
server_addr: addr.to_socket_addr().unwrap(),
server_info: None,
state: TransactionState::new(),
panic: false,
connection_reuse_count: 0,
current_message: None,
configuration: Configuration {
connection_reuse_count_limit: 100,
enable_connection_reuse: false,
line_length_limit: 998,
destination_recipient_limit: 100,
hello_name: "localhost".to_string(),
}
},
state: State {
transaction_state: TransactionState::new(),
panic: false,
connection_reuse_count: 0,
current_message: None,
},
}
}
@@ -142,14 +149,9 @@ impl<S = TcpStream> Client<S> {
}
/// Set the maximum number of emails sent using one connection
pub fn set_connection_reuse_count_limit(&mut self, count: usize) {
pub fn set_connection_reuse_count_limit(&mut self, count: u16) {
self.configuration.connection_reuse_count_limit = count
}
/// Set the client configuration
pub fn set_configuration(&mut self, configuration: Configuration) {
self.configuration = configuration
}
}
impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
@@ -165,18 +167,18 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
// Reset the client state
self.stream = None;
self.state = TransactionState::new();
self.state.transaction_state = TransactionState::new();
self.server_info = None;
self.panic = false;
self.connection_reuse_count = 0;
self.current_message = None;
self.state.panic = false;
self.state.connection_reuse_count = 0;
self.state.current_message = None;
}
/// Sends an email
pub fn send<T: SendableEmail>(&mut self, mut email: T) -> SmtpResult {
// If there is a usable connection, test if the server answers and hello has been sent
if self.connection_reuse_count > 0 {
if self.state.connection_reuse_count > 0 {
if self.noop().is_err() {
self.reset();
}
@@ -202,8 +204,8 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
debug!("server {}", self.server_info.as_ref().unwrap());
}
self.current_message = Some(Uuid::new_v4());
email.set_message_id(format!("<{}@{}>", self.current_message.as_ref().unwrap(),
self.state.current_message = Some(Uuid::new_v4());
email.set_message_id(format!("<{}@{}>", self.state.current_message.as_ref().unwrap(),
self.configuration.hello_name.clone()));
let from_address = email.from_address();
@@ -248,7 +250,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
let result = try!(self.stream.as_mut().unwrap().get_reply());
let checked_result = try!(command.test_success(result));
self.state = self.state.next_state(&command).unwrap();
self.state.transaction_state = self.state.transaction_state.next_state(&command).unwrap();
Ok(checked_result)
}
@@ -285,7 +287,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
});
let checked_result = try!(command.test_success(result));
self.state = self.state.next_state(&command).unwrap();
self.state.transaction_state = self.state.transaction_state.next_state(&command).unwrap();
Ok(checked_result)
}
@@ -326,8 +328,8 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
pub fn mail(&mut self, from_address: &str) -> SmtpResult {
// Generate an ID for the logs if None was provided
if self.current_message.is_none() {
self.current_message = Some(Uuid::new_v4());
if self.state.current_message.is_none() {
self.state.current_message = Some(Uuid::new_v4());
}
let server_info = match self.server_info.clone() {
@@ -350,7 +352,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
if result.is_ok() {
// Log the mail command
info!("{}: from=<{}>", self.current_message.as_ref().unwrap(), from_address);
info!("{}: from=<{}>", self.state.current_message.as_ref().unwrap(), from_address);
}
result
@@ -364,7 +366,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
if result.is_ok() {
// Log the rcpt command
info!("{}: to=<{}>", self.current_message.as_ref().unwrap(), to_address);
info!("{}: to=<{}>", self.state.current_message.as_ref().unwrap(), to_address);
}
result
@@ -397,17 +399,17 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
if result.is_ok() {
// Increment the connection reuse counter
self.connection_reuse_count = self.connection_reuse_count + 1;
self.state.connection_reuse_count = self.state.connection_reuse_count + 1;
// Log the message
info!("{}: conn_use={}, size={}, status=sent ({})", self.current_message.as_ref().unwrap(),
self.connection_reuse_count, message_content.len(), result.as_ref().ok().unwrap());
info!("{}: conn_use={}, size={}, status=sent ({})", self.state.current_message.as_ref().unwrap(),
self.state.connection_reuse_count, message_content.len(), result.as_ref().ok().unwrap());
}
self.current_message = None;
self.state.current_message = None;
// Test if we can reuse the existing connection
if (!self.configuration.enable_connection_reuse) ||
(self.connection_reuse_count == self.configuration.connection_reuse_count_limit) {
(self.state.connection_reuse_count == self.configuration.connection_reuse_count_limit) {
self.reset();
}