Files
nym/nym-connect/src-tauri/src/tasks.rs
T
Jędrzej Stuczyński 97b01db23e Chore/more error macros (#2686)
* cleaned up MixProcessingError

* Added Error impl to (hopefully) all error enums in the codebase

* Replaced all occurences of error("{0}") with error(transparent)

* Changelog entry
2022-12-13 17:42:11 +00:00

166 lines
5.8 KiB
Rust

use client_core::config::{ClientCoreConfigTrait, GatewayEndpointConfig};
use futures::{channel::mpsc, StreamExt};
use std::sync::Arc;
use tap::TapFallible;
use tokio::sync::RwLock;
use config_common::NymConfig;
#[cfg(not(feature = "coconut"))]
use nym_socks5::client::NymClient as Socks5NymClient;
use nym_socks5::client::{config::Config as Socks5Config, Socks5ControlMessageSender};
use crate::{error::Result, 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,
) -> Result<(
Socks5ControlMessageSender,
task::StatusReceiver,
ExitStatusReceiver,
GatewayEndpointConfig,
)> {
log::info!("Loading config from file: {id}");
let config = Socks5Config::load_from_file(Some(id))
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
let used_gateway = config.get_base().get_gateway_endpoint().clone();
let socks5_client = Socks5NymClient::new(config);
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,
))
}
#[derive(Clone, serde::Serialize)]
struct Payload {
title: String,
message: String,
}
/// The status listener listens for non-exit status messages from the background socks5 proxy task.
pub fn start_status_listener(
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);
window
.emit(
"socks5-event",
Payload {
title: "SOCKS5 update".into(),
message: msg.to_string(),
},
)
.unwrap();
}
});
}
/// 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");
window
.emit(
"socks5-event",
Payload {
title: "SOCKS5 finished".into(),
message: "SOCKS5 task reported it has finished".into(),
},
)
.unwrap();
}
Ok(Socks5ExitStatusMessage::Failed(err)) => {
log::info!("SOCKS5 task reported error: {err}");
window
.emit(
"socks5-event",
Payload {
title: "SOCKS5 error".into(),
message: format!("SOCKS5 failed: {err}"),
},
)
.unwrap();
}
Err(_) => {
log::info!("SOCKS5 task appears to have stopped abruptly");
window
.emit(
"socks5-event",
Payload {
title: "SOCKS5 error".into(),
message: "SOCKS5 stopped abruptly. Please try reconnecting.".into(),
},
)
.unwrap();
}
}
let mut state_w = state.write().await;
state_w.mark_disconnected(&window);
});
}