use base64::{engine::general_purpose::STANDARD, Engine as _};
use rustls::{ClientConfig, RootCertStore};
use std::path::Path;
use std::sync::Arc;
use super::{CookieJar, HttpClient, HttpHeaders, HttpSyncClient, ProxyType};
use crate::{tls_noverify, user_agent};
#[derive(Debug, Clone)]
pub struct HttpClientConfig {
pub tls_config: Arc<rustls::ClientConfig>,
pub user_agent: Option<String>,
pub headers: HttpHeaders,
pub cookie: CookieJar,
pub follow_location: bool,
pub timeout: u64,
pub proxy_type: ProxyType,
pub proxy_host: String,
pub proxy_port: u16,
pub proxy_user: String,
pub proxy_password: String,
}
pub struct HttpClientBuilder {
config: HttpClientConfig,
}
impl Default for HttpClientBuilder {
fn default() -> HttpClientBuilder {
Self::new()
}
}
impl HttpClientBuilder {
pub fn new() -> Self {
Self {
config: HttpClientConfig::default()
}
}
pub fn build_async(&mut self) -> HttpClient {
HttpClient::new(&self.config)
}
pub fn build_sync(&mut self) -> HttpSyncClient {
HttpSyncClient::new(&self.config)
}
pub fn follow_location(mut self) -> Self {
self.config.follow_location = true;
self
}
pub fn timeout(mut self, seconds: u64) -> Self {
self.config.timeout = seconds;
self
}
pub fn cookie_jar(mut self, jar_file: &str) -> Self {
if !Path::new(&jar_file).exists() {
self.config.cookie.set_jar_file(jar_file);
self.config.cookie.set_auto_update(true);
} else {
self.config.cookie = CookieJar::from_file(jar_file, true).unwrap();
}
self
}
pub fn cookie_string(mut self, cookie_str: &str) -> Self {
self.config.cookie = CookieJar::from_string(&cookie_str.to_string());
self
}
pub fn noverify_ssl(mut self) -> Self {
let mut root_store = RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let mut tls_config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
tls_config.dangerous().set_certificate_verifier(Arc::new(
tls_noverify::NoCertificateVerification::new(rustls::crypto::ring::default_provider()),
));
self.config.tls_config = Arc::new(tls_config);
self
}
pub fn user_agent(mut self, user_agent: &str) -> Self {
self.config.user_agent = Some(user_agent.to_string());
self
}
pub fn browser(mut self) -> Self {
self.config.headers = HttpHeaders::new();
self.config.headers.set(
"Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
);
self.config.headers.set("Accept-Language", "en-US,en;q=0.5");
self.config.headers.set("Accept-Encoding", "identity");
self.config.headers.set("Connection", "close");
if self.config.user_agent.is_none() {
self.config.user_agent = Some(user_agent::random());
}
self.config.follow_location = true;
self
}
pub fn basic_auth(mut self, user: &str, password: &str) -> Self {
if user.is_empty() {
self.config.headers.delete("Authorization");
return self;
}
let auth_userpass = format!("{}:{}", user, password);
let auth_line = format!("Basic {}", STANDARD.encode(auth_userpass));
self.config.headers.set("Authorization", auth_line.as_str());
self
}
pub fn tor(mut self) -> Self {
self.config.proxy_host = "127.0.0.1".to_string();
self.config.proxy_port = 9050;
self.config.proxy_type = ProxyType::SOCKS5;
self
}
pub fn proxy(mut self, host: &str, port: &u16) -> Self {
if self.config.proxy_type == ProxyType::None {
self.config.proxy_type = ProxyType::SOCKS5;
}
self.config.proxy_host = host.to_string();
self.config.proxy_port = *port;
self
}
pub fn proxy_auth(mut self, user: &str, password: &str) -> Self {
self.config.proxy_user = user.to_string();
self.config.proxy_password = password.to_string();
if self.config.proxy_user.is_empty() {
self.config.headers.delete("Proxy-Authorization");
} else {
let auth_userpass = format!("{}:{}", user, password);
let auth_line = format!("Basic {}", STANDARD.encode(auth_userpass));
self.config
.headers
.set("Proxy-Authorization", auth_line.as_str());
}
self
}
pub fn proxy_type(mut self, proxy_type: ProxyType) -> Self {
self.config.proxy_type = proxy_type;
self
}
}
impl Default for HttpClientConfig {
fn default() -> HttpClientConfig {
let mut root_store = RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let tls_config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
HttpClientConfig {
tls_config: Arc::new(tls_config),
user_agent: None,
headers: HttpHeaders::from_vec(&vec!["Connection: close".to_string()]),
cookie: CookieJar::new(),
follow_location: false,
timeout: 5,
proxy_type: ProxyType::None,
proxy_host: String::new(),
proxy_port: 0,
proxy_user: String::new(),
proxy_password: String::new(),
}
}
}