From 1c658766c12a0aa295e03fe7648f78a61ff3d0ef Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 1 Feb 2022 12:45:15 -0600 Subject: [PATCH 1/8] refactor: HttpSettings, HttpConnection location update: replace timeout::Settings with TimeoutConfig add: HttpConnector to aws_types::Config and Builder refactor: divide default_provider.rs modules into separate files --- .../aws-config/src/default_provider.rs | 829 +----------------- .../src/default_provider/app_name.rs | 90 ++ .../src/default_provider/credentials.rs | 436 +++++++++ .../aws-config/src/default_provider/region.rs | 74 ++ .../src/default_provider/retry_config.rs | 99 +++ .../src/default_provider/timeout_config.rs | 112 +++ aws/rust-runtime/aws-config/src/ecs.rs | 2 +- .../aws-config/src/environment/mod.rs | 1 + ...rovider.rs => http_credential_provider.rs} | 30 +- .../aws-config/src/imds/client.rs | 25 +- aws/rust-runtime/aws-config/src/lib.rs | 90 +- .../aws-config/src/provider_config.rs | 63 +- aws/rust-runtime/aws-config/src/sso.rs | 10 +- aws/rust-runtime/aws-config/src/sts.rs | 56 +- .../aws-config/src/sts/assume_role.rs | 10 +- aws/rust-runtime/aws-config/src/sts/util.rs | 53 ++ aws/rust-runtime/aws-config/src/test_case.rs | 9 +- aws/rust-runtime/aws-types/Cargo.toml | 1 + aws/rust-runtime/aws-types/src/config.rs | 22 + aws/rust-runtime/aws-types/src/lib.rs | 1 + .../aws-smithy-client/src/http_connector.rs | 115 +++ .../aws-smithy-client/src/hyper_ext.rs | 28 +- rust-runtime/aws-smithy-client/src/lib.rs | 10 +- 23 files changed, 1119 insertions(+), 1047 deletions(-) create mode 100644 aws/rust-runtime/aws-config/src/default_provider/app_name.rs create mode 100644 aws/rust-runtime/aws-config/src/default_provider/credentials.rs create mode 100644 aws/rust-runtime/aws-config/src/default_provider/region.rs create mode 100644 aws/rust-runtime/aws-config/src/default_provider/retry_config.rs create mode 100644 aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs rename aws/rust-runtime/aws-config/src/{http_provider.rs => http_credential_provider.rs} (91%) create mode 100644 aws/rust-runtime/aws-config/src/sts/util.rs create mode 100644 rust-runtime/aws-smithy-client/src/http_connector.rs diff --git a/aws/rust-runtime/aws-config/src/default_provider.rs b/aws/rust-runtime/aws-config/src/default_provider.rs index 241f0be028..4ffe879ab1 100644 --- a/aws/rust-runtime/aws-config/src/default_provider.rs +++ b/aws/rust-runtime/aws-config/src/default_provider.rs @@ -13,847 +13,28 @@ /// /// Typically, this module is used via [`load_from_env`](crate::load_from_env) or [`from_env`](crate::from_env). It should only be used directly /// if you need to set custom configuration options to override the default resolution chain. -pub mod region { - use aws_types::region::Region; - - use crate::environment::region::EnvironmentVariableRegionProvider; - use crate::meta::region::{ProvideRegion, RegionProviderChain}; - use crate::provider_config::ProviderConfig; - use crate::{imds, profile}; - - /// Default Region Provider chain - /// - /// This provider will check the following sources in order: - /// 1. [Environment variables](EnvironmentVariableRegionProvider) - /// 2. [Profile file](crate::profile::region::ProfileFileRegionProvider) - /// 3. [EC2 IMDSv2](crate::imds::region) - pub fn default_provider() -> impl ProvideRegion { - Builder::default().build() - } - - /// Default region provider chain - #[derive(Debug)] - pub struct DefaultRegionChain(RegionProviderChain); - - impl DefaultRegionChain { - /// Load a region from this chain - pub async fn region(&self) -> Option { - self.0.region().await - } - - /// Builder for [`DefaultRegionChain`] - pub fn builder() -> Builder { - Builder::default() - } - } - - /// Builder for [DefaultRegionChain] - #[derive(Default)] - pub struct Builder { - env_provider: EnvironmentVariableRegionProvider, - profile_file: profile::region::Builder, - imds: imds::region::Builder, - } - - impl Builder { - #[doc(hidden)] - /// Configure the default chain - /// - /// Exposed for overriding the environment when unit-testing providers - pub fn configure(mut self, configuration: &ProviderConfig) -> Self { - self.env_provider = - EnvironmentVariableRegionProvider::new_with_env(configuration.env()); - self.profile_file = self.profile_file.configure(configuration); - self.imds = self.imds.configure(configuration); - self - } - - /// Override the profile name used by this provider - pub fn profile_name(mut self, name: &str) -> Self { - self.profile_file = self.profile_file.profile_name(name); - self - } - - /// Build a [DefaultRegionChain] - pub fn build(self) -> DefaultRegionChain { - DefaultRegionChain( - RegionProviderChain::first_try(self.env_provider) - .or_else(self.profile_file.build()) - .or_else(self.imds.build()), - ) - } - } - - impl ProvideRegion for DefaultRegionChain { - fn region(&self) -> crate::meta::region::future::ProvideRegion { - ProvideRegion::region(&self.0) - } - } -} +pub mod region; /// Default retry behavior configuration provider chain /// /// Typically, this module is used via [`load_from_env`](crate::load_from_env) or [`from_env`](crate::from_env). It should only be used directly /// if you need to set custom configuration options to override the default resolution chain. -pub mod retry_config { - use aws_smithy_types::retry::RetryConfig; - - use crate::environment::retry_config::EnvironmentVariableRetryConfigProvider; - use crate::profile; - use crate::provider_config::ProviderConfig; - - /// Default RetryConfig Provider chain - /// - /// Unlike other "providers" `RetryConfig` has no related `RetryConfigProvider` trait. Instead, - /// a builder struct is returned which has a similar API. - /// - /// This provider will check the following sources in order: - /// 1. [Environment variables](EnvironmentVariableRetryConfigProvider) - /// 2. [Profile file](crate::profile::retry_config::ProfileFileRetryConfigProvider) - /// - /// # Example - /// - /// When running [`aws_config::from_env()`](crate::from_env()), a [`ConfigLoader`](crate::ConfigLoader) - /// is created that will then create a [`RetryConfig`] from the default_provider. There is no - /// need to call `default_provider` and the example below is only for illustration purposes. - /// - /// ```no_run - /// # use std::error::Error; - /// # #[tokio::main] - /// # async fn main() -> Result<(), Box> { - /// use aws_config::default_provider::retry_config; - /// - /// // Load a retry config from a specific profile - /// let retry_config = retry_config::default_provider() - /// .profile_name("other_profile") - /// .retry_config() - /// .await; - /// let config = aws_config::from_env() - /// // Override the retry config set by the default profile - /// .retry_config(retry_config) - /// .load() - /// .await; - /// // instantiate a service client: - /// // ::Client::new(&config); - /// # Ok(()) - /// # } - /// ``` - pub fn default_provider() -> Builder { - Builder::default() - } - - /// Builder for RetryConfig that checks the environment and aws profile for configuration - #[derive(Default)] - pub struct Builder { - env_provider: EnvironmentVariableRetryConfigProvider, - profile_file: profile::retry_config::Builder, - } - - impl Builder { - /// Configure the default chain - /// - /// Exposed for overriding the environment when unit-testing providers - pub fn configure(mut self, configuration: &ProviderConfig) -> Self { - self.env_provider = - EnvironmentVariableRetryConfigProvider::new_with_env(configuration.env()); - self.profile_file = self.profile_file.configure(configuration); - self - } - - /// Override the profile name used by this provider - pub fn profile_name(mut self, name: &str) -> Self { - self.profile_file = self.profile_file.profile_name(name); - self - } - - /// Attempt to create a [RetryConfig](aws_smithy_types::retry::RetryConfig) from following sources in order: - /// 1. [Environment variables](crate::environment::retry_config::EnvironmentVariableRetryConfigProvider) - /// 2. [Profile file](crate::profile::retry_config::ProfileFileRetryConfigProvider) - /// 3. [RetryConfig::default()](aws_smithy_types::retry::RetryConfig::default) - /// - /// Precedence is considered on a per-field basis - /// - /// # Panics - /// - /// - Panics if the `AWS_MAX_ATTEMPTS` env var or `max_attempts` profile var is set to 0 - /// - Panics if the `AWS_RETRY_MODE` env var or `retry_mode` profile var is set to "adaptive" (it's not yet supported) - pub async fn retry_config(self) -> RetryConfig { - // Both of these can return errors due to invalid config settings and we want to surface those as early as possible - // hence, we'll panic if any config values are invalid (missing values are OK though) - // We match this instead of unwrapping so we can print the error with the `Display` impl instead of the `Debug` impl that unwrap uses - let builder_from_env = match self.env_provider.retry_config_builder() { - Ok(retry_config_builder) => retry_config_builder, - Err(err) => panic!("{}", err), - }; - let builder_from_profile = match self.profile_file.build().retry_config_builder().await - { - Ok(retry_config_builder) => retry_config_builder, - Err(err) => panic!("{}", err), - }; - - builder_from_env - .take_unset_from(builder_from_profile) - .build() - } - } -} +pub mod retry_config; /// Default app name provider chain /// /// Typically, this module is used via [`load_from_env`](crate::load_from_env) or [`from_env`](crate::from_env). It should only be used directly /// if you need to set custom configuration options to override the default resolution chain. -pub mod app_name { - use crate::environment::app_name::EnvironmentVariableAppNameProvider; - use crate::profile::app_name; - use crate::provider_config::ProviderConfig; - use aws_types::app_name::AppName; - - /// Default App Name Provider chain - /// - /// This provider will check the following sources in order: - /// 1. [Environment variables](EnvironmentVariableAppNameProvider) - /// 2. [Profile file](crate::profile::app_name::ProfileFileAppNameProvider) - pub fn default_provider() -> Builder { - Builder::default() - } - - /// Default provider builder for [`AppName`] - #[derive(Default)] - pub struct Builder { - env_provider: EnvironmentVariableAppNameProvider, - profile_file: app_name::Builder, - } - - impl Builder { - #[doc(hidden)] - /// Configure the default chain - /// - /// Exposed for overriding the environment when unit-testing providers - pub fn configure(mut self, configuration: &ProviderConfig) -> Self { - self.env_provider = - EnvironmentVariableAppNameProvider::new_with_env(configuration.env()); - self.profile_file = self.profile_file.configure(configuration); - self - } - - /// Override the profile name used by this provider - pub fn profile_name(mut self, name: &str) -> Self { - self.profile_file = self.profile_file.profile_name(name); - self - } - - /// Build an [`AppName`] from the default chain - pub async fn app_name(self) -> Option { - self.env_provider - .app_name() - .or(self.profile_file.build().app_name().await) - } - } - - #[cfg(test)] - mod tests { - use super::*; - use crate::provider_config::ProviderConfig; - use crate::test_case::no_traffic_connector; - use aws_types::os_shim_internal::{Env, Fs}; - - #[tokio::test] - async fn prefer_env_to_profile() { - let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = wrong")]); - let env = Env::from_slice(&[ - ("AWS_CONFIG_FILE", "test_config"), - ("AWS_SDK_UA_APP_ID", "correct"), - ]); - let app_name = Builder::default() - .configure( - &ProviderConfig::no_configuration() - .with_fs(fs) - .with_env(env) - .with_http_connector(no_traffic_connector()), - ) - .app_name() - .await; - - assert_eq!(Some(AppName::new("correct").unwrap()), app_name); - } - - #[tokio::test] - async fn load_from_profile() { - let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = correct")]); - let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); - let app_name = Builder::default() - .configure( - &ProviderConfig::empty() - .with_fs(fs) - .with_env(env) - .with_http_connector(no_traffic_connector()), - ) - .app_name() - .await; - - assert_eq!(Some(AppName::new("correct").unwrap()), app_name); - } - } -} +pub mod app_name; /// Default timeout configuration provider chain /// /// Typically, this module is used via [`load_from_env`](crate::load_from_env) or [`from_env`](crate::from_env). It should only be used directly /// if you need to set custom configuration options to override the default resolution chain. -pub mod timeout_config { - use aws_smithy_types::timeout::TimeoutConfig; - - use crate::environment::timeout_config::EnvironmentVariableTimeoutConfigProvider; - use crate::profile; - use crate::provider_config::ProviderConfig; - - /// Default [`TimeoutConfig`] Provider chain - /// - /// Unlike other credentials and region, [`TimeoutConfig`] has no related `TimeoutConfigProvider` trait. Instead, - /// a builder struct is returned which has a similar API. - /// - /// This provider will check the following sources in order: - /// 1. [Environment variables](EnvironmentVariableTimeoutConfigProvider) - /// 2. [Profile file](crate::profile::timeout_config::ProfileFileTimeoutConfigProvider) (`~/.aws/config`) - /// - /// # Example - /// - /// ```no_run - /// # use std::error::Error; - /// # #[tokio::main] - /// # async fn main() { - /// use aws_config::default_provider::timeout_config; - /// - /// // Load a timeout config from a specific profile - /// let timeout_config = timeout_config::default_provider() - /// .profile_name("other_profile") - /// .timeout_config() - /// .await; - /// let config = aws_config::from_env() - /// // Override the timeout config set by the default profile - /// .timeout_config(timeout_config) - /// .load() - /// .await; - /// // instantiate a service client: - /// // ::Client::new(&config); - /// # } - /// ``` - pub fn default_provider() -> Builder { - Builder::default() - } - - /// Builder for [`TimeoutConfig`] that checks the environment variables and AWS profile files for configuration - #[derive(Default)] - pub struct Builder { - env_provider: EnvironmentVariableTimeoutConfigProvider, - profile_file: profile::timeout_config::Builder, - } - - impl Builder { - /// Configure the default chain - /// - /// Exposed for overriding the environment when unit-testing providers - pub fn configure(mut self, configuration: &ProviderConfig) -> Self { - self.env_provider = - EnvironmentVariableTimeoutConfigProvider::new_with_env(configuration.env()); - self.profile_file = self.profile_file.configure(configuration); - self - } - - /// Override the profile name used by this provider - pub fn profile_name(mut self, name: &str) -> Self { - self.profile_file = self.profile_file.profile_name(name); - self - } - - /// Attempt to create a [`TimeoutConfig`](aws_smithy_types::timeout::TimeoutConfig) from following sources in order: - /// 1. [Environment variables](crate::environment::timeout_config::EnvironmentVariableTimeoutConfigProvider) - /// 2. [Profile file](crate::profile::timeout_config::ProfileFileTimeoutConfigProvider) - /// - /// Precedence is considered on a per-field basis. If no timeout is specified, requests will never time out. - /// - /// # Panics - /// - /// This will panic if: - /// - a timeout is set to `NaN`, a negative number, or infinity - /// - a timeout can't be parsed as a floating point number - pub async fn timeout_config(self) -> TimeoutConfig { - // Both of these can return errors due to invalid config settings and we want to surface those as early as possible - // hence, we'll panic if any config values are invalid (missing values are OK though) - // We match this instead of unwrapping so we can print the error with the `Display` impl instead of the `Debug` impl that unwrap uses - let builder_from_env = match self.env_provider.timeout_config() { - Ok(timeout_config_builder) => timeout_config_builder, - Err(err) => panic!("{}", err), - }; - let builder_from_profile = match self.profile_file.build().timeout_config().await { - Ok(timeout_config_builder) => timeout_config_builder, - Err(err) => panic!("{}", err), - }; - - let conf = builder_from_env.take_unset_from(builder_from_profile); - - if conf.tls_negotiation_timeout().is_some() { - tracing::warn!( - "A TLS negotiation timeout was set but that feature is currently unimplemented so the setting will be ignored. \ - To help us prioritize support for this feature, please upvote aws-sdk-rust#151 (https://github.com/awslabs/aws-sdk-rust/issues/151)") - } - - if conf.connect_timeout().is_some() { - tracing::warn!( - "A connect timeout was set but that feature is currently unimplemented so the setting will be ignored. \ - To help us prioritize support for this feature, please upvote aws-sdk-rust#151 (https://github.com/awslabs/aws-sdk-rust/issues/151)") - } - - if conf.read_timeout().is_some() { - tracing::warn!( - "A read timeout was set but that feature is currently unimplemented so the setting will be ignored. \ - To help us prioritize support for this feature, please upvote aws-sdk-rust#151 (https://github.com/awslabs/aws-sdk-rust/issues/151)") - } - - conf - } - } -} +pub mod timeout_config; /// Default credentials provider chain /// /// Typically, this module is used via [`load_from_env`](crate::load_from_env) or [`from_env`](crate::from_env). It should only be used directly /// if you need to set custom configuration options like [`region`](credentials::Builder::region) or [`profile_name`](credentials::Builder::profile_name). -pub mod credentials { - use aws_types::credentials; - use std::borrow::Cow; - - use aws_types::credentials::{future, ProvideCredentials}; - use tracing::Instrument; - - use crate::environment::credentials::EnvironmentVariableCredentialsProvider; - use crate::meta::credentials::{CredentialsProviderChain, LazyCachingCredentialsProvider}; - use crate::meta::region::ProvideRegion; - use crate::provider_config::ProviderConfig; - - #[cfg(any(feature = "rustls", feature = "native-tls"))] - /// Default Credentials Provider chain - /// - /// The region from the default region provider will be used - pub async fn default_provider() -> impl ProvideCredentials { - DefaultCredentialsChain::builder().build().await - } - - /// Default AWS Credential Provider Chain - /// - /// Resolution order: - /// 1. Environment variables: [`EnvironmentVariableCredentialsProvider`](crate::environment::EnvironmentVariableCredentialsProvider) - /// 2. Shared config (`~/.aws/config`, `~/.aws/credentials`): [`SharedConfigCredentialsProvider`](crate::profile::ProfileFileCredentialsProvider) - /// 3. [Web Identity Tokens](crate::web_identity_token) - /// 4. ECS (IAM Roles for Tasks) & General HTTP credentials: [`ecs`](crate::ecs) - /// 5. [EC2 IMDSv2](crate::imds) - /// - /// The outer provider is wrapped in a refreshing cache. - /// - /// More providers are a work in progress. - /// - /// # Examples - /// Create a default chain with a custom region: - /// ```no_run - /// use aws_types::region::Region; - /// use aws_config::default_provider::credentials::DefaultCredentialsChain; - /// let credentials_provider = DefaultCredentialsChain::builder() - /// .region(Region::new("us-west-1")) - /// .build(); - /// ``` - /// - /// Create a default chain with no overrides: - /// ```no_run - /// use aws_config::default_provider::credentials::DefaultCredentialsChain; - /// let credentials_provider = DefaultCredentialsChain::builder().build(); - /// ``` - /// - /// Create a default chain that uses a different profile: - /// ```no_run - /// use aws_config::default_provider::credentials::DefaultCredentialsChain; - /// let credentials_provider = DefaultCredentialsChain::builder() - /// .profile_name("otherprofile") - /// .build(); - /// ``` - #[derive(Debug)] - pub struct DefaultCredentialsChain(LazyCachingCredentialsProvider); - - impl DefaultCredentialsChain { - /// Builder for `DefaultCredentialsChain` - pub fn builder() -> Builder { - Builder::default() - } - - async fn credentials(&self) -> credentials::Result { - self.0 - .provide_credentials() - .instrument(tracing::info_span!("provide_credentials", provider = %"default_chain")) - .await - } - } - - impl ProvideCredentials for DefaultCredentialsChain { - fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> - where - Self: 'a, - { - future::ProvideCredentials::new(self.credentials()) - } - } - - /// Builder for [`DefaultCredentialsChain`](DefaultCredentialsChain) - #[derive(Default)] - pub struct Builder { - profile_file_builder: crate::profile::credentials::Builder, - web_identity_builder: crate::web_identity_token::Builder, - imds_builder: crate::imds::credentials::Builder, - ecs_builder: crate::ecs::Builder, - credential_cache: crate::meta::credentials::lazy_caching::Builder, - region_override: Option>, - region_chain: crate::default_provider::region::Builder, - conf: Option, - } - - impl Builder { - /// Sets the region used when making requests to AWS services - /// - /// When unset, the default region resolver chain will be used. - pub fn region(mut self, region: impl ProvideRegion + 'static) -> Self { - self.set_region(Some(region)); - self - } - - /// Sets the region used when making requests to AWS services - /// - /// When unset, the default region resolver chain will be used. - pub fn set_region(&mut self, region: Option) -> &mut Self { - self.region_override = region.map(|provider| Box::new(provider) as _); - self - } - - /// Add an additional credential source for the ProfileProvider - /// - /// Assume role profiles may specify named credential sources: - /// ```ini - /// [default] - /// role_arn = arn:aws:iam::123456789:role/RoleA - /// credential_source = MyCustomProvider - /// ``` - /// - /// Typically, these are built-in providers like `Environment`, however, custom sources may - /// also be used. - /// - /// See [`with_custom_provider`](crate::profile::credentials::Builder::with_custom_provider) - pub fn with_custom_credential_source( - mut self, - name: impl Into>, - provider: impl ProvideCredentials + 'static, - ) -> Self { - self.profile_file_builder = self - .profile_file_builder - .with_custom_provider(name, provider); - self - } - - /// Override the profile name used by this provider - /// - /// When unset, the value of the `AWS_PROFILE` environment variable will be used. - pub fn profile_name(mut self, name: &str) -> Self { - self.profile_file_builder = self.profile_file_builder.profile_name(name); - self.region_chain = self.region_chain.profile_name(name); - self - } - - /// Override the configuration used for this provider - pub fn configure(mut self, config: ProviderConfig) -> Self { - self.region_chain = self.region_chain.configure(&config); - self.conf = Some(config); - self - } - - /// Creates a `DefaultCredentialsChain` - /// - /// ## Panics - /// This function will panic if no connector has been set and neither `rustls` and `native-tls` - /// features have both been disabled. - pub async fn build(self) -> DefaultCredentialsChain { - let region = match self.region_override { - Some(provider) => provider.region().await, - None => self.region_chain.build().region().await, - }; - - let conf = self.conf.unwrap_or_default().with_region(region); - - let env_provider = EnvironmentVariableCredentialsProvider::new_with_env(conf.env()); - let profile_provider = self.profile_file_builder.configure(&conf).build(); - let web_identity_token_provider = self.web_identity_builder.configure(&conf).build(); - let imds_provider = self.imds_builder.configure(&conf).build(); - let ecs_provider = self.ecs_builder.configure(&conf).build(); - - let provider_chain = CredentialsProviderChain::first_try("Environment", env_provider) - .or_else("Profile", profile_provider) - .or_else("WebIdentityToken", web_identity_token_provider) - .or_else("EcsContainer", ecs_provider) - .or_else("Ec2InstanceMetadata", imds_provider); - let cached_provider = self.credential_cache.configure(&conf).load(provider_chain); - - DefaultCredentialsChain(cached_provider.build()) - } - } - - #[cfg(test)] - mod test { - use tracing_test::traced_test; - - use aws_smithy_types::retry::{RetryConfig, RetryMode}; - use aws_types::credentials::ProvideCredentials; - use aws_types::os_shim_internal::{Env, Fs}; - - use crate::default_provider::credentials::DefaultCredentialsChain; - use crate::default_provider::retry_config; - use crate::provider_config::ProviderConfig; - use crate::test_case::TestEnvironment; - - /// Test generation macro - /// - /// # Examples - /// **Run the test case in `test-data/default-provider-chain/test_name` - /// ```no_run - /// make_test!(test_name); - /// ``` - /// - /// **Update (responses are replayed but new requests are recorded) the test case**: - /// ```no_run - /// make_test!(update: test_name) - /// ``` - /// - /// **Run the test case against a real HTTPS connection:** - /// > Note: Be careful to remove sensitive information before committing. Always use a temporary - /// > AWS account when recording live traffic. - /// ```no_run - /// make_test!(live: test_name) - /// ``` - macro_rules! make_test { - ($name: ident) => { - make_test!($name, execute); - }; - (update: $name:ident) => { - make_test!($name, execute_and_update); - }; - (live: $name:ident) => { - make_test!($name, execute_from_live_traffic); - }; - ($name: ident, $func: ident) => { - #[traced_test] - #[tokio::test] - async fn $name() { - crate::test_case::TestEnvironment::from_dir(concat!( - "./test-data/default-provider-chain/", - stringify!($name) - )) - .unwrap() - .$func(|conf| async { - crate::default_provider::credentials::Builder::default() - .configure(conf) - .build() - .await - }) - .await - } - }; - } - - make_test!(prefer_environment); - make_test!(profile_static_keys); - make_test!(web_identity_token_env); - make_test!(web_identity_source_profile_no_env); - make_test!(web_identity_token_invalid_jwt); - make_test!(web_identity_token_source_profile); - make_test!(web_identity_token_profile); - make_test!(profile_name); - make_test!(profile_overrides_web_identity); - make_test!(imds_token_fail); - - make_test!(imds_no_iam_role); - make_test!(imds_default_chain_error); - make_test!(imds_default_chain_success); - make_test!(imds_assume_role); - make_test!(imds_config_with_no_creds); - make_test!(imds_disabled); - make_test!(imds_default_chain_retries); - - make_test!(ecs_assume_role); - make_test!(ecs_credentials); - - make_test!(sso_assume_role); - make_test!(sso_no_token_file); - - #[tokio::test] - async fn profile_name_override() { - let (_, conf) = - TestEnvironment::from_dir("./test-data/default-provider-chain/profile_static_keys") - .unwrap() - .provider_config() - .await; - let provider = DefaultCredentialsChain::builder() - .profile_name("secondary") - .configure(conf) - .build() - .await; - let creds = provider - .provide_credentials() - .await - .expect("creds should load"); - assert_eq!(creds.access_key_id(), "correct_key_secondary"); - } - - #[tokio::test] - #[traced_test] - async fn no_providers_configured_err() { - use aws_smithy_async::rt::sleep::TokioSleep; - use aws_smithy_client::erase::boxclone::BoxCloneService; - use aws_smithy_client::never::NeverConnected; - use aws_types::credentials::CredentialsError; - use aws_types::os_shim_internal::TimeSource; - - tokio::time::pause(); - let conf = ProviderConfig::no_configuration() - .with_tcp_connector(BoxCloneService::new(NeverConnected::new())) - .with_time_source(TimeSource::real()) - .with_sleep(TokioSleep::new()); - let provider = DefaultCredentialsChain::builder() - .configure(conf) - .build() - .await; - let creds = provider - .provide_credentials() - .await - .expect_err("no providers enabled"); - assert!( - matches!(creds, CredentialsError::CredentialsNotLoaded { .. }), - "should be NotLoaded: {:?}", - creds - ) - } - - #[tokio::test] - async fn test_returns_default_retry_config_from_empty_profile() { - let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); - let fs = Fs::from_slice(&[("config", "[default]\n")]); - - let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); - - let actual_retry_config = retry_config::default_provider() - .configure(&provider_config) - .retry_config() - .await; - - let expected_retry_config = RetryConfig::new(); - - assert_eq!(actual_retry_config, expected_retry_config); - // This is redundant but it's really important to make sure that - // we're setting these exact values by default so we check twice - assert_eq!(actual_retry_config.max_attempts(), 3); - assert_eq!(actual_retry_config.mode(), RetryMode::Standard); - } - - #[tokio::test] - async fn test_no_retry_config_in_empty_profile() { - let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); - let fs = Fs::from_slice(&[("config", "[default]\n")]); - - let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); - - let actual_retry_config = retry_config::default_provider() - .configure(&provider_config) - .retry_config() - .await; - - let expected_retry_config = RetryConfig::new(); - - assert_eq!(actual_retry_config, expected_retry_config) - } - - #[tokio::test] - async fn test_creation_of_retry_config_from_profile() { - let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); - // TODO(https://github.com/awslabs/aws-sdk-rust/issues/247): standard is the default mode; - // this test would be better if it was setting it to adaptive mode - // adaptive mode is currently unsupported so that would panic - let fs = Fs::from_slice(&[( - "config", - // If the lines with the vars have preceding spaces, they don't get read - r#"[default] -max_attempts = 1 -retry_mode = standard - "#, - )]); - - let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); - - let actual_retry_config = retry_config::default_provider() - .configure(&provider_config) - .retry_config() - .await; - - let expected_retry_config = RetryConfig::new() - .with_max_attempts(1) - .with_retry_mode(RetryMode::Standard); - - assert_eq!(actual_retry_config, expected_retry_config) - } - - #[tokio::test] - async fn test_env_retry_config_takes_precedence_over_profile_retry_config() { - let env = Env::from_slice(&[ - ("AWS_CONFIG_FILE", "config"), - ("AWS_MAX_ATTEMPTS", "42"), - ("AWS_RETRY_MODE", "standard"), - ]); - // TODO(https://github.com/awslabs/aws-sdk-rust/issues/247) standard is the default mode; - // this test would be better if it was setting it to adaptive mode - // adaptive mode is currently unsupported so that would panic - let fs = Fs::from_slice(&[( - "config", - // If the lines with the vars have preceding spaces, they don't get read - r#"[default] -max_attempts = 88 -retry_mode = standard - "#, - )]); - - let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); - - let actual_retry_config = retry_config::default_provider() - .configure(&provider_config) - .retry_config() - .await; - - let expected_retry_config = RetryConfig::new() - .with_max_attempts(42) - .with_retry_mode(RetryMode::Standard); - - assert_eq!(actual_retry_config, expected_retry_config) - } - - #[tokio::test] - #[should_panic = "failed to parse max attempts set by aws profile: invalid digit found in string"] - async fn test_invalid_profile_retry_config_panics() { - let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); - let fs = Fs::from_slice(&[( - "config", - // If the lines with the vars have preceding spaces, they don't get read - r#"[default] -max_attempts = potato - "#, - )]); - - let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); - - let _ = retry_config::default_provider() - .configure(&provider_config) - .retry_config() - .await; - } - } -} +pub mod credentials; diff --git a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs new file mode 100644 index 0000000000..fb5a375093 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs @@ -0,0 +1,90 @@ +use crate::environment::app_name::EnvironmentVariableAppNameProvider; +use crate::profile::app_name; +use crate::provider_config::ProviderConfig; +use aws_types::app_name::AppName; + +/// Default App Name Provider chain +/// +/// This provider will check the following sources in order: +/// 1. [Environment variables](EnvironmentVariableAppNameProvider) +/// 2. [Profile file](crate::profile::app_name::ProfileFileAppNameProvider) +pub fn default_provider() -> Builder { + Builder::default() +} + +/// Default provider builder for [`AppName`] +#[derive(Default)] +pub struct Builder { + env_provider: EnvironmentVariableAppNameProvider, + profile_file: app_name::Builder, +} + +impl Builder { + #[doc(hidden)] + /// Configure the default chain + /// + /// Exposed for overriding the environment when unit-testing providers + pub fn configure(mut self, configuration: &ProviderConfig) -> Self { + self.env_provider = EnvironmentVariableAppNameProvider::new_with_env(configuration.env()); + self.profile_file = self.profile_file.configure(configuration); + self + } + + /// Override the profile name used by this provider + pub fn profile_name(mut self, name: &str) -> Self { + self.profile_file = self.profile_file.profile_name(name); + self + } + + /// Build an [`AppName`] from the default chain + pub async fn app_name(self) -> Option { + self.env_provider + .app_name() + .or(self.profile_file.build().app_name().await) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::provider_config::ProviderConfig; + use crate::test_case::no_traffic_connector; + use aws_types::os_shim_internal::{Env, Fs}; + + #[tokio::test] + async fn prefer_env_to_profile() { + let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = wrong")]); + let env = Env::from_slice(&[ + ("AWS_CONFIG_FILE", "test_config"), + ("AWS_SDK_UA_APP_ID", "correct"), + ]); + let app_name = Builder::default() + .configure( + &ProviderConfig::no_configuration() + .with_fs(fs) + .with_env(env) + .with_http_connector(no_traffic_connector()), + ) + .app_name() + .await; + + assert_eq!(Some(AppName::new("correct").unwrap()), app_name); + } + + #[tokio::test] + async fn load_from_profile() { + let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = correct")]); + let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); + let app_name = Builder::default() + .configure( + &ProviderConfig::empty() + .with_fs(fs) + .with_env(env) + .with_http_connector(no_traffic_connector()), + ) + .app_name() + .await; + + assert_eq!(Some(AppName::new("correct").unwrap()), app_name); + } +} diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs new file mode 100644 index 0000000000..46598522f1 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -0,0 +1,436 @@ +use aws_types::credentials; +use std::borrow::Cow; + +use aws_types::credentials::{future, ProvideCredentials}; +use tracing::Instrument; + +use crate::environment::credentials::EnvironmentVariableCredentialsProvider; +use crate::meta::credentials::{CredentialsProviderChain, LazyCachingCredentialsProvider}; +use crate::meta::region::ProvideRegion; +use crate::provider_config::ProviderConfig; + +#[cfg(any(feature = "rustls", feature = "native-tls"))] +/// Default Credentials Provider chain +/// +/// The region from the default region provider will be used +pub async fn default_provider() -> impl ProvideCredentials { + DefaultCredentialsChain::builder().build().await +} + +/// Default AWS Credential Provider Chain +/// +/// Resolution order: +/// 1. Environment variables: [`EnvironmentVariableCredentialsProvider`](crate::environment::EnvironmentVariableCredentialsProvider) +/// 2. Shared config (`~/.aws/config`, `~/.aws/credentials`): [`SharedConfigCredentialsProvider`](crate::profile::ProfileFileCredentialsProvider) +/// 3. [Web Identity Tokens](crate::web_identity_token) +/// 4. ECS (IAM Roles for Tasks) & General HTTP credentials: [`ecs`](crate::ecs) +/// 5. [EC2 IMDSv2](crate::imds) +/// +/// The outer provider is wrapped in a refreshing cache. +/// +/// More providers are a work in progress. +/// +/// # Examples +/// Create a default chain with a custom region: +/// ```no_run +/// use aws_types::region::Region; +/// use aws_config::default_provider::credentials::DefaultCredentialsChain; +/// let credentials_provider = DefaultCredentialsChain::builder() +/// .region(Region::new("us-west-1")) +/// .build(); +/// ``` +/// +/// Create a default chain with no overrides: +/// ```no_run +/// use aws_config::default_provider::credentials::DefaultCredentialsChain; +/// let credentials_provider = DefaultCredentialsChain::builder().build(); +/// ``` +/// +/// Create a default chain that uses a different profile: +/// ```no_run +/// use aws_config::default_provider::credentials::DefaultCredentialsChain; +/// let credentials_provider = DefaultCredentialsChain::builder() +/// .profile_name("otherprofile") +/// .build(); +/// ``` +#[derive(Debug)] +pub struct DefaultCredentialsChain(LazyCachingCredentialsProvider); + +impl DefaultCredentialsChain { + /// Builder for `DefaultCredentialsChain` + pub fn builder() -> Builder { + Builder::default() + } + + async fn credentials(&self) -> credentials::Result { + self.0 + .provide_credentials() + .instrument(tracing::info_span!("provide_credentials", provider = %"default_chain")) + .await + } +} + +impl ProvideCredentials for DefaultCredentialsChain { + fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> + where + Self: 'a, + { + future::ProvideCredentials::new(self.credentials()) + } +} + +/// Builder for [`DefaultCredentialsChain`](DefaultCredentialsChain) +#[derive(Default)] +pub struct Builder { + profile_file_builder: crate::profile::credentials::Builder, + web_identity_builder: crate::web_identity_token::Builder, + imds_builder: crate::imds::credentials::Builder, + ecs_builder: crate::ecs::Builder, + credential_cache: crate::meta::credentials::lazy_caching::Builder, + region_override: Option>, + region_chain: crate::default_provider::region::Builder, + conf: Option, +} + +impl Builder { + /// Sets the region used when making requests to AWS services + /// + /// When unset, the default region resolver chain will be used. + pub fn region(mut self, region: impl ProvideRegion + 'static) -> Self { + self.set_region(Some(region)); + self + } + + /// Sets the region used when making requests to AWS services + /// + /// When unset, the default region resolver chain will be used. + pub fn set_region(&mut self, region: Option) -> &mut Self { + self.region_override = region.map(|provider| Box::new(provider) as _); + self + } + + /// Add an additional credential source for the ProfileProvider + /// + /// Assume role profiles may specify named credential sources: + /// ```ini + /// [default] + /// role_arn = arn:aws:iam::123456789:role/RoleA + /// credential_source = MyCustomProvider + /// ``` + /// + /// Typically, these are built-in providers like `Environment`, however, custom sources may + /// also be used. + /// + /// See [`with_custom_provider`](crate::profile::credentials::Builder::with_custom_provider) + pub fn with_custom_credential_source( + mut self, + name: impl Into>, + provider: impl ProvideCredentials + 'static, + ) -> Self { + self.profile_file_builder = self + .profile_file_builder + .with_custom_provider(name, provider); + self + } + + /// Override the profile name used by this provider + /// + /// When unset, the value of the `AWS_PROFILE` environment variable will be used. + pub fn profile_name(mut self, name: &str) -> Self { + self.profile_file_builder = self.profile_file_builder.profile_name(name); + self.region_chain = self.region_chain.profile_name(name); + self + } + + /// Override the configuration used for this provider + pub fn configure(mut self, config: ProviderConfig) -> Self { + self.region_chain = self.region_chain.configure(&config); + self.conf = Some(config); + self + } + + /// Creates a `DefaultCredentialsChain` + /// + /// ## Panics + /// This function will panic if no connector has been set and neither `rustls` and `native-tls` + /// features have both been disabled. + pub async fn build(self) -> DefaultCredentialsChain { + let region = match self.region_override { + Some(provider) => provider.region().await, + None => self.region_chain.build().region().await, + }; + + let conf = self.conf.unwrap_or_default().with_region(region); + + let env_provider = EnvironmentVariableCredentialsProvider::new_with_env(conf.env()); + let profile_provider = self.profile_file_builder.configure(&conf).build(); + let web_identity_token_provider = self.web_identity_builder.configure(&conf).build(); + let imds_provider = self.imds_builder.configure(&conf).build(); + let ecs_provider = self.ecs_builder.configure(&conf).build(); + + let provider_chain = CredentialsProviderChain::first_try("Environment", env_provider) + .or_else("Profile", profile_provider) + .or_else("WebIdentityToken", web_identity_token_provider) + .or_else("EcsContainer", ecs_provider) + .or_else("Ec2InstanceMetadata", imds_provider); + let cached_provider = self.credential_cache.configure(&conf).load(provider_chain); + + DefaultCredentialsChain(cached_provider.build()) + } +} + +#[cfg(test)] +mod test { + use tracing_test::traced_test; + + use aws_smithy_types::retry::{RetryConfig, RetryMode}; + use aws_types::credentials::ProvideCredentials; + use aws_types::os_shim_internal::{Env, Fs}; + + use crate::default_provider::credentials::DefaultCredentialsChain; + use crate::default_provider::retry_config; + use crate::provider_config::ProviderConfig; + use crate::test_case::TestEnvironment; + + /// Test generation macro + /// + /// # Examples + /// **Run the test case in `test-data/default-provider-chain/test_name` + /// ```no_run + /// make_test!(test_name); + /// ``` + /// + /// **Update (responses are replayed but new requests are recorded) the test case**: + /// ```no_run + /// make_test!(update: test_name) + /// ``` + /// + /// **Run the test case against a real HTTPS connection:** + /// > Note: Be careful to remove sensitive information before committing. Always use a temporary + /// > AWS account when recording live traffic. + /// ```no_run + /// make_test!(live: test_name) + /// ``` + macro_rules! make_test { + ($name: ident) => { + make_test!($name, execute); + }; + (update: $name:ident) => { + make_test!($name, execute_and_update); + }; + (live: $name:ident) => { + make_test!($name, execute_from_live_traffic); + }; + ($name: ident, $func: ident) => { + #[traced_test] + #[tokio::test] + async fn $name() { + crate::test_case::TestEnvironment::from_dir(concat!( + "./test-data/default-provider-chain/", + stringify!($name) + )) + .unwrap() + .$func(|conf| async { + crate::default_provider::credentials::Builder::default() + .configure(conf) + .build() + .await + }) + .await + } + }; + } + + make_test!(prefer_environment); + make_test!(profile_static_keys); + make_test!(web_identity_token_env); + make_test!(web_identity_source_profile_no_env); + make_test!(web_identity_token_invalid_jwt); + make_test!(web_identity_token_source_profile); + make_test!(web_identity_token_profile); + make_test!(profile_name); + make_test!(profile_overrides_web_identity); + make_test!(imds_token_fail); + + make_test!(imds_no_iam_role); + make_test!(imds_default_chain_error); + make_test!(imds_default_chain_success); + make_test!(imds_assume_role); + make_test!(imds_config_with_no_creds); + make_test!(imds_disabled); + make_test!(imds_default_chain_retries); + + make_test!(ecs_assume_role); + make_test!(ecs_credentials); + + make_test!(sso_assume_role); + make_test!(sso_no_token_file); + + #[tokio::test] + async fn profile_name_override() { + let (_, conf) = + TestEnvironment::from_dir("./test-data/default-provider-chain/profile_static_keys") + .unwrap() + .provider_config() + .await; + let provider = DefaultCredentialsChain::builder() + .profile_name("secondary") + .configure(conf) + .build() + .await; + let creds = provider + .provide_credentials() + .await + .expect("creds should load"); + assert_eq!(creds.access_key_id(), "correct_key_secondary"); + } + + #[tokio::test] + #[traced_test] + async fn no_providers_configured_err() { + use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_client::erase::boxclone::BoxCloneService; + use aws_smithy_client::never::NeverConnected; + use aws_types::credentials::CredentialsError; + use aws_types::os_shim_internal::TimeSource; + + tokio::time::pause(); + let conf = ProviderConfig::no_configuration() + .with_tcp_connector(BoxCloneService::new(NeverConnected::new())) + .with_time_source(TimeSource::real()) + .with_sleep(TokioSleep::new()); + let provider = DefaultCredentialsChain::builder() + .configure(conf) + .build() + .await; + let creds = provider + .provide_credentials() + .await + .expect_err("no providers enabled"); + assert!( + matches!(creds, CredentialsError::CredentialsNotLoaded { .. }), + "should be NotLoaded: {:?}", + creds + ) + } + + #[tokio::test] + async fn test_returns_default_retry_config_from_empty_profile() { + let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); + let fs = Fs::from_slice(&[("config", "[default]\n")]); + + let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); + + let actual_retry_config = retry_config::default_provider() + .configure(&provider_config) + .retry_config() + .await; + + let expected_retry_config = RetryConfig::new(); + + assert_eq!(actual_retry_config, expected_retry_config); + // This is redundant but it's really important to make sure that + // we're setting these exact values by default so we check twice + assert_eq!(actual_retry_config.max_attempts(), 3); + assert_eq!(actual_retry_config.mode(), RetryMode::Standard); + } + + #[tokio::test] + async fn test_no_retry_config_in_empty_profile() { + let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); + let fs = Fs::from_slice(&[("config", "[default]\n")]); + + let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); + + let actual_retry_config = retry_config::default_provider() + .configure(&provider_config) + .retry_config() + .await; + + let expected_retry_config = RetryConfig::new(); + + assert_eq!(actual_retry_config, expected_retry_config) + } + + #[tokio::test] + async fn test_creation_of_retry_config_from_profile() { + let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); + // TODO(https://github.com/awslabs/aws-sdk-rust/issues/247): standard is the default mode; + // this test would be better if it was setting it to adaptive mode + // adaptive mode is currently unsupported so that would panic + let fs = Fs::from_slice(&[( + "config", + // If the lines with the vars have preceding spaces, they don't get read + r#"[default] +max_attempts = 1 +retry_mode = standard + "#, + )]); + + let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); + + let actual_retry_config = retry_config::default_provider() + .configure(&provider_config) + .retry_config() + .await; + + let expected_retry_config = RetryConfig::new() + .with_max_attempts(1) + .with_retry_mode(RetryMode::Standard); + + assert_eq!(actual_retry_config, expected_retry_config) + } + + #[tokio::test] + async fn test_env_retry_config_takes_precedence_over_profile_retry_config() { + let env = Env::from_slice(&[ + ("AWS_CONFIG_FILE", "config"), + ("AWS_MAX_ATTEMPTS", "42"), + ("AWS_RETRY_MODE", "standard"), + ]); + // TODO(https://github.com/awslabs/aws-sdk-rust/issues/247) standard is the default mode; + // this test would be better if it was setting it to adaptive mode + // adaptive mode is currently unsupported so that would panic + let fs = Fs::from_slice(&[( + "config", + // If the lines with the vars have preceding spaces, they don't get read + r#"[default] +max_attempts = 88 +retry_mode = standard + "#, + )]); + + let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); + + let actual_retry_config = retry_config::default_provider() + .configure(&provider_config) + .retry_config() + .await; + + let expected_retry_config = RetryConfig::new() + .with_max_attempts(42) + .with_retry_mode(RetryMode::Standard); + + assert_eq!(actual_retry_config, expected_retry_config) + } + + #[tokio::test] + #[should_panic = "failed to parse max attempts set by aws profile: invalid digit found in string"] + async fn test_invalid_profile_retry_config_panics() { + let env = Env::from_slice(&[("AWS_CONFIG_FILE", "config")]); + let fs = Fs::from_slice(&[( + "config", + // If the lines with the vars have preceding spaces, they don't get read + r#"[default] +max_attempts = potato + "#, + )]); + + let provider_config = ProviderConfig::no_configuration().with_env(env).with_fs(fs); + + let _ = retry_config::default_provider() + .configure(&provider_config) + .retry_config() + .await; + } +} diff --git a/aws/rust-runtime/aws-config/src/default_provider/region.rs b/aws/rust-runtime/aws-config/src/default_provider/region.rs new file mode 100644 index 0000000000..bd0849e70b --- /dev/null +++ b/aws/rust-runtime/aws-config/src/default_provider/region.rs @@ -0,0 +1,74 @@ +use aws_types::region::Region; + +use crate::environment::region::EnvironmentVariableRegionProvider; +use crate::meta::region::{ProvideRegion, RegionProviderChain}; +use crate::provider_config::ProviderConfig; +use crate::{imds, profile}; + +/// Default Region Provider chain +/// +/// This provider will check the following sources in order: +/// 1. [Environment variables](EnvironmentVariableRegionProvider) +/// 2. [Profile file](crate::profile::region::ProfileFileRegionProvider) +/// 3. [EC2 IMDSv2](crate::imds::region) +pub fn default_provider() -> impl ProvideRegion { + Builder::default().build() +} + +/// Default region provider chain +#[derive(Debug)] +pub struct DefaultRegionChain(RegionProviderChain); + +impl DefaultRegionChain { + /// Load a region from this chain + pub async fn region(&self) -> Option { + self.0.region().await + } + + /// Builder for [`DefaultRegionChain`] + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Builder for [DefaultRegionChain] +#[derive(Default)] +pub struct Builder { + env_provider: EnvironmentVariableRegionProvider, + profile_file: profile::region::Builder, + imds: imds::region::Builder, +} + +impl Builder { + #[doc(hidden)] + /// Configure the default chain + /// + /// Exposed for overriding the environment when unit-testing providers + pub fn configure(mut self, configuration: &ProviderConfig) -> Self { + self.env_provider = EnvironmentVariableRegionProvider::new_with_env(configuration.env()); + self.profile_file = self.profile_file.configure(configuration); + self.imds = self.imds.configure(configuration); + self + } + + /// Override the profile name used by this provider + pub fn profile_name(mut self, name: &str) -> Self { + self.profile_file = self.profile_file.profile_name(name); + self + } + + /// Build a [DefaultRegionChain] + pub fn build(self) -> DefaultRegionChain { + DefaultRegionChain( + RegionProviderChain::first_try(self.env_provider) + .or_else(self.profile_file.build()) + .or_else(self.imds.build()), + ) + } +} + +impl ProvideRegion for DefaultRegionChain { + fn region(&self) -> crate::meta::region::future::ProvideRegion { + ProvideRegion::region(&self.0) + } +} diff --git a/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs b/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs new file mode 100644 index 0000000000..2da79dfd73 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs @@ -0,0 +1,99 @@ +use aws_smithy_types::retry::RetryConfig; + +use crate::environment::retry_config::EnvironmentVariableRetryConfigProvider; +use crate::profile; +use crate::provider_config::ProviderConfig; + +/// Default RetryConfig Provider chain +/// +/// Unlike other "providers" `RetryConfig` has no related `RetryConfigProvider` trait. Instead, +/// a builder struct is returned which has a similar API. +/// +/// This provider will check the following sources in order: +/// 1. [Environment variables](EnvironmentVariableRetryConfigProvider) +/// 2. [Profile file](crate::profile::retry_config::ProfileFileRetryConfigProvider) +/// +/// # Example +/// +/// When running [`aws_config::from_env()`](crate::from_env()), a [`ConfigLoader`](crate::ConfigLoader) +/// is created that will then create a [`RetryConfig`] from the default_provider. There is no +/// need to call `default_provider` and the example below is only for illustration purposes. +/// +/// ```no_run +/// # use std::error::Error; +/// # #[tokio::main] +/// # async fn main() -> Result<(), Box> { +/// use aws_config::default_provider::retry_config; +/// +/// // Load a retry config from a specific profile +/// let retry_config = retry_config::default_provider() +/// .profile_name("other_profile") +/// .retry_config() +/// .await; +/// let config = aws_config::from_env() +/// // Override the retry config set by the default profile +/// .retry_config(retry_config) +/// .load() +/// .await; +/// // instantiate a service client: +/// // ::Client::new(&config); +/// # Ok(()) +/// # } +/// ``` +pub fn default_provider() -> Builder { + Builder::default() +} + +/// Builder for RetryConfig that checks the environment and aws profile for configuration +#[derive(Default)] +pub struct Builder { + env_provider: EnvironmentVariableRetryConfigProvider, + profile_file: profile::retry_config::Builder, +} + +impl Builder { + /// Configure the default chain + /// + /// Exposed for overriding the environment when unit-testing providers + pub fn configure(mut self, configuration: &ProviderConfig) -> Self { + self.env_provider = + EnvironmentVariableRetryConfigProvider::new_with_env(configuration.env()); + self.profile_file = self.profile_file.configure(configuration); + self + } + + /// Override the profile name used by this provider + pub fn profile_name(mut self, name: &str) -> Self { + self.profile_file = self.profile_file.profile_name(name); + self + } + + /// Attempt to create a [RetryConfig](aws_smithy_types::retry::RetryConfig) from following sources in order: + /// 1. [Environment variables](crate::environment::retry_config::EnvironmentVariableRetryConfigProvider) + /// 2. [Profile file](crate::profile::retry_config::ProfileFileRetryConfigProvider) + /// 3. [RetryConfig::default()](aws_smithy_types::retry::RetryConfig::default) + /// + /// Precedence is considered on a per-field basis + /// + /// # Panics + /// + /// - Panics if the `AWS_MAX_ATTEMPTS` env var or `max_attempts` profile var is set to 0 + /// - Panics if the `AWS_RETRY_MODE` env var or `retry_mode` profile var is set to "adaptive" (it's not yet supported) + pub async fn retry_config(self) -> RetryConfig { + // Both of these can return errors due to invalid config settings and we want to surface those as early as possible + // hence, we'll panic if any config values are invalid (missing values are OK though) + // We match this instead of unwrapping so we can print the error with the `Display` impl instead of the `Debug` impl that unwrap uses + let builder_from_env = match self.env_provider.retry_config_builder() { + Ok(retry_config_builder) => retry_config_builder, + Err(err) => panic!("{}", err), + }; + let builder_from_profile = match self.profile_file.build().retry_config_builder().await { + Ok(retry_config_builder) => retry_config_builder, + Err(err) => panic!("{}", err), + }; + + builder_from_env + .take_unset_from(builder_from_profile) + .build() + } +} diff --git a/aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs b/aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs new file mode 100644 index 0000000000..f19fab6ab4 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs @@ -0,0 +1,112 @@ +use aws_smithy_types::timeout::TimeoutConfig; + +use crate::environment::timeout_config::EnvironmentVariableTimeoutConfigProvider; +use crate::profile; +use crate::provider_config::ProviderConfig; + +/// Default [`TimeoutConfig`] Provider chain +/// +/// Unlike other credentials and region, [`TimeoutConfig`] has no related `TimeoutConfigProvider` trait. Instead, +/// a builder struct is returned which has a similar API. +/// +/// This provider will check the following sources in order: +/// 1. [Environment variables](EnvironmentVariableTimeoutConfigProvider) +/// 2. [Profile file](crate::profile::timeout_config::ProfileFileTimeoutConfigProvider) (`~/.aws/config`) +/// +/// # Example +/// +/// ```no_run +/// # use std::error::Error; +/// # #[tokio::main] +/// # async fn main() { +/// use aws_config::default_provider::timeout_config; +/// +/// // Load a timeout config from a specific profile +/// let timeout_config = timeout_config::default_provider() +/// .profile_name("other_profile") +/// .timeout_config() +/// .await; +/// let config = aws_config::from_env() +/// // Override the timeout config set by the default profile +/// .timeout_config(timeout_config) +/// .load() +/// .await; +/// // instantiate a service client: +/// // ::Client::new(&config); +/// # } +/// ``` +pub fn default_provider() -> Builder { + Builder::default() +} + +/// Builder for [`TimeoutConfig`] that checks the environment variables and AWS profile files for configuration +#[derive(Default)] +pub struct Builder { + env_provider: EnvironmentVariableTimeoutConfigProvider, + profile_file: profile::timeout_config::Builder, +} + +impl Builder { + /// Configure the default chain + /// + /// Exposed for overriding the environment when unit-testing providers + pub fn configure(mut self, configuration: &ProviderConfig) -> Self { + self.env_provider = + EnvironmentVariableTimeoutConfigProvider::new_with_env(configuration.env()); + self.profile_file = self.profile_file.configure(configuration); + self + } + + /// Override the profile name used by this provider + pub fn profile_name(mut self, name: &str) -> Self { + self.profile_file = self.profile_file.profile_name(name); + self + } + + /// Attempt to create a [`TimeoutConfig`](aws_smithy_types::timeout::TimeoutConfig) from following sources in order: + /// 1. [Environment variables](crate::environment::timeout_config::EnvironmentVariableTimeoutConfigProvider) + /// 2. [Profile file](crate::profile::timeout_config::ProfileFileTimeoutConfigProvider) + /// + /// Precedence is considered on a per-field basis. If no timeout is specified, requests will never time out. + /// + /// # Panics + /// + /// This will panic if: + /// - a timeout is set to `NaN`, a negative number, or infinity + /// - a timeout can't be parsed as a floating point number + pub async fn timeout_config(self) -> TimeoutConfig { + // Both of these can return errors due to invalid config settings and we want to surface those as early as possible + // hence, we'll panic if any config values are invalid (missing values are OK though) + // We match this instead of unwrapping so we can print the error with the `Display` impl instead of the `Debug` impl that unwrap uses + let builder_from_env = match self.env_provider.timeout_config() { + Ok(timeout_config_builder) => timeout_config_builder, + Err(err) => panic!("{}", err), + }; + let builder_from_profile = match self.profile_file.build().timeout_config().await { + Ok(timeout_config_builder) => timeout_config_builder, + Err(err) => panic!("{}", err), + }; + + let conf = builder_from_env.take_unset_from(builder_from_profile); + + if conf.tls_negotiation_timeout().is_some() { + tracing::warn!( + "A TLS negotiation timeout was set but that feature is currently unimplemented so the setting will be ignored. \ + To help us prioritize support for this feature, please upvote aws-sdk-rust#151 (https://github.com/awslabs/aws-sdk-rust/issues/151)") + } + + if conf.connect_timeout().is_some() { + tracing::warn!( + "A connect timeout was set but that feature is currently unimplemented so the setting will be ignored. \ + To help us prioritize support for this feature, please upvote aws-sdk-rust#151 (https://github.com/awslabs/aws-sdk-rust/issues/151)") + } + + if conf.read_timeout().is_some() { + tracing::warn!( + "A read timeout was set but that feature is currently unimplemented so the setting will be ignored. \ + To help us prioritize support for this feature, please upvote aws-sdk-rust#151 (https://github.com/awslabs/aws-sdk-rust/issues/151)") + } + + conf + } +} diff --git a/aws/rust-runtime/aws-config/src/ecs.rs b/aws/rust-runtime/aws-config/src/ecs.rs index 8a52b269ea..4b7f1f487a 100644 --- a/aws/rust-runtime/aws-config/src/ecs.rs +++ b/aws/rust-runtime/aws-config/src/ecs.rs @@ -59,7 +59,7 @@ use http::uri::{InvalidUri, Scheme}; use http::{HeaderValue, Uri}; use tower::{Service, ServiceExt}; -use crate::http_provider::HttpCredentialProvider; +use crate::http_credential_provider::HttpCredentialProvider; use crate::provider_config::ProviderConfig; use aws_types::os_shim_internal::Env; use http::header::InvalidHeaderValue; diff --git a/aws/rust-runtime/aws-config/src/environment/mod.rs b/aws/rust-runtime/aws-config/src/environment/mod.rs index f6cddbdb45..520a7b7191 100644 --- a/aws/rust-runtime/aws-config/src/environment/mod.rs +++ b/aws/rust-runtime/aws-config/src/environment/mod.rs @@ -6,6 +6,7 @@ /// Load app name from the environment pub mod app_name; pub use app_name::EnvironmentVariableAppNameProvider; + /// Load credentials from the environment pub mod credentials; pub use credentials::EnvironmentVariableCredentialsProvider; diff --git a/aws/rust-runtime/aws-config/src/http_provider.rs b/aws/rust-runtime/aws-config/src/http_credential_provider.rs similarity index 91% rename from aws/rust-runtime/aws-config/src/http_provider.rs rename to aws/rust-runtime/aws-config/src/http_credential_provider.rs index 121c16d297..fa172b2776 100644 --- a/aws/rust-runtime/aws-config/src/http_provider.rs +++ b/aws/rust-runtime/aws-config/src/http_credential_provider.rs @@ -8,21 +8,22 @@ //! //! Future work will stabilize this interface and enable it to be used directly. +use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::http_connector::HttpSettings; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation::{Operation, Request}; use aws_smithy_http::response::ParseStrictResponse; use aws_smithy_http::result::{SdkError, SdkSuccess}; use aws_smithy_http::retry::ClassifyResponse; use aws_smithy_types::retry::{ErrorKind, RetryKind}; +use aws_smithy_types::timeout::TimeoutConfig; use aws_types::credentials::CredentialsError; use aws_types::{credentials, Credentials}; -use crate::connector::expect_connector; +use crate::expect_connector; use crate::json_credentials::{parse_json_credentials, JsonCredentials}; -use crate::provider_config::{HttpSettings, ProviderConfig}; +use crate::provider_config::ProviderConfig; -use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::timeout; use bytes::Bytes; use http::header::{ACCEPT, AUTHORIZATION}; use http::{HeaderValue, Response, Uri}; @@ -78,8 +79,7 @@ impl HttpCredentialProvider { #[derive(Default)] pub(crate) struct Builder { provider_config: Option, - connect_timeout: Option, - read_timeout: Option, + timeout_config: TimeoutConfig, } impl Builder { @@ -90,25 +90,23 @@ impl Builder { // read_timeout and connect_timeout accept options to enable easy pass through from // other builders - pub(crate) fn read_timeout(mut self, read_timeout: Option) -> Self { - self.read_timeout = read_timeout; + self.timeout_config = self.timeout_config.with_read_timeout(read_timeout); self } pub(crate) fn connect_timeout(mut self, connect_timeout: Option) -> Self { - self.connect_timeout = connect_timeout; + self.timeout_config = self.timeout_config.with_connect_timeout(connect_timeout); self } pub(crate) fn build(self, provider_name: &'static str, uri: Uri) -> HttpCredentialProvider { let provider_config = self.provider_config.unwrap_or_default(); - let connect_timeout = self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT); - let read_timeout = self.read_timeout.unwrap_or(DEFAULT_READ_TIMEOUT); - let timeout_settings = timeout::Settings::default() - .with_read_timeout(read_timeout) - .with_connect_timeout(connect_timeout); - let http_settings = HttpSettings { timeout_settings }; + let default_timeout_config = TimeoutConfig::new() + .with_connect_timeout(Some(DEFAULT_CONNECT_TIMEOUT)) + .with_read_timeout(Some(DEFAULT_READ_TIMEOUT)); + let timeout_config = self.timeout_config.take_unset_from(default_timeout_config); + let http_settings = HttpSettings::default().with_timeout_config(timeout_config); let connector = expect_connector(provider_config.connector(&http_settings)); let client = aws_smithy_client::Builder::new() .connector(connector) @@ -201,7 +199,7 @@ impl ClassifyResponse, SdkError> #[cfg(test)] mod test { - use crate::http_provider::{CredentialsResponseParser, HttpCredentialRetryPolicy}; + use crate::http_credential_provider::{CredentialsResponseParser, HttpCredentialRetryPolicy}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation; use aws_smithy_http::response::ParseStrictResponse; diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index b496cb99f2..6dedbef549 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -15,7 +15,7 @@ use std::str::FromStr; use std::time::Duration; use aws_http::user_agent::{ApiMetadata, AwsUserAgent, UserAgentStage}; -use aws_smithy_client::{erase::DynConnector, timeout, SdkSuccess}; +use aws_smithy_client::{erase::DynConnector, SdkSuccess}; use aws_smithy_client::{retry, SdkError}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::endpoint::Endpoint; @@ -29,24 +29,26 @@ use aws_smithy_http_tower::map_request::{ use aws_smithy_types::retry::{ErrorKind, RetryKind}; use aws_smithy_types::timeout::TimeoutConfig; use aws_types::os_shim_internal::{Env, Fs}; + use bytes::Bytes; use http::uri::InvalidUri; use http::{Response, Uri}; +use tokio::sync::OnceCell; -use crate::connector::expect_connector; +use crate::expect_connector; use crate::imds::client::token::TokenMiddleware; use crate::profile::ProfileParseError; -use crate::provider_config::{HttpSettings, ProviderConfig}; +use crate::provider_config::ProviderConfig; use crate::{profile, PKG_VERSION}; -use tokio::sync::OnceCell; +use aws_smithy_client::http_connector::HttpSettings; mod token; // 6 hours const DEFAULT_TOKEN_TTL: Duration = Duration::from_secs(21_600); const DEFAULT_ATTEMPTS: u32 = 4; -const DEFAULT_CONNECT_TIMEOUT: Duration = Duration::from_secs(1); -const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(1); +const DEFAULT_CONNECT_TIMEOUT: Option = Some(Duration::from_secs(1)); +const DEFAULT_READ_TIMEOUT: Option = Some(Duration::from_secs(1)); fn user_agent() -> AwsUserAgent { AwsUserAgent::new_from_environment(Env::real(), ApiMetadata::new("imds", PKG_VERSION)) @@ -533,12 +535,11 @@ impl Builder { /// Build an IMDSv2 Client pub async fn build(self) -> Result { let config = self.config.unwrap_or_default(); - let timeout_config = timeout::Settings::default() - .with_connect_timeout(self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT)) - .with_read_timeout(self.read_timeout.unwrap_or(DEFAULT_READ_TIMEOUT)); - let connector = expect_connector(config.connector(&HttpSettings { - timeout_settings: timeout_config, - })); + let timeout_config = TimeoutConfig::new() + .with_connect_timeout(self.connect_timeout.or(DEFAULT_CONNECT_TIMEOUT)) + .with_read_timeout(self.read_timeout.or(DEFAULT_READ_TIMEOUT)); + let http_settings = HttpSettings::default().with_timeout_config(timeout_config); + let connector = expect_connector(config.connector(&http_settings)); let endpoint_source = self .endpoint .unwrap_or_else(|| EndpointSource::Env(config.env(), config.fs())); diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 6e9838e3c7..094bf9039b 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -77,7 +77,9 @@ pub mod imds; mod json_credentials; mod fs_util; -mod http_provider; + +mod http_credential_provider; + pub mod sso; // Re-export types from smithy-types @@ -115,6 +117,7 @@ mod loader { use std::sync::Arc; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; + use aws_smithy_client::http_connector::{HttpConnector, HttpSettings}; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; use aws_types::app_name::AppName; @@ -140,6 +143,7 @@ mod loader { sleep: Option>, timeout_config: Option, provider_config: Option, + http_connector: Option, } impl ConfigLoader { @@ -204,6 +208,12 @@ mod loader { self } + /// Override the [`HttpConnector`] used to build [`Config`](aws_types::config::Config). + pub fn http_connector(mut self, http_connector: HttpConnector) -> Self { + self.http_connector = Some(http_connector); + self + } + /// Override the credentials provider used to build [`Config`](aws_types::config::Config). /// /// # Examples @@ -315,6 +325,16 @@ mod loader { self.sleep }; + let http_connector: HttpConnector = if let Some(http_connector) = self.http_connector { + http_connector + } else { + let settings = HttpSettings::default().with_timeout_config(timeout_config.clone()); + let sleep_impl = sleep_impl.clone(); + HttpConnector::Prebuilt(aws_smithy_client::http_connector::default_connector( + &settings, sleep_impl, + )) + }; + let credentials_provider = if let Some(provider) = self.credentials_provider { provider } else { @@ -327,7 +347,8 @@ mod loader { .region(region) .retry_config(retry_config) .timeout_config(timeout_config) - .credentials_provider(credentials_provider); + .credentials_provider(credentials_provider) + .http_connector(http_connector); builder.set_app_name(app_name); builder.set_sleep_impl(sleep_impl); @@ -376,63 +397,10 @@ mod loader { } } -mod connector { - - // create a default connector given the currently enabled cargo features. - // rustls | native tls | result - // ----------------------------- - // yes | yes | rustls - // yes | no | rustls - // no | yes | native_tls - // no | no | no default - - use crate::provider_config::HttpSettings; - use aws_smithy_async::rt::sleep::AsyncSleep; - use aws_smithy_client::erase::DynConnector; - use std::sync::Arc; - - // unused when all crate features are disabled - #[allow(dead_code)] - pub(crate) fn expect_connector(connector: Option) -> DynConnector { - connector.expect("A connector was not available. Either set a custom connector or enable the `rustls` and `native-tls` crate features.") - } - - #[cfg(any(feature = "rustls", feature = "native-tls"))] - fn base( - settings: &HttpSettings, - sleep: Option>, - ) -> aws_smithy_client::hyper_ext::Builder { - let mut hyper = - aws_smithy_client::hyper_ext::Adapter::builder().timeout(&settings.timeout_settings); - if let Some(sleep) = sleep { - hyper = hyper.sleep_impl(sleep); - } - hyper - } - - #[cfg(feature = "rustls")] - pub(crate) fn default_connector( - settings: &HttpSettings, - sleep: Option>, - ) -> Option { - let hyper = base(settings, sleep).build(aws_smithy_client::conns::https()); - Some(DynConnector::new(hyper)) - } - - #[cfg(all(not(feature = "rustls"), feature = "native-tls"))] - pub(crate) fn default_connector( - settings: &HttpSettings, - sleep: Option>, - ) -> Option { - let hyper = base(settings, sleep).build(aws_smithy_client::conns::native_tls()); - Some(DynConnector::new(hyper)) - } - - #[cfg(not(any(feature = "rustls", feature = "native-tls")))] - pub(crate) fn default_connector( - _settings: &HttpSettings, - _sleep: Option>, - ) -> Option { - None - } +// unused when all crate features are disabled +/// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` +pub fn expect_connector( + connector: Option, +) -> aws_smithy_client::erase::DynConnector { + connector.expect("A connector was not available. Either set a custom connector or enable the `rustls` and `native-tls` crate features.") } diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index 80a19d0d37..7f0a56e32c 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -5,14 +5,15 @@ //! Configuration Options for Credential Providers -use crate::connector::default_connector; -use std::error::Error; - use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::timeout; use aws_types::os_shim_internal::{Env, Fs, TimeSource}; -use aws_types::region::Region; +use aws_types::{ + http_connector::{HttpConnector, HttpSettings}, + region::Region, +}; + +use std::error::Error; use std::fmt::{Debug, Formatter}; use std::sync::Arc; @@ -38,38 +39,6 @@ pub struct ProviderConfig { region: Option, } -pub(crate) type MakeConnectorFn = - dyn Fn(&HttpSettings, Option>) -> Option + Send + Sync; - -#[derive(Clone)] -pub(crate) enum HttpConnector { - Prebuilt(Option), - ConnectorFn(Arc), -} - -impl Default for HttpConnector { - fn default() -> Self { - Self::ConnectorFn(Arc::new( - |settings: &HttpSettings, sleep: Option>| { - default_connector(settings, sleep) - }, - )) - } -} - -impl HttpConnector { - fn make_connector( - &self, - settings: &HttpSettings, - sleep: Option>, - ) -> Option { - match self { - HttpConnector::Prebuilt(conn) => conn.clone(), - HttpConnector::ConnectorFn(func) => func(settings, sleep), - } - } -} - impl Debug for ProviderConfig { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("ProviderConfig") @@ -115,18 +84,6 @@ impl ProviderConfig { } } -/// HttpSettings for HTTP connectors -/// -/// # Stability -/// As HTTP settings stabilize, they will move to `aws-types::config::Config` so that they -/// can be used to configure HTTP connectors for service clients. -#[non_exhaustive] -#[derive(Default)] -pub(crate) struct HttpSettings { - #[allow(dead_code)] // Always set, but only referenced in certain feature configurations - pub(crate) timeout_settings: timeout::Settings, -} - impl ProviderConfig { /// Create a default provider config with the region unset. /// @@ -201,12 +158,12 @@ impl ProviderConfig { #[allow(dead_code)] pub(crate) fn default_connector(&self) -> Option { self.connector - .make_connector(&HttpSettings::default(), self.sleep.clone()) + .connector(&HttpSettings::default(), self.sleep.clone()) } #[allow(dead_code)] pub(crate) fn connector(&self, settings: &HttpSettings) -> Option { - self.connector.make_connector(settings, self.sleep.clone()) + self.connector.connector(settings, self.sleep.clone()) } #[allow(dead_code)] @@ -284,8 +241,8 @@ impl ProviderConfig { C::Error: Into>, { let connector_fn = move |settings: &HttpSettings, sleep: Option>| { - let mut builder = aws_smithy_client::hyper_ext::Adapter::builder() - .timeout(&settings.timeout_settings); + let mut builder = + aws_smithy_client::hyper_ext::Adapter::builder().timeout(&settings.timeout_config); if let Some(sleep) = sleep { builder = builder.sleep_impl(sleep); }; diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index 1ca99c554c..a9108f15b9 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -13,30 +13,32 @@ use crate::fs_util::{home_dir, Os}; use crate::json_credentials::{json_parse_loop, InvalidJsonCredentials}; use crate::provider_config::ProviderConfig; + use aws_sdk_sso::middleware::DefaultMiddleware as SsoMiddleware; use aws_sdk_sso::model::RoleCredentials; use aws_smithy_client::erase::DynConnector; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; - use aws_types::credentials::{CredentialsError, ProvideCredentials}; use aws_types::os_shim_internal::{Env, Fs}; use aws_types::region::Region; use aws_types::{credentials, Credentials}; -use ring::digest; + use std::convert::TryInto; use std::error::Error; use std::fmt::{Display, Formatter}; use std::io; use std::path::PathBuf; + +use ring::digest; use zeroize::Zeroizing; impl crate::provider_config::ProviderConfig { pub(crate) fn sso_client( &self, ) -> aws_smithy_client::Client { - use crate::connector::expect_connector; - use crate::provider_config::HttpSettings; + use crate::expect_connector; + use aws_smithy_client::http_connector::HttpSettings; aws_smithy_client::Builder::<(), SsoMiddleware>::new() .connector(expect_connector(self.connector(&HttpSettings::default()))) diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index 5f973d23ab..fe29a6c12c 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -7,6 +7,8 @@ mod assume_role; +pub(crate) mod util; + pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder}; use aws_sdk_sts::middleware::DefaultMiddleware; @@ -15,8 +17,8 @@ impl crate::provider_config::ProviderConfig { pub(crate) fn sts_client( &self, ) -> aws_smithy_client::Client { - use crate::connector::expect_connector; - use crate::provider_config::HttpSettings; + use crate::expect_connector; + use aws_smithy_client::http_connector::HttpSettings; aws_smithy_client::Builder::<(), DefaultMiddleware>::new() .connector(expect_connector(self.connector(&HttpSettings::default()))) @@ -24,53 +26,3 @@ impl crate::provider_config::ProviderConfig { .build() } } - -pub(crate) mod util { - use aws_sdk_sts::model::Credentials as StsCredentials; - use aws_types::credentials::{self, CredentialsError}; - use aws_types::Credentials as AwsCredentials; - use std::convert::TryFrom; - use std::time::{SystemTime, UNIX_EPOCH}; - - /// Convert STS credentials to aws_auth::Credentials - pub(crate) fn into_credentials( - sts_credentials: Option, - provider_name: &'static str, - ) -> credentials::Result { - let sts_credentials = sts_credentials - .ok_or_else(|| CredentialsError::unhandled("STS credentials must be defined"))?; - let expiration = SystemTime::try_from( - sts_credentials - .expiration - .ok_or_else(|| CredentialsError::unhandled("missing expiration"))?, - ) - .map_err(|_| { - CredentialsError::unhandled( - "credential expiration time cannot be represented by a SystemTime", - ) - })?; - Ok(AwsCredentials::new( - sts_credentials - .access_key_id - .ok_or_else(|| CredentialsError::unhandled("access key id missing from result"))?, - sts_credentials - .secret_access_key - .ok_or_else(|| CredentialsError::unhandled("secret access token missing"))?, - sts_credentials.session_token, - Some(expiration), - provider_name, - )) - } - - /// Create a default STS session name - /// - /// STS Assume Role providers MUST assign a name to their generated session. When a user does not - /// provide a name for the session, the provider will choose a name composed of a base + a timestamp, - /// e.g. `profile-file-provider-123456789` - pub(crate) fn default_session_name(base: &str) -> String { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("post epoch"); - format!("{}-{}", base, now.as_millis()) - } -} diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index c65930ff36..108c00c074 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -8,15 +8,15 @@ use aws_sdk_sts::error::AssumeRoleErrorKind; use aws_sdk_sts::middleware::DefaultMiddleware; use aws_sdk_sts::operation::AssumeRole; +use aws_smithy_async::rt::sleep::default_async_sleep; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::http_connector::HttpSettings; +use aws_smithy_http::result::SdkError; use aws_types::credentials::{ self, future, CredentialsError, ProvideCredentials, SharedCredentialsProvider, }; use aws_types::region::Region; -use crate::provider_config::HttpSettings; -use aws_smithy_async::rt::sleep::default_async_sleep; -use aws_smithy_client::erase::DynConnector; -use aws_smithy_http::result::SdkError; use tracing::Instrument; /// Credentials provider that uses credentials provided by another provider to assume a role @@ -134,7 +134,7 @@ impl AssumeRoleProviderBuilder { .build(); let conn = self.connection.unwrap_or_else(|| { - crate::connector::expect_connector(crate::connector::default_connector( + crate::expect_connector(aws_smithy_client::http_connector::default_connector( &HttpSettings::default(), default_async_sleep(), )) diff --git a/aws/rust-runtime/aws-config/src/sts/util.rs b/aws/rust-runtime/aws-config/src/sts/util.rs new file mode 100644 index 0000000000..68d6ec844b --- /dev/null +++ b/aws/rust-runtime/aws-config/src/sts/util.rs @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use aws_sdk_sts::model::Credentials as StsCredentials; +use aws_types::credentials::{self, CredentialsError}; +use aws_types::Credentials as AwsCredentials; + +use std::convert::TryFrom; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Convert STS credentials to aws_auth::Credentials +pub(crate) fn into_credentials( + sts_credentials: Option, + provider_name: &'static str, +) -> credentials::Result { + let sts_credentials = sts_credentials + .ok_or_else(|| CredentialsError::unhandled("STS credentials must be defined"))?; + let expiration = SystemTime::try_from( + sts_credentials + .expiration + .ok_or_else(|| CredentialsError::unhandled("missing expiration"))?, + ) + .map_err(|_| { + CredentialsError::unhandled( + "credential expiration time cannot be represented by a SystemTime", + ) + })?; + Ok(AwsCredentials::new( + sts_credentials + .access_key_id + .ok_or_else(|| CredentialsError::unhandled("access key id missing from result"))?, + sts_credentials + .secret_access_key + .ok_or_else(|| CredentialsError::unhandled("secret access token missing"))?, + sts_credentials.session_token, + Some(expiration), + provider_name, + )) +} + +/// Create a default STS session name +/// +/// STS Assume Role providers MUST assign a name to their generated session. When a user does not +/// provide a name for the session, the provider will choose a name composed of a base + a timestamp, +/// e.g. `profile-file-provider-123456789` +pub(crate) fn default_session_name(base: &str) -> String { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("post epoch"); + format!("{}-{}", base, now.as_millis()) +} diff --git a/aws/rust-runtime/aws-config/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs index a68bb8e764..402a9386ad 100644 --- a/aws/rust-runtime/aws-config/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -3,13 +3,17 @@ * SPDX-License-Identifier: Apache-2.0. */ -use crate::provider_config::{HttpSettings, ProviderConfig}; +use crate::provider_config::ProviderConfig; + use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep, TokioSleep}; use aws_smithy_client::dvr::{NetworkTraffic, RecordingConnection, ReplayingConnection}; use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::http_connector::HttpSettings; use aws_types::credentials::{self, ProvideCredentials}; use aws_types::os_shim_internal::{Env, Fs}; + use serde::Deserialize; + use std::collections::HashMap; use std::error::Error; use std::fmt::Debug; @@ -178,7 +182,8 @@ impl TestEnvironment { let settings = HttpSettings::default(); let (_test_connector, config) = self.provider_config().await; let live_connector = - crate::connector::default_connector(&settings, config.sleep()).unwrap(); + aws_smithy_client::http_connector::default_connector(&settings, config.sleep()) + .unwrap(); let live_connector = RecordingConnection::new(live_connector); let config = config.with_http_connector(DynConnector::new(live_connector.clone())); let provider = make_provider(config).await; diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 36b71ecab9..642d567dcd 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -13,6 +13,7 @@ hardcoded-credentials = [] [dependencies] aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } +aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } tracing = "0.1" zeroize = "1.4.1" diff --git a/aws/rust-runtime/aws-types/src/config.rs b/aws/rust-runtime/aws-types/src/config.rs index 68d6743c60..c5c6494a80 100644 --- a/aws/rust-runtime/aws-types/src/config.rs +++ b/aws/rust-runtime/aws-types/src/config.rs @@ -12,6 +12,7 @@ use std::sync::Arc; use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -28,6 +29,7 @@ pub struct Config { retry_config: Option, sleep_impl: Option>, timeout_config: Option, + http_connector: Option, } /// Builder for AWS Shared Configuration @@ -39,6 +41,7 @@ pub struct Builder { retry_config: Option, sleep_impl: Option>, timeout_config: Option, + http_connector: Option, } impl Builder { @@ -283,6 +286,24 @@ impl Builder { self } + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn http_connector(mut self, http_connector: HttpConnector) -> Self { + self.set_http_connector(Some(http_connector)); + self + } + + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn set_http_connector(&mut self, http_connector: Option) -> &mut Self { + self.http_connector = http_connector; + self + } + /// Build a [`Config`](Config) from this builder pub fn build(self) -> Config { Config { @@ -292,6 +313,7 @@ impl Builder { retry_config: self.retry_config, sleep_impl: self.sleep_impl, timeout_config: self.timeout_config, + http_connector: self.http_connector, } } } diff --git a/aws/rust-runtime/aws-types/src/lib.rs b/aws/rust-runtime/aws-types/src/lib.rs index a35849fd6b..896fc15f64 100644 --- a/aws/rust-runtime/aws-types/src/lib.rs +++ b/aws/rust-runtime/aws-types/src/lib.rs @@ -21,6 +21,7 @@ pub mod credentials; pub mod os_shim_internal; pub mod region; +pub use aws_smithy_client::http_connector; pub use credentials::Credentials; use std::borrow::Cow; diff --git a/rust-runtime/aws-smithy-client/src/http_connector.rs b/rust-runtime/aws-smithy-client/src/http_connector.rs new file mode 100644 index 0000000000..6fa627b751 --- /dev/null +++ b/rust-runtime/aws-smithy-client/src/http_connector.rs @@ -0,0 +1,115 @@ +//! Default connectors based on what TLS features are active. Also contains HTTP-related abstractions +//! that enable passing HTTP connectors around. + +use crate::erase::DynConnector; +use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_types::timeout::TimeoutConfig; +use std::{fmt::Debug, sync::Arc}; + +#[cfg(any(feature = "rustls", feature = "native-tls"))] +fn base(settings: &HttpSettings, sleep: Option>) -> crate::hyper_ext::Builder { + let mut hyper = crate::hyper_ext::Adapter::builder().timeout(&settings.timeout_config); + if let Some(sleep) = sleep { + hyper = hyper.sleep_impl(sleep); + } + hyper +} + +/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. +#[cfg(feature = "rustls")] +pub fn default_connector( + settings: &HttpSettings, + sleep: Option>, +) -> Option { + let hyper = base(settings, sleep).build(crate::conns::https()); + Some(DynConnector::new(hyper)) +} + +/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. +#[cfg(all(not(feature = "rustls"), feature = "native-tls"))] +pub fn default_connector( + settings: &HttpSettings, + sleep: Option>, +) -> Option { + let hyper = base(settings, sleep).build(crate::conns::native_tls()); + Some(DynConnector::new(hyper)) +} + +/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. +#[cfg(not(any(feature = "rustls", feature = "native-tls")))] +pub fn default_connector( + _settings: &HttpSettings, + _sleep: Option>, +) -> Option { + None +} + +/// Type alias for a Connector factory function. +pub type MakeConnectorFn = + dyn Fn(&HttpSettings, Option>) -> Option + Send + Sync; + +/// Enum for describing the two "kinds" of HTTP Connectors in smithy-rs. +#[derive(Clone)] +pub enum HttpConnector { + /// A `DynConnector` to be used for all requests. + Prebuilt(Option), + /// A factory function that will be used to create new `DynConnector`s whenever one is needed. + ConnectorFn(Arc), +} + +impl Default for HttpConnector { + fn default() -> Self { + Self::ConnectorFn(Arc::new( + |settings: &HttpSettings, sleep: Option>| { + default_connector(settings, sleep) + }, + )) + } +} + +impl Debug for HttpConnector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Prebuilt(Some(connector)) => { + write!(f, "Prebuilt({:?})", connector) + } + Self::Prebuilt(None) => { + write!(f, "Prebuilt(None)") + } + Self::ConnectorFn(_) => { + write!(f, "ConnectorFn()") + } + } + } +} + +impl HttpConnector { + /// If `HttpConnector` is `Prebuilt`, return a clone of that connector. + /// If `HttpConnector` is `ConnectorFn`, generate a new connector from settings and return it. + pub fn connector( + &self, + settings: &HttpSettings, + sleep: Option>, + ) -> Option { + match self { + HttpConnector::Prebuilt(conn) => conn.clone(), + HttpConnector::ConnectorFn(func) => func(settings, sleep), + } + } +} + +/// HttpSettings for HTTP Connectors +#[non_exhaustive] +#[derive(Default, Debug)] +pub struct HttpSettings { + /// Timeout configuration used when sending out requests + pub timeout_config: TimeoutConfig, +} + +impl HttpSettings { + /// Set the Timeout Config to be used when making HTTP requests + pub fn with_timeout_config(mut self, timeout_config: TimeoutConfig) -> Self { + self.timeout_config = timeout_config; + self + } +} diff --git a/rust-runtime/aws-smithy-client/src/hyper_ext.rs b/rust-runtime/aws-smithy-client/src/hyper_ext.rs index 6a829cc5f2..6d0bb6c4e9 100644 --- a/rust-runtime/aws-smithy-client/src/hyper_ext.rs +++ b/rust-runtime/aws-smithy-client/src/hyper_ext.rs @@ -2,6 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ + //! Implementation of [`SmithyConnector`](crate::bounds::SmithyConnector) for Hyper //! //! The module provides [`Adapter`] which enables using a [`hyper::Client`] as the connector for a Smithy @@ -28,9 +29,9 @@ //! use std::time::Duration; //! use aws_smithy_client::{Client, conns, hyper_ext}; //! use aws_smithy_client::erase::DynConnector; -//! use aws_smithy_client::timeout::Settings; +//! use aws_smithy_types::timeout::TimeoutConfig; //! -//! let timeout = Settings::new().with_connect_timeout(Duration::from_secs(1)); +//! let timeout = TimeoutConfig::new().with_connect_timeout(Some(Duration::from_secs(1))); //! let connector = hyper_ext::Adapter::builder().timeout(&timeout).build(conns::https()); //! // Replace this with your middleware //! type MyMiddleware = tower::layer::util::Identity; @@ -51,10 +52,11 @@ use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_types::retry::ErrorKind; +use aws_smithy_types::timeout::TimeoutConfig; use crate::erase::DynConnector; use crate::never::stream::EmptyStream; -use crate::{timeout, Builder as ClientBuilder}; +use crate::Builder as ClientBuilder; use self::timeout_middleware::{ConnectTimeout, HttpReadTimeout, HttpTimeoutError}; @@ -179,7 +181,7 @@ fn find_source<'a, E: Error + 'static>(err: &'a (dyn Error + 'static)) -> Option /// let client = aws_smithy_client::Client::::new(DynConnector::new(hyper_connector)); /// ``` pub struct Builder { - timeout: timeout::Settings, + timeout_config: TimeoutConfig, sleep: Option>, client_builder: hyper::client::Builder, } @@ -196,7 +198,7 @@ impl Builder { { // if we are using Hyper, Tokio must already be enabled so we can fallback to Tokio. let sleep = self.sleep.or_else(default_async_sleep); - let connector = match self.timeout.connect() { + let connector = match self.timeout_config.connect_timeout() { Some(duration) => ConnectTimeout::new( connector, sleep @@ -207,7 +209,7 @@ impl Builder { None => ConnectTimeout::no_timeout(connector), }; let base = self.client_builder.build(connector); - let http_timeout = match self.timeout.read() { + let http_timeout = match self.timeout_config.read_timeout() { Some(duration) => HttpReadTimeout::new( base, sleep @@ -234,9 +236,9 @@ impl Builder { /// Configure the timeout for the HyperAdapter /// /// When unset, the underlying adaptor will not use any timeouts. - pub fn timeout(self, timeout_config: &timeout::Settings) -> Self { + pub fn timeout(self, timeout_config: &TimeoutConfig) -> Self { Self { - timeout: timeout_config.clone(), + timeout_config: timeout_config.clone(), ..self } } @@ -534,7 +536,7 @@ mod timeout_middleware { use crate::hyper_ext::Adapter; use crate::never::{NeverConnected, NeverReplies}; - use crate::timeout; + use crate::TimeoutConfig; #[allow(unused)] fn connect_timeout_is_correct() { @@ -547,7 +549,7 @@ mod timeout_middleware { #[tokio::test] async fn http_connect_timeout_works() { let inner = NeverConnected::new(); - let timeout = timeout::Settings::new().with_connect_timeout(Duration::from_secs(1)); + let timeout = TimeoutConfig::new().with_connect_timeout(Some(Duration::from_secs(1))); let mut hyper = Adapter::builder() .timeout(&timeout) .sleep_impl(TokioSleep::new()) @@ -578,9 +580,9 @@ mod timeout_middleware { #[tokio::test] async fn http_read_timeout_works() { let inner = NeverReplies::new(); - let timeout = timeout::Settings::new() - .with_connect_timeout(Duration::from_secs(1)) - .with_read_timeout(Duration::from_secs(2)); + let timeout = TimeoutConfig::new() + .with_connect_timeout(Some(Duration::from_secs(1))) + .with_read_timeout(Some(Duration::from_secs(2))); let mut hyper = Adapter::builder() .timeout(&timeout) .sleep_impl(TokioSleep::new()) diff --git a/rust-runtime/aws-smithy-client/src/lib.rs b/rust-runtime/aws-smithy-client/src/lib.rs index 1cee2c6597..a1f7d12397 100644 --- a/rust-runtime/aws-smithy-client/src/lib.rs +++ b/rust-runtime/aws-smithy-client/src/lib.rs @@ -35,6 +35,8 @@ pub mod dvr; #[cfg(feature = "test-util")] pub mod test_connection; +pub mod http_connector; + #[cfg(feature = "client-hyper")] pub mod hyper_ext; @@ -162,13 +164,13 @@ impl Client { impl Client { /// Set the client's timeout configuration. - pub fn set_timeout_config(&mut self, config: TimeoutConfig) { - self.timeout_config = config; + pub fn set_timeout_config(&mut self, timeout_config: TimeoutConfig) { + self.timeout_config = timeout_config; } /// Set the client's timeout configuration. - pub fn with_timeout_config(mut self, config: TimeoutConfig) -> Self { - self.set_timeout_config(config); + pub fn with_timeout_config(mut self, timeout_config: TimeoutConfig) -> Self { + self.set_timeout_config(timeout_config); self } From a8c6fb441e46b9b2c32322eb7b1873491fa201dc Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 2 Feb 2022 11:05:07 -0600 Subject: [PATCH 2/8] add: missing copyright headers --- aws/rust-runtime/aws-config/src/default_provider/app_name.rs | 5 +++++ .../aws-config/src/default_provider/credentials.rs | 5 +++++ aws/rust-runtime/aws-config/src/default_provider/region.rs | 5 +++++ .../aws-config/src/default_provider/retry_config.rs | 5 +++++ .../aws-config/src/default_provider/timeout_config.rs | 5 +++++ rust-runtime/aws-smithy-client/src/http_connector.rs | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs index fb5a375093..2909b170a5 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + use crate::environment::app_name::EnvironmentVariableAppNameProvider; use crate::profile::app_name; use crate::provider_config::ProviderConfig; diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index 46598522f1..c8b11811aa 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + use aws_types::credentials; use std::borrow::Cow; diff --git a/aws/rust-runtime/aws-config/src/default_provider/region.rs b/aws/rust-runtime/aws-config/src/default_provider/region.rs index bd0849e70b..0da5d39368 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/region.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/region.rs @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + use aws_types::region::Region; use crate::environment::region::EnvironmentVariableRegionProvider; diff --git a/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs b/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs index 2da79dfd73..d71bb891c8 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + use aws_smithy_types::retry::RetryConfig; use crate::environment::retry_config::EnvironmentVariableRetryConfigProvider; diff --git a/aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs b/aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs index f19fab6ab4..2ac370e099 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/timeout_config.rs @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + use aws_smithy_types::timeout::TimeoutConfig; use crate::environment::timeout_config::EnvironmentVariableTimeoutConfigProvider; diff --git a/rust-runtime/aws-smithy-client/src/http_connector.rs b/rust-runtime/aws-smithy-client/src/http_connector.rs index 6fa627b751..853936991d 100644 --- a/rust-runtime/aws-smithy-client/src/http_connector.rs +++ b/rust-runtime/aws-smithy-client/src/http_connector.rs @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + //! Default connectors based on what TLS features are active. Also contains HTTP-related abstractions //! that enable passing HTTP connectors around. From 737d3e4438d0c8ef95c1c436d1f13e9dcc6af7b4 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 2 Feb 2022 12:40:17 -0600 Subject: [PATCH 3/8] add: missing http_connector accessor to Config --- aws/rust-runtime/aws-types/src/config.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/aws/rust-runtime/aws-types/src/config.rs b/aws/rust-runtime/aws-types/src/config.rs index c5c6494a80..dc54e0bbd3 100644 --- a/aws/rust-runtime/aws-types/src/config.rs +++ b/aws/rust-runtime/aws-types/src/config.rs @@ -12,6 +12,7 @@ use std::sync::Arc; use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_client::erase::DynConnector; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -286,19 +287,13 @@ impl Builder { self } - /// Sets the name of the app that is using the client. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. + /// Sets the HTTP connector that clients will use to make HTTP requests. pub fn http_connector(mut self, http_connector: HttpConnector) -> Self { self.set_http_connector(Some(http_connector)); self } - /// Sets the name of the app that is using the client. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. + /// Sets the HTTP connector that clients will use to make HTTP requests. pub fn set_http_connector(&mut self, http_connector: Option) -> &mut Self { self.http_connector = http_connector; self @@ -350,6 +345,11 @@ impl Config { self.app_name.as_ref() } + /// Configured HTTP Connector + pub fn http_connector(&self) -> Option<&HttpConnector> { + self.http_connector.as_ref() + } + /// Config builder pub fn builder() -> Builder { Builder::default() From c902645a756dcd5f74a4365ad2bf646a1715e3e2 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 2 Feb 2022 12:48:15 -0600 Subject: [PATCH 4/8] remove: unused import --- aws/rust-runtime/aws-types/src/config.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/rust-runtime/aws-types/src/config.rs b/aws/rust-runtime/aws-types/src/config.rs index dc54e0bbd3..c2cf0d1b27 100644 --- a/aws/rust-runtime/aws-types/src/config.rs +++ b/aws/rust-runtime/aws-types/src/config.rs @@ -12,7 +12,6 @@ use std::sync::Arc; use aws_smithy_async::rt::sleep::AsyncSleep; -use aws_smithy_client::erase::DynConnector; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; From 94506d487d1e62d7ad1491f2dbce38eeb360355c Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Thu, 3 Feb 2022 16:07:54 -0600 Subject: [PATCH 5/8] update: undo changes to aws_config::connector module remove: impl Default for HttpConnector --- aws/rust-runtime/aws-config/src/connector.rs | 60 +++++++++++++++++++ .../src/http_credential_provider.rs | 2 +- .../aws-config/src/imds/client.rs | 2 +- aws/rust-runtime/aws-config/src/lib.rs | 15 ++--- .../aws-config/src/provider_config.rs | 9 ++- aws/rust-runtime/aws-config/src/sso.rs | 2 +- aws/rust-runtime/aws-config/src/sts.rs | 13 ++-- .../aws-config/src/sts/assume_role.rs | 3 +- aws/rust-runtime/aws-config/src/test_case.rs | 5 +- .../aws-smithy-client/src/http_connector.rs | 48 --------------- 10 files changed, 85 insertions(+), 74 deletions(-) create mode 100644 aws/rust-runtime/aws-config/src/connector.rs diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs new file mode 100644 index 0000000000..f002c32b90 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +//! Functionality related to creating new HTTP Connectors + +use std::sync::Arc; + +use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_client::conns; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::http_connector::HttpSettings; +use aws_smithy_client::hyper_ext; + +// unused when all crate features are disabled +/// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` +pub(crate) fn expect_connector( + connector: Option, +) -> aws_smithy_client::erase::DynConnector { + connector.expect("A connector was not available. Either set a custom connector or enable the `rustls` and `native-tls` crate features.") +} + +#[cfg(any(feature = "rustls", feature = "native-tls"))] +fn base(settings: &HttpSettings, sleep: Option>) -> hyper_ext::Builder { + let mut hyper = hyper_ext::Adapter::builder().timeout(&settings.timeout_config); + if let Some(sleep) = sleep { + hyper = hyper.sleep_impl(sleep); + } + hyper +} + +/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. +#[cfg(feature = "rustls")] +pub fn default_connector( + settings: &HttpSettings, + sleep: Option>, +) -> Option { + let hyper = base(settings, sleep).build(conns::https()); + Some(DynConnector::new(hyper)) +} + +/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. +#[cfg(all(not(feature = "rustls"), feature = "native-tls"))] +pub fn default_connector( + settings: &HttpSettings, + sleep: Option>, +) -> Option { + let hyper = base(settings, sleep).build(conns::native_tls()); + Some(DynConnector::new(hyper)) +} + +/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. +#[cfg(not(any(feature = "rustls", feature = "native-tls")))] +pub fn default_connector( + _settings: &HttpSettings, + _sleep: Option>, +) -> Option { + None +} diff --git a/aws/rust-runtime/aws-config/src/http_credential_provider.rs b/aws/rust-runtime/aws-config/src/http_credential_provider.rs index fa172b2776..ae152fe4e9 100644 --- a/aws/rust-runtime/aws-config/src/http_credential_provider.rs +++ b/aws/rust-runtime/aws-config/src/http_credential_provider.rs @@ -20,7 +20,7 @@ use aws_smithy_types::timeout::TimeoutConfig; use aws_types::credentials::CredentialsError; use aws_types::{credentials, Credentials}; -use crate::expect_connector; +use crate::connector::expect_connector; use crate::json_credentials::{parse_json_credentials, JsonCredentials}; use crate::provider_config::ProviderConfig; diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index 6dedbef549..6f52aa66b0 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -35,7 +35,7 @@ use http::uri::InvalidUri; use http::{Response, Uri}; use tokio::sync::OnceCell; -use crate::expect_connector; +use crate::connector::expect_connector; use crate::imds::client::token::TokenMiddleware; use crate::profile::ProfileParseError; use crate::provider_config::ProviderConfig; diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 094bf9039b..1f7bd87225 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -82,6 +82,8 @@ mod http_credential_provider; pub mod sso; +pub mod connector; + // Re-export types from smithy-types pub use aws_smithy_types::retry::RetryConfig; pub use aws_smithy_types::timeout::TimeoutConfig; @@ -116,6 +118,7 @@ pub use loader::ConfigLoader; mod loader { use std::sync::Arc; + use crate::connector::default_connector; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; use aws_smithy_client::http_connector::{HttpConnector, HttpSettings}; use aws_smithy_types::retry::RetryConfig; @@ -330,9 +333,7 @@ mod loader { } else { let settings = HttpSettings::default().with_timeout_config(timeout_config.clone()); let sleep_impl = sleep_impl.clone(); - HttpConnector::Prebuilt(aws_smithy_client::http_connector::default_connector( - &settings, sleep_impl, - )) + HttpConnector::Prebuilt(default_connector(&settings, sleep_impl)) }; let credentials_provider = if let Some(provider) = self.credentials_provider { @@ -396,11 +397,3 @@ mod loader { } } } - -// unused when all crate features are disabled -/// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` -pub fn expect_connector( - connector: Option, -) -> aws_smithy_client::erase::DynConnector { - connector.expect("A connector was not available. Either set a custom connector or enable the `rustls` and `native-tls` crate features.") -} diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index 7f0a56e32c..36b618809e 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -17,6 +17,7 @@ use std::error::Error; use std::fmt::{Debug, Formatter}; use std::sync::Arc; +use crate::connector::default_connector; use http::Uri; use hyper::client::connect::Connection; use tokio::io::{AsyncRead, AsyncWrite}; @@ -52,11 +53,17 @@ impl Debug for ProviderConfig { impl Default for ProviderConfig { fn default() -> Self { + let connector = HttpConnector::ConnectorFn(Arc::new( + |settings: &HttpSettings, sleep: Option>| { + default_connector(settings, sleep) + }, + )); + Self { env: Env::default(), fs: Fs::default(), time_source: TimeSource::default(), - connector: HttpConnector::default(), + connector, sleep: default_async_sleep(), region: None, } diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index a9108f15b9..434173d836 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -37,7 +37,7 @@ impl crate::provider_config::ProviderConfig { pub(crate) fn sso_client( &self, ) -> aws_smithy_client::Client { - use crate::expect_connector; + use crate::connector::expect_connector; use aws_smithy_client::http_connector::HttpSettings; aws_smithy_client::Builder::<(), SsoMiddleware>::new() diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index fe29a6c12c..8d53cbfcf4 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -9,18 +9,17 @@ mod assume_role; pub(crate) mod util; +use crate::connector::expect_connector; pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder}; use aws_sdk_sts::middleware::DefaultMiddleware; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::http_connector::HttpSettings; +use aws_smithy_client::{Builder, Client}; impl crate::provider_config::ProviderConfig { - pub(crate) fn sts_client( - &self, - ) -> aws_smithy_client::Client { - use crate::expect_connector; - use aws_smithy_client::http_connector::HttpSettings; - - aws_smithy_client::Builder::<(), DefaultMiddleware>::new() + pub(crate) fn sts_client(&self) -> Client { + Builder::<(), DefaultMiddleware>::new() .connector(expect_connector(self.connector(&HttpSettings::default()))) .sleep_impl(self.sleep()) .build() diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 108c00c074..c64fb5d190 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -17,6 +17,7 @@ use aws_types::credentials::{ }; use aws_types::region::Region; +use crate::connector::{default_connector, expect_connector}; use tracing::Instrument; /// Credentials provider that uses credentials provided by another provider to assume a role @@ -134,7 +135,7 @@ impl AssumeRoleProviderBuilder { .build(); let conn = self.connection.unwrap_or_else(|| { - crate::expect_connector(aws_smithy_client::http_connector::default_connector( + expect_connector(default_connector( &HttpSettings::default(), default_async_sleep(), )) diff --git a/aws/rust-runtime/aws-config/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs index 402a9386ad..4d4988b7de 100644 --- a/aws/rust-runtime/aws-config/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -14,6 +14,7 @@ use aws_types::os_shim_internal::{Env, Fs}; use serde::Deserialize; +use crate::connector::default_connector; use std::collections::HashMap; use std::error::Error; use std::fmt::Debug; @@ -181,9 +182,7 @@ impl TestEnvironment { // swap out the connector generated from `http-traffic.json` for a real connector: let settings = HttpSettings::default(); let (_test_connector, config) = self.provider_config().await; - let live_connector = - aws_smithy_client::http_connector::default_connector(&settings, config.sleep()) - .unwrap(); + let live_connector = default_connector(&settings, config.sleep()).unwrap(); let live_connector = RecordingConnection::new(live_connector); let config = config.with_http_connector(DynConnector::new(live_connector.clone())); let provider = make_provider(config).await; diff --git a/rust-runtime/aws-smithy-client/src/http_connector.rs b/rust-runtime/aws-smithy-client/src/http_connector.rs index 853936991d..45e23063f4 100644 --- a/rust-runtime/aws-smithy-client/src/http_connector.rs +++ b/rust-runtime/aws-smithy-client/src/http_connector.rs @@ -11,44 +11,6 @@ use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_types::timeout::TimeoutConfig; use std::{fmt::Debug, sync::Arc}; -#[cfg(any(feature = "rustls", feature = "native-tls"))] -fn base(settings: &HttpSettings, sleep: Option>) -> crate::hyper_ext::Builder { - let mut hyper = crate::hyper_ext::Adapter::builder().timeout(&settings.timeout_config); - if let Some(sleep) = sleep { - hyper = hyper.sleep_impl(sleep); - } - hyper -} - -/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(feature = "rustls")] -pub fn default_connector( - settings: &HttpSettings, - sleep: Option>, -) -> Option { - let hyper = base(settings, sleep).build(crate::conns::https()); - Some(DynConnector::new(hyper)) -} - -/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(all(not(feature = "rustls"), feature = "native-tls"))] -pub fn default_connector( - settings: &HttpSettings, - sleep: Option>, -) -> Option { - let hyper = base(settings, sleep).build(crate::conns::native_tls()); - Some(DynConnector::new(hyper)) -} - -/// Given `HttpSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(not(any(feature = "rustls", feature = "native-tls")))] -pub fn default_connector( - _settings: &HttpSettings, - _sleep: Option>, -) -> Option { - None -} - /// Type alias for a Connector factory function. pub type MakeConnectorFn = dyn Fn(&HttpSettings, Option>) -> Option + Send + Sync; @@ -62,16 +24,6 @@ pub enum HttpConnector { ConnectorFn(Arc), } -impl Default for HttpConnector { - fn default() -> Self { - Self::ConnectorFn(Arc::new( - |settings: &HttpSettings, sleep: Option>| { - default_connector(settings, sleep) - }, - )) - } -} - impl Debug for HttpConnector { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { From b5d719d574a45b19e2e1e9cc692e53aeb7bfb0dd Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Thu, 3 Feb 2022 16:32:49 -0600 Subject: [PATCH 6/8] update: CHANGELOG.next.toml --- CHANGELOG.next.toml | 97 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 73682d55b6..90475695d0 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,103 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false } # author = "rcoh" +[[aws-sdk-rust]] +message = "Several modules defined in the `aws_config` crate that used to be declared within another module's file have been moved to their own files. The moved modules are `sts`, `connector`, and `default_providers`. They still have the exact same import paths." +references = ["smithy-rs#1144"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "Velfi" + +[[aws-sdk-rust]] +message = "The `aws_config::http_provider` module has been renamed to `aws_config::http_credential_provider` to better reflect its purpose." +references = ["smithy-rs#1144"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "Velfi" + +[[aws-sdk-rust]] +message = """ +Some APIs required that timeout configuration be specified with an `aws_smithy_client::timeout::Settings` struct while +others required an `aws_smithy_types::timeout::TimeoutConfig` struct. Both were equivalent. Now `aws_smithy_types::timeout::TimeoutConfig` +is used everywhere and `aws_smithy_client::timeout::Settings` has been removed. Here's how to migrate code your code that +depended on `timeout::Settings`: + +The old way: +```rust +let timeout = timeout::Settings::new() + .with_connect_timeout(Duration::from_secs(1)) + .with_read_timeout(Duration::from_secs(2)); +``` + +The new way: +```rust +// This example is passing values, so they're wrapped in `Option::Some`. You can disable a timeout by passing `None`. +let timeout = TimeoutConfig::new() + .with_connect_timeout(Some(Duration::from_secs(1))) + .with_read_timeout(Some(Duration::from_secs(2))); +``` +""" +references = ["smithy-rs#1144"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "Velfi" + +[[smithy-rs]] +message = """ +Some APIs required that timeout configuration be specified with an `aws_smithy_client::timeout::Settings` struct while +others required an `aws_smithy_types::timeout::TimeoutConfig` struct. Both were equivalent. Now `aws_smithy_types::timeout::TimeoutConfig` +is used everywhere and `aws_smithy_client::timeout::Settings` has been removed. Here's how to migrate code your code that +depended on `timeout::Settings`: + +The old way: +```rust +let timeout = timeout::Settings::new() + .with_connect_timeout(Duration::from_secs(1)) + .with_read_timeout(Duration::from_secs(2)); +``` + +The new way: +```rust +// This example is passing values, so they're wrapped in `Option::Some`. You can disable a timeout by passing `None`. +let timeout = TimeoutConfig::new() + .with_connect_timeout(Some(Duration::from_secs(1))) + .with_read_timeout(Some(Duration::from_secs(2))); +``` +""" +references = ["smithy-rs#1144"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "Velfi" + +[[aws-sdk-rust]] +message = """ +`MakeConnectorFn`, `HttpConnector`, and `HttpSettings` have been moved from `aws_config::provider_config` to +`aws_smithy_client::http_connector`. This is in preparation for a later update that will change how connectors are +created and configured. + +If you were using these structs/enums, you can migrate your old code by importing them from their new location. +""" +references = ["smithy-rs#1144"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "Velfi" + +[[smithy-rs]] +message = """ +`MakeConnectorFn`, `HttpConnector`, and `HttpSettings` have been moved from `aws_config::provider_config` to +`aws_smithy_client::http_connector`. This is in preparation for a later update that will change how connectors are +created and configured. +""" +references = ["smithy-rs#1144"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "Velfi" + +[[aws-sdk-rust]] +message = """ +Along with moving `HttpConnector` to `aws_smithy_client`, the `HttpConnector::make_connector` method has been renamed to +`HttpConnector::connector`. + +If you were using this method, you can migrate your old code by calling `connector` instead of `make_connector`. +""" +references = ["smithy-rs#1144"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "Velfi" + [[smithy-rs]] message = "Refactor `Document` shape parser generation" references = ["smithy-rs#1123"] From 9fee4bad686b132901fe502c0a2c33d4694aa4a2 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Fri, 4 Feb 2022 10:11:27 -0600 Subject: [PATCH 7/8] remove: unused imports --- aws/rust-runtime/aws-config/src/connector.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index f002c32b90..0664931169 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -8,10 +8,8 @@ use std::sync::Arc; use aws_smithy_async::rt::sleep::AsyncSleep; -use aws_smithy_client::conns; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::http_connector::HttpSettings; -use aws_smithy_client::hyper_ext; // unused when all crate features are disabled /// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` From d5bc067daf4f0503c7db611a7b4c185fadd7d0e0 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Fri, 4 Feb 2022 13:53:54 -0600 Subject: [PATCH 8/8] fix: incorrectly specified dependencies --- aws/rust-runtime/aws-config/src/connector.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index 0664931169..6e8a6524d8 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -13,15 +13,17 @@ use aws_smithy_client::http_connector::HttpSettings; // unused when all crate features are disabled /// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` -pub(crate) fn expect_connector( - connector: Option, -) -> aws_smithy_client::erase::DynConnector { +pub(crate) fn expect_connector(connector: Option) -> DynConnector { connector.expect("A connector was not available. Either set a custom connector or enable the `rustls` and `native-tls` crate features.") } #[cfg(any(feature = "rustls", feature = "native-tls"))] -fn base(settings: &HttpSettings, sleep: Option>) -> hyper_ext::Builder { - let mut hyper = hyper_ext::Adapter::builder().timeout(&settings.timeout_config); +fn base( + settings: &HttpSettings, + sleep: Option>, +) -> aws_smithy_client::hyper_ext::Builder { + let mut hyper = + aws_smithy_client::hyper_ext::Adapter::builder().timeout(&settings.timeout_config); if let Some(sleep) = sleep { hyper = hyper.sleep_impl(sleep); } @@ -34,7 +36,7 @@ pub fn default_connector( settings: &HttpSettings, sleep: Option>, ) -> Option { - let hyper = base(settings, sleep).build(conns::https()); + let hyper = base(settings, sleep).build(aws_smithy_client::conns::https()); Some(DynConnector::new(hyper)) } @@ -44,7 +46,7 @@ pub fn default_connector( settings: &HttpSettings, sleep: Option>, ) -> Option { - let hyper = base(settings, sleep).build(conns::native_tls()); + let hyper = base(settings, sleep).build(aws_smithy_client::conns::native_tls()); Some(DynConnector::new(hyper)) }