Skip to content

Commit da54f2a

Browse files
authored
refactor: update middleware to use HttpCall (#93)
1 parent fb23ef0 commit da54f2a

File tree

15 files changed

+117
-83
lines changed

15 files changed

+117
-83
lines changed

client-runtime/auth/common/src/aws/sdk/kotlin/runtime/auth/AwsSigv4Signer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class AwsSigv4Signer internal constructor(config: Config) : Feature {
5555

5656
// FIXME - this is an area where not having to sign a CRT HTTP request might be useful if we could just wrap our own type
5757
// otherwise to sign a request we need to convert: builder -> crt kotlin HttpRequest (which underneath converts to aws-c-http message) and back
58-
val signableRequest = req.builder.toSignableCrtRequest()
58+
val signableRequest = req.subject.toSignableCrtRequest()
5959

6060
val signingConfig: AwsSigningConfig = AwsSigningConfig.build {
6161
region = req.context[AuthAttributes.SigningRegion]
@@ -71,7 +71,7 @@ public class AwsSigv4Signer internal constructor(config: Config) : Feature {
7171
// TODO - expose additional signing config as needed as context attributes?
7272
}
7373
val signedRequest = AwsSigner.signRequest(signableRequest, signingConfig)
74-
req.builder.update(signedRequest)
74+
req.subject.update(signedRequest)
7575

7676
next.call(req)
7777
}

client-runtime/auth/common/test/aws/sdk/kotlin/runtime/auth/AwsSigv4SignerTest.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,13 @@ import software.aws.clientrt.client.ExecutionContext
1111
import software.aws.clientrt.http.*
1212
import software.aws.clientrt.http.content.ByteArrayContent
1313
import software.aws.clientrt.http.engine.HttpClientEngine
14-
import software.aws.clientrt.http.operation.HttpSerialize
15-
import software.aws.clientrt.http.operation.IdentityDeserializer
16-
import software.aws.clientrt.http.operation.SdkHttpOperation
17-
import software.aws.clientrt.http.operation.context
18-
import software.aws.clientrt.http.operation.roundTrip
14+
import software.aws.clientrt.http.operation.*
1915
import software.aws.clientrt.http.request.HttpRequest
2016
import software.aws.clientrt.http.request.HttpRequestBuilder
17+
import software.aws.clientrt.http.response.HttpCall
2118
import software.aws.clientrt.http.response.HttpResponse
2219
import software.aws.clientrt.time.Instant
20+
import software.aws.clientrt.util.get
2321
import kotlin.test.Test
2422
import kotlin.test.assertEquals
2523

@@ -60,8 +58,10 @@ class AwsSigv4SignerTest {
6058

6159
private suspend fun getSignedRequest(operation: SdkHttpOperation<Unit, HttpResponse>): HttpRequest {
6260
val mockEngine = object : HttpClientEngine {
63-
override suspend fun roundTrip(requestBuilder: HttpRequestBuilder): HttpResponse {
64-
return HttpResponse(HttpStatusCode.fromValue(200), Headers {}, HttpBody.Empty, requestBuilder.build())
61+
override suspend fun roundTrip(request: HttpRequest): HttpCall {
62+
val now = Instant.now()
63+
val resp = HttpResponse(HttpStatusCode.fromValue(200), Headers.Empty, HttpBody.Empty)
64+
return HttpCall(request, resp, now, now)
6565
}
6666
}
6767
val client = sdkHttpClient(mockEngine)
@@ -71,8 +71,8 @@ class AwsSigv4SignerTest {
7171
signingService = "demo"
7272
}
7373

74-
val response = operation.roundTrip(client, Unit)
75-
return response.request
74+
operation.roundTrip(client, Unit)
75+
return operation.context[HttpOperationContext.HttpCallList].last().request
7676
}
7777

7878
@Test

client-runtime/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ val platforms = listOf("common", "jvm")
1717
// See: https://kotlinlang.org/docs/reference/opt-in-requirements.html#opting-in-to-using-api
1818
val experimentalAnnotations = listOf(
1919
"kotlin.RequiresOptIn",
20-
"software.aws.clientrt.util.InternalAPI",
20+
"software.aws.clientrt.util.InternalApi",
2121
"aws.sdk.kotlin.runtime.InternalSdkApi"
2222
)
2323

client-runtime/protocols/aws-json-protocols/common/src/aws/sdk/kotlin/runtime/protocol/json/AwsJsonProtocol.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
*/
55
package aws.sdk.kotlin.runtime.protocol.json
66

7+
import aws.sdk.kotlin.runtime.InternalSdkApi
78
import software.aws.clientrt.client.SdkClientOption
89
import software.aws.clientrt.http.*
910
import software.aws.clientrt.http.content.ByteArrayContent
1011
import software.aws.clientrt.http.operation.SdkHttpOperation
11-
import software.aws.clientrt.util.InternalAPI
1212
import software.aws.clientrt.util.get
1313

1414
/**
@@ -20,7 +20,7 @@ import software.aws.clientrt.util.get
2020
* - setting the `Content-Type` and `X-Amz-Target` headers
2121
* - providing an empty json {} body when no body is serialized
2222
*/
23-
@InternalAPI
23+
@InternalSdkApi
2424
public class AwsJsonProtocol(config: Config) : Feature {
2525
private val version: String = requireNotNull(config.version) { "AWS JSON Protocol version must be specified" }
2626

@@ -48,14 +48,14 @@ public class AwsJsonProtocol(config: Config) : Feature {
4848
val operationName = context[SdkClientOption.OperationName]
4949

5050
// see: https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#protocol-behaviors
51-
req.builder.headers.append("X-Amz-Target", "$serviceName.$operationName")
52-
req.builder.headers.setMissing("Content-Type", "application/x-amz-json-$version")
51+
req.subject.headers.append("X-Amz-Target", "$serviceName.$operationName")
52+
req.subject.headers.setMissing("Content-Type", "application/x-amz-json-$version")
5353

54-
if (req.builder.body is HttpBody.Empty) {
54+
if (req.subject.body is HttpBody.Empty) {
5555
// Empty body is required by AWS JSON 1.x protocols
5656
// https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#empty-body-serialization
5757
// https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_1-protocol.html#empty-body-serialization
58-
req.builder.body = ByteArrayContent("{}".encodeToByteArray())
58+
req.subject.body = ByteArrayContent("{}".encodeToByteArray())
5959
}
6060

6161
next.call(req)

client-runtime/protocols/aws-json-protocols/common/src/aws/sdk/kotlin/runtime/protocol/json/RestJsonError.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package aws.sdk.kotlin.runtime.protocol.json
66

77
import aws.sdk.kotlin.runtime.AwsServiceException
88
import aws.sdk.kotlin.runtime.ClientException
9+
import aws.sdk.kotlin.runtime.InternalSdkApi
910
import aws.sdk.kotlin.runtime.UnknownServiceErrorException
1011
import aws.sdk.kotlin.runtime.http.ExceptionMetadata
1112
import aws.sdk.kotlin.runtime.http.ExceptionRegistry
@@ -16,15 +17,14 @@ import software.aws.clientrt.http.operation.HttpDeserialize
1617
import software.aws.clientrt.http.operation.HttpOperationContext
1718
import software.aws.clientrt.http.operation.SdkHttpOperation
1819
import software.aws.clientrt.http.response.HttpResponse
19-
import software.aws.clientrt.util.InternalAPI
2020

2121
/**
2222
* Http feature that inspects responses and throws the appropriate modeled service error that matches
2323
*
2424
* @property registry Modeled exceptions registered with the feature. All responses will be inspected to
2525
* see if one of the registered errors matches
2626
*/
27-
@InternalAPI
27+
@InternalSdkApi
2828
public class RestJsonError(private val registry: ExceptionRegistry) : Feature {
2929
public class Config {
3030
public var registry: ExceptionRegistry = ExceptionRegistry()
@@ -49,11 +49,12 @@ public class RestJsonError(private val registry: ExceptionRegistry) : Feature {
4949
override fun <I, O> install(operation: SdkHttpOperation<I, O>) {
5050
// intercept at first chance we get
5151
operation.execution.receive.intercept { req, next ->
52-
val httpResponse = next.call(req)
52+
val call = next.call(req)
53+
val httpResponse = call.response
5354

5455
val context = req.context
5556
val expectedStatus = context.getOrNull(HttpOperationContext.ExpectedHttpStatus)?.let { HttpStatusCode.fromValue(it) }
56-
if (httpResponse.status.matches(expectedStatus)) return@intercept httpResponse
57+
if (httpResponse.status.matches(expectedStatus)) return@intercept call
5758

5859
val payload = httpResponse.body.readAll()
5960
val wrappedResponse = httpResponse.withPayload(payload)

client-runtime/protocols/aws-json-protocols/common/test/aws/sdk/kotlin/runtime/protocol/json/AwsJsonProtocolTest.kt

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ import software.aws.clientrt.http.*
1111
import software.aws.clientrt.http.content.ByteArrayContent
1212
import software.aws.clientrt.http.engine.HttpClientEngine
1313
import software.aws.clientrt.http.operation.*
14+
import software.aws.clientrt.http.request.HttpRequest
1415
import software.aws.clientrt.http.request.HttpRequestBuilder
16+
import software.aws.clientrt.http.response.HttpCall
1517
import software.aws.clientrt.http.response.HttpResponse
18+
import software.aws.clientrt.time.Instant
19+
import software.aws.clientrt.util.get
1620
import kotlin.test.Test
1721
import kotlin.test.assertEquals
1822

@@ -21,8 +25,10 @@ class AwsJsonProtocolTest {
2125
@Test
2226
fun testSetJsonProtocolHeaders() = runSuspendTest {
2327
val mockEngine = object : HttpClientEngine {
24-
override suspend fun roundTrip(requestBuilder: HttpRequestBuilder): HttpResponse {
25-
return HttpResponse(HttpStatusCode.OK, Headers {}, HttpBody.Empty, requestBuilder.build())
28+
override suspend fun roundTrip(request: HttpRequest): HttpCall {
29+
val resp = HttpResponse(HttpStatusCode.OK, Headers.Empty, HttpBody.Empty)
30+
val now = Instant.now()
31+
return HttpCall(request, resp, now, now)
2632
}
2733
}
2834

@@ -39,17 +45,20 @@ class AwsJsonProtocolTest {
3945
version = "1.1"
4046
}
4147

42-
val response = op.roundTrip(client, Unit)
48+
op.roundTrip(client, Unit)
49+
val request = op.context[HttpOperationContext.HttpCallList].last().request
4350

44-
assertEquals("application/x-amz-json-1.1", response.request.headers["Content-Type"])
45-
assertEquals("FooService.Bar", response.request.headers["X-Amz-Target"])
51+
assertEquals("application/x-amz-json-1.1", request.headers["Content-Type"])
52+
assertEquals("FooService.Bar", request.headers["X-Amz-Target"])
4653
}
4754

4855
@Test
4956
fun testEmptyBody() = runSuspendTest {
5057
val mockEngine = object : HttpClientEngine {
51-
override suspend fun roundTrip(requestBuilder: HttpRequestBuilder): HttpResponse {
52-
return HttpResponse(HttpStatusCode.OK, Headers {}, HttpBody.Empty, requestBuilder.build())
58+
override suspend fun roundTrip(request: HttpRequest): HttpCall {
59+
val resp = HttpResponse(HttpStatusCode.OK, Headers.Empty, HttpBody.Empty)
60+
val now = Instant.now()
61+
return HttpCall(request, resp, now, now)
5362
}
5463
}
5564

@@ -66,17 +75,20 @@ class AwsJsonProtocolTest {
6675
version = "1.1"
6776
}
6877

69-
val response = op.roundTrip(client, Unit)
70-
val actual = response.request.body.readAll()?.decodeToString()
78+
op.roundTrip(client, Unit)
79+
val request = op.context[HttpOperationContext.HttpCallList].last().request
80+
val actual = request.body.readAll()?.decodeToString()
7181

7282
assertEquals("{}", actual)
7383
}
7484

7585
@Test
7686
fun testDoesNotOverride() = runSuspendTest {
7787
val mockEngine = object : HttpClientEngine {
78-
override suspend fun roundTrip(requestBuilder: HttpRequestBuilder): HttpResponse {
79-
return HttpResponse(HttpStatusCode.OK, Headers {}, HttpBody.Empty, requestBuilder.build())
88+
override suspend fun roundTrip(request: HttpRequest): HttpCall {
89+
val resp = HttpResponse(HttpStatusCode.OK, Headers.Empty, HttpBody.Empty)
90+
val now = Instant.now()
91+
return HttpCall(request, resp, now, now)
8092
}
8193
}
8294

@@ -100,9 +112,10 @@ class AwsJsonProtocolTest {
100112
version = "1.1"
101113
}
102114

103-
val response = op.roundTrip(client, Unit)
104-
val actual = response.request.body.readAll()?.decodeToString()
105-
assertEquals("application/xml", response.request.headers["Content-Type"])
115+
op.roundTrip(client, Unit)
116+
val request = op.context[HttpOperationContext.HttpCallList].last().request
117+
val actual = request.body.readAll()?.decodeToString()
118+
assertEquals("application/xml", request.headers["Content-Type"])
106119
assertEquals("foo", actual)
107120
}
108121
}

client-runtime/protocols/aws-json-protocols/common/test/aws/sdk/kotlin/runtime/protocol/json/RestJsonErrorDeserializerTest.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import software.aws.clientrt.http.Headers
99
import software.aws.clientrt.http.HttpBody
1010
import software.aws.clientrt.http.HttpStatusCode
1111
import software.aws.clientrt.http.content.ByteArrayContent
12-
import software.aws.clientrt.http.request.HttpRequestBuilder
1312
import software.aws.clientrt.http.response.HttpResponse
1413
import kotlin.test.Test
1514
import kotlin.test.assertEquals
@@ -34,7 +33,7 @@ class RestJsonErrorDeserializerTest {
3433
append(X_AMZN_ERROR_TYPE_HEADER_NAME, value)
3534
}
3635

37-
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, HttpBody.Empty, HttpRequestBuilder().build())
36+
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, HttpBody.Empty)
3837
val actual = RestJsonErrorDeserializer.deserialize(resp, null)
3938
assertEquals(expected, actual.code)
4039
}
@@ -50,7 +49,7 @@ class RestJsonErrorDeserializerTest {
5049
}
5150
""".trimIndent().encodeToByteArray()
5251
val body = ByteArrayContent(contents)
53-
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, body, HttpRequestBuilder().build())
52+
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, body)
5453
val actual = RestJsonErrorDeserializer.deserialize(resp, contents)
5554
assertEquals(expected, actual.code)
5655
}
@@ -66,7 +65,7 @@ class RestJsonErrorDeserializerTest {
6665
}
6766
""".trimIndent().encodeToByteArray()
6867
val body = ByteArrayContent(contents)
69-
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, body, HttpRequestBuilder().build())
68+
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, body)
7069
val actual = RestJsonErrorDeserializer.deserialize(resp, contents)
7170
assertEquals(expected, actual.code)
7271
}
@@ -84,7 +83,7 @@ class RestJsonErrorDeserializerTest {
8483
append(name, expected)
8584
}
8685

87-
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, HttpBody.Empty, HttpRequestBuilder().build())
86+
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, HttpBody.Empty)
8887
val actual = RestJsonErrorDeserializer.deserialize(resp, null)
8988
assertEquals(expected, actual.message)
9089
}
@@ -101,7 +100,7 @@ class RestJsonErrorDeserializerTest {
101100
}
102101
""".trimIndent().encodeToByteArray()
103102
val body = ByteArrayContent(contents)
104-
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, body, HttpRequestBuilder().build())
103+
val resp = HttpResponse(HttpStatusCode.BadRequest, headers, body)
105104
val actual = RestJsonErrorDeserializer.deserialize(resp, contents)
106105
assertEquals(expected, actual.message)
107106
}

client-runtime/protocols/aws-json-protocols/common/test/aws/sdk/kotlin/runtime/protocol/json/RestJsonErrorTest.kt

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ import software.aws.clientrt.http.operation.UnitDeserializer
1818
import software.aws.clientrt.http.operation.UnitSerializer
1919
import software.aws.clientrt.http.operation.context
2020
import software.aws.clientrt.http.operation.roundTrip
21-
import software.aws.clientrt.http.request.HttpRequestBuilder
21+
import software.aws.clientrt.http.request.HttpRequest
22+
import software.aws.clientrt.http.response.HttpCall
2223
import software.aws.clientrt.http.response.HttpResponse
2324
import software.aws.clientrt.http.response.header
2425
import software.aws.clientrt.serde.*
2526
import software.aws.clientrt.serde.json.JsonSerdeProvider
27+
import software.aws.clientrt.time.Instant
2628
import kotlin.test.*
2729

2830
@OptIn(ExperimentalStdlibApi::class)
@@ -89,18 +91,20 @@ class RestJsonErrorTest {
8991
@Test
9092
fun `it throws matching errors`() = runSuspendTest {
9193

92-
val req = HttpRequestBuilder().build()
9394
val headers = Headers {
9495
append("X-Test-Header", "12")
9596
append(X_AMZN_REQUEST_ID_HEADER, "guid")
9697
append(X_AMZN_ERROR_TYPE_HEADER_NAME, "FooError")
9798
}
9899
val payload = """{"baz":"quux","string":"hello world","message":"server do better next time"}"""
99100
val body = ByteArrayContent(payload.encodeToByteArray())
100-
val httpResp = HttpResponse(HttpStatusCode.fromValue(502), headers, body, req)
101+
val httpResp = HttpResponse(HttpStatusCode.fromValue(502), headers, body)
101102

102103
val mockEngine = object : HttpClientEngine {
103-
override suspend fun roundTrip(requestBuilder: HttpRequestBuilder): HttpResponse { return httpResp }
104+
override suspend fun roundTrip(request: HttpRequest): HttpCall {
105+
val now = Instant.now()
106+
return HttpCall(request, httpResp, now, now)
107+
}
104108
}
105109

106110
val client = sdkHttpClient(mockEngine)
@@ -137,19 +141,20 @@ class RestJsonErrorTest {
137141

138142
@Test
139143
fun `it throws unknown`() = runSuspendTest {
140-
141-
val req = HttpRequestBuilder().build()
142144
val headers = Headers {
143145
append("X-Test-Header", "12")
144146
append(X_AMZN_REQUEST_ID_HEADER, "guid")
145147
append(X_AMZN_ERROR_TYPE_HEADER_NAME, "BarError")
146148
}
147149
val payload = """{"baz":"quux","string":"hello world","message":"server do better next time"}"""
148150
val body = ByteArrayContent(payload.encodeToByteArray())
149-
val httpResp = HttpResponse(HttpStatusCode.fromValue(502), headers, body, req)
151+
val httpResp = HttpResponse(HttpStatusCode.fromValue(502), headers, body)
150152

151153
val mockEngine = object : HttpClientEngine {
152-
override suspend fun roundTrip(requestBuilder: HttpRequestBuilder): HttpResponse { return httpResp }
154+
override suspend fun roundTrip(request: HttpRequest): HttpCall {
155+
val now = Instant.now()
156+
return HttpCall(request, httpResp, now, now)
157+
}
153158
}
154159

155160
val client = sdkHttpClient(mockEngine)
@@ -193,7 +198,6 @@ class RestJsonErrorTest {
193198
@Test
194199
fun `it handles non-json payloads`() = runSuspendTest {
195200
// the service itself may talk rest-json but errors (like signature mismatch) may return unknown payloads
196-
val req = HttpRequestBuilder().build()
197201
val headers = Headers {
198202
append("X-Test-Header", "12")
199203
append(X_AMZN_REQUEST_ID_HEADER, "guid")
@@ -206,10 +210,13 @@ class RestJsonErrorTest {
206210
""".trimIndent()
207211

208212
val body = ByteArrayContent(payload.encodeToByteArray())
209-
val httpResp = HttpResponse(HttpStatusCode.fromValue(502), headers, body, req)
213+
val httpResp = HttpResponse(HttpStatusCode.fromValue(502), headers, body)
210214

211215
val mockEngine = object : HttpClientEngine {
212-
override suspend fun roundTrip(requestBuilder: HttpRequestBuilder): HttpResponse { return httpResp }
216+
override suspend fun roundTrip(request: HttpRequest): HttpCall {
217+
val now = Instant.now()
218+
return HttpCall(request, httpResp, now, now)
219+
}
213220
}
214221

215222
val client = sdkHttpClient(mockEngine)

client-runtime/protocols/http/common/src/aws/sdk/kotlin/runtime/http/ResponseUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ public fun HttpResponse.withPayload(payload: ByteArray?): HttpResponse {
2525
HttpBody.Empty
2626
}
2727

28-
return HttpResponse(status, headers, newBody, request)
28+
return HttpResponse(status, headers, newBody)
2929
}

0 commit comments

Comments
 (0)