finchers_session/
in_memory.rs

1//! The session backend using in-memory database.
2//!
3//! # Example
4//!
5//! ```
6//! #[macro_use]
7//! extern crate finchers;
8//! extern crate finchers_session;
9//!
10//! use finchers::prelude::*;
11//! use finchers_session::Session;
12//! use finchers_session::in_memory::{
13//!     InMemoryBackend,
14//!     InMemorySession,
15//! };
16//!
17//! # fn main() {
18//! let backend = InMemoryBackend::default();
19//!
20//! let endpoint = path!(@get /)
21//!     .and(backend)
22//!     .and_then(|session: Session<InMemorySession>| {
23//!         session.with(|_session| {
24//!             // ...
25//! #           Ok("done")
26//!         })
27//!     });
28//! # drop(move || finchers::server::start(endpoint).serve("127.0.0.1:4000"));
29//! # }
30//! ```
31
32extern crate cookie;
33
34use std::collections::HashMap;
35use std::sync::{Arc, RwLock};
36
37use finchers;
38use finchers::endpoint::{ApplyContext, ApplyResult, Endpoint};
39use finchers::error::Error;
40use finchers::input::Input;
41
42use self::cookie::Cookie;
43use futures::future;
44use uuid::Uuid;
45
46use session::{RawSession, Session};
47
48#[derive(Debug, Default)]
49struct Storage {
50    inner: RwLock<HashMap<Uuid, String>>,
51}
52
53impl Storage {
54    fn get(&self, session_id: &Uuid) -> Result<Option<String>, Error> {
55        let inner = self.inner.read().map_err(|e| format_err!("{}", e))?;
56        Ok(inner.get(&session_id).cloned())
57    }
58
59    fn set(&self, session_id: Uuid, value: String) -> Result<(), Error> {
60        let mut inner = self.inner.write().map_err(|e| format_err!("{}", e))?;
61        inner.insert(session_id, value);
62        Ok(())
63    }
64
65    fn remove(&self, session_id: &Uuid) -> Result<(), Error> {
66        let mut inner = self.inner.write().map_err(|e| format_err!("{}", e))?;
67        inner.remove(&session_id);
68        Ok(())
69    }
70}
71
72#[allow(missing_docs)]
73#[derive(Debug, Clone, Default)]
74pub struct InMemoryBackend {
75    inner: Arc<Inner>,
76}
77
78#[derive(Debug, Default)]
79struct Inner {
80    storage: Storage,
81}
82
83impl InMemoryBackend {
84    fn read_value(&self, input: &mut Input) -> Result<(Option<String>, Option<Uuid>), Error> {
85        match input.cookies()?.get("session-id") {
86            Some(cookie) => {
87                let session_id: Uuid = cookie
88                    .value()
89                    .parse()
90                    .map_err(finchers::error::bad_request)?;
91                let value = self.inner.storage.get(&session_id)?;
92                Ok((value, Some(session_id)))
93            }
94            None => Ok((None, None)),
95        }
96    }
97
98    fn write_value(&self, input: &mut Input, session_id: Uuid, value: String) -> Result<(), Error> {
99        self.inner.storage.set(session_id.clone(), value)?;
100        input
101            .cookies()?
102            .add(Cookie::new("session-id", session_id.to_string()));
103        Ok(())
104    }
105
106    fn remove_value(&self, input: &mut Input, session_id: Uuid) -> Result<(), Error> {
107        self.inner.storage.remove(&session_id)?;
108        input.cookies()?.remove(Cookie::named("session-id"));
109        Ok(())
110    }
111}
112
113impl<'a> Endpoint<'a> for InMemoryBackend {
114    type Output = (Session<InMemorySession>,);
115    type Future = future::FutureResult<Self::Output, Error>;
116
117    fn apply(&self, cx: &mut ApplyContext<'_>) -> ApplyResult<Self::Future> {
118        Ok(future::result(self.read_value(cx.input()).map(
119            |(value, session_id)| {
120                (Session::new(InMemorySession {
121                    backend: self.clone(),
122                    value,
123                    session_id,
124                }),)
125            },
126        )))
127    }
128}
129
130#[allow(missing_docs)]
131#[derive(Debug)]
132pub struct InMemorySession {
133    backend: InMemoryBackend,
134    session_id: Option<Uuid>,
135    value: Option<String>,
136}
137
138impl InMemorySession {
139    fn write_impl(self, input: &mut Input) -> Result<(), Error> {
140        match self.value {
141            Some(value) => {
142                let session_id = self.session_id.unwrap_or_else(Uuid::new_v4);
143                self.backend.write_value(input, session_id, value)
144            }
145            None => match self.session_id {
146                Some(session_id) => self.backend.remove_value(input, session_id),
147                None => Ok(()),
148            },
149        }
150    }
151}
152
153impl RawSession for InMemorySession {
154    type WriteFuture = future::FutureResult<(), Error>;
155
156    fn get(&self) -> Option<&str> {
157        self.value.as_ref().map(|s| s.as_ref())
158    }
159
160    fn set(&mut self, value: String) {
161        self.value = Some(value);
162    }
163
164    fn remove(&mut self) {
165        self.value = None;
166    }
167
168    fn write(self, input: &mut Input) -> Self::WriteFuture {
169        future::result(self.write_impl(input))
170    }
171}