Skip to content
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

chore: upgrade to ktor v3 #914

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<kotlin.version>2.1.0</kotlin.version>
<ktor.version>2.3.13</ktor.version>
<ktor.version>3.0.3</ktor.version>

<ktlint-plugin.version>3.3.0</ktlint-plugin.version>
<build-helper-maven-plugin.version>3.6.0</build-helper-maven-plugin.version>
Expand Down
22 changes: 3 additions & 19 deletions core/src/main/kotlin/com/expediagroup/sdk/core/client/Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import okhttp3.Dispatcher
import okhttp3.OkHttpClient

// Create a Dispatcher with limits
val dispatcher = Dispatcher().apply {
val configuredDispatcher = Dispatcher().apply {
maxRequests = 10000 // Maximum number of concurrent requests
maxRequestsPerHost = 1000
}
Expand All @@ -63,7 +63,7 @@ val DEFAULT_HTTP_CLIENT_ENGINE: HttpClientEngine =
OkHttp.create {
config {
eventListenerFactory(OkHttpEventListener.FACTORY)
dispatcher(dispatcher)
dispatcher(configuredDispatcher)
}
}

Expand Down Expand Up @@ -102,7 +102,6 @@ abstract class Client(
val connectionTimeout: Long = configurationProvider.connectionTimeout ?: fireMissingConfigurationIssue(ConfigurationName.CONNECTION_TIMEOUT_MILLIS)
val socketTimeout: Long = configurationProvider.socketTimeout ?: fireMissingConfigurationIssue(ConfigurationName.SOCKET_TIMEOUT_MILLIS)
val maskedLoggingHeaders: Set<String> = configurationProvider.maskedLoggingHeaders ?: setOf()
val maskedLoggingBodyFields: Set<String> = configurationProvider.maskedLoggingBodyFields ?: setOf()

val authenticationConfiguration =
AuthenticationConfiguration.from(
Expand All @@ -113,7 +112,7 @@ abstract class Client(
)

plugins {
use(LoggingPlugin).with(LoggingConfiguration.from(httpClientConfig, maskedLoggingHeaders, maskedLoggingBodyFields))
use(LoggingPlugin).with(LoggingConfiguration.from(httpClientConfig, maskedLoggingHeaders))
use(SerializationPlugin).with(SerializationConfiguration.from(httpClientConfig))
use(AuthenticationPlugin).with(authenticationConfiguration)
use(DefaultRequestPlugin).with(DefaultRequestConfiguration.from(httpClientConfig, endpoint))
Expand Down Expand Up @@ -163,9 +162,6 @@ abstract class Client(
/** Sets tne body fields to be masked in logging. */
protected var maskedLoggingHeaders: Set<String>? = null

/** Sets tne body fields to be masked in logging. */
protected var maskedLoggingBodyFields: Set<String>? = null

/** Sets the API key to use for authentication.
*
* @param key The API key to use for authentication.
Expand Down Expand Up @@ -209,18 +205,6 @@ abstract class Client(
return self()
}

/**
* Sets tne body fields to be masked in logging.
*
* @param fields the body fields to be masked in logging.
* @return The [Builder] instance.
*/
fun maskedLoggingBodyFields(vararg fields: String): SELF {
this.maskedLoggingBodyFields = fields.toSet()
log.info(LoggingMessageProvider.getRuntimeConfigurationProviderMessage(ConfigurationName.MASKED_LOGGING_BODY_FIELDS, fields.joinToString()))
return self()
}

/** Create a [Client] object. */
abstract fun build(): Client

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,20 @@ abstract class ExpediaGroupClient(
return self()
}
}

@Suppress("unused", "UnnecessaryAbstractClass") // This is used by the generated SDK clients.
abstract class BuilderWithHttpClient<SELF : Client.BuilderWithHttpClient<SELF>> : Client.BuilderWithHttpClient<SELF>() {
/** Sets the API auth endpoint to use for requests. */
protected var authEndpoint: String? = null

/** Sets the API auth endpoint to use for requests.
*
* @param authEndpoint The API auth endpoint to use for requests.
* @return The [Builder] instance.
*/
fun authEndpoint(authEndpoint: String): SELF {
this.authEndpoint = authEndpoint
return self()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ interface ClientConfiguration {
val connectionTimeout: Long?
val socketTimeout: Long?
val maskedLoggingHeaders: Set<String>?
val maskedLoggingBodyFields: Set<String>?
val okHttpClient: OkHttpClient?

/** Build a [RuntimeConfigurationProvider] from a [ClientConfiguration]. */
Expand All @@ -39,7 +38,6 @@ interface ClientConfiguration {
connectionTimeout = connectionTimeout,
socketTimeout = socketTimeout,
maskedLoggingHeaders = maskedLoggingHeaders,
maskedLoggingBodyFields = maskedLoggingBodyFields,
okHttpClient = okHttpClient
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import okhttp3.OkHttpClient
* @property connectionTimeout The connection timeout to be used in milliseconds.
* @property socketTimeout The socket timeout to be used in milliseconds.
* @property maskedLoggingHeaders The headers to be masked in logging.
* @property maskedLoggingBodyFields The body fields to be masked in logging.
* @property authEndpoint The API endpoint to use for authentication.
* @property okHttpClient The okhttp client to be used by the sdk.
*/
Expand All @@ -41,7 +40,6 @@ data class ExpediaGroupClientConfiguration(
override val connectionTimeout: Long? = null,
override val socketTimeout: Long? = null,
override val maskedLoggingHeaders: Set<String>? = null,
override val maskedLoggingBodyFields: Set<String>? = null,
override val okHttpClient: OkHttpClient? = null,
val authEndpoint: String? = null
) : ClientConfiguration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import okhttp3.OkHttpClient
* @property connectionTimeout The connection timeout to be used in milliseconds.
* @property socketTimeout The socket timeout to be used in milliseconds.
* @property maskedLoggingHeaders The headers to be masked in logging.
* @property maskedLoggingBodyFields The body fields to be masked in logging.
*/
data class RapidClientConfiguration(
override val key: String? = null,
Expand All @@ -38,6 +37,5 @@ data class RapidClientConfiguration(
override val connectionTimeout: Long? = null,
override val socketTimeout: Long? = null,
override val maskedLoggingHeaders: Set<String>? = null,
override val maskedLoggingBodyFields: Set<String>? = null,
override val okHttpClient: OkHttpClient? = null
) : ClientConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import okhttp3.OkHttpClient
* @property connectionTimeout The connection timeout to be used in milliseconds.
* @property socketTimeout The socket timeout to be used in milliseconds.
* @property maskedLoggingHeaders The headers to be masked in logging.
* @property maskedLoggingBodyFields The body fields to be masked in logging.
*/
data class XapClientConfiguration(
override val key: String? = null,
Expand All @@ -38,6 +37,5 @@ data class XapClientConfiguration(
override val connectionTimeout: Long? = null,
override val socketTimeout: Long? = null,
override val maskedLoggingHeaders: Set<String>? = null,
override val maskedLoggingBodyFields: Set<String>? = null,
override val okHttpClient: OkHttpClient? = null
) : ClientConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.expediagroup.sdk.core.constant.ConfigurationName.CONFIGURATION_COLLEC
import com.expediagroup.sdk.core.constant.ConfigurationName.CONNECTION_TIMEOUT_MILLIS
import com.expediagroup.sdk.core.constant.ConfigurationName.ENDPOINT
import com.expediagroup.sdk.core.constant.ConfigurationName.KEY
import com.expediagroup.sdk.core.constant.ConfigurationName.MASKED_LOGGING_BODY_FIELDS
import com.expediagroup.sdk.core.constant.ConfigurationName.MASKED_LOGGING_HEADERS
import com.expediagroup.sdk.core.constant.ConfigurationName.REQUEST_TIMEOUT_MILLIS
import com.expediagroup.sdk.core.constant.ConfigurationName.SECRET
Expand Down Expand Up @@ -66,7 +65,6 @@ internal class ConfigurationCollector private constructor(providers: Configurati
override val connectionTimeout: Long? = providers.firstWith { it.connectionTimeout }.also { it?.log(CONNECTION_TIMEOUT_MILLIS) }?.retrieve()
override val socketTimeout: Long? = providers.firstWith { it.socketTimeout }.also { it?.log(SOCKET_TIMEOUT_MILLIS) }?.retrieve()
override val maskedLoggingHeaders: Set<String>? = providers.firstWith { it.maskedLoggingHeaders }.also { it?.log(MASKED_LOGGING_HEADERS) }?.retrieve()
override val maskedLoggingBodyFields: Set<String>? = providers.firstWith { it.maskedLoggingBodyFields }.also { it?.log(MASKED_LOGGING_BODY_FIELDS) }?.retrieve()
override val okHttpClient: OkHttpClient? = providers.firstWith { it.okHttpClient }?.retrieve()

private fun <T> ProvidedConfiguration<T>.log(configurationName: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ interface ConfigurationProvider {
val maskedLoggingHeaders: Set<String>?
get() = setOf()

/** The body fields to be masked in logging.*/
val maskedLoggingBodyFields: Set<String>?
get() = setOf()

/** The okhttp client to be used by the sdk.*/
val okHttpClient: OkHttpClient?
get() = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import okhttp3.OkHttpClient
* @property connectionTimeout The connection timeout to be used in milliseconds.
* @property socketTimeout The socket timeout to be used in milliseconds.
* @property maskedLoggingHeaders The headers to be masked in logging.
* @property maskedLoggingBodyFields The body fields to be masked in logging.
*/
data class RuntimeConfigurationProvider(
override val name: String = RUNTIME_CONFIGURATION_PROVIDER,
Expand All @@ -42,6 +41,5 @@ data class RuntimeConfigurationProvider(
override val connectionTimeout: Long? = null,
override val socketTimeout: Long? = null,
override val maskedLoggingHeaders: Set<String>? = null,
override val maskedLoggingBodyFields: Set<String>? = null,
override val okHttpClient: OkHttpClient? = null
) : ConfigurationProvider
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@
*/
package com.expediagroup.sdk.core.configuration.provider

import com.expediagroup.sdk.core.configuration.provider.RapidConfigurationProvider.connectionTimeout
import com.expediagroup.sdk.core.configuration.provider.RapidConfigurationProvider.endpoint
import com.expediagroup.sdk.core.configuration.provider.RapidConfigurationProvider.name
import com.expediagroup.sdk.core.configuration.provider.RapidConfigurationProvider.requestTimeout
import com.expediagroup.sdk.core.configuration.provider.RapidConfigurationProvider.socketTimeout
import com.expediagroup.sdk.core.constant.Constant

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
*/
package com.expediagroup.sdk.core.constant

import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.HttpTimeoutConfig

internal object Constant {
const val EMPTY_STRING = ""
const val TEN_SECONDS_IN_MILLIS = 10_000L
const val FIFTEEN_SECONDS_IN_MILLIS = 15_000L
const val INFINITE_TIMEOUT = HttpTimeout.INFINITE_TIMEOUT_MS
const val INFINITE_TIMEOUT = HttpTimeoutConfig.INFINITE_TIMEOUT_MS

private const val SUCCESSFUL_STATUS_CODES_RANGE_START = 200
private const val SUCCESSFUL_STATUS_CODES_RANGE_END = 299
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,29 @@
package com.expediagroup.sdk.core.constant

import io.ktor.http.HttpHeaders
import java.io.Serializable

internal data object LogMaskingFields {
val DEFAULT_MASKED_HEADER_FIELDS: Set<String> = setOf(HttpHeaders.Authorization)
val DEFAULT_MASKED_BODY_FIELDS: Set<String> =
val DEFAULT_MASKED_BODY_FIELDS: Set<LogMaskingField> =
setOf(
"cvv",
"pin",
"card_cvv",
"card_cvv2",
"card_number",
"access_token",
"security_code",
"account_number",
"card_avs_response",
"card_cvv_response",
"card_cvv2_response"
LogMaskingField.Single("cvv"),
LogMaskingField.Single("pin"),
LogMaskingField.Single("card_cvv"),
LogMaskingField.Single("card_cvv2"),
LogMaskingField.Single("card_number"),
LogMaskingField.Single("access_token"),
LogMaskingField.Single("security_code"),
LogMaskingField.Single("account_number"),
LogMaskingField.Single("card_avs_response"),
LogMaskingField.Single("card_cvv_response"),
LogMaskingField.Single("card_cvv2_response"),
LogMaskingField.Pair("payment", "number"),
LogMaskingField.Pair("payment", "security_code")
)
}

sealed class LogMaskingField {
data class Single(val value: String) : LogMaskingField()
data class Pair(val first: String, val second: String) : LogMaskingField()
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import io.ktor.client.statement.HttpResponse
sealed class BasePaginator<R, T>(
private val client: Client,
firstResponse: Response<T>,
private val fallbackBody: T,
private val getBody: suspend (HttpResponse) -> T
) : Iterator<R> {
private var state: ResponseState<T> = DefaultResponseState(firstResponse)
Expand All @@ -42,7 +41,7 @@ sealed class BasePaginator<R, T>(

protected fun nextResponse(): Response<T> {
val response = state.getNextResponse()
state = ResponseStateFactory.getState(extractLink(response.headers), client, fallbackBody, getBody)
state = ResponseStateFactory.getState(extractLink(response.headers), client, getBody)
return response
}
}
Expand All @@ -57,9 +56,8 @@ sealed class BasePaginator<R, T>(
class Paginator<T>(
client: Client,
firstResponse: Response<T>,
fallbackBody: T,
getBody: suspend (HttpResponse) -> T
) : BasePaginator<T, T>(client, firstResponse, fallbackBody, getBody) {
) : BasePaginator<T, T>(client, firstResponse, getBody) {
/**
* Returns the body of the next response.
*
Expand All @@ -78,9 +76,8 @@ class Paginator<T>(
class ResponsePaginator<T>(
client: Client,
firstResponse: Response<T>,
fallbackBody: T,
getBody: suspend (HttpResponse) -> T
) : BasePaginator<Response<T>, T>(client, firstResponse, fallbackBody, getBody) {
) : BasePaginator<Response<T>, T>(client, firstResponse, getBody) {
/**
* Returns the next response.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,9 @@
package com.expediagroup.sdk.core.model.paging

import com.expediagroup.sdk.core.client.Client
import com.expediagroup.sdk.core.constant.HeaderValue
import com.expediagroup.sdk.core.model.Response
import com.expediagroup.sdk.core.plugin.logging.GZipEncoder.decode
import com.expediagroup.sdk.core.plugin.logging.contentEncoding
import io.ktor.client.statement.HttpResponse
import io.ktor.util.InternalAPI
import io.ktor.util.moveToByteArray
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.bits.Memory
import kotlinx.coroutines.runBlocking
import java.nio.ByteBuffer

internal interface ResponseState<T> {
fun getNextResponse(): Response<T>
Expand Down Expand Up @@ -59,7 +51,6 @@ internal class LastResponseState<T> : ResponseState<T> {
internal class FetchLinkState<T>(
private val link: String,
private val client: Client,
private val fallbackBody: T,
private val getBody: suspend (HttpResponse) -> T
) : ResponseState<T> {
override fun getNextResponse(): Response<T> {
Expand All @@ -75,23 +66,7 @@ internal class FetchLinkState<T>(
}

private suspend fun parseBody(response: HttpResponse): T {
return if (decodeBody(response).isEmpty()) fallbackBody else getBody(response)
}

private suspend fun decodeBody(response: HttpResponse): String {
val byteReadChannel = prepareByteReadChannel(response)
val decodedByteReadChannel = if (response.contentEncoding().equals(HeaderValue.GZIP)) client.httpClient.decode(byteReadChannel) else byteReadChannel
val bodyString: String = decodedByteReadChannel.readRemaining().readText()
return bodyString
}

@OptIn(InternalAPI::class)
private suspend fun prepareByteReadChannel(response: HttpResponse): ByteReadChannel {
val bufferSize = response.content.availableForRead
val buffer = ByteBuffer.allocate(bufferSize)
val numberOfBytesRead = response.content.peekTo(Memory(buffer), 0, 0, 0, bufferSize.toLong()).toInt()
val byteReadChannel = ByteReadChannel(buffer.moveToByteArray(), 0, numberOfBytesRead)
return byteReadChannel
return getBody(response)
}
}

Expand All @@ -100,10 +75,9 @@ internal class ResponseStateFactory {
fun <T> getState(
link: String?,
client: Client,
fallbackBody: T,
getBody: suspend (HttpResponse) -> T
): ResponseState<T> {
return link?.let { FetchLinkState(it, client, fallbackBody, getBody) } ?: LastResponseState()
return link?.let { FetchLinkState(it, client, getBody) } ?: LastResponseState()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import com.expediagroup.sdk.core.plugin.authentication.AuthenticationConfigurati
import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationStrategy.AuthenticationType.BASIC
import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationStrategy.AuthenticationType.BEARER
import com.expediagroup.sdk.core.plugin.authentication.strategy.AuthenticationStrategy.AuthenticationType.SIGNATURE
import io.ktor.client.plugins.auth.Auth
import io.ktor.client.plugins.auth.AuthConfig
import io.ktor.client.request.HttpRequestBuilder

internal interface AuthenticationStrategy {
fun loadAuth(auth: Auth) {}
fun loadAuth(auth: AuthConfig) {}

fun isTokenAboutToExpire(): Boolean

Expand Down
Loading
Loading