http_type/response/impl.rs
1use crate::*;
2
3impl Default for Response {
4 fn default() -> Self {
5 Self::new()
6 }
7}
8
9impl Response {
10 /// Creates a new instance of `Response`.
11 ///
12 /// # Returns
13 /// - An initialized `Response` with default values.
14 pub fn new() -> Self {
15 Response {
16 version: HTTP_VERSION_1_1.to_owned(),
17 status_code: 200,
18 reason_phrase: EMPTY_STR.to_owned(),
19 headers: hash_map_xx_hash3_64(),
20 body: Vec::new(),
21 }
22 }
23
24 /// Retrieves the value of a response header by its key.
25 ///
26 /// # Parameters
27 /// - `key`: The header's key, which can be of any type that implements `Into<ResponseHeadersKey>`.
28 ///
29 /// # Returns
30 /// - `OptionResponseHeadersValue`: Returns `Some(value)` if the key exists in the response headers,
31 /// or `None` if the key does not exist.
32 pub fn get_header<K>(&self, key: K) -> OptionResponseHeadersValue
33 where
34 K: Into<ResponseHeadersKey>,
35 {
36 self.headers
37 .get(&key.into())
38 .and_then(|data| Some(data.clone()))
39 }
40
41 /// Retrieves the body content of the object as a UTF-8 encoded string.
42 ///
43 /// This method uses `String::from_utf8_lossy` to convert the byte slice returned by `self.get_body()` into a string.
44 /// If the byte slice contains invalid UTF-8 sequences, they will be replaced with the Unicode replacement character (�).
45 ///
46 /// # Returns
47 /// A `String` containing the body content.
48 pub fn get_body_string(&self) -> String {
49 String::from_utf8_lossy(self.get_body()).into_owned()
50 }
51
52 /// Deserializes the body content of the object into a specified type `T`.
53 ///
54 /// This method first retrieves the body content as a UTF-8 encoded string using `self.get_body()`.
55 /// It then attempts to deserialize the string into the specified type `T` using `serde_json::from_str`.
56 ///
57 /// # Type Parameters
58 /// - `T`: The target type to deserialize into. It must implement the `DeserializeOwned` trait.
59 ///
60 /// # Returns
61 /// - `Ok(T)`: The deserialized object of type `T` if the deserialization is successful.
62 /// - `Err(serde_json::Error)`: An error if the deserialization fails (e.g., invalid JSON format or type mismatch).
63 pub fn get_body_json<T>(&self) -> ResultSerdeJsonError<T>
64 where
65 T: DeserializeOwned,
66 {
67 serde_json::from_slice(self.get_body())
68 }
69
70 /// Adds a header to the response.
71 ///
72 /// This function inserts a key-value pair into the response headers.
73 /// The key and value are converted into `ResponseHeadersKey`, allowing for efficient handling of both owned and borrowed string data.
74 ///
75 /// # Parameters
76 /// - `key`: The header key, which will be converted into a `ResponseHeadersKey`.
77 /// - `value`: The value of the header, which will be converted into a `ResponseHeadersValue`.
78 ///
79 /// # Returns
80 /// - Returns a mutable reference to the current instance (`&mut Self`), allowing for method chaining.
81 pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
82 where
83 K: Into<ResponseHeadersKey>,
84 V: Into<ResponseHeadersValue>,
85 {
86 self.headers.insert(key.into(), value.into());
87 self
88 }
89
90 /// Set the body of the response.
91 ///
92 /// This method allows you to set the body of the response by converting the provided
93 /// value into a `ResponseBody` type. The `body` is updated with the converted value,
94 /// and the method returns a mutable reference to the current instance for method chaining.
95 ///
96 /// # Parameters
97 /// - `body`: The body of the response to be set. It can be any type that can be converted
98 /// into a `ResponseBody` using the `Into` trait.
99 ///
100 /// # Return Value
101 /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
102 /// Set the body of the response.
103 ///
104 /// This method allows you to set the body of the response by converting the provided
105 /// value into a `ResponseBody` type. The `body` is updated with the converted value,
106 /// and the method returns a mutable reference to the current instance for method chaining.
107 ///
108 /// # Parameters
109 /// - `body`: The body of the response to be set. It can be any type that can be converted
110 /// into a `ResponseBody` using the `Into` trait.
111 ///
112 /// # Return Value
113 /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
114 pub fn set_body<T: Into<ResponseBody>>(&mut self, body: T) -> &mut Self {
115 self.body = body.into();
116 self
117 }
118
119 /// Set the reason phrase of the response.
120 ///
121 /// This method allows you to set the reason phrase of the response by converting the
122 /// provided value into a `ResponseReasonPhrase` type. The `reason_phrase` is updated
123 /// with the converted value, and the method returns a mutable reference to the current
124 /// instance for method chaining.
125 ///
126 /// # Parameters
127 /// - `reason_phrase`: The reason phrase to be set for the response. It can be any type
128 /// that can be converted into a `ResponseReasonPhrase` using the `Into` trait.
129 ///
130 /// # Return Value
131 /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
132 pub fn set_reason_phrase<T: Into<ResponseReasonPhrase>>(
133 &mut self,
134 reason_phrase: T,
135 ) -> &mut Self {
136 self.reason_phrase = reason_phrase.into();
137 self
138 }
139
140 /// Pushes a header with a key and value into the response string.
141 ///
142 /// # Parameters
143 /// - `response_string`: A mutable reference to the string where the header will be added.
144 /// - `key`: The header key as a string slice (`&str`).
145 /// - `value`: The header value as a string slice (`&str`).
146 pub(super) fn push_header(response_string: &mut String, key: &str, value: &str) {
147 response_string.push_str(&format!("{}{}{}{}", key, COLON_SPACE, value, HTTP_BR));
148 }
149
150 /// Pushes the first line of an HTTP response (version, status code, and reason phrase) into the response string.
151 /// This corresponds to the status line of the HTTP response.
152 ///
153 /// # Parameters
154 /// - `response_string`: A mutable reference to the string where the first line will be added.
155 pub(super) fn push_http_response_first_line(&self, response_string: &mut String) {
156 response_string.push_str(&format!(
157 "{}{}{}{}{}{}",
158 self.get_version(),
159 SPACE,
160 self.get_status_code(),
161 SPACE,
162 self.get_reason_phrase(),
163 HTTP_BR
164 ));
165 }
166
167 /// Builds the full HTTP response as a byte vector.
168 /// # Returns
169 /// - `ResponseData`: response data
170 pub fn build(&mut self) -> ResponseData {
171 if self.reason_phrase.is_empty() {
172 self.set_reason_phrase(HttpStatus::phrase(*self.get_status_code()));
173 }
174 let mut response_string: String = String::new();
175 self.push_http_response_first_line(&mut response_string);
176 let mut compress_type_opt: OptionCompress = None;
177 let mut connection_opt: OptionString = None;
178 let mut content_type_opt: OptionString = None;
179 let headers: ResponseHeaders = self
180 .get_mut_headers()
181 .drain()
182 .map(|(key, value)| (key.to_lowercase(), value))
183 .collect();
184 let mut unset_content_length: bool = false;
185 for (key, value) in headers.iter() {
186 if key == CONTENT_LENGTH {
187 continue;
188 } else if key == CONTENT_ENCODING {
189 compress_type_opt = Some(value.parse::<Compress>().unwrap_or_default());
190 } else if key == CONNECTION {
191 connection_opt = Some(value.to_owned());
192 } else if key == CONTENT_TYPE {
193 content_type_opt = Some(value.to_owned());
194 if value.to_ascii_lowercase() == TEXT_EVENT_STREAM {
195 unset_content_length = true;
196 }
197 }
198 Self::push_header(&mut response_string, key, value);
199 }
200 if connection_opt.is_none() {
201 Self::push_header(&mut response_string, CONNECTION, KEEP_ALIVE);
202 }
203 if content_type_opt.is_none() {
204 Self::push_header(
205 &mut response_string,
206 CONTENT_TYPE,
207 &format!("{}{}{}", TEXT_HTML, SEMICOLON_SPACE, CHARSET_UTF_8),
208 );
209 }
210 let mut body: Cow<Vec<u8>> = Cow::Borrowed(self.get_body());
211 if !unset_content_length {
212 if let Some(compress_type) = compress_type_opt {
213 if !compress_type.is_unknown() {
214 let tmp_body: Cow<'_, Vec<u8>> =
215 compress_type.encode(&body, DEFAULT_BUFFER_SIZE);
216 body = Cow::Owned(tmp_body.into_owned());
217 }
218 }
219 let len_string: String = body.len().to_string();
220 Self::push_header(&mut response_string, CONTENT_LENGTH, &len_string);
221 }
222 response_string.push_str(HTTP_BR);
223 let mut response_bytes: Vec<u8> = response_string.into_bytes();
224 response_bytes.extend_from_slice(&body);
225 response_bytes
226 }
227
228 /// Converts the response to a formatted string representation.
229 ///
230 /// - Returns: A `String` containing formatted response details.
231 pub fn get_string(&self) -> String {
232 let body: &Vec<u8> = self.get_body();
233 format!(
234 "[Response] => [Version]: {}; [Status Code]: {}; [Reason]: {}; [Headers]: {:?}; [Body]: {};",
235 self.get_version(),
236 self.get_status_code(),
237 self.get_reason_phrase(),
238 self.get_headers(),
239 match std::str::from_utf8(body) {
240 Ok(string_data) => Cow::Borrowed(string_data),
241 Err(_) => Cow::Owned(format!("binary data len: {}", body.len())),
242 },
243 )
244 }
245}