micro_http/codec/body/
length_encoder.rs

1//! Encoder implementation for HTTP messages with Content-Length header.
2//!
3//! This module provides functionality to encode HTTP messages where the payload size
4//! is specified by the Content-Length header, ensuring the total bytes sent matches
5//! the declared content length.
6
7use crate::protocol::{PayloadItem, SendError};
8use bytes::{Buf, BytesMut};
9use tokio_util::codec::Encoder;
10use tracing::warn;
11
12/// An encoder for handling HTTP messages with a known content length.
13///
14/// The encoder tracks the remaining bytes to be sent and ensures the total
15/// payload matches the specified content length.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct LengthEncoder {
18    /// Indicates if the final EOF marker has been received
19    received_eof: bool,
20    /// The number of bytes remaining to be sent
21    length: u64,
22}
23
24impl LengthEncoder {
25    /// Creates a new LengthEncoder instance.
26    ///
27    /// # Arguments
28    /// * `length` - The total content length to encode, specified by Content-Length header
29    pub fn new(length: u64) -> Self {
30        Self { received_eof: false, length }
31    }
32
33    /// Returns whether the encoder has finished sending all data.
34    ///
35    /// Returns true if all bytes have been sent and EOF has been received.
36    pub fn is_finish(&self) -> bool {
37        self.length == 0 && self.received_eof
38    }
39}
40
41/// Implementation of the Encoder trait for content-length based encoding.
42///
43/// This implementation tracks the remaining bytes to send and ensures the total
44/// payload matches the specified content length.
45impl<D: Buf> Encoder<PayloadItem<D>> for LengthEncoder {
46    type Error = SendError;
47
48    /// Encodes a PayloadItem according to the content length.
49    ///
50    /// # Arguments
51    /// * `item` - The PayloadItem to encode (either Chunk or Eof)
52    /// * `dst` - The output buffer to write the encoded data to
53    ///
54    /// # Returns
55    /// * `Ok(())` if encoding succeeds
56    /// * `Err(SendError)` if encoding fails
57    fn encode(&mut self, item: PayloadItem<D>, dst: &mut BytesMut) -> Result<(), Self::Error> {
58        if self.length == 0 && !item.is_eof() {
59            warn!("encode payload_item but no need to encode anymore");
60            return Ok(());
61        }
62
63        match item {
64            PayloadItem::Chunk(bytes) => {
65                if !bytes.has_remaining() {
66                    return Ok(());
67                }
68                dst.extend_from_slice(bytes.chunk());
69                self.length -= bytes.remaining() as u64;
70                Ok(())
71            }
72            PayloadItem::Eof => {
73                self.received_eof = true;
74                Ok(())
75            }
76        }
77    }
78}