-
Notifications
You must be signed in to change notification settings - Fork 31
feat: OkHttpEngine BYOC #1437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: OkHttpEngine BYOC #1437
Changes from 12 commits
ec59c2b
f6f4451
f7ae60c
7dc390a
abb8946
2413081
23d869a
70b841d
267a073
e3078cb
8f32408
1d100a6
7e0ea3c
7fccd8e
cfddcfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "id": "4d3a104a-3225-4dcb-be05-f5155d320952", | ||
| "type": "feature", | ||
| "description": "Allow configuring a custom OkHttpClient in OkHttpEngine", | ||
| "issues": [ | ||
| "https://github.com/aws/aws-sdk-kotlin/issues/1707" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import aws.smithy.kotlin.runtime.InternalApi | |
| import aws.smithy.kotlin.runtime.http.HttpCall | ||
| import aws.smithy.kotlin.runtime.http.config.EngineFactory | ||
| import aws.smithy.kotlin.runtime.http.engine.AlpnId | ||
| import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine | ||
| import aws.smithy.kotlin.runtime.http.engine.HttpClientEngineBase | ||
| import aws.smithy.kotlin.runtime.http.engine.TlsContext | ||
| import aws.smithy.kotlin.runtime.http.engine.callContext | ||
|
|
@@ -39,6 +40,11 @@ public class OkHttpEngine( | |
| ) : HttpClientEngineBase("OkHttp") { | ||
| public constructor() : this(OkHttpEngineConfig.Default) | ||
|
|
||
| private var userProvidedClient: OkHttpClient? = null | ||
| public constructor(client: OkHttpClient) : this(OkHttpEngineConfig.Default) { | ||
| userProvidedClient = client | ||
| } | ||
|
|
||
|
||
| public companion object : EngineFactory<OkHttpEngineConfig.Builder, OkHttpEngine> { | ||
| /** | ||
| * Initializes a new [OkHttpEngine] via a DSL builder block | ||
|
|
@@ -57,7 +63,10 @@ public class OkHttpEngine( | |
| } | ||
|
|
||
| private val metrics = HttpClientMetrics(TELEMETRY_SCOPE, config.telemetryProvider) | ||
| private val client = config.buildClient(metrics, connectionMonitoringListener) | ||
| private val client: OkHttpClient by lazy { | ||
| userProvidedClient?.withMetrics(metrics, config) | ||
| ?: config.buildClient(metrics, connectionMonitoringListener) | ||
| } | ||
|
|
||
| @OptIn(ExperimentalCoroutinesApi::class) | ||
| override suspend fun roundTrip(context: ExecutionContext, request: HttpRequest): HttpCall { | ||
|
|
@@ -85,9 +94,11 @@ public class OkHttpEngine( | |
|
|
||
| override fun shutdown() { | ||
| connectionMonitoringListener?.closeIfCloseable() | ||
| client.connectionPool.evictAll() | ||
| client.dispatcher.executorService.shutdown() | ||
| metrics.close() | ||
| if (userProvidedClient == null) { | ||
| client.connectionPool.evictAll() | ||
| client.dispatcher.executorService.shutdown() | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -170,6 +181,18 @@ public fun OkHttpEngineConfig.buildClient( | |
| }.build() | ||
| } | ||
|
|
||
| // Configure a user-provided client to collect SDK metrics | ||
| private fun OkHttpClient.withMetrics(metrics: HttpClientMetrics, config: OkHttpEngineConfig) = newBuilder().apply { | ||
| eventListenerFactory { call -> | ||
| EventListenerChain( | ||
| listOf( | ||
| HttpEngineEventListener(connectionPool, config.hostResolver, dispatcher, metrics, call), | ||
| ), | ||
| ) | ||
| } | ||
| addInterceptor(MetricsInterceptor) | ||
| }.build() | ||
|
|
||
| private fun tlsConnectionSpec(tlsContext: TlsContext, cipherSuites: List<String>?): ConnectionSpec { | ||
| val minVersion = tlsContext.minVersion ?: TlsVersion.TLS_1_2 | ||
| val okHttpTlsVersions = SdkTlsVersion | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| package aws.smithy.kotlin.runtime.http.engine.okhttp | ||
|
|
||
| import aws.smithy.kotlin.runtime.content.ByteStream | ||
| import aws.smithy.kotlin.runtime.http.Headers | ||
| import aws.smithy.kotlin.runtime.http.HttpException | ||
| import aws.smithy.kotlin.runtime.http.HttpMethod | ||
| import aws.smithy.kotlin.runtime.http.SdkHttpClient | ||
| import aws.smithy.kotlin.runtime.http.request.HttpRequest | ||
| import aws.smithy.kotlin.runtime.http.toHttpBody | ||
| import aws.smithy.kotlin.runtime.net.url.Url | ||
| import kotlinx.coroutines.test.runTest | ||
| import okhttp3.OkHttpClient | ||
| import java.io.IOException | ||
| import kotlin.test.Test | ||
| import kotlin.test.assertFailsWith | ||
| import kotlin.test.assertTrue | ||
|
|
||
| class OkHttpEngineConfigTest { | ||
| @Test | ||
| fun testUserClient() = runTest { | ||
| val userClient = OkHttpClient.Builder().apply { | ||
| addInterceptor { throw DummyOkHttpClientException() } | ||
| }.build() | ||
|
|
||
| val engine = OkHttpEngine(userClient) | ||
| val sdkClient = SdkHttpClient(engine) | ||
|
|
||
| val data = "a".repeat(100) | ||
| val url = Url.parse("https://aws.amazon.com") | ||
| val request = HttpRequest(HttpMethod.POST, url, Headers.Empty, ByteStream.fromString(data).toHttpBody()) | ||
|
|
||
| val ex = assertFailsWith<HttpException> { | ||
| sdkClient.call(request) | ||
| } | ||
| assertTrue(ex.cause is DummyOkHttpClientException) | ||
| } | ||
|
|
||
| private class DummyOkHttpClientException : IOException("Custom OkHttpClient interceptor was called") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Why did this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why it was commented, but I don't think it should be. This is necessary for TLS tests to pass, it was originally added to aws-crt-kotlin and I applied it here too, but it might not be necessary since our CI has been passing without it. Want me to delete it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not necessary for CI and we don't need it for any other reason then yes, I think we should remove it.