async_session/
memory_store.rs1use crate::{async_trait, log, Result, Session, SessionStore};
2use async_lock::RwLock;
3use std::{collections::HashMap, sync::Arc};
4
5#[derive(Debug, Clone)]
12pub struct MemoryStore {
13 inner: Arc<RwLock<HashMap<String, Session>>>,
14}
15
16#[async_trait]
17impl SessionStore for MemoryStore {
18 async fn load_session(&self, cookie_value: String) -> Result<Option<Session>> {
19 let id = Session::id_from_cookie_value(&cookie_value)?;
20 log::trace!("loading session by id `{}`", id);
21 Ok(self
22 .inner
23 .read()
24 .await
25 .get(&id)
26 .cloned()
27 .and_then(Session::validate))
28 }
29
30 async fn store_session(&self, session: Session) -> Result<Option<String>> {
31 log::trace!("storing session by id `{}`", session.id());
32 self.inner
33 .write()
34 .await
35 .insert(session.id().to_string(), session.clone());
36
37 session.reset_data_changed();
38 Ok(session.into_cookie_value())
39 }
40
41 async fn destroy_session(&self, session: Session) -> Result {
42 log::trace!("destroying session by id `{}`", session.id());
43 self.inner.write().await.remove(session.id());
44 Ok(())
45 }
46
47 async fn clear_store(&self) -> Result {
48 log::trace!("clearing memory store");
49 self.inner.write().await.clear();
50 Ok(())
51 }
52}
53
54impl MemoryStore {
55 pub fn new() -> Self {
57 Self {
58 inner: Arc::new(RwLock::new(HashMap::new())),
59 }
60 }
61
62 pub async fn cleanup(&self) -> Result {
66 log::trace!("cleaning up memory store...");
67 let ids_to_delete: Vec<_> = self
68 .inner
69 .read()
70 .await
71 .values()
72 .filter_map(|session| {
73 if session.is_expired() {
74 Some(session.id().to_owned())
75 } else {
76 None
77 }
78 })
79 .collect();
80
81 log::trace!("found {} expired sessions", ids_to_delete.len());
82 for id in ids_to_delete {
83 self.inner.write().await.remove(&id);
84 }
85 Ok(())
86 }
87
88 pub async fn count(&self) -> usize {
100 let data = self.inner.read().await;
101 data.len()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use async_std::task;
109 use std::time::Duration;
110 #[async_std::test]
111 async fn creating_a_new_session_with_no_expiry() -> Result {
112 let store = MemoryStore::new();
113 let mut session = Session::new();
114 session.insert("key", "Hello")?;
115 let cloned = session.clone();
116 let cookie_value = store.store_session(session).await?.unwrap();
117 let loaded_session = store.load_session(cookie_value).await?.unwrap();
118 assert_eq!(cloned.id(), loaded_session.id());
119 assert_eq!("Hello", &loaded_session.get::<String>("key").unwrap());
120 assert!(!loaded_session.is_expired());
121 assert!(loaded_session.validate().is_some());
122 Ok(())
123 }
124
125 #[async_std::test]
126 async fn updating_a_session() -> Result {
127 let store = MemoryStore::new();
128 let mut session = Session::new();
129
130 session.insert("key", "value")?;
131 let cookie_value = store.store_session(session).await?.unwrap();
132
133 let mut session = store.load_session(cookie_value.clone()).await?.unwrap();
134 session.insert("key", "other value")?;
135
136 assert_eq!(store.store_session(session).await?, None);
137 let session = store.load_session(cookie_value).await?.unwrap();
138 assert_eq!(&session.get::<String>("key").unwrap(), "other value");
139
140 Ok(())
141 }
142
143 #[async_std::test]
144 async fn updating_a_session_extending_expiry() -> Result {
145 let store = MemoryStore::new();
146 let mut session = Session::new();
147 session.expire_in(Duration::from_secs(1));
148 let original_expires = session.expiry().unwrap().clone();
149 let cookie_value = store.store_session(session).await?.unwrap();
150
151 let mut session = store.load_session(cookie_value.clone()).await?.unwrap();
152
153 assert_eq!(session.expiry().unwrap(), &original_expires);
154 session.expire_in(Duration::from_secs(3));
155 let new_expires = session.expiry().unwrap().clone();
156 assert_eq!(None, store.store_session(session).await?);
157
158 let session = store.load_session(cookie_value.clone()).await?.unwrap();
159 assert_eq!(session.expiry().unwrap(), &new_expires);
160
161 task::sleep(Duration::from_secs(3)).await;
162 assert_eq!(None, store.load_session(cookie_value).await?);
163
164 Ok(())
165 }
166
167 #[async_std::test]
168 async fn creating_a_new_session_with_expiry() -> Result {
169 let store = MemoryStore::new();
170 let mut session = Session::new();
171 session.expire_in(Duration::from_secs(3));
172 session.insert("key", "value")?;
173 let cloned = session.clone();
174
175 let cookie_value = store.store_session(session).await?.unwrap();
176
177 let loaded_session = store.load_session(cookie_value.clone()).await?.unwrap();
178 assert_eq!(cloned.id(), loaded_session.id());
179 assert_eq!("value", &*loaded_session.get::<String>("key").unwrap());
180
181 assert!(!loaded_session.is_expired());
182
183 task::sleep(Duration::from_secs(3)).await;
184 assert_eq!(None, store.load_session(cookie_value).await?);
185
186 Ok(())
187 }
188
189 #[async_std::test]
190 async fn destroying_a_single_session() -> Result {
191 let store = MemoryStore::new();
192 for _ in 0..3i8 {
193 store.store_session(Session::new()).await?;
194 }
195
196 let cookie = store.store_session(Session::new()).await?.unwrap();
197 assert_eq!(4, store.count().await);
198 let session = store.load_session(cookie.clone()).await?.unwrap();
199 store.destroy_session(session.clone()).await?;
200 assert_eq!(None, store.load_session(cookie).await?);
201 assert_eq!(3, store.count().await);
202
203 assert!(store.destroy_session(session).await.is_ok());
205 Ok(())
206 }
207
208 #[async_std::test]
209 async fn clearing_the_whole_store() -> Result {
210 let store = MemoryStore::new();
211 for _ in 0..3i8 {
212 store.store_session(Session::new()).await?;
213 }
214
215 assert_eq!(3, store.count().await);
216 store.clear_store().await.unwrap();
217 assert_eq!(0, store.count().await);
218
219 Ok(())
220 }
221}