Skip to content

Commit fdc2659

Browse files
committed
fix: preserve logging conext for streaming body signing
1 parent 28465b6 commit fdc2659

File tree

12 files changed

+149
-38
lines changed

12 files changed

+149
-38
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "15fd2749-fec2-4e7a-9639-3683fa5e94ed",
3+
"type": "bugfix",
4+
"description": "Preserve logging context for streaming payload signing"
5+
}

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/eventstream/EventStreamSerializerGenerator.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import software.amazon.smithy.kotlin.codegen.core.*
1111
import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes
1212
import software.amazon.smithy.kotlin.codegen.model.*
1313
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
14-
import software.amazon.smithy.kotlin.codegen.rendering.serde.*
14+
import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataSerializerGenerator
15+
import software.amazon.smithy.kotlin.codegen.rendering.serde.bodySerializer
16+
import software.amazon.smithy.kotlin.codegen.rendering.serde.bodySerializerName
17+
import software.amazon.smithy.kotlin.codegen.rendering.serde.serializerName
1518
import software.amazon.smithy.kotlin.codegen.utils.getOrNull
1619
import software.amazon.smithy.model.shapes.*
1720
import software.amazon.smithy.model.traits.EventHeaderTrait
@@ -110,7 +113,7 @@ class EventStreamSerializerGenerator(
110113
}
111114

112115
writer.write("")
113-
writer.write("return messages.#T(context)", RuntimeTypes.AwsEventStream.asEventStreamHttpBody)
116+
writer.write("return messages.#T()", RuntimeTypes.AwsEventStream.asEventStreamHttpBody)
114117
}
115118

116119
private fun encodeEventStreamMessage(

runtime/auth/aws-signing-common/api/aws-signing-common.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public final class aws/smithy/kotlin/runtime/auth/awssigning/AwsChunkedByteReadC
2222
public final class aws/smithy/kotlin/runtime/auth/awssigning/AwsChunkedSource : aws/smithy/kotlin/runtime/io/SdkSource {
2323
public fun <init> (Laws/smithy/kotlin/runtime/io/SdkSource;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;[BLaws/smithy/kotlin/runtime/http/DeferredHeaders;)V
2424
public synthetic fun <init> (Laws/smithy/kotlin/runtime/io/SdkSource;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;[BLaws/smithy/kotlin/runtime/http/DeferredHeaders;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
25+
public fun <init> (Laws/smithy/kotlin/runtime/io/SdkSource;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;[BLaws/smithy/kotlin/runtime/http/DeferredHeaders;Lkotlin/coroutines/CoroutineContext;)V
26+
public synthetic fun <init> (Laws/smithy/kotlin/runtime/io/SdkSource;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;[BLaws/smithy/kotlin/runtime/http/DeferredHeaders;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
2527
public fun close ()V
2628
public fun read (Laws/smithy/kotlin/runtime/io/SdkBuffer;J)J
2729
}
@@ -205,6 +207,7 @@ public final class aws/smithy/kotlin/runtime/auth/awssigning/internal/AwsChunked
205207
public static final fun getUseAwsChunkedEncoding (Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;)Z
206208
public static final fun isEligibleForAwsChunkedStreaming (Laws/smithy/kotlin/runtime/http/HttpBody;)Z
207209
public static final fun setAwsChunkedBody (Laws/smithy/kotlin/runtime/http/request/HttpRequestBuilder;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;[BLaws/smithy/kotlin/runtime/http/DeferredHeaders;)V
210+
public static final fun setAwsChunkedBody (Laws/smithy/kotlin/runtime/http/request/HttpRequestBuilder;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;[BLaws/smithy/kotlin/runtime/http/DeferredHeaders;Lkotlin/coroutines/CoroutineContext;)V
208211
public static final fun setAwsChunkedHeaders (Laws/smithy/kotlin/runtime/http/request/HttpRequestBuilder;)V
209212
}
210213

runtime/auth/aws-signing-common/common/src/aws/smithy/kotlin/runtime/auth/awssigning/AwsChunkedSource.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
package aws.smithy.kotlin.runtime.auth.awssigning
66

77
import aws.smithy.kotlin.runtime.InternalApi
8+
import aws.smithy.kotlin.runtime.PlannedRemoval
89
import aws.smithy.kotlin.runtime.auth.awssigning.internal.AwsChunkedReader
910
import aws.smithy.kotlin.runtime.http.DeferredHeaders
1011
import aws.smithy.kotlin.runtime.io.SdkBuffer
1112
import aws.smithy.kotlin.runtime.io.SdkSource
1213
import aws.smithy.kotlin.runtime.io.buffer
1314
import kotlinx.coroutines.runBlocking
15+
import kotlin.coroutines.CoroutineContext
16+
import kotlin.coroutines.EmptyCoroutineContext
1417

1518
/**
1619
* aws-chunked content encoding.
@@ -31,7 +34,25 @@ public class AwsChunkedSource(
3134
signingConfig: AwsSigningConfig,
3235
previousSignature: ByteArray,
3336
trailingHeaders: DeferredHeaders = DeferredHeaders.Empty,
37+
private val coroutineContext: CoroutineContext,
3438
) : SdkSource {
39+
@Deprecated(
40+
"This overload causes `runBlocking` to be called without a CoroutineContext which leads to forgetting " +
41+
"logging context. This overload will be removed in minor version 1.7.",
42+
ReplaceWith(
43+
"AwsChunkedSource(delegate, signer, signingConfig, previousSignature, trailingHeaders, coroutineContext)",
44+
"kotlin.coroutines.coroutineContext",
45+
),
46+
)
47+
@PlannedRemoval(major = 1, minor = 7)
48+
public constructor(
49+
delegate: SdkSource,
50+
signer: AwsSigner,
51+
signingConfig: AwsSigningConfig,
52+
previousSignature: ByteArray,
53+
trailingHeaders: DeferredHeaders = DeferredHeaders.Empty,
54+
) : this(delegate, signer, signingConfig, previousSignature, trailingHeaders, EmptyCoroutineContext)
55+
3556
private val chunkReader = AwsChunkedReader(
3657
delegate.asStream(),
3758
signer,
@@ -43,7 +64,7 @@ public class AwsChunkedSource(
4364
override fun read(sink: SdkBuffer, limit: Long): Long {
4465
require(limit >= 0L) { "Invalid limit ($limit) must be >= 0L" }
4566
// COROUTINE SAFETY: runBlocking is allowed here because SdkSource is a synchronous blocking interface
46-
val isChunkValid = runBlocking {
67+
val isChunkValid = runBlocking(coroutineContext) {
4768
chunkReader.ensureValidChunk()
4869
}
4970
if (!isChunkValid) return -1L

runtime/auth/aws-signing-common/common/src/aws/smithy/kotlin/runtime/auth/awssigning/internal/AwsChunkedUtil.kt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ package aws.smithy.kotlin.runtime.auth.awssigning.internal
77

88
import aws.smithy.kotlin.runtime.ClientException
99
import aws.smithy.kotlin.runtime.InternalApi
10+
import aws.smithy.kotlin.runtime.PlannedRemoval
1011
import aws.smithy.kotlin.runtime.auth.awssigning.*
1112
import aws.smithy.kotlin.runtime.http.*
1213
import aws.smithy.kotlin.runtime.http.request.HttpRequestBuilder
1314
import aws.smithy.kotlin.runtime.io.SdkBuffer
15+
import kotlin.coroutines.CoroutineContext
16+
import kotlin.coroutines.EmptyCoroutineContext
1417

1518
/**
1619
* Chunk size used by Transfer-Encoding `aws-chunked`
@@ -76,8 +79,34 @@ public fun HttpRequestBuilder.setAwsChunkedHeaders() {
7679
/**
7780
* Update the HTTP body to use aws-chunked content encoding
7881
*/
82+
@Deprecated(
83+
"This overload causes `runBlocking` to be called without a CoroutineContext which leads to forgetting logging " +
84+
"context. This overload will be removed in minor version 1.7.",
85+
ReplaceWith(
86+
"AwsChunkedSource(delegate, signer, signingConfig, previousSignature, trailingHeaders, coroutineContext)",
87+
"kotlin.coroutines.coroutineContext",
88+
),
89+
)
90+
@PlannedRemoval(major = 1, minor = 7)
7991
@InternalApi
80-
public fun HttpRequestBuilder.setAwsChunkedBody(signer: AwsSigner, signingConfig: AwsSigningConfig, signature: ByteArray, trailingHeaders: DeferredHeaders) {
92+
public fun HttpRequestBuilder.setAwsChunkedBody(
93+
signer: AwsSigner,
94+
signingConfig: AwsSigningConfig,
95+
signature: ByteArray,
96+
trailingHeaders: DeferredHeaders,
97+
): Unit = setAwsChunkedBody(signer, signingConfig, signature, trailingHeaders, EmptyCoroutineContext)
98+
99+
/**
100+
* Update the HTTP body to use aws-chunked content encoding
101+
*/
102+
@InternalApi
103+
public fun HttpRequestBuilder.setAwsChunkedBody(
104+
signer: AwsSigner,
105+
signingConfig: AwsSigningConfig,
106+
signature: ByteArray,
107+
trailingHeaders: DeferredHeaders,
108+
coroutineContext: CoroutineContext,
109+
) {
81110
body = when (body) {
82111
is HttpBody.ChannelContent -> AwsChunkedByteReadChannel(
83112
checkNotNull(body.toSdkByteReadChannel()),
@@ -93,6 +122,7 @@ public fun HttpRequestBuilder.setAwsChunkedBody(signer: AwsSigner, signingConfig
93122
signingConfig,
94123
signature,
95124
trailingHeaders,
125+
coroutineContext,
96126
).toHttpBody(-1)
97127

98128
else -> throw ClientException("HttpBody type is not supported")

runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedByteReadChannelTestBase.kt

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,42 @@
55

66
package aws.smithy.kotlin.runtime.auth.awssigning.tests
77

8-
import aws.smithy.kotlin.runtime.auth.awssigning.*
8+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsChunkedByteReadChannel
9+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigner
10+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigningConfig
911
import aws.smithy.kotlin.runtime.auth.awssigning.internal.CHUNK_SIZE_BYTES
12+
import aws.smithy.kotlin.runtime.http.DeferredHeaders
1013
import aws.smithy.kotlin.runtime.io.*
1114
import kotlinx.coroutines.delay
1215
import kotlinx.coroutines.launch
1316
import kotlinx.coroutines.test.TestResult
1417
import kotlinx.coroutines.test.runTest
1518
import kotlin.random.Random
16-
import kotlin.test.*
19+
import kotlin.test.Test
20+
import kotlin.test.assertEquals
21+
import kotlin.test.assertTrue
1722
import kotlin.time.Duration.Companion.milliseconds
1823

19-
abstract class AwsChunkedByteReadChannelTestBase : AwsChunkedTestBase(AwsChunkedReaderFactory.Channel) {
24+
private val chunkedChannelFactory = object : AwsChunkedReaderFactory {
25+
override fun create(
26+
data: ByteArray,
27+
signer: AwsSigner,
28+
signingConfig: AwsSigningConfig,
29+
previousSignature: ByteArray,
30+
trailingHeaders: DeferredHeaders,
31+
): AwsChunkedTestReader {
32+
33+
val ch = SdkByteReadChannel(data)
34+
val chunked = AwsChunkedByteReadChannel(ch, signer, signingConfig, previousSignature, trailingHeaders)
35+
return object : AwsChunkedTestReader {
36+
override fun isClosedForRead(): Boolean = chunked.isClosedForRead
37+
override suspend fun read(sink: SdkBuffer, limit: Long): Long = chunked.read(sink, limit)
38+
override fun close() = Unit
39+
}
40+
}
41+
}
42+
43+
abstract class AwsChunkedByteReadChannelTestBase : AwsChunkedTestBase(chunkedChannelFactory) {
2044
@Test
2145
fun testSlowProducerMultipleChunksPartialLast(): TestResult = runTest {
2246
val numChunks = 6

runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedSourceTestBase.kt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,44 @@
44
*/
55

66
package aws.smithy.kotlin.runtime.auth.awssigning.tests
7+
78
import aws.smithy.kotlin.runtime.auth.awssigning.AwsChunkedSource
9+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigner
10+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigningConfig
11+
import aws.smithy.kotlin.runtime.http.DeferredHeaders
812
import aws.smithy.kotlin.runtime.io.SdkBuffer
913
import aws.smithy.kotlin.runtime.io.source
14+
import kotlinx.coroutines.Dispatchers
1015

11-
val AwsChunkedReaderFactory.Companion.Source: AwsChunkedReaderFactory
12-
get() = AwsChunkedReaderFactory { data, signer, signingConfig, previousSignature, trailingHeaders ->
16+
private val chunkedSourceFactory = object : AwsChunkedReaderFactory {
17+
override fun create(
18+
data: ByteArray,
19+
signer: AwsSigner,
20+
signingConfig: AwsSigningConfig,
21+
previousSignature: ByteArray,
22+
trailingHeaders: DeferredHeaders
23+
): AwsChunkedTestReader {
1324
val source = data.source()
14-
val chunked = AwsChunkedSource(source, signer, signingConfig, previousSignature, trailingHeaders)
15-
object : AwsChunkedTestReader {
25+
val chunked = AwsChunkedSource(
26+
source,
27+
signer,
28+
signingConfig,
29+
previousSignature,
30+
trailingHeaders,
31+
Dispatchers.IO, // Cannot use default TestDispatcher because it doesn't support parallelism and causes hangs
32+
)
33+
return object : AwsChunkedTestReader {
1634
override fun isClosedForRead(): Boolean {
1735
val sink = SdkBuffer()
1836
val rc = chunked.read(sink, Long.MAX_VALUE)
1937
return rc == -1L
2038
}
2139
override suspend fun read(sink: SdkBuffer, limit: Long): Long = chunked.read(sink, limit)
40+
override fun close() {
41+
source.close()
42+
}
2243
}
2344
}
45+
}
2446

25-
public abstract class AwsChunkedSourceTestBase : AwsChunkedTestBase(AwsChunkedReaderFactory.Source)
47+
abstract class AwsChunkedSourceTestBase : AwsChunkedTestBase(chunkedSourceFactory)

runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedTestBase.kt

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55

66
package aws.smithy.kotlin.runtime.auth.awssigning.tests
77

8-
import aws.smithy.kotlin.runtime.auth.awssigning.*
8+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSignatureType
9+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigner
10+
import aws.smithy.kotlin.runtime.auth.awssigning.AwsSigningConfig
11+
import aws.smithy.kotlin.runtime.auth.awssigning.HashSpecification
912
import aws.smithy.kotlin.runtime.auth.awssigning.internal.CHUNK_SIZE_BYTES
1013
import aws.smithy.kotlin.runtime.http.DeferredHeaders
1114
import aws.smithy.kotlin.runtime.http.toHeaders
12-
import aws.smithy.kotlin.runtime.io.*
15+
import aws.smithy.kotlin.runtime.io.Closeable
16+
import aws.smithy.kotlin.runtime.io.SdkBuffer
1317
import aws.smithy.kotlin.runtime.time.Instant
1418
import kotlinx.coroutines.test.TestResult
1519
import kotlinx.coroutines.test.runTest
@@ -18,40 +22,22 @@ import kotlin.random.Random
1822
import kotlin.test.*
1923
import kotlin.time.Duration.Companion.seconds
2024

21-
interface AwsChunkedTestReader {
25+
interface AwsChunkedTestReader : Closeable {
2226
// This may modify the chunked reader state and cause loss of data!
2327
fun isClosedForRead(): Boolean
2428
suspend fun read(sink: SdkBuffer, limit: Long): Long
2529
}
2630

27-
fun interface AwsChunkedReaderFactory {
28-
companion object {
29-
val Channel = AwsChunkedReaderFactory { data, signer, signingConfig, previousSignature, trailingHeaders ->
30-
val ch = SdkByteReadChannel(data)
31-
val chunked = AwsChunkedByteReadChannel(ch, signer, signingConfig, previousSignature, trailingHeaders)
32-
object : AwsChunkedTestReader {
33-
override fun isClosedForRead(): Boolean = chunked.isClosedForRead
34-
override suspend fun read(sink: SdkBuffer, limit: Long): Long = chunked.read(sink, limit)
35-
}
36-
}
37-
}
38-
31+
interface AwsChunkedReaderFactory {
3932
fun create(
4033
data: ByteArray,
4134
signer: AwsSigner,
4235
signingConfig: AwsSigningConfig,
4336
previousSignature: ByteArray,
44-
trailingHeaders: DeferredHeaders,
37+
trailingHeaders: DeferredHeaders = DeferredHeaders.Empty,
4538
): AwsChunkedTestReader
4639
}
4740

48-
fun AwsChunkedReaderFactory.create(
49-
data: ByteArray,
50-
signer: AwsSigner,
51-
signingConfig: AwsSigningConfig,
52-
previousSignature: ByteArray,
53-
): AwsChunkedTestReader = create(data, signer, signingConfig, previousSignature, DeferredHeaders.Empty)
54-
5541
private val CHUNK_SIGNATURE_REGEX = Regex("chunk-signature=[a-zA-Z0-9]{64}") // alphanumeric, length of 64
5642
private val CHUNK_SIZE_REGEX = Regex("[0-9a-f]+;chunk-signature=") // hexadecimal, any length, immediately followed by the chunk signature
5743
private val UNSIGNED_CHUNK_SIZE_REGEX = Regex("[0-9a-f]+\r\n")

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import aws.smithy.kotlin.runtime.http.operation.HttpOperationContext
1919
import aws.smithy.kotlin.runtime.http.request.HttpRequest
2020
import aws.smithy.kotlin.runtime.http.request.HttpRequestBuilder
2121
import aws.smithy.kotlin.runtime.time.Instant
22+
import kotlin.coroutines.coroutineContext
2223
import kotlin.time.Duration
2324

2425
/**
@@ -195,6 +196,7 @@ public class AwsHttpSigner(private val config: Config) : HttpSigner {
195196
signingConfig,
196197
signingResult.signature,
197198
request.trailingHeaders.build(),
199+
coroutineContext,
198200
)
199201
}
200202
}

runtime/protocol/aws-event-stream/api/aws-event-stream.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public final class aws/smithy/kotlin/runtime/awsprotocol/eventstream/FrameDecode
1212
}
1313

1414
public final class aws/smithy/kotlin/runtime/awsprotocol/eventstream/FrameEncoderKt {
15+
public static final fun asEventStreamHttpBody (Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1516
public static final fun asEventStreamHttpBody (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1617
public static final fun encode (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
1718
}

0 commit comments

Comments
 (0)