Skip to content

Commit aefbfa6

Browse files
authored
fix: correctly apply resolved endpoint to presigned requests (#1025)
1 parent 49a0089 commit aefbfa6

File tree

5 files changed

+37
-12
lines changed

5 files changed

+37
-12
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "80e3c346-9a4a-4d47-84f1-1655742f9711",
3+
"type": "bugfix",
4+
"description": "Correctly apply resolved endpoint to presigned requests",
5+
"issues": [
6+
"awslabs/aws-sdk-kotlin#1173"
7+
]
8+
}

runtime/auth/aws-signing-common/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ kotlin {
2323
commonTest {
2424
dependencies {
2525
implementation(libs.kotlinx.coroutines.test)
26+
27+
// Needed for an actual signer implementation in tests
28+
implementation(project(":runtime:auth:aws-signing-default"))
2629
}
2730
}
2831

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import aws.smithy.kotlin.runtime.collections.emptyAttributes
1212
import aws.smithy.kotlin.runtime.http.*
1313
import aws.smithy.kotlin.runtime.http.operation.EndpointResolver
1414
import aws.smithy.kotlin.runtime.http.operation.ResolveEndpointRequest
15+
import aws.smithy.kotlin.runtime.http.operation.setResolvedEndpoint
1516
import aws.smithy.kotlin.runtime.http.request.HttpRequest
1617
import aws.smithy.kotlin.runtime.http.request.HttpRequestBuilder
17-
import aws.smithy.kotlin.runtime.http.request.header
1818
import aws.smithy.kotlin.runtime.net.url.Url
1919
import aws.smithy.kotlin.runtime.operation.ExecutionContext
2020

@@ -32,9 +32,9 @@ public suspend fun presignRequest(
3232
val credentials = credentialsProvider.resolve()
3333
val eprRequest = ResolveEndpointRequest(ctx, unsignedRequestBuilder.build(), credentials)
3434
val endpoint = endpointResolver.resolve(eprRequest)
35-
val signingContext = endpoint.authOptions.firstOrNull { it.schemeId == AuthSchemeId.AwsSigV4 }?.attributes ?: emptyAttributes()
35+
setResolvedEndpoint(unsignedRequestBuilder, ctx, endpoint)
3636

37-
val unsignedRequest = unsignedRequestBuilder.apply { header("host", endpoint.uri.host.toString()) }.build()
37+
val signingContext = endpoint.authOptions.firstOrNull { it.schemeId == AuthSchemeId.AwsSigV4 }?.attributes ?: emptyAttributes()
3838

3939
val config = AwsSigningConfig {
4040
region = signingContext.getOrNull(AwsSigningAttributes.SigningRegion)
@@ -48,7 +48,7 @@ public suspend fun presignRequest(
4848
signingConfig()
4949
}
5050

51-
val result = signer.sign(unsignedRequest, config)
51+
val result = signer.sign(unsignedRequestBuilder.build(), config)
5252
val signedRequest = result.output
5353

5454
return HttpRequest(

runtime/auth/aws-signing-common/common/test/aws/smithy/kotlin/runtime/auth/awssigning/PresignerTest.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ import aws.smithy.kotlin.runtime.operation.ExecutionContext
1919
import kotlinx.coroutines.test.runTest
2020
import kotlin.test.Test
2121
import kotlin.test.assertEquals
22-
23-
private const val NON_HTTPS_URL = "http://localhost:8080/path/to/resource?foo=bar"
22+
import kotlin.test.assertTrue
2423

2524
class PresignerTest {
2625
// Verify that custom endpoint URL schemes aren't changed.
@@ -40,7 +39,7 @@ class PresignerTest {
4039
val ctx = ExecutionContext()
4140
val credentialsProvider = TestCredentialsProvider(Credentials("foo", "bar"))
4241
val endpointResolver = TestEndpointResolver(Endpoint(expectedUrl))
43-
val signer = TestSigner(HttpRequest { url(expectedUrl) })
42+
val signer = DefaultAwsSigner
4443
val signingConfig: AwsSigningConfig.Builder.() -> Unit = {
4544
service = "launch-service"
4645
region = "the-moon"
@@ -61,7 +60,10 @@ class PresignerTest {
6160
assertEquals(expectedUrl.host, actualUrl.host)
6261
assertEquals(expectedUrl.port, actualUrl.port)
6362
assertEquals(expectedUrl.path, actualUrl.path)
64-
assertEquals(expectedUrl.parameters, actualUrl.parameters)
63+
64+
expectedUrl.parameters.encodedParameters.entryValues.forEach { (key, value) ->
65+
assertTrue(actualUrl.parameters.encodedParameters.contains(key, value))
66+
}
6567
}
6668
}
6769

runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/OperationEndpoint.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package aws.smithy.kotlin.runtime.http.operation
88
import aws.smithy.kotlin.runtime.InternalApi
99
import aws.smithy.kotlin.runtime.client.endpoints.Endpoint
1010
import aws.smithy.kotlin.runtime.http.request.HttpRequest
11+
import aws.smithy.kotlin.runtime.http.request.HttpRequestBuilder
1112
import aws.smithy.kotlin.runtime.http.request.url
1213
import aws.smithy.kotlin.runtime.identity.Identity
1314
import aws.smithy.kotlin.runtime.net.Host
@@ -48,10 +49,21 @@ public data class ResolveEndpointRequest(
4849
*/
4950
@InternalApi
5051
public fun setResolvedEndpoint(req: SdkHttpRequest, endpoint: Endpoint) {
51-
val hostPrefix = req.context.getOrNull(HttpOperationContext.HostPrefix) ?: ""
52+
setResolvedEndpoint(req.subject, req.context, endpoint)
53+
}
54+
55+
/**
56+
* Update an existing request with a resolved endpoint.
57+
*
58+
* Any values serialized to the HTTP path or query string are preserved (in the case of path, the existing serialized one
59+
* is appended to what was resolved).
60+
*/
61+
@InternalApi
62+
public fun setResolvedEndpoint(req: HttpRequestBuilder, ctx: ExecutionContext, endpoint: Endpoint) {
63+
val hostPrefix = ctx.getOrNull(HttpOperationContext.HostPrefix) ?: ""
5264
val hostname = "$hostPrefix${endpoint.uri.host}"
5365

54-
req.subject.url {
66+
req.url {
5567
// Can't use Url.Builder.copyFrom because we need to keep existing path/parameters and merge in new ones
5668
scheme = endpoint.uri.scheme
5769
userInfo.copyFrom(endpoint.uri.userInfo)
@@ -65,6 +77,6 @@ public fun setResolvedEndpoint(req: SdkHttpRequest, endpoint: Endpoint) {
6577
encodedFragment = endpoint.uri.fragment?.encoded
6678
}
6779

68-
req.subject.headers["Host"] = hostname
69-
endpoint.headers?.let { req.subject.headers.appendAll(it) }
80+
req.headers["Host"] = hostname
81+
endpoint.headers?.let { req.headers.appendAll(it) }
7082
}

0 commit comments

Comments
 (0)