Kotlin logo

Kotlin

A concise multiplatform language developed by JetBrains

Ktor News

Ktor 3.0 Is Now Available With New Features and Improved Performance

Read this post in other languages:

This new version uses Kotlin 2.0 and switches to kotlinx-io, making Ktor more up to date and better connected with other Kotlin tools. Ktor 3.0 works faster and gives you more options for building client-server applications.

If you’re new to Ktor or need a reminder about how it works, our recent blog post, Ktor 101: Efficient JVM HTTP Toolkit, explains its main features.

The following post will give you a solid overview of what’s new and improved in Ktor 3.0.

Build now with Ktor 3.0!

Migrating to kotlinx-io

The biggest change in this new version is the switch to the kotlinx-io library, which is based on Okio. This change may affect you if you use Ktor’s low-level IO APIs. We made this change to standardize IO functionality across Kotlin libraries and improve performance, as shown in our benchmarks.

Breaking changes

The changes mainly affect low-level IO APIs, including classes like Input, Output, ByteReadChannel, and ByteWriteChannel. If you use these classes directly, you’ll see deprecation warnings. You should update your code to use the alternatives provided by the kotlinx-io library. Don’t worry – we’ll keep supporting the old APIs until version 4.0, giving you ample time to migrate.

The new kotlinx-io library offers a multiplatform API that can handle various data sources and provides a range of capabilities, including working with files, using compression, and more. For more details, check out the kotlinx-io API documentation.

Performance improvements

By switching to kotlinx-io, we’ve cut down on the unnecessary copying of bytes between ByteReadChannel, ByteWriteChannel, and network interfaces. This allows for more efficient byte transformations and parsing, making room for future performance improvements.

Our IO benchmark tests, based on real-world Ktor applications, show impressive results. Some tests saw over 90% improvement, and we’re working on even more enhancements.

Support for server-sent events (SSE)

In Ktor 3.0, we added the initial support for server-sent events (SSE) both for the server and the client.

Server-sent events is a technology that enables servers to push events to clients over an HTTP connection. SSE provides a one-way communication channel from the server to the client. This approach is useful for scenarios where the server needs to send event-based updates without requiring the client to repeatedly poll for new information.

To implement SSE support in the Ktor application, first, add the SSE dependency to your project’s build script:

implementation("io.ktor:ktor-server-sse-jvm")

Next, install the SSE plugin in your Ktor application, and create an endpoint using the sse{} function:

import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.sse.*
import io.ktor.sse.*
import kotlinx.coroutines.delay

fun main() {
   embeddedServer(Netty, port = 8080) {
       install(SSE)
       routing {
           sse {
               repeat(42) {
                   val name = call.parameters["name"] ?: "World"
                   send(ServerSentEvent(data = "Hello, $name! $it"))
                   delay(1000)
               }
               close()
           }
       }
   }.start(wait = true)
}

Within the sse {...} block, you have access to a ServerSSESession instance with the following capabilities.

  • send(): Create and send a ServerSentEvent to the client.
  • call: Access the associated ApplicationCall that initiated the session.
  • close(): End the session and terminate the connection with the client.

Please note that currently, Ktor does not provide support for data compression of SSE responses. If you use the Compression plugin, it will skip compression for SSE responses by default.

To consume the events using the Ktor client, in the simplest case, you only need to invoke the sse function on the client instance as follows:

import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.sse.*
import kotlinx.coroutines.runBlocking

fun main() {
   val client = HttpClient(CIO) {
       install(SSE)
   }

   runBlocking {
       client.sse(host = "127.0.0.1", port = 8080, path = "/sse") {
           incoming.collect { event -> println(event) }
       }
   }
}

In the example above, the client will consume the events as long as the server is sending the data.

Serving static resources from ZIP archives

The new function, staticZip, allows users to serve the contents of a ZIP archive as static content.

Here’s a basic example of how to use it:

routing {
   staticZip(
       remotePath = "/",
       basePath = "base", 
       Path("files/text-files.zip")
   ) {
       default("file.txt")

       //modify the response by adding the HTTP Etag header
       modify { path, call ->
           call.response.headers.append(HttpHeaders.ETag,
        path.fileName.toString())
       }
   }
}

