viz_core/middleware/
cookie.rs1use std::fmt;
4
5use crate::{
6 header::{HeaderValue, COOKIE, SET_COOKIE},
7 types::{Cookie, CookieJar, Cookies},
8 Handler, IntoResponse, Request, Response, Result, Transform,
9};
10
11#[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
12use crate::types::CookieKey;
13
14pub struct Config {
16 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
17 key: std::sync::Arc<CookieKey>,
18}
19
20impl Config {
21 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
23 #[must_use]
24 pub fn with_key(key: CookieKey) -> Self {
25 Self {
26 key: std::sync::Arc::new(key),
27 }
28 }
29}
30
31impl Default for Config {
32 fn default() -> Self {
33 Self {
34 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
35 key: std::sync::Arc::new(CookieKey::generate()),
36 }
37 }
38}
39
40impl fmt::Debug for Config {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 let mut d = f.debug_struct("CookieConfig");
43
44 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
45 d.field("key", &[..]);
46
47 d.finish()
48 }
49}
50
51impl<H> Transform<H> for Config
52where
53 H: Clone,
54{
55 type Output = CookieMiddleware<H>;
56
57 fn transform(&self, h: H) -> Self::Output {
58 CookieMiddleware {
59 h,
60 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
61 key: self.key.clone(),
62 }
63 }
64}
65
66#[derive(Clone)]
68pub struct CookieMiddleware<H> {
69 h: H,
70 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
71 key: std::sync::Arc<CookieKey>,
72}
73
74impl<H> fmt::Debug for CookieMiddleware<H> {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let mut d = f.debug_struct("CookieMiddleware");
77
78 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
79 d.field("key", &[..]);
80
81 d.finish()
82 }
83}
84
85#[crate::async_trait]
86impl<H, O> Handler<Request> for CookieMiddleware<H>
87where
88 H: Handler<Request, Output = Result<O>>,
89 O: IntoResponse,
90{
91 type Output = Result<Response>;
92
93 async fn call(&self, mut req: Request) -> Self::Output {
94 let jar = req
95 .headers()
96 .get_all(COOKIE)
97 .iter()
98 .map(HeaderValue::to_str)
99 .filter_map(Result::ok)
100 .fold(CookieJar::new(), add_cookie);
101
102 let cookies = Cookies::new(jar);
103 #[cfg(any(feature = "cookie-signed", feature = "cookie-private"))]
104 let cookies = cookies.with_key(self.key.clone());
105
106 req.extensions_mut().insert::<Cookies>(cookies.clone());
107
108 self.h
109 .call(req)
110 .await
111 .map(IntoResponse::into_response)
112 .map(|mut res| {
113 if let Ok(c) = cookies.jar().lock() {
114 c.delta()
115 .map(Cookie::encoded)
116 .map(|cookie| HeaderValue::from_str(&cookie.to_string()))
117 .filter_map(Result::ok)
118 .fold(res.headers_mut(), |headers, cookie| {
119 headers.append(SET_COOKIE, cookie);
120 headers
121 });
122 }
123 res
124 })
125 }
126}
127
128#[inline]
129fn add_cookie(mut jar: CookieJar, value: &str) -> CookieJar {
130 Cookie::split_parse_encoded(value)
131 .filter_map(Result::ok)
132 .map(Cookie::into_owned)
133 .for_each(|cookie| jar.add_original(cookie));
134 jar
135}