diff --git a/core/build.gradle.kts b/core/build.gradle.kts index b75e1905..a8613771 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -39,6 +39,7 @@ kotlin { api(libs.kotlinx.datetime) implementation(libs.kotlinx.coroutines.core) implementation(libs.ktor.client.core) + implementation(libs.ktor.client.encoding) // implementation(project(":crypto")) implementation(libs.lightspark.crypto) } diff --git a/core/src/commonMain/kotlin/com/lightspark/sdk/core/requester/Requester.kt b/core/src/commonMain/kotlin/com/lightspark/sdk/core/requester/Requester.kt index c00086e5..577a0157 100644 --- a/core/src/commonMain/kotlin/com/lightspark/sdk/core/requester/Requester.kt +++ b/core/src/commonMain/kotlin/com/lightspark/sdk/core/requester/Requester.kt @@ -10,6 +10,7 @@ import com.lightspark.sdk.core.crypto.NodeKeyCache import com.lightspark.sdk.core.crypto.nextInt import com.lightspark.sdk.core.util.getPlatform import io.ktor.client.HttpClient +import io.ktor.client.plugins.compression.ContentEncoding import io.ktor.client.plugins.websocket.WebSockets import io.ktor.client.request.headers import io.ktor.client.request.post @@ -34,6 +35,7 @@ import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive +import java.util.zip.Deflater private const val DEFAULT_BASE_URL = "api.lightspark.com" @@ -45,6 +47,9 @@ class Requester constructor( private val baseUrl: String = DEFAULT_BASE_URL, ) { private val httpClient = HttpClient { + install(ContentEncoding) { + gzip() // Switch to deflate() when https://youtrack.jetbrains.com/issue/KTOR-6999 is fixed + } install(WebSockets) } private val userAgent = @@ -87,15 +92,15 @@ class Requester constructor( ) } val operation = operationMatch.groupValues[2] - var bodyData = buildJsonObject { + var payload = buildJsonObject { put("query", JsonPrimitive(queryPayload)) variables?.let { put("variables", it) } put("operationName", JsonPrimitive(operation)) } - var headers = defaultHeaders + authProvider.getCredentialHeaders() + var headers = defaultHeaders + authProvider.getCredentialHeaders() + mapOf("X-GraphQL-Operation" to operation) val signedBodyAndHeaders = try { addSigningDataIfNeeded( - bodyData, + payload, headers, signingNodeId, ) @@ -106,11 +111,25 @@ class Requester constructor( e, ) } - bodyData = signedBodyAndHeaders.first + var body = signedBodyAndHeaders.first headers = signedBodyAndHeaders.second + if (body.size > 1024) { + val output = ByteArray(body.size) + val deflater = Deflater(Deflater.BEST_SPEED) + deflater.setInput(body) + deflater.finish() + val length = deflater.deflate(output) + deflater.end() + body = output.copyOfRange(0, length) + + headers = headers.toMutableMap().apply { + this["Content-Encoding"] = "deflate" + } + } + val response = httpClient.post("https://$baseUrl/$schemaEndpoint") { - setBody(bodyData.toString()) + setBody(body) headers { headers.forEach { (key, value) -> append(key, value) @@ -193,9 +212,9 @@ class Requester constructor( bodyData: JsonObject, headers: Map, signingNodeId: String?, - ): Pair> { + ): Pair> { if (signingNodeId == null) { - return bodyData to headers + return bodyData.toString().encodeToByteArray() to headers } if (!nodeKeyCache.contains(signingNodeId)) { throw MissingKeyException(signingNodeId) @@ -207,13 +226,13 @@ class Requester constructor( put("nonce", JsonPrimitive(nextInt().toUInt().toLong())) put("expires_at", JsonPrimitive(anHourFromNowISOString())) }.let { JsonObject(it) } - val newBodyString = Json.encodeToString(newBodyData) - val signature = nodeKeyCache[signingNodeId].sign(newBodyString.encodeToByteArray()) + val newBodyString = newBodyData.toString().encodeToByteArray() + val signature = nodeKeyCache[signingNodeId].sign(newBodyString) val newHeaders = headers.toMutableMap().apply { this["X-LIGHTSPARK-SIGNING"] = "{\"v\":1,\"signature\":\"${signature.base64Encoded}\"}" } - return newBodyData to newHeaders + return newBodyString to newHeaders } private fun anHourFromNowISOString() = Clock.System.now().plus(1.hours).toString() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 413dd5f6..661218f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -99,6 +99,7 @@ ktor-client-serialization = { module = "io.ktor:ktor-client-serialization", vers ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } ktor-client-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" } +ktor-client-encoding = { module = "io.ktor:ktor-client-encoding", version.ref = "ktor" } kotest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" } @@ -140,4 +141,4 @@ ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } gradleS3 = { id = "com.mgd.core.gradle.s3", version.ref = "gradleS3" } -downloadFile = { id = "de.undercouch.download", version.ref = "downloadFile" } \ No newline at end of file +downloadFile = { id = "de.undercouch.download", version.ref = "downloadFile" }