in aws-runtime/aws-signing/common/src/aws/sdk/kotlin/runtime/auth/signing/AwsSigV4SigningMiddleware.kt [92:154]
override suspend fun modifyRequest(req: SdkHttpRequest): SdkHttpRequest {
val credentialsProvider = checkNotNull(config.credentialsProvider)
val resolvedCredentials = credentialsProvider.getCredentials()
val logger = req.context.getLogger("AwsSigv4SigningMiddleware")
val isUnsignedRequest = req.context.isUnsignedRequest()
// FIXME - an alternative here would be to just pre-compute the sha256 of the payload ourselves and set
// the signed body value on the signing config. This would prevent needing to launch a coroutine
// for streaming requests since we already have a suspend context.
val signableRequest = req.subject.toSignableCrtRequest(isUnsignedRequest)
// SDKs are supposed to default to signed payload _always_ when possible (and when `unsignedPayload` trait isn't present).
//
// There are a few escape hatches/special cases:
// 1. Customer explicitly disables signed payload (via AuthAttributes.UnsignedPayload)
// 2. Customer provides a (potentially) unbounded stream (via HttpBody.Streaming)
//
// When an unbounded stream (2) is given we proceed as follows:
// 2.1. is it replayable?
// (2.1.1) yes -> sign the payload (stream can be consumed more than once)
// (2.1.2) no -> unsigned payload
//
// NOTE: Chunked signing is NOT enabled through this middleware.
// NOTE: 2.1.2 is handled below
// FIXME - see: https://github.com/awslabs/smithy-kotlin/issues/296
// if we know we have a (streaming) body and toSignableRequest() fails to convert it to a CRT equivalent
// then we must decide how to compute the payload hash ourselves (defaults to unsigned payload)
val isUnboundedStream = signableRequest.body == null && req.subject.body is HttpBody.Streaming
// operation signing config is baseConfig + operation specific config/overrides
val opSigningConfig = AwsSigningConfig {
region = req.context[AuthAttributes.SigningRegion]
service = req.context.getOrNull(AuthAttributes.SigningService) ?: checkNotNull(config.signingService)
credentials = resolvedCredentials
algorithm = config.algorithm
date = req.context.getOrNull(AuthAttributes.SigningDate)
signatureType = config.signatureType
omitSessionToken = config.omitSessionToken
normalizeUriPath = config.normalizeUriPath
useDoubleUriEncode = config.useDoubleUriEncode
expiresAfter = config.expiresAfter
signedBodyHeader = config.signedBodyHeaderType
signedBodyValue = when {
isUnsignedRequest -> AwsSignedBodyValue.UNSIGNED_PAYLOAD
req.subject.body is HttpBody.Empty -> AwsSignedBodyValue.EMPTY_SHA256
isUnboundedStream -> {
logger.warn { "unable to compute hash for unbounded stream; defaulting to unsigned payload" }
AwsSignedBodyValue.UNSIGNED_PAYLOAD
}
// use the payload to compute the hash
else -> null
}
}
val signedRequest = AwsSigner.signRequest(signableRequest, opSigningConfig.toCrt())
req.subject.update(signedRequest)
req.subject.body.resetStream()
return req
}