From 7ef9a007dbb80992b6751c302601d4d1decde6b2 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 28 Oct 2024 17:53:43 +0300 Subject: [PATCH 1/6] enhance(http): persist cookies on disk closes tauri-apps/tauri#11518 --- .changes/persist-cookies.md | 6 +++++ Cargo.lock | 13 +++++++++++ plugins/http/Cargo.toml | 3 ++- plugins/http/src/lib.rs | 46 +++++++++++++++++++++++++++++++++++-- 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 .changes/persist-cookies.md diff --git a/.changes/persist-cookies.md b/.changes/persist-cookies.md new file mode 100644 index 000000000..d81a85a23 --- /dev/null +++ b/.changes/persist-cookies.md @@ -0,0 +1,6 @@ +--- +"http": "patch" +--- + +Persist cookies to disk and load it on next app start. + diff --git a/Cargo.lock b/Cargo.lock index 2522f63da..3b9a036ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5323,6 +5323,18 @@ dependencies = [ "windows-registry 0.2.0", ] +[[package]] +name = "reqwest_cookie_store" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b36498c7452f11b1833900f31fbb01fc46be20992a50269c88cf59d79f54e9" +dependencies = [ + "bytes", + "cookie_store", + "reqwest", + "url", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -6907,6 +6919,7 @@ dependencies = [ "http", "regex", "reqwest", + "reqwest_cookie_store", "schemars", "serde", "serde_json", diff --git a/plugins/http/Cargo.toml b/plugins/http/Cargo.toml index 8c1801a38..5c7a6c839 100644 --- a/plugins/http/Cargo.toml +++ b/plugins/http/Cargo.toml @@ -41,6 +41,7 @@ http = "1" reqwest = { version = "0.12", default-features = false } url = { workspace = true } data-url = "0.3" +reqwest_cookie_store = { version = "0.8", optional = true } [features] default = [ @@ -61,7 +62,7 @@ rustls-tls-manual-roots = ["reqwest/rustls-tls-manual-roots"] rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"] rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"] blocking = ["reqwest/blocking"] -cookies = ["reqwest/cookies"] +cookies = ["reqwest/cookies", "dep:reqwest_cookie_store"] gzip = ["reqwest/gzip"] brotli = ["reqwest/brotli"] deflate = ["reqwest/deflate"] diff --git a/plugins/http/src/lib.rs b/plugins/http/src/lib.rs index cdffeb6e1..d19bc2adf 100644 --- a/plugins/http/src/lib.rs +++ b/plugins/http/src/lib.rs @@ -20,21 +20,63 @@ mod scope; pub(crate) struct Http { #[cfg(feature = "cookies")] - cookies_jar: std::sync::Arc, + cookies_jar_path: std::path::PathBuf, + #[cfg(feature = "cookies")] + cookies_jar: std::sync::Arc, } pub fn init() -> TauriPlugin { Builder::::new("http") .setup(|app, _| { + #[cfg(feature = "cookies")] + let (cookies_jar_path, cookies_jar) = { + use reqwest_cookie_store::*; + use std::fs::File; + use std::io::BufReader; + use std::sync::Arc; + + let cache_dir = app.path().app_cache_dir()?; + std::fs::create_dir_all(&cache_dir)?; + + let path = cache_dir.join("Cookies"); + let file = File::options() + .create(true) + .write(true) + .read(true) + .open(&path)?; + + let reader = BufReader::new(file); + let store = CookieStore::load_json(reader).map_err(|e| e.to_string())?; + + (path, Arc::new(CookieStoreMutex::new(store))) + }; + let state = Http { #[cfg(feature = "cookies")] - cookies_jar: std::sync::Arc::new(reqwest::cookie::Jar::default()), + cookies_jar_path, + #[cfg(feature = "cookies")] + cookies_jar, }; app.manage(state); Ok(()) }) + .on_event(|app, event| { + #[cfg(feature = "cookies")] + if let tauri::RunEvent::Exit = event { + use std::fs::File; + use std::io::BufWriter; + + let state = app.state::(); + + if let Ok(file) = File::create(&state.cookies_jar_path) { + let store = state.cookies_jar.lock().expect("poisoned cookie jar mutex"); + let mut writer = BufWriter::new(file); + let _ = store.save_json(&mut writer); + } + } + }) .invoke_handler(tauri::generate_handler![ commands::fetch, commands::fetch_cancel, From 24f1e76b7116aaf2fefe45a0990b79bcd62b59fd Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 28 Oct 2024 18:01:19 +0300 Subject: [PATCH 2/6] clippy --- plugins/http/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/http/src/lib.rs b/plugins/http/src/lib.rs index d19bc2adf..7c27075eb 100644 --- a/plugins/http/src/lib.rs +++ b/plugins/http/src/lib.rs @@ -41,7 +41,7 @@ pub fn init() -> TauriPlugin { let path = cache_dir.join("Cookies"); let file = File::options() .create(true) - .write(true) + .append(true) .read(true) .open(&path)?; From 514afdbf0f89cca1ec83148aa99529d27786d841 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 9 Dec 2024 02:45:15 +0200 Subject: [PATCH 3/6] inline reqwest_cookie_store to fix clippy --- Cargo.lock | 15 +---- plugins/http/Cargo.toml | 5 +- plugins/http/src/lib.rs | 13 ++-- plugins/http/src/reqwest_cookie_store.rs | 83 ++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 plugins/http/src/reqwest_cookie_store.rs diff --git a/Cargo.lock b/Cargo.lock index c460f3bbd..a7bc742c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5041,18 +5041,6 @@ dependencies = [ "windows-registry 0.2.0", ] -[[package]] -name = "reqwest_cookie_store" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b36498c7452f11b1833900f31fbb01fc46be20992a50269c88cf59d79f54e9" -dependencies = [ - "bytes", - "cookie_store", - "reqwest", - "url", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -6632,11 +6620,12 @@ dependencies = [ name = "tauri-plugin-http" version = "2.0.4" dependencies = [ + "bytes", + "cookie_store", "data-url", "http", "regex", "reqwest", - "reqwest_cookie_store", "schemars", "serde", "serde_json", diff --git a/plugins/http/Cargo.toml b/plugins/http/Cargo.toml index b609913c7..c88dc77c7 100644 --- a/plugins/http/Cargo.toml +++ b/plugins/http/Cargo.toml @@ -41,7 +41,8 @@ http = "1" reqwest = { version = "0.12", default-features = false } url = { workspace = true } data-url = "0.3" -reqwest_cookie_store = { version = "0.8", optional = true } +cookie_store = { version = "0.21.1", optional = true, features = ["serde"] } +bytes = { version = "1.9", optional = true } tracing = { workspace = true, optional = true } [features] @@ -63,7 +64,7 @@ rustls-tls-manual-roots = ["reqwest/rustls-tls-manual-roots"] rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"] rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"] blocking = ["reqwest/blocking"] -cookies = ["reqwest/cookies", "dep:reqwest_cookie_store"] +cookies = ["reqwest/cookies", "dep:cookie_store", "dep:bytes"] gzip = ["reqwest/gzip"] brotli = ["reqwest/brotli"] deflate = ["reqwest/deflate"] diff --git a/plugins/http/src/lib.rs b/plugins/http/src/lib.rs index 51f82630c..131d10d45 100644 --- a/plugins/http/src/lib.rs +++ b/plugins/http/src/lib.rs @@ -14,13 +14,15 @@ pub use error::{Error, Result}; mod commands; mod error; +#[cfg(feature = "cookies")] +mod reqwest_cookie_store; mod scope; pub(crate) struct Http { #[cfg(feature = "cookies")] cookies_jar_path: std::path::PathBuf, #[cfg(feature = "cookies")] - cookies_jar: std::sync::Arc, + cookies_jar: std::sync::Arc, } pub fn init() -> TauriPlugin { @@ -28,7 +30,7 @@ pub fn init() -> TauriPlugin { .setup(|app, _| { #[cfg(feature = "cookies")] let (cookies_jar_path, cookies_jar) = { - use reqwest_cookie_store::*; + use crate::reqwest_cookie_store::*; use std::fs::File; use std::io::BufReader; use std::sync::Arc; @@ -44,9 +46,9 @@ pub fn init() -> TauriPlugin { .open(&path)?; let reader = BufReader::new(file); - let store = CookieStore::load_json(reader).map_err(|e| e.to_string())?; + let store = CookieStoreMutex::load(reader).map_err(|e| e.to_string())?; - (path, Arc::new(CookieStoreMutex::new(store))) + (path, Arc::new(store)) }; let state = Http { @@ -69,9 +71,8 @@ pub fn init() -> TauriPlugin { let state = app.state::(); if let Ok(file) = File::create(&state.cookies_jar_path) { - let store = state.cookies_jar.lock().expect("poisoned cookie jar mutex"); let mut writer = BufWriter::new(file); - let _ = store.save_json(&mut writer); + let _ = state.cookies_jar.save(&mut writer); } } }) diff --git a/plugins/http/src/reqwest_cookie_store.rs b/plugins/http/src/reqwest_cookie_store.rs new file mode 100644 index 000000000..d52282a6e --- /dev/null +++ b/plugins/http/src/reqwest_cookie_store.rs @@ -0,0 +1,83 @@ +// taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs + +use std::sync::{Mutex, MutexGuard, PoisonError}; + +use cookie_store::{CookieStore, RawCookie, RawCookieParseError}; +use reqwest::header::HeaderValue; +use serde::{Deserialize, Serialize}; + +fn set_cookies( + cookie_store: &mut CookieStore, + cookie_headers: &mut dyn Iterator, + url: &url::Url, +) { + let cookies = cookie_headers.filter_map(|val| { + std::str::from_utf8(val.as_bytes()) + .map_err(RawCookieParseError::from) + .and_then(RawCookie::parse) + .map(|c| c.into_owned()) + .ok() + }); + cookie_store.store_response_cookies(cookies, url); +} + +fn cookies(cookie_store: &CookieStore, url: &url::Url) -> Option { + let s = cookie_store + .get_request_values(url) + .map(|(name, value)| format!("{}={}", name, value)) + .collect::>() + .join("; "); + + if s.is_empty() { + return None; + } + + HeaderValue::from_maybe_shared(bytes::Bytes::from(s)).ok() +} + +/// A [`cookie_store::CookieStore`] wrapped internally by a [`std::sync::Mutex`], suitable for use in +/// async/concurrent contexts. +#[derive(Debug, Serialize, Deserialize)] +pub struct CookieStoreMutex(Mutex); + +impl Default for CookieStoreMutex { + /// Create a new, empty [`CookieStoreMutex`] + fn default() -> Self { + CookieStoreMutex::new(CookieStore::default()) + } +} + +impl CookieStoreMutex { + /// Create a new [`CookieStoreMutex`] from an existing [`cookie_store::CookieStore`]. + pub const fn new(cookie_store: CookieStore) -> CookieStoreMutex { + CookieStoreMutex(Mutex::new(cookie_store)) + } + + /// Lock and get a handle to the contained [`cookie_store::CookieStore`]. + pub fn lock( + &self, + ) -> Result, PoisonError>> { + self.0.lock() + } + + pub fn load(reader: R) -> cookie_store::Result { + cookie_store::serde::load(reader, |c| serde_json::from_str(c)).map(CookieStoreMutex::new) + } + + pub fn save(&self, writer: &mut W) -> cookie_store::Result<()> { + let store = self.lock().expect("poisoned cookie jar mutex"); + cookie_store::serde::save(&store, writer, |c| serde_json::to_string(c)) + } +} + +impl reqwest::cookie::CookieStore for CookieStoreMutex { + fn set_cookies(&self, cookie_headers: &mut dyn Iterator, url: &url::Url) { + let mut store = self.0.lock().unwrap(); + set_cookies(&mut store, cookie_headers, url); + } + + fn cookies(&self, url: &url::Url) -> Option { + let store = self.0.lock().unwrap(); + cookies(&store, url) + } +} From 8f5b6ca57b7fdeacef62814b6103f09c4ef94dcd Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 9 Dec 2024 02:46:41 +0200 Subject: [PATCH 4/6] clippy --- plugins/http/src/reqwest_cookie_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/http/src/reqwest_cookie_store.rs b/plugins/http/src/reqwest_cookie_store.rs index d52282a6e..54c587d3d 100644 --- a/plugins/http/src/reqwest_cookie_store.rs +++ b/plugins/http/src/reqwest_cookie_store.rs @@ -66,7 +66,7 @@ impl CookieStoreMutex { pub fn save(&self, writer: &mut W) -> cookie_store::Result<()> { let store = self.lock().expect("poisoned cookie jar mutex"); - cookie_store::serde::save(&store, writer, |c| serde_json::to_string(c)) + cookie_store::serde::save(&store, writer, serde_json::to_string) } } From f2fcf2b42350c3d2e861bdc17dcfdcc6edd39868 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Mon, 9 Dec 2024 03:51:42 +0200 Subject: [PATCH 5/6] Update .changes/persist-cookies.md --- .changes/persist-cookies.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changes/persist-cookies.md b/.changes/persist-cookies.md index d81a85a23..7160fb7b2 100644 --- a/.changes/persist-cookies.md +++ b/.changes/persist-cookies.md @@ -1,5 +1,6 @@ --- "http": "patch" +"http-js": "patch" --- Persist cookies to disk and load it on next app start. From 10ce3537b460a1e72e8c9063f6ad910a24140664 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Mon, 9 Dec 2024 03:52:42 +0200 Subject: [PATCH 6/6] Update plugins/http/src/reqwest_cookie_store.rs --- plugins/http/src/reqwest_cookie_store.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/http/src/reqwest_cookie_store.rs b/plugins/http/src/reqwest_cookie_store.rs index 54c587d3d..dab24cfe4 100644 --- a/plugins/http/src/reqwest_cookie_store.rs +++ b/plugins/http/src/reqwest_cookie_store.rs @@ -1,3 +1,7 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + // taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs use std::sync::{Mutex, MutexGuard, PoisonError};