use std::path::Path;
use std::time::Duration;
use foundation_netio::shared::client::body_reader::{collect_bytes_into, collect_strings_from_send_safe};
use foundation_netio::http::SimpleHttpClient;
use crate::config::{Result, TestbedError};
const MIN_IMAGE_SIZE: u64 = 50 * 1_048_576;
pub fn http_get(url: &str, read_timeout_secs: u64) -> Result<String> {
let resp = http_client(read_timeout_secs)
.get(url)
.map_err(|e| TestbedError::DownloadFailed { status: 0, url: format!("{url}: {e}") })?
.build_client()
.map_err(|e| TestbedError::DownloadFailed { status: 0, url: format!("{url}: {e}") })?
.send()
.map_err(|e| TestbedError::DownloadFailed { status: 0, url: format!("{url}: {e}") })?;
if !resp.is_success() {
return Err(TestbedError::DownloadFailed {
status: resp.get_status().into_usize() as u16,
url: url.to_string(),
});
}
let (.., body, _, _) = resp.into_parts();
collect_strings_from_send_safe(body).map_err(|e| TestbedError::Qcow2Error {
message: format!("decoding response body from {url}: {e}"),
})
}
pub fn http_download(url: &str, dest: &Path, _progress_label: &str, read_timeout_secs: u64) -> Result<()> {
let resp = http_client(read_timeout_secs)
.get(url)
.map_err(|e| TestbedError::DownloadFailed { status: 0, url: format!("{url}: {e}") })?
.build_client()
.map_err(|e| TestbedError::DownloadFailed { status: 0, url: format!("{url}: {e}") })?
.send()
.map_err(|e| TestbedError::DownloadFailed { status: 0, url: format!("{url}: {e}") })?;
if !resp.is_success() {
return Err(TestbedError::DownloadFailed {
status: resp.get_status().into_usize() as u16,
url: url.to_string(),
});
}
let tmp = dest.with_extension("downloading");
let mut file = std::fs::File::create(&tmp).map_err(|e| TestbedError::Qcow2Error {
message: format!("creating {tmp:?}: {e}"),
})?;
let (.., body, _, _) = resp.into_parts();
let written = collect_bytes_into(body, &mut file).map_err(|e| TestbedError::Qcow2Error {
message: format!("writing download to {tmp:?}: {e}"),
})?;
if written < MIN_IMAGE_SIZE {
let _ = std::fs::remove_file(&tmp);
return Err(TestbedError::Qcow2Error {
message: "downloaded file too small — likely failed".to_string(),
});
}
std::fs::rename(&tmp, dest).map_err(|e| TestbedError::Qcow2Error {
message: format!("moving download to cache: {e}"),
})?;
Ok(())
}
fn http_client(read_timeout_secs: u64) -> SimpleHttpClient {
SimpleHttpClient::from_system()
.max_retries(3)
.read_timeout(Duration::from_secs(read_timeout_secs))
}