diff options
Diffstat (limited to 'src/response.rs')
-rw-r--r-- | src/response.rs | 82 |
1 files changed, 71 insertions, 11 deletions
diff --git a/src/response.rs b/src/response.rs index cb87a80..c9d83a7 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,9 +1,6 @@ //! Provides convenience traits and functions to build HTTP responses. -use std::convert::TryInto; - -use cookie::Cookie; -use time::{Duration, OffsetDateTime}; +use std::{convert::TryInto, fmt::Display, time::{Duration, SystemTime}}; use crate::http::{self, HeaderMap, StatusCode, header, response::Builder}; @@ -16,6 +13,67 @@ pub trait SputnikBuilder { fn set_cookie(self, cookie: Cookie) -> Builder; } +#[derive(Default, Debug)] +pub struct Cookie { + pub name: String, + pub value: String, + pub expires: Option<SystemTime>, + pub max_age: Option<Duration>, + pub domain: Option<String>, + pub path: Option<String>, + pub secure: Option<bool>, + pub http_only: Option<bool>, + pub same_site: Option<SameSite>, +} + +impl Display for Cookie { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}={}", self.name, self.value)?; + if let Some(true) = self.http_only { + write!(f, "; HttpOnly")?; + } + if let Some(same_site) = &self.same_site { + write!(f, "; SameSite={}", same_site)?; + + if same_site == &SameSite::None && self.secure.is_none() { + write!(f, "; Secure")?; + } + } + if let Some(true) = self.secure { + write!(f, "; Secure")?; + } + if let Some(path) = &self.path { + write!(f, "; Path={}", path)?; + } + if let Some(domain) = &self.domain { + write!(f, "; Domain={}", domain)?; + } + if let Some(max_age) = &self.max_age { + write!(f, "; Max-Age={}", max_age.as_secs())?; + } + if let Some(time) = self.expires { + write!(f, "; Expires={}", httpdate::fmt_http_date(time))?; + } + + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub enum SameSite { + Strict, Lax, None +} + +impl Display for SameSite { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SameSite::Strict => write!(f, "Strict"), + SameSite::Lax => write!(f, "Lax"), + SameSite::None => write!(f, "None"), + } + } +} + /// Creates a new builder with a given Location header and status code. pub fn redirect(location: &str, code: StatusCode) -> Builder { Builder::new().status(code).header(header::LOCATION, location) @@ -35,10 +93,12 @@ impl SputnikBuilder for Builder { /// Constructs an expired cookie to delete a cookie. pub fn delete_cookie(name: &str) -> Cookie { - let mut cookie = Cookie::new(name, ""); - cookie.set_max_age(Duration::seconds(0)); - cookie.set_expires(OffsetDateTime::now_utc() - Duration::days(365)); - cookie + Cookie{ + name: name.into(), + max_age: Some(Duration::from_secs(0)), + expires: Some(SystemTime::now() - Duration::from_secs(60*60*24)), + ..Default::default() + } } /// Adds convenience methods to [`HeaderMap`]. @@ -56,7 +116,7 @@ impl SputnikHeaders for HeaderMap { } fn set_cookie(&mut self, cookie: Cookie) { - self.append(header::SET_COOKIE, cookie.encoded().to_string().try_into().unwrap()); + self.append(header::SET_COOKIE, cookie.to_string().try_into().unwrap()); } } @@ -73,8 +133,8 @@ mod tests { #[test] fn test_set_cookie() { let mut map = HeaderMap::new(); - map.set_cookie(Cookie::new("some", "cookie")); - map.set_cookie(Cookie::new("some", "cookie")); + map.set_cookie(Cookie{name: "some".into(), value: "cookie".into(), ..Default::default()}); + map.set_cookie(Cookie{name: "some".into(), value: "cookie".into(), ..Default::default()}); assert_eq!(map.len(), 2); } |