diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 164719bb6d..cf15871921 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -92,6 +92,99 @@ references = ["smithy-rs#1923"] meta = { "breaking" = false, "tada" = false, "bug" = false } author = "ysaito1001" +[[aws-sdk-rust]] +message = """ +
+The HTTP connector used when making requests is now configurable through `SdkConfig`. + +```rust +use std::time::Duration; +use aws_smithy_client::{Client, hyper_ext}; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::http_connector::ConnectorSettings; +use aws_types::SdkConfig; + +let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + .with_webpki_roots() + .https_only() + .enable_http1() + .enable_http2() + .build(); + +let smithy_connector = hyper_ext::Adapter::builder() + // Optionally set things like timeouts as well + .connector_settings( + ConnectorSettings::builder() + .connect_timeout(Duration::from_secs(5)) + .build() + ) + .build(https_connector); + +let sdk_config = aws_config::from_env() + .http_connector(smithy_connector) + .load() + .await; + +let client = Client::new(&sdk_config); + +// When sent, this operation will go through the custom smithy connector instead of +// the default HTTP connector. +let op = client + .get_object() + .bucket("some-test-bucket") + .key("test.txt") + .send() + .await + .unwrap(); +``` + +
+""" +references = ["smithy-rs#1225", "smithy-rs#1918"] +meta = { "breaking" = false, "tada" = true, "bug" = false } +author = "Velfi" + +[[aws-sdk-rust]] +message = """ +`::Client::from_conf_conn` has been removed since it's now possible to configure the connection from the +shared and service configs. To update your code, pass connections to the `http_connector` method during config creation. + +
+Example + +before: + +```rust + let conf = aws_sdk_sts::Config::builder() + // The builder has no defaults but setting other fields is omitted for brevity... + .build(); + let (server, request) = capture_request(None); + let client = aws_sdk_sts::Client::from_conf_conn(conf, server); +``` + +after: + +```rust + let (server, request) = capture_request(None); + let conf = aws_sdk_sts::Config::builder() + // The builder has no defaults but setting other fields is omitted for brevity... + .http_connector(server) + .build(); + let client = aws_sdk_sts::Client::from_conf(conf); +``` + +
+""" +references = ["smithy-rs#1225", "smithy-rs#1918"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "Velfi" + +[[aws-sdk-rust]] +message = "Add `to_vec` method to `aws_smithy_http::byte_stream::AggregatedBytes`." +references = ["smithy-rs#1918"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "Velfi" + [[aws-sdk-rust]] message = "Ability to add an inline policy or a list of policy ARNs to the `AssumeRoleProvider` builder." references = ["aws-sdk-rust#641", "smithy-rs#1892"] diff --git a/aws/SDK_CHANGELOG.next.json b/aws/SDK_CHANGELOG.next.json index 9fb8b3ef9d..08ffbf6682 100644 --- a/aws/SDK_CHANGELOG.next.json +++ b/aws/SDK_CHANGELOG.next.json @@ -341,4 +341,4 @@ } ], "aws-sdk-model": [] -} \ No newline at end of file +} diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 2cafa33a75..5e6727da45 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -17,21 +17,20 @@ rt-tokio = ["aws-smithy-async/rt-tokio", "tokio/rt"] default = ["client-hyper", "rustls", "rt-tokio"] [dependencies] -aws-sdk-sts = { path = "../../sdk/build/aws-sdk/sdk/sts", default-features = false } +aws-http = { path = "../../sdk/build/aws-sdk/sdk/aws-http" } aws-sdk-sso = { path = "../../sdk/build/aws-sdk/sdk/sso", default-features = false } +aws-sdk-sts = { path = "../../sdk/build/aws-sdk/sdk/sts", default-features = false } aws-smithy-async = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-async" } aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", default-features = false } +aws-smithy-http = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http" } +aws-smithy-http-tower = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http-tower" } +aws-smithy-json = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-json" } aws-smithy-types = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-types" } aws-types = { path = "../../sdk/build/aws-sdk/sdk/aws-types" } +hyper = { version = "0.14.12", default-features = false } time = { version = "0.3.4", features = ["parsing"] } tokio = { version = "1.8.4", features = ["sync"] } tracing = { version = "0.1" } -hyper = { version = "0.14.12", default-features = false } - -aws-http = { path = "../../sdk/build/aws-sdk/sdk/aws-http" } -aws-smithy-http = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http" } -aws-smithy-http-tower = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http-tower" } -aws-smithy-json = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-json" } # implementation detail of SSO credential caching ring = "0.16" diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index 3834902b6f..9c85536c88 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -12,6 +12,7 @@ allowed_external_types = [ "aws_smithy_client::http_connector::ConnectorSettings", "aws_smithy_http::body::SdkBody", "aws_smithy_http::result::SdkError", + "aws_smithy_http::endpoint", "aws_smithy_types::retry", "aws_smithy_types::retry::*", "aws_smithy_types::timeout", diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 6d4506fc4e..73a492bb26 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -91,6 +91,18 @@ //! # } //! ``` +pub use aws_smithy_http::endpoint; +// Re-export types from smithy-types +pub use aws_smithy_types::retry; +pub use aws_smithy_types::timeout; +// Re-export types from aws-types +pub use aws_types::{ + app_name::{AppName, InvalidAppName}, + SdkConfig, +}; +/// Load default sources for all configuration with override support +pub use loader::ConfigLoader; + #[allow(dead_code)] const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -132,16 +144,6 @@ pub mod connector; pub mod credential_process; -// Re-export types from smithy-types -pub use aws_smithy_types::retry; -pub use aws_smithy_types::timeout; - -// Re-export types from aws-types -pub use aws_types::{ - app_name::{AppName, InvalidAppName}, - SdkConfig, -}; - /// Create an environment loader for AWS Configuration /// /// # Examples @@ -162,13 +164,9 @@ pub async fn load_from_env() -> aws_types::SdkConfig { from_env().load().await } -/// Load default sources for all configuration with override support -pub use loader::ConfigLoader; - mod loader { use std::sync::Arc; - use crate::connector::default_connector; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; use aws_smithy_client::http_connector::{ConnectorSettings, HttpConnector}; use aws_smithy_types::retry::RetryConfig; @@ -178,6 +176,7 @@ mod loader { use aws_types::endpoint::ResolveAwsEndpoint; use aws_types::SdkConfig; + use crate::connector::default_connector; use crate::default_provider::{app_name, credentials, region, retry_config, timeout_config}; use crate::meta::region::ProvideRegion; use crate::provider_config::ProviderConfig; @@ -222,11 +221,13 @@ mod loader { /// /// # Examples /// ```no_run - /// # use aws_smithy_types::retry::RetryConfig; /// # async fn create_config() { - /// let config = aws_config::from_env() - /// .retry_config(RetryConfig::standard().with_max_attempts(2)) - /// .load().await; + /// use aws_config::retry::RetryConfig; + /// + /// let config = aws_config::from_env() + /// .retry_config(RetryConfig::standard().with_max_attempts(2)) + /// .load() + /// .await; /// # } /// ``` pub fn retry_config(mut self, retry_config: RetryConfig) -> Self { @@ -242,16 +243,16 @@ mod loader { /// ```no_run /// # use std::time::Duration; /// # async fn create_config() { - /// use aws_smithy_types::timeout::TimeoutConfig; + /// use aws_config::timeout::TimeoutConfig; /// - /// let config = aws_config::from_env() - /// .timeout_config( - /// TimeoutConfig::builder() - /// .operation_timeout(Duration::from_secs(5)) - /// .build() - /// ) - /// .load() - /// .await; + /// let config = aws_config::from_env() + /// .timeout_config( + /// TimeoutConfig::builder() + /// .operation_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .load() + /// .await; /// # } /// ``` pub fn timeout_config(mut self, timeout_config: TimeoutConfig) -> Self { @@ -267,9 +268,41 @@ mod loader { self } - /// Override the [`HttpConnector`] used to build [`SdkConfig`](aws_types::SdkConfig). - pub fn http_connector(mut self, http_connector: HttpConnector) -> Self { - self.http_connector = Some(http_connector); + /// Override the [`HttpConnector`] for this [`ConfigLoader`]. The connector will be used when + /// sending operations. This **does not set** the HTTP connector used by config providers. + /// To change that connector, use [ConfigLoader::configure]. + /// + /// ## Examples + /// ```no_run + /// # #[cfg(feature = "client-hyper")] + /// # async fn create_config() { + /// use std::time::Duration; + /// use aws_smithy_client::{Client, hyper_ext}; + /// use aws_smithy_client::erase::DynConnector; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// let sdk_config = aws_config::from_env() + /// .http_connector(smithy_connector) + /// .load() + /// .await; + /// # } + /// ``` + pub fn http_connector(mut self, http_connector: impl Into) -> Self { + self.http_connector = Some(http_connector.into()); self } @@ -308,11 +341,13 @@ mod loader { /// /// Use a static endpoint for all services /// ```no_run - /// # async fn doc() { - /// use aws_smithy_http::endpoint::Endpoint; + /// # async fn create_config() { + /// use aws_config::endpoint::Endpoint; + /// /// let sdk_config = aws_config::from_env() - /// .endpoint_resolver(Endpoint::immutable("http://localhost:1234".parse().expect("valid URI"))) - /// .load().await; + /// .endpoint_resolver(Endpoint::immutable("http://localhost:1234".parse().expect("valid URI"))) + /// .load() + /// .await; /// # } pub fn endpoint_resolver( mut self, @@ -325,12 +360,14 @@ mod loader { /// Set configuration for all sub-loaders (credentials, region etc.) /// /// Update the `ProviderConfig` used for all nested loaders. This can be used to override - /// the HTTPs connector used or to stub in an in memory `Env` or `Fs` for testing. + /// the HTTPs connector used by providers or to stub in an in memory `Env` or `Fs` for testing. + /// This **does not set** the HTTP connector used when sending operations. To change that + /// connector, use [ConfigLoader::http_connector]. /// /// # Examples /// ```no_run /// # #[cfg(feature = "hyper-client")] - /// # async fn docs() { + /// # async fn create_config() { /// use aws_config::provider_config::ProviderConfig; /// let custom_https_connector = hyper_rustls::HttpsConnectorBuilder::new(). /// with_webpki_roots() @@ -444,14 +481,15 @@ mod loader { #[cfg(test)] mod test { - use crate::from_env; - use crate::provider_config::ProviderConfig; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::never::NeverConnector; use aws_types::credentials::ProvideCredentials; use aws_types::os_shim_internal::Env; + use crate::from_env; + use crate::provider_config::ProviderConfig; + #[tokio::test] async fn provider_config_used() { let env = Env::from_slice(&[ diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 54b599ed29..755ccdba07 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -9,6 +9,8 @@ repository = "https://github.com/awslabs/smithy-rs" [features] hardcoded-credentials = [] +# This feature is to be used only for doc comments +examples = ["aws-smithy-client/client-hyper", "aws-smithy-client/rustls", "hyper-rustls"] [dependencies] aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } @@ -18,6 +20,10 @@ aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } tracing = "0.1" zeroize = "1" http = "0.2.6" +# cargo does not support optional test dependencies, so to completely disable rustls when +# the native-tls feature is enabled, we need to add the webpki-roots feature here. +# https://github.com/rust-lang/cargo/issues/1596 +hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2", "webpki-roots"] } [dev-dependencies] futures-util = "0.3.16" diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index 08849fe6ee..6fb916421a 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -359,15 +359,81 @@ impl Builder { self } - /// Sets the HTTP connector that clients will use to make HTTP requests. - pub fn http_connector(mut self, http_connector: HttpConnector) -> Self { + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// # #[cfg(feature = "examples")] + /// # fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::{Client, hyper_ext}; + /// use aws_smithy_client::erase::DynConnector; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use aws_types::SdkConfig; + /// + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// let sdk_config = SdkConfig::builder() + /// .http_connector(smithy_connector) + /// .build(); + /// # } + /// ``` + pub fn http_connector(mut self, http_connector: impl Into) -> Self { self.set_http_connector(Some(http_connector)); self } - /// Sets the HTTP connector that clients will use to make HTTP requests. - pub fn set_http_connector(&mut self, http_connector: Option) -> &mut Self { - self.http_connector = http_connector; + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// # #[cfg(feature = "examples")] + /// # fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::hyper_ext; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use aws_types::sdk_config::{SdkConfig, Builder}; + /// + /// fn override_http_connector(builder: &mut Builder) { + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// builder.set_http_connector(Some(smithy_connector)); + /// } + /// + /// let mut builder = SdkConfig::builder(); + /// override_http_connector(&mut builder); + /// let config = builder.build(); + /// # } + /// ``` + pub fn set_http_connector( + &mut self, + http_connector: Option>, + ) -> &mut Self { + self.http_connector = http_connector.map(|inner| inner.into()); self } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index aec623d221..8102ef579c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -35,6 +35,7 @@ val DECORATORS = listOf( ServiceConfigDecorator(), AwsPresigningDecorator(), AwsReadmeDecorator(), + HttpConnectorDecorator(), // Service specific decorators ApiGatewayDecorator(), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index bbdb9d80a5..2f0ea31f26 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -158,25 +158,7 @@ private class AwsFluentClientExtensions(types: Types) { writer.rustBlockTemplate("impl Client", *codegenScope) { rustTemplate( """ - /// Creates a client with the given service config and connector override. - pub fn from_conf_conn(conf: crate::Config, conn: C) -> Self - where - C: #{SmithyConnector} + Send + 'static, - E: Into<#{ConnectorError}>, - { - let retry_config = conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); - let timeout_config = conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); - let mut builder = #{aws_smithy_client}::Builder::new() - .connector(#{DynConnector}::new(conn)) - .middleware(#{DynMiddleware}::new(#{Middleware}::new())) - .retry_config(retry_config.into()) - .operation_timeout_config(timeout_config.into()); - builder.set_sleep_impl(conf.sleep_impl()); - let client = builder.build(); - Self { handle: std::sync::Arc::new(Handle { client, conf }) } - } - - /// Creates a new client from a shared config. + /// Creates a new client from an [SDK Config](#{aws_types}::sdk_config::SdkConfig). ##[cfg(any(feature = "rustls", feature = "native-tls"))] pub fn new(sdk_config: &#{aws_types}::sdk_config::SdkConfig) -> Self { Self::from_conf(sdk_config.into()) @@ -192,8 +174,26 @@ private class AwsFluentClientExtensions(types: Types) { panic!("An async sleep implementation is required for retries or timeouts to work. \ Set the `sleep_impl` on the Config passed into this function to fix this panic."); } - let mut builder = #{aws_smithy_client}::Builder::new() - .dyn_https_connector(#{ConnectorSettings}::from_timeout_config(&timeout_config)) + + let connector = conf.http_connector().and_then(|c| { + let timeout_config = conf + .timeout_config() + .cloned() + .unwrap_or_else(#{TimeoutConfig}::disabled); + let connector_settings = #{ConnectorSettings}::from_timeout_config( + &timeout_config, + ); + c.connector(&connector_settings, conf.sleep_impl()) + }); + + let builder = #{aws_smithy_client}::Builder::new(); + let builder = match connector { + // Use provided connector + Some(c) => builder.connector(c), + // Use default connector based on enabled features + None => builder.dyn_https_connector(#{ConnectorSettings}::from_timeout_config(&timeout_config)), + }; + let mut builder = builder .middleware(#{DynMiddleware}::new(#{Middleware}::new())) .retry_config(retry_config.into()) .operation_timeout_config(timeout_config.into()); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index a65cb4fcae..e7d418e33f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -262,19 +262,19 @@ class AwsPresignedFluentBuilderMethod( documentPresignedMethod(hasConfigArg = false) rustBlockTemplate( """ - pub async fn presigned( - self, - presigning_config: #{PresigningConfig}, - ) -> Result<#{PresignedRequest}, #{SdkError}<#{OpError}>> - """, + pub async fn presigned( + self, + presigning_config: #{PresigningConfig}, + ) -> Result<#{PresignedRequest}, #{SdkError}<#{OpError}>> + """, *codegenScope, "OpError" to section.operationErrorType, ) { rustTemplate( """ - let input = self.inner.build().map_err(|err| #{SdkError}::ConstructionFailure(err.into()))?; - input.presigned(&self.handle.conf, presigning_config).await - """, + let input = self.inner.build().map_err(|err| #{SdkError}::ConstructionFailure(err.into()))?; + input.presigned(&self.handle.conf, presigning_config).await + """, *codegenScope, ) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt new file mode 100644 index 0000000000..1957d5a208 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -0,0 +1,155 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.SmithyClient +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.asType +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext + +class HttpConnectorDecorator : RustCodegenDecorator { + override val name: String = "HttpConnectorDecorator" + override val order: Byte = 0 + + override fun supportsCodegenContext(clazz: Class): Boolean = + clazz.isAssignableFrom(ClientCodegenContext::class.java) + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List { + return baseCustomizations + HttpConnectorConfigCustomization(codegenContext) + } +} + +class HttpConnectorConfigCustomization( + codegenContext: CodegenContext, +) : ConfigCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val moduleUseName = codegenContext.moduleUseName() + private val codegenScope = arrayOf( + "HttpConnector" to SmithyClient(runtimeConfig).asType().member("http_connector::HttpConnector"), + ) + + override fun section(section: ServiceConfig): Writable { + return when (section) { + is ServiceConfig.ConfigStruct -> writable { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } + is ServiceConfig.ConfigImpl -> writable { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.http_connector.as_ref() + } + """, + *codegenScope, + ) + } + is ServiceConfig.BuilderStruct -> writable { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } + ServiceConfig.BuilderImpl -> writable { + rustTemplate( + """ + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::{Client, hyper_ext}; + /// use aws_smithy_client::erase::DynConnector; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use $moduleUseName::config::Config; + /// + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// ## } + /// ## } + /// ``` + pub fn http_connector(mut self, http_connector: impl Into<#{HttpConnector}>) -> Self { + self.http_connector = Some(http_connector.into()); + self + } + + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::hyper_ext; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use crate::sdk_config::{SdkConfig, Builder}; + /// use $moduleUseName::config::{Builder, Config}; + /// + /// fn override_http_connector(builder: &mut Builder) { + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// builder.set_http_connector(Some(smithy_connector)); + /// } + /// + /// let mut builder = $moduleUseName::Config::builder(); + /// override_http_connector(&mut builder); + /// let config = builder.build(); + /// ## } + /// ## } + /// ``` + pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { + self.http_connector = http_connector.map(|inner| inner.into()); + self + } + """, + *codegenScope, + ) + } + is ServiceConfig.BuilderBuild -> writable { + rust("http_connector: self.http_connector,") + } + else -> emptySection + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index 5a4135429c..eb7630d6c1 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -54,6 +54,7 @@ class SdkConfigDecorator : RustCodegenDecorator Config { +fn stub_config(conn: impl Into) -> Config { Config::builder() .region(Region::new("us-east-1")) .credentials_provider(Credentials::new("akid", "secret", None, None, "test")) + .http_connector(conn) .build() } @@ -25,7 +29,7 @@ fn stub_config() -> Config { #[tokio::test] async fn paginators_pass_args() { let (conn, request) = capture_request(None); - let client = Client::from_conf_conn(stub_config(), conn); + let client = Client::from_conf(stub_config(conn)); let mut paginator = client .scan() .table_name("test-table") @@ -88,7 +92,7 @@ async fn paginators_loop_until_completion() { ), ), ]); - let client = Client::from_conf_conn(stub_config(), conn.clone()); + let client = Client::from_conf(stub_config(conn.clone())); let mut paginator = client .scan() .table_name("test-table") @@ -150,7 +154,7 @@ async fn paginators_handle_errors() { }"#, ), )]); - let client = Client::from_conf_conn(stub_config(), conn.clone()); + let client = Client::from_conf(stub_config(conn.clone())); let mut rows = client .scan() .table_name("test-table") @@ -196,7 +200,7 @@ async fn paginators_stop_on_duplicate_token_by_default() { mk_response(response), ), ]); - let client = Client::from_conf_conn(stub_config(), conn.clone()); + let client = Client::from_conf(stub_config(conn.clone())); let mut rows = client .scan() .table_name("test-table") @@ -255,7 +259,7 @@ async fn paginators_can_continue_on_duplicate_token() { mk_response(response), ), ]); - let client = Client::from_conf_conn(stub_config(), conn.clone()); + let client = Client::from_conf(stub_config(conn.clone())); let mut rows = client .scan() .table_name("test-table") diff --git a/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs b/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs index 7d57882e30..14f1b07299 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/shared-config.rs @@ -12,11 +12,12 @@ async fn shared_config_testbed() { let shared_config = aws_types::SdkConfig::builder() .region(Region::new("us-east-4")) .build(); + let (conn, request) = aws_smithy_client::test_connection::capture_request(None); let conf = aws_sdk_dynamodb::config::Builder::from(&shared_config) .credentials_provider(Credentials::new("asdf", "asdf", None, None, "test")) + .http_connector(conn) .build(); - let (conn, request) = aws_smithy_client::test_connection::capture_request(None); - let svc = aws_sdk_dynamodb::Client::from_conf_conn(conf, conn); + let svc = aws_sdk_dynamodb::Client::from_conf(conf); let _ = svc.list_tables().send().await; assert_eq!( request.expect_request().uri(), diff --git a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs index 170822acc8..9a09673335 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +use std::sync::Arc; +use std::time::Duration; + use aws_sdk_dynamodb::types::SdkError; use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; @@ -11,8 +14,6 @@ use aws_smithy_types::timeout::TimeoutConfig; use aws_types::credentials::SharedCredentialsProvider; use aws_types::region::Region; use aws_types::{Credentials, SdkConfig}; -use std::sync::Arc; -use std::time::Duration; #[derive(Debug, Clone)] struct InstantSleep; @@ -27,6 +28,7 @@ async fn api_call_timeout_retries() { let conn = NeverConnector::new(); let conf = SdkConfig::builder() .region(Region::new("us-east-2")) + .http_connector(conn.clone()) .credentials_provider(SharedCredentialsProvider::new(Credentials::new( "stub", "stub", None, None, "test", ))) @@ -38,10 +40,7 @@ async fn api_call_timeout_retries() { .retry_config(RetryConfig::standard()) .sleep_impl(Arc::new(InstantSleep)) .build(); - let client = aws_sdk_dynamodb::Client::from_conf_conn( - aws_sdk_dynamodb::Config::new(&conf), - conn.clone(), - ); + let client = aws_sdk_dynamodb::Client::from_conf(aws_sdk_dynamodb::Config::new(&conf)); let resp = client .list_tables() .send() @@ -64,6 +63,7 @@ async fn no_retries_on_operation_timeout() { let conn = NeverConnector::new(); let conf = SdkConfig::builder() .region(Region::new("us-east-2")) + .http_connector(conn.clone()) .credentials_provider(SharedCredentialsProvider::new(Credentials::new( "stub", "stub", None, None, "test", ))) @@ -75,10 +75,7 @@ async fn no_retries_on_operation_timeout() { .retry_config(RetryConfig::standard()) .sleep_impl(Arc::new(InstantSleep)) .build(); - let client = aws_sdk_dynamodb::Client::from_conf_conn( - aws_sdk_dynamodb::Config::new(&conf), - conn.clone(), - ); + let client = aws_sdk_dynamodb::Client::from_conf(aws_sdk_dynamodb::Config::new(&conf)); let resp = client .list_tables() .send() diff --git a/aws/sdk/integration-tests/ec2/tests/paginators.rs b/aws/sdk/integration-tests/ec2/tests/paginators.rs index abb0746ae5..43fe53bce1 100644 --- a/aws/sdk/integration-tests/ec2/tests/paginators.rs +++ b/aws/sdk/integration-tests/ec2/tests/paginators.rs @@ -3,14 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ +use tokio_stream::StreamExt; + use aws_sdk_ec2::{model::InstanceType, Client, Config, Credentials, Region}; +use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_client::test_connection::TestConnection; -use tokio_stream::StreamExt; -fn stub_config() -> Config { +fn stub_config(conn: impl Into) -> Config { Config::builder() .region(Region::new("us-east-1")) .credentials_provider(Credentials::new("akid", "secret", None, None, "test")) + .http_connector(conn) .build() } @@ -36,7 +39,7 @@ async fn paginators_handle_empty_tokens() { .body(response) .unwrap(), )]); - let client = Client::from_conf_conn(stub_config(), conn.clone()); + let client = Client::from_conf(stub_config(conn.clone())); let instance_type = InstanceType::from("g5.48xlarge"); let mut paginator = client .describe_spot_price_history() @@ -72,7 +75,7 @@ async fn paginators_handle_unset_tokens() { .body(response) .unwrap(), )]); - let client = Client::from_conf_conn(stub_config(), conn.clone()); + let client = Client::from_conf(stub_config(conn.clone())); let instance_type = InstanceType::from("g5.48xlarge"); let mut paginator = client .describe_spot_price_history() diff --git a/aws/sdk/integration-tests/glacier/tests/custom-headers.rs b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs index 15f6169c85..d336dd1855 100644 --- a/aws/sdk/integration-tests/glacier/tests/custom-headers.rs +++ b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs @@ -14,9 +14,10 @@ async fn set_correct_headers() { let conf = aws_sdk_glacier::Config::builder() .region(Region::new("us-east-1")) .credentials_provider(Credentials::new("key", "secret", None, None, "test")) + .http_connector(conn) .build(); - let client = aws_sdk_glacier::Client::from_conf_conn(conf, conn); + let client = aws_sdk_glacier::Client::from_conf(conf); let _resp = client .upload_archive() .vault_name("vault") diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs index df04b3c569..c11e931b7b 100644 --- a/aws/sdk/integration-tests/kms/tests/integration.rs +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -43,8 +43,9 @@ async fn generate_random_cn() { let conf = Config::builder() .region(Region::new("cn-north-1")) .credentials_provider(creds) + .http_connector(conn.clone()) .build(); - let client = kms::Client::from_conf_conn(conf, conn.clone()); + let client = kms::Client::from_conf(conf); let _ = client .generate_random() .number_of_bytes(64) diff --git a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs index 1526a6d32a..8682bc7ace 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -82,11 +82,12 @@ async fn timeout_test(sleep_impl: Arc) -> Result<(), Box) -> Result<(), Box) -> Result<(), Box = CoreClient; +use std::{ + convert::Infallible, + time::{Duration, UNIX_EPOCH}, +}; /// Test connection for the movies IT /// headers are signed with actual creds, at some point we could replace them with verifiable test @@ -65,37 +52,43 @@ async fn test_checksum_on_streaming_response( checksum_header_name: &'static str, checksum_header_value: &'static str, ) -> GetObjectOutput { - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) - .region(Region::new("us-east-1")) - .build(); let conn = new_checksum_validated_response_test_connection( checksum_header_name, checksum_header_value, ); - let client = Client::new(conn.clone()); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) + .region(Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); - let mut op = GetObject::builder() + let client = Client::new(&sdk_config); + + let res = client + .get_object() .bucket("some-test-bucket") .key("test.txt") .checksum_mode(aws_sdk_s3::model::ChecksumMode::Enabled) - .build() + .customize() + .await + .unwrap() + .map_operation(|mut op| { + op.properties_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); + op.properties_mut().insert(AwsUserAgent::for_tests()); + + Result::Ok::<_, Infallible>(op) + }) .unwrap() - .make_operation(&conf) + .send() .await .unwrap(); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - let res = client.call(op).await.unwrap(); conn.assert_requests_match(&[http::header::HeaderName::from_static("x-amz-checksum-mode")]); @@ -164,21 +157,20 @@ async fn test_checksum_on_streaming_request<'a>( expected_encoded_content_length: &'a str, expected_aws_chunked_encoded_body: &'a str, ) { - let creds = aws_sdk_s3::Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) - .region(aws_sdk_s3::Region::new("us-east-1")) - .build(); let (conn, rcvr) = capture_request(None); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) + .region(Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); - let client: aws_smithy_client::Client<_, aws_sdk_s3::middleware::DefaultMiddleware> = - aws_smithy_client::Client::new(conn.clone()); + let client = Client::new(&sdk_config); // ByteStreams created from a file are streaming and have a known size let mut file = tempfile::NamedTempFile::new().unwrap(); @@ -192,23 +184,29 @@ async fn test_checksum_on_streaming_request<'a>( .await .unwrap(); - let mut op = PutObject::builder() + // The response from the fake connection won't return the expected XML but we don't care about + // that error in this test + let _ = client + .put_object() .bucket("test-bucket") .key("test.txt") .body(body) .checksum_algorithm(checksum_algorithm) - .build() + .customize() + .await + .unwrap() + .map_operation(|mut op| { + op.properties_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); + op.properties_mut().insert(AwsUserAgent::for_tests()); + + Result::Ok::<_, Infallible>(op) + }) .unwrap() - .make_operation(&conf) + .send() .await - .expect("failed to construct operation"); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); + .unwrap(); - // The response from the fake connection won't return the expected XML but we don't care about - // that error in this test - let _ = client.call(op).await; let req = rcvr.expect_request(); let headers = req.headers(); diff --git a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs index b5ed6787d2..e68b07a8da 100644 --- a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs +++ b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs @@ -3,32 +3,31 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_config::SdkConfig; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{Credentials, Region}; -use aws_smithy_async::rt::sleep::TokioSleep; +use aws_sdk_s3::{Client, Credentials, Region}; use aws_smithy_client::test_connection::capture_request; +use aws_types::credentials::SharedCredentialsProvider; use std::convert::Infallible; -use std::sync::Arc; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] -async fn test_s3_ops_are_customizable() -> Result<(), aws_sdk_s3::Error> { - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) +async fn test_s3_ops_are_customizable() { + let (conn, rcvr) = capture_request(None); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) .region(Region::new("us-east-1")) - .sleep_impl(Arc::new(TokioSleep::new())) + .http_connector(conn.clone()) .build(); - let (conn, rcvr) = capture_request(None); - let client = aws_sdk_s3::Client::from_conf_conn(conf, conn); + let client = Client::new(&sdk_config); let op = client .list_buckets() @@ -70,6 +69,4 @@ async fn test_s3_ops_are_customizable() -> Result<(), aws_sdk_s3::Error> { auth_header.to_str().unwrap(), snapshot_signature ); - - Ok(()) } diff --git a/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs b/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs index 6c1f6db5c0..beabefea95 100644 --- a/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs +++ b/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs @@ -4,22 +4,19 @@ */ use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::{ - middleware::DefaultMiddleware, model::ObjectAttributes, operation::GetObjectAttributes, - Credentials, Region, -}; -use aws_smithy_client::{test_connection::TestConnection, Client as CoreClient}; +use aws_sdk_s3::{model::ObjectAttributes, Client, Credentials, Region}; +use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; -use std::time::{Duration, UNIX_EPOCH}; - -pub type Client = CoreClient; +use aws_types::{credentials::SharedCredentialsProvider, SdkConfig}; +use std::{ + convert::Infallible, + time::{Duration, UNIX_EPOCH}, +}; const RESPONSE_BODY_XML: &[u8] = b"\ne1AsOh9IyGCa4hLN+2Od7jlnP14="; #[tokio::test] async fn ignore_invalid_xml_body_root() { - tracing_subscriber::fmt::init(); - let conn = TestConnection::new(vec![ (http::Request::builder() .header("x-amz-object-attributes", "Checksum") @@ -45,35 +42,39 @@ async fn ignore_invalid_xml_body_root() { .body(RESPONSE_BODY_XML) .unwrap()) ]); - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) + + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) .region(Region::new("us-east-1")) + .http_connector(conn.clone()) .build(); - let client = Client::new(conn.clone()); + let client = Client::new(&sdk_config); - let mut op = GetObjectAttributes::builder() + let _ = client + .get_object_attributes() .bucket("some-test-bucket") .key("test.txt") .object_attributes(ObjectAttributes::Checksum) - .build() + .customize() + .await + .unwrap() + .map_operation(|mut op| { + op.properties_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); + op.properties_mut().insert(AwsUserAgent::for_tests()); + + Result::Ok::<_, Infallible>(op) + }) .unwrap() - .make_operation(&conf) + .send() .await .unwrap(); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - let res = client.call(op).await.unwrap(); conn.assert_requests_match(&[]); - - println!("res: {:#?}", res) } diff --git a/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs b/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs index 8b4e90c2e3..05259f9774 100644 --- a/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs +++ b/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs @@ -4,16 +4,14 @@ */ use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::middleware::DefaultMiddleware; -use aws_sdk_s3::operation::PutObject; -use aws_sdk_s3::types::ByteStream; -use aws_sdk_s3::{Credentials, Region}; +use aws_sdk_s3::{types::ByteStream, Client, Credentials, Region}; use aws_smithy_client::test_connection::capture_request; -use aws_smithy_client::Client as CoreClient; +use aws_types::{credentials::SharedCredentialsProvider, SdkConfig}; use http::HeaderValue; -use std::time::UNIX_EPOCH; -use tokio::time::Duration; -pub type Client = CoreClient; +use std::{ + convert::Infallible, + time::{Duration, UNIX_EPOCH}, +}; const NAUGHTY_STRINGS: &str = include_str!("blns/blns.txt"); @@ -52,22 +50,23 @@ const NAUGHTY_STRINGS: &str = include_str!("blns/blns.txt"); // } #[tokio::test] -async fn test_s3_signer_with_naughty_string_metadata() -> Result<(), aws_sdk_s3::Error> { - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) +async fn test_s3_signer_with_naughty_string_metadata() { + let (conn, rcvr) = capture_request(None); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) .region(Region::new("us-east-1")) + .http_connector(conn.clone()) .build(); - let (conn, rcvr) = capture_request(None); - let client = Client::new(conn.clone()); - let mut builder = PutObject::builder() + let client = Client::new(&sdk_config); + let mut builder = client + .put_object() .bucket("test-bucket") .key("text.txt") .body(ByteStream::from_static(b"some test text")); @@ -82,17 +81,21 @@ async fn test_s3_signer_with_naughty_string_metadata() -> Result<(), aws_sdk_s3: } } - let mut op = builder - .build() + let _ = builder + .customize() + .await + .unwrap() + .map_operation(|mut op| { + op.properties_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); + op.properties_mut().insert(AwsUserAgent::for_tests()); + + Result::Ok::<_, Infallible>(op) + }) .unwrap() - .make_operation(&conf) + .send() .await .unwrap(); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - client.call(op).await.unwrap(); let expected_req = rcvr.expect_request(); let auth_header = expected_req @@ -113,6 +116,4 @@ async fn test_s3_signer_with_naughty_string_metadata() -> Result<(), aws_sdk_s3: auth_header.to_str().unwrap(), snapshot_signature ); - - Ok(()) } diff --git a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs index 8a898524e8..73584521e0 100644 --- a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs +++ b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs @@ -3,51 +3,53 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_config::SdkConfig; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::middleware::DefaultMiddleware; -use aws_sdk_s3::operation::ListObjectsV2; -use aws_sdk_s3::{Credentials, Region}; +use aws_sdk_s3::{Client, Credentials, Region}; use aws_smithy_client::test_connection::capture_request; -use aws_smithy_client::Client as CoreClient; +use aws_types::credentials::SharedCredentialsProvider; +use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; -pub type Client = CoreClient; - #[tokio::test] -async fn test_s3_signer_query_string_with_all_valid_chars() -> Result<(), aws_sdk_s3::Error> { - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) +async fn test_s3_signer_query_string_with_all_valid_chars() { + let (conn, rcvr) = capture_request(None); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) .region(Region::new("us-east-1")) + .http_connector(conn.clone()) .build(); - let (conn, rcvr) = capture_request(None); - let client = Client::new(conn.clone()); + let client = Client::new(&sdk_config); // Generate a string containing all printable ASCII chars let prefix: String = (32u8..127).map(char::from).collect(); - let mut op = ListObjectsV2::builder() + // The response from the fake connection won't return the expected XML but we don't care about + // that error in this test + let _ = client + .list_objects_v2() .bucket("test-bucket") .prefix(&prefix) - .build() - .unwrap() - .make_operation(&conf) + .customize() .await - .expect("failed to construct operation"); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); + .unwrap() + .map_operation(|mut op| { + op.properties_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); + op.properties_mut().insert(AwsUserAgent::for_tests()); - // The response from the fake connection won't return the expected XML but we don't care about - // that error in this test - let _ = client.call(op).await; + Result::Ok::<_, Infallible>(op) + }) + .unwrap() + .send() + .await; let expected_req = rcvr.expect_request(); let auth_header = expected_req @@ -68,21 +70,19 @@ async fn test_s3_signer_query_string_with_all_valid_chars() -> Result<(), aws_sd auth_header.to_str().unwrap(), snapshot_signature ); - - Ok(()) } // This test can help identify individual characters that break the signing of query strings. This // test must be run against an actual bucket so we `ignore` it unless the runner specifically requests it #[tokio::test] #[ignore] -async fn test_query_strings_are_correctly_encoded() -> Result<(), aws_sdk_s3::Error> { +async fn test_query_strings_are_correctly_encoded() { use aws_sdk_s3::error::{ListObjectsV2Error, ListObjectsV2ErrorKind}; use aws_smithy_http::result::SdkError; tracing_subscriber::fmt::init(); let config = aws_config::load_from_env().await; - let client = aws_sdk_s3::Client::new(&config); + let client = Client::new(&config); let mut chars_that_break_signing = Vec::new(); let mut chars_that_break_uri_parsing = Vec::new(); @@ -128,41 +128,42 @@ async fn test_query_strings_are_correctly_encoded() -> Result<(), aws_sdk_s3::Er && chars_that_break_uri_parsing.is_empty() && chars_that_are_invalid_arguments.is_empty() { - Ok(()) - } else { - fn char_transform(c: u8) -> String { - format!("byte {}: {}\n", c, char::from(c)) - } - if !chars_that_break_signing.is_empty() { - tracing::error!( - "The following characters caused a signature mismatch:\n{}(end)", - chars_that_break_signing - .clone() - .into_iter() - .map(char_transform) - .collect::() - ); - } - if !chars_that_break_uri_parsing.is_empty() { - tracing::error!( - "The following characters caused a URI parse failure:\n{}(end)", - chars_that_break_uri_parsing - .clone() - .into_iter() - .map(char_transform) - .collect::() - ); - } - if !chars_that_are_invalid_arguments.is_empty() { - tracing::error!( - "The following characters caused an \"Invalid Argument\" failure:\n{}(end)", - chars_that_are_invalid_arguments - .clone() - .into_iter() - .map(char_transform) - .collect::() - ); - } - panic!("test failed, see logs for the problem chars") + return; } + + fn char_transform(c: u8) -> String { + format!("byte {}: {}\n", c, char::from(c)) + } + if !chars_that_break_signing.is_empty() { + eprintln!( + "The following characters caused a signature mismatch:\n{}(end)", + chars_that_break_signing + .clone() + .into_iter() + .map(char_transform) + .collect::() + ); + } + if !chars_that_break_uri_parsing.is_empty() { + eprintln!( + "The following characters caused a URI parse failure:\n{}(end)", + chars_that_break_uri_parsing + .clone() + .into_iter() + .map(char_transform) + .collect::() + ); + } + if !chars_that_are_invalid_arguments.is_empty() { + eprintln!( + "The following characters caused an \"Invalid Argument\" failure:\n{}(end)", + chars_that_are_invalid_arguments + .clone() + .into_iter() + .map(char_transform) + .collect::() + ); + } + + panic!("test failed due to invalid characters") } diff --git a/aws/sdk/integration-tests/s3/tests/recursion-detection.rs b/aws/sdk/integration-tests/s3/tests/recursion-detection.rs index 07a1f2eaed..fb7f70db5f 100644 --- a/aws/sdk/integration-tests/s3/tests/recursion-detection.rs +++ b/aws/sdk/integration-tests/s3/tests/recursion-detection.rs @@ -3,8 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::{Credentials, Region}; +use aws_config::SdkConfig; +use aws_sdk_s3::{Client, Credentials, Region}; use aws_smithy_client::test_connection::capture_request; +use aws_types::credentials::SharedCredentialsProvider; use http::HeaderValue; #[tokio::test] @@ -12,20 +14,19 @@ async fn recursion_detection_applied() { std::env::set_var("AWS_LAMBDA_FUNCTION_NAME", "some-function"); std::env::set_var("_X_AMZN_TRACE_ID", "traceid"); let (conn, captured_request) = capture_request(None); - - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) .region(Region::new("us-east-1")) + .http_connector(conn.clone()) .build(); - let client = aws_sdk_s3::Client::from_conf_conn(conf, conn); - let _response = client.list_objects_v2().bucket("test-bucket").send().await; + let client = Client::new(&sdk_config); + let _ = client.list_objects_v2().bucket("test-bucket").send().await; assert_eq!( captured_request .expect_request() diff --git a/aws/sdk/integration-tests/s3/tests/select-object-content.rs b/aws/sdk/integration-tests/s3/tests/select-object-content.rs index d882b7644f..69e81c4948 100644 --- a/aws/sdk/integration-tests/s3/tests/select-object-content.rs +++ b/aws/sdk/integration-tests/s3/tests/select-object-content.rs @@ -3,28 +3,30 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_config::SdkConfig; use aws_sdk_s3::model::{ CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization, OutputSerialization, SelectObjectContentEventStream, }; -use aws_sdk_s3::{Client, Config, Credentials, Region}; +use aws_sdk_s3::{Client, Credentials, Region}; use aws_smithy_client::dvr::{Event, ReplayingConnection}; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; -use std::error::Error as StdError; +use aws_types::credentials::SharedCredentialsProvider; +use std::error::Error; #[tokio::test] async fn test_success() { let events: Vec = serde_json::from_str(include_str!("select-object-content.json")).unwrap(); let replayer = ReplayingConnection::new(events); - - let region = Region::from_static("us-east-2"); - let credentials = Credentials::new("test", "test", None, None, "test"); - let config = Config::builder() - .region(region) - .credentials_provider(credentials) + let sdk_config = SdkConfig::builder() + .region(Region::from_static("us-east-2")) + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "test", "test", None, None, "test", + ))) + .http_connector(replayer.clone()) .build(); - let client = Client::from_conf_conn(config, replayer.clone()); + let client = Client::new(&sdk_config); let mut output = client .select_object_content() @@ -88,7 +90,7 @@ async fn test_success() { .unwrap(); } -fn body_validator(expected_body: &[u8], actual_body: &[u8]) -> Result<(), Box> { +fn body_validator(expected_body: &[u8], actual_body: &[u8]) -> Result<(), Box> { let expected = std::str::from_utf8(expected_body).unwrap(); let actual = std::str::from_utf8(actual_body).unwrap(); assert_ok(validate_body(actual, expected, MediaType::Xml)); diff --git a/aws/sdk/integration-tests/s3/tests/signing-it.rs b/aws/sdk/integration-tests/s3/tests/signing-it.rs index d72c093ade..45f200f6e3 100644 --- a/aws/sdk/integration-tests/s3/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3/tests/signing-it.rs @@ -3,29 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_config::SdkConfig; use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::middleware::DefaultMiddleware; -use aws_sdk_s3::operation::ListObjectsV2; -use aws_sdk_s3::{Credentials, Region}; +use aws_sdk_s3::{Client, Credentials, Region}; use aws_smithy_client::test_connection::TestConnection; -use aws_smithy_client::Client as CoreClient; use aws_smithy_http::body::SdkBody; +use aws_types::credentials::SharedCredentialsProvider; +use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; -pub type Client = CoreClient; #[tokio::test] -async fn test_signer() -> Result<(), aws_sdk_s3::Error> { - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) - .region(Region::new("us-east-1")) - .build(); +async fn test_signer() { let conn = TestConnection::new(vec![( http::Request::builder() .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=6233614b69271e15db079287874a654183916e509909b5719b00cd8d5f31299e") @@ -34,20 +22,35 @@ async fn test_signer() -> Result<(), aws_sdk_s3::Error> { .unwrap(), http::Response::builder().status(200).body("").unwrap(), )]); - let client = Client::new(conn.clone()); - let mut op = ListObjectsV2::builder() + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) + .region(Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); + let client = Client::new(&sdk_config); + let _ = client + .list_objects_v2() .bucket("test-bucket") .prefix("prefix~") - .build() - .unwrap() - .make_operation(&conf) + .customize() .await - .unwrap(); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); + .unwrap() + .map_operation(|mut op| { + op.properties_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); + op.properties_mut().insert(AwsUserAgent::for_tests()); + + Result::Ok::<_, Infallible>(op) + }) + .unwrap() + .send() + .await; - client.call(op).await.expect_err("empty response"); conn.assert_requests_match(&[]); - Ok(()) } diff --git a/aws/sdk/integration-tests/s3/tests/streaming-response.rs b/aws/sdk/integration-tests/s3/tests/streaming-response.rs index 6105fc6d7a..0b5d17a95b 100644 --- a/aws/sdk/integration-tests/s3/tests/streaming-response.rs +++ b/aws/sdk/integration-tests/s3/tests/streaming-response.rs @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::{Credentials, Endpoint, Region}; +use aws_config::SdkConfig; +use aws_sdk_s3::{Client, Credentials, Endpoint, Region}; +use aws_types::credentials::SharedCredentialsProvider; use bytes::BytesMut; use std::future::Future; use std::net::SocketAddr; @@ -21,23 +23,21 @@ async fn test_streaming_response_fails_when_eof_comes_before_content_length_reac let (server, server_addr) = start_faulty_server().await; let _ = tokio::spawn(server); - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) .region(Region::new("us-east-1")) .endpoint_resolver(Endpoint::immutable( format!("http://{server_addr}").parse().expect("valid URI"), )) .build(); - let client = aws_sdk_s3::client::Client::from_conf(conf); + let client = Client::new(&sdk_config); // This will succeed b/c the head of the response is fine. let res = client diff --git a/aws/sdk/integration-tests/s3/tests/timeouts.rs b/aws/sdk/integration-tests/s3/tests/timeouts.rs index f444c63c22..5a8cd08dba 100644 --- a/aws/sdk/integration-tests/s3/tests/timeouts.rs +++ b/aws/sdk/integration-tests/s3/tests/timeouts.rs @@ -8,14 +8,14 @@ use aws_sdk_s3::model::{ CompressionType, CsvInput, CsvOutput, ExpressionType, FileHeaderInfo, InputSerialization, OutputSerialization, }; -use aws_sdk_s3::{Client, Config, Credentials, Endpoint, Region}; +use aws_sdk_s3::{Client, Credentials, Endpoint, Region}; use aws_smithy_async::assert_elapsed; -use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, TokioSleep}; -use aws_smithy_client::never::NeverService; -use aws_smithy_http::body::SdkBody; -use aws_smithy_http::result::ConnectorError; +use aws_smithy_async::rt::sleep::{default_async_sleep, TokioSleep}; +use aws_smithy_client::never::NeverConnector; use aws_smithy_types::timeout::TimeoutConfig; use aws_types::credentials::SharedCredentialsProvider; +use std::future::Future; +use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use tokio::net::TcpListener; @@ -23,21 +23,20 @@ use tokio::time::timeout; #[tokio::test(start_paused = true)] async fn test_timeout_service_ends_request_that_never_completes() { - let conn: NeverService, http::Response, ConnectorError> = - NeverService::new(); - let region = Region::from_static("us-east-2"); - let credentials = Credentials::new("test", "test", None, None, "test"); - let timeout_config = TimeoutConfig::builder() - .operation_timeout(Duration::from_secs_f32(0.5)) - .build(); - let sleep_impl: Arc = Arc::new(TokioSleep::new()); - let config = Config::builder() - .region(region) - .credentials_provider(credentials) - .timeout_config(timeout_config) - .sleep_impl(sleep_impl) + let sdk_config = SdkConfig::builder() + .region(Region::from_static("us-east-2")) + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "test", "test", None, None, "test", + ))) + .http_connector(NeverConnector::new()) + .timeout_config( + TimeoutConfig::builder() + .operation_timeout(Duration::from_secs_f32(0.5)) + .build(), + ) + .sleep_impl(Arc::new(TokioSleep::new())) .build(); - let client = Client::from_conf_conn(config, conn.clone()); + let client = Client::new(&sdk_config); let now = tokio::time::Instant::now(); @@ -72,18 +71,29 @@ async fn test_timeout_service_ends_request_that_never_completes() { #[tokio::test] async fn test_read_timeout() { - async fn run_server(mut shutdown_receiver: tokio::sync::oneshot::Receiver<()>) { - let listener = TcpListener::bind("127.0.0.1:18103").await.unwrap(); - while shutdown_receiver.try_recv().is_err() { - if let Ok(result) = timeout(Duration::from_millis(100), listener.accept()).await { - if let Ok((_socket, _)) = result { - tokio::time::sleep(Duration::from_millis(1000)).await; + async fn run_server( + mut shutdown_receiver: tokio::sync::oneshot::Receiver<()>, + ) -> (impl Future, SocketAddr) { + let listener = TcpListener::bind("0.0.0.0:0").await.unwrap(); + let listener_addr = listener.local_addr().unwrap(); + + ( + async move { + while shutdown_receiver.try_recv().is_err() { + if let Ok(result) = timeout(Duration::from_millis(100), listener.accept()).await + { + if let Ok((_socket, _)) = result { + tokio::time::sleep(Duration::from_millis(1000)).await; + } + } } - } - } + }, + listener_addr, + ) } let (server_shutdown, server_shutdown_receiver) = tokio::sync::oneshot::channel(); - let server_handle = tokio::spawn(run_server(server_shutdown_receiver)); + let (server_fut, server_addr) = run_server(server_shutdown_receiver).await; + let server_handle = tokio::spawn(server_fut); tokio::time::sleep(Duration::from_millis(100)).await; let config = SdkConfig::builder() @@ -94,7 +104,7 @@ async fn test_read_timeout() { .build(), ) .endpoint_resolver(Endpoint::immutable( - "http://127.0.0.1:18103".parse().unwrap(), + format!("http://{server_addr}").parse().expect("valid URI"), )) .region(Some(Region::from_static("us-east-1"))) .credentials_provider(SharedCredentialsProvider::new(Credentials::new( diff --git a/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs b/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs index f4727806f4..74a08a470d 100644 --- a/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs +++ b/aws/sdk/integration-tests/s3/tests/user-agent-app-name.rs @@ -3,29 +3,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::{AppName, Credentials, Region}; +use aws_config::SdkConfig; +use aws_sdk_s3::{AppName, Client, Credentials, Region}; use aws_smithy_client::test_connection::capture_request; +use aws_types::credentials::SharedCredentialsProvider; #[tokio::test] -async fn user_agent_app_name() -> Result<(), aws_sdk_s3::Error> { - let (conn, handler) = capture_request(None); - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3::Config::builder() - .credentials_provider(creds) +async fn user_agent_app_name() { + let (conn, rcvr) = capture_request(None); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) .region(Region::new("us-east-1")) + .http_connector(conn.clone()) .app_name(AppName::new("test-app-name").expect("valid app name")) // set app name in config .build(); - let client = aws_sdk_s3::Client::from_conf_conn(conf, conn); - let _response = client.list_objects_v2().bucket("test-bucket").send().await; + let client = Client::new(&sdk_config); + let _ = client.list_objects_v2().bucket("test-bucket").send().await; // verify app name made it to the user agent - let request = handler.expect_request(); + let request = rcvr.expect_request(); let formatted = std::str::from_utf8( request .headers() @@ -39,6 +41,4 @@ async fn user_agent_app_name() -> Result<(), aws_sdk_s3::Error> { "'{}' didn't end with the app name", formatted ); - - Ok(()) } diff --git a/aws/sdk/integration-tests/s3control/Cargo.toml b/aws/sdk/integration-tests/s3control/Cargo.toml index 8ffa8e0bc8..888825b677 100644 --- a/aws/sdk/integration-tests/s3control/Cargo.toml +++ b/aws/sdk/integration-tests/s3control/Cargo.toml @@ -12,6 +12,7 @@ aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } aws-sdk-s3control = { path = "../../build/aws-sdk/sdk/s3control" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } +aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1.0.0" http = "0.2.0" serde_json = "1.0.0" diff --git a/aws/sdk/integration-tests/s3control/tests/signing-it.rs b/aws/sdk/integration-tests/s3control/tests/signing-it.rs index ae49d4fb7e..9d1f8f55e3 100644 --- a/aws/sdk/integration-tests/s3control/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3control/tests/signing-it.rs @@ -4,29 +4,16 @@ */ use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3control::operation::ListAccessPoints; -use aws_sdk_s3control::{Credentials, Region}; +use aws_sdk_s3control::{Client, Credentials, Region}; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; +use aws_types::credentials::SharedCredentialsProvider; +use aws_types::SdkConfig; +use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; -use aws_sdk_s3control::middleware::DefaultMiddleware; -use aws_smithy_client::Client as CoreClient; -pub type Client = CoreClient; - #[tokio::test] -async fn test_signer() -> Result<(), aws_sdk_s3control::Error> { - let creds = Credentials::new( - "ANOTREAL", - "notrealrnrELgWzOk3IfjzDKtFBhDby", - Some("notarealsessiontoken".to_string()), - None, - "test", - ); - let conf = aws_sdk_s3control::Config::builder() - .credentials_provider(creds) - .region(Region::new("us-east-1")) - .build(); +async fn test_signer() { let conn = TestConnection::new(vec![( http::Request::builder() .header("authorization", @@ -38,19 +25,36 @@ async fn test_signer() -> Result<(), aws_sdk_s3control::Error> { .unwrap(), http::Response::builder().status(200).body("").unwrap(), )]); - let client = Client::new(conn.clone()); - let mut op = ListAccessPoints::builder() + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + None, + "test", + ))) + .http_connector(conn.clone()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::new(&sdk_config); + + let _ = client + .list_access_points() .account_id("test-bucket") - .build() + .customize() + .await + .unwrap() + .map_operation(|mut op| { + op.properties_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1636751225)); + op.properties_mut().insert(AwsUserAgent::for_tests()); + + Result::Ok::<_, Infallible>(op) + }) .unwrap() - .make_operation(&conf) + .send() .await - .unwrap(); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1636751225)); - op.properties_mut().insert(AwsUserAgent::for_tests()); + .expect_err("empty response"); - client.call(op).await.expect_err("empty response"); conn.assert_requests_match(&[]); - Ok(()) } diff --git a/aws/sdk/integration-tests/sts/tests/signing-it.rs b/aws/sdk/integration-tests/sts/tests/signing-it.rs index e685aabe3b..d356a43cc3 100644 --- a/aws/sdk/integration-tests/sts/tests/signing-it.rs +++ b/aws/sdk/integration-tests/sts/tests/signing-it.rs @@ -15,12 +15,13 @@ async fn assume_role_signed() { None, "test", ); + let (server, request) = capture_request(None); let conf = aws_sdk_sts::Config::builder() .credentials_provider(creds) .region(Region::new("us-east-1")) + .http_connector(server) .build(); - let (server, request) = capture_request(None); - let client = aws_sdk_sts::Client::from_conf_conn(conf, server); + let client = aws_sdk_sts::Client::from_conf(conf); let _ = client.assume_role().send().await; // assume role should have an auth header assert_ne!( @@ -38,12 +39,13 @@ async fn web_identity_unsigned() { None, "test", ); + let (server, request) = capture_request(None); let conf = aws_sdk_sts::Config::builder() .credentials_provider(creds) .region(Region::new("us-east-1")) + .http_connector(server) .build(); - let (server, request) = capture_request(None); - let client = aws_sdk_sts::Client::from_conf_conn(conf, server); + let client = aws_sdk_sts::Client::from_conf(conf); let _ = client.assume_role_with_web_identity().send().await; // web identity should be unsigned assert_eq!( @@ -54,11 +56,12 @@ async fn web_identity_unsigned() { #[tokio::test] async fn assume_role_saml_unsigned() { + let (server, request) = capture_request(None); let conf = aws_sdk_sts::Config::builder() .region(Region::new("us-east-1")) + .http_connector(server) .build(); - let (server, request) = capture_request(None); - let client = aws_sdk_sts::Client::from_conf_conn(conf, server); + let client = aws_sdk_sts::Client::from_conf(conf); let _ = client.assume_role_with_saml().send().await; // web identity should be unsigned assert_eq!( @@ -69,11 +72,12 @@ async fn assume_role_saml_unsigned() { #[tokio::test] async fn web_identity_no_creds() { + let (server, request) = capture_request(None); let conf = aws_sdk_sts::Config::builder() .region(Region::new("us-east-1")) + .http_connector(server) .build(); - let (server, request) = capture_request(None); - let client = aws_sdk_sts::Client::from_conf_conn(conf, server); + let client = aws_sdk_sts::Client::from_conf(conf); let _ = client.assume_role_with_web_identity().send().await; // web identity should be unsigned and work without credentials assert_eq!( diff --git a/aws/sdk/integration-tests/transcribestreaming/tests/test.rs b/aws/sdk/integration-tests/transcribestreaming/tests/test.rs index 64d889f122..fd3233b124 100644 --- a/aws/sdk/integration-tests/transcribestreaming/tests/test.rs +++ b/aws/sdk/integration-tests/transcribestreaming/tests/test.rs @@ -110,9 +110,10 @@ async fn start_request( let credentials = Credentials::new("test", "test", None, None, "test"); let config = Config::builder() .region(region) + .http_connector(replayer.clone()) .credentials_provider(credentials) .build(); - let client = Client::from_conf_conn(config, replayer.clone()); + let client = Client::from_conf(config); let output = client .start_stream_transcription() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt index adfb3b8721..8b8d419ad0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ClientDocsGenerator.kt @@ -25,16 +25,16 @@ class ClientDocsGenerator : LibRsCustomization() { writable { containerDocs( """ - The entry point for most customers will be [`Client`]. [`Client`] exposes one method for each API offered - by the service. + The entry point for most customers will be [`Client`]. [`Client`] exposes one method for each API offered + by the service. - Some APIs require complex or nested arguments. These exist in [`model`](crate::model). + Some APIs require complex or nested arguments. These exist in [`model`](crate::model). - Lastly, errors that can be returned by the service are contained within [`error`]. [`Error`] defines a meta - error encompassing all possible errors that can be returned by the service. + Lastly, errors that can be returned by the service are contained within [`error`]. [`Error`] defines a meta + error encompassing all possible errors that can be returned by the service. - The other modules within this crate are not required for normal usage. - """.trimEnd(), + The other modules within this crate are not required for normal usage. + """.trimEnd(), ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 40131d517a..df0c086976 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -32,31 +32,31 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust when (section) { is ServiceConfig.ConfigStruct -> rustTemplate( """ - retry_config: Option<#{RetryConfig}>, - sleep_impl: Option>, - timeout_config: Option<#{TimeoutConfig}>, - """, + retry_config: Option<#{RetryConfig}>, + sleep_impl: Option>, + timeout_config: Option<#{TimeoutConfig}>, + """, *codegenScope, ) is ServiceConfig.ConfigImpl -> { rustTemplate( """ - /// Return a reference to the retry configuration contained in this config, if any. - pub fn retry_config(&self) -> Option<&#{RetryConfig}> { - self.retry_config.as_ref() - } + /// Return a reference to the retry configuration contained in this config, if any. + pub fn retry_config(&self) -> Option<&#{RetryConfig}> { + self.retry_config.as_ref() + } - /// Return a cloned Arc containing the async sleep implementation from this config, if any. - pub fn sleep_impl(&self) -> Option> { - self.sleep_impl.clone() - } + /// Return a cloned Arc containing the async sleep implementation from this config, if any. + pub fn sleep_impl(&self) -> Option> { + self.sleep_impl.clone() + } - /// Return a reference to the timeout configuration contained in this config, if any. - pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { - self.timeout_config.as_ref() - } - """, + /// Return a reference to the timeout configuration contained in this config, if any. + pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { + self.timeout_config.as_ref() + } + """, *codegenScope, ) } @@ -64,159 +64,159 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust is ServiceConfig.BuilderStruct -> rustTemplate( """ - retry_config: Option<#{RetryConfig}>, - sleep_impl: Option>, - timeout_config: Option<#{TimeoutConfig}>, - """, + retry_config: Option<#{RetryConfig}>, + sleep_impl: Option>, + timeout_config: Option<#{TimeoutConfig}>, + """, *codegenScope, ) ServiceConfig.BuilderImpl -> rustTemplate( """ - /// Set the retry_config for the builder - /// - /// ## Examples - /// ```no_run - /// use $moduleUseName::config::Config; - /// use $moduleUseName::config::retry::RetryConfig; - /// - /// let retry_config = RetryConfig::standard().with_max_attempts(5); - /// let config = Config::builder().retry_config(retry_config).build(); - /// ``` - pub fn retry_config(mut self, retry_config: #{RetryConfig}) -> Self { - self.set_retry_config(Some(retry_config)); - self - } + /// Set the retry_config for the builder + /// + /// ## Examples + /// ```no_run + /// use $moduleUseName::config::Config; + /// use $moduleUseName::config::retry::RetryConfig; + /// + /// let retry_config = RetryConfig::standard().with_max_attempts(5); + /// let config = Config::builder().retry_config(retry_config).build(); + /// ``` + pub fn retry_config(mut self, retry_config: #{RetryConfig}) -> Self { + self.set_retry_config(Some(retry_config)); + self + } - /// Set the retry_config for the builder - /// - /// ## Examples - /// ```no_run - /// use $moduleUseName::config::{Builder, Config}; - /// use $moduleUseName::config::retry::RetryConfig; - /// - /// fn disable_retries(builder: &mut Builder) { - /// let retry_config = RetryConfig::standard().with_max_attempts(1); - /// builder.set_retry_config(Some(retry_config)); - /// } - /// - /// let mut builder = Config::builder(); - /// disable_retries(&mut builder); - /// let config = builder.build(); - /// ``` - pub fn set_retry_config(&mut self, retry_config: Option<#{RetryConfig}>) -> &mut Self { - self.retry_config = retry_config; - self - } + /// Set the retry_config for the builder + /// + /// ## Examples + /// ```no_run + /// use $moduleUseName::config::{Builder, Config}; + /// use $moduleUseName::config::retry::RetryConfig; + /// + /// fn disable_retries(builder: &mut Builder) { + /// let retry_config = RetryConfig::standard().with_max_attempts(1); + /// builder.set_retry_config(Some(retry_config)); + /// } + /// + /// let mut builder = Config::builder(); + /// disable_retries(&mut builder); + /// let config = builder.build(); + /// ``` + pub fn set_retry_config(&mut self, retry_config: Option<#{RetryConfig}>) -> &mut Self { + self.retry_config = retry_config; + self + } - /// Set the sleep_impl for the builder - /// - /// ## Examples - /// - /// ```no_run - /// use $moduleUseName::config::{AsyncSleep, Sleep, Config}; - /// - /// ##[derive(Debug)] - /// pub struct ForeverSleep; - /// - /// impl AsyncSleep for ForeverSleep { - /// fn sleep(&self, duration: std::time::Duration) -> Sleep { - /// Sleep::new(std::future::pending()) - /// } - /// } - /// - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); - /// let config = Config::builder().sleep_impl(sleep_impl).build(); - /// ``` - pub fn sleep_impl(mut self, sleep_impl: std::sync::Arc) -> Self { - self.set_sleep_impl(Some(sleep_impl)); - self - } + /// Set the sleep_impl for the builder + /// + /// ## Examples + /// + /// ```no_run + /// use $moduleUseName::config::{AsyncSleep, Sleep, Config}; + /// + /// ##[derive(Debug)] + /// pub struct ForeverSleep; + /// + /// impl AsyncSleep for ForeverSleep { + /// fn sleep(&self, duration: std::time::Duration) -> Sleep { + /// Sleep::new(std::future::pending()) + /// } + /// } + /// + /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// let config = Config::builder().sleep_impl(sleep_impl).build(); + /// ``` + pub fn sleep_impl(mut self, sleep_impl: std::sync::Arc) -> Self { + self.set_sleep_impl(Some(sleep_impl)); + self + } - /// Set the sleep_impl for the builder - /// - /// ## Examples - /// - /// ```no_run - /// use $moduleUseName::config::{AsyncSleep, Sleep, Builder, Config}; - /// - /// ##[derive(Debug)] - /// pub struct ForeverSleep; - /// - /// impl AsyncSleep for ForeverSleep { - /// fn sleep(&self, duration: std::time::Duration) -> Sleep { - /// Sleep::new(std::future::pending()) - /// } - /// } - /// - /// fn set_never_ending_sleep_impl(builder: &mut Builder) { - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); - /// builder.set_sleep_impl(Some(sleep_impl)); - /// } - /// - /// let mut builder = Config::builder(); - /// set_never_ending_sleep_impl(&mut builder); - /// let config = builder.build(); - /// ``` - pub fn set_sleep_impl(&mut self, sleep_impl: Option>) -> &mut Self { - self.sleep_impl = sleep_impl; - self - } + /// Set the sleep_impl for the builder + /// + /// ## Examples + /// + /// ```no_run + /// use $moduleUseName::config::{AsyncSleep, Sleep, Builder, Config}; + /// + /// ##[derive(Debug)] + /// pub struct ForeverSleep; + /// + /// impl AsyncSleep for ForeverSleep { + /// fn sleep(&self, duration: std::time::Duration) -> Sleep { + /// Sleep::new(std::future::pending()) + /// } + /// } + /// + /// fn set_never_ending_sleep_impl(builder: &mut Builder) { + /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// builder.set_sleep_impl(Some(sleep_impl)); + /// } + /// + /// let mut builder = Config::builder(); + /// set_never_ending_sleep_impl(&mut builder); + /// let config = builder.build(); + /// ``` + pub fn set_sleep_impl(&mut self, sleep_impl: Option>) -> &mut Self { + self.sleep_impl = sleep_impl; + self + } - /// Set the timeout_config for the builder - /// - /// ## Examples - /// - /// ```no_run - /// ## use std::time::Duration; - /// use $moduleUseName::config::Config; - /// use $moduleUseName::config::timeout::TimeoutConfig; - /// - /// let timeout_config = TimeoutConfig::builder() - /// .operation_attempt_timeout(Duration::from_secs(1)) - /// .build(); - /// let config = Config::builder().timeout_config(timeout_config).build(); - /// ``` - pub fn timeout_config(mut self, timeout_config: #{TimeoutConfig}) -> Self { - self.set_timeout_config(Some(timeout_config)); - self - } + /// Set the timeout_config for the builder + /// + /// ## Examples + /// + /// ```no_run + /// ## use std::time::Duration; + /// use $moduleUseName::config::Config; + /// use $moduleUseName::config::timeout::TimeoutConfig; + /// + /// let timeout_config = TimeoutConfig::builder() + /// .operation_attempt_timeout(Duration::from_secs(1)) + /// .build(); + /// let config = Config::builder().timeout_config(timeout_config).build(); + /// ``` + pub fn timeout_config(mut self, timeout_config: #{TimeoutConfig}) -> Self { + self.set_timeout_config(Some(timeout_config)); + self + } - /// Set the timeout_config for the builder - /// - /// ## Examples - /// - /// ```no_run - /// ## use std::time::Duration; - /// use $moduleUseName::config::{Builder, Config}; - /// use $moduleUseName::config::timeout::TimeoutConfig; - /// - /// fn set_request_timeout(builder: &mut Builder) { - /// let timeout_config = TimeoutConfig::builder() - /// .operation_attempt_timeout(Duration::from_secs(1)) - /// .build(); - /// builder.set_timeout_config(Some(timeout_config)); - /// } - /// - /// let mut builder = Config::builder(); - /// set_request_timeout(&mut builder); - /// let config = builder.build(); - /// ``` - pub fn set_timeout_config(&mut self, timeout_config: Option<#{TimeoutConfig}>) -> &mut Self { - self.timeout_config = timeout_config; - self - } - """, + /// Set the timeout_config for the builder + /// + /// ## Examples + /// + /// ```no_run + /// ## use std::time::Duration; + /// use $moduleUseName::config::{Builder, Config}; + /// use $moduleUseName::config::timeout::TimeoutConfig; + /// + /// fn set_request_timeout(builder: &mut Builder) { + /// let timeout_config = TimeoutConfig::builder() + /// .operation_attempt_timeout(Duration::from_secs(1)) + /// .build(); + /// builder.set_timeout_config(Some(timeout_config)); + /// } + /// + /// let mut builder = Config::builder(); + /// set_request_timeout(&mut builder); + /// let config = builder.build(); + /// ``` + pub fn set_timeout_config(&mut self, timeout_config: Option<#{TimeoutConfig}>) -> &mut Self { + self.timeout_config = timeout_config; + self + } + """, *codegenScope, ) ServiceConfig.BuilderBuild -> rustTemplate( """ - retry_config: self.retry_config, - sleep_impl: self.sleep_impl, - timeout_config: self.timeout_config, - """, + retry_config: self.retry_config, + sleep_impl: self.sleep_impl, + timeout_config: self.timeout_config, + """, *codegenScope, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt index 8b7672fe8f..7348293c73 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt @@ -79,11 +79,11 @@ class NestedAccessorGenerator(private val symbolProvider: RustSymbolProvider) { if (symbolProvider.toSymbol(head).isOptional()) { rust( """ - let input = match ${ref}input.${symbolProvider.toMemberName(head)} { - None => return None, - Some(t) => t - }; - """, + let input = match ${ref}input.${symbolProvider.toMemberName(head)} { + None => return None, + Some(t) => t + }; + """, ) } else { rust("let input = input.${symbolProvider.toMemberName(head)};") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt index f855156820..b0e4beecfb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt @@ -28,7 +28,7 @@ interface FluentClientGenerics { val bounds: Writable /** Bounds for generated `send()` functions */ - fun sendBounds(operation: Symbol, output: Symbol, error: RuntimeType, retryClassifier: RuntimeType): Writable + fun sendBounds(operation: Symbol, operationOutput: Symbol, operationError: RuntimeType, retryClassifier: RuntimeType): Writable /** Convert this `FluentClientGenerics` into the more general `RustGenerics` */ fun toRustGenerics(): RustGenerics diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt index e55f98c0d3..f507ba2d4c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt @@ -24,13 +24,13 @@ import software.amazon.smithy.rust.codegen.core.util.lookup internal class ClientInstantiatorTest { private val model = """ namespace com.test - + @enum([ { value: "t2.nano" }, { value: "t2.micro" }, ]) string UnnamedEnum - + @enum([ { value: "t2.nano", @@ -42,7 +42,7 @@ internal class ClientInstantiatorTest { }, ]) string NamedEnum - """.asSmithyModel() + """.asSmithyModel() private val codegenContext = testCodegenContext(model) private val symbolProvider = codegenContext.symbolProvider diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 8d089ef8bc..2a54f56741 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -492,10 +492,10 @@ class HttpBindingGenerator( """ let header_value = $safeName; let header_value = http::header::HeaderValue::try_from(&*header_value).map_err(|err| { - #{build_error}::InvalidField { - field: "$memberName", + #{build_error}::InvalidField { + field: "$memberName", details: format!( - "`{}` cannot be used as a header value: {}", + "`{}` cannot be used as a header value: {}", &${memberShape.redactIfNecessary(model, "header_value")}, err, ) @@ -531,7 +531,7 @@ class HttpBindingGenerator( #{build_error}::InvalidField { field: "$memberName", details: format!( - "`{}` cannot be used as a header value: {}", + "`{}` cannot be used as a header value: {}", ${memberShape.redactIfNecessary(model, "v")}, err, ) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt index 73dbe86d5b..9dc1e0592d 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt @@ -31,7 +31,7 @@ import software.amazon.smithy.rust.codegen.core.util.lookup class InstantiatorTest { private val model = """ namespace com.test - + @documentation("this documents the shape") structure MyStruct { foo: String, @@ -122,8 +122,8 @@ class InstantiatorTest { } rust( """ - assert_eq!(result.bar, 10); - assert_eq!(result.foo.unwrap(), "hello"); + assert_eq!(result.bar, 10); + assert_eq!(result.foo.unwrap(), "hello"); """, ) } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt index 777a6b6c1b..ee5fbd2fad 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt @@ -179,8 +179,8 @@ class PythonApplicationGenerator( """ fn build_service(&mut self, event_loop: &#{pyo3}::PyAny) -> #{pyo3}::PyResult< #{tower}::util::BoxCloneService< - #{http}::Request<#{SmithyServer}::body::Body>, - #{http}::Response<#{SmithyServer}::body::BoxBody>, + #{http}::Request<#{SmithyServer}::body::Body>, + #{http}::Response<#{SmithyServer}::body::BoxBody>, std::convert::Infallible > > diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt index ae50d8a0a5..945dac55dc 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt @@ -31,9 +31,9 @@ class ServerInstantiatorTest { // This model started off from the one in `InstantiatorTest.kt` from `codegen-core`. private val model = """ namespace com.test - + use smithy.framework#ValidationException - + @documentation("this documents the shape") structure MyStruct { foo: String, @@ -83,7 +83,7 @@ class ServerInstantiatorTest { @required num: Integer } - + structure MyStructRequired { @required str: String, @@ -106,13 +106,13 @@ class ServerInstantiatorTest { @required doc: Document } - + @enum([ { value: "t2.nano" }, { value: "t2.micro" }, ]) string UnnamedEnum - + @enum([ { value: "t2.nano", diff --git a/rust-runtime/aws-smithy-client/src/bounds.rs b/rust-runtime/aws-smithy-client/src/bounds.rs index 32478e1d12..0cddf86096 100644 --- a/rust-runtime/aws-smithy-client/src/bounds.rs +++ b/rust-runtime/aws-smithy-client/src/bounds.rs @@ -15,6 +15,8 @@ //! [compiler limitations]: https://github.com/rust-lang/rust/issues/20671 //! [do not need to be repeated]: https://github.com/rust-lang/rust/issues/20671#issuecomment-529752828 +use crate::erase::DynConnector; +use crate::http_connector::HttpConnector; use crate::*; use aws_smithy_http::result::ConnectorError; @@ -62,6 +64,17 @@ where type Future = T::Future; } +impl From for HttpConnector +where + E: Into + Send + Sync + 'static, + F: Send + 'static, + T: SmithyConnector>, +{ + fn from(smithy_connector: T) -> Self { + HttpConnector::Prebuilt(Some(DynConnector::new(smithy_connector))) + } +} + /// A Smithy middleware service that adjusts [`aws_smithy_http::operation::Request`]s. /// /// This trait has a blanket implementation for all compatible types, and should never be diff --git a/rust-runtime/aws-smithy-client/src/dvr/replay.rs b/rust-runtime/aws-smithy-client/src/dvr/replay.rs index b3d7df6154..95bb78468d 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/replay.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/replay.rs @@ -3,20 +3,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::dvr::{Action, ConnectionId, Direction, Event}; -use aws_smithy_http::body::SdkBody; -use aws_smithy_http::result::ConnectorError; -use bytes::{Bytes, BytesMut}; -use http::{Request, Version}; -use http_body::Body; use std::collections::{HashMap, VecDeque}; use std::error::Error; use std::ops::DerefMut; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; + +use bytes::{Bytes, BytesMut}; +use http::{Request, Version}; +use http_body::Body; use tokio::task::JoinHandle; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::result::ConnectorError; + +use crate::dvr::{Action, ConnectionId, Direction, Event}; + /// Wrapper type to enable optionally waiting for a future to complete #[derive(Debug)] enum Waitable { @@ -227,7 +230,7 @@ impl tower::Service> for ReplayingConnection { return Box::pin(std::future::ready(Err(ConnectorError::other( format!("no data for event {}. req: {:?}", event_id.0, req).into(), None, - )))) + )))); } }; diff --git a/rust-runtime/aws-smithy-client/src/erase.rs b/rust-runtime/aws-smithy-client/src/erase.rs index a097b75d67..2cac5afeaa 100644 --- a/rust-runtime/aws-smithy-client/src/erase.rs +++ b/rust-runtime/aws-smithy-client/src/erase.rs @@ -5,17 +5,20 @@ //! Type-erased variants of [`Client`] and friends. +use std::fmt; + +use tower::{Layer, Service, ServiceExt}; + +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::result::ConnectorError; +use boxclone::*; + +use crate::{bounds, retry, Client}; + // These types are technically public in that they're reachable from the public trait impls on // DynMiddleware, but no-one should ever look at them or use them. #[doc(hidden)] pub mod boxclone; -use boxclone::*; - -use crate::{bounds, http_connector::HttpConnector, retry, Client}; -use aws_smithy_http::body::SdkBody; -use aws_smithy_http::result::ConnectorError; -use std::fmt; -use tower::{Layer, Service, ServiceExt}; /// A [`Client`] whose connector and middleware types have been erased. /// @@ -178,12 +181,6 @@ impl Service> for DynConnector { } } -impl From for HttpConnector { - fn from(connector: DynConnector) -> Self { - HttpConnector::Prebuilt(Some(connector)) - } -} - /// A Smithy middleware that uses dynamic dispatch. /// /// This type allows you to pay a small runtime cost to avoid having to name the exact middleware diff --git a/rust-runtime/aws-smithy-client/src/never.rs b/rust-runtime/aws-smithy-client/src/never.rs index 7989800d6b..b6dcc07ea2 100644 --- a/rust-runtime/aws-smithy-client/src/never.rs +++ b/rust-runtime/aws-smithy-client/src/never.rs @@ -5,20 +5,19 @@ //! Test connectors that never return data -use http::Uri; - -use aws_smithy_async::future::never::Never; - use std::marker::PhantomData; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; - use std::task::{Context, Poll}; -use crate::erase::boxclone::BoxFuture; +use http::Uri; +use tower::BoxError; + +use aws_smithy_async::future::never::Never; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; -use tower::BoxError; + +use crate::erase::boxclone::BoxFuture; /// A service that will never return whatever it is you want /// @@ -71,8 +70,8 @@ pub type NeverConnected = NeverService; pub(crate) mod stream { use std::io::Error; use std::pin::Pin; - use std::task::{Context, Poll}; + use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; /// A stream that will never return or accept any data diff --git a/rust-runtime/aws-smithy-client/src/test_connection.rs b/rust-runtime/aws-smithy-client/src/test_connection.rs index d82720025d..571f2524ab 100644 --- a/rust-runtime/aws-smithy-client/src/test_connection.rs +++ b/rust-runtime/aws-smithy-client/src/test_connection.rs @@ -7,22 +7,22 @@ // TODO(docs) #![allow(missing_docs)] -use http::header::{HeaderName, CONTENT_TYPE}; -use http::Request; - -use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; - -use aws_smithy_http::body::SdkBody; -use aws_smithy_http::result::ConnectorError; use std::future::Ready; - use std::ops::Deref; - use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; +use http::header::{HeaderName, CONTENT_TYPE}; +use http::Request; use tokio::sync::oneshot; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::result::ConnectorError; +use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; + +#[doc(inline)] +pub use crate::never; + /// Test Connection to capture a single request #[derive(Debug, Clone)] pub struct CaptureRequestHandler(Arc>); @@ -45,9 +45,6 @@ impl CaptureRequestReceiver { } } -#[doc(inline)] -pub use crate::never; - impl tower::Service> for CaptureRequestHandler { type Response = http::Response; type Error = ConnectorError; @@ -79,7 +76,10 @@ impl tower::Service> for CaptureRequestHandler { /// Example: /// ```rust,compile_fail /// let (server, request) = capture_request(None); -/// let client = aws_sdk_sts::Client::from_conf_conn(conf, server); +/// let conf = aws_sdk_sts::Config::builder() +/// .http_connector(server) +/// .build(); +/// let client = aws_sdk_sts::Client::from_conf(conf); /// let _ = client.assume_role_with_saml().send().await; /// // web identity should be unsigned /// assert_eq!( @@ -256,12 +256,14 @@ where #[cfg(test)] mod tests { + use hyper::service::Service; + + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::result::ConnectorError; + use crate::bounds::SmithyConnector; use crate::test_connection::{capture_request, never::NeverService, TestConnection}; use crate::Client; - use aws_smithy_http::body::SdkBody; - use aws_smithy_http::result::ConnectorError; - use hyper::service::Service; fn is_send_sync(_: T) {} @@ -277,6 +279,7 @@ mod tests { T: SmithyConnector, { } + fn quacks_like_a_connector(_: &T) where T: Service, Response = http::Response> diff --git a/rust-runtime/aws-smithy-http/src/byte_stream.rs b/rust-runtime/aws-smithy-http/src/byte_stream.rs index 09ab48df9e..e6ead7c9da 100644 --- a/rust-runtime/aws-smithy-http/src/byte_stream.rs +++ b/rust-runtime/aws-smithy-http/src/byte_stream.rs @@ -500,6 +500,15 @@ impl AggregatedBytes { pub fn into_bytes(mut self) -> Bytes { self.0.copy_to_bytes(self.0.remaining()) } + + /// Convert this buffer into a `Vec` + pub fn to_vec(self) -> Vec { + self.0 + .into_inner() + .into_iter() + .flat_map(|b| b.to_vec()) + .collect() + } } impl Buf for AggregatedBytes {