prevent race condition

pull/1978/head
Lucas Nogueira 4 months ago
parent e76347b85c
commit be11ca8298
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")]
{
builder = builder.cookie_provider(Arc::new(state.cookies_jar.clone()));
builder = builder.cookie_provider(state.cookies_jar.clone());
}
let mut request = builder.build()?.request(method.clone(), url);

@ -23,7 +23,7 @@ const COOKIES_FILENAME: &str = ".cookies";
pub(crate) struct Http {
#[cfg(feature = "cookies")]
cookies_jar: crate::reqwest_cookie_store::CookieStoreMutex,
cookies_jar: std::sync::Arc<crate::reqwest_cookie_store::CookieStoreMutex>,
}
pub fn init<R: Runtime>() -> TauriPlugin<R> {
@ -57,7 +57,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
let state = Http {
#[cfg(feature = "cookies")]
cookies_jar,
cookies_jar: std::sync::Arc::new(cookies_jar),
};
app.manage(state);
@ -69,11 +69,16 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
if let tauri::RunEvent::Exit = event {
let state = app.state::<Http>();
if let Err(_e) = state.cookies_jar.save() {
match state.cookies_jar.request_save() {
Ok(rx) => {
let _ = rx.recv();
}
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("failed to save cookie jar: {_e}");
}
}
}
})
.invoke_handler(tauri::generate_handler![
commands::fetch,

@ -5,15 +5,12 @@
// taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs
use std::{
fs::File,
io::BufWriter,
path::PathBuf,
sync::{Arc, Mutex, MutexGuard, PoisonError},
sync::{mpsc::Receiver, Mutex},
};
use cookie_store::{CookieStore, RawCookie, RawCookieParseError};
use reqwest::header::HeaderValue;
use serde::{Deserialize, Serialize};
fn set_cookies(
cookie_store: &mut CookieStore,
@ -46,10 +43,11 @@ 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
/// async/concurrent contexts.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug)]
pub struct CookieStoreMutex {
pub path: PathBuf,
store: Arc<Mutex<CookieStore>>,
store: Mutex<CookieStore>,
save_task: Mutex<Option<CancellableTask>>,
}
impl CookieStoreMutex {
@ -57,17 +55,11 @@ impl CookieStoreMutex {
pub fn new(path: PathBuf, cookie_store: CookieStore) -> CookieStoreMutex {
CookieStoreMutex {
path,
store: Arc::new(Mutex::new(cookie_store)),
store: Mutex::new(cookie_store),
save_task: Default::default(),
}
}
/// Lock and get a handle to the contained [`cookie_store::CookieStore`].
pub fn lock(
&self,
) -> Result<MutexGuard<'_, CookieStore>, PoisonError<MutexGuard<'_, CookieStore>>> {
self.store.lock()
}
pub fn load<R: std::io::BufRead>(
path: PathBuf,
reader: R,
@ -76,27 +68,53 @@ impl CookieStoreMutex {
.map(|store| CookieStoreMutex::new(path, store))
}
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");
cookie_store::serde::save(&store, &mut writer, serde_json::to_string)
fn cookies_to_str(&self) -> Result<String, serde_json::Error> {
let mut cookies = Vec::new();
for cookie in self
.store
.lock()
.expect("poisoned cookie jar mutex")
.iter_unexpired()
{
if cookie.is_persistent() {
cookies.push(cookie.clone());
}
}
serde_json::to_string(&cookies)
}
pub fn request_save(&self) -> cookie_store::Result<Receiver<()>> {
let cookie_str = self.cookies_to_str()?;
let path = self.path.clone();
let (tx, rx) = std::sync::mpsc::channel();
let task = tauri::async_runtime::spawn(async move {
match tokio::fs::write(&path, &cookie_str).await {
Ok(()) => {
let _ = tx.send(());
}
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("failed to save cookie jar: {_e}");
}
}
});
self.save_task
.lock()
.unwrap()
.replace(CancellableTask(task));
Ok(rx)
}
}
impl reqwest::cookie::CookieStore for CookieStoreMutex {
fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, url: &url::Url) {
let mut store = self.store.lock().unwrap();
set_cookies(&mut store, cookie_headers, url);
set_cookies(&mut self.store.lock().unwrap(), 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() {
if let Err(_e) = self.request_save() {
#[cfg(feature = "tracing")]
tracing::error!("failed to save cookie jar: {_e}");
}
});
}
fn cookies(&self, url: &url::Url) -> Option<HeaderValue> {
@ -104,3 +122,12 @@ impl reqwest::cookie::CookieStore for CookieStoreMutex {
cookies(&store, url)
}
}
#[derive(Debug)]
struct CancellableTask(tauri::async_runtime::JoinHandle<()>);
impl Drop for CancellableTask {
fn drop(&mut self) {
self.0.abort();
}
}

Loading…
Cancel
Save