refactor: parse form bodies with form_urlencoded instead of by hand
commit
a15f980refactor: parse form bodies with form_urlencoded instead of by hand
The hand-rolled percent_decode/hex_value pair reimplemented what form_urlencoded already does, tested, for x-www-form-urlencoded bodies.
deps: add form_urlencoded refactor: replace write.rs’s percent_decode/hex_value with form_urlencoded::parse Assisted-by: Claude:claude-sonnet-5
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
Cargo.lock
@@ -1042,6 +1042,7 @@
"clap",
"clap_mangen",
"facet",
+ "form_urlencoded",
"getrandom",
"git-ents",
"git-store",
Cargo.toml
@@ -38,6 +38,7 @@
facet = { version = "0.50.0-rc.0", features = ["reflect"] }
getrandom = "0.4"
facet-git-tree = { git = "https://github.com/git-ents/facet-git-tree" }
+form_urlencoded = "1"
git-store = { path = "crates/git-store" }
gix = "0.84"
gix-hash = { version = "0.25", features = ["sha1"] }
crates/git-ents-server/Cargo.toml
@@ -15,6 +15,7 @@
clap = { workspace = true }
clap_mangen = { workspace = true }
facet = { workspace = true }
+form_urlencoded = { workspace = true }
getrandom = { workspace = true }
git-store = { workspace = true }
gix-actor = { workspace = true }
crates/git-ents-server/src/web/write.rs
@@ -473,44 +473,7 @@
/// Parse an `application/x-www-form-urlencoded` body into its fields.
fn form(body: &[u8]) -> HashMap<String, String> {
- let text = String::from_utf8_lossy(body);
- text.split('&')
- .filter_map(|pair| {
- let (key, value) = pair.split_once('=')?;
- Some((percent_decode(key), percent_decode(value)))
- })
- .collect()
-}
-
-/// Decode one form field: `+` to space and `%XX` to its byte.
-fn percent_decode(input: &str) -> String {
- let spaced = input.replace('+', " ");
- let mut parts = spaced.split('%');
- let mut out: Vec<u8> = parts.next().unwrap_or_default().as_bytes().to_vec();
- for part in parts {
- let bytes = part.as_bytes();
- match (
- bytes.first().copied().and_then(hex_value),
- bytes.get(1).copied().and_then(hex_value),
- ) {
- (Some(hi), Some(lo)) => {
- out.push((hi << 4) | lo);
- out.extend_from_slice(part.get(2..).unwrap_or_default().as_bytes());
- }
- _ => {
- out.push(b'%');
- out.extend_from_slice(bytes);
- }
- }
- }
- String::from_utf8_lossy(&out).into_owned()
-}
-
-/// A single hex digit's value, `0..=15`.
-fn hex_value(byte: u8) -> Option<u8> {
- (byte as char)
- .to_digit(16)
- .and_then(|d| u8::try_from(d).ok())
+ form_urlencoded::parse(body).into_owned().collect()
}
#[cfg(test)]