From 1073b5ee4dc000b4ca870793ada69a33d03a3333 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Sun, 12 May 2024 10:47:12 +0200 Subject: [PATCH] Commonize ktor plugins setup --- src/backend/build.gradle.kts | 12 +--- .../main/kotlin/org/icpclive/Application.kt | 38 ++----------- .../kotlin/org/icpclive/util/WebSockets.kt | 3 +- src/cds-converter/build.gradle.kts | 15 ++--- .../src/main/kotlin/Application.kt | 44 +-------------- src/oracle-tools/build.gradle.kts | 13 +---- .../kotlin/org/icpclive/oracle/Application.kt | 55 ++----------------- src/server-shared/build.gradle.kts | 7 ++- .../org/icpclive/server}/JsonSettings.kt | 4 +- .../kotlin/org/icpclive/server/KtorPlugins.kt | 35 ++++++++++++ 10 files changed, 66 insertions(+), 160 deletions(-) rename src/{backend/src/main/kotlin/org/icpclive/util => server-shared/src/main/kotlin/org/icpclive/server}/JsonSettings.kt (82%) create mode 100644 src/server-shared/src/main/kotlin/org/icpclive/server/KtorPlugins.kt diff --git a/src/backend/build.gradle.kts b/src/backend/build.gradle.kts index 50394e3e8..f58852800 100644 --- a/src/backend/build.gradle.kts +++ b/src/backend/build.gradle.kts @@ -42,20 +42,14 @@ tasks { } dependencies { - implementation(projects.cds.full) - implementation(projects.backendApi) - implementation(projects.serverShared) implementation(libs.cli) implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.ktor.server.auth) - implementation(libs.ktor.server.autoHeadResponse) - implementation(libs.ktor.server.callLogging) implementation(libs.ktor.server.contentNegotiation) implementation(libs.ktor.server.core) - implementation(libs.ktor.server.cors) - implementation(libs.ktor.server.defaultHeaders) - implementation(libs.ktor.server.netty) - implementation(libs.ktor.server.statusPages) implementation(libs.ktor.server.websockets) implementation(libs.logback) + implementation(projects.backendApi) + implementation(projects.cds.full) + implementation(projects.serverShared) } diff --git a/src/backend/src/main/kotlin/org/icpclive/Application.kt b/src/backend/src/main/kotlin/org/icpclive/Application.kt index 0cd8b257f..7143f2907 100644 --- a/src/backend/src/main/kotlin/org/icpclive/Application.kt +++ b/src/backend/src/main/kotlin/org/icpclive/Application.kt @@ -1,20 +1,12 @@ package org.icpclive -import org.icpclive.util.completeOrThrow -import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* +import org.icpclive.util.completeOrThrow import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.http.content.* -import io.ktor.server.plugins.autohead.* -import io.ktor.server.plugins.callloging.* import io.ktor.server.plugins.contentnegotiation.* -import io.ktor.server.plugins.cors.routing.* -import io.ktor.server.plugins.defaultheaders.* -import io.ktor.server.plugins.statuspages.* -import io.ktor.server.request.* import io.ktor.server.routing.* -import io.ktor.server.util.* import io.ktor.server.websocket.* import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.* @@ -26,9 +18,9 @@ import org.icpclive.cds.util.* import org.icpclive.data.Controllers import org.icpclive.data.DataBus import org.icpclive.overlay.configureOverlayRouting +import org.icpclive.server.serverResponseJsonSettings +import org.icpclive.server.setupDefaultKtorPlugins import org.icpclive.service.launchServices -import org.icpclive.util.defaultJsonSettings -import org.slf4j.event.Level import java.time.Duration import kotlin.system.exitProcess @@ -36,20 +28,8 @@ import kotlin.system.exitProcess fun main(args: Array): Unit = Config.main(args) private fun Application.setupKtorPlugins() { - install(DefaultHeaders) - install(CallLogging) { - level = Level.INFO - filter { call -> call.request.path().startsWith("/") } - } - install(StatusPages) { - exception { call, ex -> - call.application.environment.log.error("Query ${call.url()} failed with exception", ex) - throw ex - } - } - install(AutoHeadResponse) - install(IgnoreTrailingSlash) - install(ContentNegotiation) { json(defaultJsonSettings()) } + setupDefaultKtorPlugins() + install(ContentNegotiation) { json(serverResponseJsonSettings()) } install(WebSockets) { pingPeriod = Duration.ofSeconds(15) timeout = Duration.ofSeconds(15) @@ -76,14 +56,6 @@ private fun Application.setupKtorPlugins() { } } } - install(CORS) { - allowHeader(HttpHeaders.Authorization) - allowHeader(HttpHeaders.ContentType) - allowHeader("*") - allowMethod(HttpMethod.Delete) - allowSameOrigin = true - anyHost() - } } @Suppress("unused") // application.yaml references the main function. This annotation prevents the IDE from marking it as unused. diff --git a/src/backend/src/main/kotlin/org/icpclive/util/WebSockets.kt b/src/backend/src/main/kotlin/org/icpclive/util/WebSockets.kt index 8459dced8..81967a4ab 100644 --- a/src/backend/src/main/kotlin/org/icpclive/util/WebSockets.kt +++ b/src/backend/src/main/kotlin/org/icpclive/util/WebSockets.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.serialization.encodeToString +import org.icpclive.server.serverResponseJsonSettings suspend fun DefaultWebSocketServerSession.sendFlow(flow: Flow) { val sender = async { @@ -24,6 +25,6 @@ suspend fun DefaultWebSocketServerSession.sendFlow(flow: Flow) { } suspend inline fun DefaultWebSocketServerSession.sendJsonFlow(flow: Flow) { - val formatter = defaultJsonSettings() + val formatter = serverResponseJsonSettings() sendFlow(flow.map { formatter.encodeToString(it) }) } \ No newline at end of file diff --git a/src/cds-converter/build.gradle.kts b/src/cds-converter/build.gradle.kts index 8843d649a..7d922d78c 100644 --- a/src/cds-converter/build.gradle.kts +++ b/src/cds-converter/build.gradle.kts @@ -18,19 +18,12 @@ tasks.runTask { } dependencies { - implementation(projects.cds.full) - implementation(projects.clicsApi) - implementation(projects.serverShared) + implementation(libs.apache.commons.csv) implementation(libs.cli) implementation(libs.ktor.serialization.kotlinx.json) - implementation(libs.ktor.server.autoHeadResponse) - implementation(libs.ktor.server.callLogging) implementation(libs.ktor.server.contentNegotiation) implementation(libs.ktor.server.core) - implementation(libs.ktor.server.cors) - implementation(libs.ktor.server.defaultHeaders) - implementation(libs.ktor.server.netty) - implementation(libs.ktor.server.statusPages) - implementation(libs.ktor.server.websockets) - implementation(libs.apache.commons.csv) + implementation(projects.cds.full) + implementation(projects.clicsApi) + implementation(projects.serverShared) } diff --git a/src/cds-converter/src/main/kotlin/Application.kt b/src/cds-converter/src/main/kotlin/Application.kt index 1fad40fec..91de20633 100644 --- a/src/cds-converter/src/main/kotlin/Application.kt +++ b/src/cds-converter/src/main/kotlin/Application.kt @@ -6,19 +6,10 @@ import com.github.ajalt.clikt.core.* import com.github.ajalt.clikt.output.MordantHelpFormatter import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple -import com.github.ajalt.clikt.parameters.options.* import io.ktor.http.* import io.ktor.server.application.* -import io.ktor.server.plugins.autohead.* -import io.ktor.server.plugins.callloging.* -import io.ktor.server.plugins.cors.routing.* -import io.ktor.server.plugins.defaultheaders.* -import io.ktor.server.plugins.statuspages.* -import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -import io.ktor.server.util.* -import io.ktor.server.websocket.* import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.shareIn @@ -26,8 +17,7 @@ import kotlinx.coroutines.plus import org.icpclive.export.clics.ClicsExporter import org.icpclive.export.icpc.csv.IcpcCsvExporter import org.icpclive.export.pcms.PCMSExporter -import org.slf4j.event.Level -import java.time.Duration +import org.icpclive.server.setupDefaultKtorPlugins import kotlin.system.exitProcess @@ -56,39 +46,9 @@ fun main(args: Array): Unit = MainCommand.subcommands( ).main(args) -private fun Application.setupKtorPlugins() { - install(DefaultHeaders) - install(CallLogging) { - level = Level.INFO - filter { call -> call.request.path().startsWith("/") } - } - install(StatusPages) { - exception { call, ex -> - call.application.environment.log.error("Query ${call.url()} failed with exception", ex) - throw ex - } - } - install(AutoHeadResponse) - install(IgnoreTrailingSlash) - install(WebSockets) { - pingPeriod = Duration.ofSeconds(15) - timeout = Duration.ofSeconds(15) - maxFrameSize = Long.MAX_VALUE - masking = false - } - install(CORS) { - allowHeader(HttpHeaders.Authorization) - allowHeader(HttpHeaders.ContentType) - allowHeader("*") - allowMethod(HttpMethod.Delete) - allowSameOrigin = true - anyHost() - } -} - @Suppress("unused") // application.yaml references the main function. This annotation prevents the IDE from marking it as unused. fun Application.module() { - setupKtorPlugins() + setupDefaultKtorPlugins() val handler = CoroutineExceptionHandler { coroutineContext, throwable -> environment.log.error("Uncaught exception in coroutine context $coroutineContext", throwable) diff --git a/src/oracle-tools/build.gradle.kts b/src/oracle-tools/build.gradle.kts index 7378e7926..f285acbc6 100644 --- a/src/oracle-tools/build.gradle.kts +++ b/src/oracle-tools/build.gradle.kts @@ -31,19 +31,12 @@ tasks { } dependencies { - implementation(projects.cds.full) - implementation(projects.backendApi) - implementation(projects.serverShared) implementation(libs.cli) implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.ktor.server.auth) - implementation(libs.ktor.server.autoHeadResponse) - implementation(libs.ktor.server.callLogging) implementation(libs.ktor.server.contentNegotiation) implementation(libs.ktor.server.core) - implementation(libs.ktor.server.cors) - implementation(libs.ktor.server.defaultHeaders) - implementation(libs.ktor.server.netty) - implementation(libs.ktor.server.statusPages) - implementation(libs.ktor.server.websockets) + implementation(projects.backendApi) + implementation(projects.cds.full) + implementation(projects.serverShared) } diff --git a/src/oracle-tools/src/main/kotlin/org/icpclive/oracle/Application.kt b/src/oracle-tools/src/main/kotlin/org/icpclive/oracle/Application.kt index 69fd98b94..91ff41c5d 100644 --- a/src/oracle-tools/src/main/kotlin/org/icpclive/oracle/Application.kt +++ b/src/oracle-tools/src/main/kotlin/org/icpclive/oracle/Application.kt @@ -1,66 +1,19 @@ package org.icpclive.oracle -import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import io.ktor.server.application.* import io.ktor.server.http.content.* -import io.ktor.server.plugins.autohead.* -import io.ktor.server.plugins.callloging.* import io.ktor.server.plugins.contentnegotiation.* -import io.ktor.server.plugins.cors.routing.* -import io.ktor.server.plugins.defaultheaders.* -import io.ktor.server.plugins.statuspages.* -import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -import io.ktor.server.util.* -import io.ktor.server.websocket.* -import kotlinx.serialization.json.Json -import org.slf4j.event.Level -import java.time.Duration +import org.icpclive.server.serverResponseJsonSettings +import org.icpclive.server.setupDefaultKtorPlugins fun main(args: Array): Unit = Config.main(args) - private fun Application.setupKtorPlugins() { - install(DefaultHeaders) - install(CallLogging) { - level = Level.INFO - filter { call -> call.request.path().startsWith("/") } - } - install(StatusPages) { - exception { call, ex -> - call.application.environment.log.error("Query ${call.url()} failed with exception", ex) - throw ex - } - } - install(AutoHeadResponse) - install(IgnoreTrailingSlash) - install(ContentNegotiation) { - json(Json { - encodeDefaults = true - isLenient = true - allowSpecialFloatingPointValues = true - allowStructuredMapKeys = true - prettyPrint = false - useArrayPolymorphism = false - explicitNulls = false - }) - } - install(WebSockets) { - pingPeriod = Duration.ofSeconds(15) - timeout = Duration.ofSeconds(15) - maxFrameSize = Long.MAX_VALUE - masking = false - } - install(CORS) { - allowHeader(HttpHeaders.Authorization) - allowHeader(HttpHeaders.ContentType) - allowHeader("*") - allowMethod(HttpMethod.Delete) - allowSameOrigin = true - anyHost() - } + setupDefaultKtorPlugins() + install(ContentNegotiation) { json(serverResponseJsonSettings()) } } @Suppress("unused") // application.yaml references the main function. This annotation prevents the IDE from marking it as unused. diff --git a/src/server-shared/build.gradle.kts b/src/server-shared/build.gradle.kts index 6bcda2f53..1e3b98a59 100644 --- a/src/server-shared/build.gradle.kts +++ b/src/server-shared/build.gradle.kts @@ -4,8 +4,13 @@ plugins { dependencies { api(libs.cli) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.ktor.server.autoHeadResponse) + implementation(libs.ktor.server.callLogging) implementation(libs.ktor.server.core) + implementation(libs.ktor.server.cors) + implementation(libs.ktor.server.defaultHeaders) implementation(libs.ktor.server.netty) - implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.ktor.server.statusPages) implementation(libs.logback) } \ No newline at end of file diff --git a/src/backend/src/main/kotlin/org/icpclive/util/JsonSettings.kt b/src/server-shared/src/main/kotlin/org/icpclive/server/JsonSettings.kt similarity index 82% rename from src/backend/src/main/kotlin/org/icpclive/util/JsonSettings.kt rename to src/server-shared/src/main/kotlin/org/icpclive/server/JsonSettings.kt index ff312ef5c..f37416b5e 100644 --- a/src/backend/src/main/kotlin/org/icpclive/util/JsonSettings.kt +++ b/src/server-shared/src/main/kotlin/org/icpclive/server/JsonSettings.kt @@ -1,10 +1,10 @@ -package org.icpclive.util +package org.icpclive.server import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json @OptIn(ExperimentalSerializationApi::class) -fun defaultJsonSettings(): Json = Json { +fun serverResponseJsonSettings(): Json = Json { encodeDefaults = true isLenient = true allowSpecialFloatingPointValues = true diff --git a/src/server-shared/src/main/kotlin/org/icpclive/server/KtorPlugins.kt b/src/server-shared/src/main/kotlin/org/icpclive/server/KtorPlugins.kt new file mode 100644 index 000000000..3d92d6620 --- /dev/null +++ b/src/server-shared/src/main/kotlin/org/icpclive/server/KtorPlugins.kt @@ -0,0 +1,35 @@ +package org.icpclive.server + +import io.ktor.server.application.* +import io.ktor.server.plugins.autohead.* +import io.ktor.server.plugins.callloging.* +import io.ktor.server.plugins.cors.routing.* +import io.ktor.server.plugins.defaultheaders.* +import io.ktor.server.plugins.statuspages.* +import io.ktor.server.request.* +import io.ktor.server.routing.* +import io.ktor.server.util.* + +fun Application.setupDefaultKtorPlugins() { + install(DefaultHeaders) + install(CallLogging) { + level = org.slf4j.event.Level.INFO + filter { call -> call.request.path().startsWith("/") } + } + install(StatusPages) { + exception { call, ex -> + call.application.environment.log.error("Query ${call.url()} failed with exception", ex) + throw ex + } + } + install(AutoHeadResponse) + install(IgnoreTrailingSlash) + install(CORS) { + allowHeader(io.ktor.http.HttpHeaders.Authorization) + allowHeader(io.ktor.http.HttpHeaders.ContentType) + allowHeader("*") + allowMethod(io.ktor.http.HttpMethod.Delete) + allowSameOrigin = true + anyHost() + } +} \ No newline at end of file