Files
firezone/rust/gui-client/src-common/src/ipc.rs
Reactor Scram 05acdd5a03 fix(gui-client): defer GUI exit until tunnel closes (#6874)
Closes #6873

The issue seems to be a race between flushing Sentry in the GUI process
and shutting down Firezone in the tunnel daemon (IPC service).

With this change, the GUI waits to hear `DisconnectedGracefully` from
the tunnel daemon before flushing Sentry, and the issue is prevented.

Adding the new state and new IPC message required small changes in
several places

---------

Signed-off-by: Reactor Scram <ReactorScram@users.noreply.github.com>
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
2024-10-01 16:01:43 +00:00

95 lines
2.5 KiB
Rust

use anyhow::{Context as _, Result};
use firezone_headless_client::{
ipc::{self, Error},
IpcClientMsg, IpcServerMsg,
};
use futures::{SinkExt, StreamExt};
use secrecy::{ExposeSecret, SecretString};
use std::net::IpAddr;
pub enum Event {
Closed,
Message(IpcServerMsg),
ReadFailed(anyhow::Error),
}
pub struct Client {
task: tokio::task::JoinHandle<Result<()>>,
// Needed temporarily to avoid a big refactor. We can remove this in the future.
tx: ipc::ClientWrite,
}
impl Drop for Client {
// Might drop in-flight IPC messages
fn drop(&mut self) {
self.task.abort();
}
}
impl Client {
pub async fn new(ctlr_tx: tokio::sync::mpsc::Sender<Event>) -> Result<Self> {
tracing::debug!(
client_pid = std::process::id(),
"Connecting to IPC service..."
);
let (mut rx, tx) = ipc::connect_to_service(ipc::ServiceId::Prod).await?;
let task = tokio::task::spawn(async move {
while let Some(result) = rx.next().await {
let event = match result {
Ok(msg) => Event::Message(msg),
Err(e) => Event::ReadFailed(e),
};
ctlr_tx.send(event).await?;
}
ctlr_tx.send(Event::Closed).await?;
Ok(())
});
Ok(Self { task, tx })
}
pub async fn disconnect_from_ipc(mut self) -> Result<()> {
self.task.abort();
self.tx.close().await?;
Ok(())
}
pub async fn send_msg(&mut self, msg: &IpcClientMsg) -> Result<()> {
self.tx
.send(msg)
.await
.context("Couldn't send IPC message")?;
Ok(())
}
pub async fn connect_to_firezone(
&mut self,
api_url: &str,
token: SecretString,
) -> Result<(), Error> {
let token = token.expose_secret().clone();
self.send_msg(&IpcClientMsg::Connect {
api_url: api_url.to_string(),
token,
})
.await
.context("Couldn't send Connect message")
.map_err(Error::Other)?;
Ok(())
}
pub async fn reset(&mut self) -> Result<()> {
self.send_msg(&IpcClientMsg::Reset)
.await
.context("Couldn't send Reset")?;
Ok(())
}
/// Tell connlib about the system's default resolvers
pub async fn set_dns(&mut self, dns: Vec<IpAddr>) -> Result<()> {
self.send_msg(&IpcClientMsg::SetDns(dns))
.await
.context("Couldn't send SetDns")?;
Ok(())
}
}