1#![allow(clippy::large_enum_variant)]
2
3use super::{
4 HttpBody, HttpClientConfig, HttpRequest, HttpResponse, HttpSyncClient, ProxyType,
5};
6use crate::client_builder::HttpClientBuilder;
7use crate::error::{Error, FileNotCreatedError, InvalidResponseError};
8use crate::socks5;
9use rustls::pki_types::ServerName;
10use std::fs::File;
11use std::io::{BufRead, BufReader, Read, Write};
12use std::net::{TcpStream, ToSocketAddrs};
13use std::path::Path;
14use std::sync::Arc;
15use std::time::Duration;
16use url::Url;
17
18#[derive(Debug, Clone)]
19pub struct HttpClient {
20 pub config: HttpClientConfig,
21}
22
23impl HttpClient {
24 pub fn new(config: &HttpClientConfig) -> Self {
25 Self {
26 config: config.clone(),
27 }
28 }
29
30 pub fn builder() -> HttpClientBuilder {
32 HttpClientBuilder::new()
33 }
34
35 pub async fn send(&mut self, req: &HttpRequest) -> Result<HttpResponse, Error> {
37 self.send_request(req, &String::new()).await
38 }
39
40 pub async fn download(&mut self, url: &str, dest_file: &str) -> Result<HttpResponse, Error> {
42 let req = HttpRequest::new("GET", url, &vec![], &HttpBody::empty());
43 self.send_request(&req, &dest_file.to_string()).await
44 }
45
46 pub async fn get(&mut self, url: &str) -> Result<HttpResponse, Error> {
48 let req = HttpRequest::new("GET", url, &Vec::new(), &HttpBody::empty());
49 self.send_request(&req, &String::new()).await
50 }
51
52 pub async fn post(&mut self, url: &str, body: &HttpBody) -> Result<HttpResponse, Error> {
54 let req = HttpRequest::new("POST", url, &Vec::new(), body);
55 self.send_request(&req, &String::new()).await
56 }
57
58 pub async fn put(&mut self, url: &str, data: &[u8]) -> Result<HttpResponse, Error> {
60 let req = HttpRequest::new("PUT", url, &Vec::new(), &HttpBody::from_raw(data));
61 self.send_request(&req, &String::new()).await
62 }
63
64 pub async fn delete(&mut self, url: &str) -> Result<HttpResponse, Error> {
66 let req = HttpRequest::new("DELETE", url, &Vec::new(), &HttpBody::empty());
67 self.send_request(&req, &String::new()).await
68 }
69
70 pub async fn options(&mut self, url: &str) -> Result<HttpResponse, Error> {
72 let req = HttpRequest::new("OPTIONS", url, &Vec::new(), &HttpBody::empty());
73 self.send_request(&req, &String::new()).await
74 }
75
76 pub async fn head(&mut self, url: &str) -> Result<HttpResponse, Error> {
78 let req = HttpRequest::new("HEAD", url, &Vec::new(), &HttpBody::empty());
79 self.send_request(&req, &String::new()).await
80 }
81
82 async fn send_request(
84 &mut self,
85 req: &HttpRequest,
86 dest_file: &String,
87 ) -> Result<HttpResponse, Error> {
88 let (uri, port, message) = req.prepare(&self.config)?;
90
91 let mut reader = self.connect(&uri, &port, &message).await?;
93
94 let mut res = HttpResponse::read_header(&mut reader, req, dest_file)?;
96 self.config.cookie.update_jar(&res.headers());
97
98 if self.config.follow_location && res.headers().has_lower("location") {
100 res = self.follow(&res, dest_file)?;
101 }
102
103 if dest_file.is_empty() {
105 return Ok(res);
106 }
107
108 let dest_path = Path::new(&dest_file);
110 let mut fh = match File::create(dest_path) {
111 Ok(r) => r,
112 Err(e) => {
113 return Err(Error::FileNotCreated(FileNotCreatedError {
114 filename: dest_file.to_string(),
115 error: e.to_string(),
116 }));
117 }
118 };
119
120 let mut buffer = [0u8; 2048];
122 loop {
123 let bytes_read = match reader.read(&mut buffer) {
124 Ok(r) => r,
125 Err(e) => {
126 return Err(Error::NoRead(InvalidResponseError {
127 url: req.url.clone(),
128 response: e.to_string(),
129 }));
130 }
131 };
132
133 if bytes_read == 0 {
134 break;
135 }
136 fh.write_all(&buffer).unwrap();
137 }
138
139 Ok(res)
140 }
141
142 fn follow(&self, res: &HttpResponse, dest_file: &String) -> Result<HttpResponse, Error> {
144 let redirect_url = res.headers().get_lower("location").unwrap();
145 let mut rhttp = HttpSyncClient::new(&self.config.clone());
146
147 let next_res = if dest_file.is_empty() {
148 rhttp.get(&redirect_url.clone())?
149 } else {
150 rhttp.download(&redirect_url.clone(), dest_file)?
151 };
152
153 Ok(next_res)
154 }
155
156 pub async fn connect(
158 &self,
159 uri: &Url,
160 port: &u16,
161 message: &[u8],
162 ) -> Result<Box<dyn BufRead>, Error> {
163 let hostname =
165 if self.config.proxy_type != ProxyType::None && !self.config.proxy_host.is_empty() {
166 format!("{}:{}", self.config.proxy_host, self.config.proxy_port)
167 } else {
168 format!("{}:{}", &uri.host_str().unwrap(), port)
169 };
170 let mut address = hostname.to_socket_addrs().unwrap();
171 let addr = address.next().unwrap();
172
173 let mut sock =
175 match TcpStream::connect_timeout(&addr, Duration::from_secs(self.config.timeout)) {
176 Ok(r) => r,
177 Err(_e) => {
178 return Err(Error::NoConnect(hostname.clone()));
179 }
180 };
181 sock.set_nodelay(true).unwrap();
182
183 if self.config.proxy_type == ProxyType::SOCKS5 {
185 socks5::connect(&mut sock, &self.config, uri, port);
186 }
187
188 if uri.scheme() == "https" && self.config.proxy_type != ProxyType::HTTP {
190 let dns_name = ServerName::try_from(uri.host_str().unwrap())
191 .unwrap()
192 .to_owned();
193 let conn = rustls::ClientConnection::new(Arc::clone(&self.config.tls_config), dns_name)
194 .unwrap();
195
196 let mut tls_stream = rustls::StreamOwned::new(conn, sock);
197 tls_stream.flush().unwrap();
198 tls_stream.write_all(message).unwrap();
199
200 let reader = BufReader::with_capacity(2048, tls_stream);
201 return Ok(Box::new(reader));
202 }
203
204 sock.write_all(message).unwrap();
206 let reader = BufReader::with_capacity(2048, sock);
207
208 Ok(Box::new(reader))
209 }
210}