diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a206757..21fef94c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,15 @@ jobs: with: command: build + build_under_wasm: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build + run: | + rustup target add wasm32-unknown-unknown + cargo build --target wasm32-unknown-unknown + build_all_features: runs-on: ${{ matrix.os }} strategy: diff --git a/Cargo.toml b/Cargo.toml index f34a557e..6a9b8ba9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,9 +63,8 @@ chrono = "0.4.24" form_urlencoded = "1" hex = "0.4" hmac = "0.12" -home = "0.5" http = "0.2" -jsonwebtoken = { version = "9", optional = true } +jsonwebtoken = { version = "9.2", optional = true } log = "0.4" once_cell = "1" percent-encoding = "2" @@ -78,8 +77,15 @@ serde = { version = "1", features = ["derive"], optional = true } serde_json = { version = "1", optional = true } sha1 = "0.10" sha2 = { version = "0.10", features = ["oid"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +home = "0.5" tokio = { version = "1", features = ["fs"], optional = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2", features = ["js"] } +tokio = { version = "1", optional = true } + [dev-dependencies] aws-sigv4 = "0.56" criterion = { version = "0.5", features = ["async_tokio", "html_reports"] } diff --git a/src/aws/config.rs b/src/aws/config.rs index e3e50afb..fd8a65a0 100644 --- a/src/aws/config.rs +++ b/src/aws/config.rs @@ -1,13 +1,19 @@ use std::collections::HashMap; use std::env; +#[cfg(not(target_arch = "wasm32"))] use std::fs; +#[cfg(not(target_arch = "wasm32"))] use anyhow::anyhow; +#[cfg(not(target_arch = "wasm32"))] use anyhow::Result; +#[cfg(not(target_arch = "wasm32"))] use ini::Ini; +#[cfg(not(target_arch = "wasm32"))] use log::debug; use super::constants::*; +#[cfg(not(target_arch = "wasm32"))] use crate::dirs::expand_homedir; /// Config for aws services. @@ -159,6 +165,7 @@ impl Config { /// /// If the env var AWS_PROFILE is set, this profile will be used, /// otherwise the contents of `self.profile` will be used. + #[cfg(not(target_arch = "wasm32"))] pub fn from_profile(mut self) -> Self { // self.profile is checked by the two load methods. if let Ok(profile) = env::var(AWS_PROFILE) { @@ -193,6 +200,7 @@ impl Config { /// - `aws_access_key_id` /// - `aws_secret_access_key` /// - `aws_session_token` + #[cfg(not(target_arch = "wasm32"))] fn load_via_profile_shared_credentials_file(&mut self) -> Result<()> { let path = expand_homedir(&self.shared_credentials_file) .ok_or_else(|| anyhow!("expand homedir failed"))?; @@ -218,6 +226,7 @@ impl Config { Ok(()) } + #[cfg(not(target_arch = "wasm32"))] fn load_via_profile_config_file(&mut self) -> Result<()> { let path = expand_homedir(&self.config_file).ok_or_else(|| anyhow!("expand homedir failed"))?; @@ -268,6 +277,7 @@ mod tests { use tempfile::tempdir; #[test] + #[cfg(not(target_arch = "wasm32"))] fn test_config_from_profile_shared_credentials() -> Result<()> { let _ = env_logger::builder().is_test(true).try_init(); diff --git a/src/aws/credential.rs b/src/aws/credential.rs index 06b957ac..ad11d888 100644 --- a/src/aws/credential.rs +++ b/src/aws/credential.rs @@ -3,7 +3,6 @@ use std::fmt::Write; use std::fs; use std::sync::Arc; use std::sync::Mutex; -use std::time::Duration; use anyhow::anyhow; use anyhow::Result; @@ -59,7 +58,8 @@ impl Credential { } /// Loader trait will try to load credential from different sources. -#[async_trait] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] pub trait CredentialLoad: 'static + Send + Sync { /// Load credential from sources. /// @@ -246,7 +246,8 @@ impl DefaultLoader { } } -#[async_trait] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl CredentialLoad for DefaultLoader { async fn load_credential(&self, _: Client) -> Result> { self.load().await @@ -332,14 +333,20 @@ impl IMDSv2Loader { } let url = "http://169.254.169.254/latest/api/token"; - let req = self + #[allow(unused_mut)] + let mut req = self .client .put(url) .header(CONTENT_LENGTH, "0") // 21600s (6h) is recommended by AWS. - .header("x-aws-ec2-metadata-token-ttl-seconds", "21600") - // Set timeout to 1s to avoid hanging on non-s3 env. - .timeout(Duration::from_secs(1)); + .header("x-aws-ec2-metadata-token-ttl-seconds", "21600"); + + // Set timeout to 1s to avoid hanging on non-s3 env. + #[cfg(not(target_arch = "wasm32"))] + { + req = req.timeout(std::time::Duration::from_secs(1)); + } + let resp = req.send().await?; if resp.status() != http::StatusCode::OK { let content = resp.text().await?; @@ -359,7 +366,8 @@ impl IMDSv2Loader { } } -#[async_trait] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl CredentialLoad for IMDSv2Loader { async fn load_credential(&self, _: Client) -> Result> { self.load().await @@ -483,7 +491,8 @@ impl AssumeRoleLoader { } } -#[async_trait] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl CredentialLoad for AssumeRoleLoader { async fn load_credential(&self, _: Client) -> Result> { self.load().await diff --git a/src/dirs.rs b/src/dirs.rs index 374a35e9..02a44e29 100644 --- a/src/dirs.rs +++ b/src/dirs.rs @@ -5,6 +5,7 @@ /// - If path not starts with `~/` or `~\\`, returns `Some(path)` directly. /// - Otherwise, replace `~` with home dir instead. /// - If home_dir is not found, returns `None`. +#[cfg(not(target_arch = "wasm32"))] pub fn expand_homedir(path: &str) -> Option { if !path.starts_with("~/") && !path.starts_with("~\\") { Some(path.to_string())