5
5
// taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs
6
6
7
7
use std:: {
8
- fs:: File ,
9
- io:: BufWriter ,
10
8
path:: PathBuf ,
11
- sync:: { Arc , Mutex , MutexGuard , PoisonError } ,
9
+ sync:: { mpsc :: Receiver , Mutex } ,
12
10
} ;
13
11
14
12
use cookie_store:: { CookieStore , RawCookie , RawCookieParseError } ;
15
13
use reqwest:: header:: HeaderValue ;
16
- use serde:: { Deserialize , Serialize } ;
17
14
18
15
fn set_cookies (
19
16
cookie_store : & mut CookieStore ,
@@ -46,28 +43,23 @@ fn cookies(cookie_store: &CookieStore, url: &url::Url) -> Option<HeaderValue> {
46
43
47
44
/// A [`cookie_store::CookieStore`] wrapped internally by a [`std::sync::Mutex`], suitable for use in
48
45
/// async/concurrent contexts.
49
- #[ derive( Debug , Clone , Serialize , Deserialize ) ]
46
+ #[ derive( Debug ) ]
50
47
pub struct CookieStoreMutex {
51
48
pub path : PathBuf ,
52
- store : Arc < Mutex < CookieStore > > ,
49
+ store : Mutex < CookieStore > ,
50
+ save_task : Mutex < Option < CancellableTask > > ,
53
51
}
54
52
55
53
impl CookieStoreMutex {
56
54
/// Create a new [`CookieStoreMutex`] from an existing [`cookie_store::CookieStore`].
57
55
pub fn new ( path : PathBuf , cookie_store : CookieStore ) -> CookieStoreMutex {
58
56
CookieStoreMutex {
59
57
path,
60
- store : Arc :: new ( Mutex :: new ( cookie_store) ) ,
58
+ store : Mutex :: new ( cookie_store) ,
59
+ save_task : Default :: default ( ) ,
61
60
}
62
61
}
63
62
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
-
71
63
pub fn load < R : std:: io:: BufRead > (
72
64
path : PathBuf ,
73
65
reader : R ,
@@ -76,31 +68,66 @@ impl CookieStoreMutex {
76
68
. map ( |store| CookieStoreMutex :: new ( path, store) )
77
69
}
78
70
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)
84
106
}
85
107
}
86
108
87
109
impl reqwest:: cookie:: CookieStore for CookieStoreMutex {
88
110
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) ;
91
112
92
113
// 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
+ }
100
118
}
101
119
102
120
fn cookies ( & self , url : & url:: Url ) -> Option < HeaderValue > {
103
121
let store = self . store . lock ( ) . unwrap ( ) ;
104
122
cookies ( & store, url)
105
123
}
106
124
}
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