use cookie::{Cookie, SplitCookies};
use http::header::COOKIE;
use std::marker::PhantomData;
use crate::middleware::{BoxFuture, Middleware, Next};
use crate::{Error, Request, Response};
pub trait ParseCookies {
type Iter: Iterator<Item = Result<Cookie<'static>, Self::Error>> + Send + 'static;
type Error: Into<Error> + Send;
fn parse_cookies(input: String) -> Self::Iter;
}
pub struct ParseEncoded;
pub struct ParseUnencoded;
pub struct CookieParser<T = ParseEncoded> {
_parse: PhantomData<T>,
}
impl CookieParser {
pub fn new() -> Self {
Self {
_parse: PhantomData,
}
}
}
impl Default for CookieParser {
fn default() -> Self {
Self::new()
}
}
impl CookieParser<ParseUnencoded> {
pub fn unencoded() -> Self {
Self {
_parse: PhantomData,
}
}
}
impl<State, T> Middleware<State> for CookieParser<T>
where
State: Send + Sync + 'static,
T: ParseCookies + Send + Sync,
{
fn call(
&self,
mut request: Request<State>,
next: Next<State>,
) -> BoxFuture<Result<Response, Error>> {
let cookie_header = request.headers().get(COOKIE);
let (parser_output, request_cookies) = match cookie_header.map(|value| value.to_str()) {
None => {
return next.call(request);
}
Some(Err(error)) => {
let _ = error; return next.call(request);
}
Some(Ok(cookie_str)) => {
let input = cookie_str.to_string();
let mut output = T::parse_cookies(input).peekable();
let cookies = if output.peek().is_some() {
request.cookies_mut()
} else {
return next.call(request);
};
(output, cookies)
}
};
parser_output.for_each(|result| match result {
Ok(cookie) => request_cookies.add_original(cookie),
Err(error) => {
let _ = error; }
});
let mut merged_cookies = Box::new(request_cookies.clone());
Box::pin(async {
let mut response = next.call(request).await?;
if let Some(cookies) = response.cookies().map(|jar| jar.iter()) {
cookies.cloned().for_each(|cookie| {
merged_cookies.add(cookie);
});
response.set_cookies(merged_cookies);
}
Ok(response)
})
}
}
impl ParseCookies for ParseEncoded {
type Iter = SplitCookies<'static>;
type Error = cookie::ParseError;
fn parse_cookies(input: String) -> Self::Iter {
Cookie::split_parse_encoded(input)
}
}
impl ParseCookies for ParseUnencoded {
type Iter = SplitCookies<'static>;
type Error = cookie::ParseError;
fn parse_cookies(input: String) -> Self::Iter {
Cookie::split_parse(input)
}
}