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}