tsukuyomi_session/backend/
cookie.rs1use {
2 crate::{Backend, RawSession},
3 cookie::{Cookie, CookieBuilder},
4 serde_json,
5 std::{borrow::Cow, collections::HashMap, fmt, sync::Arc},
6 tsukuyomi::{
7 error::{Error, Result},
8 future::{Poll, TryFuture},
9 input::{Cookies, Input},
10 },
11};
12
13#[cfg(feature = "secure")]
14use cookie::Key;
15
16#[cfg(feature = "secure")]
17enum Security {
18 Plain,
19 Signed(Key),
20 Private(Key),
21}
22
23#[cfg(not(feature = "secure"))]
24enum Security {
25 Plain,
26}
27
28#[cfg_attr(tarpaulin, skip)]
29impl fmt::Debug for Security {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Security::Plain => f.debug_tuple("Plain").finish(),
33 #[cfg(feature = "secure")]
34 Security::Signed(..) => f.debug_tuple("Signed").field(&"<secret key>").finish(),
35 #[cfg(feature = "secure")]
36 Security::Private(..) => f.debug_tuple("Private").field(&"<secret key>").finish(),
37 }
38 }
39}
40
41impl Security {
42 fn get(&self, name: &str, cookies: &mut Cookies<'_>) -> Result<Option<Cookie<'static>>> {
43 match self {
44 Security::Plain => Ok(cookies.jar()?.get(name).cloned()),
45 #[cfg(feature = "secure")]
46 Security::Signed(ref key) => Ok(cookies.signed_jar(key)?.get(name)),
47 #[cfg(feature = "secure")]
48 Security::Private(ref key) => Ok(cookies.private_jar(key)?.get(name)),
49 }
50 }
51
52 fn add(&self, cookie: Cookie<'static>, cookies: &mut Cookies<'_>) -> Result<()> {
53 match self {
54 Security::Plain => cookies.jar()?.add(cookie),
55 #[cfg(feature = "secure")]
56 Security::Signed(ref key) => cookies.signed_jar(key)?.add(cookie),
57 #[cfg(feature = "secure")]
58 Security::Private(ref key) => cookies.private_jar(key)?.add(cookie),
59 }
60 Ok(())
61 }
62}
63
64#[derive(Debug, Clone)]
66pub struct CookieBackend {
67 inner: Arc<CookieBackendInner>,
68}
69
70impl CookieBackend {
71 fn new(security: Security) -> Self {
72 Self {
73 inner: Arc::new(CookieBackendInner {
74 security,
75 cookie_name: "tsukuyomi-session".into(),
76 builder: Box::new(|cookie| cookie),
77 }),
78 }
79 }
80
81 fn inner_mut(&mut self) -> &mut CookieBackendInner {
82 Arc::get_mut(&mut self.inner).expect("the instance has already shared")
83 }
84
85 pub fn plain() -> Self {
87 Self::new(Security::Plain)
88 }
89
90 #[cfg(feature = "secure")]
92 pub fn signed(secret_key: Key) -> Self {
93 Self::new(Security::Signed(secret_key))
94 }
95
96 #[cfg(feature = "secure")]
98 pub fn private(secret_key: Key) -> Self {
99 Self::new(Security::Private(secret_key))
100 }
101
102 pub fn cookie_name(mut self, value: impl Into<Cow<'static, str>>) -> Self {
106 self.inner_mut().cookie_name = value.into();
107 self
108 }
109
110 pub fn builder(
112 mut self,
113 builder: impl Fn(CookieBuilder) -> CookieBuilder + Send + Sync + 'static,
114 ) -> Self {
115 self.inner_mut().builder = Box::new(builder);
116 self
117 }
118}
119
120struct CookieBackendInner {
121 security: Security,
122 cookie_name: Cow<'static, str>,
123 builder: Box<dyn Fn(CookieBuilder) -> CookieBuilder + Send + Sync + 'static>,
124}
125
126#[cfg_attr(tarpaulin, skip)]
127impl fmt::Debug for CookieBackendInner {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.debug_struct("CookieBackendInner")
130 .field("security", &self.security)
131 .field("cookie_name", &self.cookie_name)
132 .finish()
133 }
134}
135
136impl CookieBackendInner {
137 fn deserialize(&self, s: &str) -> Result<HashMap<String, String>> {
138 serde_json::from_str(s).map_err(tsukuyomi::error::bad_request)
139 }
140
141 fn serialize(&self, map: &HashMap<String, String>) -> String {
142 serde_json::to_string(&map).expect("should be success")
143 }
144
145 fn read(&self, input: &mut Input<'_>) -> tsukuyomi::Result<Inner> {
146 match self.security.get(&*self.cookie_name, input.cookies)? {
147 Some(cookie) => {
148 let map = self.deserialize(cookie.value())?;
149 Ok(Inner::Some(map))
150 }
151 None => Ok(Inner::Empty),
152 }
153 }
154
155 fn write(&self, input: &mut Input<'_>, inner: Inner) -> tsukuyomi::Result<()> {
156 match inner {
157 Inner::Empty => {}
158 Inner::Some(map) => {
159 let value = self.serialize(&map);
160 let cookie =
161 (self.builder)(Cookie::build(self.cookie_name.clone(), value)).finish();
162 self.security.add(cookie, input.cookies)?;
163 }
164 Inner::Clear => {
165 input
166 .cookies
167 .jar()?
168 .remove(Cookie::named(self.cookie_name.clone()));
169 }
170 }
171
172 Ok(())
173 }
174}
175
176impl Backend for CookieBackend {
177 type Session = CookieSession;
178 type ReadError = Error;
179 type ReadSession = ReadSession;
180
181 fn read(&self) -> Self::ReadSession {
182 ReadSession(Some(self.clone()))
183 }
184}
185
186#[doc(hidden)]
187#[allow(missing_debug_implementations)]
188pub struct ReadSession(Option<CookieBackend>);
189
190impl TryFuture for ReadSession {
191 type Ok = CookieSession;
192 type Error = Error;
193
194 #[inline]
195 fn poll_ready(&mut self, input: &mut Input<'_>) -> Poll<Self::Ok, Self::Error> {
196 let backend = self.0.take().expect("the future has already been polled");
197 backend
198 .inner
199 .read(input)
200 .map(|inner| CookieSession { inner, backend }.into())
201 }
202}
203
204#[derive(Debug)]
205pub struct CookieSession {
206 inner: Inner,
207 backend: CookieBackend,
208}
209
210#[derive(Debug)]
211enum Inner {
212 Empty,
213 Some(HashMap<String, String>),
214 Clear,
215}
216
217impl RawSession for CookieSession {
218 type WriteSession = WriteSession;
219 type WriteError = Error;
220
221 fn get(&self, name: &str) -> Option<&str> {
222 match self.inner {
223 Inner::Some(ref map) => map.get(name).map(|s| &**s),
224 _ => None,
225 }
226 }
227
228 fn set(&mut self, name: &str, value: String) {
229 match self.inner {
230 Inner::Empty => {}
231 Inner::Some(ref mut map) => {
232 map.insert(name.to_owned(), value);
233 return;
234 }
235 Inner::Clear => return,
236 }
237
238 match std::mem::replace(&mut self.inner, Inner::Empty) {
239 Inner::Empty => {
240 self.inner = Inner::Some({
241 let mut map = HashMap::new();
242 map.insert(name.to_owned(), value);
243 map
244 });
245 }
246 Inner::Some(..) | Inner::Clear => unreachable!(),
247 }
248 }
249
250 fn remove(&mut self, name: &str) {
251 if let Inner::Some(ref mut map) = self.inner {
252 map.remove(name);
253 }
254 }
255
256 fn clear(&mut self) {
257 self.inner = Inner::Clear;
258 }
259
260 fn write(self) -> Self::WriteSession {
261 WriteSession(Some(self))
262 }
263}
264
265#[doc(hidden)]
266#[allow(missing_debug_implementations)]
267pub struct WriteSession(Option<CookieSession>);
268
269impl TryFuture for WriteSession {
270 type Ok = ();
271 type Error = Error;
272
273 #[inline]
274 fn poll_ready(&mut self, input: &mut Input<'_>) -> Poll<Self::Ok, Self::Error> {
275 let session = self.0.take().expect("the future has already been polled");
276 session
277 .backend
278 .inner
279 .write(input, session.inner)
280 .map(Into::into)
281 }
282}