Skip to content

Commit

Permalink
feat: SDK-1568 expose okhttp client as a configuration in sdk core
Browse files Browse the repository at this point in the history
  • Loading branch information
dtayeh committed Nov 19, 2024
1 parent f7cb286 commit 1e3eec7
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import com.expediagroup.sdk.core.configuration.provider.ConfigurationProvider
import com.expediagroup.sdk.core.configuration.provider.RapidConfigurationProvider
import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationStrategy
import io.ktor.client.HttpClient
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.*
import io.ktor.client.engine.okhttp.*
import okhttp3.OkHttpClient

/**
* The integration point between the SDK Core and the product SDKs.
Expand All @@ -32,14 +34,25 @@ import io.ktor.client.engine.HttpClientEngine
abstract class BaseRapidClient(
namespace: String,
clientConfiguration: RapidClientConfiguration,
httpClientEngine: HttpClientEngine = DEFAULT_HTTP_CLIENT_ENGINE
) : Client(namespace) {
private val _configurationProvider: ConfigurationProvider =
ConfigurationCollector.create(
clientConfiguration.toProvider(),
RapidConfigurationProvider
)
private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, httpClientEngine)

private val _httpClientEngine: HttpClientEngine = clientConfiguration.okHttpClient?.let {

// Compose an event listener by joining two event listeners
// Initialize the CompositeOkHttpEventListener with the OkHttpEventListener and the user passed eventListenerFactory
CompositeOkHttpEventListener.initialize(OkHttpEventListener.FACTORY, it.eventListenerFactory)

OkHttp.create {
preconfigured = it.newBuilder().eventListenerFactory(CompositeOkHttpEventListener).build()
}
} ?: DEFAULT_HTTP_CLIENT_ENGINE

private val _httpClient: HttpClient = buildHttpClient(_configurationProvider, AuthenticationStrategy.AuthenticationType.SIGNATURE, _httpClientEngine)

init {
finalize()
Expand All @@ -53,5 +66,45 @@ abstract class BaseRapidClient(

/** A [BaseRapidClient] builder. */
@Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients.
abstract class Builder<SELF : Builder<SELF>> : Client.Builder<SELF>()
abstract class Builder<SELF : Builder<SELF>> : Client.Builder<SELF>() {
/** Sets the custom okHttpClient to use for making requests. */
protected var okHttpClient: OkHttpClient? = null

/** Sets the custom okHttpClient to use for making requests.
*
* @param okHttpClient The custom okHttpClient to use for making requests.
* @return The [Builder] instance.
*/
fun okHttpClient(okHttpClient: OkHttpClient): SELF {

if (this.connectionTimeout != null || this.requestTimeout != null || this.socketTimeout != null) {
throw IllegalArgumentException("Cannot set a custom OkHttp client when using the default httpclient with configured timeouts.")
}

this.okHttpClient = okHttpClient
return self()
}

override fun requestTimeout(milliseconds: Long): SELF {
if (this.okHttpClient != null) {
throw IllegalArgumentException("Cannot set timeouts when using a custom OkHttpClient, set timeouts on the OkHttpClient instead.")
}
return super.requestTimeout(milliseconds)
}

override fun socketTimeout(milliseconds: Long): SELF {
if (this.okHttpClient != null) {
throw IllegalArgumentException("Cannot set timeouts when using a custom OkHttpClient, set timeouts on the OkHttpClient instead.")
}
return super.socketTimeout(milliseconds)
}

override fun connectionTimeout(milliseconds: Long): SELF {
if (this.okHttpClient != null) {
throw IllegalArgumentException("Cannot set timeouts when using a custom OkHttpClient, set timeouts on the OkHttpClient instead.")
}
return super.connectionTimeout(milliseconds)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ val dispatcher = Dispatcher().apply {
val DEFAULT_HTTP_CLIENT_ENGINE: HttpClientEngine =
OkHttp.create {
config {
eventListener(OkHttpEventListener)
eventListenerFactory(OkHttpEventListener.FACTORY)
dispatcher(dispatcher)
}
}
Expand Down Expand Up @@ -231,7 +231,7 @@ abstract class Client(
* @param milliseconds The request timeout to be used.
* @return The [Builder] instance.
*/
fun requestTimeout(milliseconds: Long): SELF {
open fun requestTimeout(milliseconds: Long): SELF {
this.requestTimeout = milliseconds
log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.REQUEST_TIMEOUT_MILLIS, milliseconds.toString()))
return self()
Expand All @@ -245,7 +245,7 @@ abstract class Client(
* @param milliseconds The connection timeout to be used.
* @return The [Builder] instance.
*/
fun connectionTimeout(milliseconds: Long): SELF {
open fun connectionTimeout(milliseconds: Long): SELF {
this.connectionTimeout = milliseconds
log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.CONNECTION_TIMEOUT_MILLIS, milliseconds.toString()))
return self()
Expand All @@ -259,7 +259,7 @@ abstract class Client(
* @param milliseconds The socket timeout to be used.
* @return The [Builder] instance.
*/
fun socketTimeout(milliseconds: Long): SELF {
open fun socketTimeout(milliseconds: Long): SELF {
this.socketTimeout = milliseconds
log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.SOCKET_TIMEOUT_MILLIS, milliseconds.toString()))
return self()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.expediagroup.sdk.core.client

import okhttp3.Call
import okhttp3.EventListener
import okhttp3.Response
import java.io.IOException

// A CompositeEventListener that takes multiple event listener factories
class CompositeOkHttpEventListener private constructor(private val listeners: List<EventListener>) : EventListener() {

companion object Factory : EventListener.Factory {
private lateinit var factories: Array<out EventListener.Factory>

fun initialize(vararg factories: EventListener.Factory) {
this.factories = factories
}

override fun create(call: Call): EventListener {
val listeners = factories.map { it.create(call) }
return CompositeOkHttpEventListener(listeners)
}
}

override fun callStart(call: Call) {
listeners.forEach { it.callStart(call) }
}

override fun callEnd(call: Call) {
listeners.forEach { it.callEnd(call) }
}

override fun callFailed(call: Call, ioe: IOException) {
listeners.forEach { it.callFailed(call, ioe) }
}

override fun canceled(call: Call) {
listeners.forEach { it.canceled(call) }
}

override fun connectStart(call: Call, inetSocketAddress: java.net.InetSocketAddress, proxy: java.net.Proxy) {
listeners.forEach { it.connectStart(call, inetSocketAddress, proxy) }
}

override fun connectEnd(call: Call, inetSocketAddress: java.net.InetSocketAddress, proxy: java.net.Proxy, protocol: okhttp3.Protocol?) {
listeners.forEach { it.connectEnd(call, inetSocketAddress, proxy, protocol) }
}

override fun connectFailed(call: Call, inetSocketAddress: java.net.InetSocketAddress, proxy: java.net.Proxy, protocol: okhttp3.Protocol?, ioe: IOException) {
listeners.forEach { it.connectFailed(call, inetSocketAddress, proxy, protocol, ioe) }
}

override fun requestHeadersStart(call: Call) {
listeners.forEach { it.requestHeadersStart(call) }
}

override fun requestHeadersEnd(call: Call, request: okhttp3.Request) {
listeners.forEach { it.requestHeadersEnd(call, request) }
}

override fun requestBodyStart(call: Call) {
listeners.forEach { it.requestBodyStart(call) }
}

override fun requestBodyEnd(call: Call, byteCount: Long) {
listeners.forEach { it.requestBodyEnd(call, byteCount) }
}

override fun responseHeadersStart(call: Call) {
listeners.forEach { it.responseHeadersStart(call) }
}

override fun responseHeadersEnd(call: Call, response: okhttp3.Response) {
listeners.forEach { it.responseHeadersEnd(call, response) }
}

override fun responseBodyStart(call: Call) {
listeners.forEach { it.responseBodyStart(call) }
}

override fun responseBodyEnd(call: Call, byteCount: Long) {
listeners.forEach { it.responseBodyEnd(call, byteCount) }
}

override fun responseFailed(call: Call, ioe: IOException) {
listeners.forEach { it.responseFailed(call, ioe) }
}

override fun proxySelectStart(call: Call, url: okhttp3.HttpUrl) {
listeners.forEach { it.proxySelectStart(call, url) }
}

override fun proxySelectEnd(call: Call, url: okhttp3.HttpUrl, proxies: List<java.net.Proxy>) {
listeners.forEach { it.proxySelectEnd(call, url, proxies) }
}

override fun dnsStart(call: Call, domainName: String) {
listeners.forEach { it.dnsStart(call, domainName) }
}

override fun dnsEnd(call: Call, domainName: String, inetAddressList: List<java.net.InetAddress>) {
listeners.forEach { it.dnsEnd(call, domainName, inetAddressList) }
}

override fun connectionAcquired(call: Call, connection: okhttp3.Connection) {
listeners.forEach { it.connectionAcquired(call, connection) }
}

override fun connectionReleased(call: Call, connection: okhttp3.Connection) {
listeners.forEach { it.connectionReleased(call, connection) }
}

override fun secureConnectStart(call: Call) {
listeners.forEach { it.secureConnectStart(call) }
}

override fun secureConnectEnd(call: Call, handshake: okhttp3.Handshake?) {
listeners.forEach { it.secureConnectEnd(call, handshake) }
}

override fun requestFailed(call: Call, ioe: IOException) {
listeners.forEach { it.requestFailed(call, ioe) }
}

override fun cacheHit(call: Call, response: Response) {
listeners.forEach { it.cacheHit(call, response) }
}

override fun cacheMiss(call: Call) {
listeners.forEach { it.cacheMiss(call) }
}

override fun cacheConditionalHit(call: Call, response: Response) {
listeners.forEach { it.cacheConditionalHit(call, response) }
}

override fun satisfactionFailure(call: Call, response: Response) {
listeners.forEach { it.satisfactionFailure(call, response) }
}

}
Loading

0 comments on commit 1e3eec7

Please sign in to comment.