Skip to main content

http_request/request/http_request/
impl.rs

1use super::*;
2
3impl<T: AsyncRead + AsyncWrite + Unpin + Send> AsyncReadWrite for T {}
4impl<T: Read + Write> ReadWrite for T {}
5
6/// Inherent implementation of [`HttpRequest`].
7impl HttpRequest {
8    /// Send the HTTP request synchronously, returning the parsed response.
9    ///
10    /// # Returns
11    ///
12    /// - `RequestResult` - The parsed HTTP response, or a `RequestError` on failure.
13    pub fn send(&mut self) -> RequestResult {
14        self.send_sync()
15    }
16
17    /// Send the HTTP request asynchronously, returning the parsed response.
18    ///
19    /// # Returns
20    ///
21    /// - `RequestResult` - The parsed HTTP response, or a `RequestError` on failure.
22    pub async fn send_async(&mut self) -> RequestResult {
23        self.send_async_impl().await
24    }
25
26    /// Parse the configured URL into a [`HttpUrlComponents`].
27    ///
28    /// # Returns
29    ///
30    /// - `Result<HttpUrlComponents, RequestError>` - The parsed URL components, or a `RequestError` when the URL is malformed.
31    pub(crate) fn parse_url(&self) -> Result<HttpUrlComponents, RequestError> {
32        let (scheme, remainder): (&str, &str) = self
33            .get_url()
34            .split_once(SCHEME_SEPARATOR)
35            .ok_or(RequestError::InvalidUrlScheme(HttpStatus::BadRequest))?;
36        if !scheme.eq_ignore_ascii_case(HTTP_SCHEME)
37            && !scheme.eq_ignore_ascii_case(HTTPS_LOWERCASE)
38        {
39            return Err(RequestError::InvalidUrlScheme(HttpStatus::BadRequest));
40        }
41        let authority_end: usize = remainder
42            .find([URL_SLASH, URL_QUERY, URL_FRAGMENT])
43            .unwrap_or(remainder.len());
44        let authority: &str = &remainder[..authority_end];
45        if authority.is_empty()
46            || authority.contains(URL_BACKSLASH)
47            || authority
48                .bytes()
49                .any(|byte: u8| byte.is_ascii_control() || byte == SPACE_U8)
50        {
51            return Err(RequestError::InvalidUrlHost(HttpStatus::BadRequest));
52        }
53        let target: &str = remainder[authority_end..]
54            .split(URL_FRAGMENT)
55            .next()
56            .unwrap_or_default();
57        let (path, query): (&str, &str) = target
58            .split_once(URL_QUERY)
59            .unwrap_or((target, Default::default()));
60        if path
61            .bytes()
62            .any(|byte: u8| byte.is_ascii_control() || byte == SPACE_U8)
63        {
64            return Err(RequestError::InvalidUrlPath(HttpStatus::BadRequest));
65        }
66        if query
67            .bytes()
68            .any(|byte: u8| byte.is_ascii_control() || byte == SPACE_U8)
69        {
70            return Err(RequestError::InvalidUrlQuery(HttpStatus::BadRequest));
71        }
72        let components: HttpUrlComponents = match HttpUrlComponents::parse(self.get_url()) {
73            Ok(value) => value,
74            Err(_) => return Err(RequestError::InvalidUrlHost(HttpStatus::BadRequest)),
75        };
76        let host: String = components.get_host().unwrap_or_default();
77        if host.is_empty()
78            || host
79                .bytes()
80                .any(|byte: u8| byte.is_ascii_control() || byte == SPACE_U8)
81        {
82            return Err(RequestError::InvalidUrlHost(HttpStatus::BadRequest));
83        }
84        Ok(components)
85    }
86
87    /// `Host` + path (including query string) for the request line.
88    ///
89    /// # Returns
90    ///
91    /// - `Result<String, RequestError>` - The safe request target, or an error for an invalid URL.
92    pub(crate) fn full_path(&self) -> Result<String, RequestError> {
93        let url_obj: HttpUrlComponents = self.parse_url()?;
94        let query: String = url_obj.get_query().unwrap_or_default();
95        let path: String = url_obj.get_path().unwrap_or_default();
96        if query.is_empty() {
97            Ok(path)
98        } else {
99            Ok(format!("{}{}{}", path, QUERY, query))
100        }
101    }
102
103    /// Lower-case the protocol ("http" / "https").
104    ///
105    /// # Arguments
106    ///
107    /// - `&RequestConfig` - The configuration whose HTTP version is lower-cased.
108    ///
109    /// # Returns
110    ///
111    /// - `String` - The lower-cased protocol name.
112    pub(crate) fn protocol_lower(config: &RequestConfig) -> String {
113        config.get_http_version().to_string().to_ascii_lowercase()
114    }
115
116    /// Build the wire-format header bytes, with `Host`, `Content-Length`,
117    /// `Accept`, `User-Agent` auto-filled if missing.
118    ///
119    /// # Arguments
120    ///
121    /// - `usize` - The length of the encoded request body.
122    ///
123    /// # Returns
124    ///
125    /// - `Result<Vec<u8>, RequestError>` - The validated wire-format headers, or an error for invalid fields.
126    pub(crate) fn header_bytes(&self, body_length: usize) -> Result<Vec<u8>, RequestError> {
127        let mut header: HashMap<String, String> = self.get_headers().clone();
128        let host_value: String = self.parse_url()?.get_host().unwrap_or_default();
129        let content_length_value: String = body_length.to_string();
130        if !Self::header_has_key(&header, HOST) {
131            header.insert(HOST.to_ascii_lowercase(), host_value);
132        }
133        if !Self::header_has_key(&header, CONTENT_LENGTH) {
134            header.insert(CONTENT_LENGTH.to_ascii_lowercase(), content_length_value);
135        }
136        if !Self::header_has_key(&header, ACCEPT) {
137            header.insert(ACCEPT.to_ascii_lowercase(), ACCEPT_ANY.to_owned());
138        }
139        if !Self::header_has_key(&header, USER_AGENT) {
140            header.insert(USER_AGENT.to_ascii_lowercase(), APP_NAME.to_owned());
141        }
142        validate_request_headers(&header)?;
143        let estimated_size: usize = header
144            .iter()
145            .map(|(k, v): (&String, &String)| k.len() + v.len() + 4)
146            .sum();
147        let mut out: Vec<u8> = Vec::with_capacity(estimated_size);
148        for (key, value) in &header {
149            out.extend_from_slice(key.as_bytes());
150            out.extend_from_slice(HEADER_VALUE_SEPARATOR);
151            out.extend_from_slice(value.as_bytes());
152            out.extend_from_slice(HTTP_BR_BYTES);
153        }
154        Ok(out)
155    }
156
157    /// Check whether a header map already contains a key, comparing case-insensitively.
158    ///
159    /// # Arguments
160    ///
161    /// - `&HashMap<String, String>` - The header map to search.
162    /// - `&str` - The header key to look for.
163    ///
164    /// # Returns
165    ///
166    /// - `bool` - `true` when the map contains the key, `false` otherwise.
167    fn header_has_key(header: &HashMap<String, String>, target_key: &str) -> bool {
168        header
169            .keys()
170            .any(|key: &String| key.eq_ignore_ascii_case(target_key))
171    }
172
173    /// Encode `self.body` according to the `Content-Type` header.
174    /// Returns empty bytes if no recognised content type.
175    ///
176    /// # Returns
177    ///
178    /// - `Vec<u8>` - The encoded request body, empty when the content type is unrecognised.
179    pub(crate) fn body_bytes(&self) -> Vec<u8> {
180        let ct: Option<String> = self
181            .headers
182            .iter()
183            .find(|(k, _): &(&String, &String)| k.eq_ignore_ascii_case(CONTENT_TYPE))
184            .map(|(_, v): (&String, &String)| v.clone());
185        match ct {
186            Some(value) => value
187                .to_lowercase()
188                .parse::<ContentType>()
189                .unwrap_or_default()
190                .get_body_string(&String::from_utf8_lossy(self.get_body().as_slice()))
191                .into_bytes(),
192            None => Vec::new(),
193        }
194    }
195
196    /// Synchronous send.
197    ///
198    /// # Returns
199    ///
200    /// - `RequestResult` - The parsed HTTP response, or a `RequestError` on failure.
201    pub(crate) fn send_sync(&mut self) -> RequestResult {
202        let url_obj: HttpUrlComponents = self.parse_url()?;
203        validate_request_headers(self.get_headers())?;
204        let host: String = url_obj.get_host().clone().unwrap_or_default();
205        let port: u16 = self.resolve_port(url_obj.get_port().unwrap_or_default());
206        let mut stream: BoxReadWrite = self.open_sync_stream(host, port)?;
207        let method: Method = self.get_method();
208        if method.is_get() {
209            self.send_get_request_sync(&mut stream)
210        } else if method.is_post() {
211            self.send_post_request_sync(&mut stream)
212        } else {
213            Err(RequestError::Request(METHOD_NOT_ALLOWED.to_string()))
214        }
215    }
216
217    /// Asynchronous send.
218    ///
219    /// # Returns
220    ///
221    /// - `RequestResult` - The parsed HTTP response, or a `RequestError` on failure.
222    async fn send_async_impl(&mut self) -> RequestResult {
223        let url_obj: HttpUrlComponents = self.parse_url()?;
224        validate_request_headers(self.get_headers())?;
225        let host: String = url_obj.get_host().clone().unwrap_or_default();
226        let port: u16 = self.resolve_port(url_obj.get_port().unwrap_or_default());
227        let mut stream: BoxAsyncReadWrite = self.open_async_stream(host, port).await?;
228        let method: Method = self.get_method();
229        if method.is_get() {
230            self.send_get_request_async(&mut stream).await
231        } else if method.is_post() {
232            self.send_post_request_async(&mut stream).await
233        } else {
234            Err(RequestError::Request(METHOD_NOT_ALLOWED.to_string()))
235        }
236    }
237
238    /// Resolve the effective TCP port, falling back to the protocol default.
239    ///
240    /// # Arguments
241    ///
242    /// - `u16` - The port parsed from the URL, `0` when the URL omits one.
243    ///
244    /// # Returns
245    ///
246    /// - `u16` - The port from the URL, or the protocol default when it is `0`.
247    fn resolve_port(&self, port: u16) -> u16 {
248        if port != 0 {
249            return port;
250        }
251        let protocol: String = Self::protocol_lower(self.get_config());
252        Protocol::get_port(&protocol)
253    }
254
255    /// Check whether the request uses the HTTPS protocol.
256    ///
257    /// # Returns
258    ///
259    /// - `bool` - `true` when the configured protocol is `https`, `false` otherwise.
260    fn is_https(&self) -> bool {
261        Self::protocol_lower(self.get_config()) == HTTPS_LOWERCASE
262    }
263
264    // ---------- sync stream plumbing ----------
265
266    /// Open a synchronous stream to the target host, directly or through a proxy.
267    ///
268    /// # Arguments
269    ///
270    /// - `String` - The target host name or IP address.
271    /// - `u16` - The target TCP port.
272    ///
273    /// # Returns
274    ///
275    /// - `Result<BoxReadWrite, RequestError>` - The connected stream, TLS-wrapped for HTTPS, or a `RequestError` on failure.
276    fn open_sync_stream(&self, host: String, port: u16) -> Result<BoxReadWrite, RequestError> {
277        if let Some(proxy) = &self.get_config().get_proxy() {
278            return self.open_sync_proxy_stream(host, port, proxy);
279        }
280        let timeout: Duration = Duration::from_millis(self.get_config().get_timeout());
281        let tcp: TcpStream = TcpStream::connect((host.clone(), port))
282            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
283        tcp.set_read_timeout(Some(timeout))
284            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
285        tcp.set_write_timeout(Some(timeout))
286            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
287        if self.is_https() {
288            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
289            let tls_cfg: ClientConfig = ClientConfig::builder()
290                .with_root_certificates(roots)
291                .with_no_client_auth();
292            let dns: ServerName<'static> = ServerName::try_from(host.clone())
293                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
294            let session: ClientConnection = ClientConnection::new(Arc::new(tls_cfg), dns)
295                .map_err(|e: rustls::Error| RequestError::Request(e.to_string()))?;
296            Ok(Box::new(StreamOwned::new(session, tcp)))
297        } else {
298            Ok(Box::new(tcp))
299        }
300    }
301
302    /// Write a GET request to the stream and read the response.
303    ///
304    /// # Arguments
305    ///
306    /// - `&mut BoxReadWrite` - The connected stream used to write the request and read the response.
307    ///
308    /// # Returns
309    ///
310    /// - `Result<HttpResponse, RequestError>` - The parsed response, or a `RequestError` on I/O or parse failure.
311    fn send_get_request_sync(
312        &mut self,
313        stream: &mut BoxReadWrite,
314    ) -> Result<HttpResponse, RequestError> {
315        let path: String = self.full_path()?;
316        let header_bytes: Vec<u8> = self.header_bytes(0)?;
317        let version: String = self.get_config().get_http_version().to_string();
318        let request: Vec<u8> = build_http_request("GET", path, header_bytes, None, version);
319        stream
320            .write_all(&request)
321            .and_then(|_: ()| stream.flush())
322            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
323        self.read_response_sync(stream)
324    }
325
326    /// Write a POST request with the encoded body to the stream and read the response.
327    ///
328    /// # Arguments
329    ///
330    /// - `&mut BoxReadWrite` - The connected stream used to write the request and read the response.
331    ///
332    /// # Returns
333    ///
334    /// - `Result<HttpResponse, RequestError>` - The parsed response, or a `RequestError` on I/O or parse failure.
335    fn send_post_request_sync(
336        &mut self,
337        stream: &mut BoxReadWrite,
338    ) -> Result<HttpResponse, RequestError> {
339        let body_bytes: Vec<u8> = self.body_bytes();
340        let path: String = self.full_path()?;
341        let header_bytes: Vec<u8> = self.header_bytes(body_bytes.len())?;
342        let version: String = self.get_config().get_http_version().to_string();
343        let request: Vec<u8> = build_http_request(
344            HTTP_METHOD_POST,
345            path,
346            header_bytes,
347            Some(body_bytes),
348            version,
349        );
350        stream
351            .write_all(&request)
352            .and_then(|_: ()| stream.flush())
353            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
354        self.read_response_sync(stream)
355    }
356
357    /// Read a full response from the stream, handling content-length and chunked bodies.
358    ///
359    /// # Arguments
360    ///
361    /// - `&mut BoxReadWrite` - The connected stream to read the response from.
362    ///
363    /// # Returns
364    ///
365    /// - `Result<HttpResponse, RequestError>` - The parsed response, or a `RequestError` on I/O or parse failure.
366    fn read_response_sync(
367        &mut self,
368        stream: &mut BoxReadWrite,
369    ) -> Result<HttpResponse, RequestError> {
370        let buffer_size: usize = self.get_config().buffer_size;
371        let mut buffer: Vec<u8> = vec![0u8; buffer_size];
372        let mut response_bytes: Vec<u8> = Vec::with_capacity(buffer_size.max(8192));
373        let mut headers_done: bool = false;
374        let mut content_length: usize = 0usize;
375        let mut redirect_url: Option<Vec<u8>> = None;
376        let mut headers_end_pos: usize = 0usize;
377        let mut is_chunked: bool = false;
378        let version_bytes: Vec<u8> = self
379            .config
380            .get_http_version()
381            .to_string()
382            .to_ascii_lowercase()
383            .into_bytes();
384        let location_key: Vec<u8> = format!("{}:", LOCATION.to_ascii_lowercase()).into_bytes();
385        loop {
386            let n: usize = stream
387                .read(&mut buffer)
388                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
389            if n == 0 {
390                break;
391            }
392            let new_cap: usize =
393                calculate_buffer_capacity(&response_bytes, n, response_bytes.capacity());
394            if new_cap > 0 {
395                response_bytes.reserve(new_cap - response_bytes.capacity());
396            }
397            let old_len: usize = response_bytes.len();
398            response_bytes.extend_from_slice(&buffer[..n]);
399            if !headers_done {
400                let search_start: usize = old_len.saturating_sub(3);
401                if let Some(pos) = find_double_crlf(&response_bytes, search_start) {
402                    headers_done = true;
403                    headers_end_pos = pos + 4;
404                    parse_response_headers(
405                        &response_bytes[..headers_end_pos],
406                        &version_bytes,
407                        &location_key,
408                        &mut content_length,
409                        &mut redirect_url,
410                        &mut is_chunked,
411                    )?;
412                }
413            }
414            if headers_done {
415                if is_chunked {
416                    if Self::is_chunked_response_complete(&response_bytes[headers_end_pos..])? {
417                        break;
418                    }
419                } else if response_bytes.len() >= headers_end_pos + content_length {
420                    response_bytes.truncate(headers_end_pos + content_length);
421                    break;
422                }
423            }
424        }
425        if !headers_done {
426            return Err(RequestError::HttpRequestPartsInsufficient(
427                HttpStatus::BadRequest,
428            ));
429        }
430        if !is_chunked && response_bytes.len() < headers_end_pos + content_length {
431            return Err(RequestError::InvalidContentLength(HttpStatus::BadRequest));
432        }
433        if is_chunked {
434            let body_bytes: Vec<u8> = response_bytes[headers_end_pos..].to_vec();
435            let decoded: Vec<u8> = parse_chunked_body(&body_bytes)?;
436            response_bytes.truncate(headers_end_pos);
437            response_bytes.extend_from_slice(&decoded);
438        }
439        let mut response: HttpResponse = HttpResponse::from_bytes(&response_bytes);
440        if !self.get_config().get_redirect() || redirect_url.is_none() {
441            if self.get_config().get_decode() {
442                response = response.decode(self.get_config().buffer_size);
443            }
444            return Ok(response);
445        }
446        let url_bytes: Vec<u8> = redirect_url
447            .take()
448            .ok_or_else(|| RequestError::Request(MISSING_REDIRECT_URL.to_string()))?;
449        let url: String = String::from_utf8(url_bytes)
450            .map_err(|e: FromUtf8Error| RequestError::Request(e.to_string()))?;
451        self.handle_redirect(url)
452    }
453
454    /// Follow a redirect by recording the visit and re-issuing the request.
455    ///
456    /// # Arguments
457    ///
458    /// - `String` - The absolute redirect target URL taken from the `Location` header.
459    ///
460    /// # Returns
461    ///
462    /// - `Result<HttpResponse, RequestError>` - The response of the redirect target, or a `RequestError` when redirects are disabled, looping, or exhausted.
463    fn handle_redirect(&mut self, url: String) -> Result<HttpResponse, RequestError> {
464        if !self.get_config().get_redirect() {
465            return Err(RequestError::Request(REDIRECT_NOT_ENABLED.to_string()));
466        }
467        if self.get_tmp().get_visit_url().contains(&url) {
468            return Err(RequestError::Request(REDIRECT_URL_DEAD_LOOP.to_string()));
469        }
470        self.get_mut_tmp().get_mut_visit_url().insert(url.clone());
471        if self.get_config().get_max_redirect_times() == 0 {
472            return Err(RequestError::Request(
473                MAX_REDIRECT_TIMES_EXCEEDED.to_string(),
474            ));
475        }
476        let remaining: usize = self.get_config().get_max_redirect_times();
477        self.get_mut_config()
478            .set_max_redirect_times(remaining.saturating_sub(1));
479        self.set_url(url);
480        self.send_sync()
481    }
482
483    /// Check whether a chunked response body has received its terminating zero-length chunk.
484    ///
485    /// # Arguments
486    ///
487    /// - `&[u8]` - The raw chunked body bytes received so far.
488    ///
489    /// # Returns
490    ///
491    /// - `Result<bool, RequestError>` - Completion state, or an error for invalid chunk framing.
492    fn is_chunked_response_complete(body_bytes: &[u8]) -> Result<bool, RequestError> {
493        chunked_body_end(body_bytes).map(|end: Option<usize>| end.is_some())
494    }
495
496    // ---------- async stream plumbing ----------
497
498    /// Open an asynchronous stream to the target host, directly or through a proxy.
499    ///
500    /// # Arguments
501    ///
502    /// - `String` - The target host name or IP address.
503    /// - `u16` - The target TCP port.
504    ///
505    /// # Returns
506    ///
507    /// - `Result<BoxAsyncReadWrite, RequestError>` - The connected stream, TLS-wrapped for HTTPS, or a `RequestError` on failure.
508    async fn open_async_stream(
509        &self,
510        host: String,
511        port: u16,
512    ) -> Result<BoxAsyncReadWrite, RequestError> {
513        if let Some(proxy) = &self.get_config().get_proxy() {
514            return self.open_async_proxy_stream(host, port, proxy).await;
515        }
516        let tcp: http_type::tokio::net::TcpStream =
517            http_type::tokio::net::TcpStream::connect((host.clone(), port))
518                .await
519                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
520        if self.is_https() {
521            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
522            let tls_cfg: ClientConfig = ClientConfig::builder()
523                .with_root_certificates(roots)
524                .with_no_client_auth();
525            let connector: TlsConnector = TlsConnector::from(Arc::new(tls_cfg));
526            let dns: ServerName<'static> = ServerName::try_from(host.clone())
527                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
528            let tls: tokio_rustls::client::TlsStream<http_type::tokio::net::TcpStream> = connector
529                .connect(dns, tcp)
530                .await
531                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
532            Ok(Box::new(tls))
533        } else {
534            Ok(Box::new(tcp))
535        }
536    }
537
538    /// Write a GET request to the stream and read the response.
539    ///
540    /// # Arguments
541    ///
542    /// - `&mut BoxAsyncReadWrite` - The connected stream used to write the request and read the response.
543    ///
544    /// # Returns
545    ///
546    /// - `Result<HttpResponse, RequestError>` - The parsed response, or a `RequestError` on I/O or parse failure.
547    async fn send_get_request_async(
548        &mut self,
549        stream: &mut BoxAsyncReadWrite,
550    ) -> Result<HttpResponse, RequestError> {
551        let path: String = self.full_path()?;
552        let header_bytes: Vec<u8> = self.header_bytes(0)?;
553        let version: String = self.get_config().get_http_version().to_string();
554        let request: Vec<u8> = build_http_request("GET", path, header_bytes, None, version);
555        stream
556            .write_all(&request)
557            .await
558            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
559        stream
560            .flush()
561            .await
562            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
563        self.read_response_async(stream).await
564    }
565
566    /// Write a POST request with the encoded body to the stream and read the response.
567    ///
568    /// # Arguments
569    ///
570    /// - `&mut BoxAsyncReadWrite` - The connected stream used to write the request and read the response.
571    ///
572    /// # Returns
573    ///
574    /// - `Result<HttpResponse, RequestError>` - The parsed response, or a `RequestError` on I/O or parse failure.
575    async fn send_post_request_async(
576        &mut self,
577        stream: &mut BoxAsyncReadWrite,
578    ) -> Result<HttpResponse, RequestError> {
579        let body_bytes: Vec<u8> = self.body_bytes();
580        let path: String = self.full_path()?;
581        let header_bytes: Vec<u8> = self.header_bytes(body_bytes.len())?;
582        let version: String = self.get_config().get_http_version().to_string();
583        let request: Vec<u8> = build_http_request(
584            HTTP_METHOD_POST,
585            path,
586            header_bytes,
587            Some(body_bytes),
588            version,
589        );
590        stream
591            .write_all(&request)
592            .await
593            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
594        stream
595            .flush()
596            .await
597            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
598        self.read_response_async(stream).await
599    }
600
601    /// Read a full response from the stream, handling content-length and chunked bodies.
602    ///
603    /// # Arguments
604    ///
605    /// - `&mut BoxAsyncReadWrite` - The connected stream to read the response from.
606    ///
607    /// # Returns
608    ///
609    /// - `Result<HttpResponse, RequestError>` - The parsed response, or a `RequestError` on I/O or parse failure.
610    async fn read_response_async(
611        &mut self,
612        stream: &mut BoxAsyncReadWrite,
613    ) -> Result<HttpResponse, RequestError> {
614        let buffer_size: usize = self.get_config().buffer_size;
615        let mut buffer: Vec<u8> = vec![0u8; buffer_size];
616        let mut response_bytes: Vec<u8> = Vec::with_capacity(buffer_size.max(8192));
617        let mut headers_done: bool = false;
618        let mut content_length: usize = 0usize;
619        let mut redirect_url: Option<Vec<u8>> = None;
620        let mut headers_end_pos: usize = 0usize;
621        let mut is_chunked: bool = false;
622        let version_bytes: Vec<u8> = self
623            .config
624            .get_http_version()
625            .to_string()
626            .to_ascii_lowercase()
627            .into_bytes();
628        let location_key: Vec<u8> = format!("{}:", LOCATION.to_ascii_lowercase()).into_bytes();
629        loop {
630            let n: usize = stream
631                .read(&mut buffer)
632                .await
633                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
634            if n == 0 {
635                break;
636            }
637            let new_cap: usize =
638                calculate_buffer_capacity(&response_bytes, n, response_bytes.capacity());
639            if new_cap > 0 {
640                response_bytes.reserve(new_cap - response_bytes.capacity());
641            }
642            let old_len: usize = response_bytes.len();
643            response_bytes.extend_from_slice(&buffer[..n]);
644            if !headers_done {
645                let search_start: usize = old_len.saturating_sub(3);
646                if let Some(pos) = find_double_crlf(&response_bytes, search_start) {
647                    headers_done = true;
648                    headers_end_pos = pos + 4;
649                    parse_response_headers(
650                        &response_bytes[..headers_end_pos],
651                        &version_bytes,
652                        &location_key,
653                        &mut content_length,
654                        &mut redirect_url,
655                        &mut is_chunked,
656                    )?;
657                }
658            }
659            if headers_done {
660                if is_chunked {
661                    if Self::is_chunked_response_complete(&response_bytes[headers_end_pos..])? {
662                        break;
663                    }
664                } else if response_bytes.len() >= headers_end_pos + content_length {
665                    response_bytes.truncate(headers_end_pos + content_length);
666                    break;
667                }
668            }
669        }
670        if !headers_done {
671            return Err(RequestError::HttpRequestPartsInsufficient(
672                HttpStatus::BadRequest,
673            ));
674        }
675        if !is_chunked && response_bytes.len() < headers_end_pos + content_length {
676            return Err(RequestError::InvalidContentLength(HttpStatus::BadRequest));
677        }
678        if is_chunked {
679            let body_bytes: Vec<u8> = response_bytes[headers_end_pos..].to_vec();
680            let decoded: Vec<u8> = parse_chunked_body(&body_bytes)?;
681            response_bytes.truncate(headers_end_pos);
682            response_bytes.extend_from_slice(&decoded);
683        }
684        let mut response: HttpResponse = HttpResponse::from_bytes(&response_bytes);
685        if !self.get_config().get_redirect() || redirect_url.is_none() {
686            if self.get_config().get_decode() {
687                response = response.decode(self.get_config().buffer_size);
688            }
689            return Ok(response);
690        }
691        let url_bytes: Vec<u8> = redirect_url
692            .take()
693            .ok_or_else(|| RequestError::Request(MISSING_REDIRECT_URL.to_string()))?;
694        let url: String = String::from_utf8(url_bytes)
695            .map_err(|e: FromUtf8Error| RequestError::Request(e.to_string()))?;
696        self.handle_redirect_async(url).await
697    }
698
699    /// Follow a redirect by recording the visit and re-issuing the request.
700    ///
701    /// # Arguments
702    ///
703    /// - `String` - The absolute redirect target URL taken from the `Location` header.
704    ///
705    /// # Returns
706    ///
707    /// - `Result<HttpResponse, RequestError>` - The response of the redirect target, or a `RequestError` when redirects are disabled, looping, or exhausted.
708    async fn handle_redirect_async(&mut self, url: String) -> Result<HttpResponse, RequestError> {
709        if !self.get_config().get_redirect() {
710            return Err(RequestError::Request(REDIRECT_NOT_ENABLED.to_string()));
711        }
712        if self.get_tmp().get_visit_url().contains(&url) {
713            return Err(RequestError::Request(REDIRECT_URL_DEAD_LOOP.to_string()));
714        }
715        self.get_mut_tmp().get_mut_visit_url().insert(url.clone());
716        if self.get_config().get_max_redirect_times() == 0 {
717            return Err(RequestError::Request(
718                MAX_REDIRECT_TIMES_EXCEEDED.to_string(),
719            ));
720        }
721        let remaining: usize = self.get_config().get_max_redirect_times();
722        self.get_mut_config()
723            .set_max_redirect_times(remaining.saturating_sub(1));
724        self.set_url(url);
725        Box::pin(self.send_async()).await
726    }
727
728    // ---------- proxy: sync ----------
729
730    /// Open a synchronous stream to the target through the configured proxy.
731    ///
732    /// # Arguments
733    ///
734    /// - `String` - The target host name or IP address.
735    /// - `u16` - The target TCP port.
736    /// - `&Proxy` - The proxy configuration selecting the HTTP or SOCKS5 handshake.
737    ///
738    /// # Returns
739    ///
740    /// - `Result<BoxReadWrite, RequestError>` - The tunnelled stream, or a `RequestError` when the handshake fails.
741    fn open_sync_proxy_stream(
742        &self,
743        target_host: String,
744        target_port: u16,
745        proxy: &Proxy,
746    ) -> Result<BoxReadWrite, RequestError> {
747        match *proxy.get_proxy_type() {
748            ProxyType::Http | ProxyType::Https => {
749                self.open_sync_http_proxy(target_host, target_port, proxy)
750            }
751            ProxyType::Socks5 => self.open_sync_socks5_proxy(target_host, target_port, proxy),
752        }
753    }
754
755    /// Open a synchronous stream to the target through an HTTP or HTTPS proxy.
756    ///
757    /// # Arguments
758    ///
759    /// - `String` - The target host name or IP address.
760    /// - `u16` - The target TCP port.
761    /// - `&Proxy` - The proxy configuration selecting the HTTP or SOCKS5 handshake.
762    ///
763    /// # Returns
764    ///
765    /// - `Result<BoxReadWrite, RequestError>` - The tunnelled stream, or a `RequestError` when the handshake fails.
766    fn open_sync_http_proxy(
767        &self,
768        target_host: String,
769        target_port: u16,
770        proxy: &Proxy,
771    ) -> Result<BoxReadWrite, RequestError> {
772        let timeout: Duration = Duration::from_millis(self.get_config().get_timeout());
773        let tcp: TcpStream = TcpStream::connect((proxy.get_host().clone(), proxy.get_port()))
774            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
775        tcp.set_read_timeout(Some(timeout))
776            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
777        tcp.set_write_timeout(Some(timeout))
778            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
779        let mut proxy_stream: BoxReadWrite = if proxy.get_proxy_type() == &ProxyType::Https {
780            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
781            let tls_cfg: ClientConfig = ClientConfig::builder()
782                .with_root_certificates(roots)
783                .with_no_client_auth();
784            let dns: ServerName<'static> = ServerName::try_from(proxy.get_host().clone())
785                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
786            let session: ClientConnection = ClientConnection::new(Arc::new(tls_cfg), dns)
787                .map_err(|e: rustls::Error| RequestError::Request(e.to_string()))?;
788            Box::new(StreamOwned::new(session, tcp))
789        } else {
790            Box::new(tcp)
791        };
792        let connect_request: String = if let (Some(u), Some(p)) =
793            (proxy.get_username(), proxy.get_password())
794        {
795            let auth: String = format!("{u}:{p}");
796            let encoded: String = crate::utils::base64_encode(auth.as_bytes());
797            format!(
798                "CONNECT {target_host}:{target_port} HTTP/1.1\r\nHost: {target_host}:{target_port}\r\nProxy-Authorization: Basic {encoded}\r\n\r\n"
799            )
800        } else {
801            format!(
802                "CONNECT {target_host}:{target_port} HTTP/1.1\r\nHost: {target_host}:{target_port}\r\n\r\n"
803            )
804        };
805        proxy_stream
806            .write_all(connect_request.as_bytes())
807            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
808        proxy_stream
809            .flush()
810            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
811        let mut buf: [u8; 1024] = [0u8; 1024];
812        let n: usize = proxy_stream
813            .read(&mut buf)
814            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
815        let s: &str = from_utf8(&buf[..n]).unwrap_or("");
816        let pre_read: Vec<u8> = if let Some(pos) = s.find(HEADER_TERMINATOR) {
817            let header_part: &str = &s[..pos];
818            if !header_part.starts_with(HTTP_1_1_OK_PREFIX)
819                && !header_part.starts_with(HTTP_1_0_OK_PREFIX)
820            {
821                return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
822            }
823            buf[pos + 4..n].to_vec()
824        } else {
825            if !s.starts_with(HTTP_1_1_OK_PREFIX) && !s.starts_with(HTTP_1_0_OK_PREFIX) {
826                return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
827            }
828            Vec::new()
829        };
830        if self.is_https() {
831            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
832            let tls_cfg: ClientConfig = ClientConfig::builder()
833                .with_root_certificates(roots)
834                .with_no_client_auth();
835            let dns: ServerName<'static> = ServerName::try_from(target_host.clone())
836                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
837            let session: ClientConnection = ClientConnection::new(Arc::new(tls_cfg), dns)
838                .map_err(|e: rustls::Error| RequestError::Request(e.to_string()))?;
839            let tunnel: SyncProxyTunnelStream = SyncProxyTunnelStream::new(proxy_stream, pre_read);
840            return Ok(Box::new(StreamOwned::new(session, tunnel)));
841        }
842        Ok(Box::new(SyncProxyTunnelStream::new(proxy_stream, pre_read)))
843    }
844
845    /// Open a synchronous stream to the target through a SOCKS5 proxy.
846    ///
847    /// # Arguments
848    ///
849    /// - `String` - The target host name or IP address.
850    /// - `u16` - The target TCP port.
851    /// - `&Proxy` - The proxy configuration selecting the HTTP or SOCKS5 handshake.
852    ///
853    /// # Returns
854    ///
855    /// - `Result<BoxReadWrite, RequestError>` - The tunnelled stream, or a `RequestError` when the handshake fails.
856    fn open_sync_socks5_proxy(
857        &self,
858        target_host: String,
859        target_port: u16,
860        proxy: &Proxy,
861    ) -> Result<BoxReadWrite, RequestError> {
862        let timeout: Duration = Duration::from_millis(self.get_config().get_timeout());
863        let mut tcp: TcpStream = TcpStream::connect((proxy.get_host().clone(), proxy.get_port()))
864            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
865        tcp.set_read_timeout(Some(timeout))
866            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
867        tcp.set_write_timeout(Some(timeout))
868            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
869        let auth_methods: Vec<u8> =
870            if proxy.get_username().is_some() && proxy.get_password().is_some() {
871                vec![0x05, 0x02, 0x00, 0x02]
872            } else {
873                vec![0x05, 0x01, 0x00]
874            };
875        tcp.write_all(&auth_methods)
876            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
877        let mut resp: [u8; 2] = [0u8; 2];
878        tcp.read_exact(&mut resp)
879            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
880        if resp[0] != 0x05 {
881            return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
882        }
883        match resp[1] {
884            0x00 => {}
885            0x02 => {
886                let (Some(u), Some(p)) = (proxy.get_username(), proxy.get_password()) else {
887                    return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
888                };
889                let mut auth_req: Vec<u8> = vec![0x01];
890                auth_req.push(u.len() as u8);
891                auth_req.extend_from_slice(u.as_bytes());
892                auth_req.push(p.len() as u8);
893                auth_req.extend_from_slice(p.as_bytes());
894                tcp.write_all(&auth_req)
895                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
896                let mut auth_resp: [u8; 2] = [0u8; 2];
897                tcp.read_exact(&mut auth_resp)
898                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
899                if auth_resp[1] != 0x00 {
900                    return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
901                }
902            }
903            _ => return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string())),
904        }
905        let mut connect_req: Vec<u8> = vec![0x05, 0x01, 0x00];
906        if let Ok(ip) = target_host.parse::<Ipv4Addr>() {
907            connect_req.push(0x01);
908            connect_req.extend_from_slice(&ip.octets());
909        } else if let Ok(ip) = target_host.parse::<Ipv6Addr>() {
910            connect_req.push(0x04);
911            connect_req.extend_from_slice(&ip.octets());
912        } else {
913            connect_req.push(0x03);
914            connect_req.push(target_host.len() as u8);
915            connect_req.extend_from_slice(target_host.as_bytes());
916        }
917        connect_req.extend_from_slice(&target_port.to_be_bytes());
918        tcp.write_all(&connect_req)
919            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
920        let mut connect_resp: [u8; 4] = [0u8; 4];
921        tcp.read_exact(&mut connect_resp)
922            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
923        if connect_resp[0] != 0x05 || connect_resp[1] != 0x00 {
924            return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
925        }
926        match connect_resp[3] {
927            0x01 => {
928                let mut skip: [u8; 6] = [0u8; 6];
929                tcp.read_exact(&mut skip)
930                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
931            }
932            0x03 => {
933                let mut len: [u8; 1] = [0u8; 1];
934                tcp.read_exact(&mut len)
935                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
936                let mut skip: Vec<u8> = vec![0u8; len[0] as usize + 2];
937                tcp.read_exact(&mut skip)
938                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
939            }
940            0x04 => {
941                let mut skip: [u8; 18] = [0u8; 18];
942                tcp.read_exact(&mut skip)
943                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
944            }
945            _ => return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string())),
946        }
947        if self.is_https() {
948            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
949            let tls_cfg: ClientConfig = ClientConfig::builder()
950                .with_root_certificates(roots)
951                .with_no_client_auth();
952            let dns: ServerName<'static> = ServerName::try_from(target_host)
953                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
954            let session: ClientConnection = ClientConnection::new(Arc::new(tls_cfg), dns)
955                .map_err(|e: rustls::Error| RequestError::Request(e.to_string()))?;
956            let proxy_box: BoxReadWrite = Box::new(tcp);
957            let tunnel: SyncProxyTunnelStream = SyncProxyTunnelStream::new(proxy_box, Vec::new());
958            return Ok(Box::new(StreamOwned::new(session, tunnel)));
959        }
960        Ok(Box::new(tcp))
961    }
962
963    // ---------- proxy: async ----------
964
965    /// Open an asynchronous stream to the target through the configured proxy.
966    ///
967    /// # Arguments
968    ///
969    /// - `String` - The target host name or IP address.
970    /// - `u16` - The target TCP port.
971    /// - `&Proxy` - The proxy configuration selecting the HTTP or SOCKS5 handshake.
972    ///
973    /// # Returns
974    ///
975    /// - `Result<BoxAsyncReadWrite, RequestError>` - The tunnelled stream, or a `RequestError` when the handshake fails.
976    async fn open_async_proxy_stream(
977        &self,
978        target_host: String,
979        target_port: u16,
980        proxy: &Proxy,
981    ) -> Result<BoxAsyncReadWrite, RequestError> {
982        match *proxy.get_proxy_type() {
983            ProxyType::Http | ProxyType::Https => {
984                self.open_async_http_proxy(target_host, target_port, proxy)
985                    .await
986            }
987            ProxyType::Socks5 => {
988                self.open_async_socks5_proxy(target_host, target_port, proxy)
989                    .await
990            }
991        }
992    }
993
994    /// Open an asynchronous stream to the target through an HTTP or HTTPS proxy.
995    ///
996    /// # Arguments
997    ///
998    /// - `String` - The target host name or IP address.
999    /// - `u16` - The target TCP port.
1000    /// - `&Proxy` - The proxy configuration selecting the HTTP or SOCKS5 handshake.
1001    ///
1002    /// # Returns
1003    ///
1004    /// - `Result<BoxAsyncReadWrite, RequestError>` - The tunnelled stream, or a `RequestError` when the handshake fails.
1005    async fn open_async_http_proxy(
1006        &self,
1007        target_host: String,
1008        target_port: u16,
1009        proxy: &Proxy,
1010    ) -> Result<BoxAsyncReadWrite, RequestError> {
1011        let tcp: http_type::tokio::net::TcpStream =
1012            http_type::tokio::net::TcpStream::connect((proxy.get_host().clone(), proxy.get_port()))
1013                .await
1014                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1015        let mut proxy_stream: BoxAsyncReadWrite = if proxy.get_proxy_type() == &ProxyType::Https {
1016            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
1017            let tls_cfg: ClientConfig = ClientConfig::builder()
1018                .with_root_certificates(roots)
1019                .with_no_client_auth();
1020            let connector: TlsConnector = TlsConnector::from(Arc::new(tls_cfg));
1021            let dns: ServerName<'static> = ServerName::try_from(proxy.get_host().clone())
1022                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
1023            let tls: tokio_rustls::client::TlsStream<http_type::tokio::net::TcpStream> = connector
1024                .connect(dns, tcp)
1025                .await
1026                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1027            Box::new(tls)
1028        } else {
1029            Box::new(tcp)
1030        };
1031        let connect_request: String = if let (Some(u), Some(p)) =
1032            (proxy.get_username(), proxy.get_password())
1033        {
1034            let auth: String = format!("{u}:{p}");
1035            let encoded: String = crate::utils::base64_encode(auth.as_bytes());
1036            format!(
1037                "CONNECT {target_host}:{target_port} HTTP/1.1\r\nHost: {target_host}:{target_port}\r\nProxy-Authorization: Basic {encoded}\r\n\r\n"
1038            )
1039        } else {
1040            format!(
1041                "CONNECT {target_host}:{target_port} HTTP/1.1\r\nHost: {target_host}:{target_port}\r\n\r\n"
1042            )
1043        };
1044        proxy_stream
1045            .write_all(connect_request.as_bytes())
1046            .await
1047            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1048        proxy_stream
1049            .flush()
1050            .await
1051            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1052        let mut buf: [u8; 1024] = [0u8; 1024];
1053        let n: usize = proxy_stream
1054            .read(&mut buf)
1055            .await
1056            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1057        let s: &str = from_utf8(&buf[..n]).unwrap_or("");
1058        let pre_read: Vec<u8> = if let Some(pos) = s.find(HEADER_TERMINATOR) {
1059            let header_part: &str = &s[..pos];
1060            if !header_part.starts_with(HTTP_1_1_OK_PREFIX)
1061                && !header_part.starts_with(HTTP_1_0_OK_PREFIX)
1062            {
1063                return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
1064            }
1065            buf[pos + 4..n].to_vec()
1066        } else {
1067            if !s.starts_with(HTTP_1_1_OK_PREFIX) && !s.starts_with(HTTP_1_0_OK_PREFIX) {
1068                return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
1069            }
1070            Vec::new()
1071        };
1072        if self.is_https() {
1073            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
1074            let tls_cfg: ClientConfig = ClientConfig::builder()
1075                .with_root_certificates(roots)
1076                .with_no_client_auth();
1077            let connector: TlsConnector = TlsConnector::from(Arc::new(tls_cfg));
1078            let dns: ServerName<'static> = ServerName::try_from(target_host.clone())
1079                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
1080            let tunnel: ProxyTunnelStream = ProxyTunnelStream::new(proxy_stream, pre_read);
1081            let tls: tokio_rustls::client::TlsStream<ProxyTunnelStream> = connector
1082                .connect(dns, tunnel)
1083                .await
1084                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1085            Ok(Box::new(tls))
1086        } else {
1087            Ok(Box::new(ProxyTunnelStream::new(proxy_stream, pre_read)))
1088        }
1089    }
1090
1091    /// Open an asynchronous stream to the target through a SOCKS5 proxy.
1092    ///
1093    /// # Arguments
1094    ///
1095    /// - `String` - The target host name or IP address.
1096    /// - `u16` - The target TCP port.
1097    /// - `&Proxy` - The proxy configuration selecting the HTTP or SOCKS5 handshake.
1098    ///
1099    /// # Returns
1100    ///
1101    /// - `Result<BoxAsyncReadWrite, RequestError>` - The tunnelled stream, or a `RequestError` when the handshake fails.
1102    async fn open_async_socks5_proxy(
1103        &self,
1104        target_host: String,
1105        target_port: u16,
1106        proxy: &Proxy,
1107    ) -> Result<BoxAsyncReadWrite, RequestError> {
1108        let mut tcp: http_type::tokio::net::TcpStream =
1109            http_type::tokio::net::TcpStream::connect((proxy.get_host().clone(), proxy.get_port()))
1110                .await
1111                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1112        let auth_methods: Vec<u8> =
1113            if proxy.get_username().is_some() && proxy.get_password().is_some() {
1114                vec![0x05, 0x02, 0x00, 0x02]
1115            } else {
1116                vec![0x05, 0x01, 0x00]
1117            };
1118        tcp.write_all(&auth_methods)
1119            .await
1120            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1121        let mut resp: [u8; 2] = [0u8; 2];
1122        tcp.read_exact(&mut resp)
1123            .await
1124            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1125        if resp[0] != 0x05 {
1126            return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
1127        }
1128        match resp[1] {
1129            0x00 => {}
1130            0x02 => {
1131                let (Some(u), Some(p)) = (proxy.get_username(), proxy.get_password()) else {
1132                    return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
1133                };
1134                let mut auth_req: Vec<u8> = vec![0x01u8];
1135                auth_req.push(u.len() as u8);
1136                auth_req.extend_from_slice(u.as_bytes());
1137                auth_req.push(p.len() as u8);
1138                auth_req.extend_from_slice(p.as_bytes());
1139                tcp.write_all(&auth_req)
1140                    .await
1141                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1142                let mut auth_resp: [u8; 2] = [0u8; 2];
1143                tcp.read_exact(&mut auth_resp)
1144                    .await
1145                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1146                if auth_resp[1] != 0x00 {
1147                    return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
1148                }
1149            }
1150            _ => return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string())),
1151        }
1152        let mut connect_req: Vec<u8> = vec![0x05, 0x01, 0x00];
1153        if let Ok(ip) = target_host.parse::<Ipv4Addr>() {
1154            connect_req.push(0x01);
1155            connect_req.extend_from_slice(&ip.octets());
1156        } else if let Ok(ip) = target_host.parse::<Ipv6Addr>() {
1157            connect_req.push(0x04);
1158            connect_req.extend_from_slice(&ip.octets());
1159        } else {
1160            connect_req.push(0x03);
1161            connect_req.push(target_host.len() as u8);
1162            connect_req.extend_from_slice(target_host.as_bytes());
1163        }
1164        connect_req.extend_from_slice(&target_port.to_be_bytes());
1165        tcp.write_all(&connect_req)
1166            .await
1167            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1168        let mut connect_resp: [u8; 4] = [0u8; 4];
1169        tcp.read_exact(&mut connect_resp)
1170            .await
1171            .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1172        if connect_resp[0] != 0x05 || connect_resp[1] != 0x00 {
1173            return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string()));
1174        }
1175        match connect_resp[3] {
1176            0x01 => {
1177                let mut skip: [u8; 6] = [0u8; 6];
1178                tcp.read_exact(&mut skip)
1179                    .await
1180                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1181            }
1182            0x03 => {
1183                let mut len: [u8; 1] = [0u8; 1];
1184                tcp.read_exact(&mut len)
1185                    .await
1186                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1187                let mut skip: Vec<u8> = vec![0u8; len[0] as usize + 2];
1188                tcp.read_exact(&mut skip)
1189                    .await
1190                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1191            }
1192            0x04 => {
1193                let mut skip: [u8; 18] = [0u8; 18];
1194                tcp.read_exact(&mut skip)
1195                    .await
1196                    .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1197            }
1198            _ => return Err(RequestError::Request(INTERNAL_SERVER_ERROR.to_string())),
1199        }
1200        if self.is_https() {
1201            let roots: RootCertStore = self.get_tmp().get_root_cert().clone();
1202            let tls_cfg: ClientConfig = ClientConfig::builder()
1203                .with_root_certificates(roots)
1204                .with_no_client_auth();
1205            let connector: TlsConnector = TlsConnector::from(Arc::new(tls_cfg));
1206            let dns: ServerName<'static> = ServerName::try_from(target_host)
1207                .map_err(|e: InvalidDnsNameError| RequestError::Request(e.to_string()))?;
1208            let proxy_box: BoxAsyncReadWrite = Box::new(tcp);
1209            let tunnel: ProxyTunnelStream = ProxyTunnelStream::new(proxy_box, Vec::new());
1210            let tls: tokio_rustls::client::TlsStream<ProxyTunnelStream> = connector
1211                .connect(dns, tunnel)
1212                .await
1213                .map_err(|e: io::Error| RequestError::Request(e.to_string()))?;
1214            Ok(Box::new(tls))
1215        } else {
1216            Ok(Box::new(tcp))
1217        }
1218    }
1219}
1220
1221/// Inherent implementation of [`HttpRequest`].
1222impl HttpRequest {
1223    /// Create a GET request for `url`.
1224    ///
1225    /// # Arguments
1226    ///
1227    /// - `T` - The request URL, converted into an owned `String`.
1228    pub fn get<T: Into<String>>(url: T) -> Self {
1229        Self {
1230            method: Method::Get,
1231            url: url.into(),
1232            headers: HashMap::new(),
1233            body: Body::default(),
1234            config: RequestConfig::default(),
1235            tmp: Tmp::default(),
1236        }
1237    }
1238
1239    /// Create a POST request for `url`.
1240    ///
1241    /// # Arguments
1242    ///
1243    /// - `T` - The request URL, converted into an owned `String`.
1244    pub fn post<T: Into<String>>(url: T) -> Self {
1245        Self {
1246            method: Method::Post,
1247            url: url.into(),
1248            headers: HashMap::new(),
1249            body: Body::default(),
1250            config: RequestConfig::default(),
1251            tmp: Tmp::default(),
1252        }
1253    }
1254
1255    /// Builder-style: set method.
1256    ///
1257    /// # Arguments
1258    ///
1259    /// - `Method` - The HTTP method to use for the request.
1260    ///
1261    /// # Returns
1262    ///
1263    /// - `&mut Self` - The request itself, for chaining.
1264    pub fn set_method(&mut self, method: Method) -> &mut Self {
1265        self.method = method;
1266        self
1267    }
1268
1269    /// Builder-style: set URL.
1270    ///
1271    /// # Arguments
1272    ///
1273    /// - `T` - The request URL, converted into an owned `String`.
1274    ///
1275    /// # Returns
1276    ///
1277    /// - `&mut Self` - The request itself, for chaining.
1278    pub fn set_url<T: Into<String>>(&mut self, url: T) -> &mut Self {
1279        self.url = url.into();
1280        self
1281    }
1282
1283    /// Builder-style: set a single header (case-insensitive on read; the
1284    /// last value wins for repeated keys).
1285    ///
1286    /// # Arguments
1287    ///
1288    /// - `K` - The header name, normalised to lowercase.
1289    /// - `V` - The header value, stored as an owned `String`.
1290    ///
1291    /// # Returns
1292    ///
1293    /// - `&mut Self` - The request itself, for chaining.
1294    pub fn set_header<K: AsRef<str>, V: AsRef<str>>(&mut self, key: K, value: V) -> &mut Self {
1295        let normalized: String = Self::normalize_header_key(key.as_ref());
1296        self.headers.insert(normalized, value.as_ref().to_owned());
1297        self
1298    }
1299
1300    /// Remove a header by key.
1301    ///
1302    /// # Arguments
1303    ///
1304    /// - `K` - The header name to remove, matched case-insensitively.
1305    ///
1306    /// # Returns
1307    ///
1308    /// - `&mut Self` - The request itself, for chaining.
1309    pub fn remove_header<K: AsRef<str>>(&mut self, key: K) -> &mut Self {
1310        let normalized: String = Self::normalize_header_key(key.as_ref());
1311        self.get_mut_headers().remove(&normalized);
1312        self
1313    }
1314
1315    /// Clear all headers.
1316    ///
1317    /// # Returns
1318    ///
1319    /// - `&mut Self` - The request itself, for chaining.
1320    pub fn clear_headers(&mut self) -> &mut Self {
1321        self.get_mut_headers().clear();
1322        self
1323    }
1324
1325    /// Set body (raw bytes).
1326    ///
1327    /// # Arguments
1328    ///
1329    /// - `Body` - The body payload to send.
1330    ///
1331    /// # Returns
1332    ///
1333    /// - `&mut Self` - The request itself, for chaining.
1334    pub fn set_body(&mut self, body: Body) -> &mut Self {
1335        self.body = body;
1336        self
1337    }
1338
1339    /// Set a single config field by mutating the embedded `RequestConfig`.
1340    ///
1341    /// # Arguments
1342    ///
1343    /// - `RequestConfig` - The configuration replacing the current one.
1344    ///
1345    /// # Returns
1346    ///
1347    /// - `&mut Self` - The request itself, for chaining.
1348    pub fn set_config(&mut self, config: RequestConfig) -> &mut Self {
1349        self.config = config;
1350        self
1351    }
1352
1353    /// Normalize a header key to lowercase so `set_header` /
1354    /// `remove_header` lookups are case-insensitive.
1355    ///
1356    /// # Arguments
1357    ///
1358    /// - `&str` - The header key to normalise.
1359    ///
1360    /// # Returns
1361    ///
1362    /// - `String` - The lower-cased header key.
1363    fn normalize_header_key(key: &str) -> String {
1364        key.to_ascii_lowercase()
1365    }
1366}