browser_window/
cookie.rs

1//! Module for dealing with cookies.
2
3use std::{borrow::Cow, marker::PhantomData, ops::*, time::SystemTime};
4
5use futures_channel::oneshot;
6
7use crate::core::cookie::*;
8
9pub struct Cookie(CookieImpl);
10
11pub struct CookieJar(CookieJarImpl);
12
13pub struct CookieIterator<'a> {
14	inner: CookieIteratorImpl,
15	_phantom: PhantomData<&'a u8>,
16}
17
18impl Cookie {
19	pub fn new(name: &str, value: &str) -> Self { Self(CookieImpl::new(name, value)) }
20
21	pub fn creation_time(&self) -> SystemTime { self.0.creation_time() }
22
23	pub fn expires(&self) -> Option<SystemTime> { self.0.expires() }
24
25	pub fn domain(&self) -> Cow<'_, str> { self.0.domain() }
26
27	pub fn is_http_only(&self) -> bool { self.0.is_http_only() }
28
29	pub fn name(&self) -> Cow<'_, str> { self.0.name() }
30
31	pub fn path(&self) -> Cow<'_, str> { self.0.path() }
32
33	pub fn is_secure(&self) -> bool { self.0.is_secure() }
34
35	pub fn value(&self) -> Cow<'_, str> { self.0.value() }
36
37	pub fn make_http_only(&mut self) -> &mut Self {
38		self.0.make_http_only();
39		self
40	}
41
42	pub fn make_secure(&mut self) -> &mut Self {
43		self.0.make_secure();
44		self
45	}
46
47	pub fn set_creation_time(&mut self, time: &SystemTime) -> &mut Self {
48		self.0.set_creation_time(time);
49		self
50	}
51
52	pub fn set_expires(&mut self, time: &SystemTime) -> &mut Self {
53		self.0.set_expires(time);
54		self
55	}
56
57	pub fn set_domain(&mut self, domain: &str) -> &mut Self {
58		self.0.set_domain(domain);
59		self
60	}
61
62	pub fn set_name(&mut self, name: &str) -> &mut Self {
63		self.0.set_name(name);
64		self
65	}
66
67	pub fn set_path(&mut self, path: &str) -> &mut Self {
68		self.0.set_path(path);
69		self
70	}
71
72	pub fn set_value(&mut self, value: &str) -> &mut Self {
73		self.0.set_value(value);
74		self
75	}
76}
77
78impl Drop for Cookie {
79	fn drop(&mut self) { self.0.free(); }
80}
81
82impl<'a> CookieIterator<'a> {
83	pub async fn next(&mut self) -> Option<Cookie> {
84		let (tx, rx) = oneshot::channel::<Option<Cookie>>();
85
86		let more = self._next(|result| {
87			if let Err(_) = tx.send(result) {
88				panic!("unable to send cookie iterator next result back");
89			}
90		});
91
92		if !more {
93			return None;
94		}
95
96		rx.await.unwrap()
97	}
98
99	fn _next<H>(&mut self, on_next: H) -> bool
100	where
101		H: FnOnce(Option<Cookie>),
102	{
103		let data = Box::into_raw(Box::new(on_next));
104
105		let called_closure = self
106			.inner
107			.next(cookie_iterator_next_handler::<H>, data as _);
108
109		if !called_closure {
110			unsafe {
111				let _ = Box::from_raw(data);
112			}
113		}
114
115		called_closure
116	}
117}
118
119impl<'a> Drop for CookieIterator<'a> {
120	fn drop(&mut self) { self.inner.free(); }
121}
122
123impl CookieJar {
124	/// Deletes all cookies.
125	/// If `url` is not an empty string, only the cookies of the given url will
126	/// be deleted.
127	pub async fn clear(&mut self, url: &str) -> usize { self.delete(url, "").await }
128
129	/// Like `clear`, but with `url` set empty.
130	pub async fn clear_all(&mut self) -> usize { self.clear("").await }
131
132	fn _delete<H>(&mut self, url: &str, name: &str, on_complete: H)
133	where
134		H: FnOnce(usize),
135	{
136		let data = Box::into_raw(Box::new(on_complete));
137
138		self.0
139			.delete(url, name, cookie_delete_callback::<H>, data as _);
140	}
141
142	/// Deletes all cookies with the given `name`.
143	/// If `url` is not empty, only the cookie with the given `name` at that
144	/// `url` will be deleted. If `name` is empty, all cookies at the given
145	/// `url` will be deleted. If both `url` and `name` are empty, all cookies
146	/// will be deleted.
147	pub async fn delete(&mut self, url: &str, name: &str) -> usize {
148		let (tx, rx) = oneshot::channel::<usize>();
149
150		self._delete(url, name, |result| {
151			tx.send(result)
152				.expect("unable to send back cookie delete count");
153		});
154
155		rx.await.unwrap()
156	}
157
158	/// Like `delete`, but with `url` set empty.
159	pub async fn delete_all(&mut self, name: &str) -> usize { self.delete("", name).await }
160
161	/// Finds the first cookie that has the given `name` in the given `url`.
162	/// If `include_http_only` is set to `false`, a `HttpOnly` cookie will not
163	/// be found.
164	pub async fn find(&self, url: &str, name: &str, include_http_only: bool) -> Option<Cookie> {
165		let mut iter = self.iter(url, include_http_only);
166
167		while let Some(cookie) = iter.next().await {
168			if cookie.name() == name {
169				return Some(cookie);
170			}
171		}
172
173		None
174	}
175
176	/// Finds the first cookie that has the given `name`.
177	pub async fn find_from_all(&self, name: &str) -> Option<Cookie> {
178		let mut iter = self.iter_all();
179
180		while let Some(cookie) = iter.next().await {
181			if cookie.name() == name {
182				return Some(cookie);
183			}
184		}
185
186		None
187	}
188
189	pub(crate) fn global() -> Option<Self> { CookieJarImpl::global().map(|i| Self(i)) }
190
191	/// Returns a `CookieIterator` that iterates over cookies asynchronously.
192	/// The `CookieIterator` has an async `next` function that you can use.
193	///
194	/// # Example
195	/// ```ignore
196	/// if let Some(cookie_jar) = app.cookie_jar() {
197	/// 	let mut iterator = cookie_jar.iter("http://localhost/", true);
198	///
199	/// 	while let Some(cookie) = iterator.next().await {
200	/// 		// ... do something with `cookie`
201	/// 	}
202	/// }
203	/// ```
204	pub fn iter<'a>(&'a self, url: &str, include_http_only: bool) -> CookieIterator<'a> {
205		let inner = self.0.iterator(url, include_http_only);
206
207		CookieIterator {
208			inner,
209			_phantom: PhantomData,
210		}
211	}
212
213	/// Returns a `CookieIterator` that iterators over cookies asynchronously.
214	/// Like `iter`, but iterates over all cookies from any url.
215	pub fn iter_all<'a>(&'a self) -> CookieIterator<'a> {
216		let inner = self.0.iterator_all();
217
218		CookieIterator {
219			inner,
220			_phantom: PhantomData,
221		}
222	}
223
224	fn _store<'a, H>(&mut self, url: &str, cookie: &Cookie, on_complete: H)
225	where
226		H: FnOnce(Result<(), CookieStorageError>) + 'a,
227	{
228		let data = Box::into_raw(Box::new(on_complete));
229
230		self.0.store(
231			url.into(),
232			&cookie.0,
233			Some(cookie_store_callback::<'a, H>),
234			data as _,
235		);
236	}
237
238	/// Stores the given `cookie` for the given `url`.
239	pub async fn store(&mut self, url: &str, cookie: &Cookie) -> Result<(), CookieStorageError> {
240		let (tx, rx) = oneshot::channel::<Result<(), CookieStorageError>>();
241
242		self._store(url, cookie, |result| {
243			tx.send(result)
244				.expect("unable to retrieve cookie storage error");
245		});
246
247		rx.await.unwrap()
248	}
249}
250
251impl Drop for CookieJar {
252	fn drop(&mut self) { self.0.free(); }
253}
254
255unsafe fn cookie_delete_callback<'a, H>(_handle: CookieJarImpl, cb_data: *mut (), deleted: usize)
256where
257	H: FnOnce(usize) + 'a,
258{
259	let data_ptr = cb_data as *mut H;
260	let data: Box<H> = Box::from_raw(data_ptr);
261
262	(*data)(deleted);
263}
264
265unsafe fn cookie_store_callback<'a, H>(
266	_handle: CookieJarImpl, cb_data: *mut (), result: Result<(), CookieStorageError>,
267) where
268	H: FnOnce(Result<(), CookieStorageError>) + 'a,
269{
270	let data_ptr = cb_data as *mut H;
271	let data: Box<H> = Box::from_raw(data_ptr);
272
273	(*data)(result);
274}
275
276unsafe fn cookie_iterator_next_handler<H>(
277	_handle: CookieIteratorImpl, cb_data: *mut (), cookie: Option<CookieImpl>,
278) where
279	H: FnOnce(Option<Cookie>),
280{
281	let data_ptr = cb_data as *mut H;
282	let data: Box<H> = Box::from_raw(data_ptr);
283
284	(*data)(cookie.map(|c| Cookie(c)));
285}