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