Jelajahi Sumber

Fix scheme setter

> test result: FAILED. 650 passed; 63 failed; 0 ignored; 0 measured
Jeremy Lempereur 7 tahun lalu
induk
melakukan
7efdc53193
1 mengubah file dengan 72 tambahan dan 5 penghapusan
  1. 72 5
      src/lib.rs

+ 72 - 5
src/lib.rs

@@ -1663,8 +1663,13 @@ impl Url {
                 self.set_host_internal(Host::parse_opaque(host_substr)?, None);
             }
         } else if self.has_host() {
-            if SchemeType::from(self.scheme()).is_special() {
+            let scheme_type = SchemeType::from(self.scheme());
+            if scheme_type.is_special() {
                 return Err(ParseError::EmptyHost);
+            } else {
+                if self.serialization.len() == self.path_start as usize {
+                    self.serialization.push('/');
+                }
             }
             debug_assert!(self.byte_at(self.scheme_end) == b':');
             debug_assert!(self.byte_at(self.path_start) == b'/');
@@ -1967,14 +1972,28 @@ impl Url {
     ///
     /// # fn run() -> Result<(), ParseError> {
     /// let mut url = Url::parse("https://example.net")?;
-    /// let result = url.set_scheme("foo");
-    /// assert_eq!(url.as_str(), "foo://example.net/");
+    /// let result = url.set_scheme("http");
+    /// assert_eq!(url.as_str(), "http://example.net/");
     /// assert!(result.is_ok());
     /// # Ok(())
     /// # }
     /// # run().unwrap();
     /// ```
+    /// Change the URL’s scheme from `foo` to `bar`:
     ///
+    /// ```
+    /// use url::Url;
+    /// # use url::ParseError;
+    ///
+    /// # fn run() -> Result<(), ParseError> {
+    /// let mut url = Url::parse("foo://example.net")?;
+    /// let result = url.set_scheme("bar");
+    /// assert_eq!(url.as_str(), "bar://example.net");
+    /// assert!(result.is_ok());
+    /// # Ok(())
+    /// # }
+    /// # run().unwrap();
+    /// ```
     ///
     /// Cannot change URL’s scheme from `https` to `foõ`:
     ///
@@ -2007,14 +2026,55 @@ impl Url {
     /// # }
     /// # run().unwrap();
     /// ```
+    /// Cannot change the URL’s scheme from `foo` to `https`:
+    ///
+    /// ```
+    /// use url::Url;
+    /// # use url::ParseError;
+    ///
+    /// # fn run() -> Result<(), ParseError> {
+    /// let mut url = Url::parse("foo://example.net")?;
+    /// let result = url.set_scheme("https");
+    /// assert_eq!(url.as_str(), "foo://example.net");
+    /// assert!(result.is_err());
+    /// # Ok(())
+    /// # }
+    /// # run().unwrap();
+    /// ```
+    /// Cannot change the URL’s scheme from `http` to `foo`:
+    ///
+    /// ```
+    /// use url::Url;
+    /// # use url::ParseError;
+    ///
+    /// # fn run() -> Result<(), ParseError> {
+    /// let mut url = Url::parse("http://example.net")?;
+    /// let result = url.set_scheme("foo");
+    /// assert_eq!(url.as_str(), "http://example.net/");
+    /// assert!(result.is_err());
+    /// # Ok(())
+    /// # }
+    /// # run().unwrap();
+    /// ```
     pub fn set_scheme(&mut self, scheme: &str) -> Result<(), ()> {
         let mut parser = Parser::for_setter(String::new());
         let remaining = parser.parse_scheme(parser::Input::new(scheme))?;
-        if !remaining.is_empty()
-            || (!self.has_host() && SchemeType::from(&parser.serialization).is_special())
+        let new_scheme_type = SchemeType::from(&parser.serialization);
+        let old_scheme_type = SchemeType::from(self.scheme());
+        // If url’s scheme is a special scheme and buffer is not a special scheme, then return.
+        if new_scheme_type.is_special() && !old_scheme_type.is_special() ||
+            // If url’s scheme is not a special scheme and buffer is a special scheme, then return.
+            !new_scheme_type.is_special() && old_scheme_type.is_special() ||
+            // If url includes credentials or has a non-null port, and buffer is "file", then return.
+            // If url’s scheme is "file" and its host is an empty host or null, then return.
+            new_scheme_type.is_file() && self.has_authority()
         {
             return Err(());
         }
+
+        if !remaining.is_empty() || (!self.has_host() && new_scheme_type.is_special()) {
+            return Err(());
+        }
         let old_scheme_end = self.scheme_end;
         let new_scheme_end = to_u32(parser.serialization.len()).unwrap();
         let adjust = |index: &mut u32| {
@@ -2036,6 +2096,13 @@ impl Url {
 
         parser.serialization.push_str(self.slice(old_scheme_end..));
         self.serialization = parser.serialization;
+
+        // Update the port so it can be removed
+        // If it is the scheme's default
+        // We don't mind it silently failing
+        // If there was no port in the first place
+        let _ = self.set_port(self.port());
+
         Ok(())
     }