Skip to content

Commit

Permalink
KTOR-6734 Upgrade Jetty to 12
Browse files Browse the repository at this point in the history
  • Loading branch information
bjhham committed Dec 30, 2024
1 parent 5f100bf commit 1500435
Show file tree
Hide file tree
Showing 20 changed files with 531 additions and 425 deletions.
10 changes: 5 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ netty = "4.1.116.Final"
netty-tcnative = "2.0.69.Final"

jetty = "9.4.56.v20240826"
jetty-jakarta = "11.0.24"
jetty-jakarta = "12.0.16"
jetty-alpn-api = "1.1.3.v20160715"
jetty-alpn-boot = "8.1.13.v20181017"
jetty-alpn-openjdk8 = "9.4.56.v20240826"
Expand Down Expand Up @@ -160,12 +160,12 @@ jetty-alpn-openjdk8-server = { module = "org.eclipse.jetty:jetty-alpn-openjdk8-s
jetty-alpn-openjdk8-client = { module = "org.eclipse.jetty:jetty-alpn-openjdk8-client", version.ref = "jetty-alpn-openjdk8" }

jetty-client-jakarta = { module = "org.eclipse.jetty:jetty-client", version.ref = "jetty-jakarta" }
jetty-http2-server-jakarta = { module = "org.eclipse.jetty.http2:http2-server", version.ref = "jetty-jakarta" }
jetty-http2-client-jakarta = { module = "org.eclipse.jetty.http2:http2-client", version.ref = "jetty-jakarta" }
jetty-http2-server-jakarta = { module = "org.eclipse.jetty.http2:jetty-http2-server", version.ref = "jetty-jakarta" }
jetty-http2-client-jakarta = { module = "org.eclipse.jetty.http2:jetty-http2-client", version.ref = "jetty-jakarta" }
jetty-http2-client-transport-jakarta = { module = "org.eclipse.jetty.http2:http2-http-client-transport", version.ref = "jetty-jakarta" }
jetty-server-jakarta = { module = "org.eclipse.jetty:jetty-server", version.ref = "jetty-jakarta" }
jetty-servlets-jakarta = { module = "org.eclipse.jetty:jetty-servlets", version.ref = "jetty-jakarta" }
jetty-servlet-jakarta = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "jetty-jakarta" }
jetty-servlets-jakarta = { module = "org.eclipse.jetty.ee10:jetty-ee10-servlets", version.ref = "jetty-jakarta" }
jetty-servlet-jakarta = { module = "org.eclipse.jetty.ee10:jetty-ee10-servlet", version.ref = "jetty-jakarta" }
jetty-alpn-server-jakarta = { module = "org.eclipse.jetty:jetty-alpn-server", version.ref = "jetty-jakarta" }
jetty-alpn-java-server-jakarta = { module = "org.eclipse.jetty:jetty-alpn-java-server", version.ref = "jetty-jakarta" }
jetty-alpn-java-client-jakarta = { module = "org.eclipse.jetty:jetty-alpn-java-client", version.ref = "jetty-jakarta" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ public final class io/ktor/client/engine/jetty/jakarta/JettyEngineConfig : io/kt
public fun <init> ()V
public final fun configureClient (Lkotlin/jvm/functions/Function1;)V
public final fun getClientCacheSize ()I
public final fun getSslContextFactory ()Lorg/eclipse/jetty/util/ssl/SslContextFactory;
public final fun getSslContextFactory ()Lorg/eclipse/jetty/util/ssl/SslContextFactory$Client;
public final fun setClientCacheSize (I)V
public final fun setSslContextFactory (Lorg/eclipse/jetty/util/ssl/SslContextFactory;)V
public final fun setSslContextFactory (Lorg/eclipse/jetty/util/ssl/SslContextFactory$Client;)V
}

public final class io/ktor/client/engine/jetty/jakarta/JettyEngineContainer : io/ktor/client/HttpClientEngineContainer {
Expand Down
3 changes: 1 addition & 2 deletions ktor-client/ktor-client-jetty-jakarta/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ kotlin.sourceSets {
api(project(":ktor-client:ktor-client-core"))

api(libs.jetty.http2.client.jakarta)
api(libs.jetty.alpn.openjdk8.client)
api(libs.jetty.alpn.java.client)
api(libs.jetty.alpn.java.client.jakarta)
}
}
commonTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
package io.ktor.client.engine.jetty.jakarta

import io.ktor.client.engine.*
import org.eclipse.jetty.http2.client.*
import org.eclipse.jetty.util.ssl.*
import org.eclipse.jetty.http2.client.HTTP2Client
import org.eclipse.jetty.util.ssl.SslContextFactory

