Skip to content

Commit d74c949

Browse files
committed
Refactor checksum interceptors
1 parent 0dd7886 commit d74c949

File tree

12 files changed

+306
-227
lines changed

12 files changed

+306
-227
lines changed

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ object RuntimeTypes {
8181
object Interceptors : RuntimeTypePackage(KotlinDependency.HTTP, "interceptors") {
8282
val ContinueInterceptor = symbol("ContinueInterceptor")
8383
val HttpInterceptor = symbol("HttpInterceptor")
84-
val Md5ChecksumInterceptor = symbol("Md5ChecksumInterceptor")
84+
val HttpChecksumRequiredInterceptor = symbol("HttpChecksumRequiredInterceptor")
8585
val FlexibleChecksumsRequestInterceptor = symbol("FlexibleChecksumsRequestInterceptor")
8686
val FlexibleChecksumsResponseInterceptor = symbol("FlexibleChecksumsResponseInterceptor")
8787
val ResponseLengthValidationInterceptor = symbol("ResponseLengthValidationInterceptor")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package software.amazon.smithy.kotlin.codegen.rendering.checksums
2+
3+
import software.amazon.smithy.aws.traits.HttpChecksumTrait
4+
import software.amazon.smithy.kotlin.codegen.KotlinSettings
5+
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
6+
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes
7+
import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
8+
import software.amazon.smithy.kotlin.codegen.model.hasTrait
9+
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
10+
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware
11+
import software.amazon.smithy.model.Model
12+
import software.amazon.smithy.model.shapes.OperationShape
13+
import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait
14+
15+
/**
16+
* Handles the `httpChecksumRequired` trait.
17+
* See: https://smithy.io/2.0/spec/http-bindings.html#httpchecksumrequired-trait
18+
*/
19+
class HttpChecksumRequiredIntegration : KotlinIntegration {
20+
override fun enabledForService(model: Model, settings: KotlinSettings): Boolean =
21+
model.isTraitApplied(HttpChecksumRequiredTrait::class.java)
22+
23+
override fun customizeMiddleware(
24+
ctx: ProtocolGenerator.GenerationContext,
25+
resolved: List<ProtocolMiddleware>,
26+
): List<ProtocolMiddleware> = resolved + httpChecksumRequiredDefaultAlgorithmMiddleware + httpChecksumRequiredMiddleware
27+
}
28+
29+
/**
30+
* Adds default checksum algorithm to the execution context
31+
*/
32+
private val httpChecksumRequiredDefaultAlgorithmMiddleware = object : ProtocolMiddleware {
33+
override val name: String = "httpChecksumRequiredDefaultAlgorithmMiddleware"
34+
override val order: Byte = -2 // Before S3 Express (possibly) changes the default (-1) and before calculating checksum (0)
35+
36+
override fun isEnabledFor(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Boolean =
37+
op.hasTrait<HttpChecksumRequiredTrait>() && !op.hasTrait<HttpChecksumTrait>()
38+
39+
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) {
40+
writer.write(
41+
"op.context[#T.DefaultChecksumAlgorithm] = #S",
42+
RuntimeTypes.HttpClient.Operation.HttpOperationContext,
43+
"MD5",
44+
)
45+
}
46+
}
47+
48+
/**
49+
* Adds interceptor to calculate request checksums.
50+
* The `httpChecksum` trait supersedes the `httpChecksumRequired` trait. If both are applied to an operation use `httpChecksum`.
51+
*
52+
* See: https://smithy.io/2.0/aws/aws-core.html#behavior-with-httpchecksumrequired
53+
*/
54+
private val httpChecksumRequiredMiddleware = object : ProtocolMiddleware {
55+
override val name: String = "httpChecksumRequiredMiddleware"
56+
57+
override fun isEnabledFor(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Boolean =
58+
op.hasTrait<HttpChecksumRequiredTrait>() && !op.hasTrait<HttpChecksumTrait>()
59+
60+
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) {
61+
writer.write(
62+
"op.interceptors.add(#T())",
63+
RuntimeTypes.HttpClient.Interceptors.HttpChecksumRequiredInterceptor
64+
)
65+
}
66+
}

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/protocol/HttpProtocolClientGenerator.kt

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import software.amazon.smithy.model.knowledge.OperationIndex
2121
import software.amazon.smithy.model.knowledge.TopDownIndex
2222
import software.amazon.smithy.model.shapes.OperationShape
2323
import software.amazon.smithy.model.traits.EndpointTrait
24-
import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait
2524

2625
/**
2726
* Renders an implementation of a service interface for HTTP protocol
@@ -317,8 +316,6 @@ open class HttpProtocolClientGenerator(
317316
.forEach { middleware ->
318317
middleware.render(ctx, op, writer)
319318
}
320-
321-
op.renderIsMd5ChecksumRequired(writer)
322319
}
323320

324321
/**
@@ -335,20 +332,6 @@ open class HttpProtocolClientGenerator(
335332
*/
336333
protected open fun renderAdditionalMethods(writer: KotlinWriter) { }
337334

338-
/**
339-
* Render optionally installing Md5ChecksumMiddleware.
340-
* The Md5 middleware will only be installed if the operation requires a checksum.
341-
*/
342-
private fun OperationShape.renderIsMd5ChecksumRequired(writer: KotlinWriter) {
343-
if (hasTrait<HttpChecksumRequiredTrait>()) {
344-
val interceptorSymbol = RuntimeTypes.HttpClient.Interceptors.Md5ChecksumInterceptor
345-
val inputSymbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape(inputShape))
346-
writer.withBlock("op.interceptors.add(#T<#T> {", "})", interceptorSymbol, inputSymbol) {
347-
writer.write("true")
348-
}
349-
}
350-
}
351-
352335
/**
353336
* render a utility function to populate an operation's ExecutionContext with defaults from service config, environment, etc
354337
*/

codegen/smithy-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ software.amazon.smithy.kotlin.codegen.rendering.endpoints.discovery.EndpointDisc
1212
software.amazon.smithy.kotlin.codegen.rendering.endpoints.SdkEndpointBuiltinIntegration
1313
software.amazon.smithy.kotlin.codegen.rendering.compression.RequestCompressionIntegration
1414
software.amazon.smithy.kotlin.codegen.rendering.auth.SigV4AsymmetricAuthSchemeIntegration
15-
software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestsIntegration
15+
software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestsIntegration
16+
software.amazon.smithy.kotlin.codegen.rendering.checksums.HttpChecksumRequiredIntegration

runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/AbstractChecksumInterceptor.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,33 @@ package aws.smithy.kotlin.runtime.http.interceptors
77

88
import aws.smithy.kotlin.runtime.InternalApi
99
import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext
10+
import aws.smithy.kotlin.runtime.http.operation.HttpOperationContext
1011
import aws.smithy.kotlin.runtime.http.request.HttpRequest
1112

13+
/**
14+
* Handles checksum calculation so that checksums will be cached during retry loop
15+
*/
1216
@InternalApi
1317
public abstract class AbstractChecksumInterceptor : HttpInterceptor {
1418
private var cachedChecksum: String? = null
1519

1620
override suspend fun modifyBeforeSigning(context: ProtocolRequestInterceptorContext<Any, HttpRequest>): HttpRequest {
17-
cachedChecksum ?: calculateChecksum(context).also { cachedChecksum = it }
18-
return cachedChecksum?.let { applyChecksum(context, it) } ?: context.protocolRequest
21+
cachedChecksum = cachedChecksum ?: calculateChecksum(context)
22+
23+
return if (cachedChecksum != null) {
24+
applyChecksum(context, cachedChecksum!!)
25+
} else {
26+
context.protocolRequest
27+
}
1928
}
2029

2130
public abstract suspend fun calculateChecksum(context: ProtocolRequestInterceptorContext<Any, HttpRequest>): String?
2231

2332
public abstract fun applyChecksum(context: ProtocolRequestInterceptorContext<Any, HttpRequest>, checksum: String): HttpRequest
2433
}
34+
35+
/**
36+
* @return The default checksum algorithm name, null if default checksums are disabled.
37+
*/
38+
internal fun defaultChecksumAlgorithmName(context: ProtocolRequestInterceptorContext<Any, HttpRequest>): String? =
39+
context.executionContext.getOrNull(HttpOperationContext.DefaultChecksumAlgorithm)

0 commit comments

Comments
 (0)