mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-30 10:32:55 +01:00
add sled session backend
This commit is contained in:
parent
6e79465362
commit
25f8a4cfae
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,5 +10,6 @@ guide/build/
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.pid
|
*.pid
|
||||||
*.sock
|
*.sock
|
||||||
|
*.db
|
||||||
*~
|
*~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
@ -21,8 +21,9 @@ name = "actix_session"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["sled-session"]
|
||||||
cookie-session = []
|
cookie-session = []
|
||||||
|
sled-session = ["sled"]
|
||||||
redis-actor-session = ["actix-redis", "actix", "futures-core", "rand"]
|
redis-actor-session = ["actix-redis", "actix", "futures-core", "rand"]
|
||||||
redis-rs-session = ["redis", "rand"]
|
redis-rs-session = ["redis", "rand"]
|
||||||
redis-rs-tls-session = ["redis-rs-session", "redis/tokio-native-tls-comp"]
|
redis-rs-tls-session = ["redis-rs-session", "redis/tokio-native-tls-comp"]
|
||||||
@ -40,6 +41,9 @@ serde = { version = "1" }
|
|||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
|
# sled-session
|
||||||
|
sled = { version = "0.34", optional = true }
|
||||||
|
|
||||||
# redis-actor-session
|
# redis-actor-session
|
||||||
actix = { version = "0.13", default-features = false, optional = true }
|
actix = { version = "0.13", default-features = false, optional = true }
|
||||||
actix-redis = { version = "0.12", optional = true }
|
actix-redis = { version = "0.12", optional = true }
|
||||||
@ -57,7 +61,7 @@ log = "0.4"
|
|||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "basic"
|
name = "basic"
|
||||||
required-features = ["redis-actor-session"]
|
required-features = ["sled-session"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "authentication"
|
name = "authentication"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use actix_session::{storage::RedisActorSessionStore, Session, SessionMiddleware};
|
use actix_session::{storage::SledSessionStore, Session, SessionMiddleware};
|
||||||
use actix_web::{cookie::Key, middleware, web, App, Error, HttpRequest, HttpServer, Responder};
|
use actix_web::{cookie::Key, middleware, web, App, Error, HttpRequest, HttpServer, Responder};
|
||||||
|
|
||||||
/// simple handler
|
/// simple handler
|
||||||
@ -21,7 +21,9 @@ async fn main() -> std::io::Result<()> {
|
|||||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||||
|
|
||||||
// The signing key would usually be read from a configuration file/environment variables.
|
// The signing key would usually be read from a configuration file/environment variables.
|
||||||
let signing_key = Key::generate();
|
let signing_key = Key::from(&[0; 64]);
|
||||||
|
|
||||||
|
let sled_session_store = SledSessionStore::new("./session.db").unwrap();
|
||||||
|
|
||||||
log::info!("starting HTTP server at http://localhost:8080");
|
log::info!("starting HTTP server at http://localhost:8080");
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
// cookie session middleware
|
// cookie session middleware
|
||||||
.wrap(SessionMiddleware::new(
|
.wrap(SessionMiddleware::new(
|
||||||
RedisActorSessionStore::new("127.0.0.1:6379"),
|
sled_session_store.clone(),
|
||||||
signing_key.clone(),
|
signing_key.clone(),
|
||||||
))
|
))
|
||||||
// register simple route, handle all methods
|
// register simple route, handle all methods
|
||||||
|
@ -156,6 +156,23 @@ pub mod test_helpers {
|
|||||||
|
|
||||||
use crate::{config::CookieContentSecurity, storage::SessionStore};
|
use crate::{config::CookieContentSecurity, storage::SessionStore};
|
||||||
|
|
||||||
|
/// Prints name of function it is called in.
|
||||||
|
///
|
||||||
|
/// Taken from: https://docs.rs/stdext/0.3.1/src/stdext/macros.rs.html
|
||||||
|
macro_rules! function_name {
|
||||||
|
() => {{
|
||||||
|
// Okay, this is ugly, I get it. However, this is the best we can get on a stable rust.
|
||||||
|
fn f() {}
|
||||||
|
fn type_name_of<T>(_: T) -> &'static str {
|
||||||
|
std::any::type_name::<T>()
|
||||||
|
}
|
||||||
|
let name = type_name_of(f);
|
||||||
|
// `3` is the length of the `::f`.
|
||||||
|
&name[..name.len() - 3]
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use function_name;
|
||||||
|
|
||||||
/// Generate a random cookie signing/encryption key.
|
/// Generate a random cookie signing/encryption key.
|
||||||
pub fn key() -> Key {
|
pub fn key() -> Key {
|
||||||
Key::generate()
|
Key::generate()
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto as _;
|
||||||
|
|
||||||
use actix_web::cookie::time::Duration;
|
use actix_web::cookie::time::Duration;
|
||||||
use anyhow::Error;
|
|
||||||
|
|
||||||
use super::SessionKey;
|
use super::{LoadError, SaveError, SessionKey, SessionState, UpdateError};
|
||||||
use crate::storage::{
|
use crate::storage::SessionStore;
|
||||||
interface::{LoadError, SaveError, SessionState, UpdateError},
|
|
||||||
SessionStore,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Use the session key, stored in the session cookie, as storage backend for the session state.
|
/// Use the session key, stored in the session cookie, as storage backend for the session state.
|
||||||
///
|
///
|
||||||
@ -67,7 +63,7 @@ impl SessionStore for CookieSessionStore {
|
|||||||
_ttl: &Duration,
|
_ttl: &Duration,
|
||||||
) -> Result<SessionKey, SaveError> {
|
) -> Result<SessionKey, SaveError> {
|
||||||
let session_key = serde_json::to_string(&session_state)
|
let session_key = serde_json::to_string(&session_state)
|
||||||
.map_err(anyhow::Error::new)
|
.map_err(Into::into)
|
||||||
.map_err(SaveError::Serialization)?;
|
.map_err(SaveError::Serialization)?;
|
||||||
|
|
||||||
Ok(session_key
|
Ok(session_key
|
||||||
@ -90,7 +86,11 @@ impl SessionStore for CookieSessionStore {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_ttl(&self, _session_key: &SessionKey, _ttl: &Duration) -> Result<(), Error> {
|
async fn update_ttl(
|
||||||
|
&self,
|
||||||
|
_session_key: &SessionKey,
|
||||||
|
_ttl: &Duration,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ pub enum UpdateError {
|
|||||||
Serialization(anyhow::Error),
|
Serialization(anyhow::Error),
|
||||||
|
|
||||||
/// Something went wrong when updating the session state.
|
/// Something went wrong when updating the session state.
|
||||||
#[display(fmt = "Something went wrong when updating the session state.")]
|
#[display(fmt = "Something went wrong when updating the session state")]
|
||||||
Other(anyhow::Error),
|
Other(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,12 +3,16 @@
|
|||||||
mod interface;
|
mod interface;
|
||||||
mod session_key;
|
mod session_key;
|
||||||
|
|
||||||
|
pub(crate) use self::interface::SessionState;
|
||||||
pub use self::interface::{LoadError, SaveError, SessionStore, UpdateError};
|
pub use self::interface::{LoadError, SaveError, SessionStore, UpdateError};
|
||||||
pub use self::session_key::SessionKey;
|
pub use self::session_key::SessionKey;
|
||||||
|
|
||||||
#[cfg(feature = "cookie-session")]
|
#[cfg(feature = "cookie-session")]
|
||||||
mod cookie;
|
mod cookie;
|
||||||
|
|
||||||
|
#[cfg(feature = "sled-session")]
|
||||||
|
mod sled;
|
||||||
|
|
||||||
#[cfg(feature = "redis-actor-session")]
|
#[cfg(feature = "redis-actor-session")]
|
||||||
mod redis_actor;
|
mod redis_actor;
|
||||||
|
|
||||||
@ -19,8 +23,10 @@ mod redis_rs;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
#[cfg(feature = "cookie-session")]
|
#[cfg(feature = "cookie-session")]
|
||||||
pub use cookie::CookieSessionStore;
|
pub use self::cookie::CookieSessionStore;
|
||||||
#[cfg(feature = "redis-actor-session")]
|
#[cfg(feature = "redis-actor-session")]
|
||||||
pub use redis_actor::{RedisActorSessionStore, RedisActorSessionStoreBuilder};
|
pub use self::redis_actor::{RedisActorSessionStore, RedisActorSessionStoreBuilder};
|
||||||
#[cfg(feature = "redis-rs-session")]
|
#[cfg(feature = "redis-rs-session")]
|
||||||
pub use redis_rs::{RedisSessionStore, RedisSessionStoreBuilder};
|
pub use self::redis_rs::{RedisSessionStore, RedisSessionStoreBuilder};
|
||||||
|
#[cfg(feature = "sled-session")]
|
||||||
|
pub use self::sled::SledSessionStore;
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use actix_redis::{resp_array, Command, RedisActor, RespValue};
|
use actix_redis::{resp_array, Command, RedisActor, RespValue};
|
||||||
use actix_web::cookie::time::Duration;
|
use actix_web::cookie::time::Duration;
|
||||||
use anyhow::Error;
|
|
||||||
|
|
||||||
use super::SessionKey;
|
use super::{LoadError, SaveError, SessionKey, SessionState, UpdateError};
|
||||||
use crate::storage::{
|
use crate::storage::{utils::generate_session_key, SessionStore};
|
||||||
interface::{LoadError, SaveError, SessionState, UpdateError},
|
|
||||||
utils::generate_session_key,
|
|
||||||
SessionStore,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Use Redis as session storage backend.
|
/// Use Redis as session storage backend.
|
||||||
///
|
///
|
||||||
@ -156,6 +151,7 @@ impl SessionStore for RedisActorSessionStore {
|
|||||||
let body = serde_json::to_string(&session_state)
|
let body = serde_json::to_string(&session_state)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.map_err(SaveError::Serialization)?;
|
.map_err(SaveError::Serialization)?;
|
||||||
|
|
||||||
let session_key = generate_session_key();
|
let session_key = generate_session_key();
|
||||||
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
@ -239,7 +235,11 @@ impl SessionStore for RedisActorSessionStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_ttl(&self, session_key: &SessionKey, ttl: &Duration) -> Result<(), Error> {
|
async fn update_ttl(
|
||||||
|
&self,
|
||||||
|
session_key: &SessionKey,
|
||||||
|
ttl: &Duration,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
let cmd = Command(resp_array![
|
let cmd = Command(resp_array![
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
use std::{convert::TryInto, sync::Arc};
|
use std::{convert::TryInto as _, sync::Arc};
|
||||||
|
|
||||||
use actix_web::cookie::time::Duration;
|
use actix_web::cookie::time::Duration;
|
||||||
use anyhow::{Context, Error};
|
use anyhow::Context as _;
|
||||||
use redis::{aio::ConnectionManager, AsyncCommands, Cmd, FromRedisValue, RedisResult, Value};
|
use redis::{aio::ConnectionManager, AsyncCommands, Cmd, FromRedisValue, RedisResult, Value};
|
||||||
|
|
||||||
use super::SessionKey;
|
use super::{LoadError, SaveError, SessionKey, SessionState, UpdateError};
|
||||||
use crate::storage::{
|
use crate::storage::{utils::generate_session_key, SessionStore};
|
||||||
interface::{LoadError, SaveError, SessionState, UpdateError},
|
|
||||||
utils::generate_session_key,
|
|
||||||
SessionStore,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Use Redis as session storage backend.
|
/// Use Redis as session storage backend.
|
||||||
///
|
///
|
||||||
@ -139,8 +135,8 @@ impl SessionStore for RedisSessionStore {
|
|||||||
async fn load(&self, session_key: &SessionKey) -> Result<Option<SessionState>, LoadError> {
|
async fn load(&self, session_key: &SessionKey) -> Result<Option<SessionState>, LoadError> {
|
||||||
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
let value: Option<String> = self
|
let value = self
|
||||||
.execute_command(redis::cmd("GET").arg(&[&cache_key]))
|
.execute_command::<Option<String>>(redis::cmd("GET").arg(&[&cache_key]))
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.map_err(LoadError::Other)?;
|
.map_err(LoadError::Other)?;
|
||||||
@ -161,6 +157,7 @@ impl SessionStore for RedisSessionStore {
|
|||||||
let body = serde_json::to_string(&session_state)
|
let body = serde_json::to_string(&session_state)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.map_err(SaveError::Serialization)?;
|
.map_err(SaveError::Serialization)?;
|
||||||
|
|
||||||
let session_key = generate_session_key();
|
let session_key = generate_session_key();
|
||||||
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
@ -169,7 +166,7 @@ impl SessionStore for RedisSessionStore {
|
|||||||
&body,
|
&body,
|
||||||
"NX", // NX: only set the key if it does not already exist
|
"NX", // NX: only set the key if it does not already exist
|
||||||
"EX", // EX: set expiry
|
"EX", // EX: set expiry
|
||||||
&format!("{}", ttl.whole_seconds()),
|
ttl.whole_seconds().to_string().as_str(),
|
||||||
]))
|
]))
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
@ -194,9 +191,9 @@ impl SessionStore for RedisSessionStore {
|
|||||||
.execute_command(redis::cmd("SET").arg(&[
|
.execute_command(redis::cmd("SET").arg(&[
|
||||||
&cache_key,
|
&cache_key,
|
||||||
&body,
|
&body,
|
||||||
"XX", // XX: Only set the key if it already exist.
|
"XX", // XX: only set the key if it already exists
|
||||||
"EX", // EX: set expiry
|
"EX", // EX: set expiry
|
||||||
&format!("{}", ttl.whole_seconds()),
|
ttl.whole_seconds().to_string().as_str(),
|
||||||
]))
|
]))
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
@ -223,7 +220,11 @@ impl SessionStore for RedisSessionStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_ttl(&self, session_key: &SessionKey, ttl: &Duration) -> Result<(), Error> {
|
async fn update_ttl(
|
||||||
|
&self,
|
||||||
|
session_key: &SessionKey,
|
||||||
|
ttl: &Duration,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
self.client
|
self.client
|
||||||
@ -235,6 +236,7 @@ impl SessionStore for RedisSessionStore {
|
|||||||
)?,
|
)?,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,12 +323,14 @@ mod tests {
|
|||||||
async fn loading_an_invalid_session_state_returns_deserialization_error() {
|
async fn loading_an_invalid_session_state_returns_deserialization_error() {
|
||||||
let store = redis_store().await;
|
let store = redis_store().await;
|
||||||
let session_key = generate_session_key();
|
let session_key = generate_session_key();
|
||||||
|
|
||||||
store
|
store
|
||||||
.client
|
.client
|
||||||
.clone()
|
.clone()
|
||||||
.set::<_, _, ()>(session_key.as_ref(), "random-thing-which-is-not-json")
|
.set::<_, _, ()>(session_key.as_ref(), "random-thing-which-is-not-json")
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
store.load(&session_key).await.unwrap_err(),
|
store.load(&session_key).await.unwrap_err(),
|
||||||
LoadError::Deserialization(_),
|
LoadError::Deserialization(_),
|
||||||
@ -338,10 +342,12 @@ mod tests {
|
|||||||
let store = redis_store().await;
|
let store = redis_store().await;
|
||||||
let session_key = generate_session_key();
|
let session_key = generate_session_key();
|
||||||
let initial_session_key = session_key.as_ref().to_owned();
|
let initial_session_key = session_key.as_ref().to_owned();
|
||||||
|
|
||||||
let updated_session_key = store
|
let updated_session_key = store
|
||||||
.update(session_key, HashMap::new(), &time::Duration::seconds(1))
|
.update(session_key, HashMap::new(), &time::Duration::seconds(1))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_ne!(initial_session_key, updated_session_key.as_ref());
|
assert_ne!(initial_session_key, updated_session_key.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
189
actix-session/src/storage/sled.rs
Normal file
189
actix-session/src/storage/sled.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
use std::{path::Path, sync::Arc};
|
||||||
|
|
||||||
|
use actix_web::cookie::time::Duration;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
utils::generate_session_key, LoadError, SaveError, SessionKey, SessionState, SessionStore,
|
||||||
|
UpdateError,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct CacheConfiguration {
|
||||||
|
cache_keygen: Arc<dyn Fn(&str) -> String + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CacheConfiguration {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
cache_keygen: Arc::new(str::to_owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "sled-session")))]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SledSessionStore {
|
||||||
|
configuration: CacheConfiguration,
|
||||||
|
db: sled::Db,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SledSessionStore {
|
||||||
|
/// TODO
|
||||||
|
pub fn new(db_path: impl AsRef<Path>) -> Result<Self, anyhow::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
configuration: CacheConfiguration::default(),
|
||||||
|
db: sled::open(db_path)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl SessionStore for SledSessionStore {
|
||||||
|
async fn load(&self, session_key: &SessionKey) -> Result<Option<SessionState>, LoadError> {
|
||||||
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
|
let value = self
|
||||||
|
.db
|
||||||
|
.get(cache_key)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(LoadError::Other)?;
|
||||||
|
|
||||||
|
match value {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(value) => Ok(serde_json::from_slice(&value)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(LoadError::Deserialization)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save(
|
||||||
|
&self,
|
||||||
|
session_state: SessionState,
|
||||||
|
ttl: &Duration,
|
||||||
|
) -> Result<SessionKey, SaveError> {
|
||||||
|
let session_key = generate_session_key();
|
||||||
|
|
||||||
|
self.update(session_key, session_state, ttl)
|
||||||
|
.await
|
||||||
|
.map_err(|err| match err {
|
||||||
|
UpdateError::Serialization(err) => SaveError::Serialization(err),
|
||||||
|
UpdateError::Other(err) => SaveError::Other(err),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(
|
||||||
|
&self,
|
||||||
|
session_key: SessionKey,
|
||||||
|
session_state: SessionState,
|
||||||
|
_ttl: &Duration,
|
||||||
|
) -> Result<SessionKey, UpdateError> {
|
||||||
|
let body = serde_json::to_vec(&session_state)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(UpdateError::Serialization)?;
|
||||||
|
|
||||||
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
|
self.db
|
||||||
|
.insert(cache_key, body)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(UpdateError::Other)?;
|
||||||
|
|
||||||
|
Ok(session_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_ttl(
|
||||||
|
&self,
|
||||||
|
session_key: &SessionKey,
|
||||||
|
_ttl: &Duration,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let _cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, session_key: &SessionKey) -> Result<(), anyhow::Error> {
|
||||||
|
let cache_key = (self.configuration.cache_keygen)(session_key.as_ref());
|
||||||
|
|
||||||
|
self.db
|
||||||
|
.drop_tree(&cache_key)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(UpdateError::Other)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use actix_web::cookie::time;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::test_helpers::{acceptance_test_suite, function_name};
|
||||||
|
|
||||||
|
fn sled_db(fn_name: &str) -> SledSessionStore {
|
||||||
|
let db_name = fn_name.replace("::", ".").replace(".{{closure}}", "");
|
||||||
|
|
||||||
|
SledSessionStore::new(
|
||||||
|
[
|
||||||
|
env!("CARGO_TARGET_DIR"),
|
||||||
|
"/tmp-actix-session-tests/",
|
||||||
|
&db_name,
|
||||||
|
"-",
|
||||||
|
rand::random::<u16>().to_string().as_str(),
|
||||||
|
".db",
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn session_workflow() {
|
||||||
|
let store = sled_db(function_name!());
|
||||||
|
// TODO: use invalidation_supported = true
|
||||||
|
acceptance_test_suite(move || store.clone(), false).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn loading_a_missing_session_returns_none() {
|
||||||
|
let store = sled_db(function_name!());
|
||||||
|
let session_key = generate_session_key();
|
||||||
|
assert!(store.load(&session_key).await.unwrap().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn loading_an_invalid_session_state_returns_deserialization_error() {
|
||||||
|
let store = sled_db(function_name!());
|
||||||
|
let session_key = generate_session_key();
|
||||||
|
|
||||||
|
store
|
||||||
|
.db
|
||||||
|
.insert(session_key.as_ref(), "random-thing-which-is-not-json")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
store.load(&session_key).await.unwrap_err(),
|
||||||
|
LoadError::Deserialization(_),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignored until TTL handling is implemented
|
||||||
|
#[ignore]
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn updating_of_an_expired_state_is_handled_gracefully() {
|
||||||
|
let store = sled_db(function_name!());
|
||||||
|
let session_key = generate_session_key();
|
||||||
|
let initial_session_key = session_key.as_ref().to_owned();
|
||||||
|
|
||||||
|
let updated_session_key = store
|
||||||
|
.update(session_key, HashMap::new(), &time::Duration::seconds(1))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_ne!(initial_session_key, updated_session_key.as_ref());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user