From eaf394b11d057d67d4c7eb3d6bae49584a9abb57 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Thu, 28 Sep 2023 09:51:01 +0200 Subject: [PATCH 1/8] Ensure we include the port when parsing authority --- axum/src/extract/host.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/axum/src/extract/host.rs b/axum/src/extract/host.rs index d5be6a978d..cf13e2fecf 100644 --- a/axum/src/extract/host.rs +++ b/axum/src/extract/host.rs @@ -6,6 +6,7 @@ use async_trait::async_trait; use http::{ header::{HeaderMap, FORWARDED}, request::Parts, + uri::Authority, }; const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host"; @@ -51,8 +52,8 @@ where return Ok(Host(host.to_owned())); } - if let Some(host) = parts.uri.host() { - return Ok(Host(host.to_owned())); + if let Some(authority) = parts.uri.authority() { + return Ok(Host(parse_authority(authority).to_owned())); } Err(HostRejection::FailedToResolveHost(FailedToResolveHost)) @@ -76,11 +77,18 @@ fn parse_forwarded(headers: &HeaderMap) -> Option<&str> { }) } +fn parse_authority(auth: &Authority) -> &str { + auth.as_str() + .rsplitn(2, '@') + .next() + .expect("split always has at least 1 item") +} + #[cfg(test)] mod tests { use super::*; use crate::{routing::get, test_helpers::TestClient, Router}; - use http::header::HeaderName; + use http::{header::HeaderName, Request}; fn test_client() -> TestClient { async fn host_as_body(Host(host): Host) -> String { @@ -133,8 +141,10 @@ mod tests { #[crate::test] async fn uri_host() { - let host = test_client().get("/").send().await.text().await; - assert!(host.contains("127.0.0.1")); + let mut parts = Request::new(()).into_parts().0; + parts.uri = "127.0.0.1:1234".parse().unwrap(); + let host = Host::from_request_parts(&mut parts, &()).await.unwrap(); + assert_eq!(host.0, "127.0.0.1:1234"); } #[test] From aaf765e35cda49788c27a1bb9a8b0d06fbcaf00a Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Fri, 29 Sep 2023 12:15:03 +0200 Subject: [PATCH 2/8] Add another test case and expand them --- axum/src/extract/host.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/axum/src/extract/host.rs b/axum/src/extract/host.rs index cf13e2fecf..2dfb180a00 100644 --- a/axum/src/extract/host.rs +++ b/axum/src/extract/host.rs @@ -142,9 +142,13 @@ mod tests { #[crate::test] async fn uri_host() { let mut parts = Request::new(()).into_parts().0; - parts.uri = "127.0.0.1:1234".parse().unwrap(); + parts.uri = "https://127.0.0.1:1234/image.jpg".parse().unwrap(); let host = Host::from_request_parts(&mut parts, &()).await.unwrap(); assert_eq!(host.0, "127.0.0.1:1234"); + + parts.uri = "http://cool:user@[::1]:456/file.txt".parse().unwrap(); + let host = Host::from_request_parts(&mut parts, &()).await.unwrap(); + assert_eq!(host.0, "[::1]:456"); } #[test] From 483b24608bedd675ee3419b095734c50488cb8f8 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Fri, 29 Sep 2023 12:16:07 +0200 Subject: [PATCH 3/8] Address clippy feedback --- axum/src/extract/host.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum/src/extract/host.rs b/axum/src/extract/host.rs index 2dfb180a00..a3189def0a 100644 --- a/axum/src/extract/host.rs +++ b/axum/src/extract/host.rs @@ -79,7 +79,7 @@ fn parse_forwarded(headers: &HeaderMap) -> Option<&str> { fn parse_authority(auth: &Authority) -> &str { auth.as_str() - .rsplitn(2, '@') + .rsplit('@') .next() .expect("split always has at least 1 item") } From 9ca38f0dcd040312e8306a4dd5ec43d53e37f2c3 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Thu, 2 Nov 2023 12:50:56 +0100 Subject: [PATCH 4/8] Add to changelog --- axum/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 0e4c8b20d6..c11a986fb3 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- None. +- **fixed:** Include port number when parsing authority ([#2242]) + +[#2242]: https://github.com/tokio-rs/axum/pull/2242 # 0.7.1 (27. November, 2023) From ed7d490d76e95f78ecfb5514b2dd1c416da19615 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Wed, 29 Nov 2023 09:52:47 +0100 Subject: [PATCH 5/8] Update comment to link to RFC --- axum/src/extract/host.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/axum/src/extract/host.rs b/axum/src/extract/host.rs index a3189def0a..8039553fdb 100644 --- a/axum/src/extract/host.rs +++ b/axum/src/extract/host.rs @@ -11,13 +11,16 @@ use http::{ const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host"; -/// Extractor that resolves the hostname of the request. +/// Extractor that resolves the host of the request. /// -/// Hostname is resolved through the following, in order: +/// Host is resolved through the following, in order: /// - `Forwarded` header /// - `X-Forwarded-Host` header /// - `Host` header -/// - request target / URI +/// - Authority of the request URI +/// +/// See https://www.rfc-editor.org/rfc/rfc9110.html#name-host-and-authority for the definition of +/// host. /// /// Note that user agents can set `X-Forwarded-Host` and `Host` headers to arbitrary values so make /// sure to validate them to avoid security issues. From dbde09de77970a5c223f780d134719f0a618ca54 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 20 Sep 2024 21:01:06 +0200 Subject: [PATCH 6/8] Make link clickable in rustdoc --- axum/src/extract/host.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axum/src/extract/host.rs b/axum/src/extract/host.rs index 419fdc3c38..e5e02b8618 100644 --- a/axum/src/extract/host.rs +++ b/axum/src/extract/host.rs @@ -19,7 +19,7 @@ const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host"; /// - `Host` header /// - Authority of the request URI /// -/// See https://www.rfc-editor.org/rfc/rfc9110.html#name-host-and-authority for the definition of +/// See for the definition of /// host. /// /// Note that user agents can set `X-Forwarded-Host` and `Host` headers to arbitrary values so make From e4a109ed5a713b100d6af07c6f75c392eb504df8 Mon Sep 17 00:00:00 2001 From: Yann Simon Date: Thu, 14 Nov 2024 09:56:01 +0100 Subject: [PATCH 7/8] re-use the test_client in tests --- axum-extra/src/extract/host.rs | 12 ++++++++++++ axum/src/test_helpers/test_client.rs | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/axum-extra/src/extract/host.rs b/axum-extra/src/extract/host.rs index e8402813fb..a6828d3004 100644 --- a/axum-extra/src/extract/host.rs +++ b/axum-extra/src/extract/host.rs @@ -138,11 +138,23 @@ mod tests { #[crate::test] async fn uri_host() { + let client = test_client(); + let port = client.server_port(); + let host = client.get("/").await.text().await; + assert_eq!(host, format!("127.0.0.1:{port}")); + } + + #[crate::test] + async fn ip4_uri_host() { let mut parts = Request::new(()).into_parts().0; parts.uri = "https://127.0.0.1:1234/image.jpg".parse().unwrap(); let host = Host::from_request_parts(&mut parts, &()).await.unwrap(); assert_eq!(host.0, "127.0.0.1:1234"); + } + #[crate::test] + async fn ip6_uri_host() { + let mut parts = Request::new(()).into_parts().0; parts.uri = "http://cool:user@[::1]:456/file.txt".parse().unwrap(); let host = Host::from_request_parts(&mut parts, &()).await.unwrap(); assert_eq!(host.0, "[::1]:456"); diff --git a/axum/src/test_helpers/test_client.rs b/axum/src/test_helpers/test_client.rs index 058a0245a1..2dfa95a01f 100644 --- a/axum/src/test_helpers/test_client.rs +++ b/axum/src/test_helpers/test_client.rs @@ -83,6 +83,11 @@ impl TestClient { builder: self.client.patch(format!("http://{}{}", self.addr, url)), } } + + #[allow(dead_code)] + pub(crate) fn server_port(&self) -> u16 { + self.addr.port() + } } pub(crate) struct RequestBuilder { From dfd438b933bf24f26f3c714231d43c8410989559 Mon Sep 17 00:00:00 2001 From: Yann Simon Date: Thu, 14 Nov 2024 21:22:34 +0100 Subject: [PATCH 8/8] move doc to extra and mentions Host extractor --- axum-extra/CHANGELOG.md | 2 ++ axum/CHANGELOG.md | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/axum-extra/CHANGELOG.md b/axum-extra/CHANGELOG.md index 10d8a9300c..b0caab216d 100644 --- a/axum-extra/CHANGELOG.md +++ b/axum-extra/CHANGELOG.md @@ -7,9 +7,11 @@ and this project adheres to [Semantic Versioning]. # Unreleased +- **fixed:** `Host` extractor includes port number when parsing authority ([#2242]) - **added:** Add `RouterExt::typed_connect` ([#2961]) - **added:** Add `json!` for easy construction of JSON responses ([#2962]) +[#2242]: https://github.com/tokio-rs/axum/pull/2242 [#2961]: https://github.com/tokio-rs/axum/pull/2961 [#2962]: https://github.com/tokio-rs/axum/pull/2962 diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 22a404c8df..94eca6b220 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -7,7 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # Unreleased -- **fixed:** Include port number when parsing authority ([#2242]) - **fixed:** Skip SSE incompatible chars of `serde_json::RawValue` in `Event::json_data` ([#2992]) - **breaking:** Move `Host` extractor to `axum-extra` ([#2956]) - **added:** Add `method_not_allowed_fallback` to set a fallback when a path matches but there is no handler for the given HTTP method ([#2903]) @@ -65,7 +64,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **change:** Update tokio-tungstenite to 0.23 ([#2841]) - **added:** `Serve::local_addr` and `WithGracefulShutdown::local_addr` functions ([#2881]) -[#2242]: https://github.com/tokio-rs/axum/pull/2242 [#2653]: https://github.com/tokio-rs/axum/pull/2653 [#2790]: https://github.com/tokio-rs/axum/pull/2790 [#2841]: https://github.com/tokio-rs/axum/pull/2841