Skip to content

Commit

Permalink
Remove BV changes and init trust certs at client validation time
Browse files Browse the repository at this point in the history
  • Loading branch information
jdisanti committed Nov 29, 2023
1 parent c16ece9 commit 15ee8ae
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 148 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Validate the base client configuration.

This gets called upon client construction. The full config may not be available at
this time (hence why it has [`RuntimeComponentsBuilder`] as an argument rather
than [`RuntimeComponents`]). Any error returned here will become a panic
in the client constructor.

[`RuntimeComponentsBuilder`]: crate::client::runtime_components::RuntimeComponentsBuilder
[`RuntimeComponents`]: crate::client::runtime_components::RuntimeComponents
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Validate the final client configuration.

This gets called immediately after the [`Intercept::read_before_execution`] trait hook
when the final configuration has been resolved. Any error returned here will
cause the operation to return that error.

[`Intercept::read_before_execution`]: crate::client::interceptors::Intercept::read_before_execution
72 changes: 7 additions & 65 deletions rust-runtime/aws-smithy-runtime-api/src/client/behavior_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,15 @@
* SPDX-License-Identifier: Apache-2.0
*/

//! Behavior major-version of the client
//! Behavior Major version of the client

/// Behavior major-version of the client
///
/// Over time, new best-practice behaviors are introduced. However, these behaviors might not be backwards
/// compatible. For example, a change which introduces new default timeouts or a new retry-mode for
/// all operations might be the ideal behavior but could break existing applications.
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct BehaviorVersion {
version: Version,
}

#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
enum Version {
V2023_11_09,
V2023_11_27,
}
#[derive(Debug, Clone)]
pub struct BehaviorVersion {}

impl BehaviorVersion {
/// This method will always return the latest major version.
Expand All @@ -33,63 +25,13 @@ impl BehaviorVersion {
///
/// The latest version is currently [`BehaviorVersion::v2023_11_09`]
pub fn latest() -> Self {
Self {
version: Version::V2023_11_27,
}
}

/// This method returns the behavior configuration for November 27th, 2023
///
/// When a new behavior major version is released, this method will be deprecated.
///
/// # Changes
///
/// The default HTTP client is now lazy initialized so that the price of loading
/// trusted certificates isn't paid when overriding the default client. This means
/// that the first request after client initialization will be slower for the default
/// client as it needs to load those certs upon that first request.
///
/// This first request cost can be eliminated by priming the client immediately after
/// initialization, or by overriding the default HTTP client with one that doesn't lazy
/// initialize.
pub const fn v2023_11_27() -> Self {
Self {
version: Version::V2023_11_27,
}
Self {}
}

/// This method returns the behavior configuration for November 9th, 2023
///
/// This behavior version has been superceded by v2023_11_27.
#[deprecated(
note = "Superceded by v2023_11_27. See doc comment on v2023_11_27 for details on behavior changes."
)]
pub const fn v2023_11_09() -> Self {
Self {
version: Version::V2023_11_09,
}
}

/// True if this version is greater than or equal to the given `version`.
pub fn is_at_least(&self, version: BehaviorVersion) -> bool {
self >= &version
}

/// True if this version is before the given `version`.
pub fn is_before(&self, version: BehaviorVersion) -> bool {
self < &version
}
}

#[cfg(test)]
mod tests {
use super::*;

#[allow(deprecated)]
#[test]
fn comparison() {
assert!(BehaviorVersion::v2023_11_09() < BehaviorVersion::v2023_11_27());
assert!(BehaviorVersion::v2023_11_27() == BehaviorVersion::v2023_11_27());
assert!(BehaviorVersion::v2023_11_27() > BehaviorVersion::v2023_11_09());
/// When a new behavior major version is released, this method will be deprecated.
pub fn v2023_11_09() -> Self {
Self {}
}
}
43 changes: 41 additions & 2 deletions rust-runtime/aws-smithy-runtime-api/src/client/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@
//! [`tower`]: https://crates.io/crates/tower
//! [`aws-smithy-runtime`]: https://crates.io/crates/aws-smithy-runtime

use crate::box_error::BoxError;
use crate::client::orchestrator::{HttpRequest, HttpResponse};
use crate::client::result::ConnectorError;
use crate::client::runtime_components::sealed::ValidateConfig;
use crate::client::runtime_components::RuntimeComponents;
use crate::client::runtime_components::{RuntimeComponents, RuntimeComponentsBuilder};
use crate::impl_shared_conversions;
use aws_smithy_types::config_bag::ConfigBag;
use std::fmt;
use std::sync::Arc;
use std::time::Duration;
Expand Down Expand Up @@ -143,6 +145,26 @@ pub trait HttpClient: Send + Sync + fmt::Debug {
settings: &HttpConnectorSettings,
components: &RuntimeComponents,
) -> SharedHttpConnector;

#[doc = include_str!("../../rustdoc/validate_base_client_config.md")]
fn validate_base_client_config(
&self,
runtime_components: &RuntimeComponentsBuilder,
cfg: &ConfigBag,
) -> Result<(), BoxError> {
let _ = (runtime_components, cfg);
Ok(())
}

#[doc = include_str!("../../rustdoc/validate_final_config.md")]
fn validate_final_config(
&self,
runtime_components: &RuntimeComponents,
cfg: &ConfigBag,
) -> Result<(), BoxError> {
let _ = (runtime_components, cfg);
Ok(())
}
}

