aboutsummaryrefslogtreecommitdiff
path: root/src/request.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/request.rs')
-rw-r--r--src/request.rs82
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 (`:`).