aws_config/lib.rs
1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6/* Automatically managed default lints */
7#![cfg_attr(docsrs, feature(doc_cfg))]
8/* End of automatically managed default lints */
9#![allow(clippy::derive_partial_eq_without_eq)]
10#![warn(
11 missing_debug_implementations,
12 missing_docs,
13 rust_2018_idioms,
14 rustdoc::missing_crate_level_docs,
15 unreachable_pub
16)]
17// Allow disallowed methods in tests
18#![cfg_attr(test, allow(clippy::disallowed_methods))]
19
20//! `aws-config` provides implementations of region and credential resolution.
21//!
22//! These implementations can be used either via the default chain implementation
23//! [`from_env`]/[`ConfigLoader`] or ad-hoc individual credential and region providers.
24//!
25//! [`ConfigLoader`] can combine different configuration sources into an AWS shared-config:
26//! [`SdkConfig`]. `SdkConfig` can be used configure an AWS service client.
27//!
28//! # Examples
29//!
30//! Load default SDK configuration:
31//! ```no_run
32//! use aws_config::BehaviorVersion;
33//! mod aws_sdk_dynamodb {
34//! # pub struct Client;
35//! # impl Client {
36//! # pub fn new(config: &aws_types::SdkConfig) -> Self { Client }
37//! # }
38//! # }
39//! # async fn docs() {
40//! let config = aws_config::load_defaults(BehaviorVersion::v2023_11_09()).await;
41//! let client = aws_sdk_dynamodb::Client::new(&config);
42//! # }
43//! ```
44//!
45//! Load SDK configuration with a region override:
46//! ```no_run
47//! # mod aws_sdk_dynamodb {
48//! # pub struct Client;
49//! # impl Client {
50//! # pub fn new(config: &aws_types::SdkConfig) -> Self { Client }
51//! # }
52//! # }
53//! # async fn docs() {
54//! # use aws_config::meta::region::RegionProviderChain;
55//! let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
56//! // Note: requires the `behavior-version-latest` feature enabled
57//! let config = aws_config::from_env().region(region_provider).load().await;
58//! let client = aws_sdk_dynamodb::Client::new(&config);
59//! # }
60//! ```
61//!
62//! Override configuration after construction of `SdkConfig`:
63//!
64//! ```no_run
65//! # use aws_credential_types::provider::ProvideCredentials;
66//! # use aws_types::SdkConfig;
67//! # mod aws_sdk_dynamodb {
68//! # pub mod config {
69//! # pub struct Builder;
70//! # impl Builder {
71//! # pub fn credentials_provider(
72//! # self,
73//! # credentials_provider: impl aws_credential_types::provider::ProvideCredentials + 'static) -> Self { self }
74//! # pub fn build(self) -> Builder { self }
75//! # }
76//! # impl From<&aws_types::SdkConfig> for Builder {
77//! # fn from(_: &aws_types::SdkConfig) -> Self {
78//! # todo!()
79//! # }
80//! # }
81//! # }
82//! # pub struct Client;
83//! # impl Client {
84//! # pub fn from_conf(conf: config::Builder) -> Self { Client }
85//! # pub fn new(config: &aws_types::SdkConfig) -> Self { Client }
86//! # }
87//! # }
88//! # async fn docs() {
89//! # use aws_config::meta::region::RegionProviderChain;
90//! # fn custom_provider(base: &SdkConfig) -> impl ProvideCredentials {
91//! # base.credentials_provider().unwrap().clone()
92//! # }
93//! let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
94//! let custom_credentials_provider = custom_provider(&sdk_config);
95//! let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
96//! .credentials_provider(custom_credentials_provider)
97//! .build();
98//! let client = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
99//! # }
100//! ```
101
102pub use aws_smithy_runtime_api::client::behavior_version::BehaviorVersion;
103// Re-export types from aws-types
104pub use aws_types::{
105 app_name::{AppName, InvalidAppName},
106 region::Region,
107 SdkConfig,
108};
109/// Load default sources for all configuration with override support
110pub use loader::ConfigLoader;
111
112/// Types for configuring identity caching.
113pub mod identity {
114 pub use aws_smithy_runtime::client::identity::IdentityCache;
115 pub use aws_smithy_runtime::client::identity::LazyCacheBuilder;
116}
117
118#[allow(dead_code)]
119const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
120
121mod http_credential_provider;
122mod json_credentials;
123#[cfg(test)]
124mod test_case;
125
126pub mod credential_process;
127pub mod default_provider;
128pub mod ecs;
129mod env_service_config;
130pub mod environment;
131pub mod imds;
132#[cfg(feature = "credentials-login")]
133pub mod login;
134pub mod meta;
135pub mod profile;
136pub mod provider_config;
137pub mod retry;
138mod sensitive_command;
139#[cfg(feature = "sso")]
140pub mod sso;
141pub mod stalled_stream_protection;
142pub mod sts;
143pub mod timeout;
144pub mod web_identity_token;
145
146/// Create a config loader with the _latest_ defaults.
147///
148/// This loader will always set [`BehaviorVersion::latest`].
149///
150/// For more information about default configuration, refer to the AWS SDKs and Tools [shared configuration documentation](https://docs.aws.amazon.com/sdkref/latest/guide/creds-config-files.html).
151///
152/// # Examples
153/// ```no_run
154/// # async fn create_config() {
155/// let config = aws_config::from_env().region("us-east-1").load().await;
156/// # }
157/// ```
158#[cfg(feature = "behavior-version-latest")]
159pub fn from_env() -> ConfigLoader {
160 ConfigLoader::default().behavior_version(BehaviorVersion::latest())
161}
162
163/// Load default configuration with the _latest_ defaults.
164///
165/// Convenience wrapper equivalent to `aws_config::load_defaults(BehaviorVersion::latest()).await`
166///
167/// For more information about default configuration, refer to the AWS SDKs and Tools [shared configuration documentation](https://docs.aws.amazon.com/sdkref/latest/guide/creds-config-files.html).
168#[cfg(feature = "behavior-version-latest")]
169pub async fn load_from_env() -> SdkConfig {
170 from_env().load().await
171}
172
173/// Create a config loader with the _latest_ defaults.
174#[cfg(not(feature = "behavior-version-latest"))]
175#[deprecated(
176 note = "Use the `aws_config::defaults` function. If you don't care about future default behavior changes, you can continue to use this function by enabling the `behavior-version-latest` feature. Doing so will make this deprecation notice go away."
177)]
178pub fn from_env() -> ConfigLoader {
179 ConfigLoader::default().behavior_version(BehaviorVersion::latest())
180}
181
182/// Load default configuration with the _latest_ defaults.
183#[cfg(not(feature = "behavior-version-latest"))]
184#[deprecated(
185 note = "Use the `aws_config::load_defaults` function. If you don't care about future default behavior changes, you can continue to use this function by enabling the `behavior-version-latest` feature. Doing so will make this deprecation notice go away."
186)]
187pub async fn load_from_env() -> SdkConfig {
188 load_defaults(BehaviorVersion::latest()).await
189}
190
191/// Create a config loader with the defaults for the given behavior version.
192///
193/// For more information about default configuration, refer to the AWS SDKs and Tools [shared configuration documentation](https://docs.aws.amazon.com/sdkref/latest/guide/creds-config-files.html).
194///
195/// # Examples
196/// ```no_run
197/// # async fn create_config() {
198/// use aws_config::BehaviorVersion;
199/// let config = aws_config::defaults(BehaviorVersion::v2023_11_09())
200/// .region("us-east-1")
201/// .load()
202/// .await;
203/// # }
204/// ```
205pub fn defaults(version: BehaviorVersion) -> ConfigLoader {
206 ConfigLoader::default().behavior_version(version)
207}
208
209/// Load default configuration with the given behavior version.
210///
211/// Convenience wrapper equivalent to `aws_config::defaults(behavior_version).load().await`
212///
213/// For more information about default configuration, refer to the AWS SDKs and Tools [shared configuration documentation](https://docs.aws.amazon.com/sdkref/latest/guide/creds-config-files.html).
214pub async fn load_defaults(version: BehaviorVersion) -> SdkConfig {
215 defaults(version).load().await
216}
217
218mod loader {
219 use crate::env_service_config::EnvServiceConfig;
220 use aws_credential_types::provider::{
221 token::{ProvideToken, SharedTokenProvider},
222 ProvideCredentials, SharedCredentialsProvider,
223 };
224 use aws_credential_types::Credentials;
225 use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
226 use aws_smithy_async::time::{SharedTimeSource, TimeSource};
227 use aws_smithy_runtime::client::identity::IdentityCache;
228 use aws_smithy_runtime_api::client::auth::AuthSchemePreference;
229 use aws_smithy_runtime_api::client::behavior_version::BehaviorVersion;
230 use aws_smithy_runtime_api::client::http::HttpClient;
231 use aws_smithy_runtime_api::client::identity::{ResolveCachedIdentity, SharedIdentityCache};
232 use aws_smithy_runtime_api::client::stalled_stream_protection::StalledStreamProtectionConfig;
233 use aws_smithy_runtime_api::shared::IntoShared;
234 use aws_smithy_types::checksum_config::{
235 RequestChecksumCalculation, ResponseChecksumValidation,
236 };
237 use aws_smithy_types::retry::RetryConfig;
238 use aws_smithy_types::timeout::TimeoutConfig;
239 use aws_types::app_name::AppName;
240 use aws_types::docs_for;
241 use aws_types::endpoint_config::AccountIdEndpointMode;
242 use aws_types::origin::Origin;
243 use aws_types::os_shim_internal::{Env, Fs};
244 use aws_types::region::SigningRegionSet;
245 use aws_types::sdk_config::SharedHttpClient;
246 use aws_types::SdkConfig;
247
248 use crate::default_provider::{
249 account_id_endpoint_mode, app_name, auth_scheme_preference, checksums, credentials,
250 disable_request_compression, endpoint_url, ignore_configured_endpoint_urls as ignore_ep,
251 region, request_min_compression_size_bytes, retry_config, sigv4a_signing_region_set,
252 timeout_config, use_dual_stack, use_fips,
253 };
254 use crate::meta::region::ProvideRegion;
255 #[allow(deprecated)]
256 use crate::profile::profile_file::ProfileFiles;
257 use crate::provider_config::ProviderConfig;
258
259 #[derive(Default, Debug)]
260 enum TriStateOption<T> {
261 /// No option was set by the user. We can set up the default.
262 #[default]
263 NotSet,
264 /// The option was explicitly unset. Do not set up a default.
265 ExplicitlyUnset,
266 /// Use the given user provided option.
267 Set(T),
268 }
269
270 /// Load a cross-service [`SdkConfig`] from the environment
271 ///
272 /// This builder supports overriding individual components of the generated config. Overriding a component
273 /// will skip the standard resolution chain from **for that component**. For example,
274 /// if you override the region provider, _even if that provider returns None_, the default region provider
275 /// chain will not be used.
276 #[derive(Default, Debug)]
277 pub struct ConfigLoader {
278 app_name: Option<AppName>,
279 auth_scheme_preference: Option<AuthSchemePreference>,
280 sigv4a_signing_region_set: Option<SigningRegionSet>,
281 identity_cache: Option<SharedIdentityCache>,
282 credentials_provider: TriStateOption<SharedCredentialsProvider>,
283 token_provider: Option<SharedTokenProvider>,
284 account_id_endpoint_mode: Option<AccountIdEndpointMode>,
285 endpoint_url: Option<String>,
286 region: Option<Box<dyn ProvideRegion>>,
287 retry_config: Option<RetryConfig>,
288 sleep: Option<SharedAsyncSleep>,
289 timeout_config: Option<TimeoutConfig>,
290 provider_config: Option<ProviderConfig>,
291 http_client: Option<SharedHttpClient>,
292 profile_name_override: Option<String>,
293 #[allow(deprecated)]
294 profile_files_override: Option<ProfileFiles>,
295 use_fips: Option<bool>,
296 use_dual_stack: Option<bool>,
297 time_source: Option<SharedTimeSource>,
298 disable_request_compression: Option<bool>,
299 request_min_compression_size_bytes: Option<u32>,
300 stalled_stream_protection_config: Option<StalledStreamProtectionConfig>,
301 env: Option<Env>,
302 fs: Option<Fs>,
303 behavior_version: Option<BehaviorVersion>,
304 request_checksum_calculation: Option<RequestChecksumCalculation>,
305 response_checksum_validation: Option<ResponseChecksumValidation>,
306 }
307
308 impl ConfigLoader {
309 /// Sets the [`BehaviorVersion`] used to build [`SdkConfig`].
310 pub fn behavior_version(mut self, behavior_version: BehaviorVersion) -> Self {
311 self.behavior_version = Some(behavior_version);
312 self
313 }
314
315 /// Override the region used to build [`SdkConfig`].
316 ///
317 /// # Examples
318 /// ```no_run
319 /// # async fn create_config() {
320 /// use aws_types::region::Region;
321 /// let config = aws_config::from_env()
322 /// .region(Region::new("us-east-1"))
323 /// .load().await;
324 /// # }
325 /// ```
326 pub fn region(mut self, region: impl ProvideRegion + 'static) -> Self {
327 self.region = Some(Box::new(region));
328 self
329 }
330
331 /// Override the retry_config used to build [`SdkConfig`].
332 ///
333 /// # Examples
334 /// ```no_run
335 /// # async fn create_config() {
336 /// use aws_config::retry::RetryConfig;
337 ///
338 /// let config = aws_config::from_env()
339 /// .retry_config(RetryConfig::standard().with_max_attempts(2))
340 /// .load()
341 /// .await;
342 /// # }
343 /// ```
344 pub fn retry_config(mut self, retry_config: RetryConfig) -> Self {
345 self.retry_config = Some(retry_config);
346 self
347 }
348
349 /// Override the timeout config used to build [`SdkConfig`].
350 ///
351 /// This will be merged with timeouts coming from the timeout information provider, which
352 /// currently includes a default `CONNECT` timeout of `3.1s`.
353 ///
354 /// If you want to disable timeouts, use [`TimeoutConfig::disabled`]. If you want to disable
355 /// a specific timeout, use `TimeoutConfig::set_<type>(None)`.
356 ///
357 /// **Note: This only sets timeouts for calls to AWS services.** Timeouts for the credentials
358 /// provider chain are configured separately.
359 ///
360 /// # Examples
361 /// ```no_run
362 /// # use std::time::Duration;
363 /// # async fn create_config() {
364 /// use aws_config::timeout::TimeoutConfig;
365 ///
366 /// let config = aws_config::from_env()
367 /// .timeout_config(
368 /// TimeoutConfig::builder()
369 /// .operation_timeout(Duration::from_secs(5))
370 /// .build()
371 /// )
372 /// .load()
373 /// .await;
374 /// # }
375 /// ```
376 pub fn timeout_config(mut self, timeout_config: TimeoutConfig) -> Self {
377 self.timeout_config = Some(timeout_config);
378 self
379 }
380
381 /// Override the sleep implementation for this [`ConfigLoader`].
382 ///
383 /// The sleep implementation is used to create timeout futures.
384 /// You generally won't need to change this unless you're using an async runtime other
385 /// than Tokio.
386 pub fn sleep_impl(mut self, sleep: impl AsyncSleep + 'static) -> Self {
387 // it's possible that we could wrapping an `Arc in an `Arc` and that's OK
388 self.sleep = Some(sleep.into_shared());
389 self
390 }
391
392 /// Set the time source used for tasks like signing requests.
393 ///
394 /// You generally won't need to change this unless you're compiling for a target
395 /// that can't provide a default, such as WASM, or unless you're writing a test against
396 /// the client that needs a fixed time.
397 pub fn time_source(mut self, time_source: impl TimeSource + 'static) -> Self {
398 self.time_source = Some(time_source.into_shared());
399 self
400 }
401
402 /// Override the [`HttpClient`] for this [`ConfigLoader`].
403 ///
404 /// The HTTP client will be used for both AWS services and credentials providers.
405 ///
406 /// If you wish to use a separate HTTP client for credentials providers when creating clients,
407 /// then override the HTTP client set with this function on the client-specific `Config`s.
408 pub fn http_client(mut self, http_client: impl HttpClient + 'static) -> Self {
409 self.http_client = Some(http_client.into_shared());
410 self
411 }
412
413 #[doc = docs_for!(auth_scheme_preference)]
414 ///
415 /// # Examples
416 /// ```no_run
417 /// # use aws_smithy_runtime_api::client::auth::AuthSchemeId;
418 /// # async fn create_config() {
419 /// let config = aws_config::from_env()
420 /// // Favors a custom auth scheme over the SigV4 auth scheme.
421 /// // Note: This will not result in an error, even if the custom scheme is missing from the resolved auth schemes.
422 /// .auth_scheme_preference([AuthSchemeId::from("custom"), aws_runtime::auth::sigv4::SCHEME_ID])
423 /// .load()
424 /// .await;
425 /// # }
426 /// ```
427 pub fn auth_scheme_preference(
428 mut self,
429 auth_scheme_preference: impl Into<AuthSchemePreference>,
430 ) -> Self {
431 self.auth_scheme_preference = Some(auth_scheme_preference.into());
432 self
433 }
434
435 #[doc = docs_for!(sigv4a_signing_region_set)]
436 pub fn sigv4a_signing_region_set(
437 mut self,
438 sigv4a_signing_region_set: impl Into<SigningRegionSet>,
439 ) -> Self {
440 self.sigv4a_signing_region_set = Some(sigv4a_signing_region_set.into());
441 self
442 }
443
444 /// Override the identity cache used to build [`SdkConfig`].
445 ///
446 /// The identity cache caches AWS credentials and SSO tokens. By default, a lazy cache is used
447 /// that will load credentials upon first request, cache them, and then reload them during
448 /// another request when they are close to expiring.
449 ///
450 /// # Examples
451 ///
452 /// Change a setting on the default lazy caching implementation:
453 /// ```no_run
454 /// use aws_config::identity::IdentityCache;
455 /// use std::time::Duration;
456 ///
457 /// # async fn create_config() {
458 /// let config = aws_config::from_env()
459 /// .identity_cache(
460 /// IdentityCache::lazy()
461 /// // Change the load timeout to 10 seconds.
462 /// // Note: there are other timeouts that could trigger if the load timeout is too long.
463 /// .load_timeout(Duration::from_secs(10))
464 /// .build()
465 /// )
466 /// .load()
467 /// .await;
468 /// # }
469 /// ```
470 pub fn identity_cache(
471 mut self,
472 identity_cache: impl ResolveCachedIdentity + 'static,
473 ) -> Self {
474 self.identity_cache = Some(identity_cache.into_shared());
475 self
476 }
477
478 /// Override the credentials provider used to build [`SdkConfig`].
479 ///
480 /// # Examples
481 ///
482 /// Override the credentials provider but load the default value for region:
483 /// ```no_run
484 /// # use aws_credential_types::Credentials;
485 /// # fn create_my_credential_provider() -> Credentials {
486 /// # Credentials::new("example", "example", None, None, "example")
487 /// # }
488 /// # async fn create_config() {
489 /// let config = aws_config::from_env()
490 /// .credentials_provider(create_my_credential_provider())
491 /// .load()
492 /// .await;
493 /// # }
494 /// ```
495 pub fn credentials_provider(
496 mut self,
497 credentials_provider: impl ProvideCredentials + 'static,
498 ) -> Self {
499 self.credentials_provider =
500 TriStateOption::Set(SharedCredentialsProvider::new(credentials_provider));
501 self
502 }
503
504 /// Don't use credentials to sign requests.
505 ///
506 /// Turning off signing with credentials is necessary in some cases, such as using
507 /// anonymous auth for S3, calling operations in STS that don't require a signature,
508 /// or using token-based auth.
509 ///
510 /// **Note**: For tests, e.g. with a service like DynamoDB Local, this is **not** what you
511 /// want. If credentials are disabled, requests cannot be signed. For these use cases, use
512 /// [`test_credentials`](Self::test_credentials).
513 ///
514 /// # Examples
515 ///
516 /// Turn off credentials in order to call a service without signing:
517 /// ```no_run
518 /// # async fn create_config() {
519 /// let config = aws_config::from_env()
520 /// .no_credentials()
521 /// .load()
522 /// .await;
523 /// # }
524 /// ```
525 pub fn no_credentials(mut self) -> Self {
526 self.credentials_provider = TriStateOption::ExplicitlyUnset;
527 self
528 }
529
530 /// Set test credentials for use when signing requests
531 pub fn test_credentials(self) -> Self {
532 #[allow(unused_mut)]
533 let mut ret = self.credentials_provider(Credentials::for_tests());
534 #[cfg(feature = "sso")]
535 {
536 use aws_smithy_runtime_api::client::identity::http::Token;
537 ret = ret.token_provider(Token::for_tests());
538 }
539 ret
540 }
541
542 /// Ignore any environment variables on the host during config resolution
543 ///
544 /// This allows for testing in a reproducible environment that ensures any
545 /// environment variables from the host do not influence environment variable
546 /// resolution.
547 pub fn empty_test_environment(mut self) -> Self {
548 self.env = Some(Env::from_slice(&[]));
549 self
550 }
551
552 /// Override the environment variable abstraction used during config resolution.
553 ///
554 /// This can be used with [`Env::from_custom`] to provide a custom environment
555 /// variable backend (e.g., remote config stores, vaults).
556 pub fn env(mut self, env: Env) -> Self {
557 self.env = Some(env);
558 self
559 }
560
561 /// Override the filesystem abstraction used during config resolution.
562 ///
563 /// This can be used with [`Fs::from_custom`] to provide a custom filesystem
564 /// backend (e.g., in-memory stores, encrypted filesystems).
565 pub fn fs(mut self, fs: Fs) -> Self {
566 self.fs = Some(fs);
567 self
568 }
569
570 /// Override the access token provider used to build [`SdkConfig`].
571 ///
572 /// # Examples
573 ///
574 /// Override the token provider but load the default value for region:
575 /// ```no_run
576 /// # use aws_credential_types::Token;
577 /// # fn create_my_token_provider() -> Token {
578 /// # Token::new("example", None)
579 /// # }
580 /// # async fn create_config() {
581 /// let config = aws_config::from_env()
582 /// .token_provider(create_my_token_provider())
583 /// .load()
584 /// .await;
585 /// # }
586 /// ```
587 pub fn token_provider(mut self, token_provider: impl ProvideToken + 'static) -> Self {
588 self.token_provider = Some(SharedTokenProvider::new(token_provider));
589 self
590 }
591
592 /// Override the name of the app used to build [`SdkConfig`].
593 ///
594 /// This _optional_ name is used to identify the application in the user agent header that
595 /// gets sent along with requests.
596 ///
597 /// The app name is selected from an ordered list of sources:
598 /// 1. This override.
599 /// 2. The value of the `AWS_SDK_UA_APP_ID` environment variable.
600 /// 3. Profile files from the key `sdk_ua_app_id`
601 ///
602 /// If none of those sources are set the value is `None` and it is not added to the user agent header.
603 ///
604 /// # Examples
605 /// ```no_run
606 /// # async fn create_config() {
607 /// use aws_config::AppName;
608 /// let config = aws_config::from_env()
609 /// .app_name(AppName::new("my-app-name").expect("valid app name"))
610 /// .load().await;
611 /// # }
612 /// ```
613 pub fn app_name(mut self, app_name: AppName) -> Self {
614 self.app_name = Some(app_name);
615 self
616 }
617
618 /// Provides the ability to programmatically override the profile files that get loaded by the SDK.
619 ///
620 /// The [`Default`] for `ProfileFiles` includes the default SDK config and credential files located in
621 /// `~/.aws/config` and `~/.aws/credentials` respectively.
622 ///
623 /// Any number of config and credential files may be added to the `ProfileFiles` file set, with the
624 /// only requirement being that there is at least one of each. Profile file locations will produce an
625 /// error if they don't exist, but the default config/credentials files paths are exempt from this validation.
626 ///
627 /// # Example: Using a custom profile file path
628 ///
629 /// ```no_run
630 /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider};
631 /// use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind};
632 ///
633 /// # async fn example() {
634 /// let profile_files = ProfileFiles::builder()
635 /// .with_file(ProfileFileKind::Credentials, "some/path/to/credentials-file")
636 /// .build();
637 /// let sdk_config = aws_config::from_env()
638 /// .profile_files(profile_files)
639 /// .load()
640 /// .await;
641 /// # }
642 #[allow(deprecated)]
643 pub fn profile_files(mut self, profile_files: ProfileFiles) -> Self {
644 self.profile_files_override = Some(profile_files);
645 self
646 }
647
648 /// Override the profile name used by configuration providers
649 ///
650 /// Profile name is selected from an ordered list of sources:
651 /// 1. This override.
652 /// 2. The value of the `AWS_PROFILE` environment variable.
653 /// 3. `default`
654 ///
655 /// Each AWS profile has a name. For example, in the file below, the profiles are named
656 /// `dev`, `prod` and `staging`:
657 /// ```ini
658 /// [dev]
659 /// ec2_metadata_service_endpoint = http://my-custom-endpoint:444
660 ///
661 /// [staging]
662 /// ec2_metadata_service_endpoint = http://my-custom-endpoint:444
663 ///
664 /// [prod]
665 /// ec2_metadata_service_endpoint = http://my-custom-endpoint:444
666 /// ```
667 ///
668 /// See [Named profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html)
669 /// for more information about naming profiles.
670 ///
671 /// # Example: Using a custom profile name
672 ///
673 /// ```no_run
674 /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider};
675 ///
676 /// # async fn example() {
677 /// let sdk_config = aws_config::from_env()
678 /// .profile_name("prod")
679 /// .load()
680 /// .await;
681 /// # }
682 pub fn profile_name(mut self, profile_name: impl Into<String>) -> Self {
683 self.profile_name_override = Some(profile_name.into());
684 self
685 }
686
687 #[doc = docs_for!(account_id_endpoint_mode)]
688 pub fn account_id_endpoint_mode(
689 mut self,
690 account_id_endpoint_mode: AccountIdEndpointMode,
691 ) -> Self {
692 self.account_id_endpoint_mode = Some(account_id_endpoint_mode);
693 self
694 }
695
696 /// Override the endpoint URL used for **all** AWS services.
697 ///
698 /// This method will override the endpoint URL used for **all** AWS services. This primarily
699 /// exists to set a static endpoint for tools like `LocalStack`. When sending requests to
700 /// production AWS services, this method should only be used for service-specific behavior.
701 ///
702 /// When this method is used, the [`Region`](aws_types::region::Region) is only used for signing;
703 /// It is **not** used to route the request.
704 ///
705 /// # Examples
706 ///
707 /// Use a static endpoint for all services
708 /// ```no_run
709 /// # async fn create_config() {
710 /// let sdk_config = aws_config::from_env()
711 /// .endpoint_url("http://localhost:1234")
712 /// .load()
713 /// .await;
714 /// # }
715 pub fn endpoint_url(mut self, endpoint_url: impl Into<String>) -> Self {
716 self.endpoint_url = Some(endpoint_url.into());
717 self
718 }
719
720 #[doc = docs_for!(use_fips)]
721 pub fn use_fips(mut self, use_fips: bool) -> Self {
722 self.use_fips = Some(use_fips);
723 self
724 }
725
726 #[doc = docs_for!(use_dual_stack)]
727 pub fn use_dual_stack(mut self, use_dual_stack: bool) -> Self {
728 self.use_dual_stack = Some(use_dual_stack);
729 self
730 }
731
732 #[doc = docs_for!(disable_request_compression)]
733 pub fn disable_request_compression(mut self, disable_request_compression: bool) -> Self {
734 self.disable_request_compression = Some(disable_request_compression);
735 self
736 }
737
738 #[doc = docs_for!(request_min_compression_size_bytes)]
739 pub fn request_min_compression_size_bytes(mut self, size: u32) -> Self {
740 self.request_min_compression_size_bytes = Some(size);
741 self
742 }
743
744 /// Override the [`StalledStreamProtectionConfig`] used to build [`SdkConfig`].
745 ///
746 /// This configures stalled stream protection. When enabled, download streams
747 /// that stop (stream no data) for longer than a configured grace period will return an error.
748 ///
749 /// By default, streams that transmit less than one byte per-second for five seconds will
750 /// be cancelled.
751 ///
752 /// _Note_: When an override is provided, the default implementation is replaced.
753 ///
754 /// # Examples
755 /// ```no_run
756 /// # async fn create_config() {
757 /// use aws_config::stalled_stream_protection::StalledStreamProtectionConfig;
758 /// use std::time::Duration;
759 /// let config = aws_config::from_env()
760 /// .stalled_stream_protection(
761 /// StalledStreamProtectionConfig::enabled()
762 /// .grace_period(Duration::from_secs(1))
763 /// .build()
764 /// )
765 /// .load()
766 /// .await;
767 /// # }
768 /// ```
769 pub fn stalled_stream_protection(
770 mut self,
771 stalled_stream_protection_config: StalledStreamProtectionConfig,
772 ) -> Self {
773 self.stalled_stream_protection_config = Some(stalled_stream_protection_config);
774 self
775 }
776
777 /// Set the checksum calculation strategy to use when making requests.
778 /// # Examples
779 /// ```
780 /// use aws_types::SdkConfig;
781 /// use aws_smithy_types::checksum_config::RequestChecksumCalculation;
782 /// let config = SdkConfig::builder().request_checksum_calculation(RequestChecksumCalculation::WhenSupported).build();
783 /// ```
784 pub fn request_checksum_calculation(
785 mut self,
786 request_checksum_calculation: RequestChecksumCalculation,
787 ) -> Self {
788 self.request_checksum_calculation = Some(request_checksum_calculation);
789 self
790 }
791
792 /// Set the checksum calculation strategy to use for responses.
793 /// # Examples
794 /// ```
795 /// use aws_types::SdkConfig;
796 /// use aws_smithy_types::checksum_config::ResponseChecksumValidation;
797 /// let config = SdkConfig::builder().response_checksum_validation(ResponseChecksumValidation::WhenSupported).build();
798 /// ```
799 pub fn response_checksum_validation(
800 mut self,
801 response_checksum_validation: ResponseChecksumValidation,
802 ) -> Self {
803 self.response_checksum_validation = Some(response_checksum_validation);
804 self
805 }
806
807 /// Load the default configuration chain
808 ///
809 /// If fields have been overridden during builder construction, the override values will be used.
810 ///
811 /// Otherwise, the default values for each field will be provided.
812 ///
813 /// NOTE: When an override is provided, the default implementation is **not** used as a fallback.
814 /// This means that if you provide a region provider that does not return a region, no region will
815 /// be set in the resulting [`SdkConfig`].
816 pub async fn load(self) -> SdkConfig {
817 let time_source = self.time_source.unwrap_or_default();
818
819 let sleep_impl = if self.sleep.is_some() {
820 self.sleep
821 } else {
822 if default_async_sleep().is_none() {
823 tracing::warn!(
824 "An implementation of AsyncSleep was requested by calling default_async_sleep \
825 but no default was set.
826 This happened when ConfigLoader::load was called during Config construction. \
827 You can fix this by setting a sleep_impl on the ConfigLoader before calling \
828 load or by enabling the rt-tokio feature"
829 );
830 }
831 default_async_sleep()
832 };
833
834 let conf = self
835 .provider_config
836 .unwrap_or_else(|| {
837 let mut config = ProviderConfig::init(time_source.clone(), sleep_impl.clone())
838 .with_fs(self.fs.unwrap_or_default())
839 .with_env(self.env.unwrap_or_default());
840 if let Some(http_client) = self.http_client.clone() {
841 config = config.with_http_client(http_client);
842 }
843 config
844 })
845 .with_profile_config(self.profile_files_override, self.profile_name_override);
846
847 let use_fips = if let Some(use_fips) = self.use_fips {
848 Some(use_fips)
849 } else {
850 use_fips::use_fips_provider(&conf).await
851 };
852
853 let use_dual_stack = if let Some(use_dual_stack) = self.use_dual_stack {
854 Some(use_dual_stack)
855 } else {
856 use_dual_stack::use_dual_stack_provider(&conf).await
857 };
858
859 let conf = conf
860 .with_use_fips(use_fips)
861 .with_use_dual_stack(use_dual_stack);
862
863 let region = if let Some(provider) = self.region {
864 provider.region().await
865 } else {
866 region::Builder::default()
867 .configure(&conf)
868 .build()
869 .region()
870 .await
871 };
872 let conf = conf.with_region(region.clone());
873
874 let retry_config = if let Some(retry_config) = self.retry_config {
875 retry_config
876 } else {
877 retry_config::default_provider()
878 .configure(&conf)
879 .retry_config()
880 .await
881 };
882
883 let app_name = if self.app_name.is_some() {
884 self.app_name
885 } else {
886 app_name::default_provider()
887 .configure(&conf)
888 .app_name()
889 .await
890 };
891
892 let disable_request_compression = if self.disable_request_compression.is_some() {
893 self.disable_request_compression
894 } else {
895 disable_request_compression::disable_request_compression_provider(&conf).await
896 };
897
898 let request_min_compression_size_bytes =
899 if self.request_min_compression_size_bytes.is_some() {
900 self.request_min_compression_size_bytes
901 } else {
902 request_min_compression_size_bytes::request_min_compression_size_bytes_provider(
903 &conf,
904 )
905 .await
906 };
907
908 let base_config = timeout_config::default_provider()
909 .configure(&conf)
910 .timeout_config()
911 .await;
912 let mut timeout_config = self
913 .timeout_config
914 .unwrap_or_else(|| TimeoutConfig::builder().build());
915 timeout_config.take_defaults_from(&base_config);
916
917 let credentials_provider = match self.credentials_provider {
918 TriStateOption::Set(provider) => Some(provider),
919 TriStateOption::NotSet => {
920 let mut builder =
921 credentials::DefaultCredentialsChain::builder().configure(conf.clone());
922 builder.set_region(region.clone());
923 Some(SharedCredentialsProvider::new(builder.build().await))
924 }
925 TriStateOption::ExplicitlyUnset => None,
926 };
927
928 let profiles = conf.profile().await;
929 let service_config = EnvServiceConfig {
930 env: conf.env(),
931 env_config_sections: profiles.cloned().unwrap_or_default(),
932 };
933 let mut builder = SdkConfig::builder()
934 .region(region.clone())
935 .retry_config(retry_config)
936 .timeout_config(timeout_config)
937 .time_source(time_source)
938 .service_config(service_config);
939
940 // If an endpoint URL is set programmatically, then our work is done.
941 let endpoint_url = if self.endpoint_url.is_some() {
942 builder.insert_origin("endpoint_url", Origin::shared_config());
943 self.endpoint_url
944 } else {
945 // Otherwise, check to see if we should ignore EP URLs set in the environment.
946 let ignore_configured_endpoint_urls =
947 ignore_ep::ignore_configured_endpoint_urls_provider(&conf)
948 .await
949 .unwrap_or_default();
950
951 if ignore_configured_endpoint_urls {
952 // If yes, log a trace and return `None`.
953 tracing::trace!(
954 "`ignore_configured_endpoint_urls` is set, any endpoint URLs configured in the environment will be ignored. \
955 NOTE: Endpoint URLs set programmatically WILL still be respected"
956 );
957 None
958 } else {
959 // Otherwise, attempt to resolve one.
960 let (v, origin) = endpoint_url::endpoint_url_provider_with_origin(&conf).await;
961 builder.insert_origin("endpoint_url", origin);
962 v
963 }
964 };
965
966 let token_provider = match self.token_provider {
967 Some(provider) => {
968 builder.insert_origin("token_provider", Origin::shared_config());
969 Some(provider)
970 }
971 None => {
972 #[cfg(feature = "sso")]
973 {
974 let mut builder =
975 crate::default_provider::token::DefaultTokenChain::builder()
976 .configure(conf.clone());
977 builder.set_region(region);
978 Some(SharedTokenProvider::new(builder.build().await))
979 }
980 #[cfg(not(feature = "sso"))]
981 {
982 None
983 }
984 // Not setting `Origin` in this arm, and that's good for now as long as we know
985 // it's not programmatically set in the shared config.
986 // We can consider adding `Origin::Default` if needed.
987 }
988 };
989
990 builder.set_endpoint_url(endpoint_url);
991 builder.set_behavior_version(self.behavior_version);
992 builder.set_http_client(self.http_client);
993 builder.set_app_name(app_name);
994
995 let identity_cache = match self.identity_cache {
996 None => match self.behavior_version {
997 #[allow(deprecated)]
998 Some(bv) if bv.is_at_least(BehaviorVersion::v2024_03_28()) => {
999 Some(IdentityCache::lazy().build())
1000 }
1001 _ => None,
1002 },
1003 Some(user_cache) => Some(user_cache),
1004 };
1005
1006 let request_checksum_calculation =
1007 if let Some(request_checksum_calculation) = self.request_checksum_calculation {
1008 Some(request_checksum_calculation)
1009 } else {
1010 checksums::request_checksum_calculation_provider(&conf).await
1011 };
1012
1013 let response_checksum_validation =
1014 if let Some(response_checksum_validation) = self.response_checksum_validation {
1015 Some(response_checksum_validation)
1016 } else {
1017 checksums::response_checksum_validation_provider(&conf).await
1018 };
1019
1020 let account_id_endpoint_mode =
1021 if let Some(acccount_id_endpoint_mode) = self.account_id_endpoint_mode {
1022 Some(acccount_id_endpoint_mode)
1023 } else {
1024 account_id_endpoint_mode::account_id_endpoint_mode_provider(&conf).await
1025 };
1026
1027 let auth_scheme_preference =
1028 if let Some(auth_scheme_preference) = self.auth_scheme_preference {
1029 builder.insert_origin("auth_scheme_preference", Origin::shared_config());
1030 Some(auth_scheme_preference)
1031 } else {
1032 auth_scheme_preference::auth_scheme_preference_provider(&conf).await
1033 // Not setting `Origin` in this arm, and that's good for now as long as we know
1034 // it's not programmatically set in the shared config.
1035 };
1036
1037 let sigv4a_signing_region_set =
1038 if let Some(sigv4a_signing_region_set) = self.sigv4a_signing_region_set {
1039 Some(sigv4a_signing_region_set)
1040 } else {
1041 sigv4a_signing_region_set::sigv4a_signing_region_set_provider(&conf).await
1042 };
1043
1044 builder.set_request_checksum_calculation(request_checksum_calculation);
1045 builder.set_response_checksum_validation(response_checksum_validation);
1046 builder.set_identity_cache(identity_cache);
1047 builder.set_credentials_provider(credentials_provider);
1048 builder.set_token_provider(token_provider);
1049 builder.set_sleep_impl(sleep_impl);
1050 builder.set_use_fips(use_fips);
1051 builder.set_use_dual_stack(use_dual_stack);
1052 builder.set_disable_request_compression(disable_request_compression);
1053 builder.set_request_min_compression_size_bytes(request_min_compression_size_bytes);
1054 builder.set_stalled_stream_protection(self.stalled_stream_protection_config);
1055 builder.set_account_id_endpoint_mode(account_id_endpoint_mode);
1056 builder.set_auth_scheme_preference(auth_scheme_preference);
1057 builder.set_sigv4a_signing_region_set(sigv4a_signing_region_set);
1058 builder.build()
1059 }
1060 }
1061
1062 #[cfg(test)]
1063 mod test {
1064 #[allow(deprecated)]
1065 use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
1066 use crate::test_case::{no_traffic_client, InstantSleep};
1067 use crate::BehaviorVersion;
1068 use crate::{defaults, ConfigLoader};
1069 use aws_credential_types::provider::ProvideCredentials;
1070 use aws_smithy_async::rt::sleep::TokioSleep;
1071 use aws_smithy_http_client::test_util::{infallible_client_fn, NeverClient};
1072 use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs;
1073 use aws_types::app_name::AppName;
1074 use aws_types::origin::Origin;
1075 use aws_types::os_shim_internal::{Env, Fs};
1076 use aws_types::sdk_config::{RequestChecksumCalculation, ResponseChecksumValidation};
1077 use std::sync::atomic::{AtomicUsize, Ordering};
1078 use std::sync::Arc;
1079
1080 #[tokio::test]
1081 async fn provider_config_used() {
1082 let (_guard, logs_rx) = capture_test_logs();
1083 let env = Env::from_slice(&[
1084 ("AWS_MAX_ATTEMPTS", "10"),
1085 ("AWS_REGION", "us-west-4"),
1086 ("AWS_ACCESS_KEY_ID", "akid"),
1087 ("AWS_SECRET_ACCESS_KEY", "secret"),
1088 ]);
1089 let fs =
1090 Fs::from_slice(&[("test_config", "[profile custom]\nsdk-ua-app-id = correct")]);
1091 let loader = defaults(BehaviorVersion::latest())
1092 .sleep_impl(TokioSleep::new())
1093 .env(env)
1094 .fs(fs)
1095 .http_client(NeverClient::new())
1096 .profile_name("custom")
1097 .profile_files(
1098 #[allow(deprecated)]
1099 ProfileFiles::builder()
1100 .with_file(
1101 #[allow(deprecated)]
1102 ProfileFileKind::Config,
1103 "test_config",
1104 )
1105 .build(),
1106 )
1107 .load()
1108 .await;
1109 assert_eq!(10, loader.retry_config().unwrap().max_attempts());
1110 assert_eq!("us-west-4", loader.region().unwrap().as_ref());
1111 assert_eq!(
1112 "akid",
1113 loader
1114 .credentials_provider()
1115 .unwrap()
1116 .provide_credentials()
1117 .await
1118 .unwrap()
1119 .access_key_id(),
1120 );
1121 assert_eq!(Some(&AppName::new("correct").unwrap()), loader.app_name());
1122
1123 let num_config_loader_logs = logs_rx.contents()
1124 .lines()
1125 // The logger uses fancy formatting, so we have to account for that.
1126 .filter(|l| l.contains("config file loaded \u{1b}[3mpath\u{1b}[0m\u{1b}[2m=\u{1b}[0mSome(\"test_config\") \u{1b}[3msize\u{1b}[0m\u{1b}[2m=\u{1b}"))
1127 .count();
1128
1129 match num_config_loader_logs {
1130 0 => panic!("no config file logs found!"),
1131 1 => (),
1132 more => panic!("the config file was parsed more than once! (parsed {more})",),
1133 };
1134 }
1135
1136 fn base_conf() -> ConfigLoader {
1137 defaults(BehaviorVersion::latest())
1138 .sleep_impl(InstantSleep)
1139 .http_client(no_traffic_client())
1140 }
1141
1142 #[tokio::test]
1143 async fn test_origin_programmatic() {
1144 let _ = tracing_subscriber::fmt::try_init();
1145 let loader = base_conf()
1146 .test_credentials()
1147 .profile_name("custom")
1148 .profile_files(
1149 #[allow(deprecated)]
1150 ProfileFiles::builder()
1151 .with_contents(
1152 #[allow(deprecated)]
1153 ProfileFileKind::Config,
1154 "[profile custom]\nendpoint_url = http://localhost:8989",
1155 )
1156 .build(),
1157 )
1158 .endpoint_url("http://localhost:1111")
1159 .load()
1160 .await;
1161 assert_eq!(Origin::shared_config(), loader.get_origin("endpoint_url"));
1162 }
1163
1164 #[tokio::test]
1165 async fn test_origin_env() {
1166 let _ = tracing_subscriber::fmt::try_init();
1167 let env = Env::from_slice(&[("AWS_ENDPOINT_URL", "http://localhost:7878")]);
1168 let loader = base_conf()
1169 .test_credentials()
1170 .env(env)
1171 .profile_name("custom")
1172 .profile_files(
1173 #[allow(deprecated)]
1174 ProfileFiles::builder()
1175 .with_contents(
1176 #[allow(deprecated)]
1177 ProfileFileKind::Config,
1178 "[profile custom]\nendpoint_url = http://localhost:8989",
1179 )
1180 .build(),
1181 )
1182 .load()
1183 .await;
1184 assert_eq!(
1185 Origin::shared_environment_variable(),
1186 loader.get_origin("endpoint_url")
1187 );
1188 }
1189
1190 #[tokio::test]
1191 async fn test_origin_fs() {
1192 let _ = tracing_subscriber::fmt::try_init();
1193 let loader = base_conf()
1194 .test_credentials()
1195 .profile_name("custom")
1196 .profile_files(
1197 #[allow(deprecated)]
1198 ProfileFiles::builder()
1199 .with_contents(
1200 #[allow(deprecated)]
1201 ProfileFileKind::Config,
1202 "[profile custom]\nendpoint_url = http://localhost:8989",
1203 )
1204 .build(),
1205 )
1206 .load()
1207 .await;
1208 assert_eq!(
1209 Origin::shared_profile_file(),
1210 loader.get_origin("endpoint_url")
1211 );
1212 }
1213
1214 #[tokio::test]
1215 async fn load_use_fips() {
1216 let conf = base_conf().use_fips(true).load().await;
1217 assert_eq!(Some(true), conf.use_fips());
1218 }
1219
1220 #[tokio::test]
1221 async fn load_dual_stack() {
1222 let conf = base_conf().use_dual_stack(false).load().await;
1223 assert_eq!(Some(false), conf.use_dual_stack());
1224
1225 let conf = base_conf().load().await;
1226 assert_eq!(None, conf.use_dual_stack());
1227 }
1228
1229 #[tokio::test]
1230 async fn load_disable_request_compression() {
1231 let conf = base_conf().disable_request_compression(true).load().await;
1232 assert_eq!(Some(true), conf.disable_request_compression());
1233
1234 let conf = base_conf().load().await;
1235 assert_eq!(None, conf.disable_request_compression());
1236 }
1237
1238 #[tokio::test]
1239 async fn load_request_min_compression_size_bytes() {
1240 let conf = base_conf()
1241 .request_min_compression_size_bytes(99)
1242 .load()
1243 .await;
1244 assert_eq!(Some(99), conf.request_min_compression_size_bytes());
1245
1246 let conf = base_conf().load().await;
1247 assert_eq!(None, conf.request_min_compression_size_bytes());
1248 }
1249
1250 #[tokio::test]
1251 async fn app_name() {
1252 let app_name = AppName::new("my-app-name").unwrap();
1253 let conf = base_conf().app_name(app_name.clone()).load().await;
1254 assert_eq!(Some(&app_name), conf.app_name());
1255 }
1256
1257 #[tokio::test]
1258 async fn request_checksum_calculation() {
1259 let conf = base_conf()
1260 .request_checksum_calculation(RequestChecksumCalculation::WhenRequired)
1261 .load()
1262 .await;
1263 assert_eq!(
1264 Some(RequestChecksumCalculation::WhenRequired),
1265 conf.request_checksum_calculation()
1266 );
1267 }
1268
1269 #[tokio::test]
1270 async fn response_checksum_validation() {
1271 let conf = base_conf()
1272 .response_checksum_validation(ResponseChecksumValidation::WhenRequired)
1273 .load()
1274 .await;
1275 assert_eq!(
1276 Some(ResponseChecksumValidation::WhenRequired),
1277 conf.response_checksum_validation()
1278 );
1279 }
1280
1281 #[cfg(feature = "default-https-client")]
1282 #[tokio::test]
1283 async fn disable_default_credentials() {
1284 let config = defaults(BehaviorVersion::latest())
1285 .no_credentials()
1286 .load()
1287 .await;
1288 assert!(config.credentials_provider().is_none());
1289 }
1290
1291 #[cfg(feature = "default-https-client")]
1292 #[tokio::test]
1293 async fn identity_cache_defaulted() {
1294 let config = defaults(BehaviorVersion::latest()).load().await;
1295
1296 assert!(config.identity_cache().is_some());
1297 }
1298
1299 #[cfg(feature = "default-https-client")]
1300 #[allow(deprecated)]
1301 #[tokio::test]
1302 async fn identity_cache_old_behavior_version() {
1303 let config = defaults(BehaviorVersion::v2023_11_09()).load().await;
1304
1305 assert!(config.identity_cache().is_none());
1306 }
1307
1308 #[tokio::test]
1309 async fn connector_is_shared() {
1310 let num_requests = Arc::new(AtomicUsize::new(0));
1311 let movable = num_requests.clone();
1312 let http_client = infallible_client_fn(move |_req| {
1313 movable.fetch_add(1, Ordering::Relaxed);
1314 http::Response::new("ok!")
1315 });
1316 let config = defaults(BehaviorVersion::latest())
1317 .fs(Fs::from_slice(&[]))
1318 .env(Env::from_slice(&[]))
1319 .http_client(http_client.clone())
1320 .load()
1321 .await;
1322 config
1323 .credentials_provider()
1324 .unwrap()
1325 .provide_credentials()
1326 .await
1327 .expect_err("did not expect credentials to be loaded—no traffic is allowed");
1328 let num_requests = num_requests.load(Ordering::Relaxed);
1329 assert!(num_requests > 0, "{}", num_requests);
1330 }
1331
1332 #[tokio::test]
1333 async fn endpoint_urls_may_be_ignored_from_env() {
1334 let fs = Fs::from_slice(&[(
1335 "test_config",
1336 "[profile custom]\nendpoint_url = http://profile",
1337 )]);
1338 let env = Env::from_slice(&[("AWS_IGNORE_CONFIGURED_ENDPOINT_URLS", "true")]);
1339
1340 let conf = base_conf().use_dual_stack(false).load().await;
1341 assert_eq!(Some(false), conf.use_dual_stack());
1342
1343 let conf = base_conf().load().await;
1344 assert_eq!(None, conf.use_dual_stack());
1345
1346 // Check that we get nothing back because the env said we should ignore endpoints
1347 let config = base_conf()
1348 .fs(fs.clone())
1349 .env(env)
1350 .profile_name("custom")
1351 .profile_files(
1352 #[allow(deprecated)]
1353 ProfileFiles::builder()
1354 .with_file(
1355 #[allow(deprecated)]
1356 ProfileFileKind::Config,
1357 "test_config",
1358 )
1359 .build(),
1360 )
1361 .load()
1362 .await;
1363 assert_eq!(None, config.endpoint_url());
1364
1365 // Check that without the env, we DO get something back
1366 let config = base_conf()
1367 .fs(fs)
1368 .profile_name("custom")
1369 .profile_files(
1370 #[allow(deprecated)]
1371 ProfileFiles::builder()
1372 .with_file(
1373 #[allow(deprecated)]
1374 ProfileFileKind::Config,
1375 "test_config",
1376 )
1377 .build(),
1378 )
1379 .load()
1380 .await;
1381 assert_eq!(Some("http://profile"), config.endpoint_url());
1382 }
1383
1384 #[tokio::test]
1385 async fn endpoint_urls_may_be_ignored_from_profile() {
1386 let fs = Fs::from_slice(&[(
1387 "test_config",
1388 "[profile custom]\nignore_configured_endpoint_urls = true",
1389 )]);
1390 let env = Env::from_slice(&[("AWS_ENDPOINT_URL", "http://environment")]);
1391
1392 // Check that we get nothing back because the profile said we should ignore endpoints
1393 let config = base_conf()
1394 .fs(fs)
1395 .env(env.clone())
1396 .profile_name("custom")
1397 .profile_files(
1398 #[allow(deprecated)]
1399 ProfileFiles::builder()
1400 .with_file(
1401 #[allow(deprecated)]
1402 ProfileFileKind::Config,
1403 "test_config",
1404 )
1405 .build(),
1406 )
1407 .load()
1408 .await;
1409 assert_eq!(None, config.endpoint_url());
1410
1411 // Check that without the profile, we DO get something back
1412 let config = base_conf().env(env).load().await;
1413 assert_eq!(Some("http://environment"), config.endpoint_url());
1414 }
1415
1416 #[tokio::test]
1417 async fn programmatic_endpoint_urls_may_not_be_ignored() {
1418 let fs = Fs::from_slice(&[(
1419 "test_config",
1420 "[profile custom]\nignore_configured_endpoint_urls = true",
1421 )]);
1422 let env = Env::from_slice(&[("AWS_IGNORE_CONFIGURED_ENDPOINT_URLS", "true")]);
1423
1424 // Check that we get something back because we explicitly set the loader's endpoint URL
1425 let config = base_conf()
1426 .fs(fs)
1427 .env(env)
1428 .endpoint_url("http://localhost")
1429 .profile_name("custom")
1430 .profile_files(
1431 #[allow(deprecated)]
1432 ProfileFiles::builder()
1433 .with_file(
1434 #[allow(deprecated)]
1435 ProfileFileKind::Config,
1436 "test_config",
1437 )
1438 .build(),
1439 )
1440 .load()
1441 .await;
1442 assert_eq!(Some("http://localhost"), config.endpoint_url());
1443 }
1444 }
1445}