Skip to main content

rust_web_server/cookie/
mod.rs

1#[cfg(test)]
2mod tests;
3
4#[cfg(feature = "crypto")]
5mod crypto_ext;
6#[cfg(feature = "crypto")]
7pub use crypto_ext::{decrypt_cookie, encrypted_cookie, signed_cookie, verify_signed_cookie};
8
9/// A single HTTP cookie name/value pair.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Cookie {
12    pub name: String,
13    pub value: String,
14}
15
16/// Parses the `Cookie` request header into a collection of [`Cookie`] values.
17///
18/// # Example
19/// ```
20/// use rust_web_server::cookie::CookieJar;
21///
22/// let jar = CookieJar::parse("session=abc123; theme=dark");
23/// assert_eq!(jar.get("session").unwrap().value, "abc123");
24/// ```
25pub struct CookieJar {
26    pub cookies: Vec<Cookie>,
27}
28
29impl CookieJar {
30    /// Parses the raw value of the `Cookie` header (e.g. `"a=1; b=2"`).
31    pub fn parse(header_value: &str) -> CookieJar {
32        let cookies = header_value
33            .split(';')
34            .filter_map(|pair| {
35                let pair = pair.trim();
36                let mut parts = pair.splitn(2, '=');
37                let name = parts.next()?.trim().to_string();
38                let value = parts.next().unwrap_or("").trim().to_string();
39                if name.is_empty() { None } else { Some(Cookie { name, value }) }
40            })
41            .collect();
42        CookieJar { cookies }
43    }
44
45    /// Returns the first cookie with the given name, or `None`.
46    pub fn get(&self, name: &str) -> Option<&Cookie> {
47        self.cookies.iter().find(|c| c.name == name)
48    }
49}
50
51/// Builder for the `Set-Cookie` response header value.
52///
53/// # Example
54/// ```
55/// use rust_web_server::cookie::SetCookie;
56///
57/// let header_value = SetCookie::new("session", "abc123")
58///     .path("/")
59///     .http_only()
60///     .secure()
61///     .same_site("Lax")
62///     .build();
63///
64/// assert!(header_value.starts_with("session=abc123"));
65/// assert!(header_value.contains("HttpOnly"));
66/// ```
67pub struct SetCookie {
68    pub name: String,
69    pub value: String,
70    pub path: Option<String>,
71    pub domain: Option<String>,
72    pub max_age: Option<i64>,
73    pub secure: bool,
74    pub http_only: bool,
75    pub same_site: Option<String>,
76}
77
78impl SetCookie {
79    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
80        SetCookie {
81            name: name.into(),
82            value: value.into(),
83            path: None,
84            domain: None,
85            max_age: None,
86            secure: false,
87            http_only: false,
88            same_site: None,
89        }
90    }
91
92    pub fn path(mut self, path: impl Into<String>) -> Self {
93        self.path = Some(path.into());
94        self
95    }
96
97    pub fn domain(mut self, domain: impl Into<String>) -> Self {
98        self.domain = Some(domain.into());
99        self
100    }
101
102    pub fn max_age(mut self, seconds: i64) -> Self {
103        self.max_age = Some(seconds);
104        self
105    }
106
107    pub fn secure(mut self) -> Self {
108        self.secure = true;
109        self
110    }
111
112    pub fn http_only(mut self) -> Self {
113        self.http_only = true;
114        self
115    }
116
117    pub fn same_site(mut self, policy: impl Into<String>) -> Self {
118        self.same_site = Some(policy.into());
119        self
120    }
121
122    /// Builds the `Set-Cookie` header value string.
123    pub fn build(&self) -> String {
124        let mut s = format!("{}={}", self.name, self.value);
125        if let Some(ref p) = self.path { s.push_str(&format!("; Path={}", p)); }
126        if let Some(ref d) = self.domain { s.push_str(&format!("; Domain={}", d)); }
127        if let Some(age) = self.max_age { s.push_str(&format!("; Max-Age={}", age)); }
128        if self.secure { s.push_str("; Secure"); }
129        if self.http_only { s.push_str("; HttpOnly"); }
130        if let Some(ref ss) = self.same_site { s.push_str(&format!("; SameSite={}", ss)); }
131        s
132    }
133}