Skip to content

Commit

Permalink
Fix read_to_end with chunked encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
bugadani committed Nov 11, 2023
1 parent 0341925 commit 7e6d7bf
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 8 deletions.
10 changes: 5 additions & 5 deletions src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use embedded_io_async::{BufRead, Read};

use crate::TryBufRead;

struct ReadBuffer<'buf> {
buffer: &'buf mut [u8],
loaded: usize,
pub struct ReadBuffer<'buf> {
pub buffer: &'buf mut [u8],
pub loaded: usize,
}

impl<'buf> ReadBuffer<'buf> {
Expand Down Expand Up @@ -46,8 +46,8 @@ pub struct BufferingReader<'resp, 'buf, B>
where
B: Read,
{
buffer: ReadBuffer<'buf>,
stream: &'resp mut B,
pub buffer: ReadBuffer<'buf>,
pub stream: &'resp mut B,
}

impl<'resp, 'buf, B> BufferingReader<'resp, 'buf, B>
Expand Down
57 changes: 55 additions & 2 deletions src/response/chunked.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use embedded_io_async::{BufRead, Error as _, ErrorType, Read};

use crate::Error;
use crate::{
reader::{BufferingReader, ReadBuffer},
Error, TryBufRead,
};

#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum ChunkState {
NoChunk,
NotEmpty(u32),
Expand Down Expand Up @@ -133,6 +136,56 @@ where
}
}

impl<'conn, 'buf, C> ChunkedBodyReader<BufferingReader<'conn, 'buf, C>>
where
C: Read + TryBufRead,
{
pub(crate) async fn read_to_end(self) -> Result<&'buf mut [u8], Error> {
let buffer = self.raw_body.buffer.buffer;

// We reconstruct the reader to change the 'buf lifetime.
let mut reader = ChunkedBodyReader {
raw_body: BufferingReader {
buffer: ReadBuffer {
buffer: &mut buffer[..],
loaded: self.raw_body.buffer.loaded,
},
stream: self.raw_body.stream,
},
chunk_remaining: self.chunk_remaining,
};

let mut len = 0;
while len < reader.raw_body.buffer.buffer.len() {
// Read some
let read = reader.fill_buf().await?.len();
len += read;

// Make sure we don't erase the newly read data
let was_loaded = reader.raw_body.buffer.loaded;
let fake_loaded = read.min(was_loaded);
reader.raw_body.buffer.loaded = fake_loaded;

// Consume the returned buffer
reader.consume(read);

if reader.is_done() {
// If we're done, we don't care about the rest of the housekeeping.
break;
}

// How many bytes were actually consumed from the preloaded buffer?
let consumed_from_buffer = fake_loaded - reader.raw_body.buffer.loaded;

// ... move the buffer by that many bytes to avoid overwriting in the next iteration.
reader.raw_body.buffer.loaded = was_loaded - consumed_from_buffer;
reader.raw_body.buffer.buffer = &mut reader.raw_body.buffer.buffer[consumed_from_buffer..];
}

Ok(&mut buffer[..len])
}
}

impl<C> ErrorType for ChunkedBodyReader<C> {
type Error = Error;
}
Expand Down
6 changes: 5 additions & 1 deletion src/response/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,10 @@ where

Ok(&mut self.body_buf[..content_length])
}
ReaderHint::Chunked => Err(Error::Codec),
ReaderHint::Chunked => {
let raw_body = BufferingReader::new(self.body_buf, self.raw_body_read, self.conn);
ChunkedBodyReader::new(raw_body).read_to_end().await
}
ReaderHint::ToEnd => {
let mut body_len = self.raw_body_read;
loop {
Expand Down Expand Up @@ -596,6 +599,7 @@ mod tests {
let mut conn = FakeSingleReadConnection::new(
b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHELLO\r\n6\r\n WORLD\r\n0\r\n\r\n",
);
conn.read_length = 10;
let mut header_buf = [0; 200];
let response = Response::read(&mut conn, Method::GET, &mut header_buf).await.unwrap();

Expand Down

0 comments on commit 7e6d7bf

Please sign in to comment.