Let’s break down the example into the key components:

  1. remotePath – the base URL path where the ZIP contents will be accessible.
  2. basePath – the base path within the ZIP file you wish to serve. In our example, we assume that the ZIP archive contains the base directory. All paths inside the specified basePath will be accessible recursively at "remotePath/path/to/resource". This means you can organize your ZIP file with subfolders, and they’ll be reflected in the URL structure.
  3. Path("files/text-files.zip")– the path to the ZIP file you want to serve.
  4. The default() function – this allows you to specify a default file to serve if no specific file is requested.
  5. The modify block – this enables you to customize the response. In this example, we’re adding an ETag header based on the file name.

See the full example on the Ktor samples repository on GitHub.

Support for CSRF

Support for CSRF (Cross-Site Request Forgery) protection was added with the new plugin. Protection is enabled for state-changing operations including POST, PUT, and DELETE.

Generally, it’s applicable only for projects that use session cookies and forms, since enabling this can unnecessarily complicate your application.

To enable CSRF support in the Ktor application, first, add the following dependency to your project’s build script:

implementation("io.ktor:ktor-server-csrf-jvm")

Next, you can enable the CSRF protection for the selected routes in the application as follows:

route("/csrf") {
   install(CSRF) {
       allowOrigin("https://localhost:8080")

       originMatchesHost()

       checkHeader("X-CSRF") { csrfHeader ->
           request.headers[HttpHeaders.Origin]?.let { origin ->
               csrfHeader == origin.hashCode().toString(32) // 1ndrgg9
           } == true
       }

       onFailure {
           respondText("Access denied!", status = HttpStatusCode.Forbidden)
       }
   }
   post {
       call.respondText("CSRF check was successful")
   }
}

In the configuration block, the plugin provides a few methods for verifying the requests:

  • allowOrigin specifies that only the requests from the predefined origin are allowed. In our example, it’s https://localhost:8080.
  • originMatchesHost enforces that the request’s origin must match the application’s host.
  • checkHeader enables arbitrary header validation.

If you run the application with the code above and send a POST request to the /csrf endpoint using the curl command, you will see the following:

curl -X POST -H "Content-Type: application/json" --data '{}' http://localhost:8080/csrf
Access denied!

Adding the required headers will make it pass the checks:

curl -X POST -H "X-CSRF: 1ndrgg9" -H "Origin: http://localhost:8080" -H "Content-Type: application/json" --data '{}' http://localhost:8080/csrf
CSRF check passed!

Ktor client for Wasm

The Ktor client now supports WebAssembly (Wasm) as a build target. While Kotlin/Wasm is still in its early stages (Alpha), adding Wasm support to the Ktor client is an important step in expanding the Kotlin Multiplatform ecosystem. To showcase this new capability, we’ve created a sample Compose Multiplatform project that uses the Ktor client with a WebAssembly (wasmJs) target.

To use the Ktor client, you need to add the corresponding dependency to your project’s build script:

implementation("io.ktor:ktor-client-core:$ktor_version")

Migration Guide

Ktor 3.0 brings some significant changes. We have prepared a migration guide for you to make the transition easier. Here, we highlight the most notable changes.

Whether you upgrade your existing projects to Ktor 3.0 or not, you can start building your new projects with Ktor 3.0 right away!

Start a new project with Ktor 3.0

Explicit loading of modules in TestApplication

From now on, the TestApplication class requires explicit loading of modules. The following test will start an empty application and will not load any modules:

 @Test
 fun testRoot() = testApplication {// TestApplication scope
   client.get("/").apply {
     assertEquals(HttpStatusCode.OK, status)
     assertEquals("Hello World!", bodyAsText())
   }
 }

Instead, you must explicitly load your modules within the testApplication function or load the configuration file manually.

@Test
 fun testRoot() = testApplication {
   application {
     configureRouting()
   }
   client.get("/").apply {
     assertEquals(HttpStatusCode.OK, status)
     assertEquals("Hello World!", bodyAsText())
   }
 }

Plugin updates

The CallLogging plugin package has been renamed, fixing a typo:

The Locations plugin has been replaced with the new Resources plugin for type-safe routing.

These changes aim to improve Ktor’s architecture, security, and developer experience. Users upgrading from Ktor 2.x should refer to the detailed migration guide to ensure a smooth transition to the new version.

What else to read and watch

  1. 📰 The Ktor Roadmap for 2024
  2. 📰 The Ktor Plugin Registry Has Launched!
  3. 📰 Ktor 101: Efficient JVM HTTP Toolkit
  4. 🎥 Managing Complexity With Ktor
  5. 🎥 Extending Ktor: Unleash the Full Potential of Kotlin’s Web Framework

Your Ktor team
JetBrains
The Drive to Develop

image description