use crate::error::Result;
use crate::user_agent;
use attohttpc::{Response, Session};
use std::io::{self, Read};
use std::time::Duration;
pub trait HttpClient: Send + Sync {
fn get(&self, url: &str, headers: Vec<(String, String)>) -> Result<Box<dyn HttpResponse>>;
fn set_timeout(&mut self, timeout: Duration);
}
pub trait HttpResponse: Read + Send {
fn status(&self) -> u16;
fn header(&self, name: &str) -> Option<&str>;
fn final_url(&self) -> Option<&str>;
}
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(300);
pub struct AttohttpcClient {
timeout: Duration,
user_agent: String,
}
impl AttohttpcClient {
pub fn new() -> Self {
Self {
timeout: DEFAULT_TIMEOUT,
user_agent: user_agent::download_client(),
}
}
}
impl Default for AttohttpcClient {
fn default() -> Self {
Self::new()
}
}
impl HttpClient for AttohttpcClient {
fn get(&self, url: &str, headers: Vec<(String, String)>) -> Result<Box<dyn HttpResponse>> {
let mut session = Session::new();
session.proxy_settings(attohttpc::ProxySettings::from_env());
let mut request_builder = session
.get(url)
.timeout(self.timeout)
.header("User-Agent", &self.user_agent)
.follow_redirects(true);
for (key, value) in headers {
match key.as_str() {
"Range" => {
let range_value = value.clone();
request_builder = request_builder.header("Range", range_value);
}
_ => {
}
}
}
let response = request_builder.send()?;
Ok(Box::new(AttohttpcResponse { response }))
}
fn set_timeout(&mut self, timeout: Duration) {
self.timeout = timeout;
}
}
struct AttohttpcResponse {
response: Response,
}
impl Read for AttohttpcResponse {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.response.read(buf)
}
}
impl HttpResponse for AttohttpcResponse {
fn status(&self) -> u16 {
self.response.status().as_u16()
}
fn header(&self, name: &str) -> Option<&str> {
self.response.headers().get(name)?.to_str().ok()
}
fn final_url(&self) -> Option<&str> {
Some(self.response.url().as_ref())
}
}