@@ -16,6 +16,7 @@ import aws.sdk.kotlin.runtime.execution.AuthAttributes
1616import software.aws.clientrt.client.ExecutionContext
1717import software.aws.clientrt.http.*
1818import software.aws.clientrt.http.operation.SdkHttpOperation
19+ import software.aws.clientrt.http.operation.logger
1920import software.aws.clientrt.time.epochMilliseconds
2021import software.aws.clientrt.util.get
2122
@@ -91,6 +92,28 @@ public class AwsSigV4SigningMiddleware internal constructor(private val config:
9192 // otherwise to sign a request we need to convert: builder -> crt kotlin HttpRequest (which underneath converts to aws-c-http message) and back
9293 val signableRequest = req.subject.toSignableCrtRequest()
9394
95+ // SDKs are supposed to default to signed payload _always_ when possible (and when `unsignedPayload` trait isn't present).
96+ //
97+ // There are a few escape hatches/special cases:
98+ // 1. Customer explicitly disables signed payload (via AuthAttributes.UnsignedPayload)
99+ // 2. Customer provides a (potentially) unbounded stream (via HttpBody.Streaming)
100+ //
101+ // When an unbounded stream (2) is given we proceed as follows:
102+ // 2.1. is it a file?
103+ // (2.1.1) yes -> sign the payload (bounded stream/special case)
104+ // (2.1.2) no -> unsigned payload
105+ //
106+ // NOTE: Chunked signing is NOT enabled through this middleware.
107+ // NOTE:
108+ // 2.1.1 is handled by toSignableRequest() by special casing file inputs
109+ // 2.1.2 is handled below
110+ //
111+
112+ // FIXME - see: https://github.com/awslabs/smithy-kotlin/issues/296
113+ // if we know we have a (streaming) body and toSignableRequest() fails to convert it to a CRT equivalent
114+ // then we must decide how to compute the payload hash ourselves (defaults to unsigned payload)
115+ val isUnboundedStream = signableRequest.body == null && req.subject.body is HttpBody .Streaming
116+
94117 val signingConfig: AwsSigningConfig = AwsSigningConfig .build {
95118 region = req.context[AuthAttributes .SigningRegion ]
96119 service = req.context.getOrNull(AuthAttributes .SigningService ) ? : checkNotNull(config.signingService)
@@ -107,6 +130,10 @@ public class AwsSigV4SigningMiddleware internal constructor(private val config:
107130 signedBodyValue = when {
108131 req.context.isUnsignedRequest() -> AwsSignedBodyValue .UNSIGNED_PAYLOAD
109132 req.subject.body is HttpBody .Empty -> AwsSignedBodyValue .EMPTY_SHA256
133+ isUnboundedStream -> {
134+ req.context.logger.warn { " unable to compute hash for unbounded stream; defaulting to unsigned payload" }
135+ AwsSignedBodyValue .UNSIGNED_PAYLOAD
136+ }
110137 // use the payload to compute the hash
111138 else -> null
112139 }
0 commit comments