From 01630cfef61c1544fc03f0ae2d3573fc9f371e66 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 16 Aug 2022 15:42:01 +0200 Subject: [PATCH] Add sync constructors to `CookieJar`, `SignedCookieJar`, and `PrivateCookieJar` (#1264) * Add sync constructors to `CookieJar`, `SignedCookieJar`, and `PrivateCookieJar` * Move to constructor * use `RequestParts::extract` --- axum-extra/CHANGELOG.md | 2 ++ axum-extra/src/extract/cookie/mod.rs | 39 ++++++++++++++++------ axum-extra/src/extract/cookie/private.rs | 41 +++++++++++++++++++++--- axum-extra/src/extract/cookie/signed.rs | 41 +++++++++++++++++++++--- 4 files changed, 105 insertions(+), 18 deletions(-) diff --git a/axum-extra/CHANGELOG.md b/axum-extra/CHANGELOG.md index 34ac26ab..4722a6ac 100644 --- a/axum-extra/CHANGELOG.md +++ b/axum-extra/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning]. - **added:** Support chaining handlers with `HandlerCallWithExtractors::or` ([#1170]) - **change:** axum-extra's MSRV is now 1.60 ([#1239]) - **added:** Add Protocol Buffer extractor and response ([#1239]) +- **added:** Add sync constructors to `CookieJar`, `PrivateCookieJar`, and + `SignedCookieJar` so they're easier to use in custom middleware [#1086]: https://github.com/tokio-rs/axum/pull/1086 [#1119]: https://github.com/tokio-rs/axum/pull/1119 diff --git a/axum-extra/src/extract/cookie/mod.rs b/axum-extra/src/extract/cookie/mod.rs index 04eb5877..9190424e 100644 --- a/axum-extra/src/extract/cookie/mod.rs +++ b/axum-extra/src/extract/cookie/mod.rs @@ -82,7 +82,7 @@ pub use cookie::Key; /// .route("/me", get(me)); /// # let app: Router = app; /// ``` -#[derive(Debug)] +#[derive(Debug, Default)] pub struct CookieJar { jar: cookie::CookieJar, } @@ -95,18 +95,12 @@ where type Rejection = Infallible; async fn from_request(req: &mut RequestParts) -> Result { - let mut jar = cookie::CookieJar::new(); - for cookie in cookies_from_request(req) { - jar.add_original(cookie); - } - Ok(Self { jar }) + Ok(Self::from_headers(req.headers())) } } -fn cookies_from_request( - req: &mut RequestParts, -) -> impl Iterator> + '_ { - req.headers() +fn cookies_from_request(headers: &HeaderMap) -> impl Iterator> + '_ { + headers .get_all(COOKIE) .into_iter() .filter_map(|value| value.to_str().ok()) @@ -115,6 +109,31 @@ fn cookies_from_request( } impl CookieJar { + /// Create a new `CookieJar` from a map of request headers. + /// + /// The cookies in `headers` will be added to the jar. + /// + /// This is inteded to be used in middleware and other places where it might be difficult to + /// run extractors. Normally you should create `CookieJar`s through [`FromRequest`]. + pub fn from_headers(headers: &HeaderMap) -> Self { + let mut jar = cookie::CookieJar::new(); + for cookie in cookies_from_request(headers) { + jar.add_original(cookie); + } + Self { jar } + } + + /// Create a new empty `CookieJar`. + /// + /// This is inteded to be used in middleware and other places where it might be difficult to + /// run extractors. Normally you should create `CookieJar`s through [`FromRequest`]. + /// + /// If you need a jar that contains the headers from a request use `impl From<&HeaderMap> for + /// CookieJar`. + pub fn new() -> Self { + Self::default() + } + /// Get a cookie from the jar. /// /// # Example diff --git a/axum-extra/src/extract/cookie/private.rs b/axum-extra/src/extract/cookie/private.rs index 032d020a..b606a6bd 100644 --- a/axum-extra/src/extract/cookie/private.rs +++ b/axum-extra/src/extract/cookie/private.rs @@ -6,6 +6,7 @@ use axum::{ Extension, }; use cookie::PrivateJar; +use http::HeaderMap; use std::{convert::Infallible, fmt, marker::PhantomData}; /// Extractor that grabs private cookies from the request and manages the jar. @@ -82,21 +83,53 @@ where type Rejection = as FromRequest>::Rejection; async fn from_request(req: &mut RequestParts) -> Result { - let key = Extension::::from_request(req).await?.0.into(); + let key = req.extract::>().await?.0.into(); + let PrivateCookieJar { + jar, + key, + _marker: _, + } = PrivateCookieJar::from_headers(req.headers(), key); + Ok(PrivateCookieJar { + jar, + key, + _marker: PhantomData, + }) + } +} +impl PrivateCookieJar { + /// Create a new `PrivateCookieJar` from a map of request headers. + /// + /// The valid cookies in `headers` will be added to the jar. + /// + /// This is inteded to be used in middleware and other where places it might be difficult to + /// run extractors. Normally you should create `PrivateCookieJar`s through [`FromRequest`]. + pub fn from_headers(headers: &HeaderMap, key: Key) -> Self { let mut jar = cookie::CookieJar::new(); let mut private_jar = jar.private_mut(&key); - for cookie in cookies_from_request(req) { + for cookie in cookies_from_request(headers) { if let Some(cookie) = private_jar.decrypt(cookie) { private_jar.add_original(cookie); } } - Ok(Self { + Self { jar, key, _marker: PhantomData, - }) + } + } + + /// Create a new empty `PrivateCookieJarIter`. + /// + /// This is inteded to be used in middleware and other places where it might be difficult to + /// run extractors. Normally you should create `PrivateCookieJar`s through [`FromRequest`]. + pub fn new(key: Key) -> Self { + Self { + jar: Default::default(), + key, + _marker: PhantomData, + } } } diff --git a/axum-extra/src/extract/cookie/signed.rs b/axum-extra/src/extract/cookie/signed.rs index 05a3880e..e27ebc33 100644 --- a/axum-extra/src/extract/cookie/signed.rs +++ b/axum-extra/src/extract/cookie/signed.rs @@ -7,6 +7,7 @@ use axum::{ }; use cookie::SignedJar; use cookie::{Cookie, Key}; +use http::HeaderMap; use std::{convert::Infallible, fmt, marker::PhantomData}; /// Extractor that grabs signed cookies from the request and manages the jar. @@ -100,21 +101,53 @@ where type Rejection = as FromRequest>::Rejection; async fn from_request(req: &mut RequestParts) -> Result { - let key = Extension::::from_request(req).await?.0.into(); + let key = req.extract::>().await?.0.into(); + let SignedCookieJar { + jar, + key, + _marker: _, + } = SignedCookieJar::from_headers(req.headers(), key); + Ok(SignedCookieJar { + jar, + key, + _marker: PhantomData, + }) + } +} +impl SignedCookieJar { + /// Create a new `SignedCookieJar` from a map of request headers. + /// + /// The valid cookies in `headers` will be added to the jar. + /// + /// This is inteded to be used in middleware and other places where it might be difficult to + /// run extractors. Normally you should create `SignedCookieJar`s through [`FromRequest`]. + pub fn from_headers(headers: &HeaderMap, key: Key) -> Self { let mut jar = cookie::CookieJar::new(); let mut signed_jar = jar.signed_mut(&key); - for cookie in cookies_from_request(req) { + for cookie in cookies_from_request(headers) { if let Some(cookie) = signed_jar.verify(cookie) { signed_jar.add_original(cookie); } } - Ok(Self { + Self { jar, key, _marker: PhantomData, - }) + } + } + + /// Create a new empty `SignedCookieJar`. + /// + /// This is inteded to be used in middleware and other places where it might be difficult to + /// run extractors. Normally you should create `SignedCookieJar`s through [`FromRequest`]. + pub fn new(key: Key) -> Self { + Self { + jar: Default::default(), + key, + _marker: PhantomData, + } } }