Unverified Commit d9123266 authored by John DiSanti's avatar John DiSanti Committed by GitHub
Browse files

Set up a default connector for generic clients in the orchestrator implementation (#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._
parent c6e53796
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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"]
+1 −0
Original line number Diff line number Diff line
@@ -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",
+6 −30
Original line number Diff line number Diff line
@@ -5,10 +5,7 @@

//! 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`
@@ -16,38 +13,17 @@ 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
}
+15 −6
Original line number Diff line number Diff line
@@ -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(
@@ -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"),
@@ -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"),
        )
    }

@@ -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}
+60 −0
Original line number Diff line number Diff line
@@ -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).
@@ -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.
///