Skip to content

Commit 9760ee1

Browse files
committed
Self review
1 parent 3e4c891 commit 9760ee1

File tree

7 files changed

+63
-48
lines changed

7 files changed

+63
-48
lines changed

runtime/auth/http-auth-aws/common/src/aws/smithy/kotlin/runtime/http/auth/AwsHttpSigner.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ public class AwsHttpSigner(private val config: Config) : HttpSigner {
164164
hashSpecification = when {
165165
contextHashSpecification != null -> contextHashSpecification
166166
body is HttpBody.Empty -> HashSpecification.EmptyBody
167-
(body.isEligibleForAwsChunkedStreaming && enableAwsChunked) -> { // TODO: Enable AWS chunked for all ?!
167+
body.isEligibleForAwsChunkedStreaming && enableAwsChunked -> {
168168
if (request.headers.contains("x-amz-trailer")) {
169169
if (config.isUnsignedPayload) HashSpecification.StreamingUnsignedPayloadWithTrailers else HashSpecification.StreamingAws4HmacSha256PayloadWithTrailers
170170
} else {

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

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,43 @@ import aws.smithy.kotlin.runtime.http.request.HttpRequest
1818
import aws.smithy.kotlin.runtime.http.request.header
1919
import aws.smithy.kotlin.runtime.http.request.toBuilder
2020
import aws.smithy.kotlin.runtime.io.*
21+
import aws.smithy.kotlin.runtime.telemetry.logging.Logger
22+
import aws.smithy.kotlin.runtime.telemetry.logging.debug
2123
import aws.smithy.kotlin.runtime.telemetry.logging.logger
2224
import aws.smithy.kotlin.runtime.text.encoding.encodeBase64String
2325
import kotlinx.coroutines.CompletableDeferred
2426
import kotlinx.coroutines.job
2527
import kotlin.coroutines.coroutineContext
2628

2729
/**
28-
* TODO -
30+
* Calculates a request's checksum.
31+
*
32+
* If a user supplies a checksum via an HTTP header no calculation will be done. The exception is MD5, if a user
33+
* supplies an MD5 checksum header it will be ignored.
34+
*
35+
* If the request configuration and model requires checksum calculation:
36+
* - Check if the user configured a checksum algorithm for the request and attempt to use that.
37+
* - If no checksum is configured for the request then use the default checksum algorithm to calculate a checksum.
38+
*
39+
* If the request will be streamed:
40+
* - The checksum calculation is done asynchronously using a hashing & completing body.
41+
* - The checksum will be sent in a trailing header, once the request is consumed.
42+
*
43+
* If the request will not be streamed:
44+
* - The checksum calculation is done synchronously
45+
* - The checksum will be sent in a header
46+
*
47+
* Business metrics MUST be emitted for the checksum algorithm used.
48+
*
49+
* @param requestChecksumRequired Model sourced flag indicating if checksum calculation is mandatory.
50+
* @param requestChecksumCalculation Configuration option that determines when checksum calculation should be done.
51+
* @param userSelectedChecksumAlgorithm The checksum algorithm that the user selected for the request, may be null.
2952
*/
3053
@InternalApi
3154
public class FlexibleChecksumsRequestInterceptor(
3255
requestChecksumRequired: Boolean,
3356
requestChecksumCalculation: ChecksumConfigOption?,
34-
private val userSelectedChecksumAlgorithm: String?,
57+
userSelectedChecksumAlgorithm: String?,
3558
) : AbstractChecksumInterceptor() {
3659
private val forcedToCalculateChecksum = requestChecksumRequired || requestChecksumCalculation == ChecksumConfigOption.WHEN_SUPPORTED
3760
private val checksumHeader = StringBuilder("x-amz-checksum-")
@@ -55,7 +78,7 @@ public class FlexibleChecksumsRequestInterceptor(
5578
override suspend fun modifyBeforeSigning(context: ProtocolRequestInterceptorContext<Any, HttpRequest>): HttpRequest {
5679
val logger = coroutineContext.logger<FlexibleChecksumsRequestInterceptor>()
5780

58-
userProviderChecksumHeader(context.protocolRequest)?.let {
81+
userProviderChecksumHeader(context.protocolRequest, logger)?.let {
5982
logger.debug { "User supplied a checksum via header, skipping checksum calculation" }
6083

6184
val request = context.protocolRequest.toBuilder()
@@ -72,8 +95,6 @@ public class FlexibleChecksumsRequestInterceptor(
7295

7396
val request = context.protocolRequest.toBuilder()
7497

75-
// throw Exception("\nBody type: ${request.body::class.simpleName}\nEligible for chunked streaming: ${request.body.isEligibleForAwsChunkedStreaming}\nContent Length: ${request.body.contentLength}\nIs one shot: ${request.body.isOneShot}")
76-
7798
if (request.body.isEligibleForAwsChunkedStreaming) {
7899
val deferredChecksum = CompletableDeferred<String>(context.executionContext.coroutineContext.job)
79100

@@ -216,11 +237,17 @@ public class FlexibleChecksumsRequestInterceptor(
216237
* The header must start with "x-amz-checksum-" followed by the checksum algorithm's name.
217238
* MD5 is not considered a valid checksum algorithm.
218239
*/
219-
private fun userProviderChecksumHeader(request: HttpRequest): String? {
240+
private fun userProviderChecksumHeader(request: HttpRequest, logger: Logger): String? {
220241
request.headers.entries().forEach { header ->
221242
val headerName = header.key.lowercase()
222-
if (headerName.startsWith("x-amz-checksum-") && !headerName.endsWith("md5")) {
223-
return headerName
243+
if (headerName.startsWith("x-amz-checksum-")) {
244+
if (headerName.endsWith("md5")) {
245+
logger.debug {
246+
"User provided md5 request checksum via headers, md5 is not a valid algorithm, ignoring header"
247+
}
248+
} else {
249+
return headerName
250+
}
224251
}
225252
}
226253
return null

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

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import aws.smithy.kotlin.runtime.http.response.copy
1919
import aws.smithy.kotlin.runtime.http.toHashingBody
2020
import aws.smithy.kotlin.runtime.http.toHttpBody
2121
import aws.smithy.kotlin.runtime.io.*
22-
import aws.smithy.kotlin.runtime.telemetry.logging.debug
2322
import aws.smithy.kotlin.runtime.telemetry.logging.logger
2423
import aws.smithy.kotlin.runtime.text.encoding.encodeBase64String
2524
import kotlin.coroutines.coroutineContext
@@ -41,12 +40,12 @@ internal val CHECKSUM_HEADER_VALIDATION_PRIORITY_LIST: List<String> = listOf(
4140
*
4241
* Users can check which checksum was validated by referencing the `ResponseChecksumValidated` execution context variable.
4342
*
44-
* @param responseValidation Flag indicating if the checksum validation is mandatory.
43+
* @param responseValidationRequired Flag indicating if the checksum validation is mandatory.
4544
* @param responseChecksumValidation Configuration option that determines when checksum validation should be done.
4645
*/
4746
@InternalApi
4847
public class FlexibleChecksumsResponseInterceptor(
49-
private val responseValidation: Boolean,
48+
private val responseValidationRequired: Boolean,
5049
private val responseChecksumValidation: ChecksumConfigOption?,
5150
) : HttpInterceptor {
5251
@InternalApi
@@ -58,40 +57,47 @@ public class FlexibleChecksumsResponseInterceptor(
5857
override suspend fun modifyBeforeDeserialization(context: ProtocolResponseInterceptorContext<Any, HttpRequest, HttpResponse>): HttpResponse {
5958
val logger = coroutineContext.logger<FlexibleChecksumsResponseInterceptor>()
6059

61-
val forcedToVerifyChecksum = responseValidation || responseChecksumValidation == ChecksumConfigOption.WHEN_SUPPORTED
60+
val forcedToVerifyChecksum = responseValidationRequired || responseChecksumValidation == ChecksumConfigOption.WHEN_SUPPORTED
6261
if (!forcedToVerifyChecksum) return context.protocolResponse
6362

6463
val checksumHeader = CHECKSUM_HEADER_VALIDATION_PRIORITY_LIST
6564
.firstOrNull { context.protocolResponse.headers.contains(it) } ?: run {
6665
logger.warn { "User requested checksum validation, but the response headers did not contain any valid checksums" }
6766
return context.protocolResponse
6867
}
69-
val checksumAlgorithm = checksumHeader.removePrefix("x-amz-checksum-").toHashFunction() ?: throw ClientException("Could not parse checksum algorithm from header $checksumHeader")
70-
val checksumValue = context.protocolResponse.headers[checksumHeader]!!
68+
val serviceChecksumValue = context.protocolResponse.headers[checksumHeader]!!
7169

72-
if (checksumValue.isCompositeChecksum()) {
70+
if (serviceChecksumValue.isCompositeChecksum()) {
7371
logger.debug { "Service returned composite checksum. Skipping validation." }
7472
return context.protocolResponse
7573
}
7674

7775
logger.debug { "Validating checksum from $checksumHeader" }
7876
context.executionContext[ChecksumHeaderValidated] = checksumHeader
7977

78+
val checksumAlgorithm = checksumHeader
79+
.removePrefix("x-amz-checksum-")
80+
.toHashFunction() ?: throw ClientException("Could not parse checksum algorithm from header $checksumHeader")
81+
8082
if (context.protocolResponse.body is HttpBody.Bytes) {
83+
// Calculate checksum
8184
checksumAlgorithm.update(
8285
context.protocolResponse.body.readAll() ?: byteArrayOf(),
8386
)
87+
val sdkChecksumValue = checksumAlgorithm.digest().encodeBase64String()
88+
8489
validateAndThrow(
85-
checksumValue,
86-
checksumAlgorithm.digest().encodeBase64String(),
90+
serviceChecksumValue,
91+
sdkChecksumValue,
8792
)
93+
8894
return context.protocolResponse
8995
} else {
9096
// Wrap the response body in a hashing body
9197
return context.protocolResponse.copy(
9298
body = context.protocolResponse.body
9399
.toHashingBody(checksumAlgorithm, context.protocolResponse.body.contentLength)
94-
.toChecksumValidatingBody(checksumValue),
100+
.toChecksumValidatingBody(serviceChecksumValue),
95101
)
96102
}
97103
}

runtime/protocol/http-client/common/test/aws/smithy/kotlin/runtime/http/interceptors/FlexibleChecksumsResponseInterceptorTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class FlexibleChecksumsResponseInterceptorTest {
7575

7676
op.interceptors.add(
7777
FlexibleChecksumsResponseInterceptor(
78-
responseValidation = true,
78+
responseValidationRequired = true,
7979
responseChecksumValidation = ChecksumConfigOption.WHEN_SUPPORTED,
8080
),
8181
)
@@ -102,7 +102,7 @@ class FlexibleChecksumsResponseInterceptorTest {
102102

103103
op.interceptors.add(
104104
FlexibleChecksumsResponseInterceptor(
105-
responseValidation = true,
105+
responseValidationRequired = true,
106106
responseChecksumValidation = ChecksumConfigOption.WHEN_SUPPORTED,
107107
),
108108
)
@@ -130,7 +130,7 @@ class FlexibleChecksumsResponseInterceptorTest {
130130

131131
op.interceptors.add(
132132
FlexibleChecksumsResponseInterceptor(
133-
responseValidation = true,
133+
responseValidationRequired = true,
134134
responseChecksumValidation = ChecksumConfigOption.WHEN_SUPPORTED,
135135
),
136136
)
@@ -155,7 +155,7 @@ class FlexibleChecksumsResponseInterceptorTest {
155155

156156
op.interceptors.add(
157157
FlexibleChecksumsResponseInterceptor(
158-
responseValidation = true,
158+
responseValidationRequired = true,
159159
responseChecksumValidation = ChecksumConfigOption.WHEN_SUPPORTED,
160160
),
161161
)

runtime/runtime-core/api/runtime-core.api

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -697,11 +697,6 @@ public final class aws/smithy/kotlin/runtime/hashing/HashFunctionKt {
697697
public static final fun toHashFunction (Ljava/lang/String;)Laws/smithy/kotlin/runtime/hashing/HashFunction;
698698
}
699699

700-
public final class aws/smithy/kotlin/runtime/hashing/HashingAttributes {
701-
public static final field INSTANCE Laws/smithy/kotlin/runtime/hashing/HashingAttributes;
702-
public final fun getChecksumStreamingRequest ()Laws/smithy/kotlin/runtime/collections/AttributeKey;
703-
}
704-
705700
public final class aws/smithy/kotlin/runtime/hashing/HmacKt {
706701
public static final fun hmac ([B[BLaws/smithy/kotlin/runtime/hashing/HashFunction;)[B
707702
public static final fun hmac ([B[BLkotlin/jvm/functions/Function0;)[B

runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/hashing/HashingAttributes.kt

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
11
package aws.smithy.kotlin.runtime.client.config
22

33
/**
4-
* todo
4+
* Client config for HTTP checksums
55
*/
66
public interface HttpChecksumClientConfig {
77
/**
8-
* todo
8+
* Configures request checksum calculation
99
*/
1010
public val requestChecksumCalculation: ChecksumConfigOption?
1111

1212
/**
13-
* todo
13+
* Configures response checksum validation
1414
*/
1515
public val responseChecksumValidation: ChecksumConfigOption?
1616

1717
public interface Builder {
1818
/**
19-
* todo
19+
* Configures request checksum calculation
2020
*/
2121
public var requestChecksumCalculation: ChecksumConfigOption?
2222

2323
/**
24-
* todo
24+
* Configures response checksum validation
2525
*/
2626
public var responseChecksumValidation: ChecksumConfigOption?
2727
}
2828
}
2929

3030
public enum class ChecksumConfigOption {
3131
/**
32-
* todo
32+
* SDK will create/validate checksum if the service marks it as required or if this is set.
3333
*/
3434
WHEN_SUPPORTED,
3535

3636
/**
37-
* todo
37+
* SDK will only create/validate checksum if the service marks it as required.
3838
*/
3939
WHEN_REQUIRED,
4040
}

0 commit comments

Comments
 (0)