55package aws.sdk.kotlin.codegen
66
77import aws.sdk.kotlin.codegen.model.traits.Presignable
8+ import software.amazon.smithy.aws.traits.HttpChecksumTrait
89import software.amazon.smithy.aws.traits.auth.SigV4Trait
910import software.amazon.smithy.aws.traits.protocols.AwsQueryTrait
1011import software.amazon.smithy.codegen.core.Symbol
1112import software.amazon.smithy.kotlin.codegen.core.*
13+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Core.Hashing.isSupportedForFlexibleChecksums
14+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Core.Hashing.toHashFunctionOrThrow
15+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Core.IllegalStateException
16+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Core.Text.Encoding.encodeBase64String
17+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Core.Text.lowercase
18+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Core.Utils.runBlocking
19+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Http.HttpBody
20+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Http.readAll
21+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.KotlinCoroutines.coroutineContext
22+ import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Observability.TelemetryApi.warn
1223import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
1324import software.amazon.smithy.kotlin.codegen.integration.SectionId
1425import software.amazon.smithy.kotlin.codegen.integration.SectionKey
1526import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes
16- import software.amazon.smithy.kotlin.codegen.model.buildSymbol
17- import software.amazon.smithy.kotlin.codegen.model.expectShape
18- import software.amazon.smithy.kotlin.codegen.model.getTrait
27+ import software.amazon.smithy.kotlin.codegen.model.*
1928import software.amazon.smithy.kotlin.codegen.model.knowledge.AwsSignatureVersion4
2029import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointResolverAdapterGenerator
2130import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingProtocolGenerator
2231import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver
2332import software.amazon.smithy.kotlin.codegen.rendering.serde.serializerName
33+ import software.amazon.smithy.kotlin.codegen.utils.getOrNull
2434import software.amazon.smithy.model.knowledge.TopDownIndex
2535import software.amazon.smithy.model.shapes.OperationShape
2636import software.amazon.smithy.model.shapes.ServiceShape
@@ -149,6 +159,7 @@ class PresignerGenerator : KotlinIntegration {
149159 requestSymbol,
150160 serializerSymbol,
151161 contextMap,
162+ op,
152163 )
153164 }
154165 }
@@ -193,6 +204,7 @@ class PresignerGenerator : KotlinIntegration {
193204 requestSymbol : Symbol ,
194205 serializerSymbol : Symbol ,
195206 contextMap : Map <SectionKey <* >, Any >,
207+ op : OperationShape ,
196208 ) = writer.apply {
197209 dokka {
198210 write(" Presign a [#T] using the configuration of this [#T]." , requestSymbol, serviceSymbol)
@@ -265,6 +277,45 @@ class PresignerGenerator : KotlinIntegration {
265277 )
266278 }
267279
280+ checksumAlgorithmMember(op, ctx)?.let { checksumAlgorithmMember ->
281+ withBlock(" input.#L?.value?.let { checksumAlgorithmString ->" , " }" , checksumAlgorithmMember) {
282+ withBlock(" when (unsignedRequest.body) {" , " }" ) {
283+ withBlock(" is #1T.Bytes, is #1T.Empty -> {" , " }" , HttpBody ) {
284+ write(" val checksumAlgorithm = checksumAlgorithmString.#T()" , toHashFunctionOrThrow)
285+ withInlineBlock(
286+ " if (checksumAlgorithm.#T) {" ,
287+ " }" ,
288+ isSupportedForFlexibleChecksums,
289+ ) {
290+ withBlock(" #T {" , " }" , runBlocking) {
291+ withBlock(" checksumAlgorithm.update(" , " )" ) {
292+ write(" unsignedRequest.body.#T() ?: byteArrayOf()" , readAll)
293+ }
294+ }
295+ write(
296+ " checksum = #S.#T() to checksumAlgorithm.digest().#T()" ,
297+ " x-amz-checksum-\$ {checksumAlgorithmString}" ,
298+ lowercase,
299+ encodeBase64String,
300+ )
301+ }
302+ withBlock(" else {" , " }" ) {
303+ withBlock(" #T {" , " }" , runBlocking) {
304+ write(" class Presigner" )
305+ write(
306+ " #T.#T<Presigner> { #S }" ,
307+ coroutineContext,
308+ warn,
309+ " The requested checksum algorithm is not supported for pre-signed URL checksums, sending request without checksum." ,
310+ )
311+ }
312+ }
313+ }
314+ write(" else -> throw #T(#S)" , IllegalStateException , " HTTP body type unsupported for pre-signed URL checksums." )
315+ }
316+ }
317+ }
318+
268319 declareSection(SigningConfigCustomizationSection )
269320
270321 write(" configBlock()" )
@@ -287,4 +338,24 @@ class PresignerGenerator : KotlinIntegration {
287338 * > "my-object/example/photo.user". This is an incorrect path for that object.
288339 */
289340 private fun normalizeUriPath (service : ServiceShape ) = service.sdkId != " S3"
341+
342+ /* *
343+ * Gets the checksum algorithm member if a user can configure request checksums otherwise null
344+ */
345+ private fun checksumAlgorithmMember (
346+ operationShape : OperationShape ,
347+ ctx : CodegenContext ,
348+ ): String? {
349+ operationShape.getTrait<HttpChecksumTrait >()?.let { httpChecksumTrait ->
350+ httpChecksumTrait.requestAlgorithmMember.getOrNull()?.let { requestAlgorithmMember ->
351+ val memberShape = ctx.model
352+ .expectShape<StructureShape >(operationShape.input.get())
353+ .members()
354+ .first { it.memberName == requestAlgorithmMember }
355+
356+ return ctx.symbolProvider.toMemberName(memberShape)
357+ }
358+ }
359+ return null
360+ }
290361}
0 commit comments