fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
if needle.is_empty() {
return Some(0);
}
haystack.windows(needle.len()).position(|w| w == needle)
}
pub fn scan_multipart_field<'a>(
bytes: &'a [u8],
boundary: &str,
field_name: &str,
) -> Option<&'a str> {
let delimiter = format!("--{boundary}");
let delim = delimiter.as_bytes();
let end_marker = format!("\r\n{delimiter}");
let end_bytes = end_marker.as_bytes();
let mut pos = 0;
loop {
let rel = find_bytes(&bytes[pos..], delim)?;
pos += rel + delim.len();
match bytes.get(pos..pos + 2) {
Some(b"\r\n") => pos += 2,
_ => break, }
let header_end = find_bytes(&bytes[pos..], b"\r\n\r\n")?;
let headers = std::str::from_utf8(&bytes[pos..pos + header_end]).ok()?;
let value_start = pos + header_end + 4;
let is_match = headers.lines().any(|line| {
if !line
.to_ascii_lowercase()
.starts_with("content-disposition:")
{
return false;
}
line.split(';').skip(1).any(|attr| {
attr.trim()
.strip_prefix("name=")
.map(|v| v.trim_matches('"'))
== Some(field_name)
})
});
if is_match {
let end = find_bytes(&bytes[value_start..], end_bytes)
.map_or(bytes.len(), |i| value_start + i);
return std::str::from_utf8(&bytes[value_start..end]).ok();
}
let next = find_bytes(&bytes[value_start..], end_bytes)?;
pos = value_start + next + 2;
}
None
}
#[cfg(test)]
mod tests {
use super::*;
fn multipart_body(boundary: &str, fields: &[(&str, &str)]) -> String {
use std::fmt::Write as _;
let mut body = String::new();
for (name, value) in fields {
let _ = write!(
body,
"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n"
);
}
let _ = write!(body, "--{boundary}--\r\n");
body
}
#[test]
fn find_bytes_locates_needle() {
assert_eq!(find_bytes(b"abcdef", b"cd"), Some(2));
}
#[test]
fn find_bytes_returns_none_when_absent() {
assert_eq!(find_bytes(b"abcdef", b"zz"), None);
}
#[test]
fn find_bytes_empty_needle_matches_at_start() {
assert_eq!(find_bytes(b"abcdef", b""), Some(0));
}
#[test]
fn scan_multipart_field_finds_only_field() {
let boundary = "B";
let body = multipart_body(boundary, &[("_csrf", "tok-1")]);
assert_eq!(
scan_multipart_field(body.as_bytes(), boundary, "_csrf"),
Some("tok-1")
);
}
#[test]
fn scan_multipart_field_finds_field_after_other_field() {
let boundary = "B";
let body = multipart_body(boundary, &[("name", "alice"), ("_csrf", "tok-2")]);
assert_eq!(
scan_multipart_field(body.as_bytes(), boundary, "_csrf"),
Some("tok-2")
);
}
#[test]
fn scan_multipart_field_returns_none_when_field_absent() {
let boundary = "B";
let body = multipart_body(boundary, &[("name", "alice")]);
assert_eq!(
scan_multipart_field(body.as_bytes(), boundary, "_csrf"),
None
);
}
#[test]
fn scan_multipart_field_returns_none_on_malformed_body() {
let boundary = "B";
assert_eq!(
scan_multipart_field(b"not a multipart body", boundary, "_csrf"),
None
);
}
#[test]
fn scan_multipart_field_finds_field_as_last_part() {
let boundary = "B";
let body = multipart_body(
boundary,
&[("name", "alice"), ("age", "9"), ("_csrf", "tok-3")],
);
assert_eq!(
scan_multipart_field(body.as_bytes(), boundary, "_csrf"),
Some("tok-3")
);
}
}