Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions src/automatic_relay_management.rs → src/autorelay.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
//! # Automatic relay handling (experimental, still in development)
//!
//! Chatmail relays create an account on first login,
//! so a profile can add further transports on its own without user interaction.
//! Candidate hosts come from the `relay_candidates` table,
//! which migrations seed with a list of known chatmail relays.
//!
//! Status of implementation:
//! Additions are attempted right before going into IMAP IDLE,
//! i.e. only while connected and with nothing more important to do,
//! and only if a UI opted in via [`Config::Autorelay`].
//! Once a profile has reached `NUM_TRANSPORTS_TARGET` transports,
//! [`Config::AutorelayFinished`] is set and nothing is ever added again,
//! so deleting a transport later does not pull in a replacement.

use std::pin::Pin;

use anyhow::Result;
Expand Down Expand Up @@ -46,9 +61,7 @@ async fn maybe_add_additional_relays_inner(context: &Context, skip_network: bool
// Housekeeping or automatic relay management is already running in another thread, do nothing.
return Ok(false);
};
let last_timestamp = context
.get_config_i64(Config::LastAutomaticRelayManagement)
.await?;
let last_timestamp = context.get_config_i64(Config::LastAutorelay).await?;
if last_timestamp > now {
warn!(
context,
Expand All @@ -57,33 +70,24 @@ async fn maybe_add_additional_relays_inner(context: &Context, skip_network: bool
} else if last_timestamp > now.saturating_sub(AUTOMATIC_ADDITION_DEBOUNCE_SECONDS) {
return Ok(false);
}
if !context
.get_config_bool(Config::AutomaticRelayManagement)
.await?
{
if !context.get_config_bool(Config::Autorelay).await? {
return Ok(false);
}
if context
.get_config_bool(Config::AutomaticRelayManagementFinished)
.await?
{
if context.get_config_bool(Config::AutorelayFinished).await? {
return Ok(false);
}
// Set the config at the beginning to avoid endless loops.
// Race conditions are not a concern because we locked the mutex.
context
.set_config_internal(Config::LastAutomaticRelayManagement, Some(&now.to_string()))
.set_config_internal(Config::LastAutorelay, Some(&now.to_string()))
.await?;

let mut relay_added = false;
// Using `for` instead of `while` to prevent infinite loop
for _ in 0..NUM_TRANSPORTS_TARGET {
if context.count_transports().await? >= NUM_TRANSPORTS_TARGET {
context
.set_config_internal(
Config::AutomaticRelayManagementFinished,
config::from_bool(true),
)
.set_config_internal(Config::AutorelayFinished, config::from_bool(true))
.await?;

return Ok(relay_added);
Expand Down Expand Up @@ -175,4 +179,4 @@ pub(crate) fn login_param_from_host(host: &str) -> EnteredLoginParam {
}

#[cfg(test)]
mod automatic_relay_management_tests;
mod autorelay_tests;
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,17 @@ async fn test_load_relay_candidates_multiple() -> Result<()> {
Ok(())
}

async fn assert_automatic_relay_management_does_nothing(t: &TestContext) {
async fn assert_autorelay_does_nothing(t: &TestContext) {
let transports_before = t.count_transports().await.unwrap();
let config_before = t
.get_config_i64(Config::LastAutomaticRelayManagement)
.await
.unwrap();
let config_before = t.get_config_i64(Config::LastAutorelay).await.unwrap();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renaming configs is fine IMHO. No UI uses them, and the commit marks the refactor as breaking.


let skip_network = false; // No need to skip network, nothing is supposed to happen
let relay_added = maybe_add_additional_relays_inner(t, skip_network)
.await
.unwrap();
assert_eq!(relay_added, false);

let config_after = t
.get_config_i64(Config::LastAutomaticRelayManagement)
.await
.unwrap();
let config_after = t.get_config_i64(Config::LastAutorelay).await.unwrap();
let transports_after = t.count_transports().await.unwrap();

assert_eq!(config_after, config_before);
Expand All @@ -105,7 +99,7 @@ async fn test_maybe_add_additional_relays_mutex_held() -> Result<()> {
// already running housekeeping or relay management.
let _lock = t.background_task_mutex.lock().await;

assert_automatic_relay_management_does_nothing(t).await;
assert_autorelay_does_nothing(t).await;

Ok(())
}
Expand All @@ -117,13 +111,10 @@ async fn test_maybe_add_additional_relays_debounce() -> Result<()> {
let some_seconds_ago = time() - 10;

// Pretend automatic relay management just ran.
t.set_config_internal(
Config::LastAutomaticRelayManagement,
Some(&some_seconds_ago.to_string()),
)
.await?;
t.set_config_internal(Config::LastAutorelay, Some(&some_seconds_ago.to_string()))
.await?;

assert_automatic_relay_management_does_nothing(t).await;
assert_autorelay_does_nothing(t).await;

Ok(())
}
Expand All @@ -132,7 +123,7 @@ async fn test_maybe_add_additional_relays_debounce() -> Result<()> {
async fn test_maybe_add_additional_relays_disabled() {
// By default, automatic relay management is disabled:
let t = &TestContext::new_alice().await;
assert_automatic_relay_management_does_nothing(t).await;
assert_autorelay_does_nothing(t).await;
}

/// Runs maybe_add_additional_relays_inner(), then deletes one of the transports.
Expand All @@ -158,11 +149,8 @@ async fn test_maybe_add_additional_relays_does_nothing_after_finishing_once() ->
let transports_count = t.count_transports().await?;
assert_eq!(transports_count, NUM_TRANSPORTS_TARGET - 1);

assert!(
t.get_config_bool(Config::AutomaticRelayManagementFinished)
.await?
);
assert_automatic_relay_management_does_nothing(t).await;
assert!(t.get_config_bool(Config::AutorelayFinished).await?);
assert_autorelay_does_nothing(t).await;

Ok(())
}
Expand All @@ -187,9 +175,7 @@ async fn test_maybe_add_additional_relays_add_one() -> Result<()> {
let relay_added = maybe_add_additional_relays_inner(t, skip_network).await?;
assert!(relay_added);

let config_after = t
.get_config_i64(Config::LastAutomaticRelayManagement)
.await?;
let config_after = t.get_config_i64(Config::LastAutorelay).await?;
assert!(config_after >= now);

let transports_after = t.count_transports().await?;
Expand Down Expand Up @@ -218,9 +204,7 @@ async fn test_maybe_add_additional_relays_add_multiple() -> Result<()> {
let relay_added = maybe_add_additional_relays_inner(t, skip_network).await?;
assert!(relay_added);

let config_after = t
.get_config_i64(Config::LastAutomaticRelayManagement)
.await?;
let config_after = t.get_config_i64(Config::LastAutorelay).await?;
assert!(config_after >= now);

let transports_after = t.count_transports().await?;
Expand Down Expand Up @@ -253,9 +237,7 @@ async fn test_maybe_add_additional_relays_failure() -> Result<()> {
assert_eq!(relay_added, false);

// The config is still updated:
let config_after = t
.get_config_i64(Config::LastAutomaticRelayManagement)
.await?;
let config_after = t.get_config_i64(Config::LastAutorelay).await?;
assert!(config_after >= now);

let transports_after = t.count_transports().await?;
Expand Down Expand Up @@ -286,7 +268,7 @@ async fn test_maybe_add_additional_relays_failure() -> Result<()> {

async fn enable_config(context: &Context) {
context
.set_config_bool(Config::AutomaticRelayManagement, true)
.set_config_bool(Config::Autorelay, true)
.await
.unwrap();
}
6 changes: 3 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,13 @@ pub enum Config {
LastCantDecryptOutgoingMsgs,

/// Timestamp of the last time automatic relay management was run
LastAutomaticRelayManagement,
LastAutorelay,

/// Whether to automatically add/remove transports
AutomaticRelayManagement,
Autorelay,

/// Whether automatic relay management successfully added the desired number of relays
AutomaticRelayManagementFinished,
AutorelayFinished,

/// Whether to avoid using IMAP IDLE even if the server supports it.
///
Expand Down
14 changes: 6 additions & 8 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,20 +1050,18 @@ impl Context {
.to_string(),
);
res.insert(
"last_automatic_relay_management",
self.get_config_i64(Config::LastAutomaticRelayManagement)
"last_autorelay",
self.get_config_i64(Config::LastAutorelay)
.await?
.to_string(),
);
res.insert(
"automatic_relay_management",
self.get_config_bool(Config::AutomaticRelayManagement)
.await?
.to_string(),
"autorelay",
self.get_config_bool(Config::Autorelay).await?.to_string(),
);
res.insert(
"automatic_relay_management_finished",
self.get_config_bool(Config::AutomaticRelayManagementFinished)
"autorelay_finished",
self.get_config_bool(Config::AutorelayFinished)
.await?
.to_string(),
);
Expand Down
6 changes: 3 additions & 3 deletions src/imap/idle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ impl Session {

// we try to add additional relays right before going into IDLE mode,
// because we are connected and don't have anything important to do.
tokio::task::spawn(
crate::automatic_relay_management::maybe_add_additional_relays(context.clone()),
);
tokio::task::spawn(crate::autorelay::maybe_add_additional_relays(
context.clone(),
));

let mut handle = self.inner.idle();
handle
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub use events::*;

mod aheader;
pub mod appversions;
mod automatic_relay_management;
mod autorelay;
pub mod blob;
pub mod calls;
pub mod chat;
Expand Down
2 changes: 1 addition & 1 deletion src/qr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use deltachat_contact_tools::{ContactAddress, addr_normalize, may_be_valid_addr}
use percent_encoding::{NON_ALPHANUMERIC, percent_decode_str, percent_encode};
use serde::Deserialize;

use crate::automatic_relay_management::login_param_from_host;
use crate::autorelay::login_param_from_host;
use crate::config::Config;
use crate::contact::{Contact, ContactId, Origin};
use crate::context::Context;
Expand Down