atlas_http/
client_sync.rs

1#![allow(clippy::large_enum_variant)]
2
3use super::{HttpBody, HttpClientConfig, HttpRequest, HttpResponse, ProxyType};
4use crate::error::{Error, FileNotCreatedError, InvalidResponseError};
5use rustls::pki_types::ServerName;
6use std::fs::File;
7use std::io::{BufRead, BufReader, Read, Write};
8use std::net::{TcpStream, ToSocketAddrs};
9use std::path::Path;
10use std::sync::Arc;
11use std::time::Duration;
12use url::Url;
13use crate::socks5;
14
15#[derive(Debug, Clone)]
16pub struct HttpSyncClient {
17    config: HttpClientConfig,
18}
19
20
21impl HttpSyncClient {
22    pub fn new(config: &HttpClientConfig) -> Self {
23        Self {
24            config: config.clone(),
25        }
26    }
27
28    /// Send HTTP request, and return response
29    pub fn send(&mut self, req: &HttpRequest) -> Result<HttpResponse, Error> {
30        self.send_request(req, &String::new())
31    }
32
33    /// Download a file
34    pub fn download(&mut self, url: &str, dest_file: &str) -> Result<HttpResponse, Error> {
35        let req = HttpRequest::new("GET", url, &vec![], &HttpBody::empty());
36        self.send_request(&req, &dest_file.to_string())
37    }
38
39    /// Send GET request
40    pub fn get(&mut self, url: &str) -> Result<HttpResponse, Error> {
41        let req = HttpRequest::new("GET", url, &Vec::new(), &HttpBody::empty());
42        self.send_request(&req, &String::new())
43    }
44
45    /// Send POST request
46    pub fn post(&mut self, url: &str, body: &HttpBody) -> Result<HttpResponse, Error> {
47        let req = HttpRequest::new("POST", url, &Vec::new(), body);
48        self.send_request(&req, &String::new())
49    }
50
51    /// Send PUT request
52    pub fn put(&mut self, url: &str, data: &[u8]) -> Result<HttpResponse, Error> {
53        let req = HttpRequest::new("PUT", url, &Vec::new(), &HttpBody::from_raw(data));
54        self.send_request(&req, &String::new())
55    }
56
57    /// Send DELETE request
58    pub fn delete(&mut self, url: &str) -> Result<HttpResponse, Error> {
59        let req = HttpRequest::new("DELETE", url, &Vec::new(), &HttpBody::empty());
60        self.send_request(&req, &String::new())
61    }
62
63    /// Send OPTIONS request
64    pub fn options(&mut self, url: &str) -> Result<HttpResponse, Error> {
65        let req = HttpRequest::new("OPTIONS", url, &Vec::new(), &HttpBody::empty());
66        self.send_request(&req, &String::new())
67    }
68
69    /// Send HEAD request
70    pub fn head(&mut self, url: &str) -> Result<HttpResponse, Error> {
71        let req = HttpRequest::new("HEAD", url, &Vec::new(), &HttpBody::empty());
72        self.send_request(&req, &String::new())
73    }
74
75    // Send request, used internally by the other methods.
76    fn send_request(
77        &mut self,
78        req: &HttpRequest,
79        dest_file: &String,
80    ) -> Result<HttpResponse, Error> {
81        // Prepare uri and http message
82        let (uri, port, message) = req.prepare(&self.config)?;
83
84        // Connect
85        let mut reader = self.connect(&uri, &port, &message)?;
86
87        // Read header
88        let mut res = HttpResponse::read_header(&mut reader, req, dest_file)?;
89        self.config.cookie.update_jar(&res.headers());
90
91        // Check follow location
92        if self.config.follow_location && res.headers().has_lower("location") {
93            let redirect_req = HttpRequest::new(
94                "GET",
95                res.headers().get_lower("location").unwrap().as_str(),
96                &vec![],
97                &HttpBody::empty(),
98            );
99            res = self.send_request(&redirect_req, dest_file)?;
100        }
101
102        // Return if not downloading a file
103        if dest_file.is_empty() {
104            return Ok(res);
105        }
106
107        // Save output file
108        let dest_path = Path::new(&dest_file);
109        let mut fh = match File::create(dest_path) {
110            Ok(r) => r,
111            Err(e) => {
112                return Err(Error::FileNotCreated(FileNotCreatedError {
113                    filename: dest_file.to_string(),
114                    error: e.to_string(),
115                }));
116            }
117        };
118
119        // Save file
120        let mut buffer = [0u8; 2048];
121        loop {
122            let bytes_read = match reader.read(&mut buffer) {
123                Ok(r) => r,
124                Err(e) => {
125                    return Err(Error::NoRead(InvalidResponseError {
126                        url: req.url.clone(),
127                        response: e.to_string(),
128                    }));
129                }
130            };
131
132            if bytes_read == 0 {
133                break;
134            }
135            fh.write_all(&buffer).unwrap();
136        }
137
138        Ok(res)
139    }
140
141    // Connect to remote server
142    pub fn connect(&self, uri: &Url, port: &u16, message: &Vec<u8>) -> Result<Box<dyn BufRead>, Error> {
143        // Prepare uri
144        let hostname =
145            if self.config.proxy_type != ProxyType::None && !self.config.proxy_host.is_empty() {
146                format!("{}:{}", self.config.proxy_host, self.config.proxy_port)
147            } else {
148                format!("{}:{}", &uri.host_str().unwrap(), port)
149            };
150        let mut address = hostname.to_socket_addrs().unwrap();
151        let addr = address.next().unwrap();
152
153        // Open tcp stream
154        let mut sock =
155            match TcpStream::connect_timeout(&addr, Duration::from_secs(self.config.timeout)) {
156                Ok(r) => r,
157                Err(_e) => {
158                    return Err(Error::NoConnect(hostname.clone()));
159                }
160            };
161        sock.set_nodelay(true).unwrap();
162
163        // SOCKs5 connection, if needed
164        if self.config.proxy_type == ProxyType::SOCKS5 {
165            socks5::connect(&mut sock, &self.config, uri, port);
166        }
167
168        // Connect over SSL, if needed
169        if uri.scheme() == "https" && self.config.proxy_type != ProxyType::HTTP {
170            let dns_name = ServerName::try_from(uri.host_str().unwrap())
171                .unwrap()
172                .to_owned();
173            let conn = rustls::ClientConnection::new(Arc::clone(&self.config.tls_config), dns_name)
174                .unwrap();
175
176            let mut tls_stream = rustls::StreamOwned::new(conn, sock);
177            tls_stream.flush().unwrap();
178            tls_stream.write_all(message).unwrap();
179
180            let reader = BufReader::with_capacity(2048, tls_stream);
181            return Ok(Box::new(reader));
182        }
183
184        // Get reader
185        sock.write_all(message).unwrap();
186        let reader = BufReader::with_capacity(2048, sock);
187
188        Ok(Box::new(reader))
189    }
190}