Have sessions expire.

This commit is contained in:
Jeff Baskin 2025-04-10 23:56:16 -04:00
parent 93b881bf6a
commit ebc1e42d2c
2 changed files with 151 additions and 14 deletions

View File

@ -5,7 +5,7 @@ use std::{
time::Duration, time::Duration,
}; };
const SLEEP_FOR: Duration = Duration::from_millis(1000); const SLEEP_FOR: Duration = Duration::from_secs(1);
pub struct Clock { pub struct Clock {
queue: Queue, queue: Queue,

View File

@ -2,16 +2,81 @@ use crate::{
field::Field, field::Field,
queue::{Message, MsgType, Queue}, queue::{Message, MsgType, Queue},
}; };
use chrono::prelude::*;
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::mpsc::{channel, Receiver}, sync::mpsc::{channel, Receiver},
thread::spawn, thread::spawn,
time::Duration,
}; };
use uuid::Uuid; use uuid::Uuid;
const RESPONDS_TO: [MsgType; 1] = [MsgType::SessionValidate]; const EXPIRE_IN: Duration = Duration::from_secs(60 * 60);
const RESPONDS_TO: [MsgType; 2] = [MsgType::SessionValidate, MsgType::Time];
struct SessionData; struct SessionData {
expire_on: DateTime<Utc>,
}
impl SessionData {
fn new() -> Self {
Self {
expire_on: Utc::now() + EXPIRE_IN,
}
}
fn extend(&mut self) {
self.expire_on = Utc::now() + EXPIRE_IN;
}
fn is_expired(&self, now: &DateTime<Utc>) -> bool {
now > &self.expire_on
}
}
#[cfg(test)]
mod sessiondatas {
use super::*;
#[test]
fn create_session_data() {
let expire = Utc::now() + EXPIRE_IN;
let data = SessionData::new();
assert!(
data.expire_on > expire,
"{:?} should be greater than {:?}",
data.expire_on,
expire
);
}
#[test]
fn extend_usage_time() {
let mut data = SessionData::new();
let expire = Utc::now() + EXPIRE_IN;
data.extend();
assert!(
data.expire_on > expire,
"{:?} should be greater than {:?}",
data.expire_on,
expire
);
}
#[test]
fn is_expired() {
let data = SessionData::new();
let expire = Utc::now() + EXPIRE_IN;
assert!(data.is_expired(&expire), "should be expired");
}
#[test]
fn is_not_expired() {
let expire = Utc::now() + EXPIRE_IN;
let data = SessionData::new();
assert!(!data.is_expired(&expire), "should be not expired");
}
}
pub struct Session { pub struct Session {
data: HashMap<Uuid, SessionData>, data: HashMap<Uuid, SessionData>,
@ -40,22 +105,26 @@ impl Session {
fn listen(&mut self) { fn listen(&mut self) {
loop { loop {
let msg = self.rx.recv().unwrap(); let msg = self.rx.recv().unwrap();
self.validate(msg); match msg.get_class() {
MsgType::SessionValidate => self.validate(msg),
MsgType::Time => self.expire(msg),
_ => unreachable!("received unknown message"),
};
} }
} }
fn validate(&mut self, msg: Message) { fn validate(&mut self, msg: Message) {
match msg.get_data("sess_id") { match msg.get_data("sess_id") {
Some(sid) => match sid { Some(sid) => match sid {
Field::Uuid(sess_id) => { Field::Uuid(sess_id) => match self.data.get_mut(&sess_id) {
if self.data.contains_key(&sess_id) { Some(sess_data) => {
sess_data.extend();
let mut reply = msg.reply(MsgType::Session); let mut reply = msg.reply(MsgType::Session);
reply.add_data("sess_id", sess_id.clone()); reply.add_data("sess_id", sess_id.clone());
self.queue.send(reply).unwrap(); self.queue.send(reply).unwrap();
} else {
self.new_session(msg);
}
} }
None => self.new_session(msg),
},
_ => self.new_session(msg), _ => self.new_session(msg),
}, },
None => self.new_session(msg), None => self.new_session(msg),
@ -67,11 +136,24 @@ impl Session {
while self.data.contains_key(&id) { while self.data.contains_key(&id) {
id = Uuid::new_v4(); id = Uuid::new_v4();
} }
self.data.insert(id.clone(), SessionData {}); self.data.insert(id.clone(), SessionData::new());
let mut reply = msg.reply(MsgType::Session); let mut reply = msg.reply(MsgType::Session);
reply.add_data("sess_id", id); reply.add_data("sess_id", id);
self.queue.send(reply).unwrap(); self.queue.send(reply).unwrap();
} }
fn expire(&mut self, msg: Message) {
let now = msg.get_data("time").unwrap().to_datetime().unwrap();
let mut expired: Vec<Uuid> = Vec::new();
for (id, data) in self.data.iter() {
if data.is_expired(&now) {
expired.push(id.clone());
}
}
for id in expired.iter() {
self.data.remove(id);
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -90,6 +172,13 @@ mod sessions {
(queue, rx) (queue, rx)
} }
fn create_session(queue: &Queue, rx: &Receiver<Message>) -> Uuid {
let msg = Message::new(MsgType::SessionValidate);
queue.send(msg.clone()).unwrap();
let holder = rx.recv_timeout(TIMEOUT).unwrap();
holder.get_data("sess_id").unwrap().to_uuid().unwrap()
}
#[test] #[test]
fn get_new_session() { fn get_new_session() {
let listen_for = [MsgType::Session]; let listen_for = [MsgType::Session];
@ -123,13 +212,11 @@ mod sessions {
} }
#[test] #[test]
fn existing_id_are_returned() { fn existing_id_is_returned() {
let listen_for = [MsgType::Session]; let listen_for = [MsgType::Session];
let (queue, rx) = setup_session(listen_for.to_vec()); let (queue, rx) = setup_session(listen_for.to_vec());
let id = create_session(&queue, &rx);
let mut msg = Message::new(MsgType::SessionValidate); let mut msg = Message::new(MsgType::SessionValidate);
queue.send(msg.clone()).unwrap();
let holder = rx.recv_timeout(TIMEOUT).unwrap();
let id = holder.get_data("sess_id").unwrap().to_uuid().unwrap();
msg.add_data("sess_id", id.clone()); msg.add_data("sess_id", id.clone());
queue.send(msg).unwrap(); queue.send(msg).unwrap();
let result = rx.recv_timeout(TIMEOUT).unwrap(); let result = rx.recv_timeout(TIMEOUT).unwrap();
@ -162,4 +249,54 @@ mod sessions {
let output = result.get_data("sess_id").unwrap().to_string(); let output = result.get_data("sess_id").unwrap().to_string();
assert_ne!(output, id); assert_ne!(output, id);
} }
#[test]
fn timer_does_nothing_to_unexpired() {
let expire = Utc::now() + EXPIRE_IN;
let listen_for = [MsgType::Session];
let (queue, rx) = setup_session(listen_for.to_vec());
let id = create_session(&queue, &rx);
let mut time_msg = Message::new(MsgType::Time);
time_msg.add_data("time", expire);
queue.send(time_msg).unwrap();
let mut validate_msg = Message::new(MsgType::SessionValidate);
validate_msg.add_data("sess_id", id.clone());
queue.send(validate_msg).unwrap();
let result = rx.recv_timeout(TIMEOUT).unwrap();
assert_eq!(result.get_data("sess_id").unwrap().to_uuid().unwrap(), id);
}
#[test]
fn timer_removes_expired() {
let listen_for = [MsgType::Session];
let (queue, rx) = setup_session(listen_for.to_vec());
let id = create_session(&queue, &rx);
let expire = Utc::now() + EXPIRE_IN;
let mut time_msg = Message::new(MsgType::Time);
time_msg.add_data("time", expire);
queue.send(time_msg).unwrap();
let mut validate_msg = Message::new(MsgType::SessionValidate);
validate_msg.add_data("sess_id", id.clone());
queue.send(validate_msg).unwrap();
let result = rx.recv_timeout(TIMEOUT).unwrap();
assert_ne!(result.get_data("sess_id").unwrap().to_uuid().unwrap(), id);
}
#[test]
fn validate_extends_session() {
let listen_for = [MsgType::Session];
let (queue, rx) = setup_session(listen_for.to_vec());
let id = create_session(&queue, &rx);
let mut validate_msg = Message::new(MsgType::SessionValidate);
validate_msg.add_data("sess_id", id.clone());
let expire = Utc::now() + EXPIRE_IN;
let mut time_msg = Message::new(MsgType::Time);
time_msg.add_data("time", expire);
queue.send(validate_msg.clone()).unwrap();
queue.send(time_msg).unwrap();
queue.send(validate_msg).unwrap();
rx.recv_timeout(TIMEOUT).unwrap();
let result = rx.recv_timeout(TIMEOUT).unwrap();
assert_eq!(result.get_data("sess_id").unwrap().to_uuid().unwrap(), id);
}
} }