runner_core/packs/resolver/
http.rs1use std::io::copy;
2
3use anyhow::{Context, Result, bail};
4use greentic_config_types::{NetworkConfig, TlsMode};
5use reqwest::blocking::Client;
6use std::time::Duration;
7use tempfile::NamedTempFile;
8
9use super::{FetchResponse, PackResolver};
10
11pub struct HttpResolver {
12 scheme: &'static str,
13 client: Client,
14}
15
16impl HttpResolver {
17 pub fn new(scheme: &'static str, network: Option<&NetworkConfig>) -> Result<Self> {
18 let mut builder = Client::builder();
19 if let Some(cfg) = network {
20 if let Some(proxy) = &cfg.proxy_url {
21 builder = builder.proxy(reqwest::Proxy::all(proxy)?);
22 }
23 if let Some(timeout) = cfg.connect_timeout_ms {
24 builder = builder.connect_timeout(Duration::from_millis(timeout));
25 }
26 if let Some(timeout) = cfg.read_timeout_ms {
27 builder = builder.timeout(Duration::from_millis(timeout));
28 }
29 if matches!(cfg.tls_mode, TlsMode::Disabled) {
30 bail!("TLS certificate validation cannot be disabled");
31 }
32 }
33 Ok(Self {
34 scheme,
35 client: builder.build()?,
36 })
37 }
38}
39
40impl PackResolver for HttpResolver {
41 fn scheme(&self) -> &'static str {
42 self.scheme
43 }
44
45 fn fetch(&self, locator: &str) -> Result<FetchResponse> {
46 let mut response = self
47 .client
48 .get(locator)
49 .send()
50 .with_context(|| format!("failed to download {}", locator))?
51 .error_for_status()
52 .with_context(|| format!("download failed {}", locator))?;
53
54 let mut temp = NamedTempFile::new().context("failed to allocate temp file for download")?;
55 {
56 let mut writer = temp.as_file_mut();
57 copy(&mut response, &mut writer).context("failed to stream HTTP content")?;
58 }
59 Ok(FetchResponse::from_temp(temp.into_temp_path()))
60 }
61}