Skip to content

Commit 87fadb0

Browse files
authored
refactor!: overhaul endpoint resolver types (#361)
1 parent 3dcddcf commit 87fadb0

File tree

29 files changed

+413
-596
lines changed

29 files changed

+413
-596
lines changed

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/config/imds/ImdsClient.kt

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,17 @@
66
package aws.sdk.kotlin.runtime.config.imds
77

88
import aws.sdk.kotlin.runtime.AwsServiceException
9-
import aws.sdk.kotlin.runtime.ConfigurationException
109
import aws.sdk.kotlin.runtime.client.AwsClientOption
11-
import aws.sdk.kotlin.runtime.endpoint.Endpoint
1210
import aws.sdk.kotlin.runtime.http.ApiMetadata
1311
import aws.sdk.kotlin.runtime.http.AwsUserAgentMetadata
1412
import aws.sdk.kotlin.runtime.http.engine.crt.CrtHttpEngine
15-
import aws.sdk.kotlin.runtime.http.middleware.ServiceEndpointResolver
1613
import aws.sdk.kotlin.runtime.http.middleware.UserAgent
1714
import aws.smithy.kotlin.runtime.client.ExecutionContext
1815
import aws.smithy.kotlin.runtime.client.SdkClientOption
1916
import aws.smithy.kotlin.runtime.client.SdkLogMode
2017
import aws.smithy.kotlin.runtime.http.*
2118
import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine
19+
import aws.smithy.kotlin.runtime.http.middleware.ResolveEndpoint
2220
import aws.smithy.kotlin.runtime.http.operation.*
2321
import aws.smithy.kotlin.runtime.http.response.HttpResponse
2422
import aws.smithy.kotlin.runtime.io.Closeable
@@ -78,22 +76,11 @@ public class ImdsClient private constructor(builder: Builder) : InstanceMetadata
7876
}
7977

8078
httpClient = sdkHttpClient(engine)
81-
82-
// validate the override at construction time
83-
if (endpointConfiguration is EndpointConfiguration.Custom) {
84-
val url = endpointConfiguration.endpoint.toUrl()
85-
try {
86-
Url.parse(url.toString())
87-
} catch (ex: Exception) {
88-
throw ConfigurationException("Invalid endpoint configuration: `$url` is not a valid URI", ex)
89-
}
90-
}
9179
}
9280

