hyperium / http

Rust HTTP types
Apache License 2.0
1.12k stars 283 forks source link

feat: A `username` method for `Authority` #596

Open WhyNotHugo opened 1 year ago

WhyNotHugo commented 1 year ago

A function to extract usernames from Uris/Authoritys would be super userful. E.g.:

/// Returns None if there is no `@` in the authority section.
fn (self: Authority) -> Option<&str>

The docs for http::uri::Uri::authority mention:

The authority also includes a username:password component, however the use of this is deprecated and should be avoided.

However, I can't find any reference to this being deprecated anywhere. rfc6764 is still current and expects this format to be used in the context of caldav and carddav (which is just xml over http). rfc6068 also still uses this, albeit in the context of email.

WhyNotHugo commented 1 year ago

Huh, looks like http::uri::Uri::authority is broken for URIs that are not URLs. This should fail:

let u : Uri = "mailto:someone@example.com".parse().unwrap();
assert_eq!(u.authority().unwrap().as_str(), "mailto:someone@example.com");

So while this should work, the fact that FromStr is broken is a blocker here:

pub fn username_from_uri(uri: &Uri) -> Option<&str> {
    let mut parts = uri.authority()?.as_str().rsplit('@');
    parts.next().expect("split always yields at least one item");
    parts.next()?.split(':').next()
}
WhyNotHugo commented 1 year ago

The docs for http::uri::Uri are correct in the description of an Uri:

abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|-|   |-------------------------------||--------| |-------------------| |-----|
 |                  |                       |               |              |
scheme          authority                 path            query         fragment

But the implementation is broken; only URLs are parsed correctly, anything without :// (which is indicated optional above) fails to parse:

    #[test]
    fn should_fail() {
        let u : Uri = "mailto:someone@example.com".parse().unwrap();
        assert_eq!(u.authority().unwrap().as_str(), "mailto:someone@example.com");
        assert_eq!(u.scheme(), None);
    }
WhyNotHugo commented 1 year ago

src/uri/scheme.rs:296 bails from parsing the scheme with comment Not a scheme if the colon is not followed by //.

WhyNotHugo commented 1 year ago

Actually fixing this is a lot harder that it seems, because of these two very ugly (but valid) edge cases:

WhyNotHugo commented 1 year ago

For context, I'm writing a caldav client. As per the rfc, a user should only need to input an Uri, which can be either mailto: or http(s). Discovery of the exact server host and port is done via DNS using the domain part of the mailto: Uri.

The natural choice for me is to use the existing Uri type, but the same functions can take as input both Uri variants.