1use std::borrow::Cow;
2
3pub struct Request<'a, B> {
5 pub method: Method<'a>,
6 pub path: Cow<'a, str>,
7 pub headers: Headers<'a>,
8 pub body: B,
9}
10
11pub struct Response<'a, B> {
13 pub code: ResponseCode,
14 pub headers: Headers<'a>,
15 pub body: B,
16}
17
18#[derive(Debug, PartialEq, Eq, Clone)]
19pub struct Method<'a>(pub Cow<'a, str>);
20
21impl Method<'static> {
22 pub const GET: Self = Method(Cow::Borrowed("GET"));
23 pub const HEAD: Self = Method(Cow::Borrowed("HEAD"));
24 pub const POST: Self = Method(Cow::Borrowed("POST"));
25 pub const PUT: Self = Method(Cow::Borrowed("PUT"));
26 pub const PATCH: Self = Method(Cow::Borrowed("PATCH"));
27 pub const CONNECT: Self = Method(Cow::Borrowed("CONNECT"));
28 pub const TRACE: Self = Method(Cow::Borrowed("TRACE"));
29}
30
31pub struct Body<B>(pub(crate) B);
32impl<'a> Request<'a, std::io::Empty> {
35 #[must_use]
36 pub fn new_get(path: &'a str, headers: Headers<'a>) -> Self {
37 Self { method: Method::GET, headers, path: Cow::Borrowed(path), body: std::io::empty() }
38 }
39}
40
41impl<B: std::io::Read> Body<B> {
60 pub fn new(reader: B) -> Self {
61 Self(reader)
62 }
63
64 pub fn raw(self) -> B {
65 self.0
66 }
67
68 pub fn get_reader(self, headers: &Headers) -> ResponseBody<B> {
69 let mut reader = ResponseBody::Base(self.0);
70
71 if let Some(transfer_encoding) = headers.get("Transfer-Encoding") {
72 for part in transfer_encoding.split(',').map(str::trim) {
73 match part {
74 "chunked" => {
75 let buf_reader = std::io::BufReader::new(Box::new(reader));
76 let chunked_reader = chunked::ChunkedReader::new(buf_reader);
77 reader = ResponseBody::Chunked(chunked_reader);
78 }
79 #[cfg(feature = "decompress")]
80 "gzip" => {
81 let gzip_reader = flate2::read::GzDecoder::new(Box::new(reader));
82 reader = ResponseBody::Gzipped(gzip_reader);
83 }
84 part => {
85 eprintln!("Unhandled encoding {part:?}");
86 }
87 }
88 }
89 }
90
91 if let Some(content_encoding) = headers.get("Content-Encoding") {
92 for part in content_encoding.split(',').map(str::trim) {
93 match part {
94 #[cfg(feature = "decompress")]
95 "gzip" => {
96 let gzip_reader = flate2::read::GzDecoder::new(Box::new(reader));
97 reader = ResponseBody::Gzipped(gzip_reader);
98 }
99 part => {
100 eprintln!("Unhandled encoding {part:?}");
101 }
102 }
103 }
104 }
105
106 reader
107 }
108}
109
110#[derive(PartialEq, Eq, Clone, Copy, Debug)]
111pub struct ResponseCode(pub u16);
112
113impl ResponseCode {
114 pub const CONTINUE: ResponseCode = ResponseCode(100);
115 pub const SWITCHING_PROTOCOLS: ResponseCode = ResponseCode(101);
116 pub const PROCESSING: ResponseCode = ResponseCode(102);
117 pub const EARLY_HINTS: ResponseCode = ResponseCode(103);
118
119 pub const OK: ResponseCode = ResponseCode(200);
120 pub const CREATED: ResponseCode = ResponseCode(201);
121 pub const ACCEPTED: ResponseCode = ResponseCode(202);
122 pub const NON_AUTHORITATIVE_INFORMATION: ResponseCode = ResponseCode(203);
123 pub const NO_CONTENT: ResponseCode = ResponseCode(204);
124 pub const RESET_CONTENT: ResponseCode = ResponseCode(205);
125 pub const PARTIAL_CONTENT: ResponseCode = ResponseCode(206);
126 pub const MULTI_STATUS: ResponseCode = ResponseCode(207);
127 pub const ALREADY_REPORTED: ResponseCode = ResponseCode(208);
128 pub const IM_USED: ResponseCode = ResponseCode(226);
129
130 pub const MULTIPLE_CHOICES: ResponseCode = ResponseCode(300);
131 pub const MOVED_PERMANENTLY: ResponseCode = ResponseCode(301);
132 pub const FOUND: ResponseCode = ResponseCode(302);
133 pub const SEE_OTHER: ResponseCode = ResponseCode(303);
134 pub const NOT_MODIFIED: ResponseCode = ResponseCode(304);
135 pub const TEMPORARY_REDIRECT: ResponseCode = ResponseCode(307);
136 pub const PERMANENT_REDIRECT: ResponseCode = ResponseCode(308);
137
138 pub const BAD_REQUEST: ResponseCode = ResponseCode(400);
139 pub const UNAUTHORIZED: ResponseCode = ResponseCode(401);
140 pub const PAYMENT_REQUIRED: ResponseCode = ResponseCode(402);
141 pub const FORBIDDEN: ResponseCode = ResponseCode(403);
142 pub const NOT_FOUND: ResponseCode = ResponseCode(404);
143 pub const METHOD_NOT_ALLOWED: ResponseCode = ResponseCode(405);
144 pub const NOT_ACCEPTABLE: ResponseCode = ResponseCode(406);
145 pub const PROXY_AUTHENTICATION_REQUIRED: ResponseCode = ResponseCode(407);
146 pub const REQUEST_TIMEOUT: ResponseCode = ResponseCode(408);
147 pub const CONFLICT: ResponseCode = ResponseCode(409);
148 pub const GONE: ResponseCode = ResponseCode(410);
149 pub const LENGTH_REQUIRED: ResponseCode = ResponseCode(411);
150 pub const PRECONDITION_FAILED: ResponseCode = ResponseCode(412);
151 pub const CONTENT_TOO_LARGE: ResponseCode = ResponseCode(413);
152 pub const URI_TOO_LONG: ResponseCode = ResponseCode(414);
153 pub const UNSUPPORTED_MEDIA_TYPE: ResponseCode = ResponseCode(415);
154 pub const RANGE_NOT_SATISFIABLE: ResponseCode = ResponseCode(416);
155 pub const EXPECTATION_FAILED: ResponseCode = ResponseCode(417);
156 pub const IM_A_TEAPOT: ResponseCode = ResponseCode(418);
157 pub const MISDIRECTED_REQUEST: ResponseCode = ResponseCode(421);
158 pub const UNPROCESSABLE_CONTENT: ResponseCode = ResponseCode(422);
159 pub const LOCKED: ResponseCode = ResponseCode(423);
160 pub const FAILED_DEPENDENCY: ResponseCode = ResponseCode(424);
161 pub const TOO_EARLY: ResponseCode = ResponseCode(425);
162 pub const UPGRADE_REQUIRED: ResponseCode = ResponseCode(426);
163 pub const PRECONDITION_REQUIRED: ResponseCode = ResponseCode(428);
164 pub const TOO_MANY_REQUESTS: ResponseCode = ResponseCode(429);
165 pub const REQUEST_HEADER_FIELDS_TOO_LARGE: ResponseCode = ResponseCode(431);
166 pub const UNAVAILABLE_FOR_LEGAL_REASONS: ResponseCode = ResponseCode(451);
167
168 pub const INTERNAL_SERVER_ERROR: ResponseCode = ResponseCode(500);
169 pub const NOT_IMPLEMENTED: ResponseCode = ResponseCode(501);
170 pub const BAD_GATEWAY: ResponseCode = ResponseCode(502);
171 pub const SERVICE_UNAVAILABLE: ResponseCode = ResponseCode(503);
172 pub const GATEWAY_TIMEOUT: ResponseCode = ResponseCode(504);
173 pub const HTTP_VERSION_NOT_SUPPORTED: ResponseCode = ResponseCode(505);
174 pub const VARIANT_ALSO_NEGOTIATES: ResponseCode = ResponseCode(506);
175 pub const INSUFFICIENT_STORAGE: ResponseCode = ResponseCode(507);
176 pub const LOOP_DETECTED: ResponseCode = ResponseCode(508);
177 pub const NOT_EXTENDED: ResponseCode = ResponseCode(510);
178 pub const NETWORK_AUTHENTICATION_REQUIRED: ResponseCode = ResponseCode(511);
179
180 pub fn from_line(item: &str) -> Result<Self, &str> {
184 Ok(match item {
185 "100 Continue" => Self::CONTINUE,
186 "101 Switching Protocols" => Self::SWITCHING_PROTOCOLS,
187 "102 Processing" => Self::PROCESSING,
188 "103 Early Hints" => Self::EARLY_HINTS,
189 "200 OK" => Self::OK,
190 "201 Created" => Self::CREATED,
191 "202 Accepted" => Self::ACCEPTED,
192 "203 Non-Authoritative Information" => Self::NON_AUTHORITATIVE_INFORMATION,
193 "204 No Content" => Self::NO_CONTENT,
194 "205 Reset Content" => Self::RESET_CONTENT,
195 "206 Partial Content" => Self::PARTIAL_CONTENT,
196 "207 Multi-Status" => Self::MULTI_STATUS,
197 "208 Already Reported" => Self::ALREADY_REPORTED,
198 "226 IM Used" => Self::IM_USED,
199 "300 Multiple Choices" => Self::MULTIPLE_CHOICES,
200 "301 Moved Permanently" => Self::MOVED_PERMANENTLY,
201 "302 Found" => Self::FOUND,
202 "303 See Other" => Self::SEE_OTHER,
203 "304 Not Modified" => Self::NOT_MODIFIED,
204 "307 Temporary Redirect" => Self::TEMPORARY_REDIRECT,
205 "308 Permanent Redirect" => Self::PERMANENT_REDIRECT,
206 "400 Bad Request" => Self::BAD_REQUEST,
207 "401 Unauthorized" => Self::UNAUTHORIZED,
208 "402 Payment Required" => Self::PAYMENT_REQUIRED,
209 "403 Forbidden" => Self::FORBIDDEN,
210 "404 Not Found" => Self::NOT_FOUND,
211 "405 Method Not Allowed" => Self::METHOD_NOT_ALLOWED,
212 "406 Not Acceptable" => Self::NOT_ACCEPTABLE,
213 "407 Proxy Authentication Required" => Self::PROXY_AUTHENTICATION_REQUIRED,
214 "408 Request Timeout" => Self::REQUEST_TIMEOUT,
215 "409 Conflict" => Self::CONFLICT,
216 "410 Gone" => Self::GONE,
217 "411 Length Required" => Self::LENGTH_REQUIRED,
218 "412 Precondition Failed" => Self::PRECONDITION_FAILED,
219 "413 Content Too Large" => Self::CONTENT_TOO_LARGE,
220 "414 URI Too Long" => Self::URI_TOO_LONG,
221 "415 Unsupported Media Type" => Self::UNSUPPORTED_MEDIA_TYPE,
222 "416 Range Not Satisfiable" => Self::RANGE_NOT_SATISFIABLE,
223 "417 Expectation Failed" => Self::EXPECTATION_FAILED,
224 "418 I'm a teapot" => Self::IM_A_TEAPOT,
225 "421 Misdirected Request" => Self::MISDIRECTED_REQUEST,
226 "422 Unprocessable Content" => Self::UNPROCESSABLE_CONTENT,
227 "423 Locked" => Self::LOCKED,
228 "424 Failed Dependency" => Self::FAILED_DEPENDENCY,
229 "425 Too Early" => Self::TOO_EARLY,
230 "426 Upgrade Required" => Self::UPGRADE_REQUIRED,
231 "428 Precondition Required" => Self::PRECONDITION_REQUIRED,
232 "429 Too Many Requests" => Self::TOO_MANY_REQUESTS,
233 "431 Request Header Fields Too Large" => Self::REQUEST_HEADER_FIELDS_TOO_LARGE,
234 "451 Unavailable For Legal Reasons" => Self::UNAVAILABLE_FOR_LEGAL_REASONS,
235 "500 Internal Server Error" => Self::INTERNAL_SERVER_ERROR,
236 "501 Not Implemented" => Self::NOT_IMPLEMENTED,
237 "502 Bad Gateway" => Self::BAD_GATEWAY,
238 "503 Service Unavailable" => Self::SERVICE_UNAVAILABLE,
239 "504 Gateway Timeout" => Self::GATEWAY_TIMEOUT,
240 "505 HTTP Version Not Supported" => Self::HTTP_VERSION_NOT_SUPPORTED,
241 "506 Variant Also Negotiates" => Self::VARIANT_ALSO_NEGOTIATES,
242 "507 Insufficient Storage" => Self::INSUFFICIENT_STORAGE,
243 "508 Loop Detected" => Self::LOOP_DETECTED,
244 "510 Not Extended" => Self::NOT_EXTENDED,
245 "511 Network Authentication Required" => Self::NETWORK_AUTHENTICATION_REQUIRED,
246 item => {
247 if let Some(value) =
249 item.split_once(' ').and_then(|(lhs, _)| lhs.parse::<u16>().ok())
250 {
251 Self(value)
252 } else {
253 return Err(item);
254 }
255 }
256 })
257 }
258
259 #[must_use]
263 pub fn to_str(&self) -> &str {
264 match *self {
265 Self::CONTINUE => "100 Continue",
266 Self::SWITCHING_PROTOCOLS => "101 Switching Protocols",
267 Self::PROCESSING => "102 Processing",
268 Self::EARLY_HINTS => "103 Early Hints",
269 Self::OK => "200 OK",
270 Self::CREATED => "201 Created",
271 Self::ACCEPTED => "202 Accepted",
272 Self::NON_AUTHORITATIVE_INFORMATION => "203 Non-Authoritative Information",
273 Self::NO_CONTENT => "204 No Content",
274 Self::RESET_CONTENT => "205 Reset Content",
275 Self::PARTIAL_CONTENT => "206 Partial Content",
276 Self::MULTI_STATUS => "207 Multi-Status",
277 Self::ALREADY_REPORTED => "208 Already Reported",
278 Self::IM_USED => "226 IM Used",
279 Self::MULTIPLE_CHOICES => "300 Multiple Choices",
280 Self::MOVED_PERMANENTLY => "301 Moved Permanently",
281 Self::FOUND => "302 Found",
282 Self::SEE_OTHER => "303 See Other",
283 Self::NOT_MODIFIED => "304 Not Modified",
284 Self::TEMPORARY_REDIRECT => "307 Temporary Redirect",
285 Self::PERMANENT_REDIRECT => "308 Permanent Redirect",
286 Self::BAD_REQUEST => "400 Bad Request",
287 Self::UNAUTHORIZED => "401 Unauthorized",
288 Self::PAYMENT_REQUIRED => "402 Payment Required",
289 Self::FORBIDDEN => "403 Forbidden",
290 Self::NOT_FOUND => "404 Not Found",
291 Self::METHOD_NOT_ALLOWED => "405 Method Not Allowed",
292 Self::NOT_ACCEPTABLE => "406 Not Acceptable",
293 Self::PROXY_AUTHENTICATION_REQUIRED => "407 Proxy Authentication Required",
294 Self::REQUEST_TIMEOUT => "408 Request Timeout",
295 Self::CONFLICT => "409 Conflict",
296 Self::GONE => "410 Gone",
297 Self::LENGTH_REQUIRED => "411 Length Required",
298 Self::PRECONDITION_FAILED => "412 Precondition Failed",
299 Self::CONTENT_TOO_LARGE => "413 Content Too Large",
300 Self::URI_TOO_LONG => "414 URI Too Long",
301 Self::UNSUPPORTED_MEDIA_TYPE => "415 Unsupported Media Type",
302 Self::RANGE_NOT_SATISFIABLE => "416 Range Not Satisfiable",
303 Self::EXPECTATION_FAILED => "417 Expectation Failed",
304 Self::IM_A_TEAPOT => "418 I'm a teapot",
305 Self::MISDIRECTED_REQUEST => "421 Misdirected Request",
306 Self::UNPROCESSABLE_CONTENT => "422 Unprocessable Content",
307 Self::LOCKED => "423 Locked",
308 Self::FAILED_DEPENDENCY => "424 Failed Dependency",
309 Self::TOO_EARLY => "425 Too Early",
310 Self::UPGRADE_REQUIRED => "426 Upgrade Required",
311 Self::PRECONDITION_REQUIRED => "428 Precondition Required",
312 Self::TOO_MANY_REQUESTS => "429 Too Many Requests",
313 Self::REQUEST_HEADER_FIELDS_TOO_LARGE => "431 Request Header Fields Too Large",
314 Self::UNAVAILABLE_FOR_LEGAL_REASONS => "451 Unavailable For Legal Reasons",
315 Self::INTERNAL_SERVER_ERROR => "500 Internal Server Error",
316 Self::NOT_IMPLEMENTED => "501 Not Implemented",
317 Self::BAD_GATEWAY => "502 Bad Gateway",
318 Self::SERVICE_UNAVAILABLE => "503 Service Unavailable",
319 Self::GATEWAY_TIMEOUT => "504 Gateway Timeout",
320 Self::HTTP_VERSION_NOT_SUPPORTED => "505 HTTP Version Not Supported",
321 Self::VARIANT_ALSO_NEGOTIATES => "506 Variant Also Negotiates",
322 Self::INSUFFICIENT_STORAGE => "507 Insufficient Storage",
323 Self::LOOP_DETECTED => "508 Loop Detected",
324 Self::NOT_EXTENDED => "510 Not Extended",
325 Self::NETWORK_AUTHENTICATION_REQUIRED => "511 Network Authentication Required",
326 item => {
327 todo!("custom code for {item:?}")
328 }
329 }
330 }
331}
332
333impl<B: std::io::Read> Response<'_, Body<B>> {
334 pub fn debug(self) -> impl std::fmt::Debug {
336 let body = {
337 use std::io::Read;
338
339 let mut body = String::new();
341 let mut reader = self.body.get_reader(&self.headers);
342 reader.read_to_string(&mut body).unwrap();
343 body
344 };
345
346 std::fmt::from_fn(move |fmt| {
347 let mut fmt = fmt.debug_struct("Response");
348 fmt.field("code", &self.code);
349 fmt.field("headers", &self.headers);
350 fmt.field("body", &body);
351 fmt.finish()
352 })
353 }
354}
355
356#[derive(Clone)]
357pub struct Headers<'a>(pub Cow<'a, str>);
358
359impl Headers<'static> {
360 #[must_use]
361 pub fn empty() -> Headers<'static> {
362 Headers(Cow::Borrowed(""))
363 }
364
365 pub fn from_raw(on: Vec<u8>) -> Result<Headers<'static>, std::string::FromUtf8Error> {
369 String::from_utf8(on).map(Headers::from_string)
370 }
371
372 #[must_use]
373 pub fn from_string(on: String) -> Headers<'static> {
374 Headers(Cow::Owned(on))
375 }
376}
377
378impl Headers<'_> {
379 #[must_use]
380 pub fn iter(&self) -> HeaderIter<'_> {
381 HeaderIter(self.0.lines())
382 }
383
384 #[must_use]
385 pub fn is_empty(&self) -> bool {
386 self.0.is_empty()
387 }
388
389 #[must_use]
390 pub fn is_valid(&self) -> bool {
391 self.is_empty() || self.0.ends_with("\r\n")
392 }
393
394 #[must_use]
395 pub fn get(&self, key: &str) -> Option<&str> {
396 self.iter().find_map(|(key2, value)| (key.eq_ignore_ascii_case(key2)).then_some(value))
397 }
398
399 pub fn set(&mut self, key: &str, value: &str) {
400 let mut position = None;
401 let this: &str = &self.0;
402 let offset = this.as_ptr() as usize;
403 for line in this.lines() {
404 if let Some((key2, _)) = line.split_once(':')
405 && key.eq_ignore_ascii_case(key2)
406 {
407 let start = line.as_ptr() as usize - offset;
408 position = Some((start, start + line.len() + 2));
409 break;
410 }
411 }
412 if let Some((start, end)) = position {
413 let buf = self.0.to_mut();
414 buf.drain(start..end);
415 }
416 self.append(key, value);
417 }
418
419 pub fn append(&mut self, key: &str, value: &str) {
420 let buf = self.0.to_mut();
421 buf.push_str(key);
422 buf.push_str(": ");
423 buf.push_str(value);
424 buf.push_str("\r\n");
425 }
426
427 pub fn delete(&mut self, key: &str) {
428 let mut position = None;
429 let this: &str = &self.0;
430 let offset = this.as_ptr() as usize;
431 for line in this.lines() {
432 if let Some((key2, _)) = line.split_once(':')
433 && key.eq_ignore_ascii_case(key2)
434 {
435 let start = line.as_ptr() as usize - offset;
436 position = Some((start, start + line.len() + 2));
437 break;
438 }
439 }
440 if let Some((start, end)) = position {
441 let buf = self.0.to_mut();
442 buf.drain(start..end);
443 }
444 }
445}
446
447impl std::fmt::Debug for Headers<'_> {
448 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449 let mut fmt = fmt.debug_struct("Headers");
450 for (key, value) in self {
451 fmt.field(key, &value);
452 }
453 fmt.finish()
454 }
455}
456
457impl<T: AsRef<str>> FromIterator<(T, T)> for Headers<'static> {
458 fn from_iter<I: IntoIterator<Item = (T, T)>>(iter: I) -> Self {
459 let mut buf = String::new();
460 for (key, value) in iter {
461 buf.push_str(key.as_ref());
462 buf.push_str(": ");
463 buf.push_str(value.as_ref());
464 buf.push_str("\r\n");
465 }
466 Self::from_string(buf)
467 }
468}
469
470impl<'a> IntoIterator for &'a Headers<'_> {
471 type Item = (&'a str, &'a str);
472 type IntoIter = HeaderIter<'a>;
473
474 fn into_iter(self) -> Self::IntoIter {
475 HeaderIter(self.0.lines())
476 }
477}
478
479pub struct HeaderIter<'a>(pub(super) std::str::Lines<'a>);
480
481impl<'a> Iterator for HeaderIter<'a> {
482 type Item = (&'a str, &'a str);
483
484 fn next(&mut self) -> Option<Self::Item> {
485 let row = self.0.next()?;
486 let (key, value) = row.split_once(':')?;
488 Some((key, value.trim()))
489 }
490}
491
492pub mod chunked {
493 pub struct ChunkedReader<T> {
494 reader: T,
495 to_read: usize,
496 }
497
498 impl<T: std::io::Read> ChunkedReader<T> {
499 pub fn new(reader: T) -> Self {
500 Self { reader, to_read: 0 }
501 }
502
503 pub fn into_inner(self) -> T {
504 self.reader
505 }
506 }
507
508 impl<T: std::io::BufRead> std::io::Read for ChunkedReader<T> {
509 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
510 if self.to_read == 0 {
511 let mut chunk_size_buf = String::new();
512 self.reader.read_line(&mut chunk_size_buf)?;
513
514 debug_assert!(chunk_size_buf.ends_with("\r\n"));
515 let chunk_size_str = chunk_size_buf.trim_end();
516 let hex = u64::from_str_radix(chunk_size_str, 16);
517 let Ok(chunk_size) = hex else {
518 let message = format!("invalid chunk length {chunk_size_str:?}");
519 let error = std::io::Error::new(std::io::ErrorKind::InvalidData, message);
520 return Err(error);
521 };
522
523 if chunk_size == 0 {
525 return Ok(0);
526 }
527
528 let chunk_size = usize::try_from(chunk_size).unwrap_or(usize::MAX);
530
531 self.to_read = chunk_size;
532 }
533
534 let mut reader_over_chunk = self.reader.by_ref().take(self.to_read as u64);
535 let bytes_tranferred = reader_over_chunk.read(buf)?;
536 self.to_read -= bytes_tranferred;
537
538 if self.to_read == 0 {
539 let mut end: [u8; 2] = [0, 0];
540 self.reader.read_exact(&mut end)?;
541
542 if &end != b"\r\n" {
544 let message = "expected '\r\n' at end of chunked frame";
545 let error = std::io::Error::new(std::io::ErrorKind::InvalidData, message);
546 return Err(error);
547 }
548 }
549
550 Ok(bytes_tranferred)
551 }
552 }
553
554 pub struct ChunkedIterator<I> {
577 iter: I,
578 buf: Vec<u8>,
579 finished: bool,
580 }
581
582 impl<I> ChunkedIterator<I> {
583 pub fn new(iter: I) -> Self {
584 Self { iter, buf: Vec::new(), finished: false }
585 }
586 }
587
588 impl<I: Iterator<Item = Vec<u8>>> std::io::Read for ChunkedIterator<I> {
591 fn read(&mut self, write_to: &mut [u8]) -> std::io::Result<usize> {
592 use std::io::Write;
593
594 let mut written = 0;
595 if !self.buf.is_empty() {
596 let to_remove = std::cmp::min(self.buf.len(), write_to.len());
597 for value in self.buf.drain(..to_remove) {
598 write_to[written] = value;
599 written += 1;
600 }
601 }
602
603 if let Some(remaining) = write_to.len().checked_sub(written)
605 && remaining > 2
606 && !self.finished
607 {
608 let next = self.iter.next();
610 self.finished = next.is_none();
611 let mut next = next.unwrap_or_default();
612 let data_len = next.len();
614 let mut chunk = Vec::new();
615 write!(&mut chunk, "{data_len:x}\r\n").unwrap();
616 chunk.append(&mut next);
617 write!(&mut chunk, "\r\n").unwrap();
618 let to_add = chunk.drain(..std::cmp::min(chunk.len(), remaining));
620 for value in to_add {
621 write_to[written] = value;
622 written += 1;
623 }
624 self.buf = chunk;
625 }
626
627 Ok(written)
628 }
629 }
630}
631
632pub enum ResponseBody<T> {
635 Base(T),
636 Chunked(chunked::ChunkedReader<std::io::BufReader<Box<ResponseBody<T>>>>),
639 #[cfg(feature = "decompress")]
640 Gzipped(flate2::read::GzDecoder<Box<ResponseBody<T>>>),
641 #[cfg(feature = "decompress")]
642 Deflate(flate2::read::DeflateDecoder<Box<ResponseBody<T>>>),
643}
644
645impl<B: std::io::Read> ResponseBody<B> {
646 #[must_use]
647 pub fn into_inner(self) -> Self {
648 match self {
649 ResponseBody::Base(ref _inner) => self,
651 ResponseBody::Chunked(inner) => *inner.into_inner().into_inner(),
654 #[cfg(feature = "decompress")]
655 ResponseBody::Gzipped(inner) => inner.into_inner().into_inner(),
656 #[cfg(feature = "decompress")]
657 ResponseBody::Deflate(inner) => inner.into_inner().into_inner(),
658 }
659 }
660}
661
662impl<B: std::io::Read> std::io::Read for ResponseBody<B> {
663 fn read(&mut self, into: &mut [u8]) -> std::io::Result<usize> {
665 match self {
666 Self::Base(inner) => std::io::Read::read(inner, into),
668 Self::Chunked(inner) => std::io::Read::read(inner, into),
669 #[cfg(feature = "decompress")]
672 Self::Gzipped(inner) => std::io::Read::read(inner, into),
673 #[cfg(feature = "decompress")]
674 Self::Deflate(inner) => std::io::Read::read(inner, into),
675 }
676 }
677}