9381
// cached middleware instances
9482
private val middleware: List<Feature> = listOf(
95-
ServiceEndpointResolver.create {
96-
serviceId = SERVICE
83+
ResolveEndpoint.create {
9784
resolver = ImdsEndpointResolver(platformProvider, endpointConfiguration)
9885
},
9986
UserAgent.create {
@@ -220,13 +207,13 @@ public enum class EndpointMode(internal val defaultEndpoint: Endpoint) {
220207
* IPv4 mode. This is the default unless otherwise specified
221208
* e.g. `http://169.254.169.254'
222209
*/
223-
IPv4(Endpoint("169.254.169.254", "http")),
210+
IPv4(Endpoint("http://169.254.169.254")),
224211

225212
/**
226213
* IPv6 mode
227214
* e.g. `http://[fd00:ec2::254]`
228215
*/
229-
IPv6(Endpoint("[fd00:ec2::254]", "http"));
216+
IPv6(Endpoint("http://[fd00:ec2::254]"));
230217

231218
public companion object {
232219
public fun fromValue(value: String): EndpointMode = when (value.lowercase()) {
@@ -244,13 +231,3 @@ public enum class EndpointMode(internal val defaultEndpoint: Endpoint) {
244231
* @param message The error message
245232
*/
246233
public class EC2MetadataError(public val statusCode: Int, message: String) : AwsServiceException(message)
247-
248-
private fun Endpoint.toUrl(): Url {
249-
val endpoint = this
250-
val protocol = Protocol.parse(endpoint.protocol)
251-
return Url(
252-
scheme = protocol,
253-
host = endpoint.hostname,
254-
port = endpoint.port ?: protocol.defaultPort,
255-
)
256-
}

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/config/imds/ImdsEndpointResolver.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ package aws.sdk.kotlin.runtime.config.imds
88
import aws.sdk.kotlin.runtime.config.AwsSdkSetting
99
import aws.sdk.kotlin.runtime.config.profile.loadActiveAwsProfile
1010
import aws.sdk.kotlin.runtime.config.resolve
11-
import aws.sdk.kotlin.runtime.endpoint.Endpoint
12-
import aws.sdk.kotlin.runtime.endpoint.EndpointResolver
13-
import aws.smithy.kotlin.runtime.http.Url
11+
import aws.smithy.kotlin.runtime.http.operation.Endpoint
12+
import aws.smithy.kotlin.runtime.http.operation.EndpointResolver
1413
import aws.smithy.kotlin.runtime.util.PlatformProvider
1514
import aws.smithy.kotlin.runtime.util.asyncLazy
1615

@@ -25,7 +24,7 @@ internal class ImdsEndpointResolver(
2524
private val resolvedEndpoint = asyncLazy(::doResolveEndpoint)
2625
private val activeProfile = asyncLazy { loadActiveAwsProfile(platformProvider) }
2726

28-
override suspend fun resolve(service: String, region: String): Endpoint = resolvedEndpoint.get()
27+
override suspend fun resolve(): Endpoint = resolvedEndpoint.get()
2928

3029
private suspend fun doResolveEndpoint(): Endpoint = when (endpointConfiguration) {
3130
is EndpointConfiguration.Custom -> endpointConfiguration.endpoint
@@ -63,7 +62,4 @@ internal class ImdsEndpointResolver(
6362
}
6463

6564
// Parse a string as a URL and convert to an endpoint
66-
internal fun String.toEndpoint(): Endpoint {
67-
val url = Url.parse(this)
68-
return Endpoint(url.host, url.scheme.protocolName, url.port)
69-
}
65+
internal fun String.toEndpoint(): Endpoint = Endpoint(this)

aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/config/imds/ImdsClientTest.kt

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55

66
package aws.sdk.kotlin.runtime.config.imds
77

8-
import aws.sdk.kotlin.runtime.ConfigurationException
9-
import aws.sdk.kotlin.runtime.endpoint.Endpoint
108
import aws.sdk.kotlin.runtime.testing.TestPlatformProvider
119
import aws.sdk.kotlin.runtime.testing.runSuspendTest
12-
import aws.smithy.kotlin.runtime.http.*
13-
import aws.smithy.kotlin.runtime.httptest.TestConnection
10+
import aws.smithy.kotlin.runtime.http.operation.Endpoint
1411
import aws.smithy.kotlin.runtime.httptest.buildTestConnection
1512
import aws.smithy.kotlin.runtime.time.Instant
1613
import aws.smithy.kotlin.runtime.time.ManualClock
@@ -25,19 +22,6 @@ import kotlin.time.ExperimentalTime
2522
@OptIn(ExperimentalTime::class)
2623
class ImdsClientTest {
2724

28-
@Test
29-
fun testInvalidEndpointOverrideFailsCreation() {
30-
val connection = TestConnection()
31-
assertFailsWith<ConfigurationException> {
32-
ImdsClient {
33-
engine = connection
34-
endpointConfiguration = EndpointConfiguration.Custom(
35-
Endpoint("[foo::254]", protocol = "http")
36-
)
37-
}
38-
}
39-
}
40-
4125
@Test
4226
fun testTokensAreCached() = runSuspendTest {
4327
val connection = buildTestConnection {
@@ -252,8 +236,7 @@ class ImdsClientTest {
252236
val client = ImdsClient {
253237
engine = connection
254238
test.endpointOverride?.let { endpointOverride ->
255-
val endpoint = Url.parse(endpointOverride).let { Endpoint(it.host, it.scheme.protocolName) }
256-
239+
val endpoint = Endpoint(endpointOverride)
257240
endpointConfiguration = EndpointConfiguration.Custom(endpoint)
258241
}
259242
test.modeOverride?.let {

aws-runtime/aws-endpoint/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ extra["displayName"] = "AWS :: SDK :: Kotlin :: Endpoint"
88
extra["moduleName"] = "aws.sdk.kotlin.runtime.endpoint"
99

1010
val smithyKotlinVersion: String by project
11-
val kotestVersion: String by project
1211

1312
kotlin {
1413
sourceSets {
1514
commonMain{
1615
dependencies {
1716
implementation(project(":aws-runtime:aws-core"))
17+
// exposes Endpoint
18+
api("aws.smithy.kotlin:http:$smithyKotlinVersion")
1819
}
1920
}
2021

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
package aws.sdk.kotlin.runtime.endpoint
7+
8+
import aws.smithy.kotlin.runtime.http.Url
9+
import aws.smithy.kotlin.runtime.http.operation.Endpoint
10+
11+
/**
12+
* Represents the endpoint a service client should make API operation calls to.
13+
*
14+
* The SDK will automatically resolve these endpoints per API client using an internal resolver.
15+
*
16+
* @property endpoint The endpoint clients will use to make API calls
17+
* to e.g. "{service-id}.{region}.amazonaws.com"
18+
* @property credentialScope Custom signing constraint overrides
19+
*/
20+
public data class AwsEndpoint(
21+
public val endpoint: Endpoint,
22+
public val credentialScope: CredentialScope? = null
23+
) {
24+
public constructor(url: Url, credentialScope: CredentialScope? = null) : this(Endpoint(url), credentialScope)
25+
public constructor(url: String, credentialScope: CredentialScope? = null) : this(Endpoint(Url.parse(url)), credentialScope)
26+
}

aws-runtime/aws-endpoint/common/src/aws/sdk/kotlin/runtime/endpoint/EndpointResolver.kt renamed to aws-runtime/aws-endpoint/common/src/aws/sdk/kotlin/runtime/endpoint/AwsEndpointResolver.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ package aws.sdk.kotlin.runtime.endpoint
88
/**
99
* Resolves endpoints for a given service and region
1010
*/
11-
public interface EndpointResolver {
11+
public fun interface AwsEndpointResolver {
1212

1313
/**
14-
* Resolve the [Endpoint] for the given service and region
14+
* Resolve the [AwsEndpoint] for the given service and region
1515
* @param service the service id associated with the desired endpoint
1616
* @param region the region associated with the desired endpoint
17-
* @return an [Endpoint] that can be used to connect to the service
17+
* @return an [AwsEndpoint] that can be used to connect to the service
1818
*/
19-
public suspend fun resolve(service: String, region: String): Endpoint
19+
public suspend fun resolve(service: String, region: String): AwsEndpoint
2020
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
package aws.sdk.kotlin.runtime.endpoint
7+
8+
/**
9+
* A custom signing constraint for an endpoint
10+
*
11+
* @property region A custom sigv4 signing name
12+
* @property service A custom sigv4 service name to use when signing a request
13+
*/
14+
public data class CredentialScope(val region: String? = null, val service: String? = null)

aws-runtime/aws-endpoint/common/src/aws/sdk/kotlin/runtime/endpoint/Endpoint.kt

Lines changed: 0 additions & 44 deletions
This file was deleted.

aws-runtime/aws-endpoint/common/src/aws/sdk/kotlin/runtime/endpoint/StaticEndpointResolver.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
package aws.sdk.kotlin.runtime.endpoint
77

88
/**
9-
* An [EndpointResolver] that returns a static endpoint
9+
* An [AwsEndpointResolver] that returns a static endpoint
1010
*/
11-
public class StaticEndpointResolver(private val endpoint: Endpoint) : EndpointResolver {
12-
override suspend fun resolve(service: String, region: String): Endpoint = endpoint
11+
public class StaticEndpointResolver(private val endpoint: AwsEndpoint) : AwsEndpointResolver {
12+
override suspend fun resolve(service: String, region: String): AwsEndpoint = endpoint
1313
}

aws-runtime/aws-endpoint/common/src/aws/sdk/kotlin/runtime/endpoint/internal/Partition.kt

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,17 @@
66
package aws.sdk.kotlin.runtime.endpoint.internal
77

88
import aws.sdk.kotlin.runtime.InternalSdkApi
9-
import aws.sdk.kotlin.runtime.endpoint.Endpoint
9+
import aws.sdk.kotlin.runtime.endpoint.AwsEndpoint
10+
import aws.sdk.kotlin.runtime.endpoint.CredentialScope
11+
import aws.smithy.kotlin.runtime.http.Protocol
12+
import aws.smithy.kotlin.runtime.http.Url
13+
import aws.smithy.kotlin.runtime.http.operation.Endpoint
1014

1115
private const val defaultProtocol = "https"
1216
private const val defaultSigner = "v4"
1317
private val protocolPriority = listOf("https", "http")
1418
private val signerPriority = listOf("v4")
1519

16-
/**
17-
* A custom signing constraint for an endpoint
18-
*
19-
* @property region A custom sigv4 signing name
20-
* @property service A custom sigv4 service name to use when signing a request
21-
*/
22-
@InternalSdkApi
23-
public data class CredentialScope(val region: String? = null, val service: String? = null)
24-
2520
/**
2621
* A description of a single service endpoint
2722
*/
@@ -96,7 +91,7 @@ public data class Partition(
9691
internal fun Partition.canResolveEndpoint(region: String): Boolean =
9792
endpoints.containsKey(region) || regionRegex.matches(region)
9893

99-
internal fun Partition.resolveEndpoint(region: String): Endpoint? {
94+
internal fun Partition.resolveEndpoint(region: String): AwsEndpoint {
10095
val resolvedRegion = if (region.isEmpty() && partitionEndpoint.isNotEmpty()) {
10196
partitionEndpoint
10297
} else {
@@ -119,7 +114,7 @@ private fun Partition.endpointDefinitionForRegion(region: String): EndpointDefin
119114
return match ?: EndpointDefinition()
120115
}
121116

122-
internal fun EndpointDefinition.resolve(region: String, defaults: EndpointDefinition): Endpoint {
117+
internal fun EndpointDefinition.resolve(region: String, defaults: EndpointDefinition): AwsEndpoint {
123118
val merged = mergeDefinitions(this, defaults)
124119

125120
// hostname must have been set either by default or on this endpoint
@@ -130,7 +125,9 @@ internal fun EndpointDefinition.resolve(region: String, defaults: EndpointDefini
130125
val signingName = merged.credentialScope?.service
131126
val signingRegion = merged.credentialScope?.region ?: region
132127

133-
return Endpoint(hostname, protocol, signingName = signingName, signingRegion = signingRegion)
128+
val uri = Url(Protocol.parse(protocol), hostname)
129+
val scope = CredentialScope(signingRegion, signingName)
130+
return AwsEndpoint(Endpoint(uri), scope)
134131
}
135132

136133
/**
@@ -164,7 +161,7 @@ private fun getByPriority(from: List<String>, priority: List<String>, default: S
164161
* Resolve an endpoint for a given region using the given partitions
165162
*/
166163
@InternalSdkApi
167-
public fun resolveEndpoint(partitions: List<Partition>, region: String): Endpoint? {
164+
public fun resolveEndpoint(partitions: List<Partition>, region: String): AwsEndpoint? {
168165
if (partitions.isEmpty()) return null
169166

170167
// fallback to first partition if no candidate found

0 commit comments

Comments
 (0)