diff --git a/CHANGELOG.md b/CHANGELOG.md
index e727600..9146960 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
## 0.1.4
+## Added:
+
+- `Master` trait: provides methods to manage mcaptcha
+
## Changed:
- `PoWConfig` has an extra field to send internal `PoW` salt to clients.
@@ -11,9 +15,14 @@
- `master::Master` is moved to `master::embedded::master` in preparation
for Redis based implementation.
-- `crate::mcaptcha` is moved to `master::embedded::mcaptcha` in preparation
+- `crate::mcaptcha` is moved to `master::embedded::counter` in preparation
for Redis based implementation.
+- `AddSite` message for `Master` now requires an instance of
+ `crate::master::MCaptcha`. In the case of
+ `crate::master::embedded::master`, it automatically starts `Counter`
+ actor.
+
## 0.1.3
## Added
diff --git a/examples/simple.rs b/examples/simple.rs
index e61668d..9b9e2c1 100644
--- a/examples/simple.rs
+++ b/examples/simple.rs
@@ -1,6 +1,7 @@
use libmcaptcha::{
cache::{messages::VerifyCaptchaResult, HashCache},
- master::embedded::master::{AddSiteBuilder, Master},
+ master::embedded::master::Master,
+ master::AddSiteBuilder,
pow::{ConfigBuilder, Work},
system::SystemBuilder,
DefenseBuilder, LevelBuilder, MCaptchaBuilder,
@@ -75,8 +76,7 @@ async fn main() -> std::io::Result<()> {
.duration(30)
// .cache(cache)
.build()
- .unwrap()
- .start();
+ .unwrap();
// unique value identifying an MCaptcha actor
let mcaptcha_name = "batsense.net";
@@ -84,7 +84,7 @@ async fn main() -> std::io::Result<()> {
// add MCaptcha to Master
let msg = AddSiteBuilder::default()
.id(mcaptcha_name.into())
- .addr(mcaptcha.clone())
+ .mcaptcha(mcaptcha)
.build()
.unwrap();
system.master.send(msg).await.unwrap();
diff --git a/src/lib.rs b/src/lib.rs
index 636feaa..25c525e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -48,7 +48,8 @@
//! ```rust
//! use libmcaptcha::{
//! cache::{messages::VerifyCaptchaResult, HashCache},
-//! master::embedded::master::{AddSiteBuilder, Master},
+//! master::embedded::master:: Master,
+//! master::AddSiteBuilder,
//! pow::{ConfigBuilder, Work},
//! system::SystemBuilder,
//! DefenseBuilder, LevelBuilder, MCaptchaBuilder,
@@ -123,8 +124,7 @@
//! .duration(30)
//! // .cache(cache)
//! .build()
-//! .unwrap()
-//! .start();
+//! .unwrap();
//!
//! // unique value identifying an MCaptcha actor
//! let mcaptcha_name = "batsense.net";
@@ -132,7 +132,7 @@
//! // add MCaptcha to Master
//! let msg = AddSiteBuilder::default()
//! .id(mcaptcha_name.into())
-//! .addr(mcaptcha.clone())
+//! .mcaptcha(mcaptcha)
//! .build()
//! .unwrap();
//! system.master.send(msg).await.unwrap();
@@ -196,4 +196,5 @@ mod utils;
pub use crate::cache::hashcache::HashCache;
pub use defense::{Defense, DefenseBuilder, LevelBuilder};
-pub use master::embedded::mcaptcha::{MCaptcha, MCaptchaBuilder};
+pub use master::embedded::counter::Counter;
+pub use master::MCaptchaBuilder;
diff --git a/src/master/embedded/mcaptcha.rs b/src/master/embedded/counter.rs
similarity index 80%
rename from src/master/embedded/mcaptcha.rs
rename to src/master/embedded/counter.rs
index c8d3977..bef65fb 100644
--- a/src/master/embedded/mcaptcha.rs
+++ b/src/master/embedded/counter.rs
@@ -15,11 +15,17 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-//! MCaptcha actor module that manages defense levels
+//! Counter actor module that manages defense levels
//!
//! ## Usage:
//! ```rust
-//! use libmcaptcha::{master::embedded::mcaptcha::AddVisitor, MCaptchaBuilder, cache::HashCache, LevelBuilder, DefenseBuilder};
+//! use libmcaptcha::{
+//! master::embedded::counter::{Counter, AddVisitor},
+//! master::MCaptchaBuilder,
+//! cache::HashCache,
+//! LevelBuilder,
+//! DefenseBuilder
+//! };
//! // traits from actix needs to be in scope for starting actor
//! use actix::prelude::*;
//!
@@ -55,18 +61,20 @@
//! .build()
//! .unwrap();
//!
-//! // create and start MCaptcha actor
+//! // create and start Counter actor
//! //let cache = HashCache::default().start();
//! let mcaptcha = MCaptchaBuilder::default()
//! .defense(defense)
//! // leaky bucket algorithm's emission interval
//! .duration(30)
//! .build()
-//! .unwrap()
-//! .start();
+//! .unwrap();
+//!
+//! let counter: Counter = mcaptcha.into();
+//! let counter = counter.start();
//!
//! // increment count when user visits protected routes
-//! mcaptcha.send(AddVisitor).await.unwrap();
+//! counter.send(AddVisitor).await.unwrap();
//!
//! Ok(())
//! }
@@ -78,72 +86,29 @@ use std::time::Duration;
use actix::dev::*;
use serde::{Deserialize, Serialize};
-use crate::{
- defense::Defense,
- errors::{CaptchaError, CaptchaResult},
- master::AddVisitorResult,
-};
-
-/// Builder for [MCaptcha]
-#[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct MCaptchaBuilder {
- visitor_threshold: u32,
- defense: Option,
- duration: Option,
-}
-
-impl Default for MCaptchaBuilder {
- fn default() -> Self {
- MCaptchaBuilder {
- visitor_threshold: 0,
- defense: None,
- duration: None,
- }
- }
-}
+use crate::master::MCaptcha;
+use crate::{defense::Defense, master::AddVisitorResult};
/// This struct represents the mCaptcha state and is used
/// to configure leaky-bucket lifetime and manage defense
#[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct MCaptcha {
+pub struct Counter {
visitor_threshold: u32,
defense: Defense,
duration: u64,
}
-
-impl MCaptchaBuilder {
- /// set defense
- pub fn defense(&mut self, d: Defense) -> &mut Self {
- self.defense = Some(d);
- self
- }
-
- /// set duration
- pub fn duration(&mut self, d: u64) -> &mut Self {
- self.duration = Some(d);
- self
- }
-
- /// Builds new [MCaptcha]
- pub fn build(&mut self) -> CaptchaResult {
- if self.duration.is_none() {
- Err(CaptchaError::PleaseSetValue("duration".into()))
- } else if self.defense.is_none() {
- Err(CaptchaError::PleaseSetValue("defense".into()))
- } else if self.duration <= Some(0) {
- Err(CaptchaError::CaptchaDurationZero)
- } else {
- let m = MCaptcha {
- duration: self.duration.unwrap(),
- defense: self.defense.clone().unwrap(),
- visitor_threshold: self.visitor_threshold,
- };
- Ok(m)
- }
+impl From for Counter {
+ fn from(m: MCaptcha) -> Counter {
+ let m = Counter {
+ duration: m.duration,
+ defense: m.defense,
+ visitor_threshold: m.visitor_threshold,
+ };
+ m
}
}
-impl MCaptcha {
+impl Counter {
/// increments the visitor count by one
pub fn add_visitor(&mut self) {
self.visitor_threshold += 1;
@@ -166,12 +131,12 @@ impl MCaptcha {
self.defense.get_difficulty()
}
- /// get [MCaptcha]'s lifetime
+ /// get [Counter]'s lifetime
pub fn get_duration(&self) -> u64 {
self.duration
}
}
-impl Actor for MCaptcha {
+impl Actor for Counter {
type Context = Context;
}
@@ -180,7 +145,7 @@ impl Actor for MCaptcha {
#[rtype(result = "()")]
struct DeleteVisitor;
-impl Handler for MCaptcha {
+impl Handler for Counter {
type Result = ();
fn handle(&mut self, _msg: DeleteVisitor, _ctx: &mut Self::Context) -> Self::Result {
self.decrement_visitor();
@@ -194,7 +159,7 @@ impl Handler for MCaptcha {
pub struct AddVisitor;
impl AddVisitorResult {
- fn new(m: &MCaptcha) -> Self {
+ fn new(m: &Counter) -> Self {
AddVisitorResult {
duration: m.get_duration(),
difficulty_factor: m.get_difficulty(),
@@ -202,7 +167,7 @@ impl AddVisitorResult {
}
}
-impl Handler for MCaptcha {
+impl Handler for Counter {
type Result = MessageResult;
fn handle(&mut self, _: AddVisitor, ctx: &mut Self::Context) -> Self::Result {
@@ -228,7 +193,7 @@ impl Handler for MCaptcha {
#[rtype(result = "u32")]
pub struct GetCurrentVisitorCount;
-impl Handler for MCaptcha {
+impl Handler for Counter {
type Result = MessageResult;
fn handle(&mut self, _: GetCurrentVisitorCount, _ctx: &mut Self::Context) -> Self::Result {
@@ -236,12 +201,12 @@ impl Handler for MCaptcha {
}
}
-/// Message to stop [MCaptcha]
+/// Message to stop [Counter]
#[derive(Message)]
#[rtype(result = "()")]
pub struct Stop;
-impl Handler for MCaptcha {
+impl Handler for Counter {
type Result = ();
fn handle(&mut self, _: Stop, ctx: &mut Self::Context) -> Self::Result {
@@ -253,6 +218,8 @@ impl Handler for MCaptcha {
pub mod tests {
use super::*;
use crate::defense::*;
+ use crate::errors::*;
+ use crate::master::MCaptchaBuilder;
// constants for testing
// (visitor count, level)
@@ -260,7 +227,7 @@ pub mod tests {
pub const LEVEL_2: (u32, u32) = (500, 500);
pub const DURATION: u64 = 5;
- type MyActor = Addr;
+ type MyActor = Addr;
pub fn get_defense() -> Defense {
DefenseBuilder::default()
@@ -286,13 +253,17 @@ pub mod tests {
.unwrap()
}
- async fn race(addr: Addr, count: (u32, u32)) {
+ async fn race(addr: Addr, count: (u32, u32)) {
for _ in 0..count.0 as usize - 1 {
let _ = addr.send(AddVisitor).await.unwrap();
}
}
- pub fn get_counter() -> MCaptcha {
+ pub fn get_counter() -> Counter {
+ get_mcaptcha().into()
+ }
+
+ pub fn get_mcaptcha() -> MCaptcha {
MCaptchaBuilder::default()
.defense(get_defense())
.duration(DURATION)
diff --git a/src/master/embedded/master.rs b/src/master/embedded/master.rs
index 682a54a..438c18a 100644
--- a/src/master/embedded/master.rs
+++ b/src/master/embedded/master.rs
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-//! [Master] actor module that manages [MCaptcha] actors
+//! [Master] actor module that manages [Counter] actors
use std::collections::BTreeMap;
use std::sync::mpsc::channel;
use std::time::Duration;
@@ -23,30 +23,28 @@ use std::time::Duration;
//use actix::clock::sleep;
use actix::clock::delay_for;
use actix::dev::*;
-use derive_builder::Builder;
use log::info;
-use super::mcaptcha::MCaptcha;
-use crate::master::AddVisitor;
+use super::counter::Counter;
use crate::master::Master as MasterTrait;
+use crate::master::{AddSite, AddVisitor};
-/// This Actor manages the [MCaptcha] actors.
-/// A service can have several [MCaptcha] actors with
+/// This Actor manages the [Counter] actors.
+/// A service can have several [Counter] actors with
/// varying [Defense][crate::defense::Defense] configurations
/// so a "master" actor is needed to manage them all
#[derive(Clone, Default)]
pub struct Master {
- sites: BTreeMap, Addr)>,
+ sites: BTreeMap, Addr)>,
gc: u64,
}
impl MasterTrait for Master {}
impl Master {
- /// add [MCaptcha] actor to [Master]
- pub fn add_site(&mut self, details: AddSite) {
- self.sites
- .insert(details.id, (None, details.addr.to_owned()));
+ /// add [Counter] actor to [Master]
+ pub fn add_site(&mut self, addr: Addr, id: String) {
+ self.sites.insert(id, (None, addr.to_owned()));
}
/// create new master
@@ -58,8 +56,8 @@ impl Master {
}
}
- /// get [MCaptcha] actor from [Master]
- pub fn get_site<'a, 'b>(&'a mut self, id: &'b str) -> Option> {
+ /// get [Counter] actor from [Master]
+ pub fn get_site<'a, 'b>(&'a mut self, id: &'b str) -> Option> {
let mut r = None;
if let Some((read_val, addr)) = self.sites.get_mut(id) {
r = Some(addr.clone());
@@ -68,7 +66,7 @@ impl Master {
r
}
- /// remvoes [MCaptcha] actor from [Master]
+ /// remvoes [Counter] actor from [Master]
pub fn rm_site(&mut self, id: &str) {
self.sites.remove(id);
}
@@ -99,7 +97,7 @@ impl Handler for Master {
let fut = async move {
let config = addr
.unwrap()
- .send(super::mcaptcha::AddVisitor)
+ .send(super::counter::AddVisitor)
.await
.unwrap();
@@ -112,9 +110,9 @@ impl Handler for Master {
}
}
-/// Message to get an [MCaptcha] actor from master
+/// Message to get an [Counter] actor from master
#[derive(Message)]
-#[rtype(result = "Option>")]
+#[rtype(result = "Option>")]
pub struct GetSite(pub String);
impl Handler for Master {
@@ -130,7 +128,7 @@ impl Handler for Master {
}
}
-/// Message to clean up master of [MCaptcha] actors with zero visitor count
+/// Message to clean up master of [Counter] actors with zero visitor count
#[derive(Message)]
#[rtype(result = "()")]
pub struct CleanUp;
@@ -145,7 +143,7 @@ impl Handler for Master {
info!("init master actor cleanup up");
let task = async move {
for (id, (new, addr)) in sites.iter() {
- use super::mcaptcha::{GetCurrentVisitorCount, Stop};
+ use super::counter::{GetCurrentVisitorCount, Stop};
let visitor_count = addr.send(GetCurrentVisitorCount).await.unwrap();
println!("{}", visitor_count);
if visitor_count == 0 && new.is_some() {
@@ -165,7 +163,7 @@ impl Handler for Master {
}
}
-/// Message to delete [MCaptcha] actor
+/// Message to delete [Counter] actor
#[derive(Message)]
#[rtype(result = "()")]
pub struct RemoveSite(pub String);
@@ -178,43 +176,38 @@ impl Handler for Master {
}
}
-/// Message to add an [MCaptcha] actor to [Master]
-#[derive(Message, Builder)]
-#[rtype(result = "()")]
-pub struct AddSite {
- pub id: String,
- pub addr: Addr,
-}
-
impl Handler for Master {
type Result = ();
fn handle(&mut self, m: AddSite, _ctx: &mut Self::Context) -> Self::Result {
- self.add_site(m);
+ let counter: Counter = m.mcaptcha.into();
+ let addr = counter.start();
+ self.add_site(addr, m.id);
}
}
#[cfg(test)]
mod tests {
use super::*;
- use crate::master::embedded::mcaptcha::tests::*;
+ use crate::master::embedded::counter::tests::*;
+ use crate::master::AddSiteBuilder;
#[actix_rt::test]
async fn master_actor_works() {
let addr = Master::new(1).start();
let id = "yo";
- let mcaptcha = get_counter().start();
+ let mcaptcha = get_mcaptcha();
let msg = AddSiteBuilder::default()
.id(id.into())
- .addr(mcaptcha.clone())
+ .mcaptcha(mcaptcha.clone())
.build()
.unwrap();
addr.send(msg).await.unwrap();
let mcaptcha_addr = addr.send(GetSite(id.into())).await.unwrap();
- assert_eq!(mcaptcha_addr, Some(mcaptcha));
+ assert!(mcaptcha_addr.is_some());
let addr_doesnt_exist = addr.send(GetSite("a".into())).await.unwrap();
assert!(addr_doesnt_exist.is_none());
diff --git a/src/master/embedded/mod.rs b/src/master/embedded/mod.rs
index 2de680d..ecd66b1 100644
--- a/src/master/embedded/mod.rs
+++ b/src/master/embedded/mod.rs
@@ -16,5 +16,5 @@
* along with this program. If not, see .
*/
+pub mod counter;
pub mod master;
-pub mod mcaptcha;
diff --git a/src/master/mod.rs b/src/master/mod.rs
index d297b99..bddc455 100644
--- a/src/master/mod.rs
+++ b/src/master/mod.rs
@@ -19,12 +19,18 @@
use std::sync::mpsc::Receiver;
use actix::dev::*;
+use derive_builder::Builder;
use serde::{Deserialize, Serialize};
pub mod embedded;
+use crate::defense::Defense;
+use crate::errors::*;
+
/// Describes actor handler trait impls that are required by a cache implementation
-pub trait Master: actix::Actor + actix::Handler {}
+pub trait Master: actix::Actor + actix::Handler + actix::Handler {}
+
+//+ actix::Handler
/// Message to add visitor to an [MCaptcha] actor
#[derive(Message)]
@@ -39,3 +45,68 @@ pub struct AddVisitorResult {
pub duration: u64,
pub difficulty_factor: u32,
}
+
+/// Message to add an [Counter] actor to [Master]
+#[derive(Message, Builder)]
+#[rtype(result = "()")]
+pub struct AddSite {
+ pub id: String,
+ pub mcaptcha: MCaptcha,
+}
+
+/// Builder for [MCaptcha]
+#[derive(Clone, Serialize, Deserialize, Debug)]
+pub struct MCaptchaBuilder {
+ visitor_threshold: u32,
+ defense: Option,
+ duration: Option,
+}
+
+impl Default for MCaptchaBuilder {
+ fn default() -> Self {
+ MCaptchaBuilder {
+ visitor_threshold: 0,
+ defense: None,
+ duration: None,
+ }
+ }
+}
+
+impl MCaptchaBuilder {
+ /// set defense
+ pub fn defense(&mut self, d: Defense) -> &mut Self {
+ self.defense = Some(d);
+ self
+ }
+
+ /// set duration
+ pub fn duration(&mut self, d: u64) -> &mut Self {
+ self.duration = Some(d);
+ self
+ }
+
+ /// Builds new [MCaptcha]
+ pub fn build(self: &mut MCaptchaBuilder) -> CaptchaResult {
+ if self.duration.is_none() {
+ Err(CaptchaError::PleaseSetValue("duration".into()))
+ } else if self.defense.is_none() {
+ Err(CaptchaError::PleaseSetValue("defense".into()))
+ } else if self.duration <= Some(0) {
+ Err(CaptchaError::CaptchaDurationZero)
+ } else {
+ let m = MCaptcha {
+ duration: self.duration.unwrap(),
+ defense: self.defense.clone().unwrap(),
+ visitor_threshold: self.visitor_threshold,
+ };
+ Ok(m)
+ }
+ }
+}
+
+#[derive(Clone, Serialize, Deserialize, Debug)]
+pub struct MCaptcha {
+ visitor_threshold: u32,
+ defense: Defense,
+ duration: u64,
+}
diff --git a/src/system.rs b/src/system.rs
index 72cc5e3..72ced7c 100644
--- a/src/system.rs
+++ b/src/system.rs
@@ -42,7 +42,8 @@ where
+ ToEnvelope
+ ToEnvelope,
X: Master,
- ::Context: ToEnvelope,
+ ::Context:
+ ToEnvelope + ToEnvelope,
{
/// utility function to get difficulty factor of site `id` and cache it
pub async fn get_pow(&self, id: String) -> Option {
@@ -123,21 +124,21 @@ mod tests {
use super::System;
use super::*;
use crate::cache::HashCache;
+ use crate::master::embedded::counter::tests::*;
use crate::master::embedded::master::Master;
- use crate::master::embedded::master::*;
- use crate::master::embedded::mcaptcha::tests::*;
+ use crate::master::*;
const MCAPTCHA_NAME: &str = "batsense.net";
async fn boostrap_system(gc: u64) -> System {
let master = Master::new(gc).start();
- let mcaptcha = get_counter().start();
+ let mcaptcha = get_mcaptcha();
let pow = get_config();
let cache = HashCache::default().start();
let msg = AddSiteBuilder::default()
.id(MCAPTCHA_NAME.into())
- .addr(mcaptcha.clone())
+ .mcaptcha(mcaptcha)
.build()
.unwrap();