/// Shared HTTP client for use across multiple clients and requests.
Expand Down Expand Up @@ -170,7 +192,24 @@ impl HttpClient for SharedHttpClient {
}
}

impl ValidateConfig for SharedHttpClient {}
impl ValidateConfig for SharedHttpClient {
fn validate_base_client_config(
&self,
runtime_components: &super::runtime_components::RuntimeComponentsBuilder,
cfg: &aws_smithy_types::config_bag::ConfigBag,
) -> Result<(), crate::box_error::BoxError> {
self.selector
.validate_base_client_config(runtime_components, cfg)
}

fn validate_final_config(
&self,
runtime_components: &RuntimeComponents,
cfg: &aws_smithy_types::config_bag::ConfigBag,
) -> Result<(), crate::box_error::BoxError> {
self.selector.validate_final_config(runtime_components, cfg)
}
}

impl_shared_conversions!(convert SharedHttpClient from HttpClient using SharedHttpClient::new);

Expand Down
15 changes: 2 additions & 13 deletions rust-runtime/aws-smithy-runtime-api/src/client/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,7 @@ pub trait ResolveCachedIdentity: fmt::Debug + Send + Sync {
config_bag: &'a ConfigBag,
) -> IdentityFuture<'a>;

/// Validate the base client configuration for this implementation.
///
/// This gets called upon client construction. The full config may not be available at
/// this time (hence why it has [`RuntimeComponentsBuilder`] as an argument rather
/// than [`RuntimeComponents`]). Any error returned here will become a panic
/// in the client constructor.
#[doc = include_str!("../../rustdoc/validate_base_client_config.md")]
fn validate_base_client_config(
&self,
runtime_components: &RuntimeComponentsBuilder,
Expand All @@ -79,13 +74,7 @@ pub trait ResolveCachedIdentity: fmt::Debug + Send + Sync {
Ok(())
}

/// Validate the final client configuration for this implementation.
///
/// This gets called immediately after the [`Intercept::read_before_execution`] trait hook
/// when the final configuration has been resolved. Any error returned here will
/// cause the operation to return that error.
///
/// [`Intercept::read_before_execution`]: crate::client::interceptors::Intercept::read_before_execution
#[doc = include_str!("../../rustdoc/validate_final_config.md")]
fn validate_final_config(
&self,
runtime_components: &RuntimeComponents,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,7 @@ pub(crate) mod sealed {
/// This trait can be used to validate that certain required components or config values
/// are available, and provide an error with helpful instructions if they are not.
pub trait ValidateConfig: fmt::Debug + Send + Sync {
/// Validate the base client configuration.
///
/// This gets called upon client construction. The full config may not be available at
/// this time (hence why it has [`RuntimeComponentsBuilder`] as an argument rather
/// than [`RuntimeComponents`]). Any error returned here will become a panic
/// in the client constructor.
#[doc = include_str!("../../rustdoc/validate_base_client_config.md")]
fn validate_base_client_config(
&self,
runtime_components: &RuntimeComponentsBuilder,
Expand All @@ -59,11 +54,7 @@ pub(crate) mod sealed {
Ok(())
}

/// Validate the final client configuration.
///
/// This gets called immediately after the [`Intercept::read_before_execution`] trait hook
/// when the final configuration has been resolved. Any error returned here will
/// cause the operation to return that error.
#[doc = include_str!("../../rustdoc/validate_final_config.md")]
fn validate_final_config(
&self,
runtime_components: &RuntimeComponents,
Expand Down
30 changes: 1 addition & 29 deletions rust-runtime/aws-smithy-runtime/src/client/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,6 @@ pub fn default_http_client_plugin() -> Option<SharedRuntimePlugin> {
})
}

/// Runtime plugin that provides a default connector.
///
/// This connector is lazily created on first-user
pub fn default_http_client_plugin_lazy() -> Option<SharedRuntimePlugin> {
let _default: Option<SharedHttpClient> = None;
#[cfg(feature = "connector-hyper-0-14-x")]
let _default = crate::client::http::hyper_014::default_client_lazy();

_default.map(|default| {
default_plugin("default_http_client_plugin", |components| {
components.with_http_client(Some(default))
})
.into_shared()
})
}

/// Runtime plugin that provides a default async sleep implementation.
pub fn default_sleep_impl_plugin() -> Option<SharedRuntimePlugin> {
default_async_sleep().map(|default| {
Expand Down Expand Up @@ -262,26 +246,14 @@ impl DefaultPluginParams {
self.behavior_version = Some(version);
self
}

fn behavior_version(&self) -> BehaviorVersion {
self.behavior_version
.clone()
.unwrap_or(BehaviorVersion::latest())
}
}

/// All default plugins.
pub fn default_plugins(
params: DefaultPluginParams,
) -> impl IntoIterator<Item = SharedRuntimePlugin> {
// v2023_11_27 introduced lazy HTTP clients
let http_client_plugin = match params.behavior_version() >= BehaviorVersion::v2023_11_27() {
true => default_http_client_plugin_lazy(),
false => default_http_client_plugin(),
};

[
http_client_plugin,
default_http_client_plugin(),
default_identity_cache_plugin(),
default_retry_config_plugin(
params
Expand Down
47 changes: 19 additions & 28 deletions rust-runtime/aws-smithy-runtime/src/client/http/hyper_014.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ use aws_smithy_runtime_api::client::http::{
};
use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse};
use aws_smithy_runtime_api::client::result::ConnectorError;
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents;
use aws_smithy_runtime_api::client::runtime_components::{
RuntimeComponents, RuntimeComponentsBuilder,
};
use aws_smithy_runtime_api::shared::IntoShared;
use aws_smithy_types::body::SdkBody;
use aws_smithy_types::config_bag::ConfigBag;
use aws_smithy_types::error::display::DisplayErrorContext;
use aws_smithy_types::retry::ErrorKind;
use h2::Reason;
Expand Down Expand Up @@ -125,20 +128,6 @@ pub fn default_client() -> Option<SharedHttpClient> {
}
}

/// Creates a hyper-backed HTTPS client from defaults depending on what cargo features are activated.
pub fn default_client_lazy() -> Option<SharedHttpClient> {
#[cfg(feature = "tls-rustls")]
{
tracing::trace!("creating a new default hyper 0.14.x client");
Some(HyperClientBuilder::new().build_https_lazy())
}
#[cfg(not(feature = "tls-rustls"))]
{
tracing::trace!("no default connector available");
None
}
}

/// [`HttpConnector`] that uses [`hyper_0_14`] to make HTTP requests.
///
/// This connector also implements socket connect and read timeouts.
Expand Down Expand Up @@ -492,6 +481,20 @@ where
C::Future: Unpin + Send + 'static,
C::Error: Into<BoxError>,
{
fn validate_base_client_config(
&self,
_: &RuntimeComponentsBuilder,
_: &ConfigBag,
) -> Result<(), BoxError> {
// Initialize the TCP connector at this point so that native certs load
// at client initialization time instead of upon first request. We do it
// here rather than at construction so that it won't run if this is not
// the selected HTTP client for the base config (for example, if this was
// the default HTTP client, and it was overridden by a later plugin).
let _ = (self.tcp_connector_fn)();
Ok(())
}

fn http_connector(
&self,
settings: &HttpConnectorSettings,
Expand Down Expand Up @@ -565,14 +568,6 @@ impl HyperClientBuilder {
/// This client will immediately load trusted certificates.
#[cfg(feature = "tls-rustls")]
pub fn build_https(self) -> SharedHttpClient {
self.build(default_connector::https())
}

/// Create a lazy hyper client with the default rustls HTTPS implementation.
///
/// This client won't load trusted certificates until the first request is made.
#[cfg(feature = "tls-rustls")]
pub fn build_https_lazy(self) -> SharedHttpClient {
self.build_with_fn(default_connector::https)
}

Expand All @@ -590,11 +585,7 @@ impl HyperClientBuilder {
C::Future: Unpin + Send + 'static,
C::Error: Into<BoxError>,
{
SharedHttpClient::new(HyperClient {
connector_cache: RwLock::new(HashMap::new()),
client_builder: self.client_builder.unwrap_or_default(),
tcp_connector_fn: move || tcp_connector.clone(),
})
self.build_with_fn(move || tcp_connector.clone())
}

fn build_with_fn<C, F>(self, tcp_connector_fn: F) -> SharedHttpClient
Expand Down

0 comments on commit 15ee8ae

Please sign in to comment.