From 5c9ec8982145bb07094d5f06e073b0194f9397f4 Mon Sep 17 00:00:00 2001 From: ospab Date: Sat, 27 Jun 2026 21:40:09 +0300 Subject: [PATCH] =?UTF-8?q?=C2=A7F:=20GUI=20start=5Ftunnel=20tears=20down?= =?UTF-8?q?=20existing=20tunnel=20first=20(stop+start)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously start_tunnel returned early if a tunnel was already running, so changing the server while connected silently kept the OLD connection. Per the plan ("server change = full stop+start, not hot-reload"), tear down any existing InProcess/Helper tunnel before starting a fresh one. For the elevated helper, wait ~1.2s after sending stop so it releases the ostp_tun adapter before a new helper recreates it (avoids name clashes). start_tunnel is only invoked on an explicit connect, so restarting here is safe. Co-Authored-By: Claude Opus 4.8 --- ostp-gui/src-tauri/src/lib.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/ostp-gui/src-tauri/src/lib.rs b/ostp-gui/src-tauri/src/lib.rs index 51f64ad..2a471a9 100644 --- a/ostp-gui/src-tauri/src/lib.rs +++ b/ostp-gui/src-tauri/src/lib.rs @@ -501,14 +501,25 @@ fn generate_qr(text: String) -> Result { async fn start_tunnel(state: tauri::State<'_, AppState>, app: tauri::AppHandle) -> Result { let mut guard = state.0.lock().await; - if let Some(ref t) = guard.tunnel { - match t { - TunnelHandle::InProcess(s) if !s.handle.is_finished() => return Ok(true), - TunnelHandle::Helper(_) => return Ok(true), - _ => {} + // Tear down any existing tunnel before starting a fresh one — otherwise a + // server change would silently keep the old connection/server. start_tunnel + // is only ever invoked on an explicit connect, so restarting here is safe. + // This implements the plan's "server change = full stop+start, not hot-reload". + match guard.tunnel.take() { + None => {} + Some(TunnelHandle::InProcess(mut s)) => { + if let Some(tx) = s.shutdown_tx.take() { let _ = tx.send(true); } + s.handle.abort(); + let _ = tokio::time::timeout(std::time::Duration::from_secs(2), s.handle).await; + } + Some(TunnelHandle::Helper(h)) => { + let stop_cmd = serde_json::json!({ "cmd": "stop", "token": h.token }).to_string(); + let _ = h.cmd_tx.send(format!("{}\n", stop_cmd)).await; + // Let the elevated helper stop the tunnel and release the ostp_tun + // adapter before a new helper tries to create it (avoids name clashes). + tokio::time::sleep(std::time::Duration::from_millis(1200)).await; } } - guard.tunnel = None; let path = get_config_path(); let content = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;