diff options
Diffstat (limited to 'src/request.rs')
-rw-r--r-- | src/request.rs | 82 |
1 files changed, 43 insertions, 39 deletions
diff --git a/src/request.rs b/src/request.rs index 6efacc9..4f45e04 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,12 +1,11 @@ //! Provides the [`SputnikParts`] trait. -use cookie::Cookie; use mime::Mime; use serde::de::DeserializeOwned; -use time::Duration; -use std::{collections::HashMap, sync::Arc}; +use std::str::Split; +use std::time::Duration; -use crate::response::{SputnikHeaders, delete_cookie}; +use crate::response::{Cookie, SputnikHeaders, delete_cookie}; use crate::http::{HeaderMap, header, request::Parts}; /// Adds convenience methods to [`http::request::Parts`](Parts). @@ -15,7 +14,7 @@ pub trait SputnikParts { fn query<X: DeserializeOwned>(&self) -> Result<X,QueryError>; /// Parses the cookies of the request. - fn cookies(&mut self) -> Arc<HashMap<String, Cookie<'static>>>; + fn cookies(&self) -> CookieIter; /// Enforces a specific Content-Type. fn enforce_content_type(&self, mime: Mime) -> Result<(), WrongContentTypeError>; @@ -29,6 +28,29 @@ pub trait SputnikParts { fn response_headers(&mut self) -> &mut HeaderMap; } +pub struct CookieIter<'a>(Split<'a, char>); + +impl<'a> Iterator for CookieIter<'a> { + type Item = (&'a str, &'a str); + + fn next(&mut self) -> Option<Self::Item> { + self.0.next().and_then(|str| { + let mut iter = str.splitn(2, '='); + let name = iter.next().expect("first splitn().next() returns Some"); + let value = iter.next(); + match value { + None => self.next(), + Some(mut value) => { + if value.starts_with('"') && value.ends_with('"') && value.len() >= 2 { + value = &value[1..value.len()-1]; + } + Some((name, value)) + } + } + }) + } +} + impl SputnikParts for Parts { fn query<T: DeserializeOwned>(&self) -> Result<T,QueryError> { serde_urlencoded::from_str::<T>(self.uri.query().unwrap_or("")).map_err(QueryError) @@ -41,28 +63,8 @@ impl SputnikParts for Parts { self.extensions.get_mut::<HeaderMap>().unwrap() } - fn cookies(&mut self) -> Arc<HashMap<String, Cookie<'static>>> { - let cookies: Option<&Arc<HashMap<String, Cookie>>> = self.extensions.get(); - if let Some(cookies) = cookies { - return cookies.clone(); - } - - let mut cookies = HashMap::new(); - for header in self.headers.get_all(header::COOKIE) { - let raw_str = match std::str::from_utf8(header.as_bytes()) { - Ok(string) => string, - Err(_) => continue - }; - - for cookie_str in raw_str.split(';').map(|s| s.trim()) { - if let Ok(cookie) = Cookie::parse_encoded(cookie_str) { - cookies.insert(cookie.name().to_string(), cookie.into_owned()); - } - } - } - let cookies = Arc::new(cookies); - self.extensions.insert(cookies.clone()); - cookies + fn cookies(&self) -> CookieIter { + CookieIter(self.headers.get(header::COOKIE).and_then(|h| std::str::from_utf8(h.as_bytes()).ok()).unwrap_or("").split(';')) } fn enforce_content_type(&self, mime: Mime) -> Result<(), WrongContentTypeError> { @@ -83,10 +85,14 @@ pub struct Flash { message: String, } -impl From<Flash> for Cookie<'_> { +impl From<Flash> for Cookie { fn from(flash: Flash) -> Self { - Cookie::build(FLASH_COOKIE_NAME, format!("{}:{}", flash.name, flash.message)) - .max_age(Duration::minutes(5)).finish() + Cookie { + name: FLASH_COOKIE_NAME.into(), + value: format!("{}:{}", flash.name, flash.message), + max_age: Some(Duration::from_secs(5 * 60)), + ..Default::default() + } } } @@ -94,15 +100,13 @@ impl Flash { /// If the request has a flash cookie retrieve it and append a set-cookie /// header to delete the cookie to [`SputnikParts::response_headers`]. pub fn from_request(req: &mut Parts) -> Option<Self> { - req.cookies().get(FLASH_COOKIE_NAME) - .and_then(|cookie| { - req.response_headers().set_cookie(delete_cookie(FLASH_COOKIE_NAME)); - let mut iter = cookie.value().splitn(2, ':'); - if let (Some(name), Some(message)) = (iter.next(), iter.next()) { - return Some(Flash{name: name.to_owned(), message: message.to_owned()}) - } - None - }) + let value = req.cookies().find(|(name, _value)| *name == FLASH_COOKIE_NAME)?.1.to_owned(); + req.response_headers().set_cookie(delete_cookie(FLASH_COOKIE_NAME)); + let mut iter = value.splitn(2, ':'); + if let (Some(name), Some(message)) = (iter.next(), iter.next()) { + return Some(Flash{name: name.to_owned(), message: message.to_owned()}) + } + None } /// Constructs a new Flash message. The name must not contain a colon (`:`). |