master
Avril 4 years ago
parent f43f5d238d
commit 916bc5ed0e
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,4 +1,5 @@
#![cfg_attr(nightly, feature(never_type))] #![cfg_attr(nightly, feature(never_type))]
#![feature(entry_insert)]
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_imports)] #![allow(unused_imports)]

@ -6,4 +6,5 @@ use super::*;
pub struct ServerState pub struct ServerState
{ {
root: data::Datamap, root: data::Datamap,
userspace: user::Userspace,
} }

@ -27,6 +27,8 @@ pub struct Config
} }
pub mod settings; pub mod settings;
mod session;
mod state; mod state;
mod source; mod source;

@ -0,0 +1,133 @@
//! Handles sessions and maintaining them
use super::*;
use tokio::{
time::{
self,
DelayQueue,
delay_queue,
},
sync::{
RwLock,
RwLockReadGuard,
RwLockWriteGuard,
},
};
use std::collections::{
HashMap,
};
use std::{
task::{Context,Poll},
pin::Pin,
};
use server::user::UserID;
id_type!(pub SessionID: "A unique session id");
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct Session
{
id: Option<SessionID>, // `None` for uncoupled
users: Vec<UserID>,
}
impl Session
{
fn couple_to_new(self) -> Self
{
self.couple_to(SessionID::id_new())
}
#[inline] fn couple_to(self, id: SessionID) -> Self
{
Self {
id: Some(id),
..self
}
}
/// See if this session object is coupled. If it is, return the session's ID.
#[inline] pub fn coupled(&self) -> Option<&SessionID>
{
self.id.as_ref()
}
}
pub struct SessionPurge<'a, F = fn(Session)>(&'a mut Sessions, F);
impl<'a, F> Future for SessionPurge<'a, F>
where F: FnMut(Session) + 'a + Unpin
{
type Output = Result<(), time::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
while let Some(res) = futures::ready!(this.0.expire.poll_expired(cx)) {
let ent = res?;
let _ = this.0.ids.remove(ent.get_ref()).map(|x| x.0).map(&mut this.1);
}
Poll::Ready(Ok(()))
}
}
#[derive(Debug)]
pub struct Sessions
{
ids: HashMap<SessionID, (Session, delay_queue::Key)>,
expire: DelayQueue<SessionID>,
ttl_range: (u64, u64),
rewrite_needed: !,
}
impl Sessions
{
pub fn purge(&mut self) -> SessionPurge<'_>
{
SessionPurge(self, std::mem::drop)
}
pub fn purge_now(&mut self)
{
self.purge().now_or_never();
}
pub fn new_session_id(&mut self, ses: Session) -> SessionID
{
self.purge_now();
let ses = ses.couple_to_new();
let id = ses.coupled().unwrap().clone();
let k = self.expire.insert(id.clone(), time::Duration::from_millis(self.ttl_range.jitter()));
self.ids.insert(id.clone(), (ses, k));
id
}
pub fn new_session(&mut self, ses: Session) -> &mut Session
{
self.purge_now();
let ses = ses.couple_to_new();
let id = ses.coupled().unwrap().clone();
let k = self.expire.insert(id.clone(), time::Duration::from_millis(self.ttl_range.jitter()));
&mut self.ids.entry(id).insert((ses, k)).into_mut().0
}
pub fn session(&mut self, id: &SessionID) -> Option<&mut Session>
{
/////////TODO: `Session` and `Sessions` need a whole rework. We can't be returning references that borrow this container, it will block all other consumers wanting to get their own session reference. Rewrite using `Arc<Session>`s instead of `Session` s.
self.purge_now();
self.ids.get_mut(id).map(|(ses, _)| ses)
}
pub fn new(cfg: &settings::Settings) -> Sessions
{
Self{
ids: HashMap::new(),
expire: DelayQueue::new(),
ttl_range: cfg.auth_token_ttl_millis,
}
}
}

@ -14,6 +14,8 @@ use tokio::{
RwLock, RwLock,
RwLockWriteGuard, RwLockWriteGuard,
RwLockReadGuard, RwLockReadGuard,
Notify,
}, },
time::{ time::{
self, self,
@ -130,6 +132,8 @@ impl fmt::Display for AuthCacheError
} }
} }
use session::*;
#[derive(Debug)] #[derive(Debug)]
pub struct State pub struct State
{ {
@ -137,6 +141,8 @@ pub struct State
auth_tokens: RwLock<AuthContainer>, auth_tokens: RwLock<AuthContainer>,
//TODO: user auths, public keys, hashed passwords, etc. //TODO: user auths, public keys, hashed passwords, etc.
logged_in: Arc<(RwLock<Box<Sessions>>, Notify)>,
settings: Settings, settings: Settings,
} }
@ -147,10 +153,25 @@ impl State
{ {
Self { Self {
auth_tokens: RwLock::new(AuthContainer::new()), auth_tokens: RwLock::new(AuthContainer::new()),
logged_in: Arc::new((RwLock::new(Box::new(Sessions::new(&settings))), Notify::new())),
backend: RwLock::new(backend), backend: RwLock::new(backend),
settings, settings,
} }.with_detatched_purger()
}
fn with_detatched_purger(self) -> Self
{
let logged_in = Arc::clone(&self.logged_in);
tokio::spawn(async move {
while Arc::strong_count(&logged_in) > 1 {
logged_in.1.notified().await;
// We're now allowed to perform cleanup.
{
let mut sessions = logged_in.0.write().await;
sessions.purge_now();
}
}
});
self
} }
/// The web server settings /// The web server settings

Loading…
Cancel
Save