1use std::fmt;
2use std::io::Write;
3
4use http::{header, HeaderName, HeaderValue, Method};
5
6use crate::chunk::Dechunker;
7use crate::util::{compare_lowercase_ascii, log_data, Writer};
8use crate::Error;
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<'a>(
272 http10: bool,
273 method: &Method,
274 force_send: bool,
275 header_lookup: &'a dyn Fn(http::HeaderName) -> Option<&'a str>,
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, header_lookup)?.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<'a>(
297 http10: bool,
298 method: &Method,
299 status_code: u16,
300 force_recv: bool,
301 header_lookup: &'a dyn Fn(HeaderName) -> Option<&'a str>,
302 ) -> Result<Self, Error> {
303 let header_defined =
304 Self::header_defined(http10, header_lookup)?.unwrap_or(Self::CloseDelimited);
305
306 let body_mode_defined = header_defined.body_mode() != BodyMode::CloseDelimited;
308
309 let body_allowed = response_body_allowed(method, status_code, header_defined.body_mode());
311
312 if body_mode_defined && (body_allowed || force_recv) {
313 Ok(header_defined)
316 } else if !body_mode_defined && body_allowed {
317 Ok(header_defined)
320 } else {
321 Ok(Self::NoBody)
322 }
323 }
324
325 fn header_defined<'a>(
326 http10: bool,
327 header_lookup: &'a dyn Fn(HeaderName) -> Option<&'a str>,
328 ) -> Result<Option<Self>, Error> {
329 let mut content_length: Option<u64> = None;
330 let mut chunked = false;
331
332 if let Some(value) = header_lookup(header::CONTENT_LENGTH) {
334 let v = value
335 .parse::<u64>()
336 .map_err(|_| Error::BadContentLengthHeader)?;
337 if content_length.is_some() {
338 return Err(Error::TooManyContentLengthHeaders);
339 }
340 content_length = Some(v);
341 }
342
343 if let Some(value) = header_lookup(header::TRANSFER_ENCODING) {
344 chunked = value
346 .split(',')
347 .map(|v| v.trim())
348 .any(|v| compare_lowercase_ascii(v, "chunked"));
349 }
350
351 if chunked && !http10 {
352 return Ok(Some(Self::Chunked(Dechunker::new())));
357 }
358
359 if let Some(len) = content_length {
360 return Ok(Some(Self::LengthDelimited(len)));
361 }
362
363 Ok(None)
364 }
365
366 pub fn read(
370 &mut self,
371 src: &[u8],
372 dst: &mut [u8],
373 stop_on_chunk_boundary: bool,
374 ) -> Result<(usize, usize), Error> {
375 let part = match self {
377 BodyReader::LengthDelimited(_) => self.read_limit(src, dst),
378 BodyReader::Chunked(_) => self.read_chunked(src, dst, stop_on_chunk_boundary),
379 BodyReader::NoBody => return Ok((0, 0)),
380 #[cfg(feature = "client")]
381 BodyReader::CloseDelimited => self.read_unlimit(src, dst),
382 }?;
383
384 log_data(&src[..part.0]);
385
386 Ok(part)
387 }
388
389 fn read_limit(&mut self, src: &[u8], dst: &mut [u8]) -> Result<(usize, usize), Error> {
390 let left = match self {
391 BodyReader::LengthDelimited(v) => v,
392 _ => unreachable!(),
393 };
394 let left_usize = (*left).min(usize::MAX as u64) as usize;
395
396 let to_read = src.len().min(dst.len()).min(left_usize);
397
398 dst[..to_read].copy_from_slice(&src[..to_read]);
399
400 *left -= to_read as u64;
401
402 Ok((to_read, to_read))
403 }
404
405 fn read_chunked(
406 &mut self,
407 src: &[u8],
408 dst: &mut [u8],
409 stop_on_chunk_boundary: bool,
410 ) -> Result<(usize, usize), Error> {
411 let dechunker = match self {
412 BodyReader::Chunked(v) => v,
413 _ => unreachable!(),
414 };
415
416 let mut input_used = 0;
417 let mut output_used = 0;
418
419 loop {
420 let (i, o) = dechunker.parse_input(&src[input_used..], &mut dst[output_used..])?;
421
422 input_used += i;
423 output_used += o;
424
425 if i == 0 || input_used == src.len() || output_used == dst.len() {
426 break;
427 }
428
429 if dechunker.is_ended() {
430 break;
431 }
432
433 if stop_on_chunk_boundary && dechunker.is_on_chunk_boundary() {
434 break;
435 }
436 }
437
438 Ok((input_used, output_used))
439 }
440
441 #[cfg(feature = "client")]
442 fn read_unlimit(&mut self, src: &[u8], dst: &mut [u8]) -> Result<(usize, usize), Error> {
443 let to_read = src.len().min(dst.len());
444
445 dst[..to_read].copy_from_slice(&src[..to_read]);
446
447 Ok((to_read, to_read))
448 }
449
450 pub fn is_ended(&self) -> bool {
451 match self {
452 BodyReader::NoBody => true,
453 BodyReader::LengthDelimited(v) => *v == 0,
454 BodyReader::Chunked(v) => v.is_ended(),
455 #[cfg(feature = "client")]
456 BodyReader::CloseDelimited => false,
457 }
458 }
459
460 #[cfg(feature = "client")]
461 pub fn is_ended_chunked(&self) -> bool {
462 match self {
463 BodyReader::Chunked(v) => v.is_ending() || v.is_ended(),
464 _ => false,
465 }
466 }
467
468 pub(crate) fn is_on_chunk_boundary(&self) -> bool {
469 match self {
470 BodyReader::NoBody => false,
471 BodyReader::LengthDelimited(_) => false,
472 BodyReader::Chunked(v) => v.is_on_chunk_boundary(),
473 #[cfg(feature = "client")]
474 BodyReader::CloseDelimited => false,
475 }
476 }
477}
478
479pub fn response_body_allowed(method: &Method, status_code: u16, body_mode: BodyMode) -> bool {
483 let is_success = (200..=299).contains(&status_code);
484 let is_informational = (100..=199).contains(&status_code);
485 let is_redirect = (300..=399).contains(&status_code) && status_code != 304;
486
487 let has_body_header = body_mode != BodyMode::CloseDelimited;
490
491 let body_not_allowed = method == Method::HEAD ||
496 is_success && method == Method::CONNECT ||
499 is_informational ||
502 matches!(status_code, 204 | 304) ||
503 is_redirect && !has_body_header;
506
507 !body_not_allowed
508}
509
510impl fmt::Debug for BodyReader {
511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512 match self {
513 Self::NoBody => write!(f, "NoBody"),
514 Self::LengthDelimited(arg0) => f.debug_tuple("LengthDelimited").field(arg0).finish(),
515 Self::Chunked(_) => write!(f, "Chunked"),
516 #[cfg(feature = "client")]
517 Self::CloseDelimited => write!(f, "CloseDelimited"),
518 }
519 }
520}
521
522#[cfg(test)]
523mod test {
524 use super::*;
525
526 #[test]
527 fn test_calculate_max_input() {
528 assert_eq!(calculate_max_input(0), 0);
529 assert_eq!(calculate_max_input(1), 0);
530 assert_eq!(calculate_max_input(2), 0);
531 assert_eq!(calculate_max_input(9), 1);
532 assert_eq!(calculate_max_input(10), 2);
533 assert_eq!(calculate_max_input(11), 3);
534
535 assert_eq!(calculate_max_input(10247), 10239);
536 assert_eq!(calculate_max_input(10248), 10240);
537 assert_eq!(calculate_max_input(10249), 10240);
538 assert_eq!(calculate_max_input(10250), 10240);
539
540 assert_eq!(calculate_max_input(10257), 10241);
541 assert_eq!(calculate_max_input(10258), 10242);
542 assert_eq!(calculate_max_input(10259), 10243);
543 }
544}