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}