Skip to content

Commit 71806ae

Browse files
axpnetclaude
andcommitted
test(tls): guard the rustls CryptoProvider install at the root
The FTPS-hang class of bug (c15ee3f) was a binary doing TLS without a rustls CryptoProvider installed: aws-lc-rs and ring are both in the tree (ours plus noq-proto), so rustls cannot auto-select one and panics on the first handshake, which stranded every FTPS connect in an infinite spinner. The GUI binary had simply forgotten the install call the CLI already had. Centralise the install in ftp_client_gui_lib::install_crypto_provider() and route both binaries (GUI run(), aeroftp-cli main) through it, so there is ONE code path instead of two copies that can drift. run() now debug_asserts the provider is installed. A unit test asserts install_crypto_provider() leaves a provider globally visible, which is the cheap CI guard that would have caught c15ee3f before it reached the desktop. The other binaries (aeroftp-dispatch, aerorsync_serve, seed_test_profiles) do no TLS handshake, so they need no install. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e25af60 commit 71806ae

2 files changed

Lines changed: 49 additions & 13 deletions

File tree

src-tauri/src/bin/aeroftp_cli.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55201,12 +55201,10 @@ async fn main() {
5520155201
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
5520255202
}
5520355203

55204-
// Install the process-wide rustls CryptoProvider explicitly. Both
55205-
// `aws-lc-rs` and `ring` are pulled into the dependency tree (via
55206-
// different crates), so rustls cannot auto-select a provider and panics
55207-
// on the first TLS handshake (FTPS, WebDAV over https, ...). Pin our
55208-
// chosen backend once, before any TLS connector is built.
55209-
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
55204+
// Pin the rustls CryptoProvider for this (CLI) process before any TLS
55205+
// connector is built. Shared with the GUI binary so both take ONE code path
55206+
// and one test guards it. See `ftp_client_gui_lib::install_crypto_provider`.
55207+
let _ = ftp_client_gui_lib::install_crypto_provider();
5521055208

5521155209
// Show the banner only when it's actually useful: bare invocation or
5521255210
// top-level --help. Subcommand help (e.g. `aeroftp-cli get --help`)

src-tauri/src/lib.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16485,17 +16485,55 @@ async fn mount_open_quick(id: String) -> Result<(), String> {
1648516485

1648616486
// ============ App Entry Point ============
1648716487

16488+
/// Install the process-wide rustls `CryptoProvider` for THIS binary.
16489+
///
16490+
/// Both `aws-lc-rs` and `ring` are pulled into the dependency tree (via
16491+
/// different crates: our own choice and `noq-proto`), so rustls cannot
16492+
/// auto-select a provider and panics on the first TLS handshake (FTPS, WebDAV
16493+
/// over https, ...) unless one is pinned. Each binary target runs in its own
16494+
/// process, so each must call this once before any TLS connector is built.
16495+
/// Regression `c15ee3f38`: the GUI binary forgot to, so every FTPS connect
16496+
/// panicked into an infinite spinner. Centralised here so every binary shares
16497+
/// ONE code path and the `crypto_provider_guard` test can guard the class of bug
16498+
/// at the root.
16499+
///
16500+
/// Idempotent: a second call is a no-op (rustls returns `Err` when a provider is
16501+
/// already installed, which we ignore). Returns whether a provider is installed
16502+
/// after the call, so callers/tests can assert the process is TLS-ready.
16503+
pub fn install_crypto_provider() -> bool {
16504+
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
16505+
rustls::crypto::CryptoProvider::get_default().is_some()
16506+
}
16507+
16508+
#[cfg(test)]
16509+
mod crypto_provider_guard {
16510+
//! Root-cause guard for the FTPS-hang class of bug (`c15ee3f38`): a binary
16511+
//! that does TLS without installing a rustls provider panics on the first
16512+
//! handshake. Every binary must route through `install_crypto_provider`;
16513+
//! this proves the helper actually leaves the process with a provider
16514+
//! installed and globally visible (so a fresh TLS connector can build).
16515+
#[test]
16516+
fn install_crypto_provider_makes_process_tls_ready() {
16517+
assert!(
16518+
super::install_crypto_provider(),
16519+
"install_crypto_provider must leave a rustls CryptoProvider installed"
16520+
);
16521+
assert!(rustls::crypto::CryptoProvider::get_default().is_some());
16522+
}
16523+
}
16524+
1648816525
#[cfg_attr(mobile, tauri::mobile_entry_point)]
1648916526
pub fn run() {
1649016527
use tauri::menu::{Menu, MenuItem, PredefinedMenuItem, Submenu};
1649116528

16492-
// Install the process-wide rustls CryptoProvider explicitly. Both
16493-
// `aws-lc-rs` and `ring` are pulled into the dependency tree (via
16494-
// different crates), so rustls cannot auto-select a provider and panics
16495-
// on the first TLS handshake (FTPS, WebDAV over https, ...). Pin our
16496-
// chosen backend once, before any TLS connector is built. `aeroftp_cli`
16497-
// does the same in its own `main`; every binary needs its own call.
16498-
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
16529+
// Pin the rustls CryptoProvider for this (GUI) process before any TLS
16530+
// connector is built. See `install_crypto_provider`.
16531+
debug_assert!(
16532+
install_crypto_provider(),
16533+
"rustls CryptoProvider must be installed before TLS is used"
16534+
);
16535+
#[cfg(not(debug_assertions))]
16536+
let _ = install_crypto_provider();
1649916537

1650016538
// Fix WebKitGTK rendering issues on Linux: disable DMA-BUF renderer
1650116539
// which causes canvas/WebGL artifacts in Monaco and xterm.js.

0 commit comments

Comments
 (0)