use rand;
use rand::distributions::Alphanumeric;
use rand::Rng;
use std::borrow::Cow;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use input;
use Request;
use Response;
pub fn session<'r, F>(request: &'r Request, cookie_name: &str, timeout_s: u64, inner: F) -> Response
where
F: FnOnce(&Session<'r>) -> Response,
{
let mut cookie = input::cookies(request);
let cookie = cookie.find(|&(ref k, _)| k == &cookie_name);
let cookie = cookie.map(|(_, v)| v);
let session = if let Some(cookie) = cookie {
Session {
key_was_retrieved: AtomicBool::new(false),
key_was_given: true,
key: cookie.into(),
}
} else {
Session {
key_was_retrieved: AtomicBool::new(false),
key_was_given: false,
key: generate_session_id().into(),
}
};
let mut response = inner(&session);
if session.key_was_retrieved.load(Ordering::Relaxed) {
let header_value = format!(
"{}={}; Max-Age={}; Path=/; HttpOnly",
cookie_name, session.key, timeout_s
);
response
.headers
.push(("Set-Cookie".into(), header_value.into()));
}
response
}
pub struct Session<'r> {
key_was_retrieved: AtomicBool,
key_was_given: bool,
key: Cow<'r, str>,
}
impl<'r> Session<'r> {
#[inline]
pub fn client_has_sid(&self) -> bool {
self.key_was_given
}
#[inline]
pub fn id(&self) -> &str {
self.key_was_retrieved.store(true, Ordering::Relaxed);
&self.key
}
}
pub fn generate_session_id() -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.map(char::from)
.filter(|&c| {
('a'..='z').contains(&c) || ('A'..='Z').contains(&c) || ('0'..='9').contains(&c)
})
.take(64)
.collect::<String>()
}
#[test]
fn test_generate_session_id() {
assert!(generate_session_id().len() >= 32);
}