diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index bedb7d0bcc..a583475971 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-config" -version = "1.5.7" +version = "1.5.8" authors = [ "AWS Rust SDK Team ", "Russell Cohen ", diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index 35e1e435f5..90a10398eb 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -14,7 +14,7 @@ use aws_smithy_checksums::ChecksumAlgorithm; use aws_smithy_checksums::{body::calculate, http::HttpChecksum}; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::{ - BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, Input, + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, Input, }; use aws_smithy_runtime_api::client::interceptors::Intercept; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; @@ -102,36 +102,42 @@ impl DefaultRequestChecksumOverride { } } -pub(crate) struct RequestChecksumInterceptor { +pub(crate) struct RequestChecksumInterceptor { algorithm_provider: AP, + checksum_mutator: CM, } -impl fmt::Debug for RequestChecksumInterceptor { +impl fmt::Debug for RequestChecksumInterceptor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RequestChecksumInterceptor").finish() } } -impl RequestChecksumInterceptor { - pub(crate) fn new(algorithm_provider: AP) -> Self { - Self { algorithm_provider } +impl RequestChecksumInterceptor { + pub(crate) fn new(algorithm_provider: AP, checksum_mutator: CM) -> Self { + Self { + algorithm_provider, + checksum_mutator, + } } } -impl Intercept for RequestChecksumInterceptor +impl Intercept for RequestChecksumInterceptor where AP: Fn(&Input) -> Result<(Option, bool), BoxError> + Send + Sync, + CM: Fn(&mut Input, &ConfigBag) -> Result<(), BoxError> + Send + Sync, { fn name(&self) -> &'static str { "RequestChecksumInterceptor" } - fn read_before_serialization( + fn modify_before_serialization( &self, - context: &BeforeSerializationInterceptorContextRef<'_>, + context: &mut BeforeSerializationInterceptorContextMut<'_>, _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { + let _ = (self.checksum_mutator)(context.input_mut(), cfg); let checksum_algorithm = (self.algorithm_provider)(context.input())?; let mut layer = Layer::new("RequestChecksumInterceptor"); layer.store_put(RequestChecksumInterceptorState { 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 46fb7f71fc..ed47f1a452 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 @@ -43,7 +43,6 @@ val DECORATORS: List = UserAgentDecorator(), SigV4AuthDecorator(), HttpRequestChecksumDecorator(), - HttpRequestChecksumMutationInterceptorDecorator(), HttpResponseChecksumDecorator(), HttpResponseChecksumMutationInterceptorDecorator(), IntegrationTestDecorator(), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index f9b7b9c9d0..ffd7b81062 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -160,29 +160,71 @@ class HttpRequestChecksumCustomization( writable { // Get the `HttpChecksumTrait`, returning early if this `OperationShape` doesn't have one val checksumTrait = operationShape.getTrait() ?: return@writable - val requestAlgorithmMember = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) + val requestAlgorithmMember = + checksumTrait.requestAlgorithmMemberShape(codegenContext, operationShape) ?: return@writable + val requestAlgorithmMemberName = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) val inputShape = codegenContext.model.expectShape(operationShape.inputShape) val requestChecksumRequired = checksumTrait.isRequestChecksumRequired val operationName = codegenContext.symbolProvider.toSymbol(operationShape).name + val requestAlgorithmMemberInner = + if (requestAlgorithmMember.isOptional) { + codegenContext.model.expectShape(requestAlgorithmMember.target) + } else { + requestAlgorithmMember + } when (section) { is OperationSection.AdditionalInterceptors -> { - if (requestAlgorithmMember != null) { + if (requestAlgorithmMemberName != null) { section.registerInterceptor(runtimeConfig, this) { val runtimeApi = RuntimeType.smithyRuntimeApiClient(runtimeConfig) rustTemplate( """ - #{RequestChecksumInterceptor}::new(|input: &#{Input}| { + #{RequestChecksumInterceptor}::new( + |input: &#{Input}| { let input: &#{OperationInput} = input.downcast_ref().expect("correct type"); - let checksum_algorithm = input.$requestAlgorithmMember(); + let checksum_algorithm = input.$requestAlgorithmMemberName(); #{checksum_algorithm_to_str} #{Result}::<_, #{BoxError}>::Ok((checksum_algorithm, $requestChecksumRequired)) - }) + }, + |input: &mut #{Input}, cfg: &#{ConfigBag}| { + let input = input + .downcast_mut::<#{OperationInputType}>() + .ok_or("failed to downcast to #{OperationInputType}")?; + + // This value is set by the user on the SdkConfig to indicate their preference + let request_checksum_calculation = cfg + .load::<#{RequestChecksumCalculation}>() + .unwrap_or(&#{RequestChecksumCalculation}::WhenSupported); + + // From the httpChecksum trait + let http_checksum_required = $requestChecksumRequired; + + // If the RequestChecksumCalculation is WhenSupported and the user has not set a checksum we + // default to Crc32. If it is WhenRequired and a checksum is required by the trait we also set the + // default. In all other cases we do nothing. + match ( + request_checksum_calculation, + http_checksum_required, + input.checksum_algorithm(), + ) { + (#{RequestChecksumCalculation}::WhenSupported, _, None) + | (#{RequestChecksumCalculation}::WhenRequired, true, None) => { + input.checksum_algorithm = Some(#{ChecksumAlgoShape}::Crc32); + } + _ => {}, + } + + Ok(()) + } + + ) """, *preludeScope, "BoxError" to RuntimeType.boxError(runtimeConfig), "Input" to runtimeApi.resolve("client::interceptors::context::Input"), "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), + "ConfigBag" to RuntimeType.configBag(runtimeConfig), "RequestChecksumInterceptor" to runtimeConfig.awsInlineableHttpRequestChecksum() .resolve("RequestChecksumInterceptor"), @@ -191,14 +233,19 @@ class HttpRequestChecksumCustomization( codegenContext, operationShape, ), - ) - } - section.registerInterceptor(codegenContext.runtimeConfig, this) { - val interceptorName = "${operationName}HttpRequestChecksumMutationInterceptor" - rustTemplate( - """ - $interceptorName - """, + "RequestChecksumCalculation" to + CargoDependency.smithyTypes(runtimeConfig).toType() + .resolve("checksum_config::RequestChecksumCalculation"), + "ChecksumAlgoShape" to + codegenContext.symbolProvider.toSymbol( + requestAlgorithmMemberInner, + ), + "OperationInputType" to + codegenContext.symbolProvider.toSymbol( + operationShape.inputShape( + codegenContext.model, + ), + ), ) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumMutationInterceptorGenerator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumMutationInterceptorGenerator.kt deleted file mode 100644 index ac007671c3..0000000000 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumMutationInterceptorGenerator.kt +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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.aws.traits.HttpChecksumTrait -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection -import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -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.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope -import software.amazon.smithy.rust.codegen.core.util.dq -import software.amazon.smithy.rust.codegen.core.util.expectMember -import software.amazon.smithy.rust.codegen.core.util.getTrait -import software.amazon.smithy.rust.codegen.core.util.inputShape -import software.amazon.smithy.rust.codegen.core.util.orNull - -/** - * This class generates an interceptor for operations with the `httpChecksum` trait that support request checksums. - * In the `modify_before_serialization` hook the interceptor checks the config's `request_checksum_calculation` value - * and the trait's `requestChecksumRequired` value. If `request_checksum_calculation` is `WhenSupported` or it is - * `WhenRequired` and `requestChecksumRequired` is `true` then we check the operation input's `requestAlgorithmMember`. - * If that is `None` (so has not been explicitly set by the user) we default it to `Crc32`. - * - * Note that although there is an existing inlineable `RequestChecksumInterceptor` this logic could not live there. - * Since that interceptor is inlineable it does not have access to the name of the `requestAlgorithmMember` on the - * operation's input or the `requestChecksumRequired` value from the trait, and in certain circumstances we need to - * mutate that member on the input before serializing the request and sending it to the service. - */ -class HttpRequestChecksumMutationInterceptorDecorator : ClientCodegenDecorator { - override val name: String = "HttpRequestChecksumMutationInterceptorGenerator" - override val order: Byte = 0 - - override fun operationCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List { - // If the operation doesn't have the HttpChecksum trait we return early - val checksumTrait = operation.getTrait() ?: return baseCustomizations - // Also return early if there is no requestValidationModeMember on the trait - val requestAlgorithmMember = - (checksumTrait.requestAlgorithmMemberShape(codegenContext, operation) ?: return baseCustomizations) - - return baseCustomizations + - listOf( - InterceptorSection( - codegenContext, - operation, - requestAlgorithmMember, - checksumTrait, - ), - ) - } - - private class InterceptorSection( - private val codegenContext: ClientCodegenContext, - private val operation: OperationShape, - private val requestAlgorithmMember: MemberShape, - private val checksumTrait: HttpChecksumTrait, - ) : OperationCustomization() { - override fun section(section: OperationSection): Writable = - writable { - if (section is OperationSection.RuntimePluginSupportingTypes) { - val model = codegenContext.model - val symbolProvider = codegenContext.symbolProvider - val codegenScope = - codegenContext.runtimeConfig.let { rc -> - val runtimeApi = CargoDependency.smithyRuntimeApiClient(rc).toType() - val interceptors = runtimeApi.resolve("client::interceptors") - - arrayOf( - *preludeScope, - "BoxError" to RuntimeType.boxError(rc), - "ConfigBag" to RuntimeType.configBag(rc), - "Intercept" to RuntimeType.intercept(rc), - "BeforeSerializationInterceptorContextMut" to - RuntimeType.beforeSerializationInterceptorContextMut( - rc, - ), - "Input" to interceptors.resolve("context::Input"), - "Output" to interceptors.resolve("context::Output"), - "Error" to interceptors.resolve("context::Error"), - "RuntimeComponents" to RuntimeType.runtimeComponents(rc), - "RequestChecksumCalculation" to - CargoDependency.smithyTypes(rc).toType() - .resolve("checksum_config::RequestChecksumCalculation"), - ) - } - - val requestAlgorithmMemberInner = - if (requestAlgorithmMember.isOptional) { - codegenContext.model.expectShape(requestAlgorithmMember.target) - } else { - requestAlgorithmMember - } - - val operationName = symbolProvider.toSymbol(operation).name - val interceptorName = "${operationName}HttpRequestChecksumMutationInterceptor" - val requestChecksumRequired = checksumTrait.isRequestChecksumRequired - - rustTemplate( - """ - ##[derive(Debug)] - struct $interceptorName; - - impl #{Intercept} for $interceptorName { - fn name(&self) -> &'static str { - ${interceptorName.dq()} - } - - fn modify_before_serialization( - &self, - context: &mut #{BeforeSerializationInterceptorContextMut}<'_, #{Input}, #{Output}, #{Error}>, - _runtime_comps: &#{RuntimeComponents}, - cfg: &mut #{ConfigBag}, - ) -> #{Result}<(), #{BoxError}> { - let input = context - .input_mut() - .downcast_mut::<#{OperationInputType}>() - .ok_or("failed to downcast to #{OperationInputType}")?; - - // This value is set by the user on the SdkConfig to indicate their preference - let request_checksum_calculation = cfg - .load::<#{RequestChecksumCalculation}>() - .unwrap_or(&#{RequestChecksumCalculation}::WhenSupported); - - // From the httpChecksum trait - let http_checksum_required = $requestChecksumRequired; - - // If the RequestChecksumCalculation is WhenSupported and the user has not set a checksum we - // default to Crc32. If it is WhenRequired and a checksum is required by the trait we also set the - // default. In all other cases we do nothing. - match ( - request_checksum_calculation, - http_checksum_required, - input.checksum_algorithm(), - ) { - (#{RequestChecksumCalculation}::WhenSupported, _, None) - | (#{RequestChecksumCalculation}::WhenRequired, true, None) => { - input.checksum_algorithm = Some(#{ChecksumAlgoShape}::Crc32); - } - _ => {}, - } - - #{Ok}(()) - } - } - """, - *codegenScope, - "OperationInputType" to codegenContext.symbolProvider.toSymbol(operation.inputShape(model)), - "ChecksumAlgoShape" to - codegenContext.symbolProvider.toSymbol( - requestAlgorithmMemberInner, - ), - ) - } - } - } -} - -/** - * Get the top-level operation input member used to opt-in to best-effort validation of a checksum returned in - * the HTTP response of the operation. - */ -fun HttpChecksumTrait.requestAlgorithmMemberShape( - codegenContext: ClientCodegenContext, - operationShape: OperationShape, -): MemberShape? { - val requestAlgorithmMember = this.requestAlgorithmMember.orNull() ?: return null - return operationShape.inputShape(codegenContext.model).expectMember(requestAlgorithmMember) -} diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 7b52b8b6d8..3fd9186d0f 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-smithy-types" -version = "1.2.7" +version = "1.2.8" authors = [ "AWS Rust SDK Team ", "Russell Cohen ",