diff --git a/url/src/lib.rs b/url/src/lib.rs index 6dc09d12f..32c02e396 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -792,6 +792,8 @@ impl Url { /// 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 +819,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. /// diff --git a/url/tests/unit.rs b/url/tests/unit.rs index 55ff59ada..2993430ac 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -1162,3 +1162,47 @@ fn test_make_relative() { assert_eq!(make_relative, None, "base: {}, uri: {}", base, uri); } } + +#[test] +fn test_has_authority() { + let url = Url::parse("mailto:joe@example.com").unwrap(); + assert!(!url.has_authority()); + let url = Url::parse("unix:/run/foo.socket").unwrap(); + assert!(!url.has_authority()); + let url = Url::parse("file:///tmp/foo").unwrap(); + assert!(url.has_authority()); + let url = Url::parse("http://example.com/tmp/foo").unwrap(); + assert!(url.has_authority()); +} + +#[test] +fn test_authority() { + let url = Url::parse("mailto:joe@example.com").unwrap(); + assert_eq!(url.authority(), ""); + let url = Url::parse("unix:/run/foo.socket").unwrap(); + assert_eq!(url.authority(), ""); + let url = Url::parse("file:///tmp/foo").unwrap(); + assert_eq!(url.authority(), ""); + let url = Url::parse("http://example.com/tmp/foo").unwrap(); + assert_eq!(url.authority(), "example.com"); + let url = Url::parse("ftp://127.0.0.1:21/").unwrap(); + assert_eq!(url.authority(), "127.0.0.1"); + let url = Url::parse("ftp://user@127.0.0.1:2121/").unwrap(); + assert_eq!(url.authority(), "user@127.0.0.1:2121"); + let url = Url::parse("https://:@example.com/").unwrap(); + assert_eq!(url.authority(), "example.com"); + let url = Url::parse("https://:password@[::1]:8080/").unwrap(); + assert_eq!(url.authority(), ":password@[::1]:8080"); + let url = Url::parse("gopher://user:@àlex.example.com:70").unwrap(); + assert_eq!(url.authority(), "user@%C3%A0lex.example.com:70"); + let url = Url::parse("irc://àlex:àlex@àlex.рф.example.com:6667/foo").unwrap(); + assert_eq!( + url.authority(), + "%C3%A0lex:%C3%A0lex@%C3%A0lex.%D1%80%D1%84.example.com:6667" + ); + let url = Url::parse("https://àlex:àlex@àlex.рф.example.com:443/foo").unwrap(); + assert_eq!( + url.authority(), + "%C3%A0lex:%C3%A0lex@xn--lex-8ka.xn--p1ai.example.com" + ); +}