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
|
2019-03-07 00:47:15 +01:00
|
|
|
//! 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<()> {
|
2019-03-07 00:47:15 +01:00
|
|
|
//! # 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
|
|
|
//! )
|
2019-03-07 00:47:15 +01:00
|
|
|
//! .service(web::resource("/").to(|| HttpResponse::Ok())))
|
2019-03-06 03:47:18 +01:00
|
|
|
//! .bind("127.0.0.1:59880")?
|
2019-03-07 00:47:15 +01:00
|
|
|
//! .run()
|
|
|
|
//! # );
|
|
|
|
//! # Ok(())
|
2019-03-05 19:12:49 +01:00
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
2019-03-06 03:47:18 +01:00
|
|
|
use actix_web::{Error, FromRequest, HttpMessage};
|
|
|
|
use actix_web::{ServiceFromRequest, ServiceRequest, ServiceResponse};
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|