From 7a2313cc4b6a9ff613903c7684162e3129ef7b72 Mon Sep 17 00:00:00 2001 From: Timo Caktu <74780331+TimoCak@users.noreply.github.com> Date: Mon, 10 Jun 2024 04:49:50 +0200 Subject: [PATCH] web: add `HttpRequest::full_url()` (#3096) * implemented function which returns full uir * changes added into the changelog * added test funtion for full_uri method * refactor: rename to full_url --------- Co-authored-by: Rob Ede --- actix-web/CHANGES.md | 4 ++++ actix-web/src/request.rs | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/actix-web/CHANGES.md b/actix-web/CHANGES.md index 28dd25fb5..bb0844e05 100644 --- a/actix-web/CHANGES.md +++ b/actix-web/CHANGES.md @@ -2,6 +2,10 @@ ## Unreleased +### Added + +- Add `HttpRequest::full_url()` method to get the complete URL of the request. + ### Fixed - `ConnectionInfo::realip_remote_addr()` now handles IPv6 addresses from `Forwarded` header correctly. Previously, it sometimes returned the forwarded port as well. diff --git a/actix-web/src/request.rs b/actix-web/src/request.rs index 08a222c86..47b3e3d88 100644 --- a/actix-web/src/request.rs +++ b/actix-web/src/request.rs @@ -91,6 +91,35 @@ impl HttpRequest { &self.head().uri } + /// Returns request's original full URL. + /// + /// Reconstructed URL is best-effort, using [`connection_info`](HttpRequest::connection_info()) + /// to get forwarded scheme & host. + /// + /// ``` + /// use actix_web::test::TestRequest; + /// let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo") + /// .insert_header(("host", "example.com")) + /// .to_http_request(); + /// + /// assert_eq!( + /// req.full_url().as_str(), + /// "http://example.com/api?id=4&name=foo", + /// ); + /// ``` + pub fn full_url(&self) -> url::Url { + let info = self.connection_info(); + let scheme = info.scheme(); + let host = info.host(); + let path_and_query = self + .uri() + .path_and_query() + .map(|paq| paq.as_str()) + .unwrap_or("/"); + + url::Url::parse(&format!("{scheme}://{host}{path_and_query}")).unwrap() + } + /// Read the Request method. #[inline] pub fn method(&self) -> &Method { @@ -963,4 +992,27 @@ mod tests { assert!(format!("{:?}", req).contains(location_header)); } + + #[test] + fn check_full_url() { + let req = TestRequest::with_uri("/api?id=4&name=foo").to_http_request(); + assert_eq!( + req.full_url().as_str(), + "http://localhost:8080/api?id=4&name=foo", + ); + + let req = TestRequest::with_uri("https://example.com/api?id=4&name=foo").to_http_request(); + assert_eq!( + req.full_url().as_str(), + "https://example.com/api?id=4&name=foo", + ); + + let req = TestRequest::with_uri("http://10.1.2.3:8443/api?id=4&name=foo") + .insert_header(("host", "example.com")) + .to_http_request(); + assert_eq!( + req.full_url().as_str(), + "http://example.com/api?id=4&name=foo", + ); + } }