Skip to content

Commit

Permalink
Improve Jetty connection configuration by exposing a builder on the H…
Browse files Browse the repository at this point in the history
…ttpConfiguration object
  • Loading branch information
amccague committed Oct 9, 2024
1 parent c2715b5 commit a9dbb73
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 5 deletions.
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)
}

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)
}
}

0 comments on commit a9dbb73

Please sign in to comment.