From f831b02ef61a8d479c89704da21c450af8d0a6a0 Mon Sep 17 00:00:00 2001 From: Adrian McCague Date: Tue, 8 Oct 2024 22:55:27 +0100 Subject: [PATCH] Improve Jetty connection configuration by exposing a builder on the HttpConfiguration object --- .../api/ktor-server-jetty-jakarta.api | 2 + .../jakarta/JettyApplicationEngineBase.kt | 8 ++- .../server/jetty/jakarta/ServerInitializer.kt | 3 +- .../server/jetty/jakarta/JettyEngineTest.kt | 4 +- .../jakarta/JettyHttpConfigurationTest.kt | 51 +++++++++++++++++++ .../api/ktor-server-jetty.api | 2 + .../jetty/JettyApplicationEngineBase.kt | 6 +++ .../io/ktor/server/jetty/ServerInitializer.kt | 2 +- .../jetty/JettyHttpConfigurationTest.kt | 51 +++++++++++++++++++ 9 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 ktor-server/ktor-server-jetty-jakarta/jvm/test/io/ktor/tests/server/jetty/jakarta/JettyHttpConfigurationTest.kt create mode 100644 ktor-server/ktor-server-jetty/jvm/test/io/ktor/tests/server/jetty/JettyHttpConfigurationTest.kt diff --git a/ktor-server/ktor-server-jetty-jakarta/api/ktor-server-jetty-jakarta.api b/ktor-server/ktor-server-jetty-jakarta/api/ktor-server-jetty-jakarta.api index 9a0163c1350..f0d18f5e825 100644 --- a/ktor-server/ktor-server-jetty-jakarta/api/ktor-server-jetty-jakarta.api +++ b/ktor-server/ktor-server-jetty-jakarta/api/ktor-server-jetty-jakarta.api @@ -40,8 +40,10 @@ public class io/ktor/server/jetty/jakarta/JettyApplicationEngineBase : io/ktor/s public final class io/ktor/server/jetty/jakarta/JettyApplicationEngineBase$Configuration : io/ktor/server/engine/BaseApplicationEngine$Configuration { public fun ()V public final fun getConfigureServer ()Lkotlin/jvm/functions/Function1; + public final fun getHttpConfiguration ()Lkotlin/jvm/functions/Function1; public final fun getIdleTimeout-UwyO8pc ()J public final fun setConfigureServer (Lkotlin/jvm/functions/Function1;)V + public final fun setHttpConfiguration (Lkotlin/jvm/functions/Function1;)V public final fun setIdleTimeout-LRDsOJo (J)V } diff --git a/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/JettyApplicationEngineBase.kt b/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/JettyApplicationEngineBase.kt index b30baf729c2..f3501a62db4 100644 --- a/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/JettyApplicationEngineBase.kt +++ b/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/JettyApplicationEngineBase.kt @@ -36,6 +36,12 @@ public open class JettyApplicationEngineBase( */ public var configureServer: Server.() -> Unit = {} + /** + * Property function that will be called during Jetty server initialization with the http configuration instance + * that is passed to the managed connectors as a receiver. + */ + public var httpConfiguration: HttpConfiguration.() -> Unit = {} + /** * The duration of time that a connection can be idle before the connector takes action to close the connection. */ @@ -48,8 +54,8 @@ public open class JettyApplicationEngineBase( * Jetty server instance being configuring and starting */ protected val server: Server = Server().apply { - configuration.configureServer(this) initializeServer(configuration) + configuration.configureServer(this) } override fun start(wait: Boolean): JettyApplicationEngineBase { diff --git a/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/ServerInitializer.kt b/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/ServerInitializer.kt index 9e0d7386171..7ea5b117317 100644 --- a/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/ServerInitializer.kt +++ b/ktor-server/ktor-server-jetty-jakarta/jvm/src/io/ktor/server/jetty/jakarta/ServerInitializer.kt @@ -23,7 +23,8 @@ internal fun Server.initializeServer( if (ktorConnector.type == ConnectorType.HTTPS) { addCustomizer(SecureRequestCustomizer()) } - } + + }.apply(configuration.httpConfiguration) var alpnAvailable = false var alpnConnectionFactory: ALPNServerConnectionFactory? diff --git a/ktor-server/ktor-server-jetty-jakarta/jvm/test/io/ktor/tests/server/jetty/jakarta/JettyEngineTest.kt b/ktor-server/ktor-server-jetty-jakarta/jvm/test/io/ktor/tests/server/jetty/jakarta/JettyEngineTest.kt index a836c2c423d..cbe9d722c20 100644 --- a/ktor-server/ktor-server-jetty-jakarta/jvm/test/io/ktor/tests/server/jetty/jakarta/JettyEngineTest.kt +++ b/ktor-server/ktor-server-jetty-jakarta/jvm/test/io/ktor/tests/server/jetty/jakarta/JettyEngineTest.kt @@ -43,14 +43,14 @@ class JettyHttpServerJvmTest : HttpServerJvmTestSuite + val response = client.get { + url("http://127.0.0.1:$serverPort/") + header("X-Custom-Large-Header-1", create6KBHeaderValue()) + header("X-Custom-Large-Header-2", create6KBHeaderValue()) + } + assertEquals(HttpStatusCode.NoContent, response.status) + } + } +} diff --git a/ktor-server/ktor-server-jetty/api/ktor-server-jetty.api b/ktor-server/ktor-server-jetty/api/ktor-server-jetty.api index 9bc78bb8ca3..62184e6bdb9 100644 --- a/ktor-server/ktor-server-jetty/api/ktor-server-jetty.api +++ b/ktor-server/ktor-server-jetty/api/ktor-server-jetty.api @@ -40,8 +40,10 @@ public class io/ktor/server/jetty/JettyApplicationEngineBase : io/ktor/server/en public final class io/ktor/server/jetty/JettyApplicationEngineBase$Configuration : io/ktor/server/engine/BaseApplicationEngine$Configuration { public fun ()V public final fun getConfigureServer ()Lkotlin/jvm/functions/Function1; + public final fun getHttpConfiguration ()Lkotlin/jvm/functions/Function1; public final fun getIdleTimeout-UwyO8pc ()J public final fun setConfigureServer (Lkotlin/jvm/functions/Function1;)V + public final fun setHttpConfiguration (Lkotlin/jvm/functions/Function1;)V public final fun setIdleTimeout-LRDsOJo (J)V } diff --git a/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/JettyApplicationEngineBase.kt b/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/JettyApplicationEngineBase.kt index c0cc5272cf4..3e14f4058a3 100644 --- a/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/JettyApplicationEngineBase.kt +++ b/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/JettyApplicationEngineBase.kt @@ -36,6 +36,12 @@ public open class JettyApplicationEngineBase( */ public var configureServer: Server.() -> Unit = {} + /** + * Property function that will be called during Jetty server initialization with the http configuration instance + * that is passed to the managed connectors as a receiver. + */ + public var httpConfiguration: HttpConfiguration.() -> Unit = {} + /** * The duration of time that a connection can be idle before the connector takes action to close the connection. */ diff --git a/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/ServerInitializer.kt b/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/ServerInitializer.kt index fdc036e7f0b..42213c24b53 100644 --- a/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/ServerInitializer.kt +++ b/ktor-server/ktor-server-jetty/jvm/src/io/ktor/server/jetty/ServerInitializer.kt @@ -23,7 +23,7 @@ internal fun Server.initializeServer( if (ktorConnector.type == ConnectorType.HTTPS) { addCustomizer(SecureRequestCustomizer()) } - } + }.apply(configuration.httpConfiguration) var alpnAvailable = false var alpnConnectionFactory: ALPNServerConnectionFactory? diff --git a/ktor-server/ktor-server-jetty/jvm/test/io/ktor/tests/server/jetty/JettyHttpConfigurationTest.kt b/ktor-server/ktor-server-jetty/jvm/test/io/ktor/tests/server/jetty/JettyHttpConfigurationTest.kt new file mode 100644 index 00000000000..de5d32f2c35 --- /dev/null +++ b/ktor-server/ktor-server-jetty/jvm/test/io/ktor/tests/server/jetty/JettyHttpConfigurationTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.tests.server.jetty + +import io.ktor.client.* +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.engine.* +import io.ktor.server.jetty.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import kotlinx.coroutines.test.* +import java.net.* +import kotlin.random.* +import kotlin.test.* + +class JettyHttpConfigurationTest { + + private fun findFreePort() = ServerSocket(0).use { it.localPort } + + @Test + fun `test HttpConfiguration with request header size example`() = runTest { + val serverPort = findFreePort() + + embeddedServer(Jetty, configure = { + httpConfiguration = { + requestHeaderSize = 16 * 1024 + } + connector { port = serverPort } + }) { + routing { + get("/") { + call.respond(HttpStatusCode.NoContent) + } + } + }.start(wait = false) + + val create6KBHeaderValue = { List(6 * 1024) { Random.nextInt(33, 127).toChar() }.joinToString("") } + + HttpClient().use { client -> + val response = client.get { + url("http://127.0.0.1:$serverPort/") + header("X-Custom-Large-Header-1", create6KBHeaderValue()) + header("X-Custom-Large-Header-2", create6KBHeaderValue()) + } + assertEquals(HttpStatusCode.NoContent, response.status) + } + } +}