1use std::io::{self, Read, Write};
5
6use super::ParseError;
7use crate::header::Header;
8
9pub struct ChunkedReader<R: Read> {
16 inner: R,
17 remaining: u64,
19 done: bool,
21 pub trailers: Header,
23}
24
25impl<R: Read> ChunkedReader<R> {
26 pub fn new(inner: R) -> Self {
27 Self {
28 inner,
29 remaining: 0,
30 done: false,
31 trailers: Header::new(),
32 }
33 }
34
35 pub fn into_inner(self) -> R {
37 self.inner
38 }
39
40 fn read_chunk_size(&mut self) -> Result<u64, ParseError> {
41 let mut line = Vec::with_capacity(32);
42 let mut buf = [0u8; 1];
43 loop {
44 self.inner
45 .read_exact(&mut buf)
46 .map_err(|_| ParseError::UnexpectedEof)?;
47 if buf[0] == b'\n' {
48 break;
49 }
50 if buf[0] != b'\r' {
51 line.push(buf[0]);
52 }
53 }
54 let s = std::str::from_utf8(&line)
56 .map_err(|_| ParseError::InvalidChunkSize)?
57 .split(';')
58 .next()
59 .unwrap_or("")
60 .trim();
61 u64::from_str_radix(s, 16).map_err(|_| ParseError::InvalidChunkSize)
62 }
63
64 fn consume_crlf(&mut self) -> Result<(), ParseError> {
65 let mut buf = [0u8; 2];
66 self.inner
67 .read_exact(&mut buf)
68 .map_err(|_| ParseError::UnexpectedEof)?;
69 if buf != *b"\r\n" {
70 return Err(ParseError::Other("expected CRLF after chunk data".into()));
71 }
72 Ok(())
73 }
74}
75
76impl<R: Read> Read for ChunkedReader<R> {
77 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
78 if self.done {
79 return Ok(0);
80 }
81
82 loop {
83 if self.remaining > 0 {
84 let to_read = buf.len().min(self.remaining as usize);
85 let n = self.inner.read(&mut buf[..to_read])?;
86 if n == 0 {
87 return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "chunked body truncated"));
88 }
89 self.remaining -= n as u64;
90 if self.remaining == 0 {
91 self.consume_crlf()
93 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
94 }
95 return Ok(n);
96 }
97
98 let size = self.read_chunk_size()
100 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
101
102 if size == 0 {
103 self.trailers = super::read_headers(&mut self.inner, 4096)
105 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
106 self.done = true;
107 return Ok(0);
108 }
109
110 self.remaining = size;
111 }
112 }
113}
114
115pub struct ChunkedWriter<W: Write> {
122 inner: W,
123}
124
125impl<W: Write> ChunkedWriter<W> {
126 pub fn new(inner: W) -> Self {
127 Self { inner }
128 }
129
130 pub fn finish(mut self) -> io::Result<W> {
132 self.inner.write_all(b"0\r\n\r\n")?;
133 self.inner.flush()?;
134 Ok(self.inner)
135 }
136}
137
138impl<W: Write> Write for ChunkedWriter<W> {
139 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
140 if buf.is_empty() {
141 return Ok(0);
142 }
143 write!(self.inner, "{:X}\r\n", buf.len())?;
144 self.inner.write_all(buf)?;
145 self.inner.write_all(b"\r\n")?;
146 Ok(buf.len())
147 }
148
149 fn flush(&mut self) -> io::Result<()> {
150 self.inner.flush()
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use std::io::{Cursor, Read};
158
159 #[test]
160 fn round_trip() {
161 let mut enc = Vec::new();
162 {
163 let mut w = ChunkedWriter::new(&mut enc);
164 w.write_all(b"Hello").unwrap();
165 w.write_all(b", World!").unwrap();
166 w.finish().unwrap();
167 }
168
169 let mut r = ChunkedReader::new(Cursor::new(enc));
170 let mut out = String::new();
171 r.read_to_string(&mut out).unwrap();
172 assert_eq!(out, "Hello, World!");
173 }
174
175 #[test]
176 fn empty_body() {
177 let raw = b"0\r\n\r\n";
179 let mut r = ChunkedReader::new(Cursor::new(raw.as_ref()));
180 let mut out = Vec::new();
181 r.read_to_end(&mut out).unwrap();
182 assert!(out.is_empty());
183 }
184
185 #[test]
186 fn chunk_extensions_ignored() {
187 let raw = b"5;ext=foo\r\nHello\r\n0\r\n\r\n";
189 let mut r = ChunkedReader::new(Cursor::new(raw.as_ref()));
190 let mut out = String::new();
191 r.read_to_string(&mut out).unwrap();
192 assert_eq!(out, "Hello");
193 }
194}