Have sessions expire.
This commit is contained in:
parent
93b881bf6a
commit
ebc1e42d2c
@ -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,
|
||||||
|
163
src/session.rs
163
src/session.rs
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user