/**
* A configuration for the [Jetty] client engine.
Expand All @@ -17,7 +17,7 @@ public class JettyEngineConfig : HttpClientEngineConfig() {
/**
* Allows you to configure [SSL](https://ktor.io/docs/client-ssl.html) settings for this engine.
*/
public var sslContextFactory: SslContextFactory = SslContextFactory.Client()
public var sslContextFactory: SslContextFactory.Client = SslContextFactory.Client()

/**
* Specifies the size of cache that keeps recently used [JettyHttp2Engine] instances.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import kotlinx.coroutines.*
import org.eclipse.jetty.http.*
import org.eclipse.jetty.http2.api.*
import org.eclipse.jetty.http2.client.*
import org.eclipse.jetty.http2.client.internal.HTTP2ClientSession
import org.eclipse.jetty.http2.frames.*
import org.eclipse.jetty.io.Transport
import org.eclipse.jetty.util.*
import java.net.*
import java.nio.*
Expand Down Expand Up @@ -58,12 +60,20 @@ internal suspend fun HttpRequestData.executeRequest(
)
}

private val NoopListener = object : Session.Listener {}

internal suspend fun HTTP2Client.connect(
url: Url,
config: JettyEngineConfig
): Session = withPromise { promise ->
val factory = if (url.protocol.isSecure()) config.sslContextFactory else null
connect(factory, InetSocketAddress(url.host, url.port), Session.Listener.Adapter(), promise)
): Session = withPromise { promise: Promise<Session> ->
connect(
Transport.TCP_IP,
config.sslContextFactory,
InetSocketAddress(url.host, url.port),
NoopListener,
promise,
mutableMapOf<String, Object>() as Map<String, Object>
)
}

@OptIn(InternalAPI::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,34 @@ package io.ktor.client.engine.jetty.jakarta

import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.http.HttpMethod
import io.ktor.utils.io.*
import kotlinx.coroutines.*
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import org.eclipse.jetty.http.*
import org.eclipse.jetty.http2.*
import org.eclipse.jetty.http2.api.*
import org.eclipse.jetty.http2.client.*
import org.eclipse.jetty.http2.frames.*
import org.eclipse.jetty.util.*
import java.io.*
import java.nio.*
import java.nio.channels.*
import kotlin.coroutines.*
import kotlinx.coroutines.launch
import org.eclipse.jetty.http.MetaData
import org.eclipse.jetty.http2.ErrorCode
import org.eclipse.jetty.http2.HTTP2Session
import org.eclipse.jetty.http2.api.Stream
import org.eclipse.jetty.http2.frames.HeadersFrame
import org.eclipse.jetty.http2.frames.PushPromiseFrame
import org.eclipse.jetty.http2.frames.ResetFrame
import org.eclipse.jetty.util.Callback
import org.eclipse.jetty.util.Promise
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.channels.ClosedChannelException
import java.util.concurrent.TimeoutException
import kotlin.coroutines.CoroutineContext

internal data class StatusWithHeaders(val statusCode: HttpStatusCode, val headers: Headers)

private data class JettyResponseChunk(val buffer: ByteBuffer, val callback: Callback)
private data class JettyResponseChunk(val buffer: ByteBuffer)

internal class JettyResponseListener(
private val request: HttpRequestData,
private val session: HTTP2ClientSession,
private val session: HTTP2Session,
private val channel: ByteWriteChannel,
private val callContext: CoroutineContext
) : Stream.Listener {
Expand All @@ -44,37 +50,46 @@ internal class JettyResponseListener(
return Ignore
}

override fun onIdleTimeout(stream: Stream, cause: Throwable): Boolean {
channel.close(cause)
return true
override fun onIdleTimeout(
stream: Stream?,
x: TimeoutException?,
promise: Promise<Boolean?>?
) {
channel.close(x)
}

override fun onReset(stream: Stream, frame: ResetFrame) {
val error = when (frame.error) {
0 -> null
ErrorCode.CANCEL_STREAM_ERROR.code -> ClosedChannelException()
else -> {
val code = ErrorCode.from(frame.error)
IOException("Connection reset ${code?.name ?: "with unknown error code ${frame.error}"}")
override fun onReset(
stream: Stream?,
frame: ResetFrame?,
callback: Callback?
) {
frame?.error?.let {
val error = when (frame.error) {
0 -> null
ErrorCode.CANCEL_STREAM_ERROR.code -> ClosedChannelException()
else -> {
val code = ErrorCode.from(frame.error)
IOException("Connection reset ${code?.name ?: "with unknown error code ${frame.error}"}")
}
}
}

error?.let { backendChannel.close(it) }
error?.let { backendChannel.close(it) }
}

onHeadersReceived.complete(null)
}

override fun onData(stream: Stream, frame: DataFrame, callback: Callback) {
val data = frame.data!!
override fun onDataAvailable(stream: Stream?) {
val streamData = stream?.readData() ?: return
val frame = streamData.frame()
try {
if (!backendChannel.trySend(JettyResponseChunk(data, callback)).isSuccess) {
if (!backendChannel.trySend(JettyResponseChunk(frame.byteBuffer)).isSuccess) {
throw IOException("backendChannel.offer() failed")
}

if (frame.isEndStream) backendChannel.close()
} catch (cause: Throwable) {
backendChannel.close(cause)
callback.failed(cause)
}
}

Expand All @@ -93,7 +108,7 @@ internal class JettyResponseListener(
}

override fun onHeaders(stream: Stream, frame: HeadersFrame) {
frame.metaData.fields.forEach { field ->
frame.metaData.httpFields.forEach { field ->
headersBuilder.append(field.name, field.value)
}

Expand All @@ -117,31 +132,23 @@ internal class JettyResponseListener(
@OptIn(DelicateCoroutinesApi::class)
private fun runResponseProcessing() = GlobalScope.launch(callContext) {
while (true) {
val (buffer, callback) = backendChannel.receiveCatching().getOrNull() ?: break
val (buffer) = backendChannel.receiveCatching().getOrNull() ?: break
try {
if (buffer.remaining() > 0) channel.writeFully(buffer)
callback.succeeded()
} catch (cause: ClosedWriteChannelException) {
callback.failed(cause)
session.endPoint.close()
break
} catch (cause: Throwable) {
callback.failed(cause)
session.endPoint.close()
throw cause
}
}
}.invokeOnCompletion { cause ->
channel.close(cause)
backendChannel.close()
GlobalScope.launch {
for ((_, callback) in backendChannel) {
callback.succeeded()
}
}
}

companion object {
private val Ignore = Stream.Listener.Adapter()
private val Ignore = object : Stream.Listener {}
}
}
8 changes: 8 additions & 0 deletions ktor-server/ktor-server-jetty-jakarta/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions ktor-server/ktor-server-jetty-jakarta/.idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions ktor-server/ktor-server-jetty-jakarta/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions ktor-server/ktor-server-jetty-jakarta/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ public final class io/ktor/server/jetty/jakarta/Jetty : io/ktor/server/engine/Ap
public fun create (Lio/ktor/server/application/ApplicationEnvironment;Lio/ktor/events/Events;ZLio/ktor/server/jetty/jakarta/JettyApplicationEngineBase$Configuration;Lkotlin/jvm/functions/Function0;)Lio/ktor/server/jetty/jakarta/JettyApplicationEngine;
}

public final class io/ktor/server/jetty/jakarta/JettyApplicationCall : io/ktor/server/servlet/jakarta/AsyncServletApplicationCall {
public fun <init> (Lio/ktor/server/application/Application;Lorg/eclipse/jetty/server/Request;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext;)V
public final class io/ktor/server/jetty/jakarta/JettyApplicationCall : io/ktor/server/engine/BaseApplicationCall {
public fun <init> (Lio/ktor/server/application/Application;Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lkotlin/coroutines/CoroutineContext;)V
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public synthetic fun getRequest ()Lio/ktor/server/engine/BaseApplicationRequest;
public fun getRequest ()Lio/ktor/server/jetty/jakarta/JettyApplicationRequest;
public synthetic fun getRequest ()Lio/ktor/server/request/ApplicationRequest;
public synthetic fun getRequest ()Lio/ktor/server/request/PipelineRequest;
public synthetic fun getResponse ()Lio/ktor/server/engine/BaseApplicationResponse;
public fun getResponse ()Lio/ktor/server/jetty/jakarta/JettyApplicationResponse;
public synthetic fun getResponse ()Lio/ktor/server/response/ApplicationResponse;
public synthetic fun getResponse ()Lio/ktor/server/response/PipelineResponse;
public synthetic fun getResponse ()Lio/ktor/server/servlet/jakarta/ServletApplicationResponse;
}

public final class io/ktor/server/jetty/jakarta/JettyApplicationEngine : io/ktor/server/jetty/jakarta/JettyApplicationEngineBase {
Expand Down Expand Up @@ -45,13 +49,61 @@ public final class io/ktor/server/jetty/jakarta/JettyApplicationEngineBase$Confi
public final fun setIdleTimeout-LRDsOJo (J)V
}

public final class io/ktor/server/jetty/jakarta/JettyApplicationResponse : io/ktor/server/servlet/jakarta/AsyncServletApplicationResponse {
public fun <init> (Lio/ktor/server/servlet/jakarta/AsyncServletApplicationCall;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext;Lorg/eclipse/jetty/server/Request;Lkotlin/coroutines/CoroutineContext;)V
public fun push (Lio/ktor/server/response/ResponsePushBuilder;)V
public final class io/ktor/server/jetty/jakarta/JettyApplicationRequest : io/ktor/server/engine/BaseApplicationRequest {
public fun <init> (Lio/ktor/server/application/PipelineCall;Lorg/eclipse/jetty/server/Request;)V
public fun getCookies ()Lio/ktor/server/request/RequestCookies;
public fun getLocal ()Lio/ktor/http/RequestConnectionPoint;
public fun getQueryParameters ()Lio/ktor/http/Parameters;
public fun getRawQueryParameters ()Lio/ktor/http/Parameters;
}

public final class io/ktor/server/jetty/jakarta/internal/JettyUpgradeImpl : io/ktor/server/servlet/jakarta/ServletUpgrade {
public static final field INSTANCE Lio/ktor/server/jetty/jakarta/internal/JettyUpgradeImpl;
public fun performUpgrade (Lio/ktor/http/content/OutgoingContent$ProtocolUpgrade;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final class io/ktor/server/jetty/jakarta/JettyApplicationResponse : io/ktor/server/engine/BaseApplicationResponse, kotlinx/coroutines/CoroutineScope {
public fun <init> (Lio/ktor/server/application/PipelineCall;Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lkotlin/coroutines/CoroutineContext;)V
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public fun getHeaders ()Lio/ktor/server/response/ResponseHeaders;
}

public final class io/ktor/server/jetty/jakarta/JettyConnectionPoint : io/ktor/http/RequestConnectionPoint {
public fun <init> (Lorg/eclipse/jetty/server/Request;)V
public fun getHost ()Ljava/lang/String;
public fun getLocalAddress ()Ljava/lang/String;
public fun getLocalHost ()Ljava/lang/String;
public fun getLocalPort ()I
public fun getMethod ()Lio/ktor/http/HttpMethod;
public fun getPort ()I
public fun getRemoteAddress ()Ljava/lang/String;
public fun getRemoteHost ()Ljava/lang/String;
public fun getRemotePort ()I
public fun getScheme ()Ljava/lang/String;
public fun getServerHost ()Ljava/lang/String;
public fun getServerPort ()I
public fun getUri ()Ljava/lang/String;
public fun getVersion ()Ljava/lang/String;
}

public final class io/ktor/server/jetty/jakarta/JettyHeaders : io/ktor/http/Headers {
public fun <init> (Lorg/eclipse/jetty/server/Request;)V
public fun contains (Ljava/lang/String;)Z
public fun contains (Ljava/lang/String;Ljava/lang/String;)Z
public fun entries ()Ljava/util/Set;
public fun forEach (Lkotlin/jvm/functions/Function2;)V
public fun get (Ljava/lang/String;)Ljava/lang/String;
public fun getAll (Ljava/lang/String;)Ljava/util/List;
public fun getCaseInsensitiveName ()Z
public fun isEmpty ()Z
public fun names ()Ljava/util/Set;
}

public final class io/ktor/server/jetty/jakarta/JettyRequestCookies : io/ktor/server/request/RequestCookies {
public fun <init> (Lio/ktor/server/jetty/jakarta/JettyApplicationRequest;Lorg/eclipse/jetty/server/Request;)V
}

public final class io/ktor/server/jetty/jakarta/JettyWebsocketConnection : org/eclipse/jetty/io/AbstractConnection, kotlinx/coroutines/CoroutineScope, org/eclipse/jetty/io/Connection$UpgradeTo {
public fun <init> (Lorg/eclipse/jetty/io/EndPoint;Lkotlin/coroutines/CoroutineContext;)V
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public final fun getInputChannel ()Lio/ktor/utils/io/ByteChannel;
public final fun getOutputChannel ()Lio/ktor/utils/io/ByteChannel;
public fun onFillable ()V
public fun onUpgradeTo (Ljava/nio/ByteBuffer;)V
}

1 change: 1 addition & 0 deletions ktor-server/ktor-server-jetty-jakarta/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ kotlin {
api(project(":ktor-server:ktor-server-servlet-jakarta"))
api(libs.jetty.server.jakarta)
api(libs.jetty.servlets.jakarta)
api(libs.jakarta.servlet)
api(libs.jetty.alpn.server.jakarta)
api(libs.jetty.alpn.java.server.jakarta)
api(libs.jetty.alpn.openjdk8.server)
Expand Down
Loading

0 comments on commit 1500435

Please sign in to comment.