persist cookies immediately

pull/1978/head
Lucas Nogueira 4 months ago
parent ea860c47c0
commit 356b7ece49
No known key found for this signature in database
GPG Key ID: A05EE2227C581CD7

@ -266,7 +266,7 @@ pub async fn fetch<R: Runtime>(
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
{ {
builder = builder.cookie_provider(state.cookies_jar.clone()); builder = builder.cookie_provider(Arc::new(state.cookies_jar));
} }
let mut request = builder.build()?.request(method.clone(), url); let mut request = builder.build()?.request(method.clone(), url);

@ -4,8 +4,6 @@
//! Access the HTTP client written in Rust. //! Access the HTTP client written in Rust.
use std::io::Cursor;
pub use reqwest; pub use reqwest;
use tauri::{ use tauri::{
plugin::{Builder, TauriPlugin}, plugin::{Builder, TauriPlugin},
@ -22,20 +20,17 @@ mod scope;
pub(crate) struct Http { pub(crate) struct Http {
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
cookies_jar_path: std::path::PathBuf, cookies_jar: crate::reqwest_cookie_store::CookieStoreMutex,
#[cfg(feature = "cookies")]
cookies_jar: std::sync::Arc<crate::reqwest_cookie_store::CookieStoreMutex>,
} }
pub fn init<R: Runtime>() -> TauriPlugin<R> { pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::<R>::new("http") Builder::<R>::new("http")
.setup(|app, _| { .setup(|app, _| {
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
let (cookies_jar_path, cookies_jar) = { let cookies_jar = {
use crate::reqwest_cookie_store::*; use crate::reqwest_cookie_store::*;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
use std::sync::Arc;
let cache_dir = app.path().app_cache_dir()?; let cache_dir = app.path().app_cache_dir()?;
std::fs::create_dir_all(&cache_dir)?; std::fs::create_dir_all(&cache_dir)?;
@ -48,22 +43,18 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
.open(&path)?; .open(&path)?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
let store = CookieStoreMutex::load(reader) let store = CookieStoreMutex::load(path.clone(), reader).unwrap_or_else(|_e| {
.or_else(|_e| { #[cfg(feature = "tracing")]
#[cfg(feature = "tracing")] tracing::warn!(
tracing::warn!( "failed to load cookie store: {_e}, falling back to empty store"
"failed to load cookie store: {_e}, falling back to empty store" );
); CookieStoreMutex::new(path, Default::default())
CookieStoreMutex::load(Cursor::new("[]".to_string())) });
})
.map_err(|e| e.to_string())?; store
(path, Arc::new(store))
}; };
let state = Http { let state = Http {
#[cfg(feature = "cookies")]
cookies_jar_path,
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
cookies_jar, cookies_jar,
}; };
@ -75,14 +66,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
.on_event(|app, event| { .on_event(|app, event| {
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
if let tauri::RunEvent::Exit = event { if let tauri::RunEvent::Exit = event {
use std::fs::File;
use std::io::BufWriter;
let state = app.state::<Http>(); let state = app.state::<Http>();
if let Ok(file) = File::create(&state.cookies_jar_path) { if let Err(_e) = state.cookies_jar.save() {
let mut writer = BufWriter::new(file); #[cfg(feature = "tracing")]
let _ = state.cookies_jar.save(&mut writer); tracing::error!("failed to save cookie jar: {_e}");
} }
} }
}) })

@ -4,7 +4,12 @@
// taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs // taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs
use std::sync::{Mutex, MutexGuard, PoisonError}; use std::{
fs::File,
io::BufWriter,
path::PathBuf,
sync::{Arc, Mutex, MutexGuard, PoisonError},
};
use cookie_store::{CookieStore, RawCookie, RawCookieParseError}; use cookie_store::{CookieStore, RawCookie, RawCookieParseError};
use reqwest::header::HeaderValue; use reqwest::header::HeaderValue;
@ -41,47 +46,61 @@ fn cookies(cookie_store: &CookieStore, url: &url::Url) -> Option<HeaderValue> {
/// A [`cookie_store::CookieStore`] wrapped internally by a [`std::sync::Mutex`], suitable for use in /// A [`cookie_store::CookieStore`] wrapped internally by a [`std::sync::Mutex`], suitable for use in
/// async/concurrent contexts. /// async/concurrent contexts.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CookieStoreMutex(Mutex<CookieStore>); pub struct CookieStoreMutex {
pub path: PathBuf,
impl Default for CookieStoreMutex { store: Arc<Mutex<CookieStore>>,
/// Create a new, empty [`CookieStoreMutex`]
fn default() -> Self {
CookieStoreMutex::new(CookieStore::default())
}
} }
impl CookieStoreMutex { impl CookieStoreMutex {
/// Create a new [`CookieStoreMutex`] from an existing [`cookie_store::CookieStore`]. /// Create a new [`CookieStoreMutex`] from an existing [`cookie_store::CookieStore`].
pub const fn new(cookie_store: CookieStore) -> CookieStoreMutex { pub fn new(path: PathBuf, cookie_store: CookieStore) -> CookieStoreMutex {
CookieStoreMutex(Mutex::new(cookie_store)) CookieStoreMutex {
path,
store: Arc::new(Mutex::new(cookie_store)),
}
} }
/// Lock and get a handle to the contained [`cookie_store::CookieStore`]. /// Lock and get a handle to the contained [`cookie_store::CookieStore`].
pub fn lock( pub fn lock(
&self, &self,
) -> Result<MutexGuard<'_, CookieStore>, PoisonError<MutexGuard<'_, CookieStore>>> { ) -> Result<MutexGuard<'_, CookieStore>, PoisonError<MutexGuard<'_, CookieStore>>> {
self.0.lock() self.store.lock()
} }
pub fn load<R: std::io::BufRead>(reader: R) -> cookie_store::Result<CookieStoreMutex> { pub fn load<R: std::io::BufRead>(
cookie_store::serde::load(reader, |c| serde_json::from_str(c)).map(CookieStoreMutex::new) path: PathBuf,
reader: R,
) -> cookie_store::Result<CookieStoreMutex> {
cookie_store::serde::load(reader, |c| serde_json::from_str(c))
.map(|store| CookieStoreMutex::new(path, store))
} }
pub fn save<W: std::io::Write>(&self, writer: &mut W) -> cookie_store::Result<()> { pub fn save(&self) -> cookie_store::Result<()> {
let file = File::create(&self.path)?;
let mut writer = BufWriter::new(file);
let store = self.lock().expect("poisoned cookie jar mutex"); let store = self.lock().expect("poisoned cookie jar mutex");
cookie_store::serde::save(&store, writer, serde_json::to_string) cookie_store::serde::save(&store, &mut writer, serde_json::to_string)
} }
} }
impl reqwest::cookie::CookieStore for CookieStoreMutex { impl reqwest::cookie::CookieStore for CookieStoreMutex {
fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, url: &url::Url) { fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, url: &url::Url) {
let mut store = self.0.lock().unwrap(); let mut store = self.store.lock().unwrap();
set_cookies(&mut store, cookie_headers, url); set_cookies(&mut store, cookie_headers, url);
// try to persist cookies immediately asynchronously
let cookies_jar = self.clone();
tauri::async_runtime::spawn(async move {
if let Err(_e) = cookies_jar.save() {
#[cfg(feature = "tracing")]
tracing::error!("failed to save cookie jar: {_e}");
}
});
} }
fn cookies(&self, url: &url::Url) -> Option<HeaderValue> { fn cookies(&self, url: &url::Url) -> Option<HeaderValue> {
let store = self.0.lock().unwrap(); let store = self.store.lock().unwrap();
cookies(&store, url) cookies(&store, url)
} }
} }

Loading…
Cancel
Save