Ktor
Building Asynchronous Servers and Clients in Kotlin
Ktor 2.0 Released
We’re extremely excited to announce the release of Ktor 2.0. We have been working on this version for approximately one year and happy to finally get it out the door!
Ktor 2.0 introduces many new features, but a major version which means that there are breaking changes, it has allowed us the opportunity to perform some maintenance work and get rid of legacy decisions. Don’t worry though – despite the breaking changes, we’ve tried our best to minimize these and also aid with some tooling that hopefully can help with automated migrations. More on that later though, first let’s take a look at some of the new functionality
Ktor Server
Simplified Extensibility
We’ve always built Ktor in mind for extensibility. Initially called Features, and now renamed to Plugins, all of the features (now you see why we renamed it) that Ktor offers are built using plugin architecture. The problem we encountered though was that for some folks it was hard to understand the architecture model behind it. In 2.0 we drastically simplified the extensibility API making the creation of plugins significantly easier.
Take a look at the following API code from 1.x
companion object Feature : ApplicationFeature<ApplicationCallPipeline, CustomHeader.Configuration, CustomHeader> {
override val key = AttributeKey<CustomHeader>("CustomPlugin")
override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): CustomHeader {
val configuration = Configuration().apply(configure)
val feature = CustomHeader(configuration)
pipeline.intercept(ApplicationCallPipeline.Call) {
feature.intercept(this)
}
return feature
}
}
The equivalent in 2.0 is
val myCustomPlugin = createApplicationPlugin("CustomPlugin") {
onCall {
}
onCallReceive {
}
onCallRespond {
}
}
Most of the existing plugins have been converted to use the new API and we’re pretty sure that the majority of scenarios can be covered. For more details, checkout the conversion for the `CustomHeader` plugin from the old to the new API, as well as the documentation for plugin development.
We have a lot more planned when it comes to extensibility, including facilities to easily publish and consume plugins from a marketplace!
Native support
On the server side, we now support Kotlin/Native in addition to GraalVM (which was already supported from 1.6), meaning that you have two options when it comes to self-contained server applications.
In regards to Kotin/Native support, currently it is limited to using CIO as an engine and we are continuing to work in areas of performance. We do recommend however using the new Kotlin/Native memory model.
Other server improvements
We also have a series of smaller improvements for Ktor server including random port support
fun main() {
embeddedServer(Netty, port = 0) {
configureRouting()
}.start(wait = true)
}
as well as improved testing API, type-safe routing, XML Serialization, subroutes for plugins, and 60+ bug fixes and other features.
Ktor Client
Simplified APIs
Continuing with the simplification of APIs, in the case of Ktor client, we’ve introduced new APIs to deal with common HTTP requests
val result = client.post("http://127.0.0.1:$port/") {
}
result.bodyAsText()
We’ve gotten rid of the generic post<T>, get<T> methods. Everything now returns an `HttpResponse` where you can access the body (with `bodyAsText`, `bodyAsChannel`) as well as the headers.
Retries
We now have built-in support for retries, including adjustment of time between retries
val client = HttpClient(CIO) {
install(HttpRequestRetry) {
maxRetries = 5
retryIf { request, response ->
!response.status.isSuccess()
}
retryOnExceptionIf { _, cause ->
cause is NetworkError
}
delayMillis { retry ->
retry * 3000L
} // retries in 3, 6, 9, etc. seconds
}
}
Content Negotiation
If you’ve been using the content negotiation feature in the server, you’re probably already familiar with what it is and how it works. Essentially it’s the ability for a client and server to negotiate the different types of content that can be requested and served. The negotiation aspect of it was only available up to now for the server. We’ve now brought this to the client too!
val client = HttpClient(CIO) {
install(ContentNegotiation) {
}
}
This plugin effectively replaced the `JsonFeature`.
Other client improvements
In addition to the above, the client also includes shortcut APIs for authentication such as `basic()` and `bearer()` helper functions, as well as interceptors at request level, a new metrics plugin, XML serialization, and many bug fixes and other features.
Migrating to Ktor 2.0
An issue we’ve had with Ktor has been the inconsistency of module and package names. Some had the prefix server, while others did not. Some worked for server and client, while not having any prefix whatsoever. In addition, we had multiple plugins in a single module, making discovery cumbersome.
The change to a major version has provided us the opportunity to introduce some consistent naming conventions when it comes to modules and packages. Moving forward, anything that is only server, will have the server prefix. Anything that is client, will have the client prefix. Common packages will have the no prefix. In addition, plugins are now contained in their own modules.
import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.content.* import io.ktor.util.* import io.ktor.utils.io.*
And the same goes for module names
implementation "io.ktor:ktor-server-auth:$ktor_version" implementation "io.ktor:ktor-server-default-headers:$ktor_version" implementation "io.ktor:ktor-server-sessions:$ktor_version" implementation "io.ktor:ktor-server-status-pages:$ktor_version" implementation "io.ktor:ktor-server-cors:$ktor_version" implementation "io.ktor:ktor-server-conditional-headers:$ktor_version"
To help with migration, we’re providing folks not only mapping documentation but also a migration tool with IntelliJ IDEA which attempts to migrate your Ktor project automatically (please note that Kotlin Multiplatform Projects are currently not supported).

Kotlin 1.6.20 support and up to date documentation
For folks using the latest release of Kotlin, know that Ktor 2.0 is compatible with 1.6.20. We’ve also continued to work on the documentation for Ktor, bringing it up to speed with all the changes. Moving forward we’re working on revamping the API documentation also.
For a complete list of everything in 2.0, check out the change log. And create your first Ktor 2.0 today!
Discover more
Ktor 2.0 릴리스
Ktor 2.0이 출시되었다는 기쁜 소식을 전해 드립니다. 약 1년의 작업 끝에 마침내 결실을 맺었군요!
Ktor 2.0에는 많은 새로운 기능이 도입되었지만, 주요 버전인 만큼 획기적인 변경 사항이 적용되었고, 일부 유지 관리 작업과 더불어 기존에 잘못된 결정을 철회하는 기회로도 삼았습니다. 대대적인 변경이 있었지만 이를 최소화하려고 노력했고 자동화된 마이그레이션에 도움이 될 수 있는 일부 지원 도구도 제공되니 염려하지 마세요. 먼저 몇 가지 새로운 기능을 살펴보고 그 밖의 내용은 나중에 자세히 알아보도록 하겠습니다.
Ktor 서버
단순화된 확장성
Ktor를 빌드할 때는 항상 확장성을 염두에 둡니다. 처음에는 기능이라고 불렀고 지금은 플러그인으로 이름이 바뀌었지만, Ktor가 제공하는 모든 기능은 플러그인 아키텍처를 사용하여 빌드됩니다(이름이 바뀐 이유가 납득되시죠?). 이때 제기된 문제는 어떤 사람들에게는 그 이면에 있는 아키텍처 모델이 난해하다는 것이었습니다. Ktor 2.0에서는 확장성 API를 대폭 간소화하여 플러그인을 훨씬 쉽게 만들 수 있도록 했습니다.
1.x의 다음 API 코드를 살펴보세요.
companion object Feature : ApplicationFeature<ApplicationCallPipeline, CustomHeader.Configuration, CustomHeader> {
override val key = AttributeKey<CustomHeader>("CustomPlugin")
override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): CustomHeader {
val configuration = Configuration().apply(configure)
val feature = CustomHeader(configuration)
pipeline.intercept(ApplicationCallPipeline.Call) {
feature.intercept(this)
}
return feature
}
}
2.0에서는 다음과 같이 바뀌었습니다.
val myCustomPlugin = createApplicationPlugin("CustomPlugin") {
onCall {
}
onCallReceive {
}
onCallRespond {
}
}
기존 플러그인의 대부분은 새 API를 사용하도록 변환되었으며 대부분의 상황을 포괄할 수 있을 것으로 확신합니다. 자세한 내용은 이전 API에서 새 API로의 ‘CustomHeader’ 플러그인 변환과 플러그인 개발 문서를 확인하세요.
향후 마켓플레이스에서 플러그인을 쉽게 게시하고 사용할 수 있는 기능 등 확장성과 관련하여 훨씬 더 많은 것이 준비될 예정입니다.
Native 지원
이제 서버 측에서 GraalVM(이미 1.6부터 지원됨) 외에 Kotlin/Native를 지원하여, 독립형 서버 애플리케이션과 관련하여 두 가지 옵션이 제공됩니다.
현재 Kotin/Native 지원은 CIO를 엔진으로 사용하는 것으로 제한되어 있으며 성능 개선을 위한 노력을 계속하고 있습니다. 단, 새로운 Kotlin/Native 메모리 모델을 사용할 것을 강력히 권장합니다.
기타 서버 개선 사항
또한 무작위 포트 지원을 포함하여 Ktor 서버와 관련한 일련의 부수적인 개선이 이루어졌습니다.
fun main() {
embeddedServer(Netty, port = 0) {
configureRouting()
}.start(wait = true)
}
뿐만 아니라 테스트 API 개선, Type-safe 라우팅, XML 직렬화, 플러그인용 하위 경로, 60개 이상의 버그 수정 및 기타 기능이 도입되었습니다.
Ktor 클라이언트
간소화된 API
API의 단순화 작업을 계속하여, Ktor 클라이언트의 경우 일반적인 HTTP 요청을 처리하는 새로운 API를 도입했습니다.
val result = client.post("http://127.0.0.1:$port/") {
}
result.bodyAsText()
제네릭 post<T>, get<T> 메서드를 없앴습니다. 이제 본문(‘bodyAsText’, ‘bodyAsChannel’ 포함)뿐만 아니라 헤더에도 액세스할 수 있는 ‘HttpResponse’를 모두 반환합니다.
재시도
이제 재시도 사이의 시간 조정을 포함하여 재시도에 대한 지원이 기본 제공됩니다.
val client = HttpClient(CIO) {
install(HttpRequestRetry) {
maxRetries = 5
retryIf { request, response ->
!response.status.isSuccess()
}
retryOnExceptionIf { _, cause ->
cause is NetworkError
}
delayMillis { retry ->
retry * 3000L
} // retries in 3, 6, 9, etc. seconds
}
}
콘텐츠 협상
서버에서 콘텐츠 협상 기능을 사용해 왔다면 이것이 무엇이며 어떻게 작동하는지 이미 잘 알고 있을 것입니다. 이는 기본적으로 클라이언트와 서버가 요청하고 제공할 수 있는 다양한 유형의 콘텐츠를 협상하는 기능입니다. 이와 관련된 협상 측면은 지금까지 서버에서만 사용할 수 있었지만 이제 클라이언트에서도 가능해졌습니다!
val client = HttpClient(CIO) {
install(ContentNegotiation) {
}
}
이 플러그인은 ‘JsonFeature’를 효과적으로 대체했습니다.
기타 클라이언트 개선 사항
위의 내용 외에도 클라이언트에는 ‘basic()’ 및 ‘bearer()’ 헬퍼 기능과 같은 인증을 위한 바로가기 API뿐만 아니라 요청 수준의 인터셉터, 새로운 메트릭 플러그인, XML 직렬화 및 많은 버그 수정과 기타 기능도 포함되어 있습니다.
Ktor 2.0으로 마이그레이션
Ktor와 관련된 한 가지 문제는 모듈과 패키지 이름이 일치하지 않는다는 것이었습니다. 일부에는 접두사 server가 있었고 일부에는 없었습니다. 일부는 서버와 클라이언트에서 작동했지만 접두사는 전혀 없었습니다. 또한 단일 모듈에 여러 플러그인이 있어서 검색이 번거로웠습니다.
하지만 주요 버전으로 변경하면서 모듈 및 패키지와 관련하여 일관된 명명 규칙을 도입했습니다. 앞으로는 서버에만 관련된 모든 항목에 server 접두사가 붙게 됩니다. 클라이언트의 경우에는 client 접두사가 붙습니다. 일반 패키지에는 접두사가 붙지 않습니다. 또한 플러그인은 이제 자체 모듈에 포함됩니다.
import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.content.* import io.ktor.util.* import io.ktor.utils.io.*
모듈 이름도 마찬가지입니다.
implementation "io.ktor:ktor-server-auth:$ktor_version" implementation "io.ktor:ktor-server-default-headers:$ktor_version" implementation "io.ktor:ktor-server-sessions:$ktor_version" implementation "io.ktor:ktor-server-status-pages:$ktor_version" implementation "io.ktor:ktor-server-cors:$ktor_version" implementation "io.ktor:ktor-server-conditional-headers:$ktor_version"
마이그레이션을 돕기 위해 매핑 문서뿐만 아니라 IntelliJ IDEA와 함께 Ktor 프로젝트의 자동 마이그레이션을 시도하는 마이그레이션 도구도 제공합니다(단, Kotlin Multiplatform 프로젝트는 현재 지원되지 않음).

Kotlin 1.6.20 지원 및 최신 문서
Kotlin의 최신 릴리스를 사용하는 분들은 Ktor 2.0이 1.6.20과 호환된다는 점을 알아두세요. 또한 모든 변경 사항에 빠르게 익숙해질 수 있도록 Ktor에 대한 문서 작업을 계속하고 있습니다. 앞으로 API 문서 개선을 위한 노력도 기울일 예정입니다.
2.0 버전의 전체 기능 목록은 변경 로그를 확인하세요. 그리고 오늘 바로 첫 번째 Ktor 2.0을 만들어보세요!
게시물 원문 작성자