Files
nym/nym-connect-android/src-tauri/src/tasks.rs
T
Pierre Dommerc 3e6188ed13 nym-connect-android initial version (#2907)
* feat(nym-connect): add android support

* fix(nym-connect): android linker issue with sqlite3

get rid of sqlite refs as temporary workaround

* fix(nc): fix index.ts (post rebase)

* feat(nc-android): wip

* hack in config removal of read/write

* fix(nc-android): remove more fs read/write calls

* wip

* chore: remove debug comments

* Register gateway

* client-core: remove unneeded changes

* build: revert crate name change

* refactor(socks5-client): add feature mobile

* refactor(gateway-client): rename mobile feature

* socks5: restore default_root_directory

* client-core: further simplifications

* get_config_file_location just return error

* fix(nc-mobile): fix ui mobile

* socks5: minor tweak to default_root_directory

* remove unneeded changes

* nym-connect build fixes

* Use default feature for normal credential storage

* rustfmt

* rustfmt: nym-connect

* restore Cargo.toml

* Remove --all-features from workflow

* Remove some unused use

* Remove two move --all-features from build workflow

* Allow unused

* Add continue-on-error

* another clippy --all-features remove

* remove --all-features from clippy nightly

* fix(nc-mobile): frontend code errors

* feat: restore nc, move mobile under its own dir

* fix(nc-android): build

* fix(nc-android): lint errors

Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2023-01-26 14:17:29 +01:00

206 lines
7.2 KiB
Rust

use client_core::{
client::key_manager::KeyManager,
config::{ClientCoreConfigTrait, GatewayEndpointConfig},
error::ClientCoreStatusMessage,
};
use futures::{channel::mpsc, StreamExt};
use std::sync::Arc;
use tap::TapFallible;
use task::manager::TaskStatus;
use tokio::sync::RwLock;
use config_common::NymConfig;
use nym_socks5::client::NymClient as Socks5NymClient;
use nym_socks5::client::{config::Config as Socks5Config, Socks5ControlMessageSender};
use crate::{
config::Config,
error::Result,
events::{self, emit_event, emit_status_event},
models::{ConnectionStatusKind, ConnectivityTestResult},
operations,
state::State,
};
pub type ExitStatusReceiver = futures::channel::oneshot::Receiver<Socks5ExitStatusMessage>;
/// Status messages sent by the SOCKS5 client task to the main tauri task.
#[derive(Debug)]
pub enum Socks5ExitStatusMessage {
/// The SOCKS5 task successfully stopped
Stopped,
/// The SOCKS5 task failed to start
Failed(Box<dyn std::error::Error + Send>),
}
/// The main SOCKS5 client task. It loads the configuration from file determined by the `id`.
pub fn start_nym_socks5_client(
id: &str,
config: Config,
keys: KeyManager,
) -> Result<(
Socks5ControlMessageSender,
task::StatusReceiver,
ExitStatusReceiver,
GatewayEndpointConfig,
)> {
log::info!("Loading config from file: {id}");
let used_gateway = config.get_base().get_gateway_endpoint().clone();
let socks5_client = Socks5NymClient::new_with_keys(config.socks5, Some(keys));
log::info!("Starting socks5 client");
// Channel to send control messages to the socks5 client
let (socks5_ctrl_tx, socks5_ctrl_rx) = mpsc::unbounded();
// Channel to send status update messages from the background socks5 task to the frontend.
let (socks5_status_tx, socks5_status_rx) = mpsc::channel(128);
// Channel to signal back to the main task when the socks5 client finishes, and why
let (socks5_exit_tx, socks5_exit_rx) = futures::channel::oneshot::channel();
// Spawn a separate runtime for the socks5 client so we can forcefully terminate.
// Once we can gracefully shutdown the socks5 client we can get rid of this.
// The status channel is used to both get the state of the task, and if it's closed, to check
// for panic.
std::thread::spawn(|| {
let result = tokio::runtime::Runtime::new()
.expect("Failed to create runtime for SOCKS5 client")
.block_on(async move {
socks5_client
.run_and_listen(socks5_ctrl_rx, socks5_status_tx)
.await
});
if let Err(err) = result {
log::error!("SOCKS5 proxy failed: {err}");
socks5_exit_tx
.send(Socks5ExitStatusMessage::Failed(err))
.expect("Failed to send status message back to main task");
return;
}
log::info!("SOCKS5 task finished");
socks5_exit_tx
.send(Socks5ExitStatusMessage::Stopped)
.expect("Failed to send status message back to main task");
});
Ok((
socks5_ctrl_tx,
socks5_status_rx,
socks5_exit_rx,
used_gateway,
))
}
pub fn start_connection_check(state: Arc<RwLock<State>>, window: tauri::Window<tauri::Wry>) {
log::debug!("Starting connection check handler");
tokio::spawn(async move {
if state.read().await.get_status() != ConnectionStatusKind::Connected {
log::error!("SOCKS5 connection status check failed: not connected");
return;
}
log::info!("Running connection health check");
if operations::connection::health_check::run_health_check().await {
state
.write()
.await
.set_connectivity_test_result(ConnectivityTestResult::Success);
emit_event(
"socks5-connection-success-event",
"SOCKS5 success",
"SOCKS5 connection health check successful",
&window,
);
} else if state.read().await.get_status() == ConnectionStatusKind::Connected {
state
.write()
.await
.set_connectivity_test_result(ConnectivityTestResult::Fail);
log::error!("SOCKS5 connection health check failed");
emit_event(
"socks5-connection-fail-event",
"SOCKS5 error",
"SOCKS5 connection health check failed",
&window,
);
} else {
log::debug!("SOCKS5 connection status check cancelled: not connected");
}
log::debug!("Connection check handler exiting");
});
}
/// The status listener listens for non-exit status messages from the background socks5 proxy task.
pub fn start_status_listener(
state: Arc<RwLock<State>>,
window: tauri::Window<tauri::Wry>,
mut msg_receiver: task::StatusReceiver,
) {
log::info!("Starting status listener");
tokio::spawn(async move {
while let Some(msg) = msg_receiver.next().await {
log::info!("SOCKS5 proxy sent status message: {}", msg);
if let Some(task_status) = msg.downcast_ref::<TaskStatus>() {
events::handle_task_status(task_status, &state, &window).await;
} else if let Some(client_status_message) =
msg.downcast_ref::<ClientCoreStatusMessage>()
{
events::handle_client_status_message(client_status_message, &state, &window).await;
} else {
emit_status_event("socks5-status-event", &msg, &window);
}
}
log::info!("Status listener exiting");
});
}
/// The disconnect listener listens to the channel setup between the socks5 proxy task and the main
/// tauri task. Primarily it listens for shutdown messages, and updates the state accordingly.
pub fn start_disconnect_listener(
state: Arc<RwLock<State>>,
window: tauri::Window<tauri::Wry>,
exit_status_receiver: ExitStatusReceiver,
) {
log::trace!("Starting disconnect listener");
tokio::spawn(async move {
match exit_status_receiver.await {
Ok(Socks5ExitStatusMessage::Stopped) => {
log::info!("SOCKS5 task reported it has finished");
emit_event(
"socks5-event",
"SOCKS5 finished",
"SOCKS5 task reported it has finished",
&window,
);
}
Ok(Socks5ExitStatusMessage::Failed(err)) => {
log::info!("SOCKS5 task reported error: {err}");
emit_event(
"socks5-event",
"SOCKS5 error",
&format!("SOCKS5 failed: {err}"),
&window,
);
}
Err(_) => {
log::info!("SOCKS5 task appears to have stopped abruptly");
emit_event(
"socks5-event",
"SOCKS5 error",
"SOCKS5 stopped abruptly. Please try reconnecting.",
&window,
);
}
}
let mut state_w = state.write().await;
state_w.mark_disconnected(&window);
});
}