Install the rustls crypto provider at startup
reqwest reaches BB through synckit-client and tauri-plugin-updater, both
built with `rustls-no-provider`, which makes installing a process-wide
provider the consumer app's job. BB never did it, so the first sync or update
check would panic inside reqwest's client builder with "No provider set".
audiofiles and GoingsOn both install ring at startup; BB was the gap.
Installed in `build_app` rather than in `main`, so the mobile entry point is
covered by the same line.
Twelve sync_service unit tests were failing for the same reason and now pass.
synckit-client installs ring under its own `#[cfg(test)]`, which is dead code
when it is compiled as our dependency, so the tests that construct a client
need their own install; `ensure_crypto_provider` in the shared test module
does it once.
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
6 files changed,
+31 insertions,
-0 deletions
| 451 |
451 |
|
"rand 0.10.2",
|
| 452 |
452 |
|
"regex",
|
| 453 |
453 |
|
"roxmltree",
|
|
454 |
+ |
"rustls",
|
| 454 |
455 |
|
"serde",
|
| 455 |
456 |
|
"serde_json",
|
| 456 |
457 |
|
"sha2 0.11.0",
|
| 50 |
50 |
|
|
| 51 |
51 |
|
# Cloud sync
|
| 52 |
52 |
|
synckit-client.workspace = true
|
|
53 |
+ |
# reqwest reaches us through synckit-client and tauri-plugin-updater built with
|
|
54 |
+ |
# `rustls-no-provider`, so the consumer app owes rustls a process-wide crypto
|
|
55 |
+ |
# provider before any TLS client is built. We install ring in `build_app`.
|
|
56 |
+ |
# Matches audiofiles. Trust anchors come from the OS via rustls-platform-verifier.
|
|
57 |
+ |
rustls = { version = "0.23", default-features = false, features = ["std", "tls12", "logging", "ring"] }
|
| 53 |
58 |
|
sha2 = "0.11"
|
| 54 |
59 |
|
base64.workspace = true
|
| 55 |
60 |
|
rand.workspace = true
|
| 20 |
20 |
|
|
| 21 |
21 |
|
/// Build the Tauri app with all commands registered.
|
| 22 |
22 |
|
pub fn build_app() -> tauri::Builder<tauri::Wry> {
|
|
23 |
+ |
// Install the process-wide rustls crypto provider (ring) before any TLS
|
|
24 |
+ |
// client is built. reqwest reaches us via synckit-client and the updater
|
|
25 |
+ |
// plugin, both compiled with `rustls-no-provider`, so without this the first
|
|
26 |
+ |
// sync or update check panics with "No provider set" inside reqwest's
|
|
27 |
+ |
// client builder. Both entry points (desktop `main`, `mobile_main`) come
|
|
28 |
+ |
// through here, so this is the one place that covers every platform.
|
|
29 |
+ |
// Err means a provider is already installed, which is the outcome we want.
|
|
30 |
+ |
let _ = rustls::crypto::ring::default_provider().install_default();
|
|
31 |
+ |
|
| 23 |
32 |
|
let builder = tauri::Builder::default();
|
| 24 |
33 |
|
|
| 25 |
34 |
|
// Desktop-only plugins
|
| 683 |
683 |
|
const TEST_KEY: [u8; 32] = [42u8; 32];
|
| 684 |
684 |
|
|
| 685 |
685 |
|
fn mock_client(server_url: &str) -> SyncKitClient {
|
|
686 |
+ |
ensure_crypto_provider();
|
| 686 |
687 |
|
let client = SyncKitClient::new(SyncKitConfig {
|
| 687 |
688 |
|
server_url: server_url.to_string(),
|
| 688 |
689 |
|
api_key: "test-key".to_string(),
|
| 164 |
164 |
|
use super::*;
|
| 165 |
165 |
|
use uuid::Uuid;
|
| 166 |
166 |
|
|
|
167 |
+ |
/// Install the rustls crypto provider for tests that build a `SyncKitClient`.
|
|
168 |
+ |
///
|
|
169 |
+ |
/// `build_app` does this for the real app, but no test runs it. synckit-client
|
|
170 |
+ |
/// installs ring under its own `#[cfg(test)]`, which is dead when it is
|
|
171 |
+ |
/// compiled as our dependency, so every test that constructs a client has to
|
|
172 |
+ |
/// do it here or reqwest's builder panics with "No provider set".
|
|
173 |
+ |
pub(crate) fn ensure_crypto_provider() {
|
|
174 |
+ |
static PROVIDER: std::sync::Once = std::sync::Once::new();
|
|
175 |
+ |
PROVIDER.call_once(|| {
|
|
176 |
+ |
// Err means a provider is already installed, which is the outcome we want.
|
|
177 |
+ |
let _ = rustls::crypto::ring::default_provider().install_default();
|
|
178 |
+ |
});
|
|
179 |
+ |
}
|
|
180 |
+ |
|
| 167 |
181 |
|
pub(crate) async fn setup_test_db() -> SqlitePool {
|
| 168 |
182 |
|
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
| 169 |
183 |
|
sqlx::migrate!("../migrations/sqlite")
|
| 114 |
114 |
|
|
| 115 |
115 |
|
/// Create a SyncKitClient pointing at the mock server with auth and key set.
|
| 116 |
116 |
|
fn mock_client(server_url: &str) -> SyncKitClient {
|
|
117 |
+ |
ensure_crypto_provider();
|
| 117 |
118 |
|
let client = SyncKitClient::new(SyncKitConfig {
|
| 118 |
119 |
|
server_url: server_url.to_string(),
|
| 119 |
120 |
|
api_key: "test-key".to_string(),
|