Skip to main content

mashrl/
http.rs

1use std::borrow::Cow;
2
3/// Expecting [Response]. Lifetime for using current program data in generation (not result).
4pub 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
11/// The out of a [Request]. Lifetime for using current program data in generation (not result).
12pub 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);
32// pub struct Body<B>(pub(crate) B);
33
34impl<'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
41// TODO add
42// impl<'a> Request<'a, std::io::Cursor<>> {
43// 	pub fn new_post(path: &'a str, headers: Headers<'a>) -> Self {
44// 		Self {
45// 			method: Method::GET,
46// 			headers: headers,
47// 			path: Cow::Borrowed(path),
48// 			body: std::io::empty()
49// 		}
50// 	}
51// }
52
53// impl<B: std::io::Read + 'static> Body<B> {
54//     pub fn into_dyn(self) -> Body<Box<dyn std::io::Read>> {
55//         Body(Box::new(self.0))
56//     }
57// }
58
59impl<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	/// # Errors
181	///
182	/// returns an `Err` if the input is not a known HTTP response code
183	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				// Custom status code
248				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	/// # Panics
260	///
261	/// TODO Panics on custom HTTP codes?
262	#[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	/// seperate method because it takes ownership of reading
335	pub fn debug(self) -> impl std::fmt::Debug {
336		let body = {
337			use std::io::Read;
338
339			// TODO more here
340			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	/// # Errors
366	///
367	/// returns an `Err` if the input is not `utf8`
368	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		// TODO what if `None` here?
487		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				// The terminating chunk is a zero-length chunk
524				if chunk_size == 0 {
525					return Ok(0);
526				}
527
528				// Convert u64 -> usize ?
529				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				// #[cfg(debug_assertions)]
543				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 ChunkedWriter<T> {
555	//     writer: T
556	// }
557
558	// impl<T: std::io::Write> ChunkedWriter<T> {
559	// 	pub fn new(writer: T) -> Self {
560	// 		Self { writer }
561	// 	}
562	// }
563
564	// impl<T: std::io::Write> std::io::Write for ChunkedWriter<T> {
565	// 	fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
566	// 		write!(&mut self.writer, "{len:x}\r\n", len=buf.len())?;
567	// 		write!(&mut self.writer, "{buf}")
568	// 	}
569
570	//     fn flush(&mut self) -> std::io::Result<()> {
571	// 		self.writer.flush()
572	// 	}
573	// }
574	//
575
576	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	// TODO cow
589	// TODO part sizes
590	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			// TODO 2
604			if let Some(remaining) = write_to.len().checked_sub(written)
605				&& remaining > 2
606				&& !self.finished
607			{
608				// Last is zero
609				let next = self.iter.next();
610				self.finished = next.is_none();
611				let mut next = next.unwrap_or_default();
612				// TODO faster
613				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				// TODO
619				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
632// TODO brotli?
633/// Processed body
634pub enum ResponseBody<T> {
635	Base(T),
636	// Limited(std::io::Take<InitialBody>),
637	/// TODO just `B` rather `ResponseBody`
638	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::Empty => self,
650			ResponseBody::Base(ref _inner) => self,
651			// ResponseBody::Limited(ref _inner) => self,
652			// ResponseBody::Custom(ref _inner) => self,
653			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	// impl std::io::Read for ResponseBody {
664	fn read(&mut self, into: &mut [u8]) -> std::io::Result<usize> {
665		match self {
666			// Self::Empty => Ok(0),
667			Self::Base(inner) => std::io::Read::read(inner, into),
668			Self::Chunked(inner) => std::io::Read::read(inner, into),
669			// Self::Limited(inner) => std::io::Read::read(inner, into),
670			// Self::Custom(inner) => std::io::Read::read(inner, into),
671			#[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}