Skip to content

Commit

Permalink
Set up a default connector for generic clients in the orchestrator im…
Browse files Browse the repository at this point in the history
…plementation (#2693)

## Motivation and Context
This PR makes generic clients default to the built-in rustls HTTPS
connector when no override is provided, but only for the new
orchestrator implementation.

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
  • Loading branch information
jdisanti authored May 26, 2023
1 parent c6e5379 commit d912326
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 37 deletions.
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repository = "https://github.com/awslabs/smithy-rs"

[features]
client-hyper = ["aws-smithy-client/client-hyper"]
rustls = ["aws-smithy-client/rustls"]
rustls = ["aws-smithy-client/rustls", "client-hyper"]
native-tls = []
allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI
rt-tokio = ["aws-smithy-async/rt-tokio", "tokio/rt"]
Expand Down
1 change: 1 addition & 0 deletions aws/rust-runtime/aws-config/external-types.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ allowed_external_types = [
"aws_sdk_sts::types::_policy_descriptor_type::PolicyDescriptorType",
"aws_smithy_async::rt::sleep::AsyncSleep",
"aws_smithy_client::bounds::SmithyConnector",
"aws_smithy_client::conns::default_connector::default_connector",
"aws_smithy_client::erase::DynConnector",
"aws_smithy_client::erase::boxclone::BoxCloneService",
"aws_smithy_client::http_connector::ConnectorSettings",
Expand Down
36 changes: 6 additions & 30 deletions aws/rust-runtime/aws-config/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,25 @@

//! Functionality related to creating new HTTP Connectors
use aws_smithy_async::rt::sleep::AsyncSleep;
use aws_smithy_client::erase::DynConnector;
use aws_smithy_client::http_connector::ConnectorSettings;
use std::sync::Arc;

// unused when all crate features are disabled
/// Unwrap an [`Option<DynConnector>`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None`
pub(crate) fn expect_connector(connector: Option<DynConnector>) -> DynConnector {
connector.expect("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this.")
}

#[cfg(feature = "client-hyper")]
pub use aws_smithy_client::conns::default_connector;

#[cfg(all(feature = "native-tls", not(feature = "allow-compilation")))]
compile_error!("Feature native-tls has been removed. For upgrade instructions, see: https://awslabs.github.io/smithy-rs/design/transport/connector.html");

#[cfg(feature = "rustls")]
fn base(
settings: &ConnectorSettings,
sleep: Option<Arc<dyn AsyncSleep>>,
) -> aws_smithy_client::hyper_ext::Builder {
let mut hyper =
aws_smithy_client::hyper_ext::Adapter::builder().connector_settings(settings.clone());
if let Some(sleep) = sleep {
hyper = hyper.sleep_impl(sleep);
}
hyper
}

/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated.
#[cfg(feature = "rustls")]
pub fn default_connector(
settings: &ConnectorSettings,
sleep: Option<Arc<dyn AsyncSleep>>,
) -> Option<DynConnector> {
tracing::trace!(settings = ?settings, sleep = ?sleep, "creating a new connector");
let hyper = base(settings, sleep).build(aws_smithy_client::conns::https());
Some(DynConnector::new(hyper))
}

/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated.
#[cfg(not(feature = "rustls"))]
#[cfg(not(feature = "client-hyper"))]
pub fn default_connector(
_settings: &ConnectorSettings,
_sleep: Option<Arc<dyn AsyncSleep>>,
_settings: &aws_smithy_client::http_connector::ConnectorSettings,
_sleep: Option<std::sync::Arc<dyn aws_smithy_async::rt::sleep::AsyncSleep>>,
) -> Option<DynConnector> {
None
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class ServiceRuntimePluginGenerator(
private val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext)
private val codegenScope = codegenContext.runtimeConfig.let { rc ->
val http = RuntimeType.smithyHttp(rc)
val client = RuntimeType.smithyClient(rc)
val runtime = RuntimeType.smithyRuntime(rc)
val runtimeApi = RuntimeType.smithyRuntimeApi(rc)
arrayOf(
Expand All @@ -77,6 +78,7 @@ class ServiceRuntimePluginGenerator(
"DefaultEndpointResolver" to runtime.resolve("client::orchestrator::endpoints::DefaultEndpointResolver"),
"DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"),
"HttpAuthSchemes" to runtimeApi.resolve("client::auth::HttpAuthSchemes"),
"HttpConnector" to client.resolve("http_connector::HttpConnector"),
"IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"),
"InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"),
"NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"),
Expand All @@ -85,6 +87,8 @@ class ServiceRuntimePluginGenerator(
"RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"),
"SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"),
"StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"),
"default_connector" to client.resolve("conns::default_connector"),
"require_connector" to client.resolve("conns::require_connector"),
)
}

Expand Down Expand Up @@ -121,15 +125,20 @@ class ServiceRuntimePluginGenerator(
#{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver()));
cfg.set_endpoint_resolver(endpoint_resolver);
// TODO(RuntimePlugins): Wire up standard retry
// TODO(enableNewSmithyRuntime): Wire up standard retry
cfg.set_retry_strategy(#{NeverRetryStrategy}::new());
// TODO(RuntimePlugins): Replace this with the correct long-term solution
let sleep_impl = self.handle.conf.sleep_impl();
let connection: #{Box}<dyn #{Connection}> = self.handle.conf.http_connector()
.and_then(move |c| c.connector(&#{ConnectorSettings}::default(), sleep_impl))
.map(|c| #{Box}::new(#{DynConnectorAdapter}::new(c)) as _)
.expect("connection set");
let timeout_config = self.handle.conf.timeout_config();
let connector_settings = timeout_config.map(|c| #{ConnectorSettings}::from_timeout_config(c)).unwrap_or_default();
let connection: #{Box}<dyn #{Connection}> = #{Box}::new(#{DynConnectorAdapter}::new(
// TODO(enableNewSmithyRuntime): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation
#{require_connector}(
self.handle.conf.http_connector()
.and_then(|c| c.connector(&connector_settings, sleep_impl.clone()))
.or_else(|| #{default_connector}(&connector_settings, sleep_impl))
)?
)) as _;
cfg.set_connection(connection);
#{additional_config}
Expand Down
60 changes: 60 additions & 0 deletions rust-runtime/aws-smithy-client/src/conns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

//! Type aliases for standard connection types.
use crate::erase::DynConnector;

#[cfg(feature = "rustls")]
/// A `hyper` connector that uses the `rustls` crate for TLS. To use this in a smithy client,
/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter).
Expand Down Expand Up @@ -49,6 +51,64 @@ lazy_static::lazy_static! {
};
}

mod default_connector {
use crate::erase::DynConnector;
use crate::http_connector::ConnectorSettings;
use aws_smithy_async::rt::sleep::AsyncSleep;
use std::sync::Arc;

#[cfg(feature = "rustls")]
fn base(
settings: &ConnectorSettings,
sleep: Option<Arc<dyn AsyncSleep>>,
) -> crate::hyper_ext::Builder {
let mut hyper = crate::hyper_ext::Adapter::builder().connector_settings(settings.clone());
if let Some(sleep) = sleep {
hyper = hyper.sleep_impl(sleep);
}
hyper
}

/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated.
pub fn default_connector(
settings: &ConnectorSettings,
sleep: Option<Arc<dyn AsyncSleep>>,
) -> Option<DynConnector> {
#[cfg(feature = "rustls")]
{
tracing::trace!(settings = ?settings, sleep = ?sleep, "creating a new default connector");
let hyper = base(settings, sleep).build(super::https());
Some(DynConnector::new(hyper))
}
#[cfg(not(feature = "rustls"))]
{
tracing::trace!(settings = ?settings, sleep = ?sleep, "no default connector available");
None
}
}
}
pub use default_connector::default_connector;

/// Error that indicates a connector is required.
#[non_exhaustive]
#[derive(Debug, Default)]
pub struct ConnectorRequiredError;

impl std::fmt::Display for ConnectorRequiredError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this.")
}
}

impl std::error::Error for ConnectorRequiredError {}

/// Converts an optional connector to a result.
pub fn require_connector(
connector: Option<DynConnector>,
) -> Result<DynConnector, ConnectorRequiredError> {
connector.ok_or(ConnectorRequiredError)
}

#[cfg(feature = "rustls")]
/// Return a default HTTPS connector backed by the `rustls` crate.
///
Expand Down

0 comments on commit d912326

Please sign in to comment.