1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-12-01 10:50:05 +01:00
actix-extras/actix-session/src/lib.rs

211 lines
6.0 KiB
Rust
Raw Normal View History

2019-03-05 19:12:49 +01:00
//! User sessions.
//!
2019-03-06 03:47:18 +01:00
//! Actix provides a general solution for session management. Session
//! middlewares could provide different implementations which could
//! be accessed via general session api.
2019-03-05 19:12:49 +01:00
//!
//! By default, only cookie session backend is implemented. Other
//! backend implementations can be added.
//!
2019-03-06 03:47:18 +01:00
//! In general, you insert a *session* middleware and initialize it
//! , such as a `CookieSessionBackend`. To access session data,
//! [*Session*](struct.Session.html) extractor must be used. Session
//! extractor allows us to get or set session data.
2019-03-05 19:12:49 +01:00
//!
//! ```rust
//! use actix_web::{web, App, HttpServer, HttpResponse, Error};
2019-03-06 03:47:18 +01:00
//! use actix_session::{Session, CookieSession};
2019-03-05 19:12:49 +01:00
//!
2019-03-06 03:47:18 +01:00
//! fn index(session: Session) -> Result<&'static str, Error> {
2019-03-05 19:12:49 +01:00
//! // access session data
2019-03-06 03:47:18 +01:00
//! if let Some(count) = session.get::<i32>("counter")? {
2019-03-05 19:12:49 +01:00
//! println!("SESSION value: {}", count);
2019-03-06 03:47:18 +01:00
//! session.set("counter", count+1)?;
2019-03-05 19:12:49 +01:00
//! } else {
2019-03-06 03:47:18 +01:00
//! session.set("counter", 1)?;
2019-03-05 19:12:49 +01:00
//! }
//!
//! Ok("Welcome!")
//! }
//!
2019-03-06 03:47:18 +01:00
//! fn main() -> std::io::Result<()> {
//! # std::thread::spawn(||
2019-03-06 03:47:18 +01:00
//! HttpServer::new(
//! || App::new().middleware(
//! CookieSession::signed(&[0; 32]) // <- create cookie based session middleware
2019-03-05 19:12:49 +01:00
//! .secure(false)
2019-03-06 03:47:18 +01:00
//! )
//! .service(web::resource("/").to(|| HttpResponse::Ok())))
2019-03-06 03:47:18 +01:00
//! .bind("127.0.0.1:59880")?
//! .run()
//! # );
//! # Ok(())
2019-03-05 19:12:49 +01:00
//! }
//! ```
use std::cell::RefCell;
use std::rc::Rc;
use actix_web::dev::{ServiceFromRequest, ServiceRequest, ServiceResponse};
2019-03-06 03:47:18 +01:00
use actix_web::{Error, FromRequest, HttpMessage};
use hashbrown::HashMap;
2019-03-05 19:12:49 +01:00
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json;
2019-03-06 03:47:18 +01:00
mod cookie;
pub use crate::cookie::CookieSession;
2019-03-05 19:12:49 +01:00
/// The high-level interface you use to modify session data.
///
/// Session object could be obtained with
/// [`RequestSession::session`](trait.RequestSession.html#tymethod.session)
/// method. `RequestSession` trait is implemented for `HttpRequest`.
///
/// ```rust
2019-03-06 03:47:18 +01:00
/// use actix_session::Session;
2019-03-05 19:12:49 +01:00
/// use actix_web::*;
///
2019-03-06 03:47:18 +01:00
/// fn index(session: Session) -> Result<&'static str> {
2019-03-05 19:12:49 +01:00
/// // access session data
2019-03-06 03:47:18 +01:00
/// if let Some(count) = session.get::<i32>("counter")? {
/// session.set("counter", count + 1)?;
2019-03-05 19:12:49 +01:00
/// } else {
2019-03-06 03:47:18 +01:00
/// session.set("counter", 1)?;
2019-03-05 19:12:49 +01:00
/// }
///
/// Ok("Welcome!")
/// }
/// # fn main() {}
/// ```
2019-03-06 03:47:18 +01:00
pub struct Session(Rc<RefCell<SessionInner>>);
2019-03-05 19:12:49 +01:00
2019-03-06 03:47:18 +01:00
#[derive(Default)]
struct SessionInner {
state: HashMap<String, String>,
changed: bool,
2019-03-05 19:12:49 +01:00
}
impl Session {
/// Get a `value` from the session.
2019-03-06 03:47:18 +01:00
pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, Error> {
if let Some(s) = self.0.borrow().state.get(key) {
Ok(Some(serde_json::from_str(s)?))
} else {
Ok(None)
2019-03-05 19:12:49 +01:00
}
}
/// Set a `value` from the session.
2019-03-06 03:47:18 +01:00
pub fn set<T: Serialize>(&self, key: &str, value: T) -> Result<(), Error> {
let mut inner = self.0.borrow_mut();
inner.changed = true;
inner
.state
.insert(key.to_owned(), serde_json::to_string(&value)?);
Ok(())
2019-03-05 19:12:49 +01:00
}
/// Remove value from the session.
pub fn remove(&self, key: &str) {
2019-03-06 03:47:18 +01:00
let mut inner = self.0.borrow_mut();
inner.changed = true;
inner.state.remove(key);
2019-03-05 19:12:49 +01:00
}
/// Clear the session.
pub fn clear(&self) {
2019-03-06 03:47:18 +01:00
let mut inner = self.0.borrow_mut();
inner.changed = true;
inner.state.clear()
}
pub fn set_session<P>(
data: impl Iterator<Item = (String, String)>,
req: &mut ServiceRequest<P>,
) {
let session = Session::get_session(req);
let mut inner = session.0.borrow_mut();
inner.state.extend(data);
}
pub fn get_changes<B>(
res: &mut ServiceResponse<B>,
) -> Option<impl Iterator<Item = (String, String)>> {
if let Some(s_impl) = res
.request()
.extensions()
.get::<Rc<RefCell<SessionInner>>>()
{
let state =
std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new());
Some(state.into_iter())
} else {
None
2019-03-05 19:12:49 +01:00
}
}
2019-03-06 03:47:18 +01:00
fn get_session<R: HttpMessage>(req: R) -> Session {
if let Some(s_impl) = req.extensions().get::<Rc<RefCell<SessionInner>>>() {
return Session(Rc::clone(&s_impl));
}
let inner = Rc::new(RefCell::new(SessionInner::default()));
req.extensions_mut().insert(inner.clone());
Session(inner)
}
2019-03-05 19:12:49 +01:00
}
/// Extractor implementation for Session type.
///
/// ```rust
/// # use actix_web::*;
2019-03-06 03:47:18 +01:00
/// use actix_session::Session;
2019-03-05 19:12:49 +01:00
///
/// fn index(session: Session) -> Result<&'static str> {
/// // access session data
/// if let Some(count) = session.get::<i32>("counter")? {
/// session.set("counter", count + 1)?;
/// } else {
/// session.set("counter", 1)?;
/// }
///
/// Ok("Welcome!")
/// }
/// # fn main() {}
/// ```
2019-03-06 03:47:18 +01:00
impl<P> FromRequest<P> for Session {
type Error = Error;
type Future = Result<Session, Error>;
2019-03-05 19:12:49 +01:00
#[inline]
2019-03-06 03:47:18 +01:00
fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
Ok(Session::get_session(req))
2019-03-05 19:12:49 +01:00
}
}
2019-03-25 04:21:20 +01:00
#[cfg(test)]
mod tests {
use actix_web::{test, HttpResponse};
use super::*;
#[test]
fn session() {
let mut req = test::TestRequest::default().to_service();
Session::set_session(
vec![("key".to_string(), "\"value\"".to_string())].into_iter(),
&mut req,
);
let session = Session::get_session(&mut req);
let res = session.get::<String>("key").unwrap();
assert_eq!(res, Some("value".to_string()));
session.set("key2", "value2".to_string()).unwrap();
session.remove("key");
let mut res = req.into_response(HttpResponse::Ok().finish());
let changes: Vec<_> = Session::get_changes(&mut res).unwrap().collect();
assert_eq!(changes, [("key2".to_string(), "\"value2\"".to_string())]);
}
}