use std::ffi::OsStr;
use async_trait::async_trait;
use headless_chrome::{browser::LaunchOptions, Browser};
use reqwest::Url;
use crate::{
constants::SUBSCAN_CHROME_PATH_ENV,
enums::content::Content,
error::ModuleErrorKind::GetContent,
interfaces::requester::RequesterInterface,
types::{config::requester::RequesterConfig, core::Result, env::Env},
};
pub struct ChromeBrowser {
pub config: RequesterConfig,
pub browser: Browser,
}
impl Default for ChromeBrowser {
fn default() -> Self {
Self::new()
}
}
impl ChromeBrowser {
pub fn new() -> Self {
Self {
config: RequesterConfig::default(),
browser: Browser::new(Self::default_options()).unwrap(),
}
}
pub fn with_config(config: RequesterConfig) -> Self {
Self {
config,
browser: Browser::new(Self::default_options()).unwrap(),
}
}
pub fn default_options<'a>() -> LaunchOptions<'a> {
LaunchOptions {
headless: true,
sandbox: false,
enable_gpu: false,
path: Env::from(SUBSCAN_CHROME_PATH_ENV).value.map(|path| path.into()),
args: vec![
OsStr::new("--disable-dev-shm-usage"),
OsStr::new("--disable-software-rasterizer"),
OsStr::new("--single-process"),
],
..Default::default()
}
}
}
#[async_trait]
impl RequesterInterface for ChromeBrowser {
async fn config(&mut self) -> &mut RequesterConfig {
&mut self.config
}
async fn configure(&mut self, config: RequesterConfig) {
let mut options = Self::default_options();
if let Some(proxy) = &config.proxy {
options.proxy_server = Some(proxy.as_str());
}
self.browser = Browser::new(options).unwrap();
self.config = config;
}
async fn get_content(&self, url: Url) -> Result<Content> {
let err = |_| GetContent;
let tab = self.browser.new_tab().map_err(err)?;
let headers = self.config.headers_as_hashmap();
tab.set_default_timeout(self.config.timeout);
tab.set_extra_http_headers(headers).map_err(err)?;
if self.config.credentials.is_ok() {
let username = self.config.credentials.username.value.clone();
let password = self.config.credentials.password.value.clone();
tab.authenticate(username, password).map_err(err)?;
}
tab.navigate_to(url.to_string().as_str()).map_err(err)?;
if let Ok(tab) = tab.wait_until_navigated() {
let content = tab.get_content();
tab.close(true).map_err(err)?;
Ok(content.map_err(err)?.into())
} else {
tab.close(true).map_err(err)?;
Ok(Content::Empty)
}
}
}