subscan/requesters/client.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
use async_trait::async_trait;
use reqwest::{Client, Proxy, Url};
use crate::{
enums::content::Content,
interfaces::requester::RequesterInterface,
types::{config::requester::RequesterConfig, core::Result},
};
const CLIENT_BUILD_ERR: &str = "Cannot create HTTP client!";
const PROXY_PARSE_ERR: &str = "Cannot parse proxy!";
/// HTTP requester struct, send HTTP requests via [`reqwest`] client.
/// Also its compatible with [`RequesterInterface`]
#[derive(Default)]
pub struct HTTPClient {
pub config: RequesterConfig,
pub client: Client,
}
impl HTTPClient {
/// Returns a new [`HTTPClient`] instance from given [`RequesterConfig`]
///
/// # Examples
///
/// ```no_run
/// use std::time::Duration;
/// use reqwest::header::HeaderMap;
/// use subscan::requesters::client::HTTPClient;
/// use subscan::types::config::requester::RequesterConfig;
///
/// #[tokio::main]
/// async fn main() {
/// let config = RequesterConfig {
/// timeout: Duration::from_secs(60),
/// ..Default::default()
/// };
///
/// let client = HTTPClient::with_config(config);
///
/// // do something with client
/// }
/// ```
pub fn with_config(config: RequesterConfig) -> Self {
Self {
config,
client: Client::new(),
}
}
}
#[async_trait]
impl RequesterInterface for HTTPClient {
/// Get requester config object as a [`RequesterConfig`]
///
/// # Examples
///
/// ```no_run
/// use std::time::Duration;
/// use subscan::requesters::client::HTTPClient;
/// use subscan::interfaces::requester::RequesterInterface;
///
/// #[tokio::main]
/// async fn main() {
/// let mut client = HTTPClient::default();
///
/// assert_eq!(client.config().await.timeout, Duration::from_secs(10));
/// }
/// ```
async fn config(&mut self) -> &mut RequesterConfig {
&mut self.config
}
/// Configure requester with a new config object
///
/// # Examples
///
/// ```no_run
/// use std::time::Duration;
/// use subscan::requesters::client::HTTPClient;
/// use subscan::types::config::requester::RequesterConfig;
/// use subscan::interfaces::requester::RequesterInterface;
/// use reqwest::header::HeaderMap;
///
/// #[tokio::main]
/// async fn main() {
/// let mut client = HTTPClient::default();
///
/// let new_config = RequesterConfig {
/// timeout: Duration::from_secs(120),
/// ..Default::default()
/// };
///
/// client.configure(new_config.clone()).await;
///
/// assert_eq!(client.config().await.timeout, new_config.timeout);
/// }
/// ```
async fn configure(&mut self, config: RequesterConfig) {
let mut builder = Client::builder().default_headers(config.headers.clone());
if let Some(proxy) = &config.proxy {
builder = builder.proxy(Proxy::all(proxy).expect(PROXY_PARSE_ERR));
}
self.config = config;
self.client = builder.build().expect(CLIENT_BUILD_ERR);
}
/// Get page source HTML from given [`reqwest::Url`]
///
/// # Examples
///
/// ```no_run
/// use subscan::requesters::client::HTTPClient;
/// use subscan::interfaces::requester::RequesterInterface;
/// use reqwest::Url;
///
/// #[tokio::main]
/// async fn main() {
/// let mut client = HTTPClient::default();
/// let url = Url::parse("https://foo.com").unwrap();
///
/// let content = client.get_content(url).await;
///
/// // do something with content
/// }
/// ```
async fn get_content(&self, url: Url) -> Result<Content> {
let mut builder = self.client.get(url);
// Set basic configurations
builder = builder
.timeout(self.config.timeout)
.headers(self.config.headers.clone());
// Set basic HTTP authentication if credentials provided
if self.config.credentials.is_ok() {
let username = self.config.credentials.username.value.clone();
let password = self.config.credentials.password.value.clone();
builder = builder.basic_auth(username.unwrap(), password);
}
let request = builder.build()?;
let response = self.client.execute(request).await?;
Ok(response.text().await?.into())
}
}