I just came across a panic in the latest reqwest release (both 0.10 and master). This happens when making a request.
It seems that some input in Client.get(url) parses successfully as URL at first, and then panics when attempting to parse the URL as http::Uri (internally).
Here's a very basic (abstracted) example that causes this panic:
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
reqwest::Client::new()
.get("http://\"")
.send()
.await?;
Ok(())
}
Which outputs:
thread 'main' panicked at 'a parsed Url should always be a valid Uri: InvalidUri(InvalidUriChar)', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
I believe the Client.get(url) function should return an error in all cases where the URL is invalid. This actually happens when providing something like "http://a b" (InvalidDomainCharacter). However, "http://\"" succeeds and panics when making the request with .send().
The panic is located here.
I have the following assumption on what is going on:
I think Url is used first to parse the provided URL (here). Then the assumption is made this URL is valid once parsing succeeds. Internally, when making a request through hyper, this URL is parsed to an Uri. Assuming the URL is valid this _should_ always succeed, but it does not. That probably means that either implementation of Url or Uri is incorrect, or at least not compatible.
Al right. I'm not too familiar with the URL/URI specs, and don't want to go through it right now, to confirm either implementation is correct.
I see the following options:
Uri when creating the request (and check for validity).Url implementation (if invalid)Uri implementation (if invalid)Uri throughout the crate.Result.I think the first is easiest to do on a short notice.
What would be the correct way of doing things? What are your thoughts, is the assumption I made right, and is this undesired behaviour?
I might be happy to implement a fix for this in code once I know what path I should take.
Repro'd on the Url side of things
Here are a few other random ones that came up in my logs, parseable as Url but not as Uri:
"http://鈥榥vme@nip2"
"http://-1@"
"https://a@b"
"https://鈧珸qq"
As a non-ideal bandage, I'm using these in the short term: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url
https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)
https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)
Thanks, as a workaround, I'm currently actually testing whether it will parse as Uri, to be 100% sure it works before providing it to reqwest.
I believe URI is more forgiving in terms of the spec right?
I believe
URIis more forgiving in terms of the spec right?
Not sure, it might be. But the panic came up because an Url (successfully parsed) couldn't be parsed as Uri (internally in reqwest). Testing this exact transformation before giving the URL to reqwest allows you to test whether it would panic or not, that's why I'm using this approach.
But in the end I think this issue should be fixed no matter what, of course.
I've also hit this bug, on URLs that contain "<" and ">".
LIke @timvisee I am parsing URLs as http::Uri first, which does stop panics.
It seems to me that reqwest should parse as http::Uri as soon as possible, and return an error to the caller if it isn't valid.
It seems to me that reqwest should parse as http::Uri as soon as possible, and return an error to the caller if it isn't valid.
I don't think Uri can directly replace Url in the request struct, because Url is used to manage various URL parts. An explicit parse check to mitigate this panic upon creating the Url could be done, but this introduces undesirable overhead.
I believe https://github.com/seanmonstar/reqwest/issues/668#issuecomment-540310377 is correct, in that the best approach would be to fix the Url implementation.
I don't believe this is necessarily a bug in url, as the URL spec allows for parsing a lot of non-sensical URLs.
As you've mentioned, reqwest should likely check sooner that the URL can also be a valid URI.
Most helpful comment
Not sure, it might be. But the panic came up because an
Url(successfully parsed) couldn't be parsed asUri(internally inreqwest). Testing this exact transformation before giving the URL toreqwestallows you to test whether it would panic or not, that's why I'm using this approach.But in the end I think this issue should be fixed no matter what, of course.