thruster_middleware/
cookies.rs1use std::collections::HashMap;
2
3use thruster_core::context::Context;
4use thruster_core::middleware::{MiddlewareReturnValue};
5
6#[derive(Debug)]
7pub struct Cookie {
8 pub key: String,
9 pub value: String,
10 pub options: CookieOptions
11}
12
13#[derive(Debug, PartialEq)]
14pub enum SameSite {
15 Strict,
16 Lax
17}
18
19#[derive(Debug)]
20pub struct CookieOptions {
21 pub domain: String,
22 pub path: String,
23 pub expires: u64,
24 pub http_only: bool,
25 pub max_age: u64,
26 pub secure: bool,
27 pub signed: bool,
28 pub same_site: Option<SameSite>
29}
30
31impl CookieOptions {
32 pub fn default() -> CookieOptions {
33 CookieOptions {
34 domain: "".to_owned(),
35 path: "/".to_owned(),
36 expires: 0,
37 http_only: false,
38 max_age: 0,
39 secure: false,
40 signed: false,
41 same_site: None
42 }
43 }
44}
45
46pub trait HasCookies {
47 fn set_cookies(&mut self, cookies: Vec<Cookie>);
48 fn headers(&self) -> HashMap<String, String>;
49}
50
51pub fn cookies<T: 'static + Context + HasCookies + Send>(mut context: T, next: impl Fn(T) -> MiddlewareReturnValue<T> + Send) -> MiddlewareReturnValue<T> {
52 let mut cookies = Vec::new();
53
54 {
55 let headers: HashMap<String, String> = context.headers();
56
57 if let Some(val) = headers.get("set-cookie") {
58 for cookie_string in val.split(',') {
59 cookies.push(parse_string(cookie_string));
60 }
61 }
62 }
63
64 context.set_cookies(cookies);
65
66 next(context)
67}
68
69fn parse_string(string: &str) -> Cookie {
70 let mut options = CookieOptions::default();
71
72 let mut pieces = string.split(';');
73
74 let mut key_pair = pieces.next().unwrap().split('=');
75
76 let key = key_pair.next().unwrap_or("").to_owned();
77 let value = key_pair.next().unwrap_or("").to_owned();
78
79 for option in pieces {
80 let mut option_key_pair = option.split('=');
81
82 if let Some(option_key) = option_key_pair.next() {
83 match option_key.to_lowercase().trim() {
84 "expires" => options.expires = option_key_pair.next().unwrap_or("0").parse::<u64>().unwrap_or(0),
85 "max-age" => options.max_age = option_key_pair.next().unwrap_or("0").parse::<u64>().unwrap_or(0),
86 "domain" => options.domain = option_key_pair.next().unwrap_or("").to_owned(),
87 "path" => options.path = option_key_pair.next().unwrap_or("").to_owned(),
88 "secure" => options.secure = true,
89 "httponly" => options.http_only = true,
90 "samesite" => {
91 if let Some(same_site_value) = option_key_pair.next() {
92 match same_site_value.to_lowercase().as_ref() {
93 "strict" => options.same_site = Some(SameSite::Strict),
94 "lax" => options.same_site = Some(SameSite::Lax),
95 _ => ()
96 };
97 }
98 }
99 _ => ()
100 };
101 }
102 }
103
104 Cookie {
105 key,
106 value,
107 options
108 }
109}