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

Improve Jetty connection configuration by exposing a builder on Jetty's HttpConfiguration object #4387

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 <init> ()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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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)
Comment on lines 57 to +58
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Porting this change here #4367 to Jetty Jarkarta (I did not notice this update in the first instance)

}

override fun start(wait: Boolean): JettyApplicationEngineBase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ internal fun Server.initializeServer(
if (ktorConnector.type == ConnectorType.HTTPS) {
addCustomizer(SecureRequestCustomizer())
}
}

}.apply(configuration.httpConfiguration)

var alpnAvailable = false
var alpnConnectionFactory: ALPNServerConnectionFactory?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ class JettyHttpServerJvmTest : HttpServerJvmTestSuite<JettyApplicationEngine, Je
@Test
fun testServletAttributes() = runTest {
createAndStartServer {
get("/tomcat/attributes") {
get("/jetty/attributes") {
call.respondText(
call.request.servletRequestAttributes["ktor.test.attribute"]?.toString() ?: "Not found"
)
}
}

withUrl("/tomcat/attributes", {}) {
withUrl("/jetty/attributes", {}) {
assertEquals("135", call.response.bodyAsText())
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.jakarta

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.server.engine.*
import io.ktor.server.jetty.jakarta.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.coroutines.test.*
import org.junit.jupiter.params.*
import org.junit.jupiter.params.provider.*
import java.net.*
import kotlin.random.*
import kotlin.test.*

class JettyHttpConfigurationTest {

private fun findFreePort() = ServerSocket(0).use { it.localPort }

companion object {

@JvmStatic
fun testCases() = listOf(
Arguments.of(8, 6, HttpStatusCode.RequestHeaderFieldTooLarge),
Arguments.of(16, 6, HttpStatusCode.NoContent),
Arguments.of(16, 8, HttpStatusCode.RequestHeaderFieldTooLarge)
)
}

@ParameterizedTest
@MethodSource("testCases")
fun `test HttpConfiguration with request header size example`(
requestHeaderSizeConfigKB: Int,
headerSizeKB: Int,
expectedStatusCode: HttpStatusCode
) = runTest {
val serverPort = findFreePort()

embeddedServer(Jetty, configure = {
httpConfiguration = {
requestHeaderSize = requestHeaderSizeConfigKB * 1024
}
connector { port = serverPort }
}) {
routing {
get("/") {
call.respond(HttpStatusCode.NoContent)
}
}
}.start(wait = false)

val createHeaderValue = { List(headerSizeKB * 1024) { Random.nextInt(33, 127).toChar() }.joinToString("") }

val response = HttpClient().use { client ->
client.get {
url("http://127.0.0.1:$serverPort/")
header("X-Custom-Large-Header-1", createHeaderValue())
header("X-Custom-Large-Header-2", createHeaderValue())
}
}

assertEquals(expectedStatusCode, response.status)
}
}
2 changes: 2 additions & 0 deletions ktor-server/ktor-server-jetty/api/ktor-server-jetty.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 <init> ()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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal fun Server.initializeServer(
if (ktorConnector.type == ConnectorType.HTTPS) {
addCustomizer(SecureRequestCustomizer())
}
}
}.apply(configuration.httpConfiguration)

var alpnAvailable = false
var alpnConnectionFactory: ALPNServerConnectionFactory?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 org.junit.jupiter.params.*
import org.junit.jupiter.params.provider.*
import java.net.*
import kotlin.random.*
import kotlin.test.*

class JettyHttpConfigurationTest {

private fun findFreePort() = ServerSocket(0).use { it.localPort }

companion object {

@JvmStatic
fun testCases() = listOf(
Arguments.of(8, 6, HttpStatusCode.RequestHeaderFieldTooLarge),
Arguments.of(16, 6, HttpStatusCode.NoContent),
Arguments.of(16, 8, HttpStatusCode.RequestHeaderFieldTooLarge)
)
}

@ParameterizedTest
@MethodSource("testCases")
fun `test HttpConfiguration with request header size example`(
requestHeaderSizeConfigKB: Int,
headerSizeKB: Int,
expectedStatusCode: HttpStatusCode
) = runTest {
val serverPort = findFreePort()

embeddedServer(Jetty, configure = {
httpConfiguration = {
requestHeaderSize = requestHeaderSizeConfigKB * 1024
}
connector { port = serverPort }
}) {
routing {
get("/") {
call.respond(HttpStatusCode.NoContent)
}
}
}.start(wait = false)

val createHeaderValue = { List(headerSizeKB * 1024) { Random.nextInt(33, 127).toChar() }.joinToString("") }

val response = HttpClient().use { client ->
client.get {
url("http://127.0.0.1:$serverPort/")
header("X-Custom-Large-Header-1", createHeaderValue())
header("X-Custom-Large-Header-2", createHeaderValue())
}
}

assertEquals(expectedStatusCode, response.status)
}
}