|
|
@ -1,20 +1,20 @@ |
|
|
|
use std::borrow::Cow; |
|
|
|
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
|
|
|
use std::fmt::{self, Display, Formatter}; |
|
|
|
|
|
|
|
use std::marker::PhantomData; |
|
|
|
|
|
|
|
use std::ops::Add; |
|
|
|
|
|
|
|
use std::time::{Duration, Instant}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; |
|
|
|
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; |
|
|
|
use rand::{rngs::OsRng, Rng}; |
|
|
|
use rand::Rng; |
|
|
|
|
|
|
|
|
|
|
|
use rocket::{ |
|
|
|
use rocket::{ |
|
|
|
fairing::{self, Fairing, Info}, |
|
|
|
fairing::{self, Fairing, Info}, |
|
|
|
http::{Cookie, Status}, |
|
|
|
http::{Cookie, Status}, |
|
|
|
outcome::Outcome, |
|
|
|
|
|
|
|
request::FromRequest, |
|
|
|
request::FromRequest, |
|
|
|
Build, Request, Response, Rocket, State, |
|
|
|
Outcome, Request, Response, Rocket, State, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::borrow::Cow; |
|
|
|
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
|
|
|
use std::fmt::{self, Display, Formatter}; |
|
|
|
|
|
|
|
use std::marker::PhantomData; |
|
|
|
|
|
|
|
use std::ops::Add; |
|
|
|
|
|
|
|
use std::time::{Duration, Instant}; |
|
|
|
|
|
|
|
|
|
|
|
/// Session store (shared state)
|
|
|
|
/// Session store (shared state)
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Debug)] |
|
|
|
pub struct SessionStore<D> |
|
|
|
pub struct SessionStore<D> |
|
|
@ -114,29 +114,29 @@ where |
|
|
|
D: 'static + Sync + Send + Default, |
|
|
|
D: 'static + Sync + Send + Default, |
|
|
|
{ |
|
|
|
{ |
|
|
|
/// The shared state reference
|
|
|
|
/// The shared state reference
|
|
|
|
store: &'a State<SessionStore<D>>, |
|
|
|
store: State<'a, SessionStore<D>>, |
|
|
|
/// Session ID
|
|
|
|
/// Session ID
|
|
|
|
id: &'a SessionID, |
|
|
|
id: &'a SessionID, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[rocket::async_trait] |
|
|
|
impl<'a, 'r, D> FromRequest<'a, 'r> for Session<'a, D> |
|
|
|
impl<'r, D> FromRequest<'r> for Session<'r, D> |
|
|
|
|
|
|
|
where |
|
|
|
where |
|
|
|
D: 'static + Sync + Send + Default, |
|
|
|
D: 'static + Sync + Send + Default, |
|
|
|
{ |
|
|
|
{ |
|
|
|
type Error = (); |
|
|
|
type Error = (); |
|
|
|
|
|
|
|
|
|
|
|
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, (Status, Self::Error), ()> { |
|
|
|
fn from_request(request: &'a Request<'r>) -> Outcome<Self, (Status, Self::Error), ()> { |
|
|
|
let store = request.guard::<&State<SessionStore<D>>>().await.unwrap(); |
|
|
|
let store: State<SessionStore<D>> = request.guard().unwrap(); |
|
|
|
Outcome::Success(Session { |
|
|
|
Outcome::Success(Session { |
|
|
|
id: request.local_cache(|| { |
|
|
|
id: request.local_cache(|| { |
|
|
|
let store_ug = store.inner.upgradable_read(); |
|
|
|
let store_ug = store.inner.upgradable_read(); |
|
|
|
|
|
|
|
|
|
|
|
// Resolve session ID
|
|
|
|
// Resolve session ID
|
|
|
|
let id = request |
|
|
|
let id = if let Some(cookie) = request.cookies().get(&store.config.cookie_name) { |
|
|
|
.cookies() |
|
|
|
Some(SessionID(cookie.value().to_string())) |
|
|
|
.get(&store.config.cookie_name) |
|
|
|
} else { |
|
|
|
.map(|cookie| SessionID(cookie.value().to_string())); |
|
|
|
None |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let expires = Instant::now().add(store.config.lifespan); |
|
|
|
let expires = Instant::now().add(store.config.lifespan); |
|
|
|
|
|
|
|
|
|
|
@ -176,10 +176,9 @@ where |
|
|
|
|
|
|
|
|
|
|
|
// Find a new unique ID - we are still safely inside the write guard
|
|
|
|
// Find a new unique ID - we are still safely inside the write guard
|
|
|
|
let new_id = SessionID(loop { |
|
|
|
let new_id = SessionID(loop { |
|
|
|
let token: String = OsRng |
|
|
|
let token: String = rand::thread_rng() |
|
|
|
.sample_iter(&rand::distributions::Alphanumeric) |
|
|
|
.sample_iter(&rand::distributions::Alphanumeric) |
|
|
|
.take(store.config.cookie_len) |
|
|
|
.take(store.config.cookie_len) |
|
|
|
.map(char::from) |
|
|
|
|
|
|
|
.collect(); |
|
|
|
.collect(); |
|
|
|
|
|
|
|
|
|
|
|
if !store_wg.sessions.contains_key(&token) { |
|
|
|
if !store_wg.sessions.contains_key(&token) { |
|
|
@ -295,7 +294,6 @@ where |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[rocket::async_trait] |
|
|
|
|
|
|
|
impl<D> Fairing for SessionFairing<D> |
|
|
|
impl<D> Fairing for SessionFairing<D> |
|
|
|
where |
|
|
|
where |
|
|
|
D: 'static + Sync + Send + Default, |
|
|
|
D: 'static + Sync + Send + Default, |
|
|
@ -303,11 +301,11 @@ where |
|
|
|
fn info(&self) -> Info { |
|
|
|
fn info(&self) -> Info { |
|
|
|
Info { |
|
|
|
Info { |
|
|
|
name: "Session", |
|
|
|
name: "Session", |
|
|
|
kind: fairing::Kind::Ignite | fairing::Kind::Response, |
|
|
|
kind: fairing::Kind::Attach | fairing::Kind::Response, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async fn on_ignite(&self, rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>> { |
|
|
|
fn on_attach(&self, rocket: Rocket) -> Result<Rocket, Rocket> { |
|
|
|
// install the store singleton
|
|
|
|
// install the store singleton
|
|
|
|
Ok(rocket.manage(SessionStore::<D> { |
|
|
|
Ok(rocket.manage(SessionStore::<D> { |
|
|
|
inner: Default::default(), |
|
|
|
inner: Default::default(), |
|
|
@ -315,7 +313,7 @@ where |
|
|
|
})) |
|
|
|
})) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response) { |
|
|
|
fn on_response<'r>(&self, request: &'r Request, response: &mut Response) { |
|
|
|
// send the session cookie, if session started
|
|
|
|
// send the session cookie, if session started
|
|
|
|
let session = request.local_cache(|| SessionID("".to_string())); |
|
|
|
let session = request.local_cache(|| SessionID("".to_string())); |
|
|
|
|
|
|
|
|
|
|
|