http_type/request/
impl.rs

1use super::error::Error;
2use crate::*;
3use std::{collections::HashMap, str::SplitN};
4
5impl Default for Request {
6    #[inline]
7    fn default() -> Self {
8        Self {
9            method: String::new(),
10            host: String::new(),
11            path: String::new(),
12            query: HashMap::new(),
13            headers: HashMap::new(),
14            body: Vec::new(),
15        }
16    }
17}
18
19impl Request {
20    /// Creates a new `Request` object from a TCP stream.
21    ///
22    /// # Parameters
23    /// - `reader`: A mut reference to a `&mut std::io::BufReader<&std::net::TcpStream>`
24    ///
25    /// # Returns
26    /// - `Ok`: A `Request` object populated with the HTTP request data.
27    /// - `Err`: An `Error` if the request is invalid or cannot be read.
28    #[inline]
29    pub fn from_reader(reader: &mut std::io::BufReader<&std::net::TcpStream>) -> RequestNewResult {
30        let mut request_line: String = String::new();
31        std::io::BufRead::read_line(reader, &mut request_line).map_err(|_| Error::HttpReadError)?;
32        let parts: Vec<&str> = request_line.split_whitespace().collect();
33        if parts.len() < 3 {
34            return Err(Error::InvalidHttpRequest);
35        }
36        let method: RequestMethod = parts[0].to_string();
37        let full_path: String = parts[1].to_string();
38        let hash_index: Option<usize> = full_path.find(HASH_SYMBOL);
39        let query_index: Option<usize> = full_path.find(QUERY_SYMBOL);
40        let query_string: String = query_index.map_or(EMPTY_STR.to_owned(), |i| {
41            let temp: String = full_path[i + 1..].to_string();
42            if hash_index.is_none() || hash_index.unwrap() <= i {
43                return temp.into();
44            }
45            let data: String = temp
46                .split(HASH_SYMBOL)
47                .next()
48                .unwrap_or_default()
49                .to_string();
50            data.into()
51        });
52        let query: RequestQuery = Self::parse_query(&query_string);
53        let path: RequestPath = if let Some(i) = query_index.or(hash_index) {
54            full_path[..i].to_string()
55        } else {
56            full_path
57        };
58        let mut headers: RequestHeaders = HashMap::new();
59        let mut host: RequestHost = EMPTY_STR.to_owned();
60        let mut content_length: usize = 0;
61        loop {
62            let mut header_line: String = String::new();
63            std::io::BufRead::read_line(reader, &mut header_line)
64                .map_err(|_| Error::HttpReadError)?;
65            let header_line: &str = header_line.trim();
66            if header_line.is_empty() {
67                break;
68            }
69            let parts: Vec<&str> = header_line.splitn(2, COLON_SPACE_SYMBOL).collect();
70            if parts.len() != 2 {
71                continue;
72            }
73            let key: String = parts[0].trim().to_string();
74            let value: String = parts[1].trim().to_string();
75            if key.eq_ignore_ascii_case(HOST) {
76                host = value.to_string();
77            }
78            if key.eq_ignore_ascii_case(CONTENT_LENGTH) {
79                content_length = value.parse().unwrap_or(0);
80            }
81            headers.insert(key, value);
82        }
83        let mut body: RequestBody = Vec::new();
84        if content_length > 0 {
85            body.resize(content_length, 0);
86            std::io::Read::read_exact(reader, &mut body).map_err(|_| Error::HttpReadError)?;
87        }
88        Ok(Request {
89            method,
90            host,
91            path,
92            query,
93            headers,
94            body,
95        })
96    }
97
98    /// Creates a new `Request` object from a TCP stream.
99    ///
100    /// # Parameters
101    /// - `reader`: A mut reference to a `&mut tokio::io::BufReader<&mut tokio::net::TcpStream>`
102    ///
103    /// # Returns
104    /// - `Ok`: A `Request` object populated with the HTTP request data.
105    /// - `Err`: An `Error` if the request is invalid or cannot be read.
106    #[inline]
107    pub async fn from_tokio_reader(
108        reader: &mut tokio::io::BufReader<&mut tokio::net::TcpStream>,
109    ) -> RequestNewResult {
110        let mut request_line: String = String::new();
111        let _ = tokio::io::AsyncBufReadExt::read_line(reader, &mut request_line).await;
112        let parts: Vec<&str> = request_line.split_whitespace().collect();
113        if parts.len() < 3 {
114            return Err(Error::InvalidHttpRequest);
115        }
116        let method: RequestMethod = parts[0].to_string();
117        let full_path: String = parts[1].to_string();
118        let hash_index: Option<usize> = full_path.find(HASH_SYMBOL);
119        let query_index: Option<usize> = full_path.find(QUERY_SYMBOL);
120        let query_string: String = query_index.map_or(EMPTY_STR.to_owned(), |i| {
121            let temp: String = full_path[i + 1..].to_string();
122            if hash_index.is_none() || hash_index.unwrap() <= i {
123                return temp.into();
124            }
125            let data: String = temp
126                .split(HASH_SYMBOL)
127                .next()
128                .unwrap_or_default()
129                .to_string();
130            data.into()
131        });
132        let query: RequestQuery = Self::parse_query(&query_string);
133        let path: RequestPath = if let Some(i) = query_index.or(hash_index) {
134            full_path[..i].to_string()
135        } else {
136            full_path
137        };
138        let mut headers: RequestHeaders = HashMap::new();
139        let mut host: RequestHost = EMPTY_STR.to_owned();
140        let mut content_length: usize = 0;
141        loop {
142            let mut header_line: String = String::new();
143            let _ = tokio::io::AsyncBufReadExt::read_line(reader, &mut header_line).await;
144            let header_line: &str = header_line.trim();
145            if header_line.is_empty() {
146                break;
147            }
148            let parts: Vec<&str> = header_line.splitn(2, COLON_SPACE_SYMBOL).collect();
149            if parts.len() != 2 {
150                continue;
151            }
152            let key: String = parts[0].trim().to_string();
153            let value: String = parts[1].trim().to_string();
154            if key.eq_ignore_ascii_case(HOST) {
155                host = value.to_string();
156            }
157            if key.eq_ignore_ascii_case(CONTENT_LENGTH) {
158                content_length = value.parse().unwrap_or(0);
159            }
160            headers.insert(key, value);
161        }
162        let mut body: RequestBody = Vec::new();
163        if content_length > 0 {
164            body.resize(content_length, 0);
165            let _ = tokio::io::AsyncReadExt::read_exact(reader, &mut body);
166        }
167        Ok(Request {
168            method,
169            host,
170            path,
171            query,
172            headers,
173            body,
174        })
175    }
176
177    /// Creates a new `Request` object from a TCP stream.
178    ///
179    /// # Parameters
180    /// - `stream`: A reference to a `&std::net::TcpStream` representing the incoming connection.
181    ///
182    /// # Returns
183    /// - `Ok`: A `Request` object populated with the HTTP request data.
184    /// - `Err`: An `Error` if the request is invalid or cannot be read.
185    #[inline]
186    pub fn from_stream(stream: &std::net::TcpStream) -> RequestNewResult {
187        let mut reader: std::io::BufReader<&std::net::TcpStream> = std::io::BufReader::new(stream);
188        Self::from_reader(&mut reader)
189    }
190
191    /// Creates a new `Request` object from a TCP stream.
192    ///
193    /// # Parameters
194    /// - `stream`: A reference to a `&ArcRwLockStream` representing the incoming connection.
195    ///
196    /// # Returns
197    /// - `Ok`: A `Request` object populated with the HTTP request data.
198    /// - `Err`: An `Error` if the request is invalid or cannot be read.
199    #[inline]
200    pub async fn from_tokio_stream(stream: &ArcRwLockStream) -> RequestNewResult {
201        let mut buf_stream: RwLockWriteGuard<'_, tokio::net::TcpStream> = stream.write().await;
202        let mut reader: tokio::io::BufReader<&mut tokio::net::TcpStream> =
203            tokio::io::BufReader::new(&mut buf_stream);
204        Self::from_tokio_reader(&mut reader).await
205    }
206
207    /// Parse query
208    ///
209    /// # Parameters
210    /// - `query`: &str
211    ///
212    /// # Returns
213    /// - RequestQuery
214    #[inline]
215    fn parse_query(query: &str) -> RequestQuery {
216        let mut query_map: RequestQuery = HashMap::new();
217        for pair in query.split(AND) {
218            let mut parts: SplitN<'_, &str> = pair.splitn(2, EQUAL);
219            let key: String = parts.next().unwrap_or_default().to_string();
220            let value: String = parts.next().unwrap_or_default().to_string();
221            if !key.is_empty() {
222                query_map.insert(key, value);
223            }
224        }
225        query_map
226    }
227
228    /// Adds a header to the request.
229    ///
230    /// This function inserts a key-value pair into the request headers.
231    /// The key and value are converted into `String`, allowing for efficient handling of both owned and borrowed string data.
232    ///
233    /// # Parameters
234    /// - `key`: The header key, which will be converted into a `String`.
235    /// - `value`: The value of the header, which will be converted into a `String`.
236    ///
237    /// # Returns
238    /// - Returns a mutable reference to the current instance (`&mut Self`), allowing for method chaining.
239    #[inline]
240    pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
241    where
242        K: Into<String>,
243        V: Into<String>,
244    {
245        self.headers.insert(key.into(), value.into());
246        self
247    }
248
249    /// Set the body of the response.
250    ///
251    /// This method allows you to set the body of the response by converting the provided
252    /// value into a `RequestBody` type. The `body` is updated with the converted value,
253    /// and the method returns a mutable reference to the current instance for method chaining.
254    ///
255    /// # Parameters
256    /// - `body`: The body of the response to be set. It can be any type that can be converted
257    ///   into a `RequestBody` using the `Into` trait.
258    ///
259    /// # Return Value
260    /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
261    /// Set the body of the response.
262    ///
263    /// This method allows you to set the body of the response by converting the provided
264    /// value into a `RequestBody` type. The `body` is updated with the converted value,
265    /// and the method returns a mutable reference to the current instance for method chaining.
266    ///
267    /// # Parameters
268    /// - `body`: The body of the response to be set. It can be any type that can be converted
269    ///   into a `RequestBody` using the `Into` trait.
270    ///
271    /// # Return Value
272    /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
273    #[inline]
274    pub fn set_body<T: Into<RequestBody>>(&mut self, body: T) -> &mut Self {
275        self.body = body.into();
276        self
277    }
278
279    /// Set the HTTP method of the request.
280    ///
281    /// This method allows you to set the HTTP method (e.g., GET, POST) of the request
282    /// by converting the provided value into a `RequestMethod` type. The `method` is updated
283    /// with the converted value, and the method returns a mutable reference to the current
284    /// instance for method chaining.
285    ///
286    /// # Parameters
287    /// - `method`: The HTTP method to be set for the request. It can be any type that can
288    ///   be converted into a `RequestMethod` using the `Into` trait.
289    ///
290    /// # Return Value
291    /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
292    #[inline]
293    pub fn set_method<T: Into<RequestMethod>>(&mut self, method: T) -> &mut Self {
294        self.method = method.into();
295        self
296    }
297
298    /// Set the host of the request.
299    ///
300    /// This method allows you to set the host (e.g., www.example.com) for the request
301    /// by converting the provided value into a `RequestHost` type. The `host` is updated
302    /// with the converted value, and the method returns a mutable reference to the current
303    /// instance for method chaining.
304    ///
305    /// # Parameters
306    /// - `host`: The host to be set for the request. It can be any type that can be converted
307    ///   into a `RequestHost` using the `Into` trait.
308    ///
309    /// # Return Value
310    /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
311    #[inline]
312    pub fn set_host<T: Into<RequestHost>>(&mut self, host: T) -> &mut Self {
313        self.host = host.into();
314        self
315    }
316
317    /// Set the path of the request.
318    ///
319    /// This method allows you to set the path (e.g., /api/v1/resource) for the request
320    /// by converting the provided value into a `RequestPath` type. The `path` is updated
321    /// with the converted value, and the method returns a mutable reference to the current
322    /// instance for method chaining.
323    ///
324    /// # Parameters
325    /// - `path`: The path to be set for the request. It can be any type that can be converted
326    ///   into a `RequestPath` using the `Into` trait.
327    ///
328    /// # Return Value
329    /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
330    #[inline]
331    pub fn set_path<T: Into<RequestPath>>(&mut self, path: T) -> &mut Self {
332        self.path = path.into();
333        self
334    }
335
336    /// Set the query string of the request.
337    ///
338    /// This method allows you to set the query string (e.g., ?key=value) for the request
339    /// by converting the provided value into a `RequestQuery` type. The `query` is updated
340    /// with the converted value, and the method returns a mutable reference to the current
341    /// instance for method chaining.
342    ///
343    /// # Parameters
344    /// - `query`: The query string to be set for the request. It can be any type that can
345    ///   be converted into a `RequestQuery` using the `Into` trait.
346    ///
347    /// # Return Value
348    /// - Returns a mutable reference to the current instance of the struct, enabling method chaining.
349    #[inline]
350    pub fn set_query<T: Into<RequestQuery>>(&mut self, query: T) -> &mut Self {
351        self.query = query.into();
352        self
353    }
354}