Skip to content

Commit

Permalink
Temporary commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael-F-Bryan committed May 26, 2023
1 parent 29dc3aa commit 81a96ff
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 139 deletions.
48 changes: 33 additions & 15 deletions lib/wasi/src/http/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{collections::HashSet, ops::Deref, sync::Arc};

use futures::future::BoxFuture;
use http::{HeaderMap, Method, StatusCode};
use url::Url;

/// Defines http client permissions.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -47,19 +49,27 @@ pub struct HttpRequestOptions {

// TODO: use types from http crate?
pub struct HttpRequest {
pub url: String,
pub method: String,
pub headers: Vec<(String, String)>,
pub url: Url,
pub method: Method,
pub headers: HeaderMap,
pub body: Option<Vec<u8>>,
pub options: HttpRequestOptions,
}

impl std::fmt::Debug for HttpRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let HttpRequest {
url,
method,
headers,
body,
options,
} = self;

f.debug_struct("HttpRequest")
.field("url", &self.url)
.field("method", &self.method)
.field("headers", &self.headers)
.field("url", &format_args!("{}", url))
.field("method", method)
.field("headers", headers)
.field("body", &self.body.as_deref().map(String::from_utf8_lossy))
.field("options", &self.options)
.finish()
Expand All @@ -68,25 +78,33 @@ impl std::fmt::Debug for HttpRequest {

// TODO: use types from http crate?
pub struct HttpResponse {
pub pos: usize,
pub body: Option<Vec<u8>>,
pub ok: bool,
pub redirected: bool,
pub status: u16,
pub status_text: String,
pub headers: Vec<(String, String)>,
pub status: StatusCode,
pub headers: HeaderMap,
}

impl HttpResponse {
pub fn is_ok(&self) -> bool {
!self.status.is_client_error() && !self.status.is_server_error()
}
}

impl std::fmt::Debug for HttpResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let HttpResponse {
body,
redirected,
status,
headers,
} = self;

f.debug_struct("HttpResponse")
.field("pos", &self.pos)
.field("body", &self.body.as_deref().map(String::from_utf8_lossy))
.field("ok", &self.ok)
.field("ok", &self.is_ok())
.field("redirected", &self.redirected)
.field("status", &self.status)
.field("status_text", &self.status_text)
.field("headers", &self.headers)
.field("body", &self.body.as_deref().map(String::from_utf8_lossy))
.finish()
}
}
Expand Down
13 changes: 2 additions & 11 deletions lib/wasi/src/http/reqwest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,15 @@ impl ReqwestHttpClient {

let response = client.execute(request).await?;

let status = response.status().as_u16();
let status_text = response.status().as_str().to_string();
let status = response.status();
// TODO: prevent redundant header copy.
let headers = response
.headers()
.iter()
.map(|(k, v)| (k.to_string(), v.to_str().unwrap().to_string()))
.collect();
let data = response.bytes().await?.to_vec();

Ok(HttpResponse {
pos: 0usize,
ok: true,
status,
status_text,
redirected: false,
body: Some(data),
headers,
headers: std::mem::take(response.headers_mut()),
})
}
}
Expand Down
53 changes: 24 additions & 29 deletions lib/wasi/src/runtime/package_loader/builtin_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::{

use anyhow::{Context, Error};
use bytes::Bytes;
use http::{HeaderMap, Method};
use tempfile::NamedTempFile;
use webc::{
compat::{Container, ContainerError},
Expand All @@ -16,7 +17,7 @@ use webc::{

use crate::{
bin_factory::BinaryPackage,
http::{HttpClient, HttpRequest, HttpResponse, USER_AGENT},
http::{HttpClient, HttpRequest, USER_AGENT},
runtime::{
package_loader::PackageLoader,
resolver::{DistributionInfo, PackageSummary, Resolution, WebcHash},
Expand Down Expand Up @@ -99,26 +100,19 @@ impl BuiltinPackageLoader {
}

let request = HttpRequest {
url: dist.webc.to_string(),
method: "GET".to_string(),
headers: vec![
("Accept".to_string(), "application/webc".to_string()),
("User-Agent".to_string(), USER_AGENT.to_string()),
],
url: dist.webc.clone(),
method: Method::GET,
headers: headers(),
body: None,
options: Default::default(),
};

let HttpResponse {
body,
ok,
status,
status_text,
..
} = self.client.request(request).await?;
let response = self.client.request(request).await?;

if !ok {
anyhow::bail!("{status} {status_text}");
if !response.is_ok() {
let url = &dist.webc;
return Err(crate::runtime::resolver::polyfills::http_error(&response)
.context(format!("The GET request to \"{url}\" failed")));
}

let body = body.context("The response didn't contain a body")?;
Expand Down Expand Up @@ -213,6 +207,13 @@ impl PackageLoader for BuiltinPackageLoader {
}
}

fn headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/webc".parse().unwrap());
headers.insert("User-Agent", USER_AGENT.parse().unwrap());
headers
}

fn discover_wasmer_dir() -> Option<PathBuf> {
// TODO: We should reuse the same logic from the wasmer CLI.
std::env::var("WASMER_DIR")
Expand Down Expand Up @@ -307,6 +308,7 @@ mod tests {
use std::{collections::VecDeque, sync::Mutex};

use futures::future::BoxFuture;
use http::{HeaderMap, StatusCode};
use tempfile::TempDir;

use crate::{
Expand Down Expand Up @@ -348,13 +350,10 @@ mod tests {
async fn cache_misses_will_trigger_a_download() {
let temp = TempDir::new().unwrap();
let client = Arc::new(DummyClient::with_responses([HttpResponse {
pos: 0,
body: Some(PYTHON.to_vec()),
ok: true,
redirected: false,
status: 200,
status_text: "OK".to_string(),
headers: Vec::new(),
status: StatusCode::OK,
headers: HeaderMap::new(),
}]));
let loader = BuiltinPackageLoader::new_with_client(temp.path(), client.clone());
let summary = PackageSummary {
Expand All @@ -376,15 +375,11 @@ mod tests {
// A HTTP request was sent
let requests = client.requests.lock().unwrap();
let request = &requests[0];
assert_eq!(request.url, summary.dist.webc.to_string());
assert_eq!(request.url, summary.dist.webc);
assert_eq!(request.method, "GET");
assert_eq!(
request.headers,
[
("Accept".to_string(), "application/webc".to_string()),
("User-Agent".to_string(), USER_AGENT.to_string()),
]
);
assert_eq!(request.headers.len(), 2);
assert_eq!(request.headers["Accept"], "application/webc");
assert_eq!(request.headers["User-Agent"], USER_AGENT);
// Make sure we got the right package
let manifest = container.manifest();
assert_eq!(manifest.entrypoint.as_deref(), Some("python"));
Expand Down
27 changes: 27 additions & 0 deletions lib/wasi/src/runtime/resolver/polyfills.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::path::Path;

use anyhow::Error;
use http::{HeaderMap, StatusCode};
use url::Url;

use crate::http::{HttpResponse, USER_AGENT};

/// Polyfill for [`Url::from_file_path()`] that works on `wasm32-unknown-unknown`.
pub(crate) fn url_from_file_path(path: impl AsRef<Path>) -> Option<Url> {
let path = path.as_ref();
Expand All @@ -25,6 +29,29 @@ pub(crate) fn url_from_file_path(path: impl AsRef<Path>) -> Option<Url> {
buffer.parse().ok()
}

pub(crate) fn webc_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/webc".parse().unwrap());
headers.insert("User-Agent", USER_AGENT.parse().unwrap());
headers
}

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())
{
anyhow::anyhow!("{status} (Retry After: {retry_after})");
}
}

Error::msg(status)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
57 changes: 26 additions & 31 deletions lib/wasi/src/runtime/resolver/wapm_source.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::sync::Arc;

use anyhow::{Context, Error};
use http::{HeaderMap, Method};
use semver::Version;
use url::Url;
use webc::metadata::Manifest;

use crate::{
http::{HttpClient, HttpRequest, HttpResponse},
http::{HttpClient, HttpRequest},
runtime::resolver::{
DistributionInfo, PackageInfo, PackageSpecifier, PackageSummary, Source, WebcHash,
},
Expand Down Expand Up @@ -52,33 +53,22 @@ impl Source for WapmSource {
let body = serde_json::to_string(&body)?;

let request = HttpRequest {
url: self.registry_endpoint.to_string(),
method: "POST".to_string(),
url: self.registry_endpoint.clone(),
method: Method::POST,
body: Some(body.into_bytes()),
headers: vec![
(
"User-Agent".to_string(),
crate::http::USER_AGENT.to_string(),
),
("Content-Type".to_string(), "application/json".to_string()),
],
headers: headers(),
options: Default::default(),
};

let HttpResponse {
ok,
status,
status_text,
body,
..
} = self.client.request(request).await?;
let response = self.client.request(request).await?;

if !ok {
if !response.is_ok() {
let url = &self.registry_endpoint;
anyhow::bail!("\"{url}\" replied with {status} {status_text}");
let status = response.status;
anyhow::bail!("\"{url}\" replied with {status}");
}

let body = body.unwrap_or_default();
let body = response.body.unwrap_or_default();
let response: WapmWebQuery =
serde_json::from_slice(&body).context("Unable to deserialize the response")?;

Expand All @@ -101,6 +91,13 @@ impl Source for WapmSource {
}
}

fn headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/json".parse().unwrap());
headers.insert("User-Agent", USER_AGENT.parse().unwrap());
headers
}

fn decode_summary(pkg_version: WapmWebQueryGetPackageVersion) -> Result<PackageSummary, Error> {
let WapmWebQueryGetPackageVersion {
manifest,
Expand Down Expand Up @@ -180,6 +177,8 @@ pub struct WapmWebQueryGetPackageVersionDistribution {
mod tests {
use std::collections::HashMap;

use http::{HeaderMap, StatusCode};

use crate::runtime::resolver::inputs::{DistributionInfo, PackageInfo};

use super::*;
Expand All @@ -200,12 +199,11 @@ mod tests {
// -H "Content-Type: application/json" \
// -X POST \
// -d '@wasmer_pack_cli_request.json' > wasmer_pack_cli_response.json
assert_eq!(request.method, "POST");
assert_eq!(request.url, WapmSource::WAPM_PROD_ENDPOINT);
let headers: HashMap<String, String> = request.headers.into_iter().collect();
assert_eq!(headers.len(), 2);
assert_eq!(headers["User-Agent"], crate::http::USER_AGENT);
assert_eq!(headers["Content-Type"], "application/json");
assert_eq!(request.method, http::Method::POST);
assert_eq!(request.url.as_str(), WapmSource::WAPM_PROD_ENDPOINT);
assert_eq!(request.headers.len(), 2);
assert_eq!(request.headers["User-Agent"], crate::http::USER_AGENT);
assert_eq!(request.headers["Content-Type"], "application/json");

let body: serde_json::Value =
serde_json::from_slice(request.body.as_deref().unwrap()).unwrap();
Expand All @@ -215,13 +213,10 @@ mod tests {

Box::pin(async {
Ok(HttpResponse {
pos: 0,
body: Some(WASMER_PACK_CLI_RESPONSE.to_vec()),
ok: true,
redirected: false,
status: 200,
status_text: "OK".to_string(),
headers: Vec::new(),
status: StatusCode::OK,
headers: HeaderMap::new(),
})
})
}
Expand Down
Loading

0 comments on commit 81a96ff

Please sign in to comment.