use anyhow::{Context, Result};
const USER_AGENT_STRING: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36";
pub struct NetworkClient {
client: reqwest::Client,
}
impl NetworkClient {
pub fn new() -> Self {
let client = reqwest::Client::builder()
.user_agent(USER_AGENT_STRING)
.redirect(reqwest::redirect::Policy::limited(10))
.default_headers({
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7".parse().unwrap());
headers.insert("Accept-Language", "en-US,en;q=0.9".parse().unwrap());
headers.insert("Accept-Encoding", "gzip, deflate, br".parse().unwrap());
headers.insert("Upgrade-Insecure-Requests", "1".parse().unwrap());
headers.insert("Sec-Fetch-Dest", "document".parse().unwrap());
headers.insert("Sec-Fetch-Mode", "navigate".parse().unwrap());
headers.insert("Sec-Fetch-Site", "none".parse().unwrap());
headers.insert("Sec-Fetch-User", "?1".parse().unwrap());
headers.insert("Connection", "keep-alive".parse().unwrap());
headers
})
.build()
.unwrap_or_default();
Self { client }
}
pub async fn fetch_url(&self, url: &str) -> Result<(String, String)> {
let clean_url = url
.trim()
.trim_matches('"')
.trim_matches('\'')
.replace("\"", "");
let target_url = if !clean_url.starts_with("http://") && !clean_url.starts_with("https://")
{
format!("https://{}", clean_url)
} else {
clean_url.to_string()
};
let response = self
.client
.get(&target_url)
.send()
.await
.context("Failed to send request")?;
let final_url = response.url().to_string();
let status = response.status();
if !status.is_success() {
return Err(anyhow::anyhow!("HTTP Error: {}", status));
}
let content = response
.text()
.await
.context("Failed to read response body")?;
Ok((final_url, content))
}
}