1use crate::session::{SessionId, SessionState};
2use crate::session_store::cookie_generator::SessionCookieGenerator;
3use crate::{DefaultSessionCookieGenerator, Error, Session, SessionExpiry};
4use async_trait::async_trait;
5use chrono::Utc;
6use chrono::{DateTime, Duration};
7use std::fmt::Debug;
8use std::marker::PhantomData;
9
10pub(crate) mod cookie_generator;
11
12#[derive(Debug)]
24pub struct SessionStore<
25 SessionData,
26 SessionStoreConnection,
27 CookieGenerator = DefaultSessionCookieGenerator,
28> {
29 cookie_generator: CookieGenerator,
30 session_renewal_strategy: SessionRenewalStrategy,
31 data: PhantomData<SessionData>,
32 connection: PhantomData<SessionStoreConnection>,
33}
34
35#[derive(Clone, Copy, Debug)]
37pub enum SessionRenewalStrategy {
38 Ignore,
41
42 AutomaticRenewal {
46 time_to_live: Duration,
48 maximum_remaining_time_to_live_for_renewal: Duration,
50 },
51}
52
53impl<SessionData, SessionStoreConnection>
54 SessionStore<SessionData, SessionStoreConnection, DefaultSessionCookieGenerator>
55{
56 pub fn new(expiry_strategy: SessionRenewalStrategy) -> Self {
58 Self {
59 cookie_generator: Default::default(),
60 session_renewal_strategy: expiry_strategy,
61 data: Default::default(),
62 connection: Default::default(),
63 }
64 }
65}
66
67impl<SessionData, SessionStoreConnection, CookieGenerator>
68 SessionStore<SessionData, SessionStoreConnection, CookieGenerator>
69{
70 pub fn new_with_cookie_generator(
72 cookie_generator: CookieGenerator,
73 session_renewal_strategy: SessionRenewalStrategy,
74 ) -> Self {
75 Self {
76 cookie_generator,
77 session_renewal_strategy,
78 data: Default::default(),
79 connection: Default::default(),
80 }
81 }
82
83 pub fn session_renewal_strategy(&self) -> &SessionRenewalStrategy {
85 &self.session_renewal_strategy
86 }
87
88 pub fn session_renewal_strategy_mut(&mut self) -> &mut SessionRenewalStrategy {
90 &mut self.session_renewal_strategy
91 }
92}
93
94impl<
95 SessionData: Debug,
96 SessionStoreConnection: SessionStoreConnector<SessionData>,
97 CookieGenerator: SessionCookieGenerator,
98 > SessionStore<SessionData, SessionStoreConnection, CookieGenerator>
99{
100 pub async fn store_session(
106 &self,
107 mut session: Session<SessionData>,
108 connection: &mut SessionStoreConnection,
109 ) -> Result<SessionCookieCommand, Error<SessionStoreConnection::Error>> {
110 if matches!(
111 &session.state,
112 SessionState::NewChanged { .. }
113 | SessionState::Changed { .. }
114 | SessionState::Deleted { .. }
115 ) {
116 if matches!(&session.state, SessionState::NewChanged { .. }) {
120 self.session_renewal_strategy
121 .apply_to_session(&mut session, Utc::now());
122 }
123
124 if let Some(maximum_retries_on_collision) = connection.maximum_retries_on_id_collision()
125 {
126 for _ in 0..maximum_retries_on_collision {
127 match self.try_store_session(&session, connection).await? {
128 WriteSessionResult::Ok(command) => return Ok(command),
129 WriteSessionResult::SessionIdExists => { }
130 }
131 }
132
133 Err(Error::MaximumSessionIdGenerationTriesReached {
134 maximum: maximum_retries_on_collision,
135 })
136 } else {
137 loop {
138 match self.try_store_session(&session, connection).await? {
139 WriteSessionResult::Ok(command) => return Ok(command),
140 WriteSessionResult::SessionIdExists => { }
141 }
142 }
143 }
144 } else {
145 Ok(SessionCookieCommand::DoNothing)
146 }
147 }
148
149 async fn try_store_session(
150 &self,
151 session: &Session<SessionData>,
152 connection: &mut SessionStoreConnection,
153 ) -> Result<WriteSessionResult<SessionCookieCommand>, Error<SessionStoreConnection::Error>>
154 {
155 match &session.state {
156 SessionState::NewChanged { expiry, data } => {
157 let cookie_value = self.cookie_generator.generate_cookie();
158 let id = SessionId::from_cookie_value(&cookie_value);
159 Ok(connection
160 .create_session(&id, expiry, data)
161 .await?
162 .map(|()| SessionCookieCommand::Set {
163 cookie_value,
164 expiry: *expiry,
165 }))
166 }
167 SessionState::Changed {
168 current_id: previous_id,
169 expiry,
170 data,
171 } => {
172 let cookie_value = self.cookie_generator.generate_cookie();
173 let current_id = SessionId::from_cookie_value(&cookie_value);
174 Ok(connection
175 .update_session(¤t_id, previous_id, expiry, data)
176 .await?
177 .map(|()| SessionCookieCommand::Set {
178 cookie_value,
179 expiry: *expiry,
180 }))
181 }
182 SessionState::Deleted { current_id } => {
183 connection.delete_session(current_id).await?;
184 Ok(WriteSessionResult::Ok(SessionCookieCommand::Delete))
185 }
186 SessionState::NewUnchanged { .. }
187 | SessionState::Unchanged { .. }
188 | SessionState::NewDeleted => unreachable!(),
189 SessionState::Invalid => unreachable!("Invalid state is used internally only"),
190 }
191 }
192
193 pub async fn clear_store(
195 &self,
196 connection: &mut SessionStoreConnection,
197 ) -> Result<(), Error<SessionStoreConnection::Error>> {
198 connection.clear().await
199 }
200
201 pub async fn load_session(
208 &self,
209 cookie_value: impl AsRef<str>,
210 connection: &mut SessionStoreConnection,
211 ) -> Result<Option<Session<SessionData>>, Error<SessionStoreConnection::Error>> {
212 if cookie_value.as_ref().as_bytes().len() != CookieGenerator::COOKIE_LENGTH {
213 return Err(Error::WrongCookieLength {
214 expected: CookieGenerator::COOKIE_LENGTH,
215 actual: cookie_value.as_ref().as_bytes().len(),
216 });
217 }
218
219 let session_id = SessionId::from_cookie_value(cookie_value.as_ref());
220 if let Some(mut session) = connection.read_session(session_id).await? {
221 let now = Utc::now();
222 if session.is_expired(now) {
223 return Ok(None);
227 }
228
229 self.session_renewal_strategy
230 .apply_to_session(&mut session, now);
231
232 Ok(Some(session))
233 } else {
234 Ok(None)
235 }
236 }
237}
238
239impl<SessionData, SessionStoreConnection, CookieGenerator: Clone> Clone
240 for SessionStore<SessionData, SessionStoreConnection, CookieGenerator>
241{
242 fn clone(&self) -> Self {
243 Self {
244 cookie_generator: self.cookie_generator.clone(),
245 session_renewal_strategy: self.session_renewal_strategy,
246 data: self.data,
247 connection: self.connection,
248 }
249 }
250}
251
252#[async_trait]
260pub trait SessionStoreConnector<SessionData> {
261 type Error: Debug;
263
264 fn maximum_retries_on_id_collision(&self) -> Option<u32>;
268
269 async fn create_session(
271 &mut self,
272 current_id: &SessionId,
273 expiry: &SessionExpiry,
274 data: &SessionData,
275 ) -> Result<WriteSessionResult, Error<Self::Error>>;
276
277 async fn read_session(
279 &mut self,
280 id: SessionId,
281 ) -> Result<Option<Session<SessionData>>, Error<Self::Error>>;
282
283 async fn update_session(
294 &mut self,
295 current_id: &SessionId,
296 previous_id: &SessionId,
297 expiry: &SessionExpiry,
298 data: &SessionData,
299 ) -> Result<WriteSessionResult, Error<Self::Error>>;
300
301 async fn delete_session(&mut self, id: &SessionId) -> Result<(), Error<Self::Error>>;
303
304 async fn clear(&mut self) -> Result<(), Error<Self::Error>>;
306}
307
308#[derive(Debug)]
311#[must_use]
312pub enum WriteSessionResult<OkData = ()> {
313 Ok(OkData),
315 SessionIdExists,
317}
318
319impl<OkData> WriteSessionResult<OkData> {
320 fn map<OtherOkData>(
321 self,
322 f: impl FnOnce(OkData) -> OtherOkData,
323 ) -> WriteSessionResult<OtherOkData> {
324 match self {
325 Self::Ok(data) => WriteSessionResult::Ok(f(data)),
326 Self::SessionIdExists => WriteSessionResult::SessionIdExists,
327 }
328 }
329}
330
331#[derive(Debug, Eq, PartialEq)]
335#[must_use]
336pub enum SessionCookieCommand {
337 Set {
339 cookie_value: String,
341 expiry: SessionExpiry,
343 },
344 Delete,
346 DoNothing,
349}
350
351impl SessionRenewalStrategy {
352 fn apply_to_session<SessionData: Debug>(
353 &self,
354 session: &mut Session<SessionData>,
355 now: DateTime<Utc>,
356 ) {
357 match self {
358 SessionRenewalStrategy::Ignore => { }
359 SessionRenewalStrategy::AutomaticRenewal {
360 time_to_live,
361 maximum_remaining_time_to_live_for_renewal,
362 } => {
363 let new_expiry = now + *time_to_live;
364 match *session.expiry() {
365 SessionExpiry::DateTime(old_expiry) => {
366 if old_expiry - now <= *maximum_remaining_time_to_live_for_renewal {
368 session.set_expiry(new_expiry);
369 }
370 }
371 SessionExpiry::Never => session.set_expiry(new_expiry),
373 }
374 }
375 }
376 }
377}