use std::{collections::BTreeMap, time::Duration};
use redis::{aio::ConnectionLike, AsyncCommands, Cmd};
use serde_json::Value;
use crate::{error::InternalServerError, session::session_storage::SessionStorage, Result};
#[cfg_attr(docsrs, doc(cfg(feature = "redis-session")))]
pub struct RedisStorage<T> {
connection: T,
}
impl<T> RedisStorage<T> {
pub fn new(connection: T) -> Self {
Self { connection }
}
}
#[async_trait::async_trait]
impl<T: ConnectionLike + Clone + Sync + Send> SessionStorage for RedisStorage<T> {
async fn load_session(&self, session_id: &str) -> Result<Option<BTreeMap<String, Value>>> {
let data: Option<String> = self
.connection
.clone()
.get(session_id)
.await
.map_err(InternalServerError)?;
match data {
Some(data) => match serde_json::from_str::<BTreeMap<String, Value>>(&data) {
Ok(entries) => Ok(Some(entries)),
Err(_) => Ok(None),
},
None => Ok(None),
}
}
async fn update_session(
&self,
session_id: &str,
entries: &BTreeMap<String, Value>,
expires: Option<Duration>,
) -> Result<()> {
let value = serde_json::to_string(entries).unwrap_or_default();
let cmd = match expires {
Some(expires) => Cmd::set_ex(&session_id, value, expires.as_secs() as usize),
None => Cmd::set(&session_id, value),
};
cmd.query_async(&mut self.connection.clone())
.await
.map_err(InternalServerError)?;
Ok(())
}
async fn remove_session(&self, session_id: &str) -> Result<()> {
Cmd::del(session_id)
.query_async(&mut self.connection.clone())
.await
.map_err(InternalServerError)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use redis::{aio::ConnectionManager, Client, ConnectionLike};
use super::*;
use crate::{
session::{
test_harness::{index, TestClient},
CookieConfig, ServerSession,
},
EndpointExt, Route,
};
#[tokio::test]
async fn redis_session() {
let mut client = match Client::open("redis://127.0.0.1/") {
Ok(client) => client,
Err(_) => return,
};
if !client.check_connection() {
return;
}
let app = Route::new().at("/:action", index).with(ServerSession::new(
CookieConfig::default(),
RedisStorage::new(ConnectionManager::new(client).await.unwrap()),
));
let mut client = TestClient::default();
client.call(&app, 0).await;
client.assert_cookies(vec![]);
client.call(&app, 1).await;
client.call(&app, 2).await;
client.call(&app, 7).await;
client.call(&app, 6).await;
client.call(&app, 3).await;
client.call(&app, 4).await;
client.call(&app, 5).await;
client.assert_cookies(vec![]);
}
}