Use Mutex instead of RwLock and prevent unneeded allocation on answer check

This commit is contained in:
Mo 2023-02-23 15:45:20 +01:00
parent 0b9283a69b
commit db0ad404a1
2 changed files with 39 additions and 24 deletions

View file

@ -1,58 +1,71 @@
use std::sync::RwLock; use std::{ops::Deref, sync::Mutex};
struct CaptchaSolutions { pub struct CaptchaSolutions {
last_id: u16, last_id: u16,
solutions: Vec<Option<String>>, solutions: Vec<Option<String>>,
} }
impl CaptchaSolutions { impl CaptchaSolutions {
fn get_sol(&self, id: u16) -> &Option<String> { fn get(&self, id: u16) -> &Option<String> {
&self.solutions[id as usize] &self.solutions[id as usize]
} }
fn set_sol(&mut self, id: u16, sol: Option<String>) { fn set(&mut self, id: u16, sol: Option<String>) {
self.solutions[id as usize] = sol; self.solutions[id as usize] = sol;
} }
#[must_use = "The new id has to be stored for future verification of an answer!"]
fn push(&mut self, sol: String) -> u16 {
self.last_id = self.last_id.wrapping_add(1);
self.set(self.last_id, Some(sol));
self.last_id
}
} }
pub struct SharedCaptchaSolutions { pub struct SharedCaptchaSolutions {
inner: RwLock<CaptchaSolutions>, inner: Mutex<CaptchaSolutions>,
} }
impl Default for SharedCaptchaSolutions { impl Default for SharedCaptchaSolutions {
fn default() -> Self { fn default() -> Self {
let max_size = (u16::MAX as usize) + 1; let max_size = u16::MAX as usize + 1;
let mut solutions = Vec::with_capacity(max_size);
solutions.resize(max_size, None);
Self { Self {
inner: RwLock::new(CaptchaSolutions { inner: Mutex::new(CaptchaSolutions {
last_id: 0, last_id: 0,
solutions, solutions: vec![None; max_size],
}), }),
} }
} }
} }
impl Deref for SharedCaptchaSolutions {
type Target = Mutex<CaptchaSolutions>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl SharedCaptchaSolutions { impl SharedCaptchaSolutions {
pub fn store_sol(&self, sol: String) -> u16 { #[must_use = "The new id has to be stored for future verification of an answer!"]
let mut captcha_solutions = self.inner.write().unwrap(); pub fn store_solution(&self, sol: String) -> u16 {
let mut sols = self.lock().unwrap();
let new_id = captcha_solutions.last_id.wrapping_add(1); sols.push(sol)
captcha_solutions.last_id = new_id;
captcha_solutions.set_sol(new_id, Some(sol));
new_id
} }
pub fn check_answer(&self, id: u16, answer: &str) -> bool { pub fn check_answer(&self, id: u16, answer: &str) -> bool {
if *self.inner.read().unwrap().get_sol(id) != Some(answer.trim().to_string()) { let mut sols = self.lock().unwrap();
return false;
if let Some(sol) = sols.get(id) {
if sol == answer.trim() {
sols.set(id, None);
return true;
}
} }
self.inner.write().unwrap().set_sol(id, None); false
true
} }
} }

View file

@ -43,7 +43,7 @@ pub async fn render_contact_form(params: IndexParams<'_>) -> Result<Response, er
let solution = captcha.chars_as_string(); let solution = captcha.chars_as_string();
let id = params.captcha_solutions.store_sol(solution); let id = params.captcha_solutions.store_solution(solution);
let template = templates::ContactForm { let template = templates::ContactForm {
base: templates::Base { base: templates::Base {
@ -92,6 +92,7 @@ pub async fn submit(
) -> Result<Response, errors::AppError> { ) -> Result<Response, errors::AppError> {
if !captcha_solutions.check_answer(form.id, &form.captcha_answer) { if !captcha_solutions.check_answer(form.id, &form.captcha_answer) {
info!("Wrong CAPTCHA"); info!("Wrong CAPTCHA");
return failed_submission( return failed_submission(
Arc::clone(&config), Arc::clone(&config),
captcha_solutions, captcha_solutions,
@ -105,6 +106,7 @@ pub async fn submit(
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
error!("{e:?}"); error!("{e:?}");
return failed_submission( return failed_submission(
Arc::clone(&config), Arc::clone(&config),
captcha_solutions, captcha_solutions,