http_type/response/impl.rs
1use super::error::Error;
2use crate::*;
3use http_compress::*;
4use std::{borrow::Cow, collections::HashMap};
5
6impl Default for Response {
7 #[inline]
8 fn default() -> Self {
9 Self::new()
10 }
11}
12
13impl Response {
14 /// Creates a new instance of `Response`.
15 ///
16 /// # Returns
17 /// - An initialized `Response` with default values.
18 #[inline]
19 pub fn new() -> Self {
20 Response {
21 version: HTTP_VERSION_1_1.to_owned(),
22 status_code: 200,
23 reason_phrase: EMPTY_STR.to_owned(),
24 headers: HashMap::new(),
25 body: Vec::new(),
26 response: Vec::new(),
27 }
28 }
29
30 /// Adds a header to the response.
31 ///
32 /// This function inserts a key-value pair into the response headers.
33 /// The key and value are converted into `String`, allowing for efficient handling of both owned and borrowed string data.
34 ///
35 /// # Parameters
36 /// - `key`: The header key, which will be converted into a `String`.
37 /// - `value`: The value of the header, which will be converted into a `String`.
38 ///
39 /// # Returns
40 /// - Returns a mutable reference to the current instance (`&mut Self`), allowing for method chaining.
41 #[inline]
42 pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
43 where
44 K: Into<String>,
45 V: Into<String>,
46 {
47 self.headers.insert(key.into(), value.into());
48 self
49 }
50
51 /// Set the body of the response.
52 ///
53 /// This method allows you to set the body of the response by converting the provided
54 /// value into a `ResponseBody` type. The `body` is updated with the converted value,
55 /// and the method returns a mutable reference to the current instance for method chaining.
56 ///
57 /// # Parameters
58 /// - `body`: The body of the response to be set. It can be any type that can be converted
59 /// into a `ResponseBody` using the `Into` trait.
60 ///
61 /// # Return Value
62 /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
63 /// Set the body of the response.
64 ///
65 /// This method allows you to set the body of the response by converting the provided
66 /// value into a `ResponseBody` type. The `body` is updated with the converted value,
67 /// and the method returns a mutable reference to the current instance for method chaining.
68 ///
69 /// # Parameters
70 /// - `body`: The body of the response to be set. It can be any type that can be converted
71 /// into a `ResponseBody` using the `Into` trait.
72 ///
73 /// # Return Value
74 /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
75 #[inline]
76 pub fn set_body<T: Into<ResponseBody>>(&mut self, body: T) -> &mut Self {
77 self.body = body.into();
78 self
79 }
80
81 /// Set the reason phrase of the response.
82 ///
83 /// This method allows you to set the reason phrase of the response by converting the
84 /// provided value into a `ResponseReasonPhrase` type. The `reason_phrase` is updated
85 /// with the converted value, and the method returns a mutable reference to the current
86 /// instance for method chaining.
87 ///
88 /// # Parameters
89 /// - `reason_phrase`: The reason phrase to be set for the response. It can be any type
90 /// that can be converted into a `ResponseReasonPhrase` using the `Into` trait.
91 ///
92 /// # Return Value
93 /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
94 #[inline]
95 pub fn set_reason_phrase<T: Into<ResponseReasonPhrase>>(
96 &mut self,
97 reason_phrase: T,
98 ) -> &mut Self {
99 self.reason_phrase = reason_phrase.into();
100 self
101 }
102
103 /// Pushes a header with a key and value into the response string.
104 ///
105 /// # Parameters
106 /// - `response_string`: A mutable reference to the string where the header will be added.
107 /// - `key`: The header key as a string slice (`&str`).
108 /// - `value`: The header value as a string slice (`&str`).
109 #[inline]
110 pub(super) fn push_header(response_string: &mut String, key: &str, value: &str) {
111 response_string.push_str(&format!("{}{}{}{}", key, COLON_SPACE, value, HTTP_BR));
112 }
113
114 /// Pushes the first line of an HTTP response (version, status code, and reason phrase) into the response string.
115 /// This corresponds to the status line of the HTTP response.
116 ///
117 /// # Parameters
118 /// - `response_string`: A mutable reference to the string where the first line will be added.
119 #[inline]
120 pub(super) fn push_http_response_first_line(&self, response_string: &mut String) {
121 response_string.push_str(&format!(
122 "{}{}{}{}{}{}",
123 self.get_version(),
124 SPACE,
125 self.get_status_code(),
126 SPACE,
127 self.get_reason_phrase(),
128 HTTP_BR
129 ));
130 }
131
132 /// Builds the full HTTP response as a byte vector.
133 ///
134 /// # Returns
135 /// - The serialized HTTP response including headers and body.
136 #[inline]
137 pub fn build(&mut self) -> ResponseData {
138 if self.reason_phrase.is_empty() {
139 self.set_reason_phrase(StatusCode::phrase(*self.get_status_code()));
140 }
141 let mut response_string: String = String::new();
142 self.push_http_response_first_line(&mut response_string);
143 let mut compress_type_opt: Option<Compress> = None;
144 let mut connection_opt: Option<&str> = None;
145 let mut content_type_opt: Option<&str> = None;
146 for (key, value) in self.get_headers() {
147 if key == CONTENT_LENGTH {
148 continue;
149 } else if key == CONTENT_ENCODING {
150 compress_type_opt = Some(value.parse::<Compress>().unwrap_or_default());
151 } else if key == CONNECTION {
152 connection_opt = Some(value);
153 } else if key == CONTENT_TYPE {
154 content_type_opt = Some(value);
155 }
156 Self::push_header(&mut response_string, key, &value);
157 }
158 if connection_opt.is_none() {
159 Self::push_header(&mut response_string, CONNECTION, CONNECTION_KEEP_ALIVE);
160 }
161 if content_type_opt.is_none() {
162 Self::push_header(
163 &mut response_string,
164 CONTENT_TYPE,
165 &format!("{}{}{}", TEXT_HTML, SEMICOLON_SPACE, CHARSET_UTF_8),
166 );
167 }
168 let mut body: Cow<Vec<u8>> = Cow::Borrowed(self.get_body());
169 if let Some(compress_type) = compress_type_opt {
170 if !compress_type.is_unknown() {
171 let tmp_body: Cow<'_, Vec<u8>> = compress_type.encode(&body, DEFAULT_BUFFER_SIZE);
172 body = Cow::Owned(tmp_body.into_owned());
173 }
174 }
175 let len_string: String = body.len().to_string();
176 let len_str: &str = len_string.as_str();
177 Self::push_header(&mut response_string, CONTENT_LENGTH, len_str);
178 response_string.push_str(HTTP_BR);
179 let mut response_bytes: Vec<u8> = response_string.into_bytes();
180 response_bytes.extend_from_slice(&body);
181 self.set_response(response_bytes.clone());
182 response_bytes
183 }
184
185 /// Sends the HTTP response body over a TCP stream.
186 ///
187 /// # Parameters
188 /// - `stream`: A mutable reference to the `TcpStream` to send the response.
189 ///
190 /// # Returns
191 /// - `Ok`: If the response body is successfully sent.
192 /// - `Err`: If an error occurs during sending.
193 #[inline]
194 pub async fn send_body(&mut self, stream_lock: &ArcRwLockStream) -> ResponseResult {
195 let mut stream: RwLockWriteGuard<'_, TcpStream> = stream_lock.write().await;
196 stream
197 .write_all(self.get_body())
198 .await
199 .map_err(|err| Error::ResponseError(err.to_string()))?;
200 stream
201 .flush()
202 .await
203 .map_err(|err| Error::ResponseError(err.to_string()))?;
204 Ok(())
205 }
206
207 /// Closes the stream after sending the response.
208 ///
209 /// This function is responsible for:
210 /// - Building the response using the `build()` method.
211 /// - Setting the response using the `set_response()` method.
212 /// - Shutting down the write half of the TCP stream to indicate no more data will be sent.
213 ///
214 /// # Parameters
215 /// - `stream`: A reference to the `TcpStream` that will be closed after sending the response.
216 ///
217 /// # Returns
218 /// - `CloseStreamResult`: The result of the operation, indicating whether the closure was successful or if an error occurred.
219 #[inline]
220 pub async fn close(&mut self, stream_lock: &ArcRwLockStream) -> ResponseResult {
221 let mut stream: RwLockWriteGuard<'_, TcpStream> = stream_lock.write().await;
222 let _ = stream.shutdown();
223 Ok(())
224 }
225
226 /// Sends the HTTP response over a TCP stream.
227 ///
228 /// # Parameters
229 /// - `stream`: A mutable reference to the `TcpStream` to send the response.
230 ///
231 /// # Returns
232 /// - `Ok`: If the response is successfully sent.
233 /// - `Err`: If an error occurs during sending.
234 #[inline]
235 pub async fn send(&mut self, stream_lock: &ArcRwLockStream) -> ResponseResult {
236 self.build();
237 let mut stream: RwLockWriteGuard<'_, TcpStream> = stream_lock.write().await;
238 stream
239 .write_all(&self.get_response())
240 .await
241 .map_err(|err| Error::ResponseError(err.to_string()))?;
242 stream
243 .flush()
244 .await
245 .map_err(|err| Error::ResponseError(err.to_string()))?;
246 Ok(())
247 }
248}