1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
extern crate cookie as _cookie;
#[macro_use]
extern crate error_chain;
#[cfg(feature = "redis-backend")]
#[macro_use]
extern crate iron;
#[cfg(not(feature = "redis-backend"))]
extern crate iron;
#[cfg(feature = "redis-backend")]
extern crate r2d2;
#[cfg(feature = "redis-backend")]
extern crate r2d2_redis;
extern crate rand;
#[cfg(feature = "redis-backend")]
extern crate redis;
use iron::middleware::{AroundMiddleware, Handler};
use iron::prelude::*;
use iron::typemap;
pub mod backends;
pub mod errors;
pub mod cookie {
pub use _cookie::*;
}
pub trait RawSession {
fn get_raw(&self, key: &str) -> IronResult<Option<String>>;
fn set_raw(&mut self, key: &str, value: String) -> IronResult<()>;
fn clear(&mut self) -> IronResult<()>;
fn write(&self, response: &mut Response) -> IronResult<()>;
}
pub trait SessionBackend: Send + Sync + 'static {
type S: RawSession;
fn from_request(&self, request: &mut Request) -> Self::S;
}
pub struct SessionStorage<B: SessionBackend> {
backend: B,
}
impl<B: SessionBackend> SessionStorage<B> {
pub fn new(backend: B) -> Self {
SessionStorage { backend: backend }
}
}
pub struct Session {
inner: Box<RawSession>,
has_changed: bool,
}
impl Session {
fn new(s: Box<RawSession>) -> Self {
Session {
inner: s,
has_changed: false,
}
}
}
pub trait Value: Sized + 'static {
fn get_key() -> &'static str;
fn into_raw(self) -> String;
fn from_raw(value: String) -> Option<Self>;
}
impl Session {
pub fn get<T: Value + Sized + 'static>(&self) -> IronResult<Option<T>> {
Ok(try!(self.inner.get_raw(T::get_key())).and_then(T::from_raw))
}
pub fn set<T: Value>(&mut self, t: T) -> IronResult<()> {
self.has_changed = true;
self.inner.set_raw(T::get_key(), t.into_raw())
}
pub fn clear(&mut self) -> IronResult<()> {
self.has_changed = true;
self.inner.clear()
}
}
struct SessionKey;
impl typemap::Key for SessionKey {
type Value = Session;
}
impl<B: SessionBackend> AroundMiddleware for SessionStorage<B> {
fn around(self, handler: Box<Handler>) -> Box<Handler> {
Box::new(move |req: &mut Request| -> IronResult<Response> {
let s = self.backend.from_request(req);
req.extensions
.insert::<SessionKey>(Session::new(Box::new(s)));
let mut res = handler.handle(req);
let s = req.extensions.remove::<SessionKey>().unwrap();
if s.has_changed {
match res {
Ok(ref mut r) => try!(s.inner.write(r)),
Err(ref mut e) => try!(s.inner.write(&mut e.response)),
}
};
res
})
}
}
pub trait SessionRequestExt {
fn session(&mut self) -> &mut Session;
}
impl<'a, 'b> SessionRequestExt for Request<'a, 'b> {
fn session(&mut self) -> &mut Session {
self.extensions.get_mut::<SessionKey>().unwrap()
}
}
fn get_default_cookie(key: String, value: String) -> cookie::Cookie {
let mut rv = cookie::Cookie::new(key, value);
rv.httponly = true;
rv.path = Some("/".to_owned());
rv
}
pub mod traits {
pub use super::SessionRequestExt;
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {}
}