Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

mannequin/src/nexus/proxy.rs

use super::NexusAsset;
use std::collections::HashMap;
use std::sync::Mutex;

/// Proxy-only repo: fetches crates from crates.io on demand, caches in memory.
pub struct ProxyRepo {
    cache: Mutex<HashMap<String, Vec<u8>>>,
    http_client: reqwest::Client,
    base_url: String,
}

impl ProxyRepo {
    pub fn new(base_url: &str) -> Self {
        Self {
            cache: Mutex::new(HashMap::new()),
            http_client: tls_roots(reqwest::Client::builder())
                .build()
                .expect("reqwest client"),
            base_url: base_url.to_string(),
        }
    }

    pub fn list_assets(&self) -> Vec<NexusAsset> {
        self.cache
            .lock()
            .unwrap()
            .keys()
            .map(|path| NexusAsset {
                path: path.clone(),
                download_url: format!("{}/repository/proxy-only/{}", self.base_url, path),
                content_type: Some("application/octet-stream".into()),
                size: None,
            })
            .collect()
    }

    pub async fn download(&self, path: &str) -> Option<Vec<u8>> {
        // Check cache
        if let Some(data) = self.cache.lock().unwrap().get(path) {
            return Some(data.clone());
        }

        // Proxy from crates.io
        let filename = path.rsplit('/').next()?;
        if !filename.ends_with(".crate") {
            return None;
        }

        let stem = filename.strip_suffix(".crate")?;
        let crate_name = extract_crate_name(stem)?;
        let url = format!(
            "https://static.crates.io/crates/{}/{}.crate",
            crate_name, stem
        );

        tracing::info!("proxy-only: fetching {}", url);
        let resp = self.http_client.get(&url).send().await.ok()?;
        if !resp.status().is_success() {
            tracing::warn!("proxy-only: crates.io returned {}", resp.status());
            return None;
        }

        let data = resp.bytes().await.ok()?.to_vec();
        tracing::info!("proxy-only: cached {} ({} bytes)", path, data.len());
        self.cache
            .lock()
            .unwrap()
            .insert(path.to_string(), data.clone());
        Some(data)
    }
}

fn extract_crate_name(stem: &str) -> Option<&str> {
    let mut last_sep = None;
    for (i, _) in stem.match_indices('-') {
        if stem.as_bytes().get(i + 1).map(|b| b.is_ascii_digit()) == Some(true) {
            last_sep = Some(i);
        }
    }
    last_sep.map(|i| &stem[..i])
}

/// reqwest 0.13 with reqwest 0.12 `rustls-tls` behaviour (2026-10-02): the ring
/// provider and the bundled Mozilla roots (webpki), not 0.13's aws-lc-rs + OS
/// trust store. Roots added later on the builder are merged in, as in 0.12.
#[allow(dead_code)]
fn tls_roots(b: reqwest::ClientBuilder) -> reqwest::ClientBuilder {
    let _ = rustls::crypto::ring::default_provider().install_default();
    b.tls_certs_only(
        webpki_root_certs::TLS_SERVER_ROOT_CERTS
            .iter()
            .filter_map(|c| reqwest::Certificate::from_der(c).ok()),
    )
}