Skip to content

Commit

Permalink
PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
0marperez committed Dec 11, 2024
1 parent 4a162de commit d665873
Show file tree
Hide file tree
Showing 20 changed files with 356 additions and 181 deletions.
5 changes: 1 addition & 4 deletions aws-runtime/aws-config/api/aws-config.api
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,9 @@ public final class aws/sdk/kotlin/runtime/config/AwsSdkSettingKt {
public static final fun resolveEndpointUrl (Laws/sdk/kotlin/runtime/config/AwsSdkSetting;Laws/smithy/kotlin/runtime/util/PlatformProvider;Ljava/lang/String;Ljava/lang/String;)Laws/smithy/kotlin/runtime/net/url/Url;
}

public final class aws/sdk/kotlin/runtime/config/checksums/ResolveRequestChecksumCalculationKt {
public final class aws/sdk/kotlin/runtime/config/checksums/ResolveFlexibleChecksumsConfigKt {
public static final fun resolveRequestChecksumCalculation (Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/util/LazyAsyncValue;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun resolveRequestChecksumCalculation$default (Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/util/LazyAsyncValue;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class aws/sdk/kotlin/runtime/config/checksums/ResolveResponseChecksumValidationKt {
public static final fun resolveResponseChecksumValidation (Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/util/LazyAsyncValue;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun resolveResponseChecksumValidation$default (Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/util/LazyAsyncValue;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package aws.sdk.kotlin.runtime.config.checksums

import aws.sdk.kotlin.runtime.ConfigurationException
import aws.sdk.kotlin.runtime.InternalSdkApi
import aws.sdk.kotlin.runtime.config.AwsSdkSetting
import aws.sdk.kotlin.runtime.config.profile.AwsProfile
import aws.sdk.kotlin.runtime.config.profile.requestChecksumCalculation
import aws.sdk.kotlin.runtime.config.profile.responseChecksumValidation
import aws.smithy.kotlin.runtime.client.config.HttpChecksumConfigOption
import aws.smithy.kotlin.runtime.config.resolve
import aws.smithy.kotlin.runtime.util.LazyAsyncValue
import aws.smithy.kotlin.runtime.util.PlatformProvider

/**
* Attempts to resolve requestChecksumCalculation from the specified sources.
* @return requestChecksumCalculation setting if found, the default value if not.
*/
@InternalSdkApi
public suspend fun resolveRequestChecksumCalculation(platform: PlatformProvider = PlatformProvider.System, profile: LazyAsyncValue<AwsProfile>): HttpChecksumConfigOption {
val unparsedString = AwsSdkSetting.AwsRequestChecksumCalculation.resolve(platform) ?: profile.get().requestChecksumCalculation
return parseHttpChecksumConfigOption(unparsedString, "requestChecksumCalculation")
}

/**
* Attempts to resolve responseChecksumValidation from the specified sources.
* @return responseChecksumValidation setting if found, the default value if not.
*/
@InternalSdkApi
public suspend fun resolveResponseChecksumValidation(platform: PlatformProvider = PlatformProvider.System, profile: LazyAsyncValue<AwsProfile>): HttpChecksumConfigOption {
val unparsedString = AwsSdkSetting.AwsResponseChecksumValidation.resolve(platform) ?: profile.get().responseChecksumValidation
return parseHttpChecksumConfigOption(unparsedString, "responseChecksumValidation")
}

private fun parseHttpChecksumConfigOption(unparsedString: String?, configOption: String): HttpChecksumConfigOption =
when (unparsedString?.uppercase()) {
null -> HttpChecksumConfigOption.WHEN_SUPPORTED
"WHEN_SUPPORTED" -> HttpChecksumConfigOption.WHEN_SUPPORTED
"WHEN_REQUIRED" -> HttpChecksumConfigOption.WHEN_REQUIRED
else -> throw ConfigurationException(
"'$unparsedString' is not a valid value for $configOption. Valid values are: ${HttpChecksumConfigOption.entries}",
)
}

This file was deleted.

This file was deleted.

23 changes: 23 additions & 0 deletions aws-runtime/aws-http/api/aws-http.api
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,29 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInter
public fun readBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V
}

public final class aws/sdk/kotlin/runtime/http/interceptors/S3CompositeChecksumsInterceptor : aws/smithy/kotlin/runtime/client/Interceptor {
public fun <init> ()V
public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun modifyBeforeCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun modifyBeforeDeserialization (Laws/smithy/kotlin/runtime/client/ProtocolResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun modifyBeforeRetryLoop (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun modifyBeforeSerialization (Laws/smithy/kotlin/runtime/client/RequestInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun modifyBeforeSigning (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun modifyBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun readAfterAttempt (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;)V
public fun readAfterDeserialization (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;)V
public fun readAfterExecution (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;)V
public fun readAfterSerialization (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V
public fun readAfterSigning (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V
public fun readAfterTransmit (Laws/smithy/kotlin/runtime/client/ProtocolResponseInterceptorContext;)V
public fun readBeforeAttempt (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V
public fun readBeforeDeserialization (Laws/smithy/kotlin/runtime/client/ProtocolResponseInterceptorContext;)V
public fun readBeforeExecution (Laws/smithy/kotlin/runtime/client/RequestInterceptorContext;)V
public fun readBeforeSerialization (Laws/smithy/kotlin/runtime/client/RequestInterceptorContext;)V
public fun readBeforeSigning (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V
public fun readBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V
}

public final class aws/sdk/kotlin/runtime/http/interceptors/UnsupportedSigningAlgorithmInterceptor : aws/smithy/kotlin/runtime/client/Interceptor {
public fun <init> ()V
public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package aws.sdk.kotlin.runtime.http.interceptors

import aws.smithy.kotlin.runtime.client.ProtocolResponseInterceptorContext
import aws.smithy.kotlin.runtime.http.HeadersBuilder
import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor
import aws.smithy.kotlin.runtime.http.request.HttpRequest
import aws.smithy.kotlin.runtime.http.response.HttpResponse
import aws.smithy.kotlin.runtime.http.response.toBuilder
import aws.smithy.kotlin.runtime.telemetry.logging.Logger
import aws.smithy.kotlin.runtime.telemetry.logging.logger
import kotlin.coroutines.coroutineContext

private const val CHECKSUM_HEADER_PREFIX = "x-amz-checksum-"

/**
* Removes any checksum headers that contain composite checksums from an S3 response.
*/
public class S3CompositeChecksumsInterceptor : HttpInterceptor {
override suspend fun modifyBeforeDeserialization(context: ProtocolResponseInterceptorContext<Any, HttpRequest, HttpResponse>): HttpResponse {
val response = context.protocolResponse.toBuilder()
val logger = coroutineContext.logger<S3CompositeChecksumsInterceptor>()

response.headers.removeCompositeChecksums(logger)

return response.build()
}
}

/**
* Removes any checksum headers that contain composite checksums.
*/
internal fun HeadersBuilder.removeCompositeChecksums(logger: Logger): Unit =
names().forEach { header ->
if (header.startsWith(CHECKSUM_HEADER_PREFIX, ignoreCase = true) && get(header)!!.isCompositeChecksum()) {
logger.warn { "S3 returned a composite checksum, composite checksums are not supported, removing checksum" }
remove(header)
}
}

/**
* Verifies if a checksum is composite.
*/
private fun String.isCompositeChecksum(): Boolean {
// Ends with "-#" where "#" is a number
val regex = Regex("-(\\d)+$")
return regex.containsMatchIn(this)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package aws.sdk.kotlin.runtime.http.interceptors

import aws.smithy.kotlin.runtime.http.HeadersBuilder
import aws.smithy.kotlin.runtime.telemetry.logging.logger
import kotlinx.coroutines.test.runTest
import kotlin.coroutines.coroutineContext
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class S3CompositeChecksumsInterceptorTest {
@Test
fun compositeChecksumRemoval() = runTest {
val headers = HeadersBuilder()

headers.append("x-amz-checksum-crc32", "foo-1")
headers.append("x-amz-checksum-sha256", "bar")

assertTrue(
headers.contains("x-amz-checksum-crc32"),
)
assertTrue(
headers.contains("x-amz-checksum-sha256"),
)

headers.removeCompositeChecksums(coroutineContext.logger<S3CompositeChecksumsInterceptor>())

assertFalse(
headers.contains("x-amz-checksum-crc32"),
)
assertTrue(
headers.contains("x-amz-checksum-sha256"),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ object AwsRuntimeTypes {
val UnsupportedSigningAlgorithmInterceptor = symbol("UnsupportedSigningAlgorithmInterceptor")
val BusinessMetricsInterceptor = symbol("BusinessMetricsInterceptor")
val AwsBusinessMetric = symbol("AwsBusinessMetric")
val S3CompositeChecksumsInterceptor = symbol("S3CompositeChecksumsInterceptor")
}

object Retries {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.StructureShape

/**
* AWS services that can return composite checksums in their responses
*/
private val compositeChecksumServices = setOf(
"s3",
)

/**
* Adds a middleware which validates checksums returned in responses if the user has opted-in.
*/
Expand Down Expand Up @@ -98,7 +91,6 @@ class FlexibleChecksumsResponse : KotlinIntegration {
) {
writer.write("input.#L?.value == \"ENABLED\",", requestValidationModeMemberName)
writer.write("config.responseChecksumValidation,")
writer.write("#L", compositeChecksumServices.contains(ctx.settings.sdkId))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package aws.sdk.kotlin.codegen.customization.flexiblechecksums.s3

import aws.sdk.kotlin.codegen.AwsRuntimeTypes.Http.Interceptors.S3CompositeChecksumsInterceptor
import aws.sdk.kotlin.codegen.customization.s3.isS3
import software.amazon.smithy.aws.traits.HttpChecksumTrait
import software.amazon.smithy.kotlin.codegen.KotlinSettings
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
import software.amazon.smithy.kotlin.codegen.model.expectShape
import software.amazon.smithy.kotlin.codegen.model.hasTrait
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.ServiceShape

/**
* Removes composite checksums returned by S3 so that flexible checksums won't validate them.
* Composite checksums are used for multipart uploads and end with "-#" where "#" is a number.
*/
class S3CompositeChecksumsIntegration : KotlinIntegration {
override val order: Byte
get() = -128

override fun enabledForService(model: Model, settings: KotlinSettings): Boolean =
model.expectShape<ServiceShape>(settings.service).isS3

override fun customizeMiddleware(ctx: ProtocolGenerator.GenerationContext, resolved: List<ProtocolMiddleware>) =
resolved + s3CompositeChecksumsMiddleware
}

/**
* Adds [S3CompositeChecksumsInterceptor] to interceptors when operation has flexible checksums.
*/
private val s3CompositeChecksumsMiddleware = object : ProtocolMiddleware {
override val name: String = "S3CompositeChecksumsMiddleware"

override fun isEnabledFor(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Boolean =
op.hasTrait<HttpChecksumTrait>()

override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) {
writer.write("op.interceptors.add(#T())", S3CompositeChecksumsInterceptor)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerato
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware
import software.amazon.smithy.kotlin.codegen.rendering.util.ConfigProperty
import software.amazon.smithy.kotlin.codegen.rendering.util.ConfigPropertyType
import software.amazon.smithy.kotlin.codegen.utils.getOrNull
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.*
import software.amazon.smithy.model.traits.*
Expand Down Expand Up @@ -138,12 +139,25 @@ class S3ExpressIntegration : KotlinIntegration {
op.isS3UploadPart && op.hasTrait<HttpChecksumTrait>()

override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) {
val httpChecksumTrait = op.getTrait<HttpChecksumTrait>()!!

val requestAlgorithmMemberName = httpChecksumTrait.requestAlgorithmMember?.getOrNull()?.let {
val requestAlgorithmMemberShape = ctx.model.expectShape<StructureShape>(op.input.get())
.members()
.first { it.memberName == httpChecksumTrait.requestAlgorithmMember.get() }
ctx.symbolProvider.toMemberName(requestAlgorithmMemberShape)
}

val interceptorSymbol = buildSymbol {
namespace = "aws.sdk.kotlin.services.s3.express"
name = "S3ExpressDisableChecksumInterceptor"
}
writer.addImport(interceptorSymbol)
writer.write("op.interceptors.add(#T())", interceptorSymbol)
writer.write(
"op.interceptors.add(#T(input.#L?.value != null))",
interceptorSymbol,
requestAlgorithmMemberName,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ aws.sdk.kotlin.codegen.endpoints.AddAwsEndpointFunctions
aws.sdk.kotlin.codegen.PresignerGenerator
aws.sdk.kotlin.codegen.customization.flexiblechecksums.FlexibleChecksumsRequest
aws.sdk.kotlin.codegen.customization.flexiblechecksums.FlexibleChecksumsResponse
aws.sdk.kotlin.codegen.customization.flexiblechecksums.s3.S3CompositeChecksumsIntegration
aws.sdk.kotlin.codegen.customization.AddAwsSpanAttributes
aws.sdk.kotlin.codegen.customization.s3.S3OperationErrorHandler
aws.sdk.kotlin.codegen.customization.s3.S3SigningConfig
Expand Down
Loading

0 comments on commit d665873

Please sign in to comment.