Skip to content

Commit be11ca8

Browse files
committed
prevent race condition
1 parent e76347b commit be11ca8

File tree

3 files changed

+66
-34
lines changed

3 files changed

+66
-34
lines changed

plugins/http/src/commands.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ pub async fn fetch<R: Runtime>(
266266

267267
#[cfg(feature = "cookies")]
268268
{
269-
builder = builder.cookie_provider(Arc::new(state.cookies_jar.clone()));
269+
builder = builder.cookie_provider(state.cookies_jar.clone());
270270
}
271271

272272
let mut request = builder.build()?.request(method.clone(), url);

plugins/http/src/lib.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const COOKIES_FILENAME: &str = ".cookies";
2323

2424
pub(crate) struct Http {
2525
#[cfg(feature = "cookies")]
26-
cookies_jar: crate::reqwest_cookie_store::CookieStoreMutex,
26+
cookies_jar: std::sync::Arc<crate::reqwest_cookie_store::CookieStoreMutex>,
2727
}
2828

2929
pub fn init<R: Runtime>() -> TauriPlugin<R> {
@@ -57,7 +57,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
5757

5858
let state = Http {
5959
#[cfg(feature = "cookies")]
60-
cookies_jar,
60+
cookies_jar: std::sync::Arc::new(cookies_jar),
6161
};
6262

6363
app.manage(state);
@@ -69,9 +69,14 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
6969
if let tauri::RunEvent::Exit = event {
7070
let state = app.state::<Http>();
7171

72-
if let Err(_e) = state.cookies_jar.save() {
73-
#[cfg(feature = "tracing")]
74-
tracing::error!("failed to save cookie jar: {_e}");
72+
match state.cookies_jar.request_save() {
73+
Ok(rx) => {
74+
let _ = rx.recv();
75+
}
76+
Err(_e) => {
77+
#[cfg(feature = "tracing")]
78+
tracing::error!("failed to save cookie jar: {_e}");
79+
}
7580
}
7681
}
7782
})

plugins/http/src/reqwest_cookie_store.rs

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
// taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs
66

77
use std::{
8-
fs::File,
9-
io::BufWriter,
108
path::PathBuf,
11-
sync::{Arc, Mutex, MutexGuard, PoisonError},
9+
sync::{mpsc::Receiver, Mutex},
1210
};
1311

1412
use cookie_store::{CookieStore, RawCookie, RawCookieParseError};
1513
use reqwest::header::HeaderValue;
16-
use serde::{Deserialize, Serialize};
1714

1815
fn set_cookies(
1916
cookie_store: &mut CookieStore,
@@ -46,28 +43,23 @@ fn cookies(cookie_store: &CookieStore, url: &url::Url) -> Option<HeaderValue> {
4643

4744
/// A [`cookie_store::CookieStore`] wrapped internally by a [`std::sync::Mutex`], suitable for use in
4845
/// async/concurrent contexts.
49-
#[derive(Debug, Clone, Serialize, Deserialize)]
46+
#[derive(Debug)]
5047
pub struct CookieStoreMutex {
5148
pub path: PathBuf,
52-
store: Arc<Mutex<CookieStore>>,
49+
store: Mutex<CookieStore>,
50+
save_task: Mutex<Option<CancellableTask>>,
5351
}
5452

5553
impl CookieStoreMutex {
5654
/// Create a new [`CookieStoreMutex`] from an existing [`cookie_store::CookieStore`].
5755
pub fn new(path: PathBuf, cookie_store: CookieStore) -> CookieStoreMutex {
5856
CookieStoreMutex {
5957
path,
60-
store: Arc::new(Mutex::new(cookie_store)),
58+
store: Mutex::new(cookie_store),
59+
save_task: Default::default(),
6160
}
6261
}
6362

64-
/// Lock and get a handle to the contained [`cookie_store::CookieStore`].
65-
pub fn lock(
66-
&self,
67-
) -> Result<MutexGuard<'_, CookieStore>, PoisonError<MutexGuard<'_, CookieStore>>> {
68-
self.store.lock()
69-
}
70-
7163
pub fn load<R: std::io::BufRead>(
7264
path: PathBuf,
7365
reader: R,
@@ -76,31 +68,66 @@ impl CookieStoreMutex {
7668
.map(|store| CookieStoreMutex::new(path, store))
7769
}
7870

79-
pub fn save(&self) -> cookie_store::Result<()> {
80-
let file = File::create(&self.path)?;
81-
let mut writer = BufWriter::new(file);
82-
let store = self.lock().expect("poisoned cookie jar mutex");
83-
cookie_store::serde::save(&store, &mut writer, serde_json::to_string)
71+
fn cookies_to_str(&self) -> Result<String, serde_json::Error> {
72+
let mut cookies = Vec::new();
73+
for cookie in self
74+
.store
75+
.lock()
76+
.expect("poisoned cookie jar mutex")
77+
.iter_unexpired()
78+
{
79+
if cookie.is_persistent() {
80+
cookies.push(cookie.clone());
81+
}
82+
}
83+
serde_json::to_string(&cookies)
84+
}
85+
86+
pub fn request_save(&self) -> cookie_store::Result<Receiver<()>> {
87+
let cookie_str = self.cookies_to_str()?;
88+
let path = self.path.clone();
89+
let (tx, rx) = std::sync::mpsc::channel();
90+
let task = tauri::async_runtime::spawn(async move {
91+
match tokio::fs::write(&path, &cookie_str).await {
92+
Ok(()) => {
93+
let _ = tx.send(());
94+
}
95+
Err(_e) => {
96+
#[cfg(feature = "tracing")]
97+
tracing::error!("failed to save cookie jar: {_e}");
98+
}
99+
}
100+
});
101+
self.save_task
102+
.lock()
103+
.unwrap()
104+
.replace(CancellableTask(task));
105+
Ok(rx)
84106
}
85107
}
86108

87109
impl reqwest::cookie::CookieStore for CookieStoreMutex {
88110
fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, url: &url::Url) {
89-
let mut store = self.store.lock().unwrap();
90-
set_cookies(&mut store, cookie_headers, url);
111+
set_cookies(&mut self.store.lock().unwrap(), cookie_headers, url);
91112

92113
// try to persist cookies immediately asynchronously
93-
let cookies_jar = self.clone();
94-
tauri::async_runtime::spawn(async move {
95-
if let Err(_e) = cookies_jar.save() {
96-
#[cfg(feature = "tracing")]
97-
tracing::error!("failed to save cookie jar: {_e}");
98-
}
99-
});
114+
if let Err(_e) = self.request_save() {
115+
#[cfg(feature = "tracing")]
116+
tracing::error!("failed to save cookie jar: {_e}");
117+
}
100118
}
101119

102120
fn cookies(&self, url: &url::Url) -> Option<HeaderValue> {
103121
let store = self.store.lock().unwrap();
104122
cookies(&store, url)
105123
}
106124
}
125+
126+
#[derive(Debug)]
127+
struct CancellableTask(tauri::async_runtime::JoinHandle<()>);
128+
129+
impl Drop for CancellableTask {
130+
fn drop(&mut self) {
131+
self.0.abort();
132+
}
133+
}

0 commit comments

Comments
 (0)