From c2b72e1f4a58431ad691ae4ef7473d43cdb880e9 Mon Sep 17 00:00:00 2001 From: Martin Beckmann Date: Thu, 19 Oct 2023 09:39:09 +0200 Subject: [PATCH 1/3] Add helper function to split path --- crates/shared/src/url.rs | 63 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/crates/shared/src/url.rs b/crates/shared/src/url.rs index c310838e01..acd5106a22 100644 --- a/crates/shared/src/url.rs +++ b/crates/shared/src/url.rs @@ -1,4 +1,7 @@ -use url::Url; +use { + anyhow::{Context, Result}, + url::Url, +}; /// Join a path with a URL, ensuring that there is only one slash between them. /// It doesn't matter if the URL ends with a slash or the path starts with one. @@ -12,3 +15,61 @@ pub fn join(url: &Url, mut path: &str) -> Url { } Url::parse(&format!("{url}/{path}")).unwrap() } + +/// Splits a URL right where the path begins into base and endpoint. +/// https://my.solver.xyz/solve/1?param=1#fragment=some +/// becomes +/// (https://my.solver.xyz/, /solve/1?param=1#fragment=some) +/// Path that were split like this can be joined to the original URL using +/// [`join`]. +pub fn split_at_path(url: &Url) -> Result<(Url, String)> { + let base = format!( + "{}://{}/", + url.scheme(), + url.host().context("URL should have a host")? + ) + .parse() + .expect("stripping off the path is always safe"); + let endpoint = format!( + "{}{}{}", + url.path(), + url.query() + .map(|params| format!("?{params}")) + .unwrap_or_default(), + url.fragment() + .map(|params| format!("#{params}")) + .unwrap_or_default(), + ); + Ok((base, endpoint)) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Tests that we can split a URL and join it back together without messing + /// up the URL on the way. + #[test] + fn split_and_join() { + let round_trip = |s: &str| { + let url = s.parse().unwrap(); + let (base, endpoint) = split_at_path(&url).unwrap(); + assert_eq!(url, join(&base, &endpoint)); + }; + + // base + path + multiple params + multiple fragments + round_trip("https://my.solver.xyz/solve/1?param1=1¶m2=2#fragment=1&fragment2=2"); + // base + path + multiple params + round_trip("https://my.solver.xyz/solve/1?param1=1¶m2=2"); + // base + path + multiple fragments + round_trip("https://my.solver.xyz/solve/1#fragment1=1&fragment2=2"); + // base + path + single param + single fragment + round_trip("https://my.solver.xyz/solve/1?param=1#fragment1=1"); + // base + path + round_trip("https://my.solver.xyz/solve/1"); + // base + round_trip("http://my.solver.xyz"); + // base + multiple params + multiple fragments + round_trip("https://my.solver.xyz?param1=1¶m2=2#fragment=1&fragment2=2"); + } +} From def086d4e1671bf46b77b08de39d08b14b9df571 Mon Sep 17 00:00:00 2001 From: Martin Beckmann Date: Thu, 19 Oct 2023 09:49:11 +0200 Subject: [PATCH 2/3] Use url::split_at_path --- crates/shared/src/price_estimation/factory.rs | 7 +------ crates/solvers/src/boundary/legacy.rs | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/crates/shared/src/price_estimation/factory.rs b/crates/shared/src/price_estimation/factory.rs index 2d374eca7e..351f450483 100644 --- a/crates/shared/src/price_estimation/factory.rs +++ b/crates/shared/src/price_estimation/factory.rs @@ -559,12 +559,7 @@ impl PriceEstimatorCreating for HttpPriceEstimator { impl From<&LegacySolver> for HttpPriceEstimatorParams { fn from(solver: &LegacySolver) -> Self { - let base = { - let mut url = solver.url.clone(); - url.set_path(""); - url - }; - let solve_path = solver.url.path().to_owned(); + let (base, solve_path) = crate::url::split_at_path(&solver.url).unwrap(); Self { base, solve_path, diff --git a/crates/solvers/src/boundary/legacy.rs b/crates/solvers/src/boundary/legacy.rs index 30388eed37..97ad158069 100644 --- a/crates/solvers/src/boundary/legacy.rs +++ b/crates/solvers/src/boundary/legacy.rs @@ -44,9 +44,7 @@ pub struct Legacy { impl Legacy { pub fn new(config: crate::domain::solver::legacy::Config) -> Self { - let solve_path = config.endpoint.path().to_owned(); - let mut base = config.endpoint; - base.set_path(""); + let (base, solve_path) = shared::url::split_at_path(&config.endpoint).unwrap(); Self { solver: DefaultHttpSolverApi { From 02ead745d7eb2cf195a66ac3f3766765651aa6bc Mon Sep 17 00:00:00 2001 From: Martin Beckmann Date: Thu, 19 Oct 2023 12:08:57 +0200 Subject: [PATCH 3/3] Also copy port --- crates/shared/src/url.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/shared/src/url.rs b/crates/shared/src/url.rs index acd5106a22..40d3d4312c 100644 --- a/crates/shared/src/url.rs +++ b/crates/shared/src/url.rs @@ -24,9 +24,12 @@ pub fn join(url: &Url, mut path: &str) -> Url { /// [`join`]. pub fn split_at_path(url: &Url) -> Result<(Url, String)> { let base = format!( - "{}://{}/", + "{}://{}{}/", url.scheme(), - url.host().context("URL should have a host")? + url.host().context("URL should have a host")?, + url.port() + .map(|port| format!(":{port}")) + .unwrap_or_default() ) .parse() .expect("stripping off the path is always safe"); @@ -57,6 +60,8 @@ mod tests { assert_eq!(url, join(&base, &endpoint)); }; + // base + port + path + multiple params + multiple fragments + round_trip("https://my.solver.xyz:1234/solve/1?param1=1¶m2=2#fragment=1&fragment2=2"); // base + path + multiple params + multiple fragments round_trip("https://my.solver.xyz/solve/1?param1=1¶m2=2#fragment=1&fragment2=2"); // base + path + multiple params