1use std::fmt;
2use std::io::Write;
3
4use http::{HeaderMap, HeaderName, HeaderValue, Method, header};
5
6use crate::Error;
7use crate::chunk::Dechunker;
8use crate::util::{Writer, compare_lowercase_ascii, log_data};
9
10#[derive(Debug, Clone, Copy, Default)]
11pub(crate) struct BodyWriter {
12 mode: SenderMode,
13 ended: bool,
14}
15
16#[derive(Debug, Clone, Copy, Default)]
17enum SenderMode {
18 #[default]
19 None,
20 Sized(u64),
21 Chunked,
22}
23
24pub(crate) const DEFAULT_CHUNK_SIZE: usize = 10 * 1024;
26pub(crate) const DEFAULT_CHUNK_OVERHEAD: usize = 4 + 4;
28pub(crate) const DEFAULT_CHUNK_AND_OVERHEAD: usize = DEFAULT_CHUNK_SIZE + DEFAULT_CHUNK_OVERHEAD;
29
30impl BodyWriter {
31 pub fn new_none() -> Self {
32 BodyWriter {
33 mode: SenderMode::None,
34 ended: true,
35 }
36 }
37
38 pub fn new_chunked() -> Self {
39 BodyWriter {
40 mode: SenderMode::Chunked,
41 ended: false,
42 }
43 }
44
45 pub fn new_sized(size: u64) -> Self {
46 BodyWriter {
47 mode: SenderMode::Sized(size),
48 ended: false,
49 }
50 }
51
52 #[cfg(feature = "server")]
53 pub fn body_mode(&self) -> BodyMode {
54 match self.mode {
55 SenderMode::None => BodyMode::NoBody,
56 SenderMode::Sized(n) => BodyMode::LengthDelimited(n),
57 SenderMode::Chunked => BodyMode::Chunked,
58 }
59 }
60
61 pub fn has_body(&self) -> bool {
62 match self.mode {
63 SenderMode::Sized(n) => n > 0,
64 SenderMode::Chunked => true,
65 SenderMode::None => false,
66 }
67 }
68
69 pub fn is_chunked(&self) -> bool {
70 matches!(self.mode, SenderMode::Chunked)
71 }
72
73 pub fn write(&mut self, input: &[u8], w: &mut Writer) -> usize {
74 match &mut self.mode {
75 SenderMode::None => unreachable!(),
76 SenderMode::Sized(left) => {
77 let left_usize = (*left).min(usize::MAX as u64) as usize;
78 let to_write = w.available().min(input.len()).min(left_usize);
79
80 let success = w.try_write(|w| w.write_all(&input[..to_write]));
81 assert!(success);
82
83 *left -= to_write as u64;
84
85 if *left == 0 {
86 self.ended = true;
87 }
88
89 to_write
90 }
91 SenderMode::Chunked => {
92 let mut input_used = 0;
93
94 if input.is_empty() {
95 self.finish(w);
96 self.ended = true;
97 } else {
98 while write_chunk(
101 &input[input_used..],
103 &mut input_used,
104 w,
105 DEFAULT_CHUNK_SIZE,
106 ) {}
107 }
108
109 input_used
110 }
111 }
112 }
113
114 fn finish(&self, w: &mut Writer) -> bool {
115 if self.is_chunked() {
116 let success = w.try_write(|w| w.write_all(b"0\r\n\r\n"));
117 if !success {
118 return false;
119 }
120 }
121 true
122 }
123
124 pub(crate) fn body_header(&self) -> (HeaderName, HeaderValue) {
125 match self.mode {
126 SenderMode::None => unreachable!(),
127 SenderMode::Sized(size) => (
128 header::CONTENT_LENGTH,
129 HeaderValue::from_str(&size.to_string()).unwrap(),
131 ),
132 SenderMode::Chunked => (
133 header::TRANSFER_ENCODING,
134 HeaderValue::from_static("chunked"),
135 ),
136 }
137 }
138
139 pub(crate) fn is_ended(&self) -> bool {
140 self.ended
141 }
142
143 pub(crate) fn left_to_send(&self) -> Option<u64> {
144 match self.mode {
145 SenderMode::Sized(v) => Some(v),
146 _ => None,
147 }
148 }
149
150 pub(crate) fn consume_direct_write(&mut self, amount: usize) {
151 match &mut self.mode {
152 SenderMode::None => unreachable!(),
153 SenderMode::Sized(left) => {
154 *left -= amount as u64;
155
156 if *left == 0 {
157 self.ended = true;
158 }
159 }
160 SenderMode::Chunked => unreachable!(),
161 }
162 }
163}
164
165#[allow(unused)]
166pub(crate) fn calculate_chunk_overhead(output_len: usize) -> usize {
167 ((output_len as f64).log(16.0) + 1.0).floor() as usize + 4
178}
179
180pub(crate) fn calculate_max_input(output_len: usize) -> usize {
181 let chunks = output_len / DEFAULT_CHUNK_AND_OVERHEAD;
182 let remaining = output_len % DEFAULT_CHUNK_AND_OVERHEAD;
183
184 let tail = remaining.saturating_sub(DEFAULT_CHUNK_OVERHEAD);
188
189 chunks * DEFAULT_CHUNK_SIZE + tail
190}
191
192fn write_chunk(input: &[u8], input_used: &mut usize, w: &mut Writer, max_chunk: usize) -> bool {
193 let available = w.available().saturating_sub(5);
198
199 let to_write = input.len().min(max_chunk).min(available);
200
201 let success = w.try_write(|w| {
202 write!(w, "{:0x?}\r\n", to_write)?;
204
205 w.write_all(&input[..to_write])?;
207
208 write!(w, "\r\n")
210 });
211
212 if success {
213 *input_used += to_write;
214 }
215
216 success && input.len() > to_write
218}
219
220#[derive(Clone, Copy, PartialEq, Eq)]
221pub(crate) enum BodyReader {
222 NoBody,
224 LengthDelimited(u64),
227 Chunked(Dechunker),
229 #[cfg(feature = "client")]
231 CloseDelimited,
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq)]
236pub enum BodyMode {
237 NoBody,
239 LengthDelimited(u64),
242 Chunked,
244 CloseDelimited,
246}
247
248impl BodyReader {
249 pub fn body_mode(&self) -> BodyMode {
250 match self {
251 BodyReader::NoBody => BodyMode::NoBody,
252 BodyReader::LengthDelimited(v) => BodyMode::LengthDelimited(*v),
255 BodyReader::Chunked(_) => BodyMode::Chunked,
256 #[cfg(feature = "client")]
257 BodyReader::CloseDelimited => BodyMode::CloseDelimited,
258 }
259 }
260
261 #[cfg(feature = "server")]
262 pub fn has_body(&self) -> bool {
263 match self {
264 BodyReader::NoBody => false,
265 BodyReader::LengthDelimited(v) if *v == 0 => false,
266 _ => true,
267 }
268 }
269
270 #[cfg(feature = "server")]
271 pub fn for_request(
272 http10: bool,
273 method: &Method,
274 force_send: bool,
275 headers: &HeaderMap,
276 ) -> Result<Self, Error> {
277 use crate::ext::MethodExt;
278
279 if !method.allow_request_body() && !force_send {
280 return Ok(Self::NoBody);
281 }
282
283 let ret = Self::header_defined(http10, headers)?.unwrap_or_else(|| {
284 if !http10 && method.need_request_body() {
285 Self::Chunked(Dechunker::new())
286 } else {
287 Self::NoBody
288 }
289 });
290
291 Ok(ret)
292 }
293
294 #[cfg(feature = "client")]
295 pub fn for_response(
297 http10: bool,
298 method: &Method,
299 status_code: u16,
300 force_recv: bool,
301 headers: &HeaderMap,
302 ) -> Result<Self, Error> {
303 let header_defined = Self::header_defined(http10, headers)?.unwrap_or(Self::CloseDelimited);
304
305 let body_mode_defined = header_defined.body_mode() != BodyMode::CloseDelimited;
307
308 let body_allowed = response_body_allowed(method, status_code, header_defined.body_mode());
310
311 if body_mode_defined && (body_allowed || force_recv) {
312 Ok(header_defined)
315 } else if !body_mode_defined && body_allowed {
316 Ok(header_defined)
319 } else {
320 Ok(Self::NoBody)
321 }
322 }
323
324 fn header_defined(http10: bool, headers: &HeaderMap) -> Result<Option<Self>, Error> {
325 let content_length = parse_content_length(headers.get_all(header::CONTENT_LENGTH).iter())?;
326
327 let mut chunked = false;
328
329 if let Some(value) = headers
330 .get(header::TRANSFER_ENCODING)
331 .and_then(|v| v.to_str().ok())
332 {
333 chunked = value
335 .split(',')
336 .map(|v| v.trim())
337 .any(|v| compare_lowercase_ascii(v, "chunked"));
338 }
339
340 if chunked && !http10 {
341 return Ok(Some(Self::Chunked(Dechunker::new())));
346 }
347
348 if let Some(len) = content_length {
349 return Ok(Some(Self::LengthDelimited(len)));
350 }
351
352 Ok(None)
353 }
354
355 pub fn read(
359 &mut self,
360 src: &[u8],
361 dst: &mut [u8],
362 stop_on_chunk_boundary: bool,
363 ) -> Result<(usize, usize), Error> {
364 let part = match self {
366 BodyReader::LengthDelimited(_) => self.read_limit(src, dst),
367 BodyReader::Chunked(_) => self.read_chunked(src, dst, stop_on_chunk_boundary),
368 BodyReader::NoBody => return Ok((0, 0)),
369 #[cfg(feature = "client")]
370 BodyReader::CloseDelimited => self.read_unlimit(src, dst),
371 }?;
372
373 log_data(&src[..part.0]);
374
375 Ok(part)
376 }
377
378 fn read_limit(&mut self, src: &[u8], dst: &mut [u8]) -> Result<(usize, usize), Error> {
379 let left = match self {
380 BodyReader::LengthDelimited(v) => v,
381 _ => unreachable!(),
382 };
383 let left_usize = (*left).min(usize::MAX as u64) as usize;
384
385 let to_read = src.len().min(dst.len()).min(left_usize);
386
387 dst[..to_read].copy_from_slice(&src[..to_read]);
388
389 *left -= to_read as u64;
390
391 Ok((to_read, to_read))
392 }
393
394 fn read_chunked(
395 &mut self,
396 src: &[u8],
397 dst: &mut [u8],
398 stop_on_chunk_boundary: bool,
399 ) -> Result<(usize, usize), Error> {
400 let dechunker = match self {
401 BodyReader::Chunked(v) => v,
402 _ => unreachable!(),
403 };
404
405 let mut input_used = 0;
406 let mut output_used = 0;
407
408 loop {
409 let (i, o) = dechunker.parse_input(&src[input_used..], &mut dst[output_used..])?;
410
411 input_used += i;
412 output_used += o;
413
414 if i == 0 || input_used == src.len() || output_used == dst.len() {
415 break;
416 }
417
418 if dechunker.is_ended() {
419 break;
420 }
421
422 if stop_on_chunk_boundary && dechunker.is_on_chunk_boundary() {
423 break;
424 }
425 }
426
427 Ok((input_used, output_used))
428 }
429
430 #[cfg(feature = "client")]
431 fn read_unlimit(&mut self, src: &[u8], dst: &mut [u8]) -> Result<(usize, usize), Error> {
432 let to_read = src.len().min(dst.len());
433
434 dst[..to_read].copy_from_slice(&src[..to_read]);
435
436 Ok((to_read, to_read))
437 }
438
439 pub fn is_ended(&self) -> bool {
440 match self {
441 BodyReader::NoBody => true,
442 BodyReader::LengthDelimited(v) => *v == 0,
443 BodyReader::Chunked(v) => v.is_ended(),
444 #[cfg(feature = "client")]
445 BodyReader::CloseDelimited => false,
446 }
447 }
448
449 #[cfg(feature = "client")]
450 pub fn is_ended_chunked(&self) -> bool {
451 match self {
452 BodyReader::Chunked(v) => v.is_ending() || v.is_ended(),
453 _ => false,
454 }
455 }
456
457 pub(crate) fn is_on_chunk_boundary(&self) -> bool {
458 match self {
459 BodyReader::NoBody => false,
460 BodyReader::LengthDelimited(_) => false,
461 BodyReader::Chunked(v) => v.is_on_chunk_boundary(),
462 #[cfg(feature = "client")]
463 BodyReader::CloseDelimited => false,
464 }
465 }
466}
467
468pub fn response_body_allowed(method: &Method, status_code: u16, body_mode: BodyMode) -> bool {
472 let is_success = (200..=299).contains(&status_code);
473 let is_informational = (100..=199).contains(&status_code);
474 let is_redirect = (300..=399).contains(&status_code) && status_code != 304;
475
476 let has_body_header = body_mode != BodyMode::CloseDelimited;
479
480 let body_not_allowed = method == Method::HEAD ||
485 is_success && method == Method::CONNECT ||
488 is_informational ||
491 matches!(status_code, 204 | 304) ||
492 is_redirect && !has_body_header;
495
496 !body_not_allowed
497}
498
499impl fmt::Debug for BodyReader {
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 match self {
502 Self::NoBody => write!(f, "NoBody"),
503 Self::LengthDelimited(arg0) => f.debug_tuple("LengthDelimited").field(arg0).finish(),
504 Self::Chunked(_) => write!(f, "Chunked"),
505 #[cfg(feature = "client")]
506 Self::CloseDelimited => write!(f, "CloseDelimited"),
507 }
508 }
509}
510
511pub(crate) fn parse_content_length<'a>(
526 values: impl Iterator<Item = &'a HeaderValue>,
527) -> Result<Option<u64>, Error> {
528 let mut result = None;
529
530 for value in values {
531 for element in value.as_bytes().split(|b| *b == b',') {
532 let n = parse_content_length_value(element)?;
533
534 if result.is_some_and(|prev| prev != n) {
535 return Err(Error::TooManyContentLengthHeaders);
536 }
537
538 result = Some(n);
539 }
540 }
541
542 Ok(result)
543}
544
545pub(crate) fn parse_content_length_value(value: &[u8]) -> Result<u64, Error> {
552 let trimmed = trim_ows(value);
553
554 if trimmed.is_empty() || !trimmed.iter().all(u8::is_ascii_digit) {
555 return Err(Error::BadContentLengthHeader);
556 }
557
558 std::str::from_utf8(trimmed)
561 .ok()
562 .and_then(|s| s.parse::<u64>().ok())
563 .ok_or(Error::BadContentLengthHeader)
564}
565
566fn trim_ows(mut b: &[u8]) -> &[u8] {
568 while let [b' ' | b'\t', rest @ ..] = b {
569 b = rest;
570 }
571 while let [rest @ .., b' ' | b'\t'] = b {
572 b = rest;
573 }
574 b
575}
576
577#[cfg(test)]
578mod test {
579 use super::*;
580
581 fn cl(values: &[&str]) -> Result<Option<u64>, Error> {
582 let headers: Vec<HeaderValue> = values
583 .iter()
584 .map(|v| HeaderValue::from_bytes(v.as_bytes()).unwrap())
585 .collect();
586 parse_content_length(headers.iter())
587 }
588
589 #[test]
590 fn content_length_single_values() {
591 assert_eq!(cl(&[]), Ok(None));
592 assert_eq!(cl(&["0"]), Ok(Some(0)));
593 assert_eq!(cl(&["42"]), Ok(Some(42)));
594 assert_eq!(cl(&["042"]), Ok(Some(42)));
595 assert_eq!(cl(&[" 42 "]), Ok(Some(42)));
596 assert_eq!(cl(&["18446744073709551615"]), Ok(Some(u64::MAX)));
597 }
598
599 #[test]
600 fn content_length_repeats_must_agree() {
601 assert_eq!(cl(&["42", "42"]), Ok(Some(42)));
602 assert_eq!(cl(&["42, 42"]), Ok(Some(42)));
603 assert_eq!(cl(&["42", "042"]), Ok(Some(42)));
604 assert_eq!(cl(&["42,42", "42"]), Ok(Some(42)));
605 assert_eq!(cl(&["42", "43"]), Err(Error::TooManyContentLengthHeaders));
606 assert_eq!(cl(&["42, 43"]), Err(Error::TooManyContentLengthHeaders));
607 assert_eq!(
608 cl(&["43", "42, 42"]),
609 Err(Error::TooManyContentLengthHeaders)
610 );
611 }
612
613 #[test]
614 fn content_length_must_be_digits() {
615 assert_eq!(cl(&["+42"]), Err(Error::BadContentLengthHeader));
616 assert_eq!(cl(&["-1"]), Err(Error::BadContentLengthHeader));
617 assert_eq!(cl(&["4 2"]), Err(Error::BadContentLengthHeader));
618 assert_eq!(cl(&["42abc"]), Err(Error::BadContentLengthHeader));
619 assert_eq!(cl(&["0x2a"]), Err(Error::BadContentLengthHeader));
620 assert_eq!(cl(&[""]), Err(Error::BadContentLengthHeader));
621 assert_eq!(cl(&[" "]), Err(Error::BadContentLengthHeader));
622 assert_eq!(cl(&["42,"]), Err(Error::BadContentLengthHeader));
623 assert_eq!(cl(&["42,,42"]), Err(Error::BadContentLengthHeader));
624 assert_eq!(cl(&["42", ""]), Err(Error::BadContentLengthHeader));
625 assert_eq!(
626 cl(&["18446744073709551616"]),
627 Err(Error::BadContentLengthHeader)
628 );
629 assert_eq!(
630 cl(&["99999999999999999999999"]),
631 Err(Error::BadContentLengthHeader)
632 );
633 }
634
635 #[test]
636 fn test_calculate_max_input() {
637 assert_eq!(calculate_max_input(0), 0);
638 assert_eq!(calculate_max_input(1), 0);
639 assert_eq!(calculate_max_input(2), 0);
640 assert_eq!(calculate_max_input(9), 1);
641 assert_eq!(calculate_max_input(10), 2);
642 assert_eq!(calculate_max_input(11), 3);
643
644 assert_eq!(calculate_max_input(10247), 10239);
645 assert_eq!(calculate_max_input(10248), 10240);
646 assert_eq!(calculate_max_input(10249), 10240);
647 assert_eq!(calculate_max_input(10250), 10240);
648
649 assert_eq!(calculate_max_input(10257), 10241);
650 assert_eq!(calculate_max_input(10258), 10242);
651 assert_eq!(calculate_max_input(10259), 10243);
652 }
653}