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