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/// A 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`]
38pub struct ResponseEncoder {
39    /// Encoder for HTTP response headers
40    header_encoder: HeaderEncoder,
41    /// Encoder for HTTP response payload (body)
42    payload_encoder: Option<PayloadEncoder>,
43}
44
45impl ResponseEncoder {
46    /// Creates a new `ResponseEncoder` instance
47    pub fn new() -> Self {
48        Default::default()
49    }
50}
51
52impl Default for ResponseEncoder {
53    fn default() -> Self {
54        Self { header_encoder: HeaderEncoder, payload_encoder: None }
55    }
56}
57
58impl<D: Buf> Encoder<Message<(ResponseHead, PayloadSize), D>> for ResponseEncoder {
59    type Error = SendError;
60
61    /// Attempts to encode an HTTP response to the provided buffer
62    ///
63    /// # Arguments
64    ///
65    /// * `item` - The message to encode, either headers or payload
66    /// * `dst` - The buffer to write the encoded data to
67    ///
68    /// # Returns
69    ///
70    /// - `Ok(())`: Successfully encoded the message
71    /// - `Err(_)`: Encountered an encoding error
72    fn encode(&mut self, item: Message<(ResponseHead, PayloadSize), D>, dst: &mut BytesMut) -> Result<(), Self::Error> {
73        match item {
74            Message::Header((head, payload_size)) => {
75                // If a payload encoder already exists, it's an error
76                if self.payload_encoder.is_some() {
77                    error!("expect payload item but receive response head");
78                    return Err(io::Error::from(ErrorKind::InvalidInput).into());
79                }
80
81                // Create a payload encoder based on the payload size
82                let payload_encoder = parse_payload_encoder(payload_size);
83                self.payload_encoder = Some(payload_encoder);
84                // Encode the response headers
85                self.header_encoder.encode((head, payload_size), dst)
86            }
87
88            Message::Payload(payload_item) => {
89                // Get the payload encoder, return error if it doesn't exist
90                let payload_encoder = if let Some(encoder) = &mut self.payload_encoder {
91                    encoder
92                } 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}