From 4f1c45e9d5210fff2cdd4ba98517314b3e56f1a3 Mon Sep 17 00:00:00 2001 From: Pascal Breuninger Date: Wed, 7 Feb 2024 19:27:03 +0100 Subject: [PATCH] fix(desktop): work around memory leak caused by tauris invoke api in combination with larger payloads --- desktop/src-tauri/Cargo.lock | 18 ++++++++++++++ desktop/src-tauri/Cargo.toml | 4 +++- desktop/src-tauri/src/main.rs | 1 - desktop/src-tauri/src/server.rs | 40 +++++++++++++++++++++++++------- desktop/src-tauri/src/updates.rs | 7 ------ desktop/src/client/client.ts | 8 ++++++- 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index bd006bfdc..8b7d57a65 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -869,6 +869,7 @@ dependencies = [ "cocoa", "dirs", "dispatch", + "http 1.0.0", "lazy_static", "log", "objc", @@ -888,6 +889,7 @@ dependencies = [ "tauri-plugin-store", "thiserror", "tokio", + "tower-http", "ts-rs", "url", "window-vibrancy", @@ -4481,6 +4483,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da193277a4e2c33e59e09b5861580c33dd0a637c3883d0fa74ba40c0374af2e" +dependencies = [ + "bitflags 2.4.1", + "bytes", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index b1addd55b..b6778f579 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -29,7 +29,7 @@ tauri = { version = "1.2.4", features = [ "window-set-focus", "window-start-dragging", "icon-ico", -] } +] } # Serde serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } @@ -62,6 +62,8 @@ ts-rs = { version = "6.2.1", features = ["serde-compat", "chrono-impl"] } semver = "1.0.18" strip-ansi-escapes = "0.1.1" axum = { version = "0.7.1", features = ["ws"] } +tower-http = { version = "0.5.1", features = ["cors"] } +http = "1.0.0" [target.'cfg(target_os = "windows")'.dependencies] winreg = "0.50.0" diff --git a/desktop/src-tauri/src/main.rs b/desktop/src-tauri/src/main.rs index c270e99f4..ef2fe3df3 100644 --- a/desktop/src-tauri/src/main.rs +++ b/desktop/src-tauri/src/main.rs @@ -147,7 +147,6 @@ fn main() -> anyhow::Result<()> { action_logs::sync_action_logs, install_cli::install_cli, community_contributions::get_contributions, - updates::get_releases, updates::get_pending_update, updates::check_updates ]); diff --git a/desktop/src-tauri/src/server.rs b/desktop/src-tauri/src/server.rs index 984e30c33..7cc608bac 100644 --- a/desktop/src-tauri/src/server.rs +++ b/desktop/src-tauri/src/server.rs @@ -1,25 +1,34 @@ -use crate::{custom_protocol, ui_messages, AppHandle, AppState}; +use crate::{ui_messages, AppHandle, AppState}; use axum::{ extract::{ connect_info::ConnectInfo, ws::{Message, WebSocket, WebSocketUpgrade}, }, - http::HeaderMap, - response::Response, + http::{HeaderMap, StatusCode}, + response::{IntoResponse, Response}, routing::get, - Router, ServiceExt, + Json, Router, }; +use http::Method; use log::{info, warn}; use std::net::SocketAddr; use tauri::{Manager, State}; +use tower_http::cors::{Any, CorsLayer}; pub async fn setup(app_handle: &AppHandle) -> anyhow::Result<()> { let handle = app_handle.clone(); + let handle_releases = app_handle.clone(); + let cors = CorsLayer::new() + .allow_methods([Method::GET, Method::POST]) + .allow_origin(Any); - let router = Router::new().route( - "/ws", - get(move |upgrade, headers, info| ws_handler(upgrade, headers, info, handle.clone())), - ); + let router = Router::new() + .route( + "/ws", + get(move |upgrade, headers, info| ws_handler(upgrade, headers, info, handle.clone())), + ) + .route("/releases", get(move || releases_handler(handle_releases))) + .layer(cors); let listener = tokio::net::TcpListener::bind("127.0.0.1:25842").await?; info!("Listening on {}", listener.local_addr()?); @@ -30,6 +39,21 @@ pub async fn setup(app_handle: &AppHandle) -> anyhow::Result<()> { .await .map_err(anyhow::Error::from); } +async fn releases_handler(app_handle: AppHandle) -> impl IntoResponse { + #[cfg(feature = "enable-updater")] + { + let state = app_handle.state::(); + let releases = state.releases.lock().unwrap(); + let releases = releases.clone(); + + Json(releases) + } + + #[cfg(not(feature = "enable-updater"))] + { + (StatusCode::NOT_FOUND, "Not found") + } +} async fn ws_handler( ws: WebSocketUpgrade, diff --git a/desktop/src-tauri/src/updates.rs b/desktop/src-tauri/src/updates.rs index 5fa1e1287..f2477ce72 100644 --- a/desktop/src-tauri/src/updates.rs +++ b/desktop/src-tauri/src/updates.rs @@ -121,13 +121,6 @@ pub struct Author { pub site_admin: bool, } -#[tauri::command] -pub async fn get_releases(state: tauri::State<'_, AppState>) -> Result { - let releases = state.releases.lock().unwrap(); - - Ok(releases.to_vec()) -} - #[tauri::command] pub async fn get_pending_update(state: tauri::State<'_, AppState>) -> Result { let release = state.pending_update.lock().unwrap(); diff --git a/desktop/src/client/client.ts b/desktop/src/client/client.ts index 907413f78..2fde7f8f7 100644 --- a/desktop/src/client/client.ts +++ b/desktop/src/client/client.ts @@ -135,7 +135,13 @@ class Client { public async fetchReleases(): Promise> { try { - const releases = await invoke("get_releases") + // WARN: This is a workaround for a memory leak in tauri, see https://github.com/tauri-apps/tauri/issues/4026 for more details. + // tl;dr tauri doesn't release the memory in it's invoke api properly which is specially noticeable with larger payload, like the releases. + const res = await fetch("http://localhost:25842/releases") + if (!res.ok) { + return Return.Failed(`Fetch releases: ${res.statusText}`) + } + const releases = (await res.json()) as readonly Release[] return Return.Value(releases) } catch (e) {