Table of contents:
This is the third beta of Ktor. And the first version of Ktor using Kotlin 1.3.0 and kotlinx.coroutines 1.0.0 final versions!
Published 29 Oct 2018
Version bumps:
Kotlin: 1.3.0-rc-146 -> 1.3.0Kotlinx IO: 0.1.0-alpha-25-rc13 -> 0.1.0-beta-1Kotlinx Coroutines: 1.0.0-RC1 -> 1.0.0Kotlinx Serialization: 0.8.2-rc13 -> 0.9.0AtomicFU: 0.11.11 -> 0.11.12Improvements:
Fixes:
Minor:
You need to use Kotlin 1.3.0 or later. That version should be available at Maven Central and jcenter:
buildscript {
ext.kotlin_version = '1.3.0'
}
You will also need to use the Kotlin 1.3 inside IntelliJ IDEA.
This is the second beta of Ktor.
Published 24 Oct 2018
Version bumps:
Kotlin: 1.3.0-rc-131 -> 1.3.0-rc-146Kotlinx IO: 0.1.0-alpha-19-rc13 -> 0.1.0-alpha-25-rc13Kotlinx Coroutines: 0.30.2-eap13 -> 1.0.0-RC1AtomicFU: 0.11.10-eap13 -> 0.11.11Major changes:
Marked as experimental:
Deprecations:
Added:
Minor changes:
Improvements:
Fixes:
Removed:
You need to include kotlin-eap repository and use Kotlin 1.3.0-rc-146 or later:
buildscript {
ext.kotlin_version = '1.3.0-rc-146'
repositories {
// ...
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
}
repositories {
// ...
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
maven { url 'https://dl.bintray.com/kotlin/kotlinx' }
maven { url 'https://dl.bintray.com/kotlin/ktor' }
jcenter()
}
You will also need to use the Early Access Preview 1.3 inside IntelliJ IDEA.
To do so, Tools -> Kotlin -> Configure Kotlin Updates and as Update Channel, select Early Access Preview 1.3 and install that version of the plugin.
DevelopmentEngine to EngineMainWe have renamed the DevelopmentEngine classes to EngineMain, and you will have to update your application.conf configuration files.
The DevelopmentEngine classes were used as entry point for Ktor applications. It loaded the application.conf and parsed CLI parameters, in addition to
create an embedded server and loading the specified module.
The problem with DevelopmentEngine was that it was confusing since some people identified Development as opposed to Production.
The new name reflects that it serves as entry point of the application, and that it is independent on the environment being suitable for both: development and production.
This is the first beta of Ktor! This page includes changes from the alpha-2, alpha-3 and beta-1.
Published 15 Oct 2018
Version bumps:
Kotlinx IO: 0.1.0-alpha-17-rc13 -> 0.1.0-alpha-19-rc13Additions:
Improvements:
Fixes:
Performance improvements:
Documentation:
Minor and cleanups:
You need to include kotlin-eap repository and use Kotlin 1.3.0-rc-131 or later:
buildscript {
ext.kotlin_version = '1.3.0-rc-131'
repositories {
// ...
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
}
repositories {
// ...
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
maven { url 'https://dl.bintray.com/kotlin/kotlinx' }
maven { url 'https://dl.bintray.com/kotlin/ktor' }
jcenter()
}
You will also need to use the Early Access Preview 1.3 inside IntelliJ IDEA.
To do so, Tools -> Kotlin -> Configure Kotlin Updates and as Update Channel, select Early Access Preview 1.3 and install that version of the plugin.
Ktor 1.0.0-alpha-1 is the initial pre-release version that preludes the 1.0.
This version is the first one using Kotlin 1.3.0-rc-131, and bumps the kotlinx.coroutines version to 0.30.2-eap13.
As an important change, Ktor now uses the structured concurrency from kotlinx.coroutines.
In addition it adds
Published 9 Oct 2018
Version Bumps:
Kotlin: 1.2.70 -> 1.3.0-rc-131Kotlinx IO: 0.1.0-alpha-4 -> 0.1.0-alpha-17-rc13Kotlinx Coroutines: 0.25.0 -> 0.30.2-eap13Atomic FU: 0.11.3 -> 0.11.10-eap13Major changes:
Deprecations, renames and API changes:
ByteBufferBuilderRoutingException -> LocationRoutingExceptionHttpMessageBuilder.contentLength() and HttpMessage.contentLength() now return LongWebSocketInternalAPI)AndroidClientEngine is now @UseExperimental(InternalAPI::class)ApacheBackend typealias for Apache engineFixes:
Cookie.matches ignore a leading dot in the domainAdditions:
TestApplicationEngine.cookiesSession https://github.com/ktorio/ktor/commit/1af97b8fcb3096cb390480508c4abd49c13a34deContentTypes.CSV #613IosClientEngineConfigAdded call.responseConfig.defaultCharset |
HttpResponseConfig |
Improvements:
Minor changes:
kotlin.code.style=officialSince 1.0.0-alpha-1, Ktor is using the latest kotlinx.coroutines version that is using structured concurrency.
Now each request has its own CoroutineScope and you can also use the GlobalScope.
For more information: https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/basics.md#structured-concurrency
Ktor 0.9.5 is a minor update, fixing some bugs, bumping some versions, and adding some minor stuff.
Published 19 Sep 2018
Version Bumps:
Kotlin: 1.2.61 -> 1.2.70Minor potential breaking changes:
CookiesStorage.get, CookiesStorage.addCookie and HttpCookies.get now accepts the requestUrl
instead of the host. Now the get returns a list of cookies.HttpRequest.takeFrom(builder: HttpRequestBuilder): HttpRequestBuilder. Now also sets the typed call attributes from the builder.
into a member method. Shouldn’t be a source compatibility problem when using star imports * since it is in the same package.Fixes:
HttpClient.config was not preserving the base configuration (#559)URLBuilder (#577).Additions:
CallId featureMDC support to CallLogging featureAPI Improvements:
HEAD, OPTIONS, PATCH and DELETE methods (#562)List<Cookie>.get(name: String): Cookie? shortcut.fun FormBuilder.append(key: String, filename: String, block: BytePacketBuilder.() -> Unit) to the FORM DSL.respondOutputStream { } response functionInfrastructure phase into Monitoring and Features phasesInternal:
AcceptAllCookiesStorage be the same for all the implementations.Before 0.9.5, when using the HTTP client and wanting to make a HEAD, OPTIONS, PATCH or DELETE request,
you had to either use the HttpClient.request or the HttpClient.call.
As for 0.9.5, in addition to HttpClient.get and HttpClient.post there are now four more convenience methods
HttpClient.head, HttpClient.options, HttpClient.patch and HttpClient.delete HTTP verbs.
Infrastructure phase into Monitoring and Features phasesIn most of the cases, you should use the Features phase instead of the Infrastructure one:
intercept(ApplicationCallPipeline.Infrastructure) {
// ...
}
would become:
intercept(ApplicationCallPipeline.Features) {
// ...
}
The CallLogging feature have been updated to allow to specify MDC (Mapped Diagnostic Context) keys + providers to be associated to a specific call/request regardless of the ThreadScheduler. The feature is implemented using kotlinx.coroutines ThreadContextElement, so it is transparent and works well with slf4j.
0.9.5 includes a new CallId feature that allows to identify calls from an ID. It allows to provide several methods
for receiving or generating that Call Identifier, and also optionally allows to include the id as part of the response,
for example as a header. The CallId is also associated to the MDC of the call, so the logs for a specific call can be
identified and grouped together.
Ktor 0.9.4 fixes some bugs, bumps some versions, moves a few classes, improves the overall performance of the server. This version deprecate some APIs, introduce new ones, and includes a new multiplatform HTTP client (initially supporting JVM, iOS and Android).
This version is expected to be mostly source-code compatible with 0.9.3 except for the deprecate APIs.
https://github.com/ktorio/ktor/compare/0.9.3...0.9.4
Published 29 Aug 2018
Version Bumps:
Kotlin: 1.2.50 -> 1.2.61kotlin-native -> 0.8.2jetty_alpn_boot_version: 8.1.11.v20170118 -> 8.1.12.v20180117coroutines_version: 0.23.3 -> 0.25.0atomic_fu_version: -> 0.11.3kotlinx_io_version: -> 0.1.0-alpha-4Deprecations:
response.contentType and response.contentLength extensions (UnsafeHeaderException: Header Content is controlled by the engine and cannot be set explicitly)IncomingContent deprecation, prepare for complete removalXForwardedHeadersSupport to XForwardedHeaderSupport (#547)Minor potentially breaking changes:
io.ktor:ktor-client-json artifact has been renamed to io.ktor:io.ktor:ktor-client-gson (to support a new JacksonSerializer)ktor-client-json, ktor-client-gson and ktor-client-jackson) artifacts require the kotlinx.serialization and thus require the maven { url "https://kotlin.bintray.com/kotlinx" } repo to be includedDate to GMTDate in some specific internal placesHttpRequest, HttpResponse and HttpRequestData changesio.ktor.content moved to io.ktor.http.contentencodeURLQueryComponent method is now a extension method of String fun String.encodeURLQueryComponent()PartData.FileItem now requires a lambda with an Input, instead of an InputStreamPartData has a new element PartData.BinaryItem, thus might require an additional branch or an else to be exhaustive when used in a expressionFixes:
CharBufferBuilder issue: CIO Responses with more than 2048 characters in header result in IndexOutOfBoundsException (#419)TestApplicationRequest.setBodyStringIndexOutOfBoundsException in static resource resolution (#493)-config=<filename>, environment variables are not resolved (#374)AsyncContext errorSessionTransportTransformerEncrypt return null on failureOptimizations:
HttpClient instantiation, provide better error messageImprovements to existing APIs:
Testing: Add advanced test exception logging
Add HttpStatusCode constants for the status codes defined in RFC 2518 (WebDAV)
ChannelIOExceptionWrap client exception from response pipeline with request and typeInfo
Add utility to append header value (such as content type)
New features:
EngineAPI marker annotationJacksonSerializer to HTTP ClientHttpResponse.receive methodInternal changes:
Starting with Ktor 0.9.4, there is a MPP HTTP Client included with Ktor.
In 0.9.4, the client is implemented for the JVM, Android and iOS.
We provide a sample of this feature as part of the ktor-samples repo: ktor-samples/client-mpp
You can read more about this feature in the Multiplatform HTTP Client page.
Prior to 0.9.4, you had to configure the HttpClient Engine by:
HttpClient(MyEngine.config {
// ... config ...
}) {
install {
}
}
Since 0.9.4, HttpClientConfig provides an engine method to reconfigure or configure the HttpClientEngineConfig:
HttpClient(MyEngine) {
engine {
// ... config ...
}
install {
}
}
Since 0.9.4, and after a period of deprecation, Ktor started to completely forbid to set contentType and contentLength headers directly.
This is because those are bound to the OutgoingContent instances, that is the object describing the body of the response.
The respondText, respondBytes and other methods, allow you to set the Content-Type as an optional argument.
And when using any subtype of OutgoingContent, you can set the contentType there.
For more information about this topic, check the Generating HTTP Responses page.
In some places (like Client HttpResponse) we were using Java’s Date class. Now we are using GMTDate instead.
HttpRequest, HttpResponse and HttpRequestData changesPreviously the HttpRequestData, had an attributes method to build an Attributes instance from another.
Now it is a property instead. Attributes().apply { data.attributes(this) } would become data.attributes.
This change, affects, for example when implementing a HttpRequest:
override val attributes: Attributes = Attributes().apply { data.attributes(this) }
// -->
//override val attributes: Attributes = data.attributes
And the HttpResponse changed its requestTime and responseTime from java’s Date to io.ktor.util.date.GMTDate.
Ktor 0.9.3 fixes some bugs, bumps some versions, improves the overall performance of the server, and introduces new APIs and changes for some of them.
This version is expected to be mostly source-code compatible with 0.9.2
except for advanced use-cases related with kotlinx.coroutines and Ktor Raw sockets.
kotlinx.coroutines.ktor raw sockets.https://github.com/ktorio/ktor/compare/0.9.2...0.9.3
Published 26 Jun 2018
Now webSocketSession and webSocketRawSession return DefaultClientWebSocketSession
and webSocket, ws and wss methods also receives a DefaultClientWebSocketSession and other methods
return a ClientWebSocketSession.
This prevents having to cast to access some properties from the client session:
var DefaultWebSocketSession.pingInterval: Duration?
var DefaultWebSocketSession.timeout: Duration
val DefaultWebSocketSession.closeReason: Deferred<CloseReason?>
var DefaultClientWebSocketSession.masking: Boolean
We had a MockEngine available internally for our tests for creating an HttpClient that was able to respond
from our code programmatically without actually performing any request.
We have now exposed it in the io.ktor:ktor-client-mock artifact.
For example:
val mockEngine = MockEngine { call -> // suspend HttpRequest.(call: HttpClientCall) -> MockHttpResponse
assertEquals("*/*", headers[HttpHeaders.Accept])
MockHttpResponse(call, HttpStatusCode.OK, writer(ioCoroutineDispatcher) { channel.writeStringUtf8("HELLO") }.channel, headersOf(
"X-Custom-Header" to listOf("value")
))
}
val client = HttpClient(mockEngine)
Inside the MockEngine block, you can assert things from the request, or decide how to generate a response based on the request.
Now you can instantiate a HttpClient by just HttpClient() without providing an engine.
In that case it will use a ServiceLoader to find a suitable implementation based on the artifacts you have included.
Now you can use the new client HttpRedirect feature to follow HTTP Location-based redirections.
Read the documentation for more information.
Now the HttpClient sends cookies in a standard way with one single header separating cookies by ;.
Reading an empty body as String with an Httpclient (HttpClient.get<String>) was causing a
No transformation found: class io.ktor.client.engine.apache.ApacheHttpResponse -> class kotlin.String exception.
HttpClientEngine now has a config field. So you will have to provide it in the case you have a custom engine.
Now the HttpClient sends an Accept header. If you have a test checking all the headers, you will need to update them.
Ktor implements secure sockets using CIO, and the TLS available before 0.9.3 missed some cypher suites and encryption methods. We have updated it to support more required stuff.
Some errors you could encounter that now might be fixed:
Exception in thread "io-thread-1" io.ktor.network.tls.TLSException: Received alert during handshake. Level: FATAL, code: HandshakeFailureException in thread "io-thread-2" io.ktor.network.tls.TLSException: Unsupported TLS handshake type CertificateRequestaSocket now requires a SelectorManager to be provided.
For example:
aSocket(ActorSelectorManager(ioCoroutineDispatcher))
Of course, you can reuse the SelectorManager:
val mySocketSelector = ActorSelectorManager(ioCoroutineDispatcher)
val socket = aSocket(mySocketSelector).tcp()
Before 0.9.3, when a WS request was made without the Sec-WebSocket-Key header,
an IllegalArgumentException was thrown. Now it is optional.
Now it is possible to define a set of routes where you can provide authentication, but it is optional:
authenticate("method", optional = true) {
// routes
}
The authenticate behaviour:
optional=false: challenge is performedoptional=true: route handler is executed with principal=nullBefore 0.9.3 we had call.respondText but missed call.respondBytes. We have solved this.
Ktor now uses Kotlin 1.2.50 and kotlinx.coroutines 0.23.3.
kotlinx.coroutines has some breaking changes for advanced use-cases.
We have moved the generateCertificate function for generating self-signed certificates to the ktor-network-tls artifact:
compile("io.ktor:ktor-network-tls:$ktor_version")
Before 0.9.3, we had several sample projects integrated directly in the ktor repository. They have grown, and we have moved them to their own repository: https://github.com/ktorio/ktor-samples
Ktor 0.9.2 fixes some bugs, improves the overall performance of the server, partially starts supporting JVM 9, and introduces new APIs and changes for some of them.
In this section, we will discuss how to convert existing code from 0.9.1 to 0.9.2.
The biggest change in this version since 0.9.1, is authentication. Which has been redesigned:
In previous versions, you had to define an authentication block inside your Application or in a Route block,
applying that authentication to all the subroutes matching that block.
That forced you to redefine several authentication providers, or in some cases that would force you to include authentication in unexpected routes.
authentication {
basicAuthentication("ktor") { credentials ->
if (credentials.password == "${credentials.name}123") UserIdPrincipal(credentials.name) else null
}
}
In 0.9.2, all the authentication mechanisms are defined at the Application level and have an optional name associated with them. Also, the method names for defining different mechanisms have changed. Now the new name of the authentication mechanism is part of the function call, and all its old parameters are defined using a DSL instead.
For example, to define a basic authentication myauth1, you should add this code to your application configuration:
install(Authentication) {
basic(name = "myauth1") {
realm = "Ktor Server"
validate { credentials ->
if (credentials.password == "${credentials.name}123") UserIdPrincipal(credentials.name) else null
}
}
}
And now you can create a Route node to apply the defined authentication to several routes using the authenticate method:
routing {
authenticate("myauth1") {
get("/authenticated/route1") {
// ...
}
get("/other/route2") {
// ...
}
}
get("/") {
// ...
}
}
Authentication method name changes:
basicAuthentication → basicformAuthentication → formdigestAuthentication → digestjwtAuthentication → jwtoauthAuthentication → oauthYou can read the new authentication page to see in detail how to use the new methods.
The body property from TestApplicationRequest builder has been changed to a suspend setBody method:
handleRequest(HttpMethod.Post, "/") {
addHeader("Accept", "text/plain")
addHeader("Content-Type", "application/json")
//body = """{"id":1,"title":"Hello, World!"}"""
setBody("""{"id":1,"title":"Hello, World!"}""")
}.response.let { response ->
// ...
}
Before 0.9.2, you had to call the method awaitCompletion in the response in the case the request
was not generating the response body synchronously. In 0.9.2, the awaitCompletion method doesn’t exist,
and it awaits completion automatically before returning the response.
If you are using call.request.receiveContent().readChannel(), call.request.receiveContent().multiPartData()
or call.request.receiveContent().inputStream(), you should consider changing it to
call.receive<ByteReadChannel>(), call.receive<MultiPartData>() and/or call.receive<InputStream>()
since it is deprecated and will be removed in future versions of Ktor.
Also, remember that InputStream is a synchronous API, so you should avoid it if possible.
In order to support WebSockets at the client side, we have changed some of the transitive dependencies
and moved some classes: now there is a transitive dependency called ktor-http-cio that includes common
WebSockets code among other things, which the ktor-websockets server feature depends on.
But since it is a transitive dependency, that should be transparent to you.
Classes like WebSocketSession and Frame have been moved from the io.ktor.websocket package to the
io.ktor.http.cio.websocket package.
import io.ktor.websocket.*
→
import io.ktor.http.cio.websocket.*
When building a new CIO HttpClient, while configuring the endpoint
(it has been renamed from endpointConfig to endpoint, and now the property is immutable,
so you have to mutate its contents):
Prior to 0.9.2:
val client = HttpClient(CIO.config {
endpointConfig = EndpointConfig().apply {
}
})
After 0.9.2:
val client = HttpClient(CIO.config {
endpoint.apply {
// ...
}
})