Add sync constructors to CookieJar, SignedCookieJar, and PrivateCookieJar (#1264)

* Add sync constructors to `CookieJar`, `SignedCookieJar`, and `PrivateCookieJar`

* Move to constructor

* use `RequestParts::extract`
This commit is contained in:
David Pedersen
2022-08-16 15:42:01 +02:00
committed by GitHub
parent fb32fcc07c
commit 01630cfef6
4 changed files with 105 additions and 18 deletions
+2
View File
@@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning].
- **added:** Support chaining handlers with `HandlerCallWithExtractors::or` ([#1170]) - **added:** Support chaining handlers with `HandlerCallWithExtractors::or` ([#1170])
- **change:** axum-extra's MSRV is now 1.60 ([#1239]) - **change:** axum-extra's MSRV is now 1.60 ([#1239])
- **added:** Add Protocol Buffer extractor and response ([#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 [#1086]: https://github.com/tokio-rs/axum/pull/1086
[#1119]: https://github.com/tokio-rs/axum/pull/1119 [#1119]: https://github.com/tokio-rs/axum/pull/1119
+29 -10
View File
@@ -82,7 +82,7 @@ pub use cookie::Key;
/// .route("/me", get(me)); /// .route("/me", get(me));
/// # let app: Router<axum::body::Body> = app; /// # let app: Router<axum::body::Body> = app;
/// ``` /// ```
#[derive(Debug)] #[derive(Debug, Default)]
pub struct CookieJar { pub struct CookieJar {
jar: cookie::CookieJar, jar: cookie::CookieJar,
} }
@@ -95,18 +95,12 @@ where
type Rejection = Infallible; type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> { async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let mut jar = cookie::CookieJar::new(); Ok(Self::from_headers(req.headers()))
for cookie in cookies_from_request(req) {
jar.add_original(cookie);
}
Ok(Self { jar })
} }
} }
fn cookies_from_request<B>( fn cookies_from_request(headers: &HeaderMap) -> impl Iterator<Item = Cookie<'static>> + '_ {
req: &mut RequestParts<B>, headers
) -> impl Iterator<Item = Cookie<'static>> + '_ {
req.headers()
.get_all(COOKIE) .get_all(COOKIE)
.into_iter() .into_iter()
.filter_map(|value| value.to_str().ok()) .filter_map(|value| value.to_str().ok())
@@ -115,6 +109,31 @@ fn cookies_from_request<B>(
} }
impl CookieJar { 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. /// Get a cookie from the jar.
/// ///
/// # Example /// # Example
+37 -4
View File
@@ -6,6 +6,7 @@ use axum::{
Extension, Extension,
}; };
use cookie::PrivateJar; use cookie::PrivateJar;
use http::HeaderMap;
use std::{convert::Infallible, fmt, marker::PhantomData}; use std::{convert::Infallible, fmt, marker::PhantomData};
/// Extractor that grabs private cookies from the request and manages the jar. /// Extractor that grabs private cookies from the request and manages the jar.
@@ -82,21 +83,53 @@ where
type Rejection = <axum::Extension<K> as FromRequest<B>>::Rejection; type Rejection = <axum::Extension<K> as FromRequest<B>>::Rejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> { async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let key = Extension::<K>::from_request(req).await?.0.into(); let key = req.extract::<Extension<K>>().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 jar = cookie::CookieJar::new();
let mut private_jar = jar.private_mut(&key); 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) { if let Some(cookie) = private_jar.decrypt(cookie) {
private_jar.add_original(cookie); private_jar.add_original(cookie);
} }
} }
Ok(Self { Self {
jar, jar,
key, key,
_marker: PhantomData, _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,
}
} }
} }
+37 -4
View File
@@ -7,6 +7,7 @@ use axum::{
}; };
use cookie::SignedJar; use cookie::SignedJar;
use cookie::{Cookie, Key}; use cookie::{Cookie, Key};
use http::HeaderMap;
use std::{convert::Infallible, fmt, marker::PhantomData}; use std::{convert::Infallible, fmt, marker::PhantomData};
/// Extractor that grabs signed cookies from the request and manages the jar. /// Extractor that grabs signed cookies from the request and manages the jar.
@@ -100,21 +101,53 @@ where
type Rejection = <axum::Extension<K> as FromRequest<B>>::Rejection; type Rejection = <axum::Extension<K> as FromRequest<B>>::Rejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> { async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let key = Extension::<K>::from_request(req).await?.0.into(); let key = req.extract::<Extension<K>>().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 jar = cookie::CookieJar::new();
let mut signed_jar = jar.signed_mut(&key); 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) { if let Some(cookie) = signed_jar.verify(cookie) {
signed_jar.add_original(cookie); signed_jar.add_original(cookie);
} }
} }
Ok(Self { Self {
jar, jar,
key, key,
_marker: PhantomData, _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,
}
} }
} }