actix_extended_session/
session.rs1use crate::config::SessionLifecycle;
2use actix_utils::future::{ready, Ready};
3use actix_web::{
4 body::BoxBody,
5 dev::{Extensions, Payload, ServiceRequest, ServiceResponse},
6 error::Error,
7 FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
8};
9use anyhow::Context;
10use derive_more::{Display, From};
11use serde::de::DeserializeOwned;
12use serde_json::{Map, Value};
13use std::{
14 cell::{Ref, RefCell},
15 error::Error as StdError,
16 mem,
17 rc::Rc,
18};
19
20#[derive(Clone)]
47pub struct Session(Rc<RefCell<SessionInner>>);
48
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
51pub enum SessionStatus {
52 Changed,
54
55 Purged,
60
61 Renewed,
66
67 #[default]
69 Unchanged,
70}
71
72#[derive(Default)]
73struct SessionInner {
74 state: Map<String, Value>,
75 status: SessionStatus,
76 lifecycle: SessionLifecycle,
77}
78
79impl Session {
80 pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, SessionGetError> {
84 if let Some(value) = self.0.borrow().state.get(key) {
85 Ok(Some(
86 serde_json::from_value::<T>(value.clone())
87 .with_context(|| {
88 format!(
89 "Failed to deserialize the JSON-encoded session data attached to key \
90 `{}` as a `{}` type",
91 key,
92 std::any::type_name::<T>()
93 )
94 })
95 .map_err(SessionGetError)?,
96 ))
97 } else {
98 Ok(None)
99 }
100 }
101
102 pub fn entries(&self) -> Ref<'_, Map<String, Value>> {
106 Ref::map(self.0.borrow(), |inner| &inner.state)
107 }
108
109 pub fn status(&self) -> SessionStatus {
111 Ref::map(self.0.borrow(), |inner| &inner.status).clone()
112 }
113
114 pub fn insert(&self, key: impl Into<String>, value: Value) {
116 let mut inner = self.0.borrow_mut();
117
118 if inner.status != SessionStatus::Purged {
119 if inner.status != SessionStatus::Renewed {
120 inner.status = SessionStatus::Changed;
121 }
122
123 inner.state.insert(key.into(), value);
124 }
125 }
126
127 pub fn remove(&self, key: &str) -> Option<Value> {
131 let mut inner = self.0.borrow_mut();
132
133 if inner.status != SessionStatus::Purged {
134 if inner.status != SessionStatus::Renewed {
135 inner.status = SessionStatus::Changed;
136 }
137 return inner.state.remove(key);
138 }
139
140 None
141 }
142
143 pub fn clear(&self) {
145 let mut inner = self.0.borrow_mut();
146
147 if inner.status != SessionStatus::Purged {
148 if inner.status != SessionStatus::Renewed {
149 inner.status = SessionStatus::Changed;
150 }
151 inner.state.clear()
152 }
153 }
154
155 pub fn purge(&self) {
157 let mut inner = self.0.borrow_mut();
158 inner.status = SessionStatus::Purged;
159 inner.state.clear();
160 }
161
162 pub fn renew(&self) {
164 let mut inner = self.0.borrow_mut();
165
166 if inner.status != SessionStatus::Purged {
167 inner.status = SessionStatus::Renewed;
168 }
169 }
170
171 pub fn set_lifecycle(&self, next_lifecycle: SessionLifecycle) {
173 let mut inner = self.0.borrow_mut();
174
175 if inner.status != SessionStatus::Purged {
176 if inner.status != SessionStatus::Renewed {
177 inner.status = SessionStatus::Changed;
178 }
179
180 inner.lifecycle = next_lifecycle;
181 }
182 }
183
184 pub fn get_lifecycle(&self) -> SessionLifecycle {
186 let inner = self.0.borrow_mut();
187 inner.lifecycle.to_owned()
188 }
189
190 #[allow(clippy::needless_pass_by_ref_mut)]
194 pub(crate) fn set_session(
195 req: &mut ServiceRequest,
196 data: impl IntoIterator<Item = (String, Value)>,
197 lifecycle: SessionLifecycle,
198 ) {
199 let session = Session::get_session(&mut req.extensions_mut());
200 let mut inner = session.0.borrow_mut();
201 inner.state.extend(data);
202 inner.lifecycle = lifecycle;
203 }
204
205 #[allow(clippy::needless_pass_by_ref_mut)]
211 pub(crate) fn get_changes<B>(
212 res: &mut ServiceResponse<B>,
213 ) -> (SessionLifecycle, SessionStatus, Map<String, Value>) {
214 if let Some(s_impl) = res
215 .request()
216 .extensions()
217 .get::<Rc<RefCell<SessionInner>>>()
218 {
219 let state = mem::take(&mut s_impl.borrow_mut().state);
220 (
221 s_impl.borrow().lifecycle.clone(),
222 s_impl.borrow().status.clone(),
223 state,
224 )
225 } else {
226 (
227 SessionLifecycle::PersistentSession,
228 SessionStatus::Unchanged,
229 Map::new(),
230 )
231 }
232 }
233
234 pub(crate) fn get_session(extensions: &mut Extensions) -> Session {
235 if let Some(s_impl) = extensions.get::<Rc<RefCell<SessionInner>>>() {
236 return Session(Rc::clone(s_impl));
237 }
238
239 let inner = Rc::new(RefCell::new(SessionInner::default()));
240 extensions.insert(inner.clone());
241
242 Session(inner)
243 }
244}
245
246impl FromRequest for Session {
268 type Error = Error;
269 type Future = Ready<Result<Session, Error>>;
270
271 #[inline]
272 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
273 ready(Ok(Session::get_session(&mut req.extensions_mut())))
274 }
275}
276
277#[derive(Debug, Display, From)]
279#[display(fmt = "{_0}")]
280pub struct SessionGetError(anyhow::Error);
281
282impl StdError for SessionGetError {
283 fn source(&self) -> Option<&(dyn StdError + 'static)> {
284 Some(self.0.as_ref())
285 }
286}
287
288impl ResponseError for SessionGetError {
289 fn error_response(&self) -> HttpResponse<BoxBody> {
290 HttpResponse::new(self.status_code())
291 }
292}