diff --git a/Cargo.toml b/Cargo.toml index ba62d89..683a79a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ members = [ [workspace.package] version = "1.2.3" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" license = "MIT" repository = "https://github.com/deedy5/primp" authors = ["deedy5"] diff --git a/crates/primp-h2/Cargo.toml b/crates/primp-h2/Cargo.toml index beb631f..d4699d3 100644 --- a/crates/primp-h2/Cargo.toml +++ b/crates/primp-h2/Cargo.toml @@ -9,7 +9,7 @@ authors = [ ] description = "A fork of the HTTP/2 client and server library from hyperium" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" # Aliases [lib] diff --git a/crates/primp-h2/src/frame/settings.rs b/crates/primp-h2/src/frame/settings.rs index 7839031..a88b40f 100644 --- a/crates/primp-h2/src/frame/settings.rs +++ b/crates/primp-h2/src/frame/settings.rs @@ -292,7 +292,7 @@ impl Settings { } // Ensure the payload length is correct, each setting is 6 bytes long. - if payload.len() % 6 != 0 { + if !payload.len().is_multiple_of(6) { tracing::debug!("invalid settings payload length; len={:?}", payload.len()); return Err(Error::InvalidPayloadAckSettings); } diff --git a/crates/primp-h2/src/frame/stream_id.rs b/crates/primp-h2/src/frame/stream_id.rs index 0f427df..8266044 100644 --- a/crates/primp-h2/src/frame/stream_id.rs +++ b/crates/primp-h2/src/frame/stream_id.rs @@ -47,7 +47,7 @@ impl StreamId { /// was initiated by the server. pub fn is_server_initiated(&self) -> bool { let id = self.0; - id != 0 && id % 2 == 0 + id != 0 && id.is_multiple_of(2) } /// Return a new `StreamId` for stream 0. diff --git a/crates/primp-h2/tests/h2-support/Cargo.toml b/crates/primp-h2/tests/h2-support/Cargo.toml index 1774895..3b9532c 100644 --- a/crates/primp-h2/tests/h2-support/Cargo.toml +++ b/crates/primp-h2/tests/h2-support/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Carl Lerche "] publish = false edition = "2021" -rust-version = "1.84" +rust-version = "1.89" [dependencies] h2 = { path = "../..", package = "primp-h2", features = ["stream", "unstable"] } diff --git a/crates/primp-h2/tests/h2-tests/Cargo.toml b/crates/primp-h2/tests/h2-tests/Cargo.toml index ea1e5a7..3e522cb 100644 --- a/crates/primp-h2/tests/h2-tests/Cargo.toml +++ b/crates/primp-h2/tests/h2-tests/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Carl Lerche "] publish = false edition = "2021" -rust-version = "1.84" +rust-version = "1.89" [dependencies] diff --git a/crates/primp-hyper-rustls/Cargo.toml b/crates/primp-hyper-rustls/Cargo.toml index bf8f6a8..399550e 100644 --- a/crates/primp-hyper-rustls/Cargo.toml +++ b/crates/primp-hyper-rustls/Cargo.toml @@ -2,7 +2,7 @@ name = "primp-hyper-rustls" version = "0.27.9" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" license = "Apache-2.0 OR ISC OR MIT" description = "Fork of hyper-rustls" diff --git a/crates/primp-hyper-util/Cargo.toml b/crates/primp-hyper-util/Cargo.toml index 53a1e4e..0e9aff8 100644 --- a/crates/primp-hyper-util/Cargo.toml +++ b/crates/primp-hyper-util/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.20" license = "MIT" authors = ["deedy5", "Sean McArthur "] edition = "2021" -rust-version = "1.84" +rust-version = "1.89" build = false autolib = false autobins = false diff --git a/crates/primp-hyper/Cargo.toml b/crates/primp-hyper/Cargo.toml index 3b359b9..9e5c6e4 100644 --- a/crates/primp-hyper/Cargo.toml +++ b/crates/primp-hyper/Cargo.toml @@ -6,7 +6,7 @@ readme = "README.md" license = "MIT" authors = ["deedy5", "Sean McArthur "] edition = "2021" -rust-version = "1.84" +rust-version = "1.89" build = false include = [ "Cargo.toml", diff --git a/crates/primp-python/Cargo.toml b/crates/primp-python/Cargo.toml index 5a8ddce..04e828b 100644 --- a/crates/primp-python/Cargo.toml +++ b/crates/primp-python/Cargo.toml @@ -2,7 +2,7 @@ name = "primp-python" version = "1.2.3" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" description = "HTTP client that can impersonate web browsers" authors = ["deedy5"] @@ -24,18 +24,15 @@ primp = { version = "1.2", path = "../primp", features = [ bytes = "1" encoding_rs = { version = "0.8.35" } foldhash = "0.2.0" -h2 = { version = "0.4", path = "../primp-h2", package = "primp-h2", default-features = false, features = ["unstable"] } + indexmap = { version = "2.13.0", features = ["serde"] } tokio = { version = "1.49.0", features = ["full"] } tokio-util = { version = "0.7.18", features = ["codec"] } # for multipart html2text = "0.16.6" pythonize = "0.28.0" serde_json = "1.0.149" -webpki-root-certs = "1.0.5" http = "1.4" mime = "0.3.17" rand = "0.10" -typed-builder = "0.20.0" -once_cell = "1.21" url = "2.5" diff --git a/crates/primp-python/src/async/client.rs b/crates/primp-python/src/async/client.rs index 0557642..49ee6eb 100644 --- a/crates/primp-python/src/async/client.rs +++ b/crates/primp-python/src/async/client.rs @@ -1,22 +1,17 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; -use ::primp::{ - multipart, Body, Client as PrimpClient, Method, Proxy, Response as PrimpResponse, Url, -}; +use ::primp::{multipart, Body, Client as PrimpClient, Method, Response as PrimpResponse, Url}; use pyo3::prelude::*; use pythonize::depythonize; use serde_json::Value; use tokio::fs::File; use tokio_util::codec::{BytesCodec, FramedRead}; -use crate::client_builder::{ - configure_client_builder, cookies_to_header_values, headers_without_cookie, - parse_cookies_from_header, parse_url_or_domain, IndexMapSSR, -}; +use crate::client_builder::{configure_client_builder, cookies_to_header_values, IndexMapSSR}; use crate::error::{PrimpErrorEnum, PrimpResult}; use crate::extract_cookies_to_indexmap; -use crate::traits::{HeaderMapExt, HeadersTraits}; +use crate::traits::HeadersTraits; use crate::utils::extract_encoding; /// Async HTTP client that can impersonate web browsers. @@ -120,32 +115,16 @@ impl AsyncClient { #[getter] pub fn get_headers(&self) -> PrimpResult { - let client = self.client.read().expect("client lock was poisoned"); - Ok(headers_without_cookie(client.headers())) + crate::client_builder::client_headers(&self.client) } #[setter] pub fn set_headers(&mut self, new_headers: Option) -> PrimpResult<()> { - let mut client = self.client.write().expect("client lock was poisoned"); - let headers = client.headers_mut(); - headers.clear(); - if let Some(new_headers) = new_headers { - for (k, v) in new_headers { - headers.insert_key_value(k, v)?; - } - } - Ok(()) + crate::client_builder::client_set_headers(&self.client, new_headers) } pub fn headers_update(&self, new_headers: Option) -> PrimpResult<()> { - let mut client = self.client.write().expect("client lock was poisoned"); - let headers = client.headers_mut(); - if let Some(new_headers) = new_headers { - for (k, v) in new_headers { - headers.insert_key_value(k, v)?; - } - } - Ok(()) + crate::client_builder::client_headers_update(&self.client, new_headers) } #[getter] @@ -155,37 +134,21 @@ impl AsyncClient { #[setter] pub fn set_proxy(&mut self, proxy: String) -> PrimpResult<()> { - let rproxy = Proxy::all(proxy.clone())?; - let mut client = self.client.write().expect("client lock was poisoned"); - let client_ref = &mut *client; - client_ref.set_proxies(vec![rproxy]); - self.proxy = Some(proxy); + self.proxy = Some(crate::client_builder::client_set_proxy( + &self.client, + proxy, + )?); Ok(()) } #[pyo3(signature = (url))] fn get_cookies(&self, url: &str) -> PrimpResult { - let url = Url::parse(url).map_err(|e| PrimpErrorEnum::InvalidURL(e.to_string()))?; - let client = self.client.read().expect("client lock was poisoned"); - let cookie = client - .get_cookies(&url) - .ok_or_else(|| PrimpErrorEnum::Custom("Failed to get cookies".to_string()))?; - let cookie_str = cookie - .to_str() - .map_err(|e| PrimpErrorEnum::Custom(e.to_string()))?; - Ok(parse_cookies_from_header(cookie_str)) + crate::client_builder::client_get_cookies(&self.client, url) } #[pyo3(signature = (url, cookies))] fn set_cookies(&self, url: &str, cookies: Option) -> PrimpResult<()> { - let url = - parse_url_or_domain(url).map_err(|e| PrimpErrorEnum::InvalidURL(e.to_string()))?; - if let Some(cookies) = cookies { - let header_values = cookies_to_header_values(&cookies); - let client = self.client.read().expect("client lock was poisoned"); - client.set_cookies(&url, header_values); - } - Ok(()) + crate::client_builder::client_set_cookies(&self.client, url, cookies) } /// Constructs an async HTTP request with the given method, URL, and optionally sets a timeout, headers, and query parameters. @@ -248,7 +211,7 @@ impl AsyncClient { if !client_cookies.is_empty() { let url_parsed = Url::parse(&resolved_url).map_err(Into::::into)?; let cookie_values = cookies_to_header_values(client_cookies); - let client_guard = self.client.read().expect("client lock was poisoned"); + let client_guard = self.client.read().unwrap_or_else(|e| e.into_inner()); client_guard.set_cookies(&url_parsed, cookie_values); } } @@ -257,13 +220,13 @@ impl AsyncClient { if let Some(cookies) = cookies.filter(|c| !c.is_empty()) { let url_parsed = Url::parse(&resolved_url).map_err(Into::::into)?; let cookie_values = cookies_to_header_values(&cookies); - let client_guard = self.client.read().expect("client lock was poisoned"); + let client_guard = self.client.read().unwrap_or_else(|e| e.into_inner()); client_guard.set_cookies(&url_parsed, cookie_values); } // Handle follow_redirects: set policy before cloning client if let Some(fr) = follow_redirects { - let mut client_guard = self.client.write().expect("client lock was poisoned"); + let mut client_guard = self.client.write().unwrap_or_else(|e| e.into_inner()); if fr { client_guard.set_redirect_policy(::primp::redirect::Policy::limited(20)); } else { @@ -273,7 +236,7 @@ impl AsyncClient { // Clone the client before entering the async block to avoid holding RwLockGuard across await let client = { - let client_guard = self.client.read().expect("client lock was poisoned"); + let client_guard = self.client.read().unwrap_or_else(|e| e.into_inner()); client_guard.clone() }; @@ -349,7 +312,7 @@ impl AsyncClient { // Restore redirect policy if it was changed if follow_redirects.is_some() { - let mut client_guard = self.client.write().expect("client lock was poisoned"); + let mut client_guard = self.client.write().unwrap_or_else(|e| e.into_inner()); client_guard.set_redirect_policy(::primp::redirect::Policy::limited(20)); } diff --git a/crates/primp-python/src/client_builder.rs b/crates/primp-python/src/client_builder.rs index 3af2dae..3cd7cbd 100644 --- a/crates/primp-python/src/client_builder.rs +++ b/crates/primp-python/src/client_builder.rs @@ -3,6 +3,7 @@ //! This module eliminates code duplication by providing shared configuration //! structures and functions used by both `Client` and `AsyncClient`. +use std::sync::{Arc, RwLock}; use std::time::Duration; use foldhash::fast::RandomState; @@ -10,15 +11,15 @@ use indexmap::IndexMap; use primp::{ header::{HeaderMap, HeaderValue}, redirect::Policy, - ClientBuilder, Proxy, Url, + Client as PrimpClient, ClientBuilder, Proxy, Url, }; -use crate::error::PrimpResult; +use crate::error::{PrimpErrorEnum, PrimpResult}; use crate::impersonate::{ get_random_element, parse_impersonate_os_with_fallback, parse_impersonate_with_fallback, IMPERSONATEOS_LIST, }; -use crate::traits::HeadersTraits; +use crate::traits::{HeaderMapExt, HeadersTraits}; use crate::utils::load_ca_certs; /// Type alias for IndexMap with String keys and values. @@ -211,3 +212,71 @@ pub fn headers_without_cookie(headers: &HeaderMap) -> IndexMapSSR { headers_map.swap_remove("cookie"); headers_map } + +pub fn client_headers(client: &Arc>) -> PrimpResult { + let c = client.read().unwrap_or_else(|e| e.into_inner()); + Ok(headers_without_cookie(c.headers())) +} + +pub fn client_set_headers( + client: &Arc>, + new_headers: Option, +) -> PrimpResult<()> { + let mut c = client.write().unwrap_or_else(|e| e.into_inner()); + let headers = c.headers_mut(); + headers.clear(); + if let Some(new_headers) = new_headers { + for (k, v) in new_headers { + headers.insert_key_value(k, v)?; + } + } + Ok(()) +} + +pub fn client_headers_update( + client: &Arc>, + new_headers: Option, +) -> PrimpResult<()> { + let mut c = client.write().unwrap_or_else(|e| e.into_inner()); + let headers = c.headers_mut(); + if let Some(new_headers) = new_headers { + for (k, v) in new_headers { + headers.insert_key_value(k, v)?; + } + } + Ok(()) +} + +pub fn client_set_proxy(client: &Arc>, proxy: String) -> PrimpResult { + let rproxy = Proxy::all(proxy.clone())?; + let mut c = client.write().unwrap_or_else(|e| e.into_inner()); + c.set_proxies(vec![rproxy]); + Ok(proxy) +} + +pub fn client_get_cookies( + client: &Arc>, + url: &str, +) -> PrimpResult { + let parsed = Url::parse(url).map_err(|e| PrimpErrorEnum::InvalidURL(e.to_string()))?; + let c = client.read().unwrap_or_else(|e| e.into_inner()); + let cookie = c + .get_cookies(&parsed) + .ok_or_else(|| PrimpErrorEnum::Custom("No cookies found for URL".to_string()))?; + let cookie_str = cookie.to_str()?; + Ok(parse_cookies_from_header(cookie_str)) +} + +pub fn client_set_cookies( + client: &Arc>, + url: &str, + cookies: Option, +) -> PrimpResult<()> { + let parsed = parse_url_or_domain(url).map_err(|e| PrimpErrorEnum::InvalidURL(e.to_string()))?; + if let Some(cookies) = cookies { + let header_values = cookies_to_header_values(&cookies); + let c = client.read().unwrap_or_else(|e| e.into_inner()); + c.set_cookies(&parsed, header_values); + } + Ok(()) +} diff --git a/crates/primp-python/src/lib.rs b/crates/primp-python/src/lib.rs index fff132e..fd6f64d 100644 --- a/crates/primp-python/src/lib.rs +++ b/crates/primp-python/src/lib.rs @@ -2,9 +2,7 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; -use ::primp::{ - multipart, Body, Client as PrimpClient, Method, Proxy, Response as PrimpResponse, Url, -}; +use ::primp::{multipart, Body, Client as PrimpClient, Method, Response as PrimpResponse, Url}; use pyo3::prelude::*; use pyo3::sync::PyOnceLock; use pythonize::depythonize; @@ -16,10 +14,7 @@ use tokio::{ use tokio_util::codec::{BytesCodec, FramedRead}; mod client_builder; -use client_builder::{ - configure_client_builder, cookies_to_header_values, headers_without_cookie, - parse_cookies_from_header, parse_url_or_domain, IndexMapSSR, -}; +use client_builder::{configure_client_builder, cookies_to_header_values, IndexMapSSR}; mod error; use error::{PrimpErrorEnum, PrimpResult}; @@ -31,7 +26,7 @@ use response::{BytesIterator, LinesIterator, Response, TextIterator}; mod r#async; mod traits; -use traits::{HeaderMapExt, HeadersTraits}; +use traits::HeadersTraits; mod utils; use utils::extract_encoding; @@ -216,32 +211,16 @@ impl Client { #[getter] pub fn get_headers(&self) -> PrimpResult { - let client = self.client.read().expect("client lock was poisoned"); - Ok(headers_without_cookie(client.headers())) + client_builder::client_headers(&self.client) } #[setter] pub fn set_headers(&self, new_headers: Option) -> PrimpResult<()> { - let mut client = self.client.write().expect("client lock was poisoned"); - let headers = client.headers_mut(); - headers.clear(); - if let Some(new_headers) = new_headers { - for (k, v) in new_headers { - headers.insert_key_value(k, v)?; - } - } - Ok(()) + client_builder::client_set_headers(&self.client, new_headers) } pub fn headers_update(&self, new_headers: Option) -> PrimpResult<()> { - let mut client = self.client.write().expect("client lock was poisoned"); - let headers = client.headers_mut(); - if let Some(new_headers) = new_headers { - for (k, v) in new_headers { - headers.insert_key_value(k, v)?; - } - } - Ok(()) + client_builder::client_headers_update(&self.client, new_headers) } #[getter] @@ -251,35 +230,18 @@ impl Client { #[setter] pub fn set_proxy(&mut self, proxy: String) -> PrimpResult<()> { - let rproxy = Proxy::all(proxy.clone())?; - let mut client = self.client.write().expect("client lock was poisoned"); - let client_ref = &mut *client; - client_ref.set_proxies(vec![rproxy]); - self.proxy = Some(proxy); + self.proxy = Some(client_builder::client_set_proxy(&self.client, proxy)?); Ok(()) } #[pyo3(signature = (url))] fn get_cookies(&self, url: &str) -> PrimpResult { - let url = Url::parse(url).map_err(|e| PrimpErrorEnum::InvalidURL(e.to_string()))?; - let client = self.client.read().expect("client lock was poisoned"); - let cookie = client - .get_cookies(&url) - .ok_or_else(|| PrimpErrorEnum::Custom("No cookies found for URL".to_string()))?; - let cookie_str = cookie.to_str()?; - Ok(parse_cookies_from_header(cookie_str)) + client_builder::client_get_cookies(&self.client, url) } #[pyo3(signature = (url, cookies))] fn set_cookies(&self, url: &str, cookies: Option) -> PrimpResult<()> { - let url = - parse_url_or_domain(url).map_err(|e| PrimpErrorEnum::InvalidURL(e.to_string()))?; - if let Some(cookies) = cookies { - let header_values = cookies_to_header_values(&cookies); - let client = self.client.read().expect("client lock was poisoned"); - client.set_cookies(&url, header_values); - } - Ok(()) + client_builder::client_set_cookies(&self.client, url, cookies) } /// Constructs an HTTP request with the given method, URL, and optionally sets a timeout, headers, and query parameters. @@ -363,7 +325,7 @@ impl Client { if !client_cookies.is_empty() { let url_parsed = Url::parse(&resolved_url).map_err(Into::::into)?; let cookie_values = cookies_to_header_values(client_cookies); - let client_guard = client.read().expect("client lock was poisoned"); + let client_guard = client.read().unwrap_or_else(|e| e.into_inner()); client_guard.set_cookies(&url_parsed, cookie_values); } } @@ -372,13 +334,13 @@ impl Client { if let Some(cookies) = cookies.filter(|c| !c.is_empty()) { let url_parsed = Url::parse(&resolved_url).map_err(Into::::into)?; let cookie_values = cookies_to_header_values(&cookies); - let client_guard = client.read().expect("client lock was poisoned"); + let client_guard = client.read().unwrap_or_else(|e| e.into_inner()); client_guard.set_cookies(&url_parsed, cookie_values); } // Handle follow_redirects: set policy before cloning client if let Some(fr) = follow_redirects { - let mut client_guard = client.write().expect("client lock was poisoned"); + let mut client_guard = client.write().unwrap_or_else(|e| e.into_inner()); if fr { client_guard.set_redirect_policy(::primp::redirect::Policy::limited(20)); } else { @@ -387,37 +349,15 @@ impl Client { } // Clone the inner client to avoid holding the RwLock across await points - let client_clone = client.read().expect("client lock was poisoned").clone(); - - let self_params = params - .as_ref() - .is_none() - .then_some(self.params.as_ref()) - .flatten(); - let self_auth = auth - .as_ref() - .is_none() - .then_some(self.auth.as_ref()) - .flatten(); - let self_auth_bearer = auth_bearer - .as_ref() - .is_none() - .then_some(self.auth_bearer.as_ref()) - .flatten(); + let client_clone = client.read().unwrap_or_else(|e| e.into_inner()).clone(); let future = async move { // Create request builder using the cloned client let mut request_builder = client_clone.request(method, &resolved_url); // Params - match (¶ms, self_params) { - (Some(p), _) => { - request_builder = request_builder.query(p); - } - (None, Some(sp)) => { - request_builder = request_builder.query(sp); - } - (None, None) => {} + if let Some(p) = params.as_ref().or(self.params.as_ref()) { + request_builder = request_builder.query(p); } // Headers @@ -453,25 +393,10 @@ impl Client { } // Auth - match (&auth, self_auth) { - (Some((u, p)), _) => { - request_builder = request_builder.basic_auth(u, p.as_deref()); - } - (None, Some((u, p))) => { - request_builder = request_builder.basic_auth(u, p.as_deref()); - } - (None, None) => { - // Try bearer auth if no basic auth - match (&auth_bearer, self_auth_bearer) { - (Some(t), _) => { - request_builder = request_builder.bearer_auth(t); - } - (None, Some(t)) => { - request_builder = request_builder.bearer_auth(t); - } - (None, None) => {} - } - } + if let Some((u, p)) = auth.as_ref().or(self.auth.as_ref()) { + request_builder = request_builder.basic_auth(u, p.as_deref()); + } else if let Some(t) = auth_bearer.as_ref().or(self.auth_bearer.as_ref()) { + request_builder = request_builder.bearer_auth(t); } // Timeout @@ -504,7 +429,7 @@ impl Client { // Restore redirect policy if it was changed if follow_redirects.is_some() { - let mut client_guard = client.write().expect("client lock was poisoned"); + let mut client_guard = client.write().unwrap_or_else(|e| e.into_inner()); client_guard.set_redirect_policy(::primp::redirect::Policy::limited(20)); } @@ -900,7 +825,7 @@ fn get( py, url, params, - headers, + None, cookies, content, data, @@ -988,7 +913,7 @@ fn head( py, url, params, - headers, + None, cookies, content, data, @@ -1076,7 +1001,7 @@ fn options( py, url, params, - headers, + None, cookies, content, data, @@ -1164,7 +1089,7 @@ fn delete( py, url, params, - headers, + None, cookies, content, data, @@ -1252,7 +1177,7 @@ fn post( py, url, params, - headers, + None, cookies, content, data, @@ -1340,7 +1265,7 @@ fn put( py, url, params, - headers, + None, cookies, content, data, @@ -1428,7 +1353,7 @@ fn patch( py, url, params, - headers, + None, cookies, content, data, @@ -1519,7 +1444,7 @@ fn request( method, url, params, - headers, + None, cookies, content, data, diff --git a/crates/primp-python/src/utils.rs b/crates/primp-python/src/utils.rs index b8404da..07db0ce 100644 --- a/crates/primp-python/src/utils.rs +++ b/crates/primp-python/src/utils.rs @@ -1,14 +1,14 @@ use std::path::Path; use std::path::PathBuf; +use std::sync::LazyLock; use std::sync::Mutex; use ::primp::Certificate; use mime::Mime; -use once_cell::sync::Lazy; /// Thread-safe cache for CA certificates, keyed by file path -static CA_CERT_CACHE: Lazy>> = Lazy::new(|| Mutex::new(None)); -static CA_CERTS: Lazy>>> = Lazy::new(|| Mutex::new(None)); +static CA_CERT_CACHE: LazyLock>> = LazyLock::new(|| Mutex::new(None)); +static CA_CERTS: LazyLock>>> = LazyLock::new(|| Mutex::new(None)); /// Environment variables to check for CA certificate paths (in order). const CA_CERT_ENV_VARS: &[&str] = &["PRIMP_CA_BUNDLE", "SSL_CERT_FILE", "CURL_CA_BUNDLE"]; @@ -18,8 +18,8 @@ const CA_CERT_ENV_VARS: &[&str] = &["PRIMP_CA_BUNDLE", "SSL_CERT_FILE", "CURL_CA /// The certificates are loaded once and cached in memory. /// Subsequent calls with the same path return the cached certificates. pub fn load_ca_certs_from_file(ca_cert_path: &Path) -> Option> { - let mut cache_path = CA_CERT_CACHE.lock().unwrap(); - let mut cache_certs = CA_CERTS.lock().unwrap(); + let mut cache_path = CA_CERT_CACHE.lock().unwrap_or_else(|e| e.into_inner()); + let mut cache_certs = CA_CERTS.lock().unwrap_or_else(|e| e.into_inner()); let input_path_buf = ca_cert_path.to_path_buf(); diff --git a/crates/primp-reqwest/Cargo.toml b/crates/primp-reqwest/Cargo.toml index cdae672..0fc06e4 100644 --- a/crates/primp-reqwest/Cargo.toml +++ b/crates/primp-reqwest/Cargo.toml @@ -8,7 +8,7 @@ authors = ["deedy5", "Sean McArthur "] readme = "README.md" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" autotests = true include = [ "README.md", diff --git a/crates/primp-rustls/rustls-provider-test/Cargo.toml b/crates/primp-rustls/rustls-provider-test/Cargo.toml index 7be76d8..6ee6a28 100644 --- a/crates/primp-rustls/rustls-provider-test/Cargo.toml +++ b/crates/primp-rustls/rustls-provider-test/Cargo.toml @@ -2,7 +2,7 @@ name = "rustls-provider-test" version = "0.1.0" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" license = "Apache-2.0 OR ISC OR MIT" description = "Integration tests for Rustls cryptography providers" publish = false diff --git a/crates/primp-rustls/rustls-test/Cargo.toml b/crates/primp-rustls/rustls-test/Cargo.toml index 227e23c..51fd55c 100644 --- a/crates/primp-rustls/rustls-test/Cargo.toml +++ b/crates/primp-rustls/rustls-test/Cargo.toml @@ -2,7 +2,7 @@ name = "rustls-test" version = "0.1.0" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" publish = false [dependencies] diff --git a/crates/primp-rustls/rustls/Cargo.toml b/crates/primp-rustls/rustls/Cargo.toml index 332c88e..9197734 100644 --- a/crates/primp-rustls/rustls/Cargo.toml +++ b/crates/primp-rustls/rustls/Cargo.toml @@ -2,7 +2,7 @@ name = "primp-rustls" version = "0.23.40" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" license = "Apache-2.0 OR ISC OR MIT" readme = "README.md" description = "A fork of the original rustls crate." diff --git a/crates/primp-tokio-rustls/Cargo.toml b/crates/primp-tokio-rustls/Cargo.toml index 6eecddb..51907fb 100644 --- a/crates/primp-tokio-rustls/Cargo.toml +++ b/crates/primp-tokio-rustls/Cargo.toml @@ -5,7 +5,7 @@ license = "MIT OR Apache-2.0" description = "Fork of tokio-rustls" categories = ["asynchronous", "cryptography", "network-programming"] edition = "2021" -rust-version = "1.84" +rust-version = "1.89" # Aliases [lib] diff --git a/crates/primp/Cargo.toml b/crates/primp/Cargo.toml index f41ccfd..51c826b 100644 --- a/crates/primp/Cargo.toml +++ b/crates/primp/Cargo.toml @@ -9,7 +9,7 @@ authors = ["deedy5"] readme = "README.md" license = "MIT" edition = "2021" -rust-version = "1.84" +rust-version = "1.89" autotests = true include = [ "README.md", diff --git a/crates/primp/src/imp/chrome/mod.rs b/crates/primp/src/imp/chrome/mod.rs index 1899c76..41f7acd 100644 --- a/crates/primp/src/imp/chrome/mod.rs +++ b/crates/primp/src/imp/chrome/mod.rs @@ -133,6 +133,7 @@ pub(crate) fn build_chrome_settings( gzip: true, brotli: true, zstd: true, + deflate: true, } } diff --git a/crates/primp/src/imp/edge/mod.rs b/crates/primp/src/imp/edge/mod.rs index edeb09e..2d7996c 100644 --- a/crates/primp/src/imp/edge/mod.rs +++ b/crates/primp/src/imp/edge/mod.rs @@ -134,6 +134,7 @@ pub(crate) fn build_edge_settings( gzip: true, brotli: true, zstd: true, + deflate: true, } } diff --git a/crates/primp/src/imp/firefox/mod.rs b/crates/primp/src/imp/firefox/mod.rs index 36609db..749aaf9 100644 --- a/crates/primp/src/imp/firefox/mod.rs +++ b/crates/primp/src/imp/firefox/mod.rs @@ -108,6 +108,7 @@ pub(crate) fn build_firefox_settings( gzip: true, brotli: true, zstd: true, + deflate: true, } } diff --git a/crates/primp/src/imp/mod.rs b/crates/primp/src/imp/mod.rs index a6e5068..01a19c0 100644 --- a/crates/primp/src/imp/mod.rs +++ b/crates/primp/src/imp/mod.rs @@ -59,6 +59,8 @@ pub struct BrowserSettings { pub brotli: bool, /// Whether to enable zstd compression pub zstd: bool, + /// Whether to enable deflate compression + pub deflate: bool, } impl std::fmt::Debug for BrowserSettings { @@ -70,6 +72,7 @@ impl std::fmt::Debug for BrowserSettings { .field("gzip", &self.gzip) .field("brotli", &self.brotli) .field("zstd", &self.zstd) + .field("deflate", &self.deflate) .finish() } } @@ -346,6 +349,7 @@ pub fn random_impersonate_os() -> ImpersonateOS { /// # Returns /// /// BrowserSettings with TLS, HTTP/2, and header configuration +#[cfg(feature = "impersonate")] pub fn get_browser_settings( version: Impersonate, os_type: Option, diff --git a/crates/primp/src/imp/opera/mod.rs b/crates/primp/src/imp/opera/mod.rs index 76d3d78..3bb9fb6 100644 --- a/crates/primp/src/imp/opera/mod.rs +++ b/crates/primp/src/imp/opera/mod.rs @@ -150,6 +150,7 @@ pub(crate) fn build_opera_settings( gzip: true, brotli: true, zstd: true, + deflate: true, } } diff --git a/crates/primp/src/imp/safari/mod.rs b/crates/primp/src/imp/safari/mod.rs index 292bfa9..3429d78 100644 --- a/crates/primp/src/imp/safari/mod.rs +++ b/crates/primp/src/imp/safari/mod.rs @@ -127,6 +127,7 @@ pub(crate) fn build_safari_settings( gzip: true, brotli: true, zstd: false, + deflate: true, } } diff --git a/crates/primp/src/lib.rs b/crates/primp/src/lib.rs index 0d1167a..68400a1 100644 --- a/crates/primp/src/lib.rs +++ b/crates/primp/src/lib.rs @@ -28,7 +28,7 @@ pub mod imp; pub use imp::{BrowserSettings, Http2Data, Impersonate, ImpersonateOS}; /// Re-export h2 frame types used in the public HTTP/2 API -#[cfg(feature = "http2")] +#[cfg(all(feature = "http2", not(target_arch = "wasm32")))] pub use h2::frame::{ PseudoId, PseudoOrder, PseudoOrderBuilder, SettingId, SettingsOrder, SettingsOrderBuilder, }; @@ -181,6 +181,7 @@ impl ClientBuilder { } /// Disable auto deflate decompression. + #[cfg(feature = "deflate")] pub fn no_deflate(mut self) -> Self { self.inner = self.inner.no_deflate(); self @@ -349,12 +350,14 @@ impl ClientBuilder { } /// Sets the HTTP/2 SETTINGS frame order for fingerprinting. + #[cfg(not(target_arch = "wasm32"))] pub fn http2_settings_order(mut self, order: h2::frame::SettingsOrder) -> Self { self.inner = self.inner.http2_settings_order(order); self } /// Sets the HTTP/2 pseudo-header order for fingerprinting. + #[cfg(not(target_arch = "wasm32"))] pub fn http2_headers_pseudo_order(mut self, order: h2::frame::PseudoOrder) -> Self { self.inner = self.inner.http2_headers_pseudo_order(order); self @@ -568,6 +571,9 @@ fn apply_impersonation( if !settings.zstd { builder = builder.no_zstd(); } + if !settings.deflate { + builder = builder.no_deflate(); + } builder.build() }