Skip to content

Commit 46c2683

Browse files
xinsong-cuilauzadisaws-sdk-kotlin-ci
authored
misc: catch up to main (#1205)
* misc: enhance support for replayable instances of `InputStream` (#1197) * chore: release 1.3.31 * chore: bump snapshot version to 1.3.32-SNAPSHOT * fix: CBOR protocol test assertions / blob serialization (#1198) * fix: correctly serialize subset of shape's members when configured (#1199) * chore: release 1.3.32 * chore: bump snapshot version to 1.3.33-SNAPSHOT --------- Co-authored-by: Matas <[email protected]> Co-authored-by: aws-sdk-kotlin-ci <[email protected]>
1 parent 53f23fc commit 46c2683

File tree

11 files changed

+84
-11
lines changed

11 files changed

+84
-11
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## [1.3.32] - 01/06/2025
4+
5+
### Fixes
6+
* Fix serialization of CBOR blobs
7+
8+
## [1.3.31] - 12/18/2024
9+
10+
### Features
11+
* [#1473](https://github.com/awslabs/aws-sdk-kotlin/issues/1473) Enhance support for replayable instances of `InputStream`
12+
313
## [1.3.30] - 12/16/2024
414

515
## [1.3.29] - 12/12/2024

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/QueryHttpBindingProtocolGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ abstract class AbstractQueryFormUrlSerializerGenerator(
145145
return shape.documentSerializer(ctx.settings, symbol, members) { writer ->
146146
writer.openBlock("internal fun #identifier.name:L(serializer: #T, input: #T) {", RuntimeTypes.Serde.Serializer, symbol)
147147
.call {
148-
renderSerializerBody(ctx, shape, shape.members().toList(), writer)
148+
renderSerializerBody(ctx, shape, members.toList(), writer)
149149
}
150150
.closeBlock("}")
151151
}

codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RpcV2CborTest.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package software.amazon.smithy.kotlin.codegen.aws.protocols
66

7+
import io.kotest.matchers.string.shouldNotContain
78
import software.amazon.smithy.kotlin.codegen.test.*
89
import kotlin.test.Test
910

@@ -145,4 +146,20 @@ class RpcV2CborTest {
145146
val serializeBody = serializer.lines(" override suspend fun serialize(context: ExecutionContext, input: PutFooStreamingRequest): HttpRequestBuilder {", "}")
146147
serializeBody.shouldContainOnlyOnceWithDiff("""builder.headers.setMissing("Content-Type", "application/vnd.amazon.eventstream")""")
147148
}
149+
150+
@Test
151+
fun testEventStreamInitialRequestDoesNotSerializeStreamMember() {
152+
val ctx = model.newTestContext("CborExample")
153+
154+
val generator = RpcV2Cbor()
155+
generator.generateProtocolClient(ctx.generationCtx)
156+
157+
ctx.generationCtx.delegator.finalize()
158+
ctx.generationCtx.delegator.flushWriters()
159+
160+
val documentSerializer = ctx.manifest.expectFileString("/src/main/kotlin/com/test/serde/PutFooStreamingRequestDocumentSerializer.kt")
161+
162+
val serializeBody = documentSerializer.lines(" serializer.serializeStruct(OBJ_DESCRIPTOR) {", "}")
163+
serializeBody.shouldNotContain("input.messages") // `messages` is the stream member and should not be serialized in the initial request
164+
}
148165
}

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/protocol/HttpProtocolUnitTestRequestGenerator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,11 @@ open class HttpProtocolUnitTestRequestGenerator protected constructor(builder: B
117117
write("return")
118118
}
119119
write("requireNotNull(expectedBytes) { #S }", "expected application/cbor body cannot be null")
120-
write("requireNotNull(expectedBytes) { #S }", "actual application/cbor body cannot be null")
120+
write("requireNotNull(actualBytes) { #S }", "actual application/cbor body cannot be null")
121121

122122
write("")
123123
write("val expectedRequest = #L(#T(expectedBytes))", inputDeserializer.name, RuntimeTypes.Serde.SerdeCbor.CborDeserializer)
124-
write("val actualRequest = #L(#T(expectedBytes))", inputDeserializer.name, RuntimeTypes.Serde.SerdeCbor.CborDeserializer)
124+
write("val actualRequest = #L(#T(actualBytes))", inputDeserializer.name, RuntimeTypes.Serde.SerdeCbor.CborDeserializer)
125125
write("assertEquals(expectedRequest, actualRequest)")
126126
}
127127
writer.write("")

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/CborSerializerGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class CborSerializerGenerator(
108108
val symbol = ctx.symbolProvider.toSymbol(shape)
109109
return shape.documentSerializer(ctx.settings, symbol, members) { writer ->
110110
writer.withBlock("internal fun #identifier.name:L(serializer: #T, input: #T) {", "}", RuntimeTypes.Serde.Serializer, symbol) {
111-
call { renderSerializerBody(ctx, shape, shape.members().toList(), writer) }
111+
call { renderSerializerBody(ctx, shape, members.toList(), writer) }
112112
}
113113
}
114114
}

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGenerator.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,6 @@ open class SerializeStructGenerator(
647647
val target = member.targetOrSelf(ctx.model)
648648

649649
val encoded = when {
650-
target.type == ShapeType.BLOB -> writer.format("#L.#T()", identifier, RuntimeTypes.Core.Text.Encoding.encodeBase64String)
651650
target.type == ShapeType.TIMESTAMP -> {
652651
writer.addImport(RuntimeTypes.Core.TimestampFormat)
653652
val tsFormat = member

codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/SerializeStructGeneratorTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1822,7 +1822,7 @@ class SerializeStructGeneratorTest {
18221822

18231823
val expected = """
18241824
serializer.serializeStruct(OBJ_DESCRIPTOR) {
1825-
input.fooBlob?.let { field(FOOBLOB_DESCRIPTOR, it.encodeBase64String()) }
1825+
input.fooBlob?.let { field(FOOBLOB_DESCRIPTOR, it) }
18261826
}
18271827
""".trimIndent()
18281828

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ kotlinx.atomicfu.enableNativeIrTransformation=false
1313
org.gradle.jvmargs=-Xmx2G -XX:MaxMetaspaceSize=1G
1414

1515
# SDK
16-
sdkVersion=1.3.31-SNAPSHOT
16+
sdkVersion=1.3.33-SNAPSHOT
1717

1818
# codegen
19-
codegenVersion=0.33.31-SNAPSHOT
19+
codegenVersion=0.33.33-SNAPSHOT

runtime/runtime-core/jvm/src/aws/smithy/kotlin/runtime/content/ByteStreamJVM.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,30 @@ public fun ByteStream.Companion.fromInputStream(
114114
* @param contentLength If specified, indicates how many bytes remain in this stream. Defaults to `null`.
115115
*/
116116
public fun InputStream.asByteStream(contentLength: Long? = null): ByteStream.SourceStream {
117-
val source = source()
117+
if (markSupported() && contentLength != null) {
118+
mark(contentLength.toInt())
119+
}
120+
118121
return object : ByteStream.SourceStream() {
119122
override val contentLength: Long? = contentLength
120123
override val isOneShot: Boolean = !markSupported()
121-
override fun readFrom(): SdkSource = source
124+
override fun readFrom(): SdkSource {
125+
if (markSupported() && contentLength != null) {
126+
reset()
127+
mark(contentLength.toInt())
128+
return object : SdkSource by source() {
129+
/*
130+
* This is a no-op close to prevent body hashing from closing the underlying InputStream, which causes
131+
* `IOException: Stream closed` on subsequent reads. Consider making [ByteStream.ChannelStream]/[ByteStream.SourceStream]
132+
* (or possibly even [ByteStream] itself) implement [Closeable] to better handle closing streams.
133+
* This should allow us to clean up our usage of [ByteStream.cancel()].
134+
*/
135+
override fun close() { }
136+
}
137+
}
138+
139+
return source()
140+
}
122141
}
123142
}
124143

runtime/runtime-core/jvm/test/aws/smithy/kotlin/runtime/content/ByteStreamJVMTest.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55

66
package aws.smithy.kotlin.runtime.content
77

8+
import aws.smithy.kotlin.runtime.io.readToByteArray
89
import aws.smithy.kotlin.runtime.testing.RandomTempFile
910
import kotlinx.coroutines.test.runTest
11+
import java.io.BufferedInputStream
12+
import java.io.ByteArrayInputStream
1013
import java.io.ByteArrayOutputStream
1114
import java.io.InputStream
1215
import java.io.OutputStream
@@ -228,6 +231,31 @@ class ByteStreamJVMTest {
228231
assertFalse(sos.closed)
229232
}
230233

234+
// https://github.com/awslabs/aws-sdk-kotlin/issues/1473
235+
@Test
236+
fun testReplayableInputStreamAsByteStream() = runTest {
237+
val content = "Hello, Bytes!".encodeToByteArray()
238+
val byteArrayIns = ByteArrayInputStream(content)
239+
val nonReplayableIns = NonReplayableInputStream(byteArrayIns)
240+
241+
// buffer the non-replayable stream, making it replayable...
242+
val bufferedIns = BufferedInputStream(nonReplayableIns)
243+
244+
val byteStream = bufferedIns.asByteStream(content.size.toLong())
245+
246+
// Test that it can be read at least twice (e.g. once for hashing the body, once for transmitting the body)
247+
assertContentEquals(content, byteStream.readFrom().use { it.readToByteArray() })
248+
assertContentEquals(content, byteStream.readFrom().use { it.readToByteArray() })
249+
}
250+
251+
private class NonReplayableInputStream(val inputStream: InputStream) : InputStream() {
252+
override fun markSupported(): Boolean = false // not replayable
253+
254+
override fun read(): Int = inputStream.read()
255+
override fun mark(readlimit: Int) = inputStream.mark(readlimit)
256+
override fun reset() = inputStream.reset()
257+
}
258+
231259
private class StatusTrackingOutputStream(val os: OutputStream) : OutputStream() {
232260
var closed: Boolean = false
233261

0 commit comments

Comments
 (0)