conduit_cookie/
lib.rs

1#![cfg_attr(test, deny(warnings))]
2#![warn(rust_2018_idioms)]
3
4use conduit::{header, RequestExt};
5use conduit_middleware::{AfterResult, BeforeResult};
6use cookie::{Cookie, CookieJar};
7
8pub use crate::session::{RequestSession, SessionMiddleware};
9
10mod session;
11
12#[derive(Default)]
13pub struct Middleware {}
14
15impl Middleware {
16    pub fn new() -> Self {
17        Default::default()
18    }
19}
20
21fn parse_pair(key_value: &str) -> Option<(String, String)> {
22    key_value.find('=').map(|i| {
23        (
24            key_value[..i].trim().into(),
25            key_value[(i + 1)..].trim().into(),
26        )
27    })
28}
29
30impl conduit_middleware::Middleware for Middleware {
31    fn before(&self, req: &mut dyn RequestExt) -> BeforeResult {
32        let jar = {
33            let headers = req.headers();
34            let mut jar = CookieJar::new();
35            for cookie in headers.get_all(header::COOKIE).iter() {
36                if let Ok(cookie) = cookie.to_str() {
37                    for cookie in cookie.split(';') {
38                        if let Some((key, value)) = parse_pair(cookie) {
39                            jar.add_original(Cookie::new(key, value));
40                        }
41                    }
42                }
43            }
44            jar
45        };
46        req.mut_extensions().insert(jar);
47        Ok(())
48    }
49
50    fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
51        use std::convert::TryInto;
52
53        let mut res = res?;
54
55        for delta in req.cookies().delta() {
56            if let Ok(value) = delta.to_string().try_into() {
57                res.headers_mut().append(header::SET_COOKIE, value);
58            }
59        }
60
61        Ok(res)
62    }
63}
64
65pub trait RequestCookies {
66    fn cookies(&self) -> &CookieJar;
67    fn cookies_mut(&mut self) -> &mut CookieJar;
68}
69
70impl<T: RequestExt + ?Sized> RequestCookies for T {
71    fn cookies(&self) -> &CookieJar {
72        self.extensions()
73            .get::<CookieJar>()
74            .expect("Missing cookie jar")
75    }
76
77    fn cookies_mut(&mut self) -> &mut CookieJar {
78        self.mut_extensions()
79            .get_mut::<CookieJar>()
80            .expect("Missing cookie jar")
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use conduit::{header, Body, Handler, HttpResult, Method, RequestExt, Response};
87    use conduit_middleware::MiddlewareBuilder;
88    use conduit_test::MockRequest;
89    use cookie::Cookie;
90
91    use super::{Middleware, RequestCookies};
92
93    #[test]
94    fn request_headers() {
95        let mut req = MockRequest::new(Method::POST, "/articles");
96        req.header(header::COOKIE, "foo=bar");
97
98        let mut app = MiddlewareBuilder::new(test);
99        app.add(Middleware::new());
100        assert!(app.call(&mut req).is_ok());
101
102        fn test(req: &mut dyn RequestExt) -> HttpResult {
103            assert!(req.cookies().get("foo").is_some());
104            Response::builder().body(Body::empty())
105        }
106    }
107
108    #[test]
109    fn set_cookie() {
110        let mut req = MockRequest::new(Method::POST, "/articles");
111        let mut app = MiddlewareBuilder::new(test);
112        app.add(Middleware::new());
113        let response = app.call(&mut req).ok().unwrap();
114        let v = &response
115            .headers()
116            .get_all(header::SET_COOKIE)
117            .iter()
118            .collect::<Vec<_>>();
119        assert_eq!(&v[..], ["foo=bar"]);
120
121        fn test(req: &mut dyn RequestExt) -> HttpResult {
122            let c = Cookie::new("foo".to_string(), "bar".to_string());
123            req.cookies_mut().add(c);
124            Response::builder().body(Body::empty())
125        }
126    }
127
128    #[test]
129    fn cookie_list() {
130        let mut req = MockRequest::new(Method::POST, "/articles");
131        let mut app = MiddlewareBuilder::new(test);
132        app.add(Middleware::new());
133        let response = app.call(&mut req).ok().unwrap();
134        let mut v = response
135            .headers()
136            .get_all(header::SET_COOKIE)
137            .iter()
138            .collect::<Vec<_>>();
139        v.sort();
140        assert_eq!(&v[..], ["baz=qux", "foo=bar"]);
141
142        fn test(req: &mut dyn RequestExt) -> HttpResult {
143            let c = Cookie::new("foo", "bar");
144            req.cookies_mut().add(c);
145            let c2 = Cookie::new("baz", "qux");
146            req.cookies_mut().add(c2);
147            Response::builder().body(Body::empty())
148        }
149    }
150}