From e0c31dce2b5ab91a010e22cfae78f727a89f553e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 2 Jul 2024 07:07:54 +0200 Subject: [PATCH 1/2] Fix issue where writing up to exactly the size of the chunked buffer size causes data loss This fixes an issue where a call to write() with a buffer length exactly matching the remaining size of the remainder of the current chunk causes the entire chunk to be discarded --- src/body_writer/buffering_chunked.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/body_writer/buffering_chunked.rs b/src/body_writer/buffering_chunked.rs index 6100f49..6735870 100644 --- a/src/body_writer/buffering_chunked.rs +++ b/src/body_writer/buffering_chunked.rs @@ -117,6 +117,10 @@ where self.pos = self.header_pos + self.allocated_header; } + fn current_chunk_is_full(&self) -> bool { + self.pos + NEWLINE.len() == self.buf.len() + } + async fn emit_buffered(&mut self) -> Result<(), C::Error> { self.conn.write_all(&self.buf[..self.header_pos]).await?; self.header_pos = 0; @@ -149,7 +153,7 @@ where self.emit_buffered().await.map_err(|e| e.kind())?; written = self.append_current_chunk(buf); } - if written < buf.len() { + if self.current_chunk_is_full() { self.finish_current_chunk(); self.emit_buffered().await.map_err(|e| e.kind())?; } @@ -330,7 +334,7 @@ mod tests { } #[tokio::test] - async fn current_chunk_is_emitted_before_empty_chunk_is_emitted() { + async fn written_bytes_fit_exactly() { // Given let mut conn = Vec::new(); let mut buf = [0; 14]; @@ -338,11 +342,28 @@ mod tests { // When let mut writer = BufferingChunkedBodyWriter::new_with_data(&mut conn, &mut buf, 5); + writer.write_all(b"BODY").await.unwrap(); // Can fit exactly writer.write_all(b"BODY").await.unwrap(); // Can fit + writer.terminate().await.unwrap(); // Can fit + + // Then + assert_eq!(b"HELLO4\r\nBODY\r\n4\r\nBODY\r\n0\r\n\r\n", conn.as_slice()); + } + + #[tokio::test] + async fn current_chunk_is_emitted_on_termination_before_empty_chunk_is_emitted() { + // Given + let mut conn = Vec::new(); + let mut buf = [0; 14]; + buf[..5].copy_from_slice(b"HELLO"); + + // When + let mut writer = BufferingChunkedBodyWriter::new_with_data(&mut conn, &mut buf, 5); + writer.write_all(b"BOD").await.unwrap(); // Can fit writer.terminate().await.unwrap(); // Cannot fit // Then - assert_eq!(b"HELLO4\r\nBODY\r\n0\r\n\r\n", conn.as_slice()); + assert_eq!(b"HELLO3\r\nBOD\r\n0\r\n\r\n", conn.as_slice()); } #[tokio::test] From 508f04dcdabc064769e8f46cf437584d01dda866 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 2 Jul 2024 07:19:23 +0200 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 922fd73..8bdf5c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixes * Fix bug where buffering chunked body writer could return `Ok(0)` on calls to `write()` ([#81](https://github.com/drogue-iot/reqwless/pull/81)) +* Fix bug in buffering chunked body writer where a call to `write()` with a buffer length exactly matching the remaining size of the remainder of the current chunk causes the entire chunk to be discarded ([#85](https://github.com/drogue-iot/reqwless/pull/85)) ## v0.12 (2024-05-23)