deno.land / x / wasm@wasmer-sdk-v0.6.0 / src / package_loader.rs
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163use std::{ collections::HashMap, sync::{Arc, Mutex},};
use anyhow::{Context, Error};use bytes::Bytes;use http::{HeaderMap, HeaderValue, Method, StatusCode};use wasmer_wasix::{ bin_factory::BinaryPackage, http::{HttpClient, HttpRequest, HttpResponse}, runtime::resolver::{DistributionInfo, PackageSummary, Resolution, WebcHash},};use webc::Container;
/// A package loader that uses the browser's native APIs to download packages.////// Downloads will be cached based on the [`default`] caching behaviour.////// [`default`]: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache#[derive(Debug, Clone)]pub struct PackageLoader { client: Arc<dyn HttpClient + Send + Sync>, cache: Arc<Cache>,}
impl PackageLoader { pub fn new(client: Arc<dyn HttpClient + Send + Sync>) -> Self { let cache = Arc::new(Cache::default()); PackageLoader { client, cache } }
async fn download(&self, dist: &DistributionInfo) -> Result<Bytes, Error> { let mut headers = HeaderMap::new(); headers.insert("Accept", HeaderValue::from_static("application/webc"));
let request = HttpRequest { url: dist.webc.clone(), method: Method::GET, headers, body: None, options: Default::default(), };
tracing::debug!(%request.url, %request.method, "Downloading a webc file"); tracing::trace!(?request.headers);
let response = self.client.request(request).await?;
tracing::trace!( %response.status, %response.redirected, ?response.headers, response.len=response.body.as_ref().map(|body| body.len()), "Received a response", );
if !response.is_ok() { let url = &dist.webc; return Err( http_error(&response).context(format!("The GET request to \"{url}\" failed")) ); }
let body = response .body .context("The response didn't contain a body")?;
Ok(body.into()) }
pub(crate) async fn download_cached(&self, dist: &DistributionInfo) -> Result<Bytes, Error> { let webc_hash = dist.webc_sha256;
let body = match self.cache.load(&webc_hash) { Some(body) => { tracing::debug!("Cache Hit!"); body } None => { tracing::debug!("Cache Miss"); let bytes = self.download(dist).await?; self.cache.save(webc_hash, bytes.clone()); bytes } };
Ok(body) }}
#[async_trait::async_trait]impl wasmer_wasix::runtime::package_loader::PackageLoader for PackageLoader { #[tracing::instrument( skip_all, fields( pkg.name=summary.pkg.name.as_str(), pkg.version=%summary.pkg.version, pkg.url=summary.dist.webc.as_str(), ), )] async fn load(&self, summary: &PackageSummary) -> Result<Container, Error> { let body = self.download_cached(&summary.dist).await?; let container = Container::from_bytes(body)?;
Ok(container) }
async fn load_package_tree( &self, root: &Container, resolution: &Resolution, ) -> Result<BinaryPackage, Error> { wasmer_wasix::runtime::package_loader::load_package_tree(root, self, resolution).await }}
pub(crate) fn http_error(response: &HttpResponse) -> Error { let status = response.status;
if status == StatusCode::SERVICE_UNAVAILABLE { if let Some(retry_after) = response .headers .get("Retry-After") .and_then(|retry_after| retry_after.to_str().ok()) { tracing::debug!( %retry_after, "Received 503 Service Unavailable while looking up a package. The backend may still be generating the *.webc file.", ); return anyhow::anyhow!("{status} (Retry After: {retry_after})"); } }
Error::msg(status)}
/// A quick'n'dirty cache for downloaded packages.////// This makes no attempt at verifying a cached#[derive(Debug, Default)]struct Cache(Mutex<HashMap<WebcHash, Bytes>>);
impl Cache { fn load(&self, hash: &WebcHash) -> Option<Bytes> { let cache = self.0.lock().ok()?; let bytes = cache.get(hash)?; Some(bytes.clone()) }
fn save(&self, hash: WebcHash, bytes: Bytes) { debug_assert_eq!( hash, WebcHash::sha256(bytes.as_ref()), "Mismatched webc hash" );
if let Ok(mut cache) = self.0.lock() { cache.insert(hash, bytes); } }}
Version Info