redis generic client

This commit is contained in:
Aravinth Manivannan 2021-06-09 14:03:19 +05:30
parent 05dd9bbbb6
commit 07f124fb82
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
10 changed files with 239 additions and 213 deletions

View File

@ -1,7 +1,7 @@
use libmcaptcha::{ use libmcaptcha::{
cache::{messages::VerifyCaptchaResult, HashCache}, cache::{messages::VerifyCaptchaResult, HashCache},
master::embedded::master::Master, master::embedded::master::Master,
master::AddSiteBuilder, master::messages::AddSiteBuilder,
pow::{ConfigBuilder, Work}, pow::{ConfigBuilder, Work},
system::SystemBuilder, system::SystemBuilder,
DefenseBuilder, LevelBuilder, MCaptchaBuilder, DefenseBuilder, LevelBuilder, MCaptchaBuilder,

View File

@ -49,7 +49,7 @@
//! use libmcaptcha::{ //! use libmcaptcha::{
//! cache::{messages::VerifyCaptchaResult, HashCache}, //! cache::{messages::VerifyCaptchaResult, HashCache},
//! master::embedded::master:: Master, //! master::embedded::master:: Master,
//! master::AddSiteBuilder, //! master::messages::AddSiteBuilder,
//! pow::{ConfigBuilder, Work}, //! pow::{ConfigBuilder, Work},
//! system::SystemBuilder, //! system::SystemBuilder,
//! DefenseBuilder, LevelBuilder, MCaptchaBuilder, //! DefenseBuilder, LevelBuilder, MCaptchaBuilder,
@ -187,6 +187,9 @@ pub mod defense;
pub mod errors; pub mod errors;
pub mod master; pub mod master;
#[cfg(feature = "full")]
mod redis;
/// message datatypes to interact with [MCaptcha] actor /// message datatypes to interact with [MCaptcha] actor
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod cache; pub mod cache;
@ -200,18 +203,20 @@ mod utils;
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub use crate::cache::hashcache::HashCache; pub use crate::cache::hashcache::HashCache;
pub use crate::defense::{Defense, DefenseBuilder, LevelBuilder};
pub use crate::master::{AddVisitorResult, CreateMCaptcha};
pub use crate::mcaptcha::{MCaptcha, MCaptchaBuilder};
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub use master::embedded::counter::Counter; pub use master::embedded::counter::Counter;
pub use crate::defense::{Defense, DefenseBuilder, LevelBuilder};
pub use crate::master::AddVisitorResult;
pub use crate::master::CreateMCaptcha;
pub use crate::mcaptcha::{MCaptcha, MCaptchaBuilder};
#[cfg(feature = "minimal")] #[cfg(feature = "minimal")]
pub mod dev { pub mod dev {
pub use super::AddVisitorResult;
pub use super::CreateMCaptcha;
pub use crate::defense; pub use crate::defense;
pub use crate::defense::{Defense, DefenseBuilder, LevelBuilder}; pub use crate::defense::{Defense, DefenseBuilder, LevelBuilder};
pub use crate::master::{AddVisitorResult, CreateMCaptcha};
pub use crate::mcaptcha; pub use crate::mcaptcha;
pub use crate::mcaptcha::{MCaptcha, MCaptchaBuilder}; pub use crate::mcaptcha::{MCaptcha, MCaptchaBuilder};
} }

View File

@ -212,7 +212,7 @@ pub mod tests {
use super::*; use super::*;
use crate::defense::*; use crate::defense::*;
use crate::errors::*; use crate::errors::*;
use crate::master::MCaptchaBuilder; use crate::mcaptcha::MCaptchaBuilder;
// constants for testing // constants for testing
// (visitor count, level) // (visitor count, level)

View File

@ -27,8 +27,8 @@ use log::info;
use super::counter::Counter; use super::counter::Counter;
use crate::errors::*; use crate::errors::*;
use crate::master::messages::{AddSite, AddVisitor};
use crate::master::Master as MasterTrait; use crate::master::Master as MasterTrait;
use crate::master::{AddSite, AddVisitor};
/// This Actor manages the [Counter] actors. /// This Actor manages the [Counter] actors.
/// A service can have several [Counter] actors with /// A service can have several [Counter] actors with
@ -191,7 +191,7 @@ impl Handler<AddSite> for Master {
mod tests { mod tests {
use super::*; use super::*;
use crate::master::embedded::counter::tests::*; use crate::master::embedded::counter::tests::*;
use crate::master::AddSiteBuilder; use crate::master::messages::AddSiteBuilder;
#[actix_rt::test] #[actix_rt::test]
async fn master_actor_works() { async fn master_actor_works() {

View File

@ -16,49 +16,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! [Master] actor module that manages [MCaptcha] actors //! [Master] actor module that manages [MCaptcha] actors
#[cfg(feature = "full")]
use std::sync::mpsc::Receiver;
#[cfg(feature = "full")]
use actix::dev::*;
#[cfg(feature = "full")]
use derive_builder::Builder;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "full")] use crate::mcaptcha::*;
use crate::errors::CaptchaResult;
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod embedded; pub mod embedded;
#[allow(
unused_variables,
unused_imports,
unused_variables,
dead_code,
unused_macros
)]
use crate::mcaptcha::*;
#[allow(
unused_variables,
unused_imports,
unused_variables,
dead_code,
unused_macros
)]
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod redis; pub mod redis;
#[cfg(feature = "full")] #[cfg(feature = "full")]
/// Describes actor handler trait impls that are required by a cache implementation /// Describes actor handler trait impls that are required by a cache implementation
pub trait Master: actix::Actor + actix::Handler<AddVisitor> + actix::Handler<AddSite> {} pub trait Master:
actix::Actor + actix::Handler<messages::AddVisitor> + actix::Handler<messages::AddSite>
{
}
//+ actix::Handler<AddSite> #[derive(Serialize, Deserialize)]
pub struct CreateMCaptcha {
/// Message to add visitor to an [MCaptcha] actor pub levels: Vec<crate::defense::Level>,
#[derive(Message)] pub duration: u64,
#[cfg(feature = "full")] }
#[rtype(result = "Receiver<CaptchaResult<Option<AddVisitorResult>>>")]
pub struct AddVisitor(pub String);
/// Struct representing the return datatime of /// Struct representing the return datatime of
/// [AddVisitor] message. Contains MCaptcha lifetime /// [AddVisitor] message. Contains MCaptcha lifetime
@ -69,15 +48,6 @@ pub struct AddVisitorResult {
pub difficulty_factor: u32, pub difficulty_factor: u32,
} }
/// Message to add an [Counter] actor to [Master]
#[derive(Message, Builder)]
#[rtype(result = "()")]
#[cfg(feature = "full")]
pub struct AddSite {
pub id: String,
pub mcaptcha: MCaptcha,
}
impl AddVisitorResult { impl AddVisitorResult {
pub fn new(m: &MCaptcha) -> Self { pub fn new(m: &MCaptcha) -> Self {
AddVisitorResult { AddVisitorResult {
@ -87,9 +57,26 @@ impl AddVisitorResult {
} }
} }
#[cfg(feature = "minimal")] #[cfg(feature = "full")]
#[derive(Serialize, Deserialize)] pub mod messages {
pub struct CreateMCaptcha { use std::sync::mpsc::Receiver;
pub levels: Vec<crate::defense::Level>,
pub duration: u64, use actix::dev::*;
use derive_builder::Builder;
use crate::errors::CaptchaResult;
use crate::mcaptcha::MCaptcha;
/// Message to add visitor to an [MCaptcha] actor
#[derive(Message)]
#[rtype(result = "Receiver<CaptchaResult<Option<super::AddVisitorResult>>>")]
pub struct AddVisitor(pub String);
/// Message to add an [Counter] actor to [Master]
#[derive(Message, Builder)]
#[rtype(result = "()")]
pub struct AddSite {
pub id: String,
pub mcaptcha: MCaptcha,
}
} }

View File

@ -15,51 +15,50 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use std::cell::RefCell;
use std::cell::RefMut;
use std::rc::Rc;
use redis::cluster::ClusterClient;
use redis::RedisError;
//use redis::cluster::ClusterConnection;
use redis::Client;
//use redis::Connection;
use redis::RedisResult;
use redis::Value; use redis::Value;
use redis::{aio::Connection, cluster::ClusterConnection};
use crate::errors::*; use crate::errors::*;
use crate::master::{AddSite, AddVisitor, AddVisitorResult, CreateMCaptcha}; use crate::master::messages::{AddSite, AddVisitor};
use crate::master::AddVisitorResult;
use crate::master::CreateMCaptcha;
use crate::redis::Redis;
use crate::redis::RedisConfig;
use crate::redis::RedisConnection;
pub enum RedisConnection { /// Redis instance with mCaptcha Redis module loaded
Single(Rc<RefCell<Connection>>), pub struct MCaptchaRedis(Redis);
Cluster(Rc<RefCell<ClusterConnection>>),
} /// Redis instance with mCaptcha Redis module loaded
pub struct MCaptchaRedisConnection(RedisConnection);
#[allow(dead_code)]
const GET: &str = "MCAPTCHA_CACHE.GET"; const GET: &str = "MCAPTCHA_CACHE.GET";
#[allow(dead_code)]
const ADD_VISITOR: &str = "MCAPTCHA_CACHE.ADD_VISITOR"; const ADD_VISITOR: &str = "MCAPTCHA_CACHE.ADD_VISITOR";
#[allow(dead_code)]
const DEL: &str = "MCAPTCHA_CACHE.DELETE_CAPTCHA"; const DEL: &str = "MCAPTCHA_CACHE.DELETE_CAPTCHA";
#[allow(dead_code)]
const ADD_CAPTCHA: &str = "MCAPTCHA_CACHE.ADD_CAPTCHA"; const ADD_CAPTCHA: &str = "MCAPTCHA_CACHE.ADD_CAPTCHA";
#[allow(dead_code)]
const CAPTCHA_EXISTS: &str = "MCAPTCHA_CACHE.CAPTCHA_EXISTS"; const CAPTCHA_EXISTS: &str = "MCAPTCHA_CACHE.CAPTCHA_EXISTS";
const MODULE_NAME: &str = "mcaptcha_cahce"; const MODULE_NAME: &str = "mcaptcha_cahce";
macro_rules! exec {
($cmd:expr, $con:expr) => { impl MCaptchaRedis {
match *$con { pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> {
RedisConnection::Single(con) => $cmd.query_async(&mut *con.borrow_mut()).await, let redis = Redis::new(redis).await?;
RedisConnection::Cluster(con) => $cmd.query(&mut *con.borrow_mut()), let m = MCaptchaRedis(redis);
} m.get_client().is_module_loaded().await?;
}; Ok(m)
}
pub fn get_client(&self) -> MCaptchaRedisConnection {
MCaptchaRedisConnection(self.0.get_client())
}
} }
impl RedisConnection { impl MCaptchaRedisConnection {
pub async fn is_module_loaded(&self) -> CaptchaResult<()> { pub async fn is_module_loaded(&self) -> CaptchaResult<()> {
let modules: Vec<Vec<String>> = exec!(redis::cmd("MODULE").arg(&["LIST"]), &self).unwrap(); let modules: Vec<Vec<String>> = self
.0
.exec(redis::cmd("MODULE").arg(&["LIST"]))
.await
.unwrap();
for list in modules.iter() { for list in modules.iter() {
match list.iter().find(|module| module.as_str() == MODULE_NAME) { match list.iter().find(|module| module.as_str() == MODULE_NAME) {
@ -71,7 +70,12 @@ impl RedisConnection {
let commands = vec![ADD_VISITOR, ADD_CAPTCHA, DEL, CAPTCHA_EXISTS, GET]; let commands = vec![ADD_VISITOR, ADD_CAPTCHA, DEL, CAPTCHA_EXISTS, GET];
for cmd in commands.iter() { for cmd in commands.iter() {
match exec!(redis::cmd("COMMAND").arg(&["INFO", cmd]), &self).unwrap() { match self
.0
.exec(redis::cmd("COMMAND").arg(&["INFO", cmd]))
.await
.unwrap()
{
Value::Bulk(mut val) => { Value::Bulk(mut val) => {
match val.pop() { match val.pop() {
Some(Value::Nil) => { Some(Value::Nil) => {
@ -91,7 +95,7 @@ impl RedisConnection {
} }
pub async fn add_visitor(&self, msg: AddVisitor) -> CaptchaResult<Option<AddVisitorResult>> { pub async fn add_visitor(&self, msg: AddVisitor) -> CaptchaResult<Option<AddVisitorResult>> {
let res: String = exec!(redis::cmd(ADD_VISITOR).arg(&[msg.0]), &self)?; let res: String = self.0.exec(redis::cmd(ADD_VISITOR).arg(&[msg.0])).await?;
let res: AddVisitorResult = serde_json::from_str(&res).unwrap(); let res: AddVisitorResult = serde_json::from_str(&res).unwrap();
Ok(Some(res)) Ok(Some(res))
} }
@ -100,12 +104,17 @@ impl RedisConnection {
let name = msg.id; let name = msg.id;
let captcha: CreateMCaptcha = msg.mcaptcha.into(); let captcha: CreateMCaptcha = msg.mcaptcha.into();
let payload = serde_json::to_string(&captcha).unwrap(); let payload = serde_json::to_string(&captcha).unwrap();
exec!(redis::cmd(ADD_CAPTCHA).arg(&[name, payload]), &self)?; self.0
.exec(redis::cmd(ADD_CAPTCHA).arg(&[name, payload]))
.await?;
Ok(()) Ok(())
} }
pub async fn check_captcha_exists(&self, captcha: &str) -> CaptchaResult<bool> { pub async fn check_captcha_exists(&self, captcha: &str) -> CaptchaResult<bool> {
let exists: usize = exec!(redis::cmd(CAPTCHA_EXISTS).arg(&[captcha]), &self)?; let exists: usize = self
.0
.exec(redis::cmd(CAPTCHA_EXISTS).arg(&[captcha]))
.await?;
if exists == 1 { if exists == 1 {
Ok(false) Ok(false)
} else if exists == 0 { } else if exists == 0 {
@ -121,12 +130,12 @@ impl RedisConnection {
} }
pub async fn delete_captcha(&self, captcha: &str) -> CaptchaResult<()> { pub async fn delete_captcha(&self, captcha: &str) -> CaptchaResult<()> {
exec!(redis::cmd(DEL).arg(&[captcha]), &self)?; self.0.exec(redis::cmd(DEL).arg(&[captcha])).await?;
Ok(()) Ok(())
} }
pub async fn get_visitors(&self, captcha: &str) -> CaptchaResult<usize> { pub async fn get_visitors(&self, captcha: &str) -> CaptchaResult<usize> {
let visitors: usize = exec!(redis::cmd(GET).arg(&[captcha]), &self)?; let visitors: usize = self.0.exec(redis::cmd(GET).arg(&[captcha])).await?;
Ok(visitors) Ok(visitors)
} }
} }
@ -134,32 +143,20 @@ impl RedisConnection {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::defense::{Level, LevelBuilder};
use crate::master::embedded::counter::tests::get_mcaptcha; use crate::master::embedded::counter::tests::get_mcaptcha;
use crate::master::redis::master::{Master, Redis, RedisClient}; use crate::redis::*;
pub async fn connect(redis: &Redis) -> RedisConnection {
let redis = redis.connect();
match &redis {
RedisClient::Single(c) => {
let con = c.get_async_connection().await.unwrap();
RedisConnection::Single(Rc::new(RefCell::new(con)))
}
RedisClient::Cluster(c) => {
let con = c.get_connection().unwrap();
RedisConnection::Cluster(Rc::new(RefCell::new(con)))
}
}
}
const CAPTCHA_NAME: &str = "REDIS_CAPTCHA_TEST"; const CAPTCHA_NAME: &str = "REDIS_CAPTCHA_TEST";
const DURATION: usize = 10;
const REDIS_URL: &str = "redis://127.0.1.1/"; const REDIS_URL: &str = "redis://127.0.1.1/";
#[actix_rt::test] #[actix_rt::test]
async fn redis_master_works() { async fn redis_master_works() {
let client = redis::Client::open("redis://127.0.0.1/").unwrap(); let redis = Redis::new(RedisConfig::Single(REDIS_URL.into()))
let r = connect(&Redis::Single(REDIS_URL.into())).await; .await
.unwrap();
let r = MCaptchaRedis(redis);
let r = r.get_client();
{ {
let _ = r.delete_captcha(CAPTCHA_NAME).await; let _ = r.delete_captcha(CAPTCHA_NAME).await;
} }

View File

@ -15,84 +15,26 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use std::cell::RefCell;
use std::cell::RefMut;
use std::rc::Rc;
use std::sync::mpsc; use std::sync::mpsc;
use actix::dev::*; use actix::dev::*;
use redis::cluster::ClusterClient;
use redis::RedisError;
//use redis::cluster::ClusterConnection;
use redis::Client;
//use redis::Connection;
use redis::RedisResult;
use redis::Value;
use redis::{aio::Connection, cluster::ClusterConnection};
use serde::{Deserialize, Serialize};
use crate::defense::Level; use super::connection::MCaptchaRedis;
use crate::errors::*; use crate::errors::*;
use crate::master::AddVisitorResult; use crate::master::messages::{AddSite, AddVisitor};
use crate::master::{AddSite, AddVisitor, CreateMCaptcha, Master as MasterTrait}; use crate::master::Master as MasterTrait;
use crate::redis::RedisConfig;
use super::connection::RedisConnection;
#[derive(Clone)]
pub enum Redis {
Single(String),
Cluster(Vec<String>),
}
impl Redis {
pub fn connect(&self) -> RedisClient {
match self {
Self::Single(url) => {
let client = Client::open("redis://127.0.0.1/").unwrap();
RedisClient::Single(client)
}
Self::Cluster(nodes) => {
let cluster_client = ClusterClient::open(nodes.to_owned()).unwrap();
RedisClient::Cluster(cluster_client)
}
}
}
}
#[derive(Clone)]
pub enum RedisClient {
Single(Client),
Cluster(ClusterClient),
}
pub struct Master { pub struct Master {
pub redis: RedisClient, pub redis: MCaptchaRedis,
pub con: Rc<RedisConnection>,
} }
impl Master { impl Master {
pub async fn new(redis: Redis) -> CaptchaResult<Self> { pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> {
let (redis, con) = Self::connect(redis).await; let redis = MCaptchaRedis::new(redis).await?;
con.is_module_loaded().await?; let master = Self { redis };
let con = Rc::new(con);
let master = Self { redis, con };
Ok(master) Ok(master)
} }
async fn connect(redis: Redis) -> (RedisClient, RedisConnection) {
let redis = redis.connect();
let client = match &redis {
RedisClient::Single(c) => {
let con = c.get_async_connection().await.unwrap();
RedisConnection::Single(Rc::new(RefCell::new(con)))
}
RedisClient::Cluster(c) => {
let con = c.get_connection().unwrap();
RedisConnection::Cluster(Rc::new(RefCell::new(con)))
}
};
(redis, client)
}
} }
impl MasterTrait for Master {} impl MasterTrait for Master {}
@ -107,7 +49,7 @@ impl Handler<AddVisitor> for Master {
fn handle(&mut self, m: AddVisitor, ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, m: AddVisitor, ctx: &mut Self::Context) -> Self::Result {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let con = Rc::clone(&self.con); let con = self.redis.get_client();
let fut = async move { let fut = async move {
let res = con.add_visitor(m).await; let res = con.add_visitor(m).await;
tx.send(res).unwrap() tx.send(res).unwrap()
@ -123,10 +65,9 @@ impl Handler<AddSite> for Master {
fn handle(&mut self, m: AddSite, ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, m: AddSite, ctx: &mut Self::Context) -> Self::Result {
//let (tx, rx) = mpsc::channel(); //let (tx, rx) = mpsc::channel();
let con = self.redis.get_client();
let con = Rc::clone(&self.con);
let fut = async move { let fut = async move {
let res = con.add_mcaptcha(m).await; let _res = con.add_mcaptcha(m).await;
//tx.send(res).unwrap(); //tx.send(res).unwrap();
} }
.into_actor(self); .into_actor(self);
@ -137,33 +78,34 @@ impl Handler<AddSite> for Master {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::defense::{Level, LevelBuilder};
use crate::master::embedded::counter::tests::get_mcaptcha; use crate::master::embedded::counter::tests::get_mcaptcha;
use crate::master::redis::connection::tests::connect; use crate::master::redis::master::Master;
use crate::master::redis::master::{Master, Redis}; use crate::redis::RedisConfig;
const CAPTCHA_NAME: &str = "REDIS_MASTER_CAPTCHA_TEST"; const CAPTCHA_NAME: &str = "REDIS_MASTER_CAPTCHA_TEST";
const DURATION: usize = 10;
const REDIS_URL: &str = "redis://127.0.1.1/"; const REDIS_URL: &str = "redis://127.0.1.1/";
#[actix_rt::test] #[actix_rt::test]
async fn redis_master_works() { async fn redis_master_works() {
let master = Master::new(Redis::Single(REDIS_URL.into())).await; let master = Master::new(RedisConfig::Single(REDIS_URL.into())).await;
let client = redis::Client::open("redis://127.0.0.1/").unwrap(); let sec_master = Master::new(RedisConfig::Single(REDIS_URL.into())).await;
let r = connect(&Redis::Single(REDIS_URL.into())).await; let r = sec_master.unwrap().redis.get_client();
assert!(master.is_ok()); assert!(master.is_ok());
let master = master.unwrap(); let master = master.unwrap();
{ {
let _ = r.delete_captcha(CAPTCHA_NAME).await; let _ = master.redis.get_client().delete_captcha(CAPTCHA_NAME).await;
} }
let addr = master.start(); let addr = master.start();
let mcaptcha = get_mcaptcha();
let duration = mcaptcha.get_duration();
let add_mcaptcha_msg = AddSite { let add_mcaptcha_msg = AddSite {
id: CAPTCHA_NAME.into(), id: CAPTCHA_NAME.into(),
mcaptcha: get_mcaptcha(), mcaptcha,
}; };
addr.send(add_mcaptcha_msg).await.unwrap(); addr.send(add_mcaptcha_msg).await.unwrap();
@ -171,5 +113,8 @@ mod tests {
addr.send(add_visitor_msg).await.unwrap(); addr.send(add_visitor_msg).await.unwrap();
let visitors = r.get_visitors(CAPTCHA_NAME).await.unwrap(); let visitors = r.get_visitors(CAPTCHA_NAME).await.unwrap();
assert_eq!(visitors, 1); assert_eq!(visitors, 1);
let timer_expire = std::time::Duration::new(duration, 0);
actix::clock::delay_for(timer_expire).await;
} }
} }

View File

@ -15,23 +15,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use std::cell::RefCell;
use std::cell::RefMut;
use std::rc::Rc;
use std::sync::mpsc;
use actix::dev::*; pub mod connection;
use redis::cluster::ClusterClient; pub mod master;
use redis::RedisError;
//use redis::cluster::ClusterConnection;
use redis::Client;
//use redis::Connection;
use redis::RedisResult;
use redis::Value;
use redis::{aio::Connection, cluster::ClusterConnection};
use serde::{Deserialize, Serialize};
mod connection;
mod master;
pub use master::*;

112
src/redis.rs Normal file
View File

@ -0,0 +1,112 @@
/*
* mCaptcha - A proof of work based DoS protection system
* Copyright © 2021 Aravinth Manivannan <realravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::cell::RefCell;
use std::rc::Rc;
use redis::cluster::ClusterClient;
use redis::FromRedisValue;
//use redis::cluster::ClusterConnection;
use redis::Client;
//use redis::Connection;
use redis::{aio::Connection, cluster::ClusterConnection};
use crate::errors::*;
#[derive(Clone)]
pub enum RedisConfig {
Single(String),
Cluster(Vec<String>),
}
impl RedisConfig {
pub fn connect(&self) -> RedisClient {
match self {
Self::Single(url) => {
let client = Client::open(url.as_str()).unwrap();
RedisClient::Single(client)
}
Self::Cluster(nodes) => {
let cluster_client = ClusterClient::open(nodes.to_owned()).unwrap();
RedisClient::Cluster(cluster_client)
}
}
}
}
pub enum RedisConnection {
Single(Rc<RefCell<Connection>>),
Cluster(Rc<RefCell<ClusterConnection>>),
}
impl RedisConnection {
#[inline]
pub fn get_client(&self) -> Self {
match self {
Self::Single(con) => Self::Single(Rc::clone(&con)),
Self::Cluster(con) => Self::Cluster(Rc::clone(&con)),
}
}
#[inline]
pub async fn exec<T: FromRedisValue>(&self, cmd: &mut redis::Cmd) -> redis::RedisResult<T> {
match self {
RedisConnection::Single(con) => cmd.query_async(&mut *con.borrow_mut()).await,
RedisConnection::Cluster(con) => cmd.query(&mut *con.borrow_mut()),
}
}
}
#[derive(Clone)]
pub enum RedisClient {
Single(Client),
Cluster(ClusterClient),
}
pub struct Redis {
_client: RedisClient,
connection: RedisConnection,
}
impl Redis {
pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> {
let (_client, connection) = Self::connect(redis).await;
let master = Self {
_client,
connection,
};
Ok(master)
}
pub fn get_client(&self) -> RedisConnection {
self.connection.get_client()
}
async fn connect(redis: RedisConfig) -> (RedisClient, RedisConnection) {
let redis = redis.connect();
let client = match &redis {
RedisClient::Single(c) => {
let con = c.get_async_connection().await.unwrap();
RedisConnection::Single(Rc::new(RefCell::new(con)))
}
RedisClient::Cluster(c) => {
let con = c.get_connection().unwrap();
RedisConnection::Cluster(Rc::new(RefCell::new(con)))
}
};
(redis, client)
}
}

View File

@ -23,6 +23,7 @@ use pow_sha256::Config;
use crate::cache::messages::*; use crate::cache::messages::*;
use crate::cache::Save; use crate::cache::Save;
use crate::errors::*; use crate::errors::*;
use crate::master::messages::*;
use crate::master::Master; use crate::master::Master;
use crate::pow::*; use crate::pow::*;
@ -42,13 +43,10 @@ where
+ ToEnvelope<T, CacheResult> + ToEnvelope<T, CacheResult>
+ ToEnvelope<T, VerifyCaptchaResult>, + ToEnvelope<T, VerifyCaptchaResult>,
X: Master, X: Master,
<X as actix::Actor>::Context: <X as actix::Actor>::Context: ToEnvelope<X, AddVisitor> + ToEnvelope<X, AddSite>,
ToEnvelope<X, crate::master::AddVisitor> + ToEnvelope<X, crate::master::AddSite>,
{ {
/// utility function to get difficulty factor of site `id` and cache it /// utility function to get difficulty factor of site `id` and cache it
pub async fn get_pow(&self, id: String) -> Option<PoWConfig> { pub async fn get_pow(&self, id: String) -> Option<PoWConfig> {
use crate::master::AddVisitor;
match self match self
.master .master
.send(AddVisitor(id.clone())) .send(AddVisitor(id.clone()))
@ -130,7 +128,6 @@ mod tests {
use crate::cache::HashCache; use crate::cache::HashCache;
use crate::master::embedded::counter::tests::*; use crate::master::embedded::counter::tests::*;
use crate::master::embedded::master::Master; use crate::master::embedded::master::Master;
use crate::master::*;
const MCAPTCHA_NAME: &str = "batsense.net"; const MCAPTCHA_NAME: &str = "batsense.net";