Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up a default connector for generic clients in the orchestrator implementation #2693

Merged
merged 7 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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