diff --git a/Cargo.toml b/Cargo.toml index 126c82bcd6..6a30631165 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,2 @@ [workspace] -members = [ - "s3", - "aws-region", - "aws-creds" -] \ No newline at end of file +members = ["rust-s3", "aws-creds"] diff --git a/aws-creds/Cargo.toml b/aws-creds/Cargo.toml index e4934b7d5d..2d88d3d4ba 100644 --- a/aws-creds/Cargo.toml +++ b/aws-creds/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "aws-creds" -version = "0.30.0" +version = "0.38.0" authors = ["Drazen Urch"] -description = "Tiny Rust library for working with Amazon IAM credential,s, supports `s3` crate" +description = "Rust library for working with Amazon IAM credential,s, supports `s3` crate" repository = "https://github.com/durch/rust-s3" readme = "README.md" -keywords = ["AWS", "S3", "Wasabi", "Minio", "Yandex"] +keywords = ["AWS", "S3", "Wasabi", "Minio", "R2"] license = "MIT" -documentation = "https://durch.github.io/rust-s3/" -edition = "2018" +documentation = "https://docs.rs/aws-creds/latest/awscreds/" +edition = "2021" [lib] name = "awscreds" @@ -16,22 +16,24 @@ path = "src/lib.rs" [dependencies] thiserror = "1" -dirs = "4" -rust-ini = "0.18" -attohttpc = { version = "0.19", default-features = false, features = [ +home = "0.5" +rust-ini = "0.21" +attohttpc = { version = "0.28", default-features = false, features = [ "json", ], optional = true } url = "2" -serde-xml-rs = "0.5" +quick-xml = { version = "0.32", features = ["serialize"] } serde = { version = "1", features = ["derive"] } time = { version = "^0.3.6", features = ["serde", "serde-well-known"] } +log = "0.4" [features] default = ["native-tls"] http-credentials = ["attohttpc"] native-tls = ["http-credentials", "attohttpc/tls"] +native-tls-vendored = ["http-credentials", "attohttpc/tls-vendored"] rustls-tls = ["http-credentials", "attohttpc/tls-rustls"] [dev-dependencies] -env_logger = "0.9" +env_logger = "0.11" serde_json = "1" diff --git a/aws-creds/src/credentials.rs b/aws-creds/src/credentials.rs index 340fcbb134..0b2741a140 100644 --- a/aws-creds/src/credentials.rs +++ b/aws-creds/src/credentials.rs @@ -1,13 +1,16 @@ #![allow(dead_code)] + use crate::error::CredentialsError; use ini::Ini; +use log::debug; use serde::{Deserialize, Serialize}; -use serde_xml_rs as serde_xml; use std::collections::HashMap; use std::env; +use std::ops::Deref; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; use std::time::Duration; +use time::OffsetDateTime; use url::Url; /// AWS access credentials: access key, secret key, and optional token. @@ -23,19 +26,17 @@ use url::Url; /// use awscreds::Credentials; /// /// // Load credentials from `[default]` profile -/// #[cfg(feature="http-credentials")] /// let credentials = Credentials::default(); /// /// // Also loads credentials from `[default]` profile -/// #[cfg(feature="http-credentials")] /// let credentials = Credentials::new(None, None, None, None, None); /// /// // Load credentials from `[my-profile]` profile -/// #[cfg(feature="http-credentials")] /// let credentials = Credentials::new(None, None, None, None, Some("my-profile".into())); -/// ``` +/// /// // Use anonymous credentials for public objects /// let credentials = Credentials::anonymous(); +/// ``` /// /// Credentials may also be initialized directly or by the following environment variables: /// @@ -52,14 +53,12 @@ use url::Url; /// // Load credentials directly /// let access_key = "AKIAIOSFODNN7EXAMPLE"; /// let secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; -/// #[cfg(feature="http-credentials")] /// let credentials = Credentials::new(Some(access_key), Some(secret_key), None, None, None); /// /// // Load credentials from the environment /// use std::env; /// env::set_var("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE"); /// env::set_var("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); -/// #[cfg(feature="http-credentials")] /// let credentials = Credentials::new(None, None, None, None, None); /// ``` #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -71,7 +70,31 @@ pub struct Credentials { /// Temporary token issued by AWS service. pub security_token: Option, pub session_token: Option, - pub expiration: Option, + pub expiration: Option, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[repr(transparent)] +pub struct Rfc3339OffsetDateTime(#[serde(with = "time::serde::rfc3339")] pub time::OffsetDateTime); + +impl From for Rfc3339OffsetDateTime { + fn from(v: time::OffsetDateTime) -> Self { + Self(v) + } +} + +impl From for time::OffsetDateTime { + fn from(v: Rfc3339OffsetDateTime) -> Self { + v.0 + } +} + +impl Deref for Rfc3339OffsetDateTime { + type Target = time::OffsetDateTime; + + fn deref(&self) -> &Self::Target { + &self.0 + } } #[derive(Deserialize, Debug)] @@ -96,7 +119,7 @@ pub struct AssumeRoleWithWebIdentityResult { pub struct StsResponseCredentials { pub session_token: String, pub secret_access_key: String, - pub expiration: time::OffsetDateTime, + pub expiration: Rfc3339OffsetDateTime, pub access_key_id: String, } @@ -146,20 +169,35 @@ pub fn set_request_timeout(timeout: Option) -> Option { } } -/// Sends a GET request to `url` with a request timeout if one was set. #[cfg(feature = "http-credentials")] -fn http_get(url: &str) -> attohttpc::Result { - let mut builder = attohttpc::get(url); - +fn apply_timeout(builder: attohttpc::RequestBuilder) -> attohttpc::RequestBuilder { let timeout_ms = REQUEST_TIMEOUT_MS.load(Ordering::Relaxed); if timeout_ms > 0 { - builder = builder.timeout(Duration::from_millis(timeout_ms as u64)); + return builder.timeout(Duration::from_millis(timeout_ms as u64)); } + builder +} + +/// Sends a GET request to `url` with a request timeout if one was set. +#[cfg(feature = "http-credentials")] +fn http_get(url: &str) -> attohttpc::Result { + let builder = apply_timeout(attohttpc::get(url)); builder.send() } impl Credentials { + pub fn refresh(&mut self) -> Result<(), CredentialsError> { + if let Some(expiration) = self.expiration { + if expiration.0 <= OffsetDateTime::now_utc() { + debug!("Refreshing credentials!"); + let refreshed = Credentials::default()?; + *self = refreshed + } + } + Ok(()) + } + #[cfg(feature = "http-credentials")] pub fn from_sts_env(session_name: &str) -> Result { let role_arn = env::var("AWS_ROLE_ARN")?; @@ -174,8 +212,14 @@ impl Credentials { session_name: &str, web_identity_token: &str, ) -> Result { + let use_regional_sts = env::var("AWS_STS_REGIONAL_ENDPOINTS") == Ok("regional".to_string()); + let sts_url = if use_regional_sts { + format!("https://sts.{}.amazonaws.com/", env::var("AWS_REGION")?) + } else { + "https://sts.amazonaws.com/".to_string() + }; let url = Url::parse_with_params( - "https://sts.amazonaws.com/", + sts_url.as_str(), &[ ("Action", "AssumeRoleWithWebIdentity"), ("RoleSessionName", session_name), @@ -186,8 +230,8 @@ impl Credentials { )?; let response = http_get(url.as_str())?; let serde_response = - serde_xml::from_str::(&response.text()?)?; - // assert!(serde_xml::from_str::(&response.text()?).unwrap()); + quick_xml::de::from_str::(&response.text()?)?; + // assert!(quick_xml::de::from_str::(&response.text()?).unwrap()); Ok(Credentials { access_key: Some( @@ -218,7 +262,7 @@ impl Credentials { }) } - #[cfg(feature = "http-credentials")] + #[allow(clippy::should_implement_trait)] pub fn default() -> Result { Credentials::new(None, None, None, None, None) } @@ -235,7 +279,6 @@ impl Credentials { /// Initialize Credentials directly with key ID, secret key, and optional /// token. - #[cfg(feature = "http-credentials")] pub fn new( access_key: Option<&str>, secret_key: Option<&str>, @@ -253,10 +296,15 @@ impl Credentials { }); } - Credentials::from_sts_env("aws-creds") - .or_else(|_| Credentials::from_env()) - .or_else(|_| Credentials::from_profile(profile)) - .or_else(|_| Credentials::from_instance_metadata()) + let credentials = Credentials::from_env().or_else(|_| Credentials::from_profile(profile)); + + #[cfg(feature = "http-credentials")] + let credentials = credentials + .or_else(|_| Credentials::from_sts_env("aws-creds")) + .or_else(|_| Credentials::from_instance_metadata_v2(false)) + .or_else(|_| Credentials::from_instance_metadata(false)); + + credentials.map_err(|_| CredentialsError::NoCredentials) } pub fn from_env_specific( @@ -284,30 +332,33 @@ impl Credentials { } #[cfg(feature = "http-credentials")] - pub fn from_instance_metadata() -> Result { + pub fn from_instance_metadata(not_ec2: bool) -> Result { let resp: CredentialsFromInstanceMetadata = match env::var("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") { Ok(credentials_path) => { // We are on ECS - attohttpc::get(&format!("http://169.254.170.2{}", credentials_path)) - .send()? - .json()? + apply_timeout(attohttpc::get(format!( + "http://169.254.170.2{}", + credentials_path + ))) + .send()? + .json()? } Err(_) => { - if !is_ec2() { + if !not_ec2 && !is_ec2() { return Err(CredentialsError::NotEc2); } - let role = attohttpc::get( + let role = apply_timeout(attohttpc::get( "http://169.254.169.254/latest/meta-data/iam/security-credentials", - ) + )) .send()? .text()?; - attohttpc::get(&format!( + apply_timeout(attohttpc::get(format!( "http://169.254.169.254/latest/meta-data/iam/security-credentials/{}", role - )) + ))) .send()? .json()? } @@ -322,10 +373,50 @@ impl Credentials { }) } + #[cfg(feature = "http-credentials")] + pub fn from_instance_metadata_v2(not_ec2: bool) -> Result { + if !not_ec2 && !is_ec2() { + return Err(CredentialsError::NotEc2); + } + + let token = apply_timeout(attohttpc::put("http://169.254.169.254/latest/api/token")) + .header("X-aws-ec2-metadata-token-ttl-seconds", "21600") + .send()?; + if !token.is_success() { + return Err(CredentialsError::UnexpectedStatusCode( + token.status().as_u16(), + )); + } + let token = token.text()?; + + let role = apply_timeout(attohttpc::get( + "http://169.254.169.254/latest/meta-data/iam/security-credentials", + )) + .header("X-aws-ec2-metadata-token", &token) + .send()? + .text()?; + + let resp: CredentialsFromInstanceMetadata = apply_timeout(attohttpc::get(format!( + "http://169.254.169.254/latest/meta-data/iam/security-credentials/{}", + role + ))) + .header("X-aws-ec2-metadata-token", &token) + .send()? + .json()?; + + Ok(Credentials { + access_key: Some(resp.access_key_id), + secret_key: Some(resp.secret_access_key), + security_token: Some(resp.token), + expiration: Some(resp.expiration), + session_token: None, + }) + } + pub fn from_profile(section: Option<&str>) -> Result { - let home_dir = dirs::home_dir().ok_or(CredentialsError::HomeDir)?; + let home_dir = home::home_dir().ok_or(CredentialsError::HomeDir)?; let profile = format!("{}/.aws/credentials", home_dir.display()); - let conf = Ini::load_from_file(&profile)?; + let conf = Ini::load_from_file(profile)?; let section = section.unwrap_or("default"); let data = conf .section(Some(section)) @@ -376,9 +467,9 @@ struct CredentialsFromInstanceMetadata { access_key_id: String, secret_access_key: String, token: String, - #[serde(with = "time::serde::rfc3339")] - expiration: time::OffsetDateTime, // TODO fix #163 + expiration: Rfc3339OffsetDateTime, // TODO fix #163 } + #[cfg(test)] #[test] fn test_instance_metadata_creds_deserialization() { @@ -399,3 +490,15 @@ fn test_instance_metadata_creds_deserialization() { ) .unwrap(); } + +#[cfg(test)] +#[ignore] +#[test] +fn test_credentials_refresh() { + let mut c = Credentials::default().expect("Could not generate credentials"); + let e = Rfc3339OffsetDateTime(OffsetDateTime::now_utc()); + c.expiration = Some(e); + std::thread::sleep(std::time::Duration::from_secs(3)); + c.refresh().expect("Could not refresh"); + assert!(c.expiration.is_none()) +} diff --git a/aws-creds/src/error.rs b/aws-creds/src/error.rs index 4e123576f0..a90a240b74 100644 --- a/aws-creds/src/error.rs +++ b/aws-creds/src/error.rs @@ -18,7 +18,7 @@ pub enum CredentialsError { #[error("ini: {0}")] Ini(#[from] ini::Error), #[error("serde_xml: {0}")] - SerdeXml(#[from] serde_xml_rs::Error), + SerdeXml(#[from] quick_xml::de::DeError), #[error("url parse: {0}")] UrlParse(#[from] url::ParseError), #[error("io: {0}")] @@ -27,4 +27,8 @@ pub enum CredentialsError { Env(#[from] std::env::VarError), #[error("Invalid home dir")] HomeDir, + #[error("Could not get valid credentials from STS, ENV, Profile or Instance metadata")] + NoCredentials, + #[error("unexpected status code: {0}")] + UnexpectedStatusCode(u16), } diff --git a/aws-region/Cargo.toml b/aws-region/Cargo.toml deleted file mode 100644 index 55978e6286..0000000000 --- a/aws-region/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "aws-region" -version = "0.25.1" -authors = ["Drazen Urch"] -description = "Tiny Rust library for working with Amazon AWS regions, supports `s3` crate" -repository = "https://github.com/durch/rust-s3" -readme = "README.md" -keywords = ["Amazon", "AWS", "S3", "Wasabi", "Minio"] -license = "MIT" -documentation = "https://durch.github.io/rust-s3/" -edition = "2018" - -[lib] -name = "awsregion" -path = "src/lib.rs" - -[dependencies] -thiserror = "1" -serde = { version = "1", features = ["derive"], optional = true } diff --git a/aws-region/Makefile b/aws-region/Makefile deleted file mode 100644 index b38dcfaa6d..0000000000 --- a/aws-region/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -ci: fmt-check clippy test - -fmt-check: - cargo fmt --all -- --check - -clippy: - cargo clippy --all-features -- -D warnings - -test: - cargo test --all-features - - - diff --git a/aws-region/README.md b/aws-region/README.md deleted file mode 100644 index 9cc45364c1..0000000000 --- a/aws-region/README.md +++ /dev/null @@ -1,19 +0,0 @@ -AWS S3 [region identifier](https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region), -passing in custom values is also possible, in that case it is up to you to pass a valid endpoint, -otherwise boom will happen :) - -# Example -```rust -use std::str::FromStr; -use awsregion::Region; - -// Parse from a string -let region: Region = "us-east-1".parse().unwrap(); -// Choose region directly -let region = Region::EuWest2; - -// Custom region requires valid region name and endpoint -let region_name = "nl-ams".to_string(); -let endpoint = "https://s3.nl-ams.scw.cloud".to_string(); -let region = Region::Custom { region: region_name, endpoint }; -``` \ No newline at end of file diff --git a/aws-region/src/error.rs b/aws-region/src/error.rs deleted file mode 100644 index f3eb7bbc22..0000000000 --- a/aws-region/src/error.rs +++ /dev/null @@ -1,4 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum RegionError {} diff --git a/aws-region/src/lib.rs b/aws-region/src/lib.rs deleted file mode 100644 index df7ae79e7c..0000000000 --- a/aws-region/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(unused_imports)] -#![forbid(unsafe_code)] - -mod region; -pub use region::*; -pub mod error; diff --git a/aws-region/src/region.rs b/aws-region/src/region.rs deleted file mode 100644 index 29d8b21a4c..0000000000 --- a/aws-region/src/region.rs +++ /dev/null @@ -1,266 +0,0 @@ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use std::fmt; -use std::str::{self, FromStr}; - -/// AWS S3 [region identifier](https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region), -/// passing in custom values is also possible, in that case it is up to you to pass a valid endpoint, -/// otherwise boom will happen :) -/// -/// Serde support available with the `serde` feature -/// -/// # Example -/// ``` -/// use std::str::FromStr; -/// use awsregion::Region; -/// -/// // Parse from a string -/// let region: Region = "us-east-1".parse().unwrap(); -/// -/// // Choose region directly -/// let region = Region::EuWest2; -/// -/// // Custom region requires valid region name and endpoint -/// let region_name = "nl-ams".to_string(); -/// let endpoint = "https://s3.nl-ams.scw.cloud".to_string(); -/// let region = Region::Custom { region: region_name, endpoint }; -/// -/// ``` -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Region { - /// us-east-1 - UsEast1, - /// us-east-2 - UsEast2, - /// us-west-1 - UsWest1, - /// us-west-2 - UsWest2, - /// ca-central-1 - CaCentral1, - /// af-south-1 - AfSouth1, - /// ap-east-1 - ApEast1, - /// ap-south-1 - ApSouth1, - /// ap-northeast-1 - ApNortheast1, - /// ap-northeast-2 - ApNortheast2, - /// ap-northeast-3 - ApNortheast3, - /// ap-southeast-1 - ApSoutheast1, - /// ap-southeast-2 - ApSoutheast2, - /// cn-north-1 - CnNorth1, - /// cn-northwest-1 - CnNorthwest1, - /// eu-north-1 - EuNorth1, - /// eu-central-1 - EuCentral1, - /// eu-west-1 - EuWest1, - /// eu-west-2 - EuWest2, - /// eu-west-3 - EuWest3, - /// me-south-1 - MeSouth1, - /// sa-east-1 - SaEast1, - /// Digital Ocean nyc3 - DoNyc3, - /// Digital Ocean ams3 - DoAms3, - /// Digital Ocean sgp1 - DoSgp1, - /// Digiral Ocean fra1 - DoFra1, - /// Yandex Object Storage - Yandex, - /// Wasabi us-east-1 - WaUsEast1, - /// Wasabi us-east-2 - WaUsEast2, - /// Wasabi us-west-1 - WaUsWest1, - /// Wasabi eu-central-1 - WaEuCentral1, - /// Custom region - R2 { - account_id: String, - }, - Custom { - region: String, - endpoint: String, - }, -} - -impl fmt::Display for Region { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Region::*; - match *self { - UsEast1 => write!(f, "us-east-1"), - UsEast2 => write!(f, "us-east-2"), - UsWest1 => write!(f, "us-west-1"), - UsWest2 => write!(f, "us-west-2"), - AfSouth1 => write!(f, "af-south-1"), - CaCentral1 => write!(f, "ca-central-1"), - ApEast1 => write!(f, "ap-east-1"), - ApSouth1 => write!(f, "ap-south-1"), - ApNortheast1 => write!(f, "ap-northeast-1"), - ApNortheast2 => write!(f, "ap-northeast-2"), - ApNortheast3 => write!(f, "ap-northeast-3"), - ApSoutheast1 => write!(f, "ap-southeast-1"), - ApSoutheast2 => write!(f, "ap-southeast-2"), - CnNorth1 => write!(f, "cn-north-1"), - CnNorthwest1 => write!(f, "cn-northwest-1"), - EuNorth1 => write!(f, "eu-north-1"), - EuCentral1 => write!(f, "eu-central-1"), - EuWest1 => write!(f, "eu-west-1"), - EuWest2 => write!(f, "eu-west-2"), - EuWest3 => write!(f, "eu-west-3"), - SaEast1 => write!(f, "sa-east-1"), - MeSouth1 => write!(f, "me-south-1"), - DoNyc3 => write!(f, "nyc3"), - DoAms3 => write!(f, "ams3"), - DoSgp1 => write!(f, "sgp1"), - DoFra1 => write!(f, "fra1"), - Yandex => write!(f, "ru-central1"), - WaUsEast1 => write!(f, "us-east-1"), - WaUsEast2 => write!(f, "us-east-2"), - WaUsWest1 => write!(f, "us-west-1"), - WaEuCentral1 => write!(f, "eu-central-1"), - R2 { .. } => write!(f, "auto"), - Custom { ref region, .. } => write!(f, "{}", region), - } - } -} - -impl FromStr for Region { - type Err = std::str::Utf8Error; - - fn from_str(s: &str) -> Result { - use self::Region::*; - match s { - "us-east-1" => Ok(UsEast1), - "us-east-2" => Ok(UsEast2), - "us-west-1" => Ok(UsWest1), - "us-west-2" => Ok(UsWest2), - "ca-central-1" => Ok(CaCentral1), - "af-south-1" => Ok(AfSouth1), - "ap-east-1" => Ok(ApEast1), - "ap-south-1" => Ok(ApSouth1), - "ap-northeast-1" => Ok(ApNortheast1), - "ap-northeast-2" => Ok(ApNortheast2), - "ap-northeast-3" => Ok(ApNortheast3), - "ap-southeast-1" => Ok(ApSoutheast1), - "ap-southeast-2" => Ok(ApSoutheast2), - "cn-north-1" => Ok(CnNorth1), - "cn-northwest-1" => Ok(CnNorthwest1), - "eu-north-1" => Ok(EuNorth1), - "eu-central-1" => Ok(EuCentral1), - "eu-west-1" => Ok(EuWest1), - "eu-west-2" => Ok(EuWest2), - "eu-west-3" => Ok(EuWest3), - "sa-east-1" => Ok(SaEast1), - "me-south-1" => Ok(MeSouth1), - "nyc3" => Ok(DoNyc3), - "ams3" => Ok(DoAms3), - "sgp1" => Ok(DoSgp1), - "fra1" => Ok(DoFra1), - "yandex" => Ok(Yandex), - "ru-central1" => Ok(Yandex), - "wa-us-east-1" => Ok(WaUsEast1), - "wa-us-east-2" => Ok(WaUsEast2), - "wa-us-west-1" => Ok(WaUsWest1), - "wa-eu-central-1" => Ok(WaEuCentral1), - x => Ok(Custom { - region: x.to_string(), - endpoint: x.to_string(), - }), - } - } -} - -impl Region { - pub fn endpoint(&self) -> String { - use self::Region::*; - match *self { - // Surprisingly, us-east-1 does not have a - // s3-us-east-1.amazonaws.com DNS record - UsEast1 => String::from("s3.amazonaws.com"), - UsEast2 => String::from("s3-us-east-2.amazonaws.com"), - UsWest1 => String::from("s3-us-west-1.amazonaws.com"), - UsWest2 => String::from("s3-us-west-2.amazonaws.com"), - CaCentral1 => String::from("s3-ca-central-1.amazonaws.com"), - AfSouth1 => String::from("s3-af-south-1.amazonaws.com"), - ApEast1 => String::from("s3-ap-east-1.amazonaws.com"), - ApSouth1 => String::from("s3-ap-south-1.amazonaws.com"), - ApNortheast1 => String::from("s3-ap-northeast-1.amazonaws.com"), - ApNortheast2 => String::from("s3-ap-northeast-2.amazonaws.com"), - ApNortheast3 => String::from("s3-ap-northeast-3.amazonaws.com"), - ApSoutheast1 => String::from("s3-ap-southeast-1.amazonaws.com"), - ApSoutheast2 => String::from("s3-ap-southeast-2.amazonaws.com"), - CnNorth1 => String::from("s3.cn-north-1.amazonaws.com.cn"), - CnNorthwest1 => String::from("s3.cn-northwest-1.amazonaws.com.cn"), - EuNorth1 => String::from("s3-eu-north-1.amazonaws.com"), - EuCentral1 => String::from("s3.eu-central-1.amazonaws.com"), - EuWest1 => String::from("s3-eu-west-1.amazonaws.com"), - EuWest2 => String::from("s3-eu-west-2.amazonaws.com"), - EuWest3 => String::from("s3-eu-west-3.amazonaws.com"), - SaEast1 => String::from("s3-sa-east-1.amazonaws.com"), - MeSouth1 => String::from("s3-me-south-1.amazonaws.com"), - DoNyc3 => String::from("nyc3.digitaloceanspaces.com"), - DoAms3 => String::from("ams3.digitaloceanspaces.com"), - DoSgp1 => String::from("sgp1.digitaloceanspaces.com"), - DoFra1 => String::from("fra1.digitaloceanspaces.com"), - Yandex => String::from("storage.yandexcloud.net"), - WaUsEast1 => String::from("s3.us-east-1.wasabisys.com"), - WaUsEast2 => String::from("s3.us-east-2.wasabisys.com"), - WaUsWest1 => String::from("s3.us-west-1.wasabisys.com"), - WaEuCentral1 => String::from("s3.eu-central-1.wasabisys.com"), - R2 { ref account_id } => format!("{}.r2.cloudflarestorage.com", account_id), - Custom { ref endpoint, .. } => endpoint.to_string(), - } - } - - pub fn scheme(&self) -> String { - match *self { - Region::Custom { ref endpoint, .. } => match endpoint.find("://") { - Some(pos) => endpoint[..pos].to_string(), - None => "https".to_string(), - }, - _ => "https".to_string(), - } - } - - pub fn host(&self) -> String { - match *self { - Region::Custom { ref endpoint, .. } => match endpoint.find("://") { - Some(pos) => endpoint[pos + 3..].to_string(), - None => endpoint.to_string(), - }, - _ => self.endpoint(), - } - } -} - -#[test] -fn yandex_object_storage() { - let yandex = Region::Custom { - endpoint: "storage.yandexcloud.net".to_string(), - region: "ru-central1".to_string(), - }; - - let yandex_region = "ru-central1".parse::().unwrap(); - - assert_eq!(yandex.endpoint(), yandex_region.endpoint()); - - assert_eq!(yandex.to_string(), yandex_region.to_string()); -} diff --git a/s3/Cargo.toml b/rust-s3/Cargo.toml similarity index 86% rename from s3/Cargo.toml rename to rust-s3/Cargo.toml index 873120053d..edbf33da53 100644 --- a/s3/Cargo.toml +++ b/rust-s3/Cargo.toml @@ -14,18 +14,12 @@ edition = "2018" name = "s3" path = "src/lib.rs" -# [[bin]] -# name = "simple_crud" -# path = "bin/simple_crud.rs" - [dependencies] async-std = { version = "1", optional = true } async-trait = "0.1" attohttpc = { version = "0.19", optional = true, default-features = false } -aws-creds = { version = "0.30.0", default-features = false } -# aws-creds = { path = "../aws-creds", default-features = false } -aws-region = "0.25.1" -# aws-region = {path = "../aws-region"} +aws-creds = { path = "../aws-creds" } +aws-region = { version = "0.25.4" } base64 = "0.13" cfg-if = "1" time = { version = "^0.3.6", features = ["formatting", "macros"] } @@ -78,6 +72,5 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "fs"] } async-std = { version = "1", features = ["attributes"] } uuid = { version = "1", features = ["v4"] } env_logger = "0.9" -aws-creds = { version = "0.30", default-features = false } -# aws-creds = { path = "../aws-creds", features = ["http-credentials"] } +aws-creds = { path = "../aws-creds", features = ["http-credentials"] } anyhow = "1" diff --git a/s3/Makefile b/rust-s3/Makefile similarity index 100% rename from s3/Makefile rename to rust-s3/Makefile diff --git a/s3/README.md b/rust-s3/README.md similarity index 100% rename from s3/README.md rename to rust-s3/README.md diff --git a/s3/bin/simple_crud.rs b/rust-s3/bin/simple_crud.rs similarity index 100% rename from s3/bin/simple_crud.rs rename to rust-s3/bin/simple_crud.rs diff --git a/s3/src/blocking.rs b/rust-s3/src/blocking.rs similarity index 100% rename from s3/src/blocking.rs rename to rust-s3/src/blocking.rs diff --git a/s3/src/bucket.rs b/rust-s3/src/bucket.rs similarity index 100% rename from s3/src/bucket.rs rename to rust-s3/src/bucket.rs diff --git a/s3/src/bucket_ops.rs b/rust-s3/src/bucket_ops.rs similarity index 100% rename from s3/src/bucket_ops.rs rename to rust-s3/src/bucket_ops.rs diff --git a/s3/src/command.rs b/rust-s3/src/command.rs similarity index 100% rename from s3/src/command.rs rename to rust-s3/src/command.rs diff --git a/s3/src/deserializer.rs b/rust-s3/src/deserializer.rs similarity index 100% rename from s3/src/deserializer.rs rename to rust-s3/src/deserializer.rs diff --git a/s3/src/error.rs b/rust-s3/src/error.rs similarity index 100% rename from s3/src/error.rs rename to rust-s3/src/error.rs diff --git a/s3/src/lib.rs b/rust-s3/src/lib.rs similarity index 100% rename from s3/src/lib.rs rename to rust-s3/src/lib.rs diff --git a/s3/src/request.rs b/rust-s3/src/request.rs similarity index 100% rename from s3/src/request.rs rename to rust-s3/src/request.rs diff --git a/s3/src/request_trait.rs b/rust-s3/src/request_trait.rs similarity index 100% rename from s3/src/request_trait.rs rename to rust-s3/src/request_trait.rs diff --git a/s3/src/serde_types.rs b/rust-s3/src/serde_types.rs similarity index 100% rename from s3/src/serde_types.rs rename to rust-s3/src/serde_types.rs diff --git a/s3/src/signing.rs b/rust-s3/src/signing.rs similarity index 100% rename from s3/src/signing.rs rename to rust-s3/src/signing.rs diff --git a/s3/src/surf_request.rs b/rust-s3/src/surf_request.rs similarity index 100% rename from s3/src/surf_request.rs rename to rust-s3/src/surf_request.rs diff --git a/s3/src/utils.rs b/rust-s3/src/utils.rs similarity index 100% rename from s3/src/utils.rs rename to rust-s3/src/utils.rs