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}