rust_xfinal/
cookie.rs

1use jwt::{SignWithKey, VerifyWithKey};
2
3use super::http_parser::Request;
4pub use expedite::datetime::period::Period;
5pub use expedite::datetime::time::Time;
6use hmac::Hmac;
7use sha2::Sha256;
8use std::collections::{BTreeMap, HashMap};
9use std::str::FromStr;
10use std::sync::Arc;
11
12use chrono::TimeZone;
13
14use chrono_tz::GMT;
15use chrono_tz::US::Pacific;
16
17pub struct Cookie {
18    name: String,
19    path: String,
20    domain: String,
21    max_age: Option<Time>,
22    http_only: bool,
23    data: BTreeMap<String, String>,
24    secret_key: Arc<Hmac<Sha256>>,
25}
26
27fn cookie_str_to_map(s: &str) -> HashMap<&str, &str> {
28    let mut map = HashMap::new();
29    for e in s.split(";") {
30        match e.split_once("=") {
31            Some((k, v)) => {
32                map.insert(k.trim(), v.trim());
33            }
34            None => {
35                continue;
36            }
37        }
38    }
39    map
40}
41
42impl Cookie {
43    /// > Create a cookie object from Request
44    /// >> - return a exist cookie if the client provides that
45    /// >> - Otherwise, return a new cookie
46    pub fn new(name: String, req: &Request) -> Self {
47        let key = &*req.get_secret_key();
48        let path = req.url_to_path();
49        match req.get_header("Cookie") {
50            Some(s) => match cookie_str_to_map(s).get(name.as_str()) {
51                Some(&token) => {
52                    match token.verify_with_key(key) {
53                        Ok(x) => {
54                            // let borrow:& mut BTreeMap<String, String> = & mut x;
55                            return Cookie {
56                                name,
57                                path: String::from(path),
58                                domain: String::new(),
59                                max_age: None,
60                                http_only: false,
61                                data: x,
62                                secret_key: req.get_secret_key(),
63                            };
64                        }
65                        Err(_) => {}
66                    }
67                }
68                None => {}
69            },
70            None => {}
71        }
72        Cookie {
73            name,
74            path: String::from(path),
75            domain: String::new(),
76            max_age: None,
77            http_only: false,
78            data: BTreeMap::new(),
79            secret_key: req.get_secret_key(),
80        }
81    }
82
83    /// > Set the cookie name
84    pub fn set_name(&mut self, name: String) {
85        self.name = name;
86    }
87    /// > Get the cookie name
88    pub fn get_name(&self) -> &String {
89        &self.name
90    }
91    /// > Set the cookie path
92    pub fn set_path(&mut self, path: String) {
93        self.path = path
94    }
95    /// > Get the cookie path
96    pub fn get_path(&self) -> &String {
97        &self.path
98    }
99    /// > Set the cookie domain
100    pub fn set_domain(&mut self, domain: String) {
101        self.domain = domain;
102    }
103    /// > Get the cookie domain
104    pub fn get_domain(&self) -> &String {
105        &self.domain
106    }
107
108    /// > Set the cookie max_age
109    pub fn set_max_age(&mut self, time: Time) {
110        self.max_age = Some(time);
111    }
112
113    /// > Get the cookie max_age
114    pub fn get_max_age(&self) -> &Option<Time> {
115        &self.max_age
116    }
117
118    /// > Set the cookie http-only
119    pub fn set_http_only(&mut self, v: bool) {
120        self.http_only = v;
121    }
122    /// > Get the cookie http-only
123    pub fn get_http_only(&self) -> bool {
124        self.http_only
125    }
126
127    /// > Get data from a valid cookie
128    pub fn get_data<T: FromStr>(&self, k: String) -> Option<T> {
129        match self.data.get(&k) {
130            Some(v) => match v.parse() {
131                Ok(v) => {
132                    return Some(v);
133                }
134                Err(_) => {
135                    return None;
136                }
137            },
138            None => {
139                return None;
140            }
141        }
142    }
143
144    /// > Insert data to cookie
145    pub fn insert<T: ToString>(&mut self, k: String, v: T) {
146        self.data.insert(k, v.to_string());
147    }
148
149    /// > Generate a token from the cookie
150    pub fn gen_token(&self) -> Option<String> {
151        match self.data.clone().sign_with_key(&*self.secret_key) {
152            Ok(s) => Some(s),
153            Err(_) => None,
154        }
155    }
156
157    pub(crate) fn to_string(&self) -> Option<String> {
158        let time = if let Some(ref t) = self.max_age {
159            #[allow(deprecated)]
160            let time = Pacific
161                .ymd(t.date.year as i32, t.date.month, t.date.day)
162                .and_hms(t.hours, t.minutes, t.seconds);
163            let gmt = time.with_timezone(&GMT);
164            gmt.to_rfc2822()
165        } else {
166            String::from("Session")
167        };
168        let domain = {
169            if self.domain == "" {
170                "".to_string()
171            } else {
172                format!("domain={};", self.domain)
173            }
174        };
175        let http_only = {
176            if self.http_only {
177                "HttpOnly;"
178            } else {
179                ""
180            }
181        };
182        if let Some(token) = self.gen_token() {
183            let r = format!(
184                "{name}={token}; path={path}; {domain} Expires={time}; {http_only}",
185                name = self.name,
186                path = self.path,
187                time = time.to_string()
188            );
189            Some(r)
190        } else {
191            None
192        }
193    }
194}