finchers_session/
cookie.rs1extern crate cookie;
30
31use finchers::endpoint::{ApplyContext, ApplyResult, Endpoint};
32use finchers::error::Error;
33use finchers::input::Input;
34
35#[cfg(feature = "secure")]
36use self::cookie::Key;
37use self::cookie::{Cookie, SameSite};
38use futures::future;
39use std::borrow::Cow;
40use std::fmt;
41use std::sync::Arc;
42use time::Duration;
43
44use session::{RawSession, Session};
45use util::BuilderExt;
46
47pub fn plain() -> CookieBackend {
54 CookieBackend::plain()
55}
56
57#[cfg(feature = "secure")]
62pub fn signed(master: impl AsRef<[u8]>) -> CookieBackend {
63 CookieBackend::signed(Key::from_master(master.as_ref()))
64}
65
66#[cfg(feature = "secure")]
71pub fn private(master: impl AsRef<[u8]>) -> CookieBackend {
72 CookieBackend::private(Key::from_master(master.as_ref()))
73}
74
75enum Security {
76 Plain,
77 #[cfg(feature = "secure")]
78 Signed(Key),
79 #[cfg(feature = "secure")]
80 Private(Key),
81}
82
83impl fmt::Debug for Security {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Security::Plain => f.debug_tuple("Plain").finish(),
87 #[cfg(feature = "secure")]
88 Security::Signed(..) => f.debug_tuple("Signed").finish(),
89 #[cfg(feature = "secure")]
90 Security::Private(..) => f.debug_tuple("Private").finish(),
91 }
92 }
93}
94
95#[derive(Debug)]
96struct CookieConfig {
97 security: Security,
98 name: String,
99 path: Cow<'static, str>,
100 secure: bool,
101 http_only: bool,
102 domain: Option<Cow<'static, str>>,
103 same_site: Option<SameSite>,
104 max_age: Option<Duration>,
105}
106
107impl CookieConfig {
108 fn read_value(&self, input: &mut Input) -> Result<Option<String>, Error> {
109 let jar = input.cookies()?;
110 let cookie = match self.security {
111 Security::Plain => jar.get(&self.name).cloned(),
112 #[cfg(feature = "secure")]
113 Security::Signed(ref key) => jar.signed(key).get(&self.name),
114 #[cfg(feature = "secure")]
115 Security::Private(ref key) => jar.private(key).get(&self.name),
116 };
117
118 match cookie {
119 Some(cookie) => Ok(Some(cookie.value().to_string())),
120 None => Ok(None),
121 }
122 }
123
124 fn write_value(&self, input: &mut Input, value: String) -> Result<(), Error> {
125 let cookie = Cookie::build(self.name.clone(), value)
126 .path(self.path.clone())
127 .secure(self.secure)
128 .http_only(self.http_only)
129 .if_some(self.domain.clone(), |cookie, value| cookie.domain(value))
130 .if_some(self.same_site, |cookie, value| cookie.same_site(value))
131 .if_some(self.max_age, |cookie, value| cookie.max_age(value))
132 .finish();
133
134 let jar = input.cookies()?;
135 match self.security {
136 Security::Plain => jar.add(cookie),
137 #[cfg(feature = "secure")]
138 Security::Signed(ref key) => jar.signed(key).add(cookie),
139 #[cfg(feature = "secure")]
140 Security::Private(ref key) => jar.private(key).add(cookie),
141 }
142
143 Ok(())
144 }
145
146 fn remove_value(&self, input: &mut Input) -> Result<(), Error> {
147 let cookie = Cookie::named(self.name.clone());
148 let jar = input.cookies()?;
149 match self.security {
150 Security::Plain => jar.remove(cookie),
151 #[cfg(feature = "secure")]
152 Security::Signed(ref key) => jar.signed(key).remove(cookie),
153 #[cfg(feature = "secure")]
154 Security::Private(ref key) => jar.private(key).remove(cookie),
155 }
156 Ok(())
157 }
158}
159
160#[allow(missing_docs)]
161#[derive(Debug, Clone)]
162pub struct CookieBackend {
163 config: Arc<CookieConfig>,
164}
165
166impl CookieBackend {
167 fn new(security: Security) -> CookieBackend {
168 CookieBackend {
169 config: Arc::new(CookieConfig {
170 security,
171 name: "finchers-session".into(),
172 path: "/".into(),
173 domain: None,
174 same_site: None,
175 max_age: None,
176 secure: true,
177 http_only: true,
178 }),
179 }
180 }
181
182 pub fn plain() -> CookieBackend {
184 CookieBackend::new(Security::Plain)
185 }
186
187 #[cfg(feature = "secure")]
191 pub fn signed(key: Key) -> CookieBackend {
192 CookieBackend::new(Security::Signed(key))
193 }
194
195 #[cfg(feature = "secure")]
199 pub fn private(key: Key) -> CookieBackend {
200 CookieBackend::new(Security::Private(key))
201 }
202
203 fn config_mut(&mut self) -> &mut CookieConfig {
204 Arc::get_mut(&mut self.config).expect("The instance has already shared.")
205 }
206
207 pub fn path(mut self, value: impl Into<Cow<'static, str>>) -> CookieBackend {
211 self.config_mut().path = value.into();
212 self
213 }
214
215 pub fn secure(mut self, value: bool) -> CookieBackend {
219 self.config_mut().secure = value;
220 self
221 }
222
223 pub fn http_only(mut self, value: bool) -> CookieBackend {
227 self.config_mut().http_only = value;
228 self
229 }
230
231 pub fn domain(mut self, value: impl Into<Cow<'static, str>>) -> CookieBackend {
235 self.config_mut().domain = Some(value.into());
236 self
237 }
238
239 pub fn same_site(mut self, value: SameSite) -> CookieBackend {
243 self.config_mut().same_site = Some(value);
244 self
245 }
246
247 pub fn max_age(mut self, value: Duration) -> CookieBackend {
251 self.config_mut().max_age = Some(value);
252 self
253 }
254}
255
256impl<'a> Endpoint<'a> for CookieBackend {
257 type Output = (Session<CookieSession>,);
258 type Future = future::FutureResult<Self::Output, Error>;
259
260 fn apply(&self, cx: &mut ApplyContext<'_>) -> ApplyResult<Self::Future> {
261 Ok(future::result(self.config.read_value(cx.input()).map(
262 |value| {
263 (Session::new(CookieSession {
264 config: self.config.clone(),
265 value,
266 }),)
267 },
268 )))
269 }
270}
271
272#[allow(missing_docs)]
273#[derive(Debug)]
274pub struct CookieSession {
275 config: Arc<CookieConfig>,
276 value: Option<String>,
277}
278
279impl CookieSession {
280 fn write_impl(self, input: &mut Input) -> Result<(), Error> {
281 if let Some(value) = self.value {
282 self.config.write_value(input, value)
283 } else {
284 self.config.remove_value(input)
285 }
286 }
287}
288
289impl RawSession for CookieSession {
290 type WriteFuture = future::FutureResult<(), Error>;
291
292 fn get(&self) -> Option<&str> {
293 self.value.as_ref().map(|s| s.as_str())
294 }
295
296 fn set(&mut self, value: String) {
297 self.value = Some(value);
298 }
299
300 fn remove(&mut self) {
301 self.value = None;
302 }
303
304 fn write(self, input: &mut Input) -> Self::WriteFuture {
305 future::result(self.write_impl(input))
306 }
307}