http
http copied to clipboard
Parsing "http://localhost" as URI results in `path_and_query: Some("/")`
use http::uri::Uri;
fn main() {
let uri: Uri = "http://localhost".parse().unwrap();
println!("{:?}", uri.into_parts().path_and_query);
}
Results in:
Some(/)
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).
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!
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.
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.
I also agree.
Should I make a PR fixing the parsing issue?
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?
You could still turn Uri into parts, set path_and_query to None and then use Uri::from_parts.
It turns out that Uri::from_parts will return an error if scheme is Some but path_and_query is None.
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:
- https://github.com/hyperium/http/blob/5b90e389d77b60d3ed8961cc29a31e008de77f69/src/uri/path.rs#L184-L186
- https://github.com/hyperium/http/blob/5b90e389d77b60d3ed8961cc29a31e008de77f69/src/uri/path.rs#L259-L261
- 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.
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?
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.
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
Noneinstead ofSome("/")when callingUri::path_and_queryafter parsingUriwithout 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?
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