From 7947d13a5a6c1e67b946ac24df7a2dba1fba512a Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 3 Mar 2023 12:12:08 +0000 Subject: [PATCH 1/5] Test for default typed header values Adds a failing test for missing Content-Encoding header that should result in an empty-list Content-Encoding instead of an error. --- axum/src/typed_header.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/axum/src/typed_header.rs b/axum/src/typed_header.rs index 612cd6a328..1962a08074 100644 --- a/axum/src/typed_header.rs +++ b/axum/src/typed_header.rs @@ -174,19 +174,37 @@ mod tests { async fn typed_header() { async fn handle( TypedHeader(user_agent): TypedHeader, + TypedHeader(content_encoding): TypedHeader, ) -> impl IntoResponse { - user_agent.to_string() + format!("{user_agent:?}, {content_encoding:?}") } let app = Router::new().route("/", get(handle)); let client = TestClient::new(app); + let res = client + .get("/") + .header("user-agent", "foobar") + .header("content-encoding", "gzip, br") + .header("content-encoding", "deflate") + .send() + .await; + let body = res.text().await; + assert_eq!( + body, + r#"UserAgent("foobar"), ContentEncoding("gzip, br, deflate")"# + ); + let res = client.get("/").header("user-agent", "foobar").send().await; let body = res.text().await; - assert_eq!(body, "foobar"); + assert_eq!(body, r#"UserAgent("foobar"), ContentEncoding("")"#); - let res = client.get("/").send().await; + let res = client + .get("/") + .header("content-encoding", "gzip") + .send() + .await; let body = res.text().await; assert_eq!(body, "Header of type `user-agent` was missing"); } From 48fe6f1c5fcf64b3269e54f2c87abb8e7ceb8c3a Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 3 Mar 2023 12:21:45 +0000 Subject: [PATCH 2/5] Gracefully handle missing headers Reports a missing header rejection only if the header's decoder didn't return a default value. Closes #1781. --- axum/src/typed_header.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/axum/src/typed_header.rs b/axum/src/typed_header.rs index 1962a08074..4b7f1590c0 100644 --- a/axum/src/typed_header.rs +++ b/axum/src/typed_header.rs @@ -61,17 +61,19 @@ where type Rejection = TypedHeaderRejection; async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - match parts.headers.typed_try_get::() { - Ok(Some(value)) => Ok(Self(value)), - Ok(None) => Err(TypedHeaderRejection { + let mut values = parts.headers.get_all(T::name()).iter(); + let is_missing = values.size_hint() == (0, Some(0)); + T::decode(&mut values) + .map(Self) + .map_err(|err| TypedHeaderRejection { name: T::name(), - reason: TypedHeaderRejectionReason::Missing, - }), - Err(err) => Err(TypedHeaderRejection { - name: T::name(), - reason: TypedHeaderRejectionReason::Error(err), - }), - } + reason: if is_missing { + // Report a more precise rejection for the missing header case. + TypedHeaderRejectionReason::Missing + } else { + TypedHeaderRejectionReason::Error(err) + }, + }) } } From 86efacd9720f53857e12cf1180425921e35a6199 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 3 Mar 2023 12:44:11 +0000 Subject: [PATCH 3/5] Change test header to Cookie This avoids relying on Debug representation of the opaque ContentEncoding type. --- axum/src/typed_header.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/axum/src/typed_header.rs b/axum/src/typed_header.rs index 4b7f1590c0..22d93377c7 100644 --- a/axum/src/typed_header.rs +++ b/axum/src/typed_header.rs @@ -176,9 +176,11 @@ mod tests { async fn typed_header() { async fn handle( TypedHeader(user_agent): TypedHeader, - TypedHeader(content_encoding): TypedHeader, + TypedHeader(cookies): TypedHeader, ) -> impl IntoResponse { - format!("{user_agent:?}, {content_encoding:?}") + let user_agent = user_agent.as_str(); + let cookies = cookies.iter().collect::>(); + format!("User-Agent={user_agent:?}, Cookie={cookies:?}") } let app = Router::new().route("/", get(handle)); @@ -188,25 +190,21 @@ mod tests { let res = client .get("/") .header("user-agent", "foobar") - .header("content-encoding", "gzip, br") - .header("content-encoding", "deflate") + .header("cookie", "a=1; b=2") + .header("cookie", "c=3") .send() .await; let body = res.text().await; assert_eq!( body, - r#"UserAgent("foobar"), ContentEncoding("gzip, br, deflate")"# + r#"User-Agent="foobar", Cookie=[("a", "1"), ("b", "2"), ("c", "3")]"# ); let res = client.get("/").header("user-agent", "foobar").send().await; let body = res.text().await; - assert_eq!(body, r#"UserAgent("foobar"), ContentEncoding("")"#); + assert_eq!(body, r#"User-Agent="foobar", Cookie=[]"#); - let res = client - .get("/") - .header("content-encoding", "gzip") - .send() - .await; + let res = client.get("/").header("cookie", "a=1").send().await; let body = res.text().await; assert_eq!(body, "Header of type `user-agent` was missing"); } From 8964d67d9283fb07e9d4404ee0a2ad25b5411834 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 3 Mar 2023 14:15:26 +0000 Subject: [PATCH 4/5] Update changelog --- axum/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 194c8b4d41..67308a9c70 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#1801]: https://github.com/tokio-rs/axum/pull/1801 +- **fixed:** Gracefully handle missing headers in the `TypedHeader` extractor ([#1810]) + +[#1810]: https://github.com/tokio-rs/axum/pull/1810 + # 0.6.9 (24. February, 2023) - **changed:** Update to tower-http 0.4. axum is still compatible with tower-http 0.3 ([#1783]) From 20ced4f98f22aa6c6988c16a447098b1230b7901 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Fri, 3 Mar 2023 15:26:28 +0100 Subject: [PATCH 5/5] fixup changelog --- axum/CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 67308a9c70..71179acf63 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -8,11 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased - **fixed:** Add `#[must_use]` to `WebSocketUpgrade::on_upgrade` ([#1801]) - -[#1801]: https://github.com/tokio-rs/axum/pull/1801 - - **fixed:** Gracefully handle missing headers in the `TypedHeader` extractor ([#1810]) +[#1801]: https://github.com/tokio-rs/axum/pull/1801 [#1810]: https://github.com/tokio-rs/axum/pull/1810 # 0.6.9 (24. February, 2023)