Fix bug parsing url query with escaped & or = (#4172)

I have tested this manually:

<img width="727" alt="Screenshot 2024-03-14 at 19 38 34"
src="https://github.com/emilk/egui/assets/1148717/fa1f6da4-d9e3-4f08-9812-1d61479dfd52">
This commit is contained in:
Emil Ernerfeldt 2024-03-15 06:43:27 +01:00 committed by GitHub
parent c5eaba43cd
commit bf7ffb982a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 17 additions and 12 deletions

View File

@ -718,7 +718,7 @@ pub struct WebInfo {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Location { pub struct Location {
/// The full URL (`location.href`) without the hash. /// The full URL (`location.href`) without the hash, percent-decoded.
/// ///
/// Example: `"http://www.example.com:80/index.html?foo=bar"`. /// Example: `"http://www.example.com:80/index.html?foo=bar"`.
pub url: String, pub url: String,

View File

@ -99,28 +99,25 @@ pub fn web_location() -> epi::Location {
.search() .search()
.unwrap_or_default() .unwrap_or_default()
.strip_prefix('?') .strip_prefix('?')
.map(percent_decode) .unwrap_or_default()
.unwrap_or_default(); .to_owned();
let query_map = parse_query_map(&query)
.iter()
.map(|(k, v)| ((*k).to_owned(), (*v).to_owned()))
.collect();
epi::Location { epi::Location {
// TODO(emilk): should we really percent-decode the url? 🤷‍♂️
url: percent_decode(&location.href().unwrap_or_default()), url: percent_decode(&location.href().unwrap_or_default()),
protocol: percent_decode(&location.protocol().unwrap_or_default()), protocol: percent_decode(&location.protocol().unwrap_or_default()),
host: percent_decode(&location.host().unwrap_or_default()), host: percent_decode(&location.host().unwrap_or_default()),
hostname: percent_decode(&location.hostname().unwrap_or_default()), hostname: percent_decode(&location.hostname().unwrap_or_default()),
port: percent_decode(&location.port().unwrap_or_default()), port: percent_decode(&location.port().unwrap_or_default()),
hash, hash,
query_map: parse_query_map(&query),
query, query,
query_map,
origin: percent_decode(&location.origin().unwrap_or_default()), origin: percent_decode(&location.origin().unwrap_or_default()),
} }
} }
fn parse_query_map(query: &str) -> BTreeMap<&str, &str> { /// query is percent-encoded
fn parse_query_map(query: &str) -> BTreeMap<String, String> {
query query
.split('&') .split('&')
.filter_map(|pair| { .filter_map(|pair| {
@ -128,15 +125,16 @@ fn parse_query_map(query: &str) -> BTreeMap<&str, &str> {
None None
} else { } else {
Some(if let Some((key, value)) = pair.split_once('=') { Some(if let Some((key, value)) = pair.split_once('=') {
(key, value) (percent_decode(key), percent_decode(value))
} else { } else {
(pair, "") (percent_decode(pair), String::new())
}) })
} }
}) })
.collect() .collect()
} }
// TODO(emilk): this test is never acgtually run, because this whole module is wasm32 only 🤦‍♂️
#[test] #[test]
fn test_parse_query() { fn test_parse_query() {
assert_eq!(parse_query_map(""), BTreeMap::default()); assert_eq!(parse_query_map(""), BTreeMap::default());
@ -157,4 +155,11 @@ fn test_parse_query() {
parse_query_map("foo&baz&&"), parse_query_map("foo&baz&&"),
BTreeMap::from_iter([("foo", ""), ("baz", "")]) BTreeMap::from_iter([("foo", ""), ("baz", "")])
); );
assert_eq!(
parse_query_map("badger=data.rrd%3Fparam1%3Dfoo%26param2%3Dbar&mushroom=snake"),
BTreeMap::from_iter([
("badger", "data.rrd?param1=foo&param2=bar"),
("mushroom", "snake")
])
);
} }