use motore::{Service, layer::Layer};
use tokio::sync::RwLock;
use crate::{
context::ClientContext,
error::ClientError,
request::{Request, RequestPartsExt},
response::Response,
utils::cookie::CookieStore,
};
pub struct CookieService<S> {
inner: S,
cookie_store: RwLock<CookieStore>,
}
impl<S> CookieService<S> {
fn new(inner: S, cookie_store: RwLock<CookieStore>) -> Self {
Self {
inner,
cookie_store,
}
}
}
impl<S, ReqBody, RespBody> Service<ClientContext, Request<ReqBody>> for CookieService<S>
where
S: Service<ClientContext, Request<ReqBody>, Response = Response<RespBody>, Error = ClientError>
+ Send
+ Sync
+ 'static,
ReqBody: Send,
RespBody: Send,
{
type Response = S::Response;
type Error = S::Error;
async fn call(
&self,
cx: &mut ClientContext,
mut req: Request<ReqBody>,
) -> Result<Self::Response, Self::Error> {
let url = req.url();
if let Some(url) = &url {
let (mut parts, body) = req.into_parts();
if parts.headers.get(http::header::COOKIE).is_none() {
self.cookie_store
.read()
.await
.add_cookie_header(&mut parts.headers, url);
}
req = Request::from_parts(parts, body);
}
let resp = self.inner.call(cx, req).await?;
if let Some(url) = &url {
self.cookie_store
.write()
.await
.store_response_headers(resp.headers(), url);
}
Ok(resp)
}
}
pub struct CookieLayer {
cookie_store: RwLock<CookieStore>,
}
impl CookieLayer {
pub fn new(cookie_store: cookie_store::CookieStore) -> Self {
Self {
cookie_store: RwLock::new(CookieStore::new(cookie_store)),
}
}
}
impl<S> Layer<S> for CookieLayer {
type Service = CookieService<S>;
fn layer(self, inner: S) -> Self::Service {
CookieService::new(inner, self.cookie_store)
}
}