diff --git a/integrations/actix/src/lib.rs b/integrations/actix/src/lib.rs index f4a9a8c140..f770dea019 100644 --- a/integrations/actix/src/lib.rs +++ b/integrations/actix/src/lib.rs @@ -692,12 +692,12 @@ fn provide_contexts(req: &HttpRequest, res_options: ResponseOptions) { } fn leptos_corrected_path(req: &HttpRequest) -> String { - let path = req.path(); + let path = req.path().to_string(); let query = req.query_string(); if query.is_empty() { - "http://leptos".to_string() + path + path } else { - "http://leptos".to_string() + path + "?" + query + path + "?" + query } } #[tracing::instrument(level = "trace", fields(error), skip_all)] diff --git a/integrations/axum/src/lib.rs b/integrations/axum/src/lib.rs index 61c9ba46eb..f9d1088d5c 100644 --- a/integrations/axum/src/lib.rs +++ b/integrations/axum/src/lib.rs @@ -659,12 +659,11 @@ where // Need to get the path and query string of the Request // For reasons that escape me, if the incoming URI protocol is https, it provides the absolute URI // if http, it returns a relative path. Adding .path() seems to make it explicitly return the relative uri - let path = req.uri().path_and_query().unwrap().as_str(); + let path = req.uri().path_and_query().unwrap().to_string(); - let full_path = format!("http://leptos.dev{path}"); let (_, req_parts) = generate_request_and_parts(req); move || { - provide_contexts(full_path, req_parts, default_res_options); + provide_contexts(path, req_parts, default_res_options); app_fn().into_view() } }; @@ -816,18 +815,15 @@ where // Need to get the path and query string of the Request // For reasons that escape me, if the incoming URI protocol is https, it provides the absolute URI // if http, it returns a relative path. Adding .path() seems to make it explicitly return the relative uri - let path = req.uri().path_and_query().unwrap().as_str(); - - let full_path = format!("http://leptos.dev{path}"); + let path = req.uri().path_and_query().unwrap().to_string(); let (tx, rx) = futures::channel::mpsc::channel(8); let current_span = tracing::Span::current(); spawn_task!(async move { let app = { - let full_path = full_path.clone(); let (parts, _) = req.into_parts(); move || { - provide_contexts(full_path, parts, default_res_options); + provide_contexts(path, parts, default_res_options); app_fn().into_view() } }; @@ -988,18 +984,15 @@ where // Need to get the path and query string of the Request // For reasons that escape me, if the incoming URI protocol is https, it provides the absolute URI // if http, it returns a relative path. Adding .path() seems to make it explicitly return the relative uri - let path = req.uri().path_and_query().unwrap().as_str(); - - let full_path = format!("http://leptos.dev{path}"); + let path = req.uri().path_and_query().unwrap().to_string(); let (tx, rx) = futures::channel::oneshot::channel(); spawn_task!(async move { let app = { - let full_path = full_path.clone(); let (_, req_parts) = generate_request_and_parts(req); move || { provide_contexts( - full_path, + path, req_parts, default_res_options, ); @@ -1117,19 +1110,16 @@ where // Need to get the path and query string of the Request // For reasons that escape me, if the incoming URI protocol is https, it provides the absolute URI // if http, it returns a relative path. Adding .path() seems to make it explicitly return the relative uri - let path = req.uri().path_and_query().unwrap().as_str(); - - let full_path = format!("http://leptos.dev{path}"); + let path = req.uri().path_and_query().unwrap().to_string(); let (tx, rx) = futures::channel::oneshot::channel(); spawn_task!(async move { let app = { - let full_path = full_path.clone(); let (_, req_parts) = generate_request_and_parts(req); move || { provide_contexts( - full_path, + path, req_parts, default_res_options, ); diff --git a/router/src/components/static_render.rs b/router/src/components/static_render.rs index 58245a79ff..096842baa3 100644 --- a/router/src/components/static_render.rs +++ b/router/src/components/static_render.rs @@ -177,12 +177,12 @@ impl ResolvedStaticPath { where IV: IntoView + 'static, { - let url = format!("http://leptos{}", self); + let path = self.to_string(); let app = { let app_fn = app_fn.clone(); move || { provide_context(RouterIntegrationContext::new( - ServerIntegration { path: url }, + ServerIntegration { path }, )); provide_context(MetaContext::new()); (app_fn)().into_view() diff --git a/router/src/extract_routes.rs b/router/src/extract_routes.rs index 8111d60770..9947577d75 100644 --- a/router/src/extract_routes.rs +++ b/router/src/extract_routes.rs @@ -129,7 +129,7 @@ where let runtime = create_runtime(); let integration = ServerIntegration { - path: "http://leptos.rs/".to_string(), + path: "/".to_string(), }; provide_context(RouterIntegrationContext::new(integration)); diff --git a/router/src/history/location.rs b/router/src/history/location.rs index 2428d71f39..832e4aae97 100644 --- a/router/src/history/location.rs +++ b/router/src/history/location.rs @@ -8,13 +8,24 @@ pub fn create_location( state: ReadSignal, ) -> Location { let url = create_memo(move |prev: Option<&Url>| { - path.with(|path| match Url::try_from(path.as_str()) { - Ok(url) => url, - Err(e) => { - leptos::logging::error!( - "[Leptos Router] Invalid path {path}\n\n{e:?}" + path.with(|path| { + let full_path = if path.starts_with("/") { + format!("http://leptos.dev{path}") + } else { + leptos::logging::warn!( + "[Leptos Router] create_location() should be called with \ + a path instead of {path}" ); - prev.cloned().unwrap() + path.to_string() + }; + match Url::try_from(full_path.as_str()) { + Ok(url) => url, + Err(e) => { + leptos::logging::error!( + "[Leptos Router] Invalid path {path}\n\n{e:?}" + ); + prev.cloned().unwrap() + } } }) }); diff --git a/router/src/history/mod.rs b/router/src/history/mod.rs index 29f2092e90..3e99537bc2 100644 --- a/router/src/history/mod.rs +++ b/router/src/history/mod.rs @@ -158,7 +158,7 @@ pub(crate) fn scroll_to_el(loc_scroll: bool) { /// # use leptos::*; /// # let rt = create_runtime(); /// let integration = ServerIntegration { -/// path: "http://leptos.rs/".to_string(), +/// path: "/".to_string(), /// }; /// provide_context(RouterIntegrationContext::new(integration)); /// # rt.dispose(); @@ -195,8 +195,7 @@ impl History for RouterIntegrationContext { /// # use leptos::*; /// # let rt = create_runtime(); /// let integration = ServerIntegration { -/// // Swap out with your URL if integrating manually. -/// path: "http://leptos.rs/".to_string(), +/// path: "/".to_string(), /// }; /// provide_context(RouterIntegrationContext::new(integration)); /// # rt.dispose(); diff --git a/router/src/history/url.rs b/router/src/history/url.rs index 78cfc2c1fb..24a49198cf 100644 --- a/router/src/history/url.rs +++ b/router/src/history/url.rs @@ -31,69 +31,72 @@ pub fn escape(s: &str) -> String { js_sys::encode_uri(s).as_string().unwrap() } -#[cfg(not(feature = "ssr"))] -impl TryFrom<&str> for Url { - type Error = String; +impl Url { + #[cfg(not(feature = "ssr"))] + pub fn new_with_base(url: &str, base: &str) -> Result { + let url = web_sys::Url::new_with_base(url, base).map_js_error()?; + Self::from_web_sys_url(&url) + } - fn try_from(url: &str) -> Result { - let url = web_sys::Url::new_with_base( - &if url.starts_with("//") { - let origin = - leptos::window().location().origin().unwrap_or_default(); - format!("{origin}{url}") - } else { - url.to_string() + #[cfg(not(feature = "ssr"))] + fn from_web_sys_url(url: &web_sys::Url) -> Result { + Ok( + Self { + origin: url.origin(), + pathname: url.pathname(), + search: url + .search() + .strip_prefix('?') + .map(String::from) + .unwrap_or_default(), + search_params: + ParamsMap( + try_iter(&url.search_params()) + .map_js_error()? + .ok_or( + "Failed to use URLSearchParams as an iterator" + .to_string(), + )? + .map(|value| { + let array: Array = value + .map_js_error()? + .dyn_into() + .map_js_error()?; + Ok(( + array + .get(0) + .dyn_into::() + .map_js_error()? + .into(), + array + .get(1) + .dyn_into::() + .map_js_error()? + .into(), + )) + }) + .collect::, + String, + >>()?, + ), + hash: url.hash(), }, - "http://leptos", ) - .map_js_error()?; - Ok(Self { - origin: url.origin(), - pathname: url.pathname(), - search: url - .search() - .strip_prefix('?') - .map(String::from) - .unwrap_or_default(), - search_params: ParamsMap( - try_iter(&url.search_params()) - .map_js_error()? - .ok_or( - "Failed to use URLSearchParams as an iterator" - .to_string(), - )? - .map(|value| { - let array: Array = - value.map_js_error()?.dyn_into().map_js_error()?; - Ok(( - array - .get(0) - .dyn_into::() - .map_js_error()? - .into(), - array - .get(1) - .dyn_into::() - .map_js_error()? - .into(), - )) - }) - .collect::, - Self::Error, - >>()?, - ), - hash: url.hash(), - }) } -} -#[cfg(feature = "ssr")] -impl TryFrom<&str> for Url { - type Error = String; + #[cfg(feature = "ssr")] + pub fn new_with_base(url: &str, base: &str) -> Result { + let base = url::Url::parse(base).map_err(|e| e.to_string())?; + let url = url::Url::options() + .base_url(Some(&base)) + .parse(url) + .map_err(|e| e.to_string())?; + Self::from_servo_url(&url) + } - fn try_from(url: &str) -> Result { - let url = url::Url::parse(url).map_err(|e| e.to_string())?; + #[cfg(feature = "ssr")] + fn from_servo_url(url: &url::Url) -> Result { Ok(Self { origin: url.origin().unicode_serialization(), pathname: url.path().to_string(), @@ -108,6 +111,26 @@ impl TryFrom<&str> for Url { } } +#[cfg(not(feature = "ssr"))] +impl TryFrom<&str> for Url { + type Error = String; + + fn try_from(url: &str) -> Result { + let url = web_sys::Url::new(url).map_js_error()?; + Self::from_web_sys_url(&url) + } +} + +#[cfg(feature = "ssr")] +impl TryFrom<&str> for Url { + type Error = String; + + fn try_from(url: &str) -> Result { + let url = url::Url::parse(url).map_err(|e| e.to_string())?; + Self::from_servo_url(&url) + } +} + #[cfg(not(feature = "ssr"))] trait MapJsError { fn map_js_error(self) -> Result;