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
use std::collections::HashMap; use thruster_core::context::Context; #[cfg(not(feature = "thruster_async_await"))] use thruster_core::middleware::{MiddlewareReturnValue}; #[cfg(feature = "thruster_async_await")] use thruster_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 } }