use std::collections::HashMap;
use std::sync::Mutex;
use rand;
use rand::Rng;
use Request;
use Response;
use input;
pub struct SessionsManager<T> where T: Clone {
sessions: Mutex<HashMap<String, T>>,
cookie_name: String,
timeout_s: u64,
}
impl<T> SessionsManager<T> where T: Clone {
pub fn new<S>(cookie_name: S, timeout_s: u64) -> SessionsManager<T> where S: Into<String> {
SessionsManager {
sessions: Mutex::new(HashMap::new()),
cookie_name: cookie_name.into(),
timeout_s: timeout_s,
}
}
pub fn start(&self, request: &Request) -> Session<T> {
let mut cookie = input::get_cookies(request).into_iter();
let cookie = cookie.find(|&(ref k, _)| k == &self.cookie_name);
let cookie = cookie.map(|(k, v)| v);
if let Some(cookie) = cookie {
Session {
manager: self,
key: cookie,
}
} else {
Session {
manager: self,
key: generate_session_id(),
}
}
}
}
pub struct Session<'a, T> where T: Clone + 'a {
manager: &'a SessionsManager<T>,
key: String,
}
impl<'a, T> Session<'a, T> where T: Clone {
pub fn get(&self) -> Option<T> {
let session = self.manager.sessions.lock().unwrap();
session.get(&self.key).map(|d| d.clone())
}
#[inline]
pub fn has_data(&self) -> bool {
let session = self.manager.sessions.lock().unwrap();
session.get(&self.key).is_some()
}
pub fn set(&self, value: T) {
let mut session = self.manager.sessions.lock().unwrap();
session.insert(self.key.clone(), value);
}
pub fn clear(&self) {
let mut session = self.manager.sessions.lock().unwrap();
session.remove(&self.key);
}
pub fn apply(&self, mut response: Response) -> Response {
if !self.has_data() {
return response;
}
let header_value = format!("{}={}; Max-Age={}", self.manager.cookie_name, self.key,
self.manager.timeout_s);
response.headers.push(("Set-Cookie".to_owned(), header_value));
response
}
}
pub fn generate_session_id() -> String {
rand::OsRng::new().unwrap() .gen_ascii_chars()
.filter(|&c| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9'))
.take(64).collect::<String>()
}