http icon indicating copy to clipboard operation
http copied to clipboard

Parsing "http://localhost" as URI results in `path_and_query: Some("/")`

Open LukasKalbertodt opened this issue 3 years ago • 12 comments

use http::uri::Uri;

fn main() {
    let uri: Uri = "http://localhost".parse().unwrap();
    println!("{:?}", uri.into_parts().path_and_query);
}

Results in:

Some(/)

Playground

Is that intended? I could understand path_and_query being Some(""), but Some("/") seems wrong, as there is definitely no / in the input (after the authority).

LukasKalbertodt avatar Feb 20 '21 11:02 LukasKalbertodt

It is standard behaviour for the path to default to /.

This is because you're trying to access the root, often being the folder /, which often gets translated to /index.html

Hope it helps!

Icelk avatar Feb 25 '21 18:02 Icelk

I would say this is a bug, as sometimes the distinction between no path and root path is important. For example, when calculating the effective request URI per RFC 7230 §5.5, there are certain conditions where the path and query are specified to be empty. As far as I can tell, there is not a way to represent such a URI currently with http::Uri.

acfoltzer avatar Mar 04 '21 02:03 acfoltzer

As far as I can tell, there is not a way to represent such a URI currently with http::Uri.

You can represent it:

Uri::from_parts(Parts {
    scheme: Some(Scheme::HTTPS),
    authority: Some(Authority::from_static("localhost")),
    path_and_query: None,
})

It's just that it seems you cannot obtain such a representation from parsing. So:

I would say this is a bug

I would agree with that.

LukasKalbertodt avatar Mar 04 '21 07:03 LukasKalbertodt

I also agree.

Should I make a PR fixing the parsing issue?

Icelk avatar Mar 04 '21 09:03 Icelk

You can represent it:

Uri::from_parts(Parts {
    scheme: Some(Scheme::HTTPS),
    authority: Some(Authority::from_static("localhost")),
    path_and_query: None,
})

Since uri::Parts has a private field, I don't believe this is possible outside of the http crate itself, unless there's a public constructor I'm missing?

acfoltzer avatar Mar 04 '21 17:03 acfoltzer

You could still turn Uri into parts, set path_and_query to None and then use Uri::from_parts.

jplatte avatar Mar 04 '21 17:03 jplatte

It turns out that Uri::from_parts will return an error if scheme is Some but path_and_query is None.

acfoltzer avatar Mar 04 '21 17:03 acfoltzer

Digging into this a bit more, there are a few places in the code that probably need to be addressed to fix this.

First, Uri::from_parts contains a check that doesn't seem correct, as mentioned above:

https://github.com/hyperium/http/blob/5b90e389d77b60d3ed8961cc29a31e008de77f69/src/uri/mod.rs#L206-L213

But even if that check is removed, we still get slashes showing up where they shouldn't be:

  1. https://github.com/hyperium/http/blob/5b90e389d77b60d3ed8961cc29a31e008de77f69/src/uri/path.rs#L184-L186
  2. https://github.com/hyperium/http/blob/5b90e389d77b60d3ed8961cc29a31e008de77f69/src/uri/path.rs#L259-L261
  3. https://github.com/hyperium/http/blob/5b90e389d77b60d3ed8961cc29a31e008de77f69/src/uri/path.rs#L319-L321

The last one in particular makes me nervous about just writing a patch, as removing the special-casing there will change the debug and display output of Uris, which I know are relied upon in some cases. If we decide that potential for breakage is acceptable, I'm happy to go ahead with it but it turns out to be a further-reaching change than I'd hoped.

acfoltzer avatar Mar 04 '21 21:03 acfoltzer

I propose we change this and release a 0.2.4 version, stating the changes clearly in the changelog.

This bug should not be part of the most popular HTTP type library.

Should I make a PR? Can someone with write permissions then make a release?

Icelk avatar Mar 09 '21 08:03 Icelk

I'm nervous about making this change as a patch release, because folks might not even know to look at the changelog before Cargo automatically pulls in the new version. I'm not sure what precedents have been set for similar changes in this crate previously.

acfoltzer avatar Mar 11 '21 22:03 acfoltzer

Me too. Looking back at the release history, this should be a minor release. But what does this change?

  • 1 byte in debug implementation
  • returning None instead of Some("/") when calling Uri::path_and_query after parsing Uri without path.

In both cases, I do not think people depend on (or for that part should) these mechanics.

Do you know if any other changes are blocking on v0.3.0?

Icelk avatar Mar 12 '21 08:03 Icelk

Seems to be related:

use http::uri::{Scheme, Uri};
fn main() {
    let original_uri = Uri::builder()
        .authority("localhost")
        .scheme(Scheme::HTTPS)
        .path_and_query(format!("a/b"))
        .build()
        .unwrap();
    let uri_str_roundtrip = Uri::try_from(original_uri.to_string()).unwrap();

    assert_eq!(original_uri.path_and_query(), uri_str_roundtrip.path_and_query());
}

Fails with:

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Some(/a/b)`,
 right: `Some(/b)`', src/main.rs:11:5

Playground

elichai avatar Jan 27 '23 13:01 elichai