diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..8af59dd --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +RUST_TEST_THREADS = "1" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0c30e9b..c2fb418 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -43,13 +43,6 @@ jobs: command: check args: --all --bins --examples - - name: check avoid-dev-deps - uses: actions-rs/cargo@v1 - if: matrix.rust == 'nightly' - with: - command: check - args: --all -Z avoid-dev-deps - - name: tests uses: actions-rs/cargo@v1 with: diff --git a/Cargo.toml b/Cargo.toml index e3da58f..0e608a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,15 @@ keywords = ["sessions", "tide", "async-session", "redis"] categories = ["web-programming::http-server", "web-programming", "database"] [dependencies.redis] -version = "0.21.0" -features = ["aio", "async-std-comp"] +version = "0.22.3" +features = ["aio"] [dependencies] -async-session = "3.0.0" +async-session = { git = "https://github.com/http-rs/async-session", branch = "overhaul-session-and-session-store", default-features = false } +base64 = "0.21.0" +serde_json = "1.0.93" +thiserror = "1.0.38" [dev-dependencies] -async-std = { version = "1.9.0", features = ["attributes"] } +redis = { version = "0.22.3", features = ["async-std-comp"] } +async-std = { version = "1.12.0", features = ["attributes"] } diff --git a/src/lib.rs b/src/lib.rs index 0241b08..f07d2ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,16 @@ //! # async-redis-session //! ```rust -//! use async_redis_session::RedisSessionStore; +//! use async_redis_session::{RedisSessionStore, Error}; //! use async_session::{Session, SessionStore}; //! -//! # fn main() -> async_session::Result { async_std::task::block_on(async { +//! # fn main() -> Result<(), Error> { async_std::task::block_on(async { //! let store = RedisSessionStore::new("redis://127.0.0.1/")?; //! //! let mut session = Session::new(); //! session.insert("key", "value")?; //! -//! let cookie_value = store.store_session(session).await?.unwrap(); -//! let session = store.load_session(cookie_value).await?.unwrap(); +//! let cookie_value = store.store_session(&mut session).await?.unwrap(); +//! let session = store.load_session(&cookie_value).await?.unwrap(); //! assert_eq!(&session.get::("key").unwrap(), "value"); //! # Ok(()) }) } //! ``` @@ -25,9 +25,27 @@ unused_qualifications )] -use async_session::{async_trait, serde_json, Result, Session, SessionStore}; +use async_session::{async_trait, Session, SessionStore}; use redis::{aio::Connection, AsyncCommands, Client, IntoConnectionInfo, RedisResult}; +/// Errors that can arise in the operation of the session stores +/// included in this crate +#[derive(thiserror::Error, Debug)] +#[non_exhaustive] +pub enum Error { + /// an error that comes from sqlx + #[error(transparent)] + Redis(#[from] redis::RedisError), + + /// an error that comes from serde_json + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), + + /// an error that comes from base64 + #[error(transparent)] + Base64(#[from] base64::DecodeError), +} + /// # RedisSessionStore #[derive(Clone, Debug)] pub struct RedisSessionStore { @@ -77,12 +95,12 @@ impl RedisSessionStore { self } - async fn ids(&self) -> Result> { + async fn ids(&self) -> Result, Error> { Ok(self.connection().await?.keys(self.prefix_key("*")).await?) } /// returns the number of sessions in this store - pub async fn count(&self) -> Result { + pub async fn count(&self) -> Result { if self.prefix.is_none() { let mut connection = self.connection().await?; Ok(redis::cmd("DBSIZE").query_async(&mut connection).await?) @@ -92,7 +110,7 @@ impl RedisSessionStore { } #[cfg(test)] - async fn ttl_for_session(&self, session: &Session) -> Result { + async fn ttl_for_session(&self, session: &Session) -> Result { Ok(self .connection() .await? @@ -101,22 +119,25 @@ impl RedisSessionStore { } fn prefix_key(&self, key: impl AsRef) -> String { + let key = key.as_ref(); if let Some(ref prefix) = self.prefix { - format!("{}{}", prefix, key.as_ref()) + format!("{prefix}{key}") } else { - key.as_ref().into() + key.to_string() } } async fn connection(&self) -> RedisResult { - self.client.get_async_std_connection().await + self.client.get_async_connection().await } } #[async_trait] impl SessionStore for RedisSessionStore { - async fn load_session(&self, cookie_value: String) -> Result> { - let id = Session::id_from_cookie_value(&cookie_value)?; + type Error = Error; + + async fn load_session(&self, cookie_value: &str) -> Result, Self::Error> { + let id = Session::id_from_cookie_value(cookie_value)?; let mut connection = self.connection().await?; let record: Option = connection.get(self.prefix_key(id)).await?; match record { @@ -125,7 +146,7 @@ impl SessionStore for RedisSessionStore { } } - async fn store_session(&self, session: Session) -> Result> { + async fn store_session(&self, session: &mut Session) -> Result, Self::Error> { let id = self.prefix_key(session.id()); let string = serde_json::to_string(&session)?; @@ -141,17 +162,17 @@ impl SessionStore for RedisSessionStore { } }; - Ok(session.into_cookie_value()) + Ok(session.take_cookie_value()) } - async fn destroy_session(&self, session: Session) -> Result { + async fn destroy_session(&self, session: &mut Session) -> Result<(), Self::Error> { let mut connection = self.connection().await?; - let key = self.prefix_key(session.id().to_string()); + let key = self.prefix_key(session.id()); connection.del(key).await?; Ok(()) } - async fn clear_store(&self) -> Result { + async fn clear_store(&self) -> Result<(), Self::Error> { let mut connection = self.connection().await?; if self.prefix.is_none() { @@ -179,15 +200,14 @@ mod tests { } #[async_std::test] - async fn creating_a_new_session_with_no_expiry() -> Result { + async fn creating_a_new_session_with_no_expiry() -> Result<(), Error> { let store = test_store().await; let mut session = Session::new(); session.insert("key", "value")?; - let cloned = session.clone(); - let cookie_value = store.store_session(session).await?.unwrap(); + let cookie_value = store.store_session(&mut session).await?.unwrap(); - let loaded_session = store.load_session(cookie_value).await?.unwrap(); - assert_eq!(cloned.id(), loaded_session.id()); + let loaded_session = store.load_session(&cookie_value).await?.unwrap(); + assert_eq!(session.id(), loaded_session.id()); assert_eq!("value", &loaded_session.get::("key").unwrap()); assert!(!loaded_session.is_expired()); @@ -195,18 +215,18 @@ mod tests { } #[async_std::test] - async fn updating_a_session() -> Result { + async fn updating_a_session() -> Result<(), Error> { let store = test_store().await; let mut session = Session::new(); session.insert("key", "value")?; - let cookie_value = store.store_session(session).await?.unwrap(); + let cookie_value = store.store_session(&mut session).await?.unwrap(); - let mut session = store.load_session(cookie_value.clone()).await?.unwrap(); + let mut session = store.load_session(&cookie_value).await?.unwrap(); session.insert("key", "other value")?; - assert_eq!(None, store.store_session(session).await?); + assert_eq!(None, store.store_session(&mut session).await?); - let session = store.load_session(cookie_value.clone()).await?.unwrap(); + let session = store.load_session(&cookie_value).await?.unwrap(); assert_eq!(&session.get::("key").unwrap(), "other value"); assert_eq!(1, store.count().await.unwrap()); @@ -214,23 +234,23 @@ mod tests { } #[async_std::test] - async fn updating_a_session_extending_expiry() -> Result { + async fn updating_a_session_extending_expiry() -> Result<(), Error> { let store = test_store().await; let mut session = Session::new(); session.expire_in(Duration::from_secs(5)); - let original_expires = session.expiry().unwrap().clone(); - let cookie_value = store.store_session(session).await?.unwrap(); + let original_expires = *session.expiry().unwrap(); + let cookie_value = store.store_session(&mut session).await?.unwrap(); - let mut session = store.load_session(cookie_value.clone()).await?.unwrap(); + let mut session = store.load_session(&cookie_value).await?.unwrap(); let ttl = store.ttl_for_session(&session).await?; assert!(ttl > 3 && ttl < 5); assert_eq!(session.expiry().unwrap(), &original_expires); session.expire_in(Duration::from_secs(10)); - let new_expires = session.expiry().unwrap().clone(); - store.store_session(session).await?; + let new_expires = *session.expiry().unwrap(); + store.store_session(&mut session).await?; - let session = store.load_session(cookie_value.clone()).await?.unwrap(); + let session = store.load_session(&cookie_value).await?.unwrap(); let ttl = store.ttl_for_session(&session).await?; assert!(ttl > 8 && ttl < 10); assert_eq!(session.expiry().unwrap(), &new_expires); @@ -244,53 +264,52 @@ mod tests { } #[async_std::test] - async fn creating_a_new_session_with_expiry() -> Result { + async fn creating_a_new_session_with_expiry() -> Result<(), Error> { let store = test_store().await; let mut session = Session::new(); session.expire_in(Duration::from_secs(3)); session.insert("key", "value")?; - let cloned = session.clone(); - let cookie_value = store.store_session(session).await?.unwrap(); + let cookie_value = store.store_session(&mut session).await?.unwrap(); - assert!(store.ttl_for_session(&cloned).await? > 1); + assert!(store.ttl_for_session(&session).await? > 1); - let loaded_session = store.load_session(cookie_value.clone()).await?.unwrap(); - assert_eq!(cloned.id(), loaded_session.id()); + let loaded_session = store.load_session(&cookie_value).await?.unwrap(); + assert_eq!(session.id(), loaded_session.id()); assert_eq!("value", &loaded_session.get::("key").unwrap()); assert!(!loaded_session.is_expired()); task::sleep(Duration::from_secs(2)).await; - assert_eq!(None, store.load_session(cookie_value).await?); + assert_eq!(None, store.load_session(&cookie_value).await?); Ok(()) } #[async_std::test] - async fn destroying_a_single_session() -> Result { + async fn destroying_a_single_session() -> Result<(), Error> { let store = test_store().await; for _ in 0..3i8 { - store.store_session(Session::new()).await?; + store.store_session(&mut Session::new()).await?; } - let cookie = store.store_session(Session::new()).await?.unwrap(); + let cookie = store.store_session(&mut Session::new()).await?.unwrap(); assert_eq!(4, store.count().await?); - let session = store.load_session(cookie.clone()).await?.unwrap(); - store.destroy_session(session.clone()).await.unwrap(); - assert_eq!(None, store.load_session(cookie).await?); + let mut session = store.load_session(&cookie).await?.unwrap(); + store.destroy_session(&mut session).await.unwrap(); + assert_eq!(None, store.load_session(&cookie).await?); assert_eq!(3, store.count().await?); // attempting to destroy the session again is not an error - assert!(store.destroy_session(session).await.is_ok()); + assert!(store.destroy_session(&mut session).await.is_ok()); Ok(()) } #[async_std::test] - async fn clearing_the_whole_store() -> Result { + async fn clearing_the_whole_store() -> Result<(), Error> { let store = test_store().await; for _ in 0..3i8 { - store.store_session(Session::new()).await?; + store.store_session(&mut Session::new()).await?; } assert_eq!(3, store.count().await?); @@ -301,26 +320,26 @@ mod tests { } #[async_std::test] - async fn prefixes() -> Result { + async fn prefixes() -> Result<(), Error> { test_store().await; // clear the db let store = RedisSessionStore::new("redis://127.0.0.1")?.with_prefix("sessions/"); store.clear_store().await?; for _ in 0..3i8 { - store.store_session(Session::new()).await?; + store.store_session(&mut Session::new()).await?; } let mut session = Session::new(); session.insert("key", "value")?; - let cookie_value = store.store_session(session).await?.unwrap(); + let cookie_value = store.store_session(&mut session).await?.unwrap(); - let mut session = store.load_session(cookie_value.clone()).await?.unwrap(); + let mut session = store.load_session(&cookie_value).await?.unwrap(); session.insert("key", "other value")?; - assert_eq!(None, store.store_session(session).await?); + assert_eq!(None, store.store_session(&mut session).await?); - let session = store.load_session(cookie_value.clone()).await?.unwrap(); + let session = store.load_session(&cookie_value).await?.unwrap(); assert_eq!(&session.get::("key").unwrap(), "other value"); assert_eq!(4, store.count().await.unwrap()); @@ -330,7 +349,7 @@ mod tests { assert_eq!(0, other_store.count().await.unwrap()); for _ in 0..3i8 { - other_store.store_session(Session::new()).await?; + other_store.store_session(&mut Session::new()).await?; } other_store.clear_store().await?;