From 3dd43256b89ca0ed481754140a92b6807d7da660 Mon Sep 17 00:00:00 2001 From: OpenTritium Date: Thu, 21 Aug 2025 11:34:32 +0800 Subject: [PATCH 1/2] fix: correct byte range in demo example and adjust body split to ignore CRLF --- examples/demo.rs | 6 +----- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/demo.rs b/examples/demo.rs index e73f62e..4cca75f 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -4,11 +4,7 @@ use http::header::CONTENT_TYPE; async fn main() { const URL: &str = "https://mat1.gtimg.com/pingjs/ext2020/newom/build/static/images/new_logo.png"; let client = reqwest::Client::new(); - let response = client - .get(URL) - .header("Range", "bytes=0-32,64-128") // 请求前500字节 - .send() - .await.unwrap(); + let response = client.get(URL).header("Range", "bytes=0-31,64-127").send().await.unwrap(); let boundary = response .headers() .get(CONTENT_TYPE) diff --git a/src/lib.rs b/src/lib.rs index 1dbf484..4eecedd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -170,7 +170,7 @@ where buf.get(pos..pos + 2) }; let mut split_part = |buf: &mut BytesMut| { - let body = buf.split_to(body_end).freeze(); + let body = buf.split_to(body_end - 2).freeze(); // forget CRLF Part::new(mem::take(headers), body) }; match tail { From 6359bccb0ba9cea9ded5b75e1c25a4b4e502edaf Mon Sep 17 00:00:00 2001 From: OpenTritium Date: Thu, 21 Aug 2025 11:58:40 +0800 Subject: [PATCH 2/2] fix: handle CRLF correctly in multipart body parsing and update tests --- src/lib.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4eecedd..bf9d57b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -170,7 +170,8 @@ where buf.get(pos..pos + 2) }; let mut split_part = |buf: &mut BytesMut| { - let body = buf.split_to(body_end - 2).freeze(); // forget CRLF + println!("{body_end}"); + let body = buf.split_to(body_end.saturating_sub(2)).freeze(); // forget CRLF and handle on empty body Part::new(mem::take(headers), body) }; match tail { @@ -285,7 +286,7 @@ value1\r\n\ // let x = multipart_stream.try_next().await; let part = multipart_stream.try_next().await.unwrap(); assert_eq!(part.headers().get("content-disposition").unwrap(), "form-data; name=\"field1\""); - assert_eq!(part.body(), &Bytes::from_static(b"value1\r\n")); + assert_eq!(part.body(), &Bytes::from_static(b"value1")); // 应该已经到达流的末尾 let result = multipart_stream.try_next().await; @@ -304,7 +305,7 @@ value1\r\n\ Content-Disposition: form-data; name=\"field2\"\r\n\ Content-Type: text/plain\r\n\ \r\n\ -value2 with CRLF\r\n\ +value2 with CRLF\r\n\r\n\ --X-BOUNDARY--\r\n"; // 使用一个很小的块大小来强制测试缓冲逻辑 @@ -315,7 +316,7 @@ value2 with CRLF\r\n\ let part1 = multipart_stream.try_next().await.unwrap(); assert_eq!(part1.headers().get("content-disposition").unwrap(), "form-data; name=\"field1\""); assert!(!part1.headers().contains_key("content-type")); - assert_eq!(part1.body(), &Bytes::from_static(b"value1\r\n")); + assert_eq!(part1.body(), &Bytes::from_static(b"value1")); // 解析第二部分 let part2 = multipart_stream.try_next().await.unwrap(); @@ -346,7 +347,7 @@ value1\r\n\ // let _ = multipart_stream.try_next().await; let part = multipart_stream.try_next().await.unwrap(); assert_eq!(part.headers().get("content-disposition").unwrap(), "form-data; name=\"field1\""); - assert_eq!(part.body(), &Bytes::from_static(b"value1\r\n")); + assert_eq!(part.body(), &Bytes::from_static(b"value1")); // 应该已经到达流的末尾 let result = multipart_stream.try_next().await; @@ -420,7 +421,7 @@ value2\r\n\ let mut multipart_stream = MultipartStream::new(stream, BOUNDARY.as_bytes()); let part1 = multipart_stream.try_next().await.unwrap(); assert_eq!(part1.headers().get("content-disposition").unwrap(), "form-data; name=\"field1\""); - assert_eq!(part1.body(), &Bytes::from_static(b"value1\r\n")); + assert_eq!(part1.body(), &Bytes::from_static(b"value1")); let part2 = multipart_stream.try_next().await.unwrap(); assert_eq!(part2.headers().get("content-disposition").unwrap(), "form-data; name=\"empty_field\""); @@ -428,7 +429,7 @@ value2\r\n\ let part3 = multipart_stream.try_next().await.unwrap(); assert_eq!(part3.headers().get("content-disposition").unwrap(), "form-data; name=\"field2\""); - assert_eq!(part3.body(), &Bytes::from_static(b"value2\r\n")); + assert_eq!(part3.body(), &Bytes::from_static(b"value2")); let result = multipart_stream.try_next().await; assert!(matches!(result, Err(Error::Eof)));