diff --git a/.github/workflows/artifact-size-metrics.yml b/.github/workflows/artifact-size-metrics.yml index a9d3c7b6e9..fb0be57b67 100644 --- a/.github/workflows/artifact-size-metrics.yml +++ b/.github/workflows/artifact-size-metrics.yml @@ -45,23 +45,25 @@ jobs: steps: - name: Checkout Sources uses: actions/checkout@v4 - - name: Configure JDK - uses: actions/setup-java@v3 - with: - distribution: 'corretto' - java-version: 17 - cache: 'gradle' + + - name: Setup build + uses: .github/actions/setup-build + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.CI_AWS_ROLE_ARN }} aws-region: us-west-2 + - name: Configure Gradle uses: awslabs/aws-kotlin-repo-tools/.github/actions/configure-gradle@main + - name: Generate Artifact Size Metrics run: ./gradlew -Paws.kotlin.native=false artifactSizeMetrics + - name: Analyze Artifact Size Metrics run: ./gradlew analyzeArtifactSizeMetrics + - name: Show Results uses: actions/github-script@v7 with: diff --git a/runtime/auth/aws-signing-crt/api/aws-signing-crt.api b/runtime/auth/aws-signing-crt/api/aws-signing-crt.api index ed1934cac9..39dc9fdf45 100644 --- a/runtime/auth/aws-signing-crt/api/aws-signing-crt.api +++ b/runtime/auth/aws-signing-crt/api/aws-signing-crt.api @@ -1,4 +1,4 @@ -public final class aws/smithy/kotlin/runtime/auth/awssigning/crt/CrtAwsSigner : aws/smithy/kotlin/runtime/auth/awssigning/AwsSigner { +public final class aws/smithy/kotlin/runtime/auth/awssigning/crt/CrtAwsSigner : aws/sdk/kotlin/crt/WithCrt, aws/smithy/kotlin/runtime/auth/awssigning/AwsSigner { public static final field INSTANCE Laws/smithy/kotlin/runtime/auth/awssigning/crt/CrtAwsSigner; public fun sign (Laws/smithy/kotlin/runtime/http/request/HttpRequest;Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun signChunk ([B[BLaws/smithy/kotlin/runtime/auth/awssigning/AwsSigningConfig;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/runtime/auth/aws-signing-crt/jvmAndNative/src/aws/smithy/kotlin/runtime/auth/awssigning/crt/CrtAwsSigner.kt b/runtime/auth/aws-signing-crt/jvmAndNative/src/aws/smithy/kotlin/runtime/auth/awssigning/crt/CrtAwsSigner.kt index c0069c1aca..ac039d519b 100644 --- a/runtime/auth/aws-signing-crt/jvmAndNative/src/aws/smithy/kotlin/runtime/auth/awssigning/crt/CrtAwsSigner.kt +++ b/runtime/auth/aws-signing-crt/jvmAndNative/src/aws/smithy/kotlin/runtime/auth/awssigning/crt/CrtAwsSigner.kt @@ -4,6 +4,7 @@ */ package aws.smithy.kotlin.runtime.auth.awssigning.crt +import aws.sdk.kotlin.crt.WithCrt import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awssigning.* import aws.smithy.kotlin.runtime.crt.toSignableCrtRequest @@ -24,7 +25,7 @@ import aws.sdk.kotlin.crt.http.Headers as CrtHeaders private const val S3_EXPRESS_HEADER_NAME = "X-Amz-S3session-Token" -public object CrtAwsSigner : AwsSigner { +public object CrtAwsSigner : AwsSigner, WithCrt() { override suspend fun sign(request: HttpRequest, config: AwsSigningConfig): AwsSigningResult { val isUnsigned = config.hashSpecification is HashSpecification.UnsignedPayload val isAwsChunked = request.headers.contains("Content-Encoding", "aws-chunked") diff --git a/runtime/auth/aws-signing-default/api/aws-signing-default.api b/runtime/auth/aws-signing-default/api/aws-signing-default.api index e00e6d829a..15ca42c07e 100644 --- a/runtime/auth/aws-signing-default/api/aws-signing-default.api +++ b/runtime/auth/aws-signing-default/api/aws-signing-default.api @@ -5,7 +5,7 @@ public final class aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerBui public final fun setTelemetryProvider (Laws/smithy/kotlin/runtime/telemetry/TelemetryProvider;)V } -public final class aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerKt { +public final class aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerJVMKt { public static final fun DefaultAwsSigner (Lkotlin/jvm/functions/Function1;)Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner; public static final fun getDefaultAwsSigner ()Laws/smithy/kotlin/runtime/auth/awssigning/AwsSigner; } diff --git a/runtime/auth/aws-signing-default/build.gradle.kts b/runtime/auth/aws-signing-default/build.gradle.kts index 22e20d39fd..71781c66c7 100644 --- a/runtime/auth/aws-signing-default/build.gradle.kts +++ b/runtime/auth/aws-signing-default/build.gradle.kts @@ -25,5 +25,11 @@ kotlin { all { languageSettings.optIn("aws.smithy.kotlin.runtime.InternalApi") } + + nativeMain { + dependencies { + implementation(project(":runtime:auth:aws-signing-crt")) + } + } } } diff --git a/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSigner.kt b/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSigner.kt index 85896349b1..df5d2076b9 100644 --- a/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSigner.kt +++ b/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSigner.kt @@ -4,136 +4,5 @@ */ package aws.smithy.kotlin.runtime.auth.awssigning -import aws.smithy.kotlin.runtime.ExperimentalApi -import aws.smithy.kotlin.runtime.http.Headers -import aws.smithy.kotlin.runtime.http.request.HttpRequest -import aws.smithy.kotlin.runtime.telemetry.TelemetryProvider -import aws.smithy.kotlin.runtime.telemetry.logging.logger -import aws.smithy.kotlin.runtime.time.TimestampFormat -import kotlin.coroutines.coroutineContext - /** The default implementation of [AwsSigner] */ -public val DefaultAwsSigner: AwsSigner = DefaultAwsSignerImpl() - -/** Creates a customized instance of [AwsSigner] */ -@Suppress("ktlint:standard:function-naming") -public fun DefaultAwsSigner(block: DefaultAwsSignerBuilder.() -> Unit): AwsSigner = - DefaultAwsSignerBuilder().apply(block).build() - -/** A builder class for creating instances of [AwsSigner] using the default implementation */ -public class DefaultAwsSignerBuilder { - public var telemetryProvider: TelemetryProvider? = null - - public fun build(): AwsSigner = DefaultAwsSignerImpl( - telemetryProvider = telemetryProvider, - ) -} - -private val AwsSigningAlgorithm.signatureCalculator - get() = when (this) { - AwsSigningAlgorithm.SIGV4 -> SignatureCalculator.SigV4 - AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> SignatureCalculator.SigV4a - } - -@OptIn(ExperimentalApi::class) -internal class DefaultAwsSignerImpl( - private val canonicalizer: Canonicalizer = Canonicalizer.Default, - private val requestMutator: RequestMutator = RequestMutator.Default, - private val telemetryProvider: TelemetryProvider? = null, -) : AwsSigner { - override suspend fun sign(request: HttpRequest, config: AwsSigningConfig): AwsSigningResult { - val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner") - ?: coroutineContext.logger() - - val canonical = canonicalizer.canonicalRequest(request, config) - if (config.logRequest) { - logger.trace { "Canonical request:\n${canonical.requestString}" } - } - - val signatureCalculator = config.algorithm.signatureCalculator - - val stringToSign = signatureCalculator.stringToSign(canonical.requestString, config) - logger.trace { "String to sign:\n$stringToSign" } - - val signingKey = signatureCalculator.signingKey(config) - - val signature = signatureCalculator.calculate(signingKey, stringToSign) - logger.debug { "Calculated signature: $signature" } - - val signedRequest = requestMutator.appendAuth(config, canonical, signature) - - return AwsSigningResult(signedRequest, signature.encodeToByteArray()) - } - - override suspend fun signChunk( - chunkBody: ByteArray, - prevSignature: ByteArray, - config: AwsSigningConfig, - ): AwsSigningResult { - val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner") - ?: coroutineContext.logger() - - val signatureCalculator = config.algorithm.signatureCalculator - - val stringToSign = signatureCalculator.chunkStringToSign(chunkBody, prevSignature, config) - logger.trace { "Chunk string to sign:\n$stringToSign" } - - val signingKey = signatureCalculator.signingKey(config) - - val signature = signatureCalculator.calculate(signingKey, stringToSign) - logger.debug { "Calculated chunk signature: $signature" } - - return AwsSigningResult(Unit, signature.encodeToByteArray()) - } - - override suspend fun signChunkTrailer( - trailingHeaders: Headers, - prevSignature: ByteArray, - config: AwsSigningConfig, - ): AwsSigningResult { - val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner") - ?: coroutineContext.logger() - - val signatureCalculator = config.algorithm.signatureCalculator - - // FIXME - can we share canonicalization code more than we are..., also this reduce is inefficient. - // canonicalize the headers - val trailingHeadersBytes = trailingHeaders.entries().sortedBy { e -> e.key.lowercase() } - .map { e -> - buildString { - append(e.key.lowercase()) - append(":") - append(e.value.joinToString(",") { v -> v.trim() }) - append("\n") - }.encodeToByteArray() - }.reduce { acc, bytes -> acc + bytes } - - val stringToSign = signatureCalculator.chunkTrailerStringToSign(trailingHeadersBytes, prevSignature, config) - logger.trace { "Chunk trailer string to sign:\n$stringToSign" } - - val signingKey = signatureCalculator.signingKey(config) - - val signature = signatureCalculator.calculate(signingKey, stringToSign) - logger.debug { "Calculated chunk signature: $signature" } - - return AwsSigningResult(Unit, signature.encodeToByteArray()) - } -} - -/** - * Formats a credential scope consisting of a signing date, region (SigV4 only), service, and a signature type - */ -internal val AwsSigningConfig.credentialScope: String - get() = run { - val signingDate = signingDate.format(TimestampFormat.ISO_8601_CONDENSED_DATE) - return when (algorithm) { - AwsSigningAlgorithm.SIGV4 -> "$signingDate/$region/$service/aws4_request" - AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "$signingDate/$service/aws4_request" - } - } - -/** - * Formats the value for a credential header/parameter - */ -internal fun credentialValue(config: AwsSigningConfig): String = - "${config.credentials.accessKeyId}/${config.credentialScope}" +public expect val DefaultAwsSigner: AwsSigner diff --git a/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/RequestMutator.kt b/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/RequestMutator.kt index 0c1882cd54..ee67e7833b 100644 --- a/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/RequestMutator.kt +++ b/runtime/auth/aws-signing-default/common/src/aws/smithy/kotlin/runtime/auth/awssigning/RequestMutator.kt @@ -5,6 +5,7 @@ package aws.smithy.kotlin.runtime.auth.awssigning import aws.smithy.kotlin.runtime.http.request.HttpRequest +import aws.smithy.kotlin.runtime.time.TimestampFormat /** * An object that can mutate requests to include signing attributes. @@ -55,3 +56,21 @@ internal class DefaultRequestMutator : RequestMutator { return canonical.request.build() } } + +/** + * Formats a credential scope consisting of a signing date, region (SigV4 only), service, and a signature type + */ +internal val AwsSigningConfig.credentialScope: String + get() { + val signingDate = signingDate.format(TimestampFormat.ISO_8601_CONDENSED_DATE) + return when (algorithm) { + AwsSigningAlgorithm.SIGV4 -> "$signingDate/$region/$service/aws4_request" + AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> "$signingDate/$service/aws4_request" + } + } + +/** + * Formats the value for a credential header/parameter + */ +internal fun credentialValue(config: AwsSigningConfig): String = + "${config.credentials.accessKeyId}/${config.credentialScope}" diff --git a/runtime/auth/aws-signing-default/jvm/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerJVM.kt b/runtime/auth/aws-signing-default/jvm/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerJVM.kt new file mode 100644 index 0000000000..ea1f4723d6 --- /dev/null +++ b/runtime/auth/aws-signing-default/jvm/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerJVM.kt @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.smithy.kotlin.runtime.auth.awssigning + +import aws.smithy.kotlin.runtime.ExperimentalApi +import aws.smithy.kotlin.runtime.http.Headers +import aws.smithy.kotlin.runtime.http.request.HttpRequest +import aws.smithy.kotlin.runtime.telemetry.TelemetryProvider +import aws.smithy.kotlin.runtime.telemetry.logging.logger +import kotlin.coroutines.coroutineContext + +/** The default implementation of [AwsSigner] */ +public actual val DefaultAwsSigner: AwsSigner = DefaultAwsSignerImpl() + +/** Creates a customized instance of [AwsSigner] */ +@Suppress("ktlint:standard:function-naming") +public fun DefaultAwsSigner(block: DefaultAwsSignerBuilder.() -> Unit): AwsSigner = + DefaultAwsSignerBuilder().apply(block).build() + +/** A builder class for creating instances of [AwsSigner] using the default implementation */ +public class DefaultAwsSignerBuilder { + public var telemetryProvider: TelemetryProvider? = null + + public fun build(): AwsSigner = DefaultAwsSignerImpl( + telemetryProvider = telemetryProvider, + ) +} + +private val AwsSigningAlgorithm.signatureCalculator + get() = when (this) { + AwsSigningAlgorithm.SIGV4 -> SignatureCalculator.SigV4 + AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> SignatureCalculator.SigV4a + } + +@OptIn(ExperimentalApi::class) +internal class DefaultAwsSignerImpl( + private val canonicalizer: Canonicalizer = Canonicalizer.Default, + private val requestMutator: RequestMutator = RequestMutator.Default, + private val telemetryProvider: TelemetryProvider? = null, +) : AwsSigner { + override suspend fun sign(request: HttpRequest, config: AwsSigningConfig): AwsSigningResult { + val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner") + ?: coroutineContext.logger() + + val canonical = canonicalizer.canonicalRequest(request, config) + if (config.logRequest) { + logger.trace { "Canonical request:\n${canonical.requestString}" } + } + + val signatureCalculator = config.algorithm.signatureCalculator + + val stringToSign = signatureCalculator.stringToSign(canonical.requestString, config) + logger.trace { "String to sign:\n$stringToSign" } + + val signingKey = signatureCalculator.signingKey(config) + + val signature = signatureCalculator.calculate(signingKey, stringToSign) + logger.debug { "Calculated signature: $signature" } + + val signedRequest = requestMutator.appendAuth(config, canonical, signature) + + return AwsSigningResult(signedRequest, signature.encodeToByteArray()) + } + + override suspend fun signChunk( + chunkBody: ByteArray, + prevSignature: ByteArray, + config: AwsSigningConfig, + ): AwsSigningResult { + val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner") + ?: coroutineContext.logger() + + val signatureCalculator = config.algorithm.signatureCalculator + + val stringToSign = signatureCalculator.chunkStringToSign(chunkBody, prevSignature, config) + logger.trace { "Chunk string to sign:\n$stringToSign" } + + val signingKey = signatureCalculator.signingKey(config) + + val signature = signatureCalculator.calculate(signingKey, stringToSign) + logger.debug { "Calculated chunk signature: $signature" } + + return AwsSigningResult(Unit, signature.encodeToByteArray()) + } + + override suspend fun signChunkTrailer( + trailingHeaders: Headers, + prevSignature: ByteArray, + config: AwsSigningConfig, + ): AwsSigningResult { + val logger = telemetryProvider?.loggerProvider?.getOrCreateLogger("DefaultAwsSigner") + ?: coroutineContext.logger() + + val signatureCalculator = config.algorithm.signatureCalculator + + // FIXME - can we share canonicalization code more than we are..., also this reduce is inefficient. + // canonicalize the headers + val trailingHeadersBytes = trailingHeaders.entries().sortedBy { e -> e.key.lowercase() } + .map { e -> + buildString { + append(e.key.lowercase()) + append(":") + append(e.value.joinToString(",") { v -> v.trim() }) + append("\n") + }.encodeToByteArray() + }.reduce { acc, bytes -> acc + bytes } + + val stringToSign = signatureCalculator.chunkTrailerStringToSign(trailingHeadersBytes, prevSignature, config) + logger.trace { "Chunk trailer string to sign:\n$stringToSign" } + + val signingKey = signatureCalculator.signingKey(config) + + val signature = signatureCalculator.calculate(signingKey, stringToSign) + logger.debug { "Calculated chunk signature: $signature" } + + return AwsSigningResult(Unit, signature.encodeToByteArray()) + } +} diff --git a/runtime/auth/aws-signing-default/native/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerNative.kt b/runtime/auth/aws-signing-default/native/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerNative.kt new file mode 100644 index 0000000000..0b9335ec1d --- /dev/null +++ b/runtime/auth/aws-signing-default/native/src/aws/smithy/kotlin/runtime/auth/awssigning/DefaultAwsSignerNative.kt @@ -0,0 +1,10 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.smithy.kotlin.runtime.auth.awssigning + +import aws.smithy.kotlin.runtime.auth.awssigning.crt.CrtAwsSigner + +/** The default implementation of [AwsSigner] */ +public actual val DefaultAwsSigner: AwsSigner = CrtAwsSigner diff --git a/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedByteReadChannelTestBase.kt b/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedByteReadChannelTestBase.kt index 3374dedc42..f86ef5ba3a 100644 --- a/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedByteReadChannelTestBase.kt +++ b/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedByteReadChannelTestBase.kt @@ -5,7 +5,6 @@ package aws.smithy.kotlin.runtime.auth.awssigning.tests -import aws.smithy.kotlin.runtime.IgnoreNative import aws.smithy.kotlin.runtime.auth.awssigning.* import aws.smithy.kotlin.runtime.auth.awssigning.internal.CHUNK_SIZE_BYTES import aws.smithy.kotlin.runtime.io.* @@ -18,7 +17,6 @@ import kotlin.test.* import kotlin.time.Duration.Companion.milliseconds abstract class AwsChunkedByteReadChannelTestBase : AwsChunkedTestBase(AwsChunkedReaderFactory.Channel) { - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testSlowProducerMultipleChunksPartialLast(): TestResult = runTest { val numChunks = 6 diff --git a/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedTestBase.kt b/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedTestBase.kt index be05b88eb9..10247109ae 100644 --- a/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedTestBase.kt +++ b/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/AwsChunkedTestBase.kt @@ -5,7 +5,6 @@ package aws.smithy.kotlin.runtime.auth.awssigning.tests -import aws.smithy.kotlin.runtime.IgnoreNative import aws.smithy.kotlin.runtime.auth.awssigning.* import aws.smithy.kotlin.runtime.auth.awssigning.internal.CHUNK_SIZE_BYTES import aws.smithy.kotlin.runtime.http.DeferredHeaders @@ -172,7 +171,6 @@ abstract class AwsChunkedTestBase( return length } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testReadNegativeOffset(): TestResult = runTest { val dataLengthBytes = CHUNK_SIZE_BYTES @@ -186,7 +184,6 @@ abstract class AwsChunkedTestBase( } } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testReadExactBytes(): TestResult = runTest { val dataLengthBytes = CHUNK_SIZE_BYTES @@ -220,7 +217,6 @@ abstract class AwsChunkedTestBase( assertTrue(awsChunked.isClosedForRead()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testReadExcessiveBytes(): TestResult = runTest { val dataLengthBytes = CHUNK_SIZE_BYTES @@ -250,7 +246,6 @@ abstract class AwsChunkedTestBase( assertTrue(awsChunked.isClosedForRead()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testReadFewerBytes(): TestResult = runTest { val dataLengthBytes = CHUNK_SIZE_BYTES @@ -277,7 +272,6 @@ abstract class AwsChunkedTestBase( assertFalse(awsChunked.isClosedForRead()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testReadMultipleFullChunks(): TestResult = runTest { val numChunks = 5 @@ -325,7 +319,6 @@ abstract class AwsChunkedTestBase( assertTrue(awsChunked.isClosedForRead()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testReadMultipleChunksLastChunkNotFull(): TestResult = runTest { val numChunks = 6 @@ -391,7 +384,6 @@ abstract class AwsChunkedTestBase( assertEquals(0, chunkSizes.last()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation @Test fun testReadWithTrailingHeaders(): TestResult = runTest { val dataLengthBytes = CHUNK_SIZE_BYTES @@ -442,7 +434,6 @@ abstract class AwsChunkedTestBase( assertEquals(expectedTrailerSignature.decodeToString(), trailerSignature) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation @Test fun testUnsignedChunk(): TestResult = runTest { val dataLengthBytes = CHUNK_SIZE_BYTES @@ -474,7 +465,6 @@ abstract class AwsChunkedTestBase( assertEquals(chunkSizes[1], 0) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation @Test fun testUnsignedChunkWithTrailingHeaders(): TestResult = runTest { val dataLengthBytes = CHUNK_SIZE_BYTES diff --git a/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/BasicSigningTestBase.kt b/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/BasicSigningTestBase.kt index 3f4e1e1d17..2a517fc090 100644 --- a/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/BasicSigningTestBase.kt +++ b/runtime/auth/aws-signing-tests/common/src/aws/smithy/kotlin/runtime/auth/awssigning/tests/BasicSigningTestBase.kt @@ -4,7 +4,6 @@ */ package aws.smithy.kotlin.runtime.auth.awssigning.tests -import aws.smithy.kotlin.runtime.IgnoreNative import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awssigning.* import aws.smithy.kotlin.runtime.http.HttpBody @@ -56,7 +55,6 @@ public abstract class BasicSigningTestBase : HasSigner { credentials = DEFAULT_TEST_CREDENTIALS } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test public fun testSignRequestSigV4(): TestResult = runTest { // sanity test @@ -85,7 +83,6 @@ public abstract class BasicSigningTestBase : HasSigner { assertEquals(expectedSig, result.signature.decodeToString()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test public open fun testSignRequestSigV4Asymmetric(): TestResult = runTest { // sanity test @@ -168,7 +165,6 @@ public abstract class BasicSigningTestBase : HasSigner { return chunk } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test public fun testSignChunks(): TestResult = runTest { val request = createChunkedTestRequest() @@ -192,7 +188,6 @@ public abstract class BasicSigningTestBase : HasSigner { assertEquals(EXPECTED_FINAL_CHUNK_SIGNATURE, finalChunkResult.signature.decodeToString()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test public fun testSigningCopiesInput(): TestResult = runTest { // sanity test the signer doesn't mutate the input and instead copies to a new request diff --git a/runtime/auth/http-auth-aws/common/test/aws/smithy/kotlin/runtime/http/auth/AwsHttpSignerTestBase.kt b/runtime/auth/http-auth-aws/common/test/aws/smithy/kotlin/runtime/http/auth/AwsHttpSignerTestBase.kt index 1786744b01..6797ed74fb 100644 --- a/runtime/auth/http-auth-aws/common/test/aws/smithy/kotlin/runtime/http/auth/AwsHttpSignerTestBase.kt +++ b/runtime/auth/http-auth-aws/common/test/aws/smithy/kotlin/runtime/http/auth/AwsHttpSignerTestBase.kt @@ -134,7 +134,6 @@ public abstract class AwsHttpSignerTestBase( assertEquals(expectedSig, signed.headers["Authorization"]) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation. @Test public fun testSignReplayableStreamingRequest(): TestResult = runTest { val op = buildOperation(streaming = true) @@ -148,7 +147,7 @@ public abstract class AwsHttpSignerTestBase( assertEquals(expectedSig, signed.headers["Authorization"]) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation. + @IgnoreNative // FIXME Our JVM implementation does not sign `transfer-encoding`, but CRT does, causing a signature mismatch. Upgrade to latest version of aws-c-auth to get the fix. @Test public fun testSignAwsChunkedStreamNonReplayable(): TestResult = runTest { val op = buildOperation(streaming = true, replayable = false, requestBody = "a".repeat(AWS_CHUNKED_THRESHOLD + 1)) @@ -162,7 +161,7 @@ public abstract class AwsHttpSignerTestBase( assertEquals(expectedSig, signed.headers["Authorization"]) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation. + @IgnoreNative // FIXME Our JVM implementation does not sign `transfer-encoding`, but CRT does, causing a signature mismatch. Upgrade to latest version of aws-c-auth to get the fix. @Test public fun testSignAwsChunkedStreamReplayable(): TestResult = runTest { val op = buildOperation(streaming = true, replayable = true, requestBody = "a".repeat(AWS_CHUNKED_THRESHOLD + 1)) @@ -176,7 +175,7 @@ public abstract class AwsHttpSignerTestBase( assertEquals(expectedSig, signed.headers["Authorization"]) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation. + @IgnoreNative // FIXME Our JVM implementation does not sign `transfer-encoding`, but CRT does, causing a signature mismatch. Upgrade to latest version of aws-c-auth to get the fix. @Test public fun testSignOneShotStream(): TestResult = runTest { val op = buildOperation(streaming = true, replayable = false) diff --git a/runtime/crt-util/jvmAndNative/test/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamTest.kt b/runtime/crt-util/jvmAndNative/test/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamTest.kt index b4afa25c91..eb0acf777e 100644 --- a/runtime/crt-util/jvmAndNative/test/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamTest.kt +++ b/runtime/crt-util/jvmAndNative/test/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamTest.kt @@ -6,7 +6,6 @@ package aws.smithy.kotlin.runtime.crt import aws.sdk.kotlin.crt.io.MutableBuffer -import aws.smithy.kotlin.runtime.IgnoreNative import aws.smithy.kotlin.runtime.io.SdkBuffer import aws.smithy.kotlin.runtime.io.SdkByteChannel import aws.smithy.kotlin.runtime.io.SdkByteReadChannel @@ -27,7 +26,6 @@ class ReadChannelBodyStreamTest { return MutableBuffer.of(dest) to dest } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation. kotlin.native.internal.FileFailedToInitializeException at null:-1 @Test fun testClose() = runTest { val chan = SdkByteChannel() @@ -44,7 +42,6 @@ class ReadChannelBodyStreamTest { assertTrue(stream.sendRequestBody(sendBuffer)) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation. kotlin.native.internal.FileFailedToInitializeException at null:-1 @Test fun testCancellation() = runTest { val chan = SdkByteChannel() @@ -59,7 +56,6 @@ class ReadChannelBodyStreamTest { } } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation. kotlin.NotImplementedError at null:-1 @Test fun testReadFully() = runTest { val data = byteArrayOf(1, 2, 3, 4, 5) @@ -75,7 +71,6 @@ class ReadChannelBodyStreamTest { } } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation. kotlin.NotImplementedError at null:-1 @Test fun testPartialRead() = runTest { val chan = SdkByteReadChannel("123456".encodeToByteArray()) @@ -94,7 +89,6 @@ class ReadChannelBodyStreamTest { assertEquals("456", sent2.decodeToString()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation. kotlin.native.internal.FileFailedToInitializeException at null:-1 @Test fun testLargeTransfer() = runTest { val chan = SdkByteChannel() diff --git a/runtime/crt-util/native/src/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamNative.kt b/runtime/crt-util/native/src/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamNative.kt index 09eb88dfd8..e27805bbf6 100644 --- a/runtime/crt-util/native/src/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamNative.kt +++ b/runtime/crt-util/native/src/aws/smithy/kotlin/runtime/crt/ReadChannelBodyStreamNative.kt @@ -7,4 +7,8 @@ package aws.smithy.kotlin.runtime.crt import aws.sdk.kotlin.crt.io.MutableBuffer import aws.smithy.kotlin.runtime.io.SdkBuffer -internal actual fun transferRequestBody(outgoing: SdkBuffer, dest: MutableBuffer): Int = TODO("Not yet implemented") +internal actual fun transferRequestBody(outgoing: SdkBuffer, dest: MutableBuffer): Int { + val length = minOf(outgoing.size, dest.writeRemaining.toLong()) + if (length <= 0) return 0 + return dest.write(outgoing.readByteArray(length)) +} diff --git a/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/AsyncStressTest.kt b/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/AsyncStressTest.kt index 7d0b0def83..e509808ec1 100644 --- a/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/AsyncStressTest.kt +++ b/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/AsyncStressTest.kt @@ -37,7 +37,7 @@ class AsyncStressTest : TestWithLocalServer() { } }.start() - @IgnoreNative // FIXME Re-enable after Kotlin/Native implementation + @IgnoreNative // FIXME TlsContext needs to initialize CRT. Segmentation fault. @Test fun testStreamNotConsumed() = runBlocking { // test that filling the stream window and not consuming the body stream still cleans up resources diff --git a/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/SdkStreamResponseHandlerTest.kt b/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/SdkStreamResponseHandlerTest.kt index 7382adcca4..6c09f0dd02 100644 --- a/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/SdkStreamResponseHandlerTest.kt +++ b/runtime/protocol/http-client-engines/http-client-engine-crt/jvmAndNative/test/aws/smithy/kotlin/runtime/http/engine/crt/SdkStreamResponseHandlerTest.kt @@ -5,9 +5,9 @@ package aws.smithy.kotlin.runtime.http.engine.crt +import aws.sdk.kotlin.crt.CRT import aws.sdk.kotlin.crt.http.* import aws.sdk.kotlin.crt.io.byteArrayBuffer -import aws.smithy.kotlin.runtime.IgnoreNative import aws.smithy.kotlin.runtime.http.HttpBody import aws.smithy.kotlin.runtime.http.HttpErrorCode import aws.smithy.kotlin.runtime.http.HttpException @@ -153,9 +153,9 @@ class SdkStreamResponseHandlerTest { assertEquals(data, respChan.readToBuffer().readUtf8()) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation. @Test fun testStreamError() = runTest { + CRT.initRuntime() // CRT needs to be initialized for human-readable error codes val handler = SdkStreamResponseHandler(mockConn, coroutineContext) val stream = MockHttpStream(200) val data = "foo bar" diff --git a/runtime/protocol/http-client/common/test/aws/smithy/kotlin/runtime/http/operation/HttpInterceptorTest.kt b/runtime/protocol/http-client/common/test/aws/smithy/kotlin/runtime/http/operation/HttpInterceptorTest.kt index ffa4a4d894..5d36840bcf 100644 --- a/runtime/protocol/http-client/common/test/aws/smithy/kotlin/runtime/http/operation/HttpInterceptorTest.kt +++ b/runtime/protocol/http-client/common/test/aws/smithy/kotlin/runtime/http/operation/HttpInterceptorTest.kt @@ -5,7 +5,6 @@ package aws.smithy.kotlin.runtime.http.operation -import aws.smithy.kotlin.runtime.IgnoreNative import aws.smithy.kotlin.runtime.client.* import aws.smithy.kotlin.runtime.http.Headers import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor @@ -236,7 +235,6 @@ class HttpInterceptorTest { testMapFailure(interceptor) } - @IgnoreNative // FIXME Re-enable after Kotlin/Native Implementation @Test fun testReadAfterExecutionSuppressedException() = runTest { val interceptor = object : HttpInterceptor { @@ -261,7 +259,9 @@ class HttpInterceptorTest { op.roundTrip(client, Unit) } - val cause = assertNotNull(ex.cause) + // FIXME Investigate why the exception `ex` and its cause are duplicated on JVM. + // On JVM, `ex.cause` has the suppressed exceptions. On Native, `ex` has the suppressed exceptions and it has no cause. + val cause = assertNotNull(ex.cause ?: ex) assertEquals(1, cause.suppressedExceptions.size) assertIs(cause.suppressedExceptions.last()) } diff --git a/runtime/runtime-core/native/src/aws/smithy/kotlin/runtime/hashing/EcdsaNative.kt b/runtime/runtime-core/native/src/aws/smithy/kotlin/runtime/hashing/EcdsaNative.kt index a2bca1c4e6..112b60df14 100644 --- a/runtime/runtime-core/native/src/aws/smithy/kotlin/runtime/hashing/EcdsaNative.kt +++ b/runtime/runtime-core/native/src/aws/smithy/kotlin/runtime/hashing/EcdsaNative.kt @@ -4,10 +4,7 @@ */ package aws.smithy.kotlin.runtime.hashing -// FIXME Implement using aws-c-cal: https://github.com/awslabs/aws-c-cal/blob/main/include/aws/cal/ecc.h -// Will need to be implemented and exposed in aws-crt-kotlin. Or maybe we can _only_ offer the CRT signer on Native? -// Will require updating DefaultAwsSigner to be expect/actual and set to CrtSigner on Native. /** * ECDSA on the SECP256R1 curve. */ -public actual fun ecdsaSecp256r1(key: ByteArray, message: ByteArray): ByteArray = TODO("Not yet implemented") +public actual fun ecdsaSecp256r1(key: ByteArray, message: ByteArray): ByteArray = error("This function should not be invoked on Native, which uses the CrtAwsSigner.")