From 34687a723d1a03727a7ed6c77298a4ced73c385e Mon Sep 17 00:00:00 2001 From: Samuel Vazquez Date: Tue, 2 Jan 2024 20:54:56 -0800 Subject: [PATCH] fix: create MessageDigest when needed (#1906) ### :pencil: Description `MessageDiggest` is NOT thread safe, if multiple queries are being executed concurrently it might cause a race condition and send back a `PersistedQueryIdInvalid` error https://opensource.expediagroup.com/graphql-kotlin/docs/server/automatic-persisted-queries/#errors instead we will create a `MessageDigest` instance each time. --- .../AutomaticPersistedQueriesExtensions.kt | 20 +++++++++---------- .../graphql/client/ktor/GraphQLKtorClient.kt | 6 +++--- .../graphql/client/spring/GraphQLWebClient.kt | 6 +++--- .../extensions/executionInputExtensions.kt | 13 ++++++------ 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/extensions/AutomaticPersistedQueriesExtensions.kt b/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/extensions/AutomaticPersistedQueriesExtensions.kt index eedc787f72..e9fac9f408 100644 --- a/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/extensions/AutomaticPersistedQueriesExtensions.kt +++ b/clients/graphql-kotlin-client/src/main/kotlin/com/expediagroup/graphql/client/extensions/AutomaticPersistedQueriesExtensions.kt @@ -6,18 +6,18 @@ import java.math.BigInteger import java.nio.charset.StandardCharsets import java.security.MessageDigest -internal val MESSAGE_DIGEST: MessageDigest = MessageDigest.getInstance("SHA-256") - -fun GraphQLClientRequest<*>.getQueryId(): String = - String.format( +fun GraphQLClientRequest<*>.getQueryId(): String { + val messageDigest = MessageDigest.getInstance("SHA-256") + return String.format( "%064x", - BigInteger(1, MESSAGE_DIGEST.digest(this.query?.toByteArray(StandardCharsets.UTF_8))) - ).also { - MESSAGE_DIGEST.reset() - } + BigInteger(1, messageDigest.digest(this.query?.toByteArray(StandardCharsets.UTF_8))) + ) +} + +fun AutomaticPersistedQueriesExtension.toQueryParamString(): String = + """{"persistedQuery":{"version":$version,"sha256Hash":"$sha256Hash"}}""" -fun AutomaticPersistedQueriesExtension.toQueryParamString() = """{"persistedQuery":{"version":$version,"sha256Hash":"$sha256Hash"}}""" -fun AutomaticPersistedQueriesExtension.toExtentionsBodyMap() = mapOf( +fun AutomaticPersistedQueriesExtension.toExtensionsBodyMap(): Map> = mapOf( "persistedQuery" to mapOf( "version" to version, "sha256Hash" to sha256Hash diff --git a/clients/graphql-kotlin-ktor-client/src/main/kotlin/com/expediagroup/graphql/client/ktor/GraphQLKtorClient.kt b/clients/graphql-kotlin-ktor-client/src/main/kotlin/com/expediagroup/graphql/client/ktor/GraphQLKtorClient.kt index f7e4a9e36d..83030b9f83 100644 --- a/clients/graphql-kotlin-ktor-client/src/main/kotlin/com/expediagroup/graphql/client/ktor/GraphQLKtorClient.kt +++ b/clients/graphql-kotlin-ktor-client/src/main/kotlin/com/expediagroup/graphql/client/ktor/GraphQLKtorClient.kt @@ -18,7 +18,7 @@ package com.expediagroup.graphql.client.ktor import com.expediagroup.graphql.client.GraphQLClient import com.expediagroup.graphql.client.extensions.getQueryId -import com.expediagroup.graphql.client.extensions.toExtentionsBodyMap +import com.expediagroup.graphql.client.extensions.toExtensionsBodyMap import com.expediagroup.graphql.client.extensions.toQueryParamString import com.expediagroup.graphql.client.serializer.GraphQLClientSerializer import com.expediagroup.graphql.client.serializer.defaultGraphQLSerializer @@ -60,8 +60,8 @@ open class GraphQLKtorClient( sha256Hash = queryId ) val extensions = request.extensions?.let { - automaticPersistedQueriesExtension.toExtentionsBodyMap().plus(it) - } ?: automaticPersistedQueriesExtension.toExtentionsBodyMap() + automaticPersistedQueriesExtension.toExtensionsBodyMap().plus(it) + } ?: automaticPersistedQueriesExtension.toExtensionsBodyMap() val apqRawResultWithoutQuery: String = when (automaticPersistedQueriesSettings.httpMethod) { is AutomaticPersistedQueriesSettings.HttpMethod.GET -> { diff --git a/clients/graphql-kotlin-spring-client/src/main/kotlin/com/expediagroup/graphql/client/spring/GraphQLWebClient.kt b/clients/graphql-kotlin-spring-client/src/main/kotlin/com/expediagroup/graphql/client/spring/GraphQLWebClient.kt index 1518f5b446..70758289c1 100644 --- a/clients/graphql-kotlin-spring-client/src/main/kotlin/com/expediagroup/graphql/client/spring/GraphQLWebClient.kt +++ b/clients/graphql-kotlin-spring-client/src/main/kotlin/com/expediagroup/graphql/client/spring/GraphQLWebClient.kt @@ -18,7 +18,7 @@ package com.expediagroup.graphql.client.spring import com.expediagroup.graphql.client.GraphQLClient import com.expediagroup.graphql.client.extensions.getQueryId -import com.expediagroup.graphql.client.extensions.toExtentionsBodyMap +import com.expediagroup.graphql.client.extensions.toExtensionsBodyMap import com.expediagroup.graphql.client.extensions.toQueryParamString import com.expediagroup.graphql.client.serializer.GraphQLClientSerializer import com.expediagroup.graphql.client.serializer.defaultGraphQLSerializer @@ -63,8 +63,8 @@ open class GraphQLWebClient( sha256Hash = queryId ) val extensions = request.extensions?.let { - automaticPersistedQueriesExtension.toExtentionsBodyMap().plus(it) - } ?: automaticPersistedQueriesExtension.toExtentionsBodyMap() + automaticPersistedQueriesExtension.toExtensionsBodyMap().plus(it) + } ?: automaticPersistedQueriesExtension.toExtensionsBodyMap() val apqRawResultWithoutQuery: String = when (automaticPersistedQueriesSettings.httpMethod) { is AutomaticPersistedQueriesSettings.HttpMethod.GET -> { diff --git a/executions/graphql-kotlin-automatic-persisted-queries/src/main/kotlin/com/expediagroup/graphql/apq/extensions/executionInputExtensions.kt b/executions/graphql-kotlin-automatic-persisted-queries/src/main/kotlin/com/expediagroup/graphql/apq/extensions/executionInputExtensions.kt index 79d295779c..852391e748 100644 --- a/executions/graphql-kotlin-automatic-persisted-queries/src/main/kotlin/com/expediagroup/graphql/apq/extensions/executionInputExtensions.kt +++ b/executions/graphql-kotlin-automatic-persisted-queries/src/main/kotlin/com/expediagroup/graphql/apq/extensions/executionInputExtensions.kt @@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets import java.security.MessageDigest internal const val APQ_EXTENSION_KEY: String = "persistedQuery" -internal val MESSAGE_DIGEST: MessageDigest = MessageDigest.getInstance("SHA-256") @Suppress("UNCHECKED_CAST") fun ExecutionInput.getAutomaticPersistedQueriesExtension(): AutomaticPersistedQueriesExtension? = @@ -34,13 +33,13 @@ fun ExecutionInput.getAutomaticPersistedQueriesExtension(): AutomaticPersistedQu null } -fun ExecutionInput.getQueryId(): String = - String.format( +fun ExecutionInput.getQueryId(): String { + val messageDigest = MessageDigest.getInstance("SHA-256") + return String.format( "%064x", - BigInteger(1, MESSAGE_DIGEST.digest(this.query.toByteArray(StandardCharsets.UTF_8))) - ).also { - MESSAGE_DIGEST.reset() - } + BigInteger(1, messageDigest.digest(this.query.toByteArray(StandardCharsets.UTF_8))) + ) +} fun ExecutionInput.isAutomaticPersistedQueriesExtensionInvalid( extension: AutomaticPersistedQueriesExtension