use super::*;
impl Client {
fn empty_message(event: Event) -> Message {
Message::from_raw(event, unsafe { std::mem::zeroed::<ffi::TTMessage>() })
}
pub(super) fn handle_connect_recovery(&self) {
let params = {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
if !auto.enabled {
return;
}
let params: super::super::connection::ConnectParamsOwned = match auto.params.as_ref() {
Some(params) => params.clone(),
None => return,
};
let handler: &mut super::super::connection::ReconnectHandler =
match auto.handler.as_mut() {
Some(handler) => handler,
None => return,
};
if handler.can_attempt() {
params
} else if handler.exhausted() {
let attempts = handler.attempts();
drop(auto);
let failed_event = Event::ReconnectFailed { attempts };
let msg = Self::empty_message(failed_event);
self.invoke_hooks(failed_event, &msg);
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
auto.enabled = false;
auto.handler = None;
auto.login_handler = None;
auto.join_handler = None;
return;
} else {
return;
}
};
let _ = self.disconnect();
if self.has_connection_flags() {
return;
}
let (attempt, delay) = {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
if !auto.enabled {
return;
}
let handler = match auto.handler.as_mut() {
Some(handler) => handler,
None => return,
};
if !handler.can_attempt() {
return;
}
let attempt = handler.attempts() + 1;
let delay = handler.current_delay();
handler.record_attempt();
auto.force_disconnect = false;
auto.login_gave_up = false;
auto.join_gave_up = false;
auto.recovery_completed = false;
(attempt, delay)
};
let before_event = Event::BeforeReconnect { attempt, delay };
let msg = Self::empty_message(before_event);
self.invoke_hooks(before_event, &msg);
self.invoke_hooks(Event::Reconnecting { attempt, delay }, &msg);
let _ = self.connect(¶ms.host, params.tcp, params.udp, params.encrypted);
}
pub(super) fn handle_login_recovery(&self) {
let (params, attempt, delay, gave_up_now) = {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
if !auto.enabled || auto.login_gave_up {
return;
}
let params = match auto.login.as_ref() {
Some(params) => params.clone(),
None => return,
};
let login_config = auto.workflow.login.clone();
let handler = auto.login_handler.get_or_insert_with(|| {
super::super::connection::ReconnectHandler::new(login_config)
});
if handler.can_attempt() {
let attempt = handler.attempts() + 1;
let delay = handler.current_delay();
handler.record_attempt();
(params, attempt, delay, false)
} else if handler.exhausted() {
let attempts = handler.attempts();
let delay = handler.current_delay();
auto.login_gave_up = true;
(params, attempts, delay, true)
} else {
return;
}
};
if gave_up_now {
let event = Event::AutoLoginFailed { attempts: attempt };
let msg = Self::empty_message(event);
self.invoke_hooks(event, &msg);
return;
}
let before_event = Event::BeforeAutoLogin { attempt, delay };
let msg = Self::empty_message(before_event);
self.invoke_hooks(before_event, &msg);
let cmd_id = self.login(
¶ms.nickname,
¶ms.username,
¶ms.password,
¶ms.client_name,
);
if cmd_id <= 0 {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
auto.clear_login_phase();
drop(auto);
self.set_connection_state(ConnectionState::Connected);
let failed = Event::AutoLoginFailed { attempts: attempt };
let msg = Self::empty_message(failed);
self.invoke_hooks(failed, &msg);
} else {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
auto.pending_login_cmd = Some(cmd_id);
}
}
pub(super) fn handle_join_recovery(&self) {
let (channel, password, attempt, delay, gave_up_now) = {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
if !auto.enabled || auto.join_gave_up {
return;
}
let channel = match auto.last_channel {
Some(channel) => channel,
None => return,
};
let password = auto.last_channel_password.clone();
let join_config = auto.workflow.join.clone();
let handler = auto.join_handler.get_or_insert_with(|| {
super::super::connection::ReconnectHandler::new(join_config)
});
if handler.can_attempt() {
let attempt = handler.attempts() + 1;
let delay = handler.current_delay();
handler.record_attempt();
(channel, password, attempt, delay, false)
} else if handler.exhausted() {
let attempts = handler.attempts();
let delay = handler.current_delay();
auto.join_gave_up = true;
(channel, password, attempts, delay, true)
} else {
return;
}
};
if gave_up_now {
let event = Event::AutoJoinFailed { attempts: attempt };
let msg = Self::empty_message(event);
self.invoke_hooks(event, &msg);
return;
}
let before_event = Event::BeforeAutoJoin { attempt, delay };
let msg = Self::empty_message(before_event);
self.invoke_hooks(before_event, &msg);
let cmd_id = self.join_channel(channel, password.as_deref().unwrap_or(""));
if cmd_id <= 0 {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
auto.clear_join_phase();
drop(auto);
self.set_connection_state(ConnectionState::LoggedIn);
let failed = Event::AutoJoinFailed { attempts: attempt };
let msg = Self::empty_message(failed);
self.invoke_hooks(failed, &msg);
} else {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
auto.pending_join_cmd = Some(cmd_id);
}
}
pub(super) fn handle_recovery_completed(&self) {
let (reconnect_attempts, login_attempts, join_attempts) = {
let mut auto = self
.auto_reconnect
.lock()
.unwrap_or_else(|e| e.into_inner());
if !auto.enabled || auto.recovery_completed {
return;
}
auto.recovery_completed = true;
auto.login_gave_up = false;
auto.join_gave_up = false;
let reconnect_attempts = auto
.handler
.as_ref()
.map_or(0, |handler| handler.attempts());
let login_attempts = auto
.login_handler
.as_ref()
.map_or(0, |handler| handler.attempts());
let join_attempts = auto
.join_handler
.as_ref()
.map_or(0, |handler| handler.attempts());
if let Some(handler) = auto.login_handler.as_mut() {
handler.reset();
}
if let Some(handler) = auto.join_handler.as_mut() {
handler.reset();
}
(reconnect_attempts, login_attempts, join_attempts)
};
let event = Event::AutoRecoverCompleted {
reconnect_attempts,
login_attempts,
join_attempts,
};
let msg = Self::empty_message(event);
self.invoke_hooks(event, &msg);
}
}