diff options
Diffstat (limited to 'vendor/url/src/lib.rs')
-rw-r--r-- | vendor/url/src/lib.rs | 114 |
1 files changed, 108 insertions, 6 deletions
diff --git a/vendor/url/src/lib.rs b/vendor/url/src/lib.rs index 6dc09d12f..ad3c89001 100644 --- a/vendor/url/src/lib.rs +++ b/vendor/url/src/lib.rs @@ -121,7 +121,7 @@ url = { version = "2", features = ["serde"] } */ -#![doc(html_root_url = "https://docs.rs/url/2.3.1")] +#![doc(html_root_url = "https://docs.rs/url/2.4.0")] #![cfg_attr( feature = "debugger_visualizer", feature(debugger_visualizer), @@ -322,6 +322,32 @@ impl Url { url } + /// https://url.spec.whatwg.org/#potentially-strip-trailing-spaces-from-an-opaque-path + fn strip_trailing_spaces_from_opaque_path(&mut self) { + if !self.cannot_be_a_base() { + return; + } + + if self.fragment_start.is_some() { + return; + } + + if self.query_start.is_some() { + return; + } + + let trailing_space_count = self + .serialization + .chars() + .rev() + .take_while(|c| *c == ' ') + .count(); + + let start = self.serialization.len() - trailing_space_count; + + self.serialization.truncate(start); + } + /// Parse a string as an URL, with this URL as the base URL. /// /// The inverse of this is [`make_relative`]. @@ -601,7 +627,7 @@ impl Url { } assert!(self.scheme_end >= 1); - assert!(matches!(self.byte_at(0), b'a'..=b'z' | b'A'..=b'Z')); + assert!(self.byte_at(0).is_ascii_alphabetic()); assert!(self .slice(1..self.scheme_end) .chars() @@ -657,7 +683,14 @@ impl Url { assert_eq!(self.host_end, self.scheme_end + 1); assert_eq!(self.host, HostInternal::None); assert_eq!(self.port, None); - assert_eq!(self.path_start, self.scheme_end + 1); + if self.path().starts_with("//") { + // special case when first path segment is empty + assert_eq!(self.byte_at(self.scheme_end + 1), b'/'); + assert_eq!(self.byte_at(self.scheme_end + 2), b'.'); + assert_eq!(self.path_start, self.scheme_end + 3); + } else { + assert_eq!(self.path_start, self.scheme_end + 1); + } } if let Some(start) = self.query_start { assert!(start >= self.path_start); @@ -786,12 +819,35 @@ impl Url { self.slice(..self.scheme_end) } + /// Return whether the URL is special (has a special scheme) + /// + /// # Examples + /// + /// ``` + /// use url::Url; + /// # use url::ParseError; + /// + /// # fn run() -> Result<(), ParseError> { + /// assert!(Url::parse("http:///tmp/foo")?.is_special()); + /// assert!(Url::parse("file:///tmp/foo")?.is_special()); + /// assert!(!Url::parse("moz:///tmp/foo")?.is_special()); + /// # Ok(()) + /// # } + /// # run().unwrap(); + /// ``` + pub fn is_special(&self) -> bool { + let scheme_type = SchemeType::from(self.scheme()); + scheme_type.is_special() + } + /// Return whether the URL has an 'authority', /// which can contain a username, password, host, and port number. /// /// URLs that do *not* are either path-only like `unix:/run/foo.socket` /// or cannot-be-a-base like `data:text/plain,Stuff`. /// + /// See also the `authority` method. + /// /// # Examples /// /// ``` @@ -817,6 +873,47 @@ impl Url { self.slice(self.scheme_end..).starts_with("://") } + /// Return the authority of this URL as an ASCII string. + /// + /// Non-ASCII domains are punycode-encoded per IDNA if this is the host + /// of a special URL, or percent encoded for non-special URLs. + /// IPv6 addresses are given between `[` and `]` brackets. + /// Ports are omitted if they match the well known port of a special URL. + /// + /// Username and password are percent-encoded. + /// + /// See also the `has_authority` method. + /// + /// # Examples + /// + /// ``` + /// use url::Url; + /// # use url::ParseError; + /// + /// # fn run() -> Result<(), ParseError> { + /// let url = Url::parse("unix:/run/foo.socket")?; + /// assert_eq!(url.authority(), ""); + /// let url = Url::parse("file:///tmp/foo")?; + /// assert_eq!(url.authority(), ""); + /// let url = Url::parse("https://user:password@example.com/tmp/foo")?; + /// assert_eq!(url.authority(), "user:password@example.com"); + /// let url = Url::parse("irc://àlex.рф.example.com:6667/foo")?; + /// assert_eq!(url.authority(), "%C3%A0lex.%D1%80%D1%84.example.com:6667"); + /// let url = Url::parse("http://àlex.рф.example.com:80/foo")?; + /// assert_eq!(url.authority(), "xn--lex-8ka.xn--p1ai.example.com"); + /// # Ok(()) + /// # } + /// # run().unwrap(); + /// ``` + pub fn authority(&self) -> &str { + let scheme_separator_len = "://".len() as u32; + if self.has_authority() && self.path_start > self.scheme_end + scheme_separator_len { + self.slice(self.scheme_end + scheme_separator_len..self.path_start) + } else { + "" + } + } + /// Return whether this URL is a cannot-be-a-base URL, /// meaning that parsing a relative URL string with this URL as the base will return an error. /// @@ -1391,7 +1488,8 @@ impl Url { self.serialization.push('#'); self.mutate(|parser| parser.parse_fragment(parser::Input::no_trim(input))) } else { - self.fragment_start = None + self.fragment_start = None; + self.strip_trailing_spaces_from_opaque_path(); } } @@ -1454,6 +1552,9 @@ impl Url { parser::Input::trim_tab_and_newlines(input, vfn), ) }); + } else { + self.query_start = None; + self.strip_trailing_spaces_from_opaque_path(); } self.restore_already_parsed_fragment(fragment); @@ -1989,7 +2090,8 @@ impl Url { if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" { return Err(()); } - if let Some(password) = password { + let password = password.unwrap_or_default(); + if !password.is_empty() { let host_and_after = self.slice(self.host_start..).to_owned(); self.serialization.truncate(self.username_end as usize); self.serialization.push(':'); @@ -2805,7 +2907,7 @@ fn file_url_segments_to_pathbuf( // A windows drive letter must end with a slash. if bytes.len() > 2 - && matches!(bytes[bytes.len() - 2], b'a'..=b'z' | b'A'..=b'Z') + && bytes[bytes.len() - 2].is_ascii_alphabetic() && matches!(bytes[bytes.len() - 1], b':' | b'|') { bytes.push(b'/'); |