diff --git a/crates/primp-reqwest/src/async_impl/client.rs b/crates/primp-reqwest/src/async_impl/client.rs index ba52c32..debab61 100644 --- a/crates/primp-reqwest/src/async_impl/client.rs +++ b/crates/primp-reqwest/src/async_impl/client.rs @@ -792,10 +792,10 @@ impl ClientBuilder { )); } - let roots = if config.root_certs.is_empty() { - crate::tls::default_root_store().clone() + let roots: Arc = if config.root_certs.is_empty() { + crate::tls::default_root_store_arc() } else { - crate::tls::merged_root_store(config.root_certs)? + Arc::new(crate::tls::merged_root_store(config.root_certs)?) }; config_builder.with_root_certificates(roots) diff --git a/crates/primp-reqwest/src/tls.rs b/crates/primp-reqwest/src/tls.rs index 6db5618..0646ca1 100644 --- a/crates/primp-reqwest/src/tls.rs +++ b/crates/primp-reqwest/src/tls.rs @@ -56,6 +56,7 @@ use rustls_pki_types::{ServerName, UnixTime}; use std::{ fmt, io::{BufRead, BufReader}, + sync::Arc, }; /// Represents a X509 certificate revocation list. @@ -640,6 +641,19 @@ pub fn default_root_store() -> &'static rustls::RootCertStore { }) } +/// Returns a cached `Arc`, avoiding deep clones on every call. +/// +/// The underlying store is initialized once (loading webpki + native roots), +/// and subsequent calls only increment the `Arc` reference count. +#[cfg(feature = "__rustls")] +pub fn default_root_store_arc() -> Arc { + static DEFAULT_ROOTS_ARC: std::sync::OnceLock> = + std::sync::OnceLock::new(); + DEFAULT_ROOTS_ARC + .get_or_init(|| Arc::new(default_root_store().clone())) + .clone() +} + /// Creates a root certificate store from the cached default store plus user-provided certs. #[cfg(feature = "__rustls")] pub fn merged_root_store(certs: Vec) -> crate::Result { diff --git a/crates/primp/src/imp/safari/mod.rs b/crates/primp/src/imp/safari/mod.rs index 3254d31..bdd6cc0 100644 --- a/crates/primp/src/imp/safari/mod.rs +++ b/crates/primp/src/imp/safari/mod.rs @@ -48,9 +48,7 @@ pub(crate) fn build_safari_settings( }; // Get cached browser emulator for Safari (Arc clone = cheap refcount increment) - let mut browser_emulator = safari_emulator(safari, browser_os); - // Set OS type on our unique clone (cached emulator retains None) - Arc::make_mut(&mut browser_emulator).os_type = Some(browser_os); + let browser_emulator = safari_emulator(safari, browser_os); let http2 = build_http2_settings(); @@ -118,16 +116,50 @@ fn build_http2_settings() -> crate::imp::Http2Data { fn safari_emulator(safari: Impersonate, browser_os: BrowserEmulatorOS) -> Arc { match safari { - Impersonate::SafariV18_5 => { - static EMU: OnceLock> = OnceLock::new(); - EMU.get_or_init(|| Arc::new(new_safari_18_5_emulator())) - .clone() - } - Impersonate::SafariV26 => { - static EMU: OnceLock> = OnceLock::new(); - EMU.get_or_init(|| Arc::new(new_safari_26_emulator())) - .clone() - } + Impersonate::SafariV18_5 => match browser_os { + BrowserEmulatorOS::IOS => { + static EMU_IOS: OnceLock> = OnceLock::new(); + EMU_IOS + .get_or_init(|| { + let mut emu = new_safari_18_5_emulator(); + emu.os_type = Some(BrowserEmulatorOS::IOS); + Arc::new(emu) + }) + .clone() + } + _ => { + static EMU_MACOS: OnceLock> = OnceLock::new(); + EMU_MACOS + .get_or_init(|| { + let mut emu = new_safari_18_5_emulator(); + emu.os_type = Some(BrowserEmulatorOS::MacOS); + Arc::new(emu) + }) + .clone() + } + }, + Impersonate::SafariV26 => match browser_os { + BrowserEmulatorOS::IOS => { + static EMU_IOS: OnceLock> = OnceLock::new(); + EMU_IOS + .get_or_init(|| { + let mut emu = new_safari_26_emulator(); + emu.os_type = Some(BrowserEmulatorOS::IOS); + Arc::new(emu) + }) + .clone() + } + _ => { + static EMU_MACOS: OnceLock> = OnceLock::new(); + EMU_MACOS + .get_or_init(|| { + let mut emu = new_safari_26_emulator(); + emu.os_type = Some(BrowserEmulatorOS::MacOS); + Arc::new(emu) + }) + .clone() + } + }, Impersonate::SafariV26_3 => { if browser_os == BrowserEmulatorOS::IOS { static EMU_IOS: OnceLock> = OnceLock::new();