micro_http/codec/
response_encoder.rs

1//! HTTP response encoder module
2//!
3//! This module provides functionality for encoding HTTP responses using a streaming approach.
4//! It handles both header encoding and payload encoding through a state machine pattern.
5//!
6//! # Components
7//!
8//! - [`ResponseEncoder`]: Main encoder that coordinates header and payload encoding
9//! - Header encoding: Uses [`HeaderEncoder`] for encoding response headers
10//! - Payload handling: Uses [`PayloadEncoder`] for encoding response bodies
11//!
12//! # Example
13//!
14//! ```no_run
15//! use micro_http::codec::ResponseEncoder;
16//! use tokio_util::codec::Encoder;
17//! use bytes::BytesMut;
18//!
19//! let mut encoder = ResponseEncoder::new();
20//! let mut buffer = BytesMut::new();
21//! // ... encode response data to buffer ...
22//! ```
23
24use crate::codec::body::PayloadEncoder;
25use crate::codec::header::HeaderEncoder;
26use crate::protocol::{Message, PayloadSize, ResponseHead, SendError};
27use bytes::{Buf, BytesMut};
28use std::io;
29use std::io::ErrorKind;
30use tokio_util::codec::Encoder;
31use tracing::error;
32
33/// An encoder for HTTP responses that handles both headers and payload
34///
35/// The encoder operates in two phases:
36/// 1. Header encoding: Encodes the response headers using [`HeaderEncoder`]
37/// 2. Payload encoding: If present, encodes the response body using [`PayloadEncoder`]
38#[derive(Debug)]
39pub struct ResponseEncoder {
40    /// Encoder for HTTP response headers
41    header_encoder: HeaderEncoder,
42    /// Encoder for HTTP response payload (body)
43    payload_encoder: Option<PayloadEncoder>,
44}
45
46impl ResponseEncoder {
47    /// Creates a new `ResponseEncoder` instance
48    #[must_use]
49    pub fn new() -> Self {
50        ResponseEncoder::default()
51    }
52}
53
54impl Default for ResponseEncoder {
55    fn default() -> Self {
56        Self { header_encoder: HeaderEncoder, payload_encoder: None }
57    }
58}
59
60impl<D: Buf> Encoder<Message<(ResponseHead, PayloadSize), D>> for ResponseEncoder {
61    type Error = SendError;
62
63    /// Attempts to encode an HTTP response to the provided buffer
64    ///
65    /// # Arguments
66    ///
67    /// * `item` - The message to encode, either headers or payload
68    /// * `dst` - The buffer to write the encoded data to
69    ///
70    /// # Returns
71    ///
72    /// - `Ok(())`: Successfully encoded the message
73    /// - `Err(_)`: Encountered an encoding error
74    fn encode(&mut self, item: Message<(ResponseHead, PayloadSize), D>, dst: &mut BytesMut) -> Result<(), Self::Error> {
75        match item {
76            Message::Header((head, payload_size)) => {
77                // If a payload encoder already exists, it's an error
78                if self.payload_encoder.is_some() {
79                    error!("expect payload item but receive response head");
80                    return Err(io::Error::from(ErrorKind::InvalidInput).into());
81                }
82
83                // Create a payload encoder based on the payload size
84                let payload_encoder = parse_payload_encoder(payload_size);
85                self.payload_encoder = Some(payload_encoder);
86                // Encode the response headers
87                self.header_encoder.encode((head, payload_size), dst)
88            }
89
90            Message::Payload(payload_item) => {
91                // Get the payload encoder, return error if it doesn't exist
92                let Some(payload_encoder) = &mut self.payload_encoder else {
93                    error!("expect response header but receive payload item");
94                    return Err(io::Error::from(ErrorKind::InvalidInput).into());
95                };
96
97                // Encode the payload
98                let result = payload_encoder.encode(payload_item, dst);
99
100                // Check if the payload encoder is finished
101                let is_eof = payload_encoder.is_finish();
102                // If finished, remove the payload encoder
103                if is_eof {
104                    self.payload_encoder.take();
105                }
106
107                result
108            }
109        }
110    }
111}
112
113/// Creates a payload encoder based on the payload size
114///
115/// # Arguments
116///
117/// * `payload_size` - The size specification for the payload
118///
119/// # Returns
120///
121/// Returns a [`PayloadEncoder`] configured according to the payload size
122fn parse_payload_encoder(payload_size: PayloadSize) -> PayloadEncoder {
123    match payload_size {
124        PayloadSize::Length(size) => PayloadEncoder::fix_length(size),
125        PayloadSize::Chunked => PayloadEncoder::chunked(),
126        PayloadSize::Empty => PayloadEncoder::empty(),
127    }
128}