Jelajahi Sumber

fix(#2281): Prevent double slashes when joining URLs (#2282)

Wouter Buckens 4 tahun lalu
induk
melakukan
31685c9f9d

+ 6 - 0
.changes/fix-url-concatenation.md

@@ -0,0 +1,6 @@
+---
+"tauri": patch
+---
+
+Use [`Url.join()`](https://docs.rs/url/2.2.2/url/struct.Url.html#method.join) when building webview URLs in
+`WindowManager`, to handle edge cases and leading/trailing slashes in paths and urls.

+ 1 - 0
core/tauri/Cargo.toml

@@ -36,6 +36,7 @@ serde = { version = "1.0", features = [ "derive" ] }
 tokio = { version = "1.7", features = [ "rt", "rt-multi-thread", "sync", "fs" ] }
 futures = "0.3"
 uuid = { version = "0.8", features = [ "v4" ] }
+url = { version = "2.2" }
 thiserror = "1.0"
 once_cell = "1.8"
 tauri-runtime = { version = "0.1.4", path = "../tauri-runtime" }

+ 4 - 0
core/tauri/src/error.rs

@@ -76,6 +76,10 @@ pub enum Error {
   /// Encountered an error creating the app system tray,
   #[error("error encountered during tray setup: {0}")]
   SystemTray(Box<dyn std::error::Error + Send>),
+  /// A part of the URL is malformed or invalid. This may occur when parsing and combining
+  /// user-provided URLs and paths.
+  #[error("invalid url: {0}")]
+  InvalidUrl(url::ParseError),
 }
 
 impl From<serde_json::Error> for Error {

+ 27 - 16
core/tauri/src/manager.rs

@@ -42,6 +42,7 @@ use std::{
   sync::{Arc, Mutex, MutexGuard},
 };
 use tauri_macros::default_runtime;
+use url::Url;
 use uuid::Uuid;
 
 const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
@@ -180,20 +181,27 @@ impl<R: Runtime> WindowManager<R> {
     self.inner.menu_ids.clone()
   }
 
-  // setup content for dev-server
+  /// Get the base path to serve data from.
+  ///
+  /// * In dev mode, this will be based on the `devPath` configuration value.
+  /// * Otherwise, this will be based on the `distDir` configuration value.
+  #[cfg(custom_protocol)]
+  fn base_path(&self) -> &AppUrl {
+    &self.inner.config.build.dist_dir
+  }
+
   #[cfg(dev)]
-  fn get_url(&self) -> String {
-    match &self.inner.config.build.dev_path {
-      AppUrl::Url(WindowUrl::External(url)) => url.to_string(),
-      _ => "tauri://localhost".into(),
-    }
+  fn base_path(&self) -> &AppUrl {
+    &self.inner.config.build.dev_path
   }
 
-  #[cfg(custom_protocol)]
-  fn get_url(&self) -> String {
-    match &self.inner.config.build.dist_dir {
-      AppUrl::Url(WindowUrl::External(url)) => url.to_string(),
-      _ => "tauri://localhost".into(),
+  /// Get the base URL to use for webview requests.
+  ///
+  /// In dev mode, this will be based on the `devPath` configuration value.
+  fn get_url(&self) -> Cow<'_, Url> {
+    match self.base_path() {
+      AppUrl::Url(WindowUrl::External(url)) => Cow::Borrowed(url),
+      _ => Cow::Owned(Url::parse("tauri://localhost").unwrap()),
     }
   }
 
@@ -505,10 +513,10 @@ mod test {
     );
 
     #[cfg(custom_protocol)]
-    assert_eq!(manager.get_url(), "tauri://localhost");
+    assert_eq!(manager.get_url().to_string(), "tauri://localhost");
 
     #[cfg(dev)]
-    assert_eq!(manager.get_url(), "http://localhost:4000/");
+    assert_eq!(manager.get_url().to_string(), "http://localhost:4000/");
   }
 }
 
@@ -561,13 +569,16 @@ impl<R: Runtime> WindowManager<R> {
           true,
           // ignore "index.html" just to simplify the url
           if path.to_str() != Some("index.html") {
-            format!("{}/{}", url, path.to_string_lossy())
-          } else {
             url
+              .join(&*path.to_string_lossy())
+              .map_err(crate::Error::InvalidUrl)?
+              .to_string()
+          } else {
+            url.to_string()
           },
         )
       }
-      WindowUrl::External(url) => (url.as_str().starts_with("tauri://"), url.to_string()),
+      WindowUrl::External(url) => (url.scheme() == "tauri", url.to_string()),
       _ => unimplemented!(),
     };