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}