Changelog 2.0 version
2.0.3
released 28th June 2022
Client
Unable to set the Content-Type header in a request
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1127
Ktor Version
1.1.4
Ktor Engine Used(client or server and name)
Apache
JVM Version, Operating System and Relevant Context
1.8, macOS Mojave and Linux Mint, IDEA 2019.1.2 CE
Feedback
I'm accessing and API that requires me to set Content-Type
for all requests, but I'm getting io.ktor.http.UnsafeHeaderException: Header Content-Type is controlled by the engine and cannot be set explicitly
Current implementation:
val client = HttpClient(Apache) {
install(JsonFeature) {
serializer = GsonSerializer()
}
install(Logging) {
level = LogLevel.HEADERS
}
defaultRequest {
header("Content-Type", "application/vnd.api+json")
}
}
get("/test") {
client.get<String>("url...") {
headers {
// other headers
}
}
}
submitFormWithBinaryData call leads to NPE when the ContentNegotiation plugin is installed
Ktor: 2.0.1
Tested on: iPhone 7 (15.1)
Code example (can try to do mini sample if needed):
val client = HttpClient {
install(ContentNegotiation) {
json()
}
defaultRequest {
headers.appendIfNameAbsent(HttpHeaders.ContentType, ContentType.Application.Json.toString())
}
}
client.submitFormWithBinaryData(url, formData {
append("content", bytes, Headers.build {
append(HttpHeaders.ContentType, ContentType.Application.OctetStream)
append(HttpHeaders.ContentDisposition, "filename=content")
})
}).body()
Crash log:
Uncaught Kotlin exception: kotlin.NullPointerException
at 0 MyApp 0x10457e7f7 kfun:kotlin.Throwable#<init>(){} + 75
at 1 MyApp 0x104577147 kfun:kotlin.Exception#<init>(){} + 67
at 2 MyApp 0x10457739b kfun:kotlin.RuntimeException#<init>(){} + 67
at 3 MyApp 0x104577f73 kfun:kotlin.NullPointerException#<init>(){} + 67
at 4 MyApp 0x1045aa93b ThrowNullPointerException + 107
at 5 MyApp 0x10494b6db kfun:io.ktor.client.plugins.contentnegotiation.ContentNegotiation.Plugin.$install$lambda-1COROUTINE$136.invokeSuspend#internal + 4559
at 6 MyApp 0x10494c717 kfun:io.ktor.client.plugins.contentnegotiation.ContentNegotiation.Plugin.$install$lambda-1COROUTINE$136.invoke#internal + 275
at 7 MyApp 0x10494c82f kfun:io.ktor.client.plugins.contentnegotiation.ContentNegotiation.Plugin.$install$lambda-1COROUTINE$136.invoke#internal.223 + 211
at 8 MyApp 0x104809e3b kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 787
at 9 MyApp 0x1048096cf kfun:io.ktor.util.pipeline.SuspendFunctionGun#proceed(){}1:0 + 347
at 10 MyApp 0x10480988b kfun:io.ktor.util.pipeline.SuspendFunctionGun#proceedWith(1:0){}1:0 + 175
at 11 MyApp 0x1049138df kfun:io.ktor.client.plugins.HttpCallValidator.Companion.$install$lambda-1COROUTINE$155.invokeSuspend#internal + 883
at 12 MyApp 0x104914147 kfun:io.ktor.client.plugins.HttpCallValidator.Companion.$install$lambda-1COROUTINE$155.invoke#internal + 275
at 13 MyApp 0x10491425f kfun:io.ktor.client.plugins.HttpCallValidator.Companion.$install$lambda-1COROUTINE$155.invoke#internal.183 + 211
at 14 MyApp 0x104809e3b kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 787
at 15 MyApp 0x1048096cf kfun:io.ktor.util.pipeline.SuspendFunctionGun#proceed(){}1:0 + 347
at 16 MyApp 0x10491e9ab kfun:io.ktor.client.plugins.HttpRequestLifecycle.Plugin.$install$lambda-0COROUTINE$164.invokeSuspend#internal + 1039
at 17 MyApp 0x10491f19f kfun:io.ktor.client.plugins.HttpRequestLifecycle.Plugin.$install$lambda-0COROUTINE$164.invoke#internal + 275
at 18 MyApp 0x10491f2b7 kfun:io.ktor.client.plugins.HttpRequestLifecycle.Plugin.$install$lambda-0COROUTINE$164.invoke#internal.197 + 211
at 19 MyApp 0x104809e3b kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 787
at 20 MyApp 0x1048096cf kfun:io.ktor.util.pipeline.SuspendFunctionGun#proceed(){}1:0 + 347
at 21 MyApp 0x104809a53 kfun:io.ktor.util.pipeline.SuspendFunctionGun#execute(1:0){}1:0 + 387
at 22 MyApp 0x104802453 kfun:io.ktor.util.pipeline.Pipeline#execute(1:1;1:0){}1:0 + 379
at 23 MyApp 0x1048ecb03 kfun:io.ktor.client.HttpClient.$executeCOROUTINE$138#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 587
at 24 MyApp 0x1048ecdc7 kfun:io.ktor.client.HttpClient#execute(io.ktor.client.request.HttpRequestBuilder){}io.ktor.client.call.HttpClientCall + 271
at 25 MyApp 0x104942e6b kfun:io.ktor.client.statement.HttpStatement.$executeUnsafeCOROUTINE$189#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 743
at 26 MyApp 0x1049431cf kfun:io.ktor.client.statement.HttpStatement#executeUnsafe(){}io.ktor.client.statement.HttpResponse + 239
at 27 MyApp 0x104941f33 kfun:io.ktor.client.statement.HttpStatement.$executeCOROUTINE$186#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 811
at 28 MyApp 0x10494297b kfun:io.ktor.client.statement.HttpStatement#execute(kotlin.coroutines.SuspendFunction1<io.ktor.client.statement.HttpResponse,0:0>){0§<kotlin.Any?>}0:0 + 271
at 29 MyApp 0x104942a77 kfun:io.ktor.client.statement.HttpStatement#execute(){}io.ktor.client.statement.HttpResponse + 183
at 30 MyApp 0x104ba3f6b kfun:com.mjegorovas.MyApp.server.MyAppApi.$storeFileContentCOROUTINE$58#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 1855
at 31 MyApp 0x104ba473f kfun:com.mjegorovas.MyApp.server.MyAppApi#storeFileContent(kotlin.ByteArray;kotlin.Int;kotlin.Long;kotlin.Int?){} + 367
at 32 MyApp 0x104ba4897 kfun:com.mjegorovas.MyApp.server.MyAppApi#storeFileContent$default(kotlin.ByteArray;kotlin.Int;kotlin.Long;kotlin.Int?;kotlin.Int){} + 283
at 33 MyApp 0x104c7016f kfun:com.mjegorovas.MyApp.useCases.UploadItemUseCase.$storeItems$<anonymous>_3COROUTINE$101.invokeSuspend#internal + 803
at 34 MyApp 0x104c706cf kfun:com.mjegorovas.MyApp.useCases.UploadItemUseCase.$storeItems$<anonymous>_3COROUTINE$101.invoke#internal + 255
at 35 MyApp 0x104c70907 kfun:com.mjegorovas.MyApp.useCases.UploadItemUseCase.$storeItems$<anonymous>_3COROUTINE$101.$<bridge-NNNNB>invoke(kotlin.ByteArray;kotlin.Long){}kotlin.Any?#internal + 227
at 36 MyApp 0x104c78fa3 kfun:com.mjegorovas.MyApp.util.$readByChunksCOROUTINE$107#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 1039
at 37 MyApp 0x104c7935f kfun:com.mjegorovas.MyApp.util#readByChunks__at__okio.BufferedSource(kotlin.Long;kotlin.coroutines.SuspendFunction2<kotlin.ByteArray,kotlin.Long,kotlin.Unit>){} + 295
at 38 MyApp 0x104c7947f kfun:com.mjegorovas.MyApp.util#readByChunks$default__at__okio.BufferedSource(kotlin.Long;kotlin.coroutines.SuspendFunction2<kotlin.ByteArray,kotlin.Long,kotlin.Unit>;kotlin.Int){} + 231
at 39 MyApp 0x104c6eee7 kfun:com.mjegorovas.MyApp.useCases.UploadItemUseCase.$storeItemsCOROUTINE$100.invokeSuspend#internal + 2483
at 40 MyApp 0x104c6f873 kfun:com.mjegorovas.MyApp.useCases.UploadItemUseCase.storeItems#internal + 319
at 41 MyApp 0x104c6d8a7 kfun:com.mjegorovas.MyApp.useCases.UploadItemUseCase.$uploadCOROUTINE$99#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 3335
at 42 MyApp 0x104582943 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 563
at 43 MyApp 0x1047019fb kfun:kotlinx.coroutines#resume__at__kotlinx.coroutines.DispatchedTask<0:0>(kotlin.coroutines.Continuation<0:0>;kotlin.Boolean){0§<kotlin.Any?>} + 1059
at 44 MyApp 0x104701213 kfun:kotlinx.coroutines#dispatch__at__kotlinx.coroutines.DispatchedTask<0:0>(kotlin.Int){0§<kotlin.Any?>} + 839
at 45 MyApp 0x1046afa07 kfun:kotlinx.coroutines.CancellableContinuationImpl.dispatchResume#internal + 115
at 46 MyApp 0x1046b00e7 kfun:kotlinx.coroutines.CancellableContinuationImpl.resumeImpl#internal + 783
at 47 MyApp 0x1046b0337 kfun:kotlinx.coroutines.CancellableContinuationImpl#resumeImpl$default(kotlin.Any?;kotlin.Int;kotlin.Function1<kotlin.Throwable,kotlin.Unit>?;kotlin.Int){} + 243
at 48 MyApp 0x1046aebcb kfun:kotlinx.coroutines.CancellableContinuationImpl#resumeWith(kotlin.Result<1:0>){} + 207
at 49 MyApp 0x1046d4767 kfun:kotlinx.coroutines.ResumeOnCompletion.invoke#internal + 383
at 50 MyApp 0x1046c5c33 kfun:kotlinx.coroutines.JobSupport.notifyCompletion#internal + 703
at 51 MyApp 0x1046c4e9b kfun:kotlinx.coroutines.JobSupport.completeStateFinalization#internal + 1167
at 52 MyApp 0x1046c34bb kfun:kotlinx.coroutines.JobSupport.finalizeFinishingState#internal + 1131
at 53 MyApp 0x1046cd387 kfun:kotlinx.coroutines.JobSupport.continueCompleting#internal + 371
at 54 MyApp 0x1046d030b kfun:kotlinx.coroutines.JobSupport.ChildCompletion.invoke#internal + 267
at 55 MyApp 0x1046c5c33 kfun:kotlinx.coroutines.JobSupport.notifyCompletion#internal + 703
at 56 MyApp 0x1046c4e9b kfun:kotlinx.coroutines.JobSupport.completeStateFinalization#internal + 1167
at 57 MyApp 0x1046c34bb kfun:kotlinx.coroutines.JobSupport.finalizeFinishingState#internal + 1131
at 58 MyApp 0x1046cc68b kfun:kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath#internal + 1371
at 59 MyApp 0x1046cc063 kfun:kotlinx.coroutines.JobSupport.tryMakeCompleting#internal + 639
at 60 MyApp 0x1046cbbf7 kfun:kotlinx.coroutines.JobSupport#makeCompletingOnce(kotlin.Any?){}kotlin.Any? + 355
at 61 MyApp 0x1046a4797 kfun:kotlinx.coroutines.AbstractCoroutine#resumeWith(kotlin.Result<1:0>){} + 219
at 62 MyApp 0x104582c23 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 1299
at 63 MyApp 0x10470050b kfun:kotlinx.coroutines.DispatchedTask#run(){} + 2743
at 64 MyApp 0x1046b982f kfun:kotlinx.coroutines.EventLoop#processUnconfinedEvent(){}kotlin.Boolean + 351
at 65 MyApp 0x104701463 kfun:kotlinx.coroutines.resumeUnconfined#internal + 439
at 66 MyApp 0x1047011af kfun:kotlinx.coroutines#dispatch__at__kotlinx.coroutines.DispatchedTask<0:0>(kotlin.Int){0§<kotlin.Any?>} + 739
at 67 MyApp 0x1046afa07 kfun:kotlinx.coroutines.CancellableContinuationImpl.dispatchResume#internal + 115
at 68 MyApp 0x1046b0f6f kfun:kotlinx.coroutines.CancellableContinuationImpl#completeResume(kotlin.Any){} + 115
at 69 MyApp 0x1046de31f kfun:kotlinx.coroutines.channels.AbstractChannel.ReceiveHasNext.resumeReceiveClosed#internal + 499
at 70 MyApp 0x1046e45bb kfun:kotlinx.coroutines.channels.AbstractSendChannel.helpClose#internal + 979
at 71 MyApp 0x1046e37a3 kfun:kotlinx.coroutines.channels.AbstractSendChannel#close(kotlin.Throwable?){}kotlin.Boolean + 591
at 72 MyApp 0x1046f3b4f kfun:kotlinx.coroutines.channels.SendChannel#close$default(kotlin.Throwable?;kotlin.Int){}kotlin.Boolean + 263
at 73 MyApp 0x104966f6b kfun:io.ktor.client.engine.darwin.DarwinResponseReader#objc:URLSession:task:didCompleteWithError: + 451
at 74 MyApp 0x104969963 _696f2e6b746f723a6b746f722d636c69656e742d64617277696e_knbridge1109 + 299
at 75 CFNetwork 0x1840bc4ff CFURLRequestCopyHTTPRequestMethod + 2203
at 76 libdispatch.dylib 0x183629193 <redacted> + 23
at 77 libdispatch.dylib 0x18362a197 <redacted> + 15
at 78 libdispatch.dylib 0x1835d65cf <redacted> + 939
at 79 CoreFoundation 0x1839197e7 <redacted> + 11
at 80 CoreFoundation 0x1838d70e7 <redacted> + 2527
at 81 CoreFoundation 0x1838e9d7b CFRunLoopRunSpecific + 571
at 82 GraphicsServices 0x19db5e99f GSEventRunModal + 159
at 83 UIKitCore 0x18611c05b <redacted> + 1079
at 84 UIKitCore 0x185eb1cdf UIApplicationMain + 2027
at 85 SwiftUI 0x18af821cb <redacted> + 159
at 86 SwiftUI 0x18aecbdd7 <redacted> + 179
at 87 SwiftUI 0x18aeb15ff $s7SwiftUI3AppPAAE4mainyyFZ + 95
at 88 MyApp 0x104502b5b $s5MyApp6iOSAppV5$mainyyFZ + 39
at 89 MyApp 0x104502c07 main + 11
at 90 dyld 0x1064a018f 0x0 + 4400480655
Signal: SIGABRT (signal SIGABRT)
Setting body to TextContent leads to NPE when the ContentNegotiation plugin is installed
When trying to send a String containing json with ktor while also having serializers installed I noticed that it was breaking my json string.
I tried bypassing the serializers by using TextContent because this doesn't set the bodyType on the HttpRequestBuilder.
This resulted in the following stacktrace:
java.lang.NullPointerException: null
at io.ktor.client.plugins.contentnegotiation.ContentNegotiation$Plugin$install$1.invokeSuspend(ContentNegotiation.kt:112)
at io.ktor.client.plugins.contentnegotiation.ContentNegotiation$Plugin$install$1.invoke(ContentNegotiation.kt)
at io.ktor.client.plugins.contentnegotiation.ContentNegotiation$Plugin$install$1.invoke(ContentNegotiation.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(SuspendFunctionGun.kt:91)
at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invokeSuspend(HttpCallValidator.kt:125)
at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invokeSuspend(HttpRequestLifecycle.kt:35)
at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invoke(HttpRequestLifecycle.kt)
at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invoke(HttpRequestLifecycle.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
at io.ktor.util.pipeline.SuspendFunctionGun.execute$ktor_utils(SuspendFunctionGun.kt:101)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:77)
at io.ktor.client.HttpClient.execute$ktor_client_core(HttpClient.kt:184)
at io.ktor.client.statement.HttpStatement.executeUnsafe(HttpStatement.kt:107)
at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:46)
at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:61)
at me.melijn.bot.commands.DevExtension$setup$2$1.invokeSuspend(DevExtension.kt:152)
at me.melijn.bot.commands.DevExtension$setup$2$1.invoke(DevExtension.kt)
at me.melijn.bot.commands.DevExtension$setup$2$1.invoke(DevExtension.kt)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommand$call$2.invokeSuspend(ChatCommand.kt:434)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommand$call$2.invoke(ChatCommand.kt)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommand$call$2.invoke(ChatCommand.kt)
at com.kotlindiscord.kord.extensions.types.Lockable$DefaultImpls.withLock(Lockable.kt:24)
at com.kotlindiscord.kord.extensions.commands.Command.withLock(Command.kt:39)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommand.call$suspendImpl(ChatCommand.kt:351)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommand.call(ChatCommand.kt)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommand.call$default(ChatCommand.kt:345)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommandRegistry.handleEvent$suspendImpl(ChatCommandRegistry.kt:237)
at com.kotlindiscord.kord.extensions.commands.chat.ChatCommandRegistry$handleEvent$1.invokeSuspend(ChatCommandRegistry.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Here is a code snippet to reproduce this:
I've had it with this issue editor, it wants to place everything on one line so here it is ig..
https://gist.github.com/ToxicMushroom/975601e26be80994a57487e043a3e6dc
[JS client] Cannot change redirect policy by followRedirects=false
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1968
Ktor Version and Engine Used (client or server and name)
Ktor HTTP client 1.3.2 on Kotlin/JS; dependencies:
implementation("io.ktor:ktor-client-json:$ktorVersion")
implementation("io.ktor:ktor-client-js:$ktorVersion")
implementation("io.ktor:ktor-client-json-js:$ktorVersion")
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
Describe the bug
While debugging the situation with redirects location, I found that if you call something like println(resp)
where resp is a HttpResponse
result of a request that returns HTTP/1.1 302 Found
, string will be HttpResponse[http://localhost:3000/api/v1/auth/logout, 200 OK]
, where I excected something like HttpResponse[http://localhost:3000/api/v1/auth/logout, 302 Found]
.
In network tab of browser developer tools response shown as 302 Found
with correct Location
header, but ktor client shows different results...
To Reproduce
Steps to reproduce the behavior:
- Take this repo for example, change url in
App.kt
to something suitable in your environment (httpbin, for example) - Open in browser localhost:3000
- See string representation of response with 200 OK
Expected behavior
302 Found with Location
header
Screenshots
If what, I can try to debug this and fix by myself, but later...
P.S. Thank you for ktor framework and client. They're awesome.
WebSocket client closes connection due to an HTTP request timeout
This broke between 2.0.1 and 2.0.2.
val websocketClient = HttpClient(CIO) {
install(HttpTimeout) {
connectTimeoutMillis = 2000L
}
install(WebSockets) {
pingInterval = 30000L
}
}
val session = websocketClient.webSocketSession("ws://some-url")
session.launch {
while (isActive) {
val frame = session.incoming.receive()
// to things with frame
}
}
The websocket server shows the client as connected and eventually reports an abnormal close. On the client side the coroutine is cancelled with a request timeout exception from ktor:
java.util.concurrent.CancellationException: Request is timed out
at kotlinx.coroutines.ExceptionsKt.CancellationException(Exceptions.kt:22)
at kotlinx.coroutines.JobKt__JobKt.cancel(Job.kt:596)
at kotlinx.coroutines.JobKt.cancel(Unknown Source)
at io.ktor.client.engine.cio.EndpointKt$setupTimeout$timeoutJob$1.invokeSuspend(Endpoint.kt:250)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Caused by: io.ktor.client.plugins.HttpRequestTimeoutException: Request timeout has expired [url=ws://mrf-rest.lab2018.qa.lbaum.eu:8081/v2/events/3853126682810736, request_timeout=unknown ms]
... 7 common frames omitted
CIO: Websockets request doesn't include query parameters
The Websocket HttpClient doesn't seem to send the query parameters since Ktor 2.0+. It looks like they are stripped.
Ktor Client code:
val client = HttpClient(CIO) {
install(WebSockets)
}
client.wss(
host = "api.acme.com",
path = "/connect?param1=value1¶m2=value2"
) {
...
}
Ktor Server code:
webSocket("/connect") {
logger.debug("call.request.path(): ${call.request.path()}")
logger.debug("call.request.uri: ${call.request.uri}")
logger.debug("call.request.rawQueryParameters: ${call.request.rawQueryParameters}")
logger.debug("call.request.queryParameters: ${call.request.queryParameters}")
logger.debug("call.parameters: ${call.parameters}")
logger.debug("call.request.queryParameters[\"param1\"]: ${call.request.queryParameters["param1"]}")
logger.debug("call.request.queryParameters[\"param2\"]: ${call.request.queryParameters["param2"]}")
}
Using the client code above with Ktor 1.6.7 results on the server:
call.request.path(): /connect
call.request.uri: /connect?param1=value1¶m2=value2
call.request.rawQueryParameters: Parameters [param1=[value1], param2=[value2]]
call.request.queryParameters: io.ktor.server.netty.NettyApplicationRequest$queryParameters$1@3456f3cc
call.parameters: Parameters [param1=[value1], param2=[value2]]
call.request.queryParameters["param1"]: value1
call.request.queryParameters["param2"]: value2
Using the client code above with Ktor 2.0.1 results on the server:
call.request.path(): /connect
call.request.uri: /connect
call.request.rawQueryParameters: Parameters []
call.request.queryParameters: io.ktor.server.netty.NettyApplicationRequest$queryParameters$1@60a44ee9
call.parameters: Parameters []
call.request.queryParameters["param1"]: null
call.request.queryParameters["param2"]: null
Any help would be much appreciated! :D
Kind regards,
Thomas
Invalid request line produced by CIO engine for URL with parameters and without path
When making a request with URL http://localhost?foo=bar
, CIO engine sends the following data to the server:
GET ?foo=bar/ HTTP/1.1
...
It seems to be caused by this normalization. The normalization should take into account the rest of the URL (parameters, fragment etc.).
Core
UDP responses are received with a huge delay on JVM Windows (due to reverse DNS lookup internally)
The raw UDP sockets in Ktor do not work as expected for me. Requests are OK, but responses come in very slowly, and only gets worse with more responses. In Wireshark you can see responses come in immediately.
After some debugging I found the cause. Apparently every UDP response causes a reverse DNS lookup in Ktor to get the hostname. Call is here (hostName):
This function is used here every time a UDP packet is received:
- https://github.com/ktorio/ktor/blob/3a0accc88eb166a0fc11324f3228d55c2abb845f/ktor-network/jvm/src/io/ktor/network/sockets/DatagramSocketImpl.kt#L81
- https://github.com/ktorio/ktor/blob/3a0accc88eb166a0fc11324f3228d55c2abb845f/ktor-network/jvm/src/io/ktor/network/sockets/DatagramSocketImpl.kt#L99
It does not seem to happen for every device on my local network. But for some devices it causes around a 4 second delay on my Windows PC, measured like the following:
val address = java.net.InetSocketAddress("192.168.86.54", 56700)
measureTimeMillis {
address.hostName
}.also { println(it.toString()) } // prints around 4000 milliseconds
Now comes the worst part. This delay is there every time. So say for example you receive 100 UDP packets at the same time. That means that with Ktor the last packet is received after 100*4 = 400 seconds
This makes the Ktor UDP sockets unusable on Windows, so this needs to be fixed. For some reason this does not happen for every local IP address, and I can also not reproduce it on macOS. It is a Windows only issue for me.
See documentation for InetSocketAddress.html#getHostName()
:
Gets the hostname. Note: This method may trigger a name service reverse lookup if the address was created with a literal IP address.
https://docs.oracle.com/javase/7/docs/api/java/net/InetSocketAddress.html#getHostName()
As an alternative, instead of using getHostName
(hostName
in Kotlin), another function called getHostString
could be used instead. This function specifically mentions in the documentation:
Returns the hostname, or the String form of the address if it doesn't have a hostname (it was created using a literal). This has the benefit of not attempting a reverse lookup.
I think this is a good replacement, although it could be seen as a minor breaking chance in Ktor, but it is much better than a 4 second delay. And there needs to be a fallback for < 1.7 as this function is only available since 1.7.
Some related information:
Thanks for looking into this.
UrlBuilder escapes fragment parameters
val url = URLBuilder(
Url("https://localhost")
).apply {
fragment = "action_uri=%3A"
}.build()
url.toString() // => https://localhost#action_uri=%253A
Url(url.toString()).toString() // => https://localhost#action_uri=%25253A
As you can see, escape sequences in the fragment are parsed without unescaping, but when converting to a string they get escaped.
Docs
Website / docs: Builtin search does not work
{width=70%}
{width=70%}
Clarify that the 'install' function should be called in the client context
ActorSelectorManager should be SelectorManager for a Socket docs
This page is written for Jvm only sockets, but can be tuned for multiplatform:
Generator
Generator generates WebSockets sample with an empty path (`/`) and it clashes with Routing feature's default path
After creating a project with WebSockets feature, you have in Sockets.kt
:
fun Application.configureSockets() {
install(WebSockets) {
...
}
routing {
webSocket("/") { // websocketSession
...
}
}
}
And Routing.kt
:
fun Application.configureRouting() {
routing {
get("/") {
...
}
}
}
WebSockets feature creates non-compilable code with non-exhaustive `when`
Create a project with WebSockets feature and in Sockets.kt
will be:
fun Application.configureSockets() {
install(WebSockets) {
...
}
routing {
webSocket("/") { // websocketSession
for (frame in incoming) {
when (frame) { // COMPILE ERROR: NON-EXHAUSTIVE WHEN
is Frame.Text -> {
...
}
}
}
}
}
}
IntelliJ IDEA Plugin
The 'Create Ktor test' intention should initialize a client with the WebSockets plugin installed
Currently, the plugin adds the following code to a test:
client.webSocket("/chat") {
TODO("Please write your test here")
}
But it would be convenient to initialize a client with the WebSockets plugin installed:
val client = createClient {
install(WebSockets)
}
client.webSocket("/chat") {
TODO("Please write your test here")
}
Migration from 2.0.0 -> 2.0.1 is not Popping Up on ktor-samples repo
The plugin doesn't add a template file when generating a project with Freemarker/etc
To reproduce the issue, generate a new project with the Freemarker plugin and leave the Add sample code
option enabled. The following endpoint will be generated:
get("/html-freemarker") {
call.respond(FreeMarkerContent("index.ftl", mapOf("data" to IndexData(listOf(1, 2, 3))), ""))
}
But the index.ftl
is missing in a project, so accessing this endpoint returns the 500 error:
Exception class freemarker.template.TemplateNotFoundException: Template not found for name "index.ftl".
Help example: https://github.com/ktorio/ktor-documentation/tree/main/codeSnippets/snippets/freemarker
The 'Migrate Ktor to latest version' intention should show some message when a project already on a latest version
Migration action preview should not consume CPU&network before appearing in actions menu
Currently migration action is only shown in Actions menu if any ktor migration is available. In order to decide upon that it does a network request to the generator backend.
Server
call.receiveText() tries to parse body as JSON when the ContentNegotiation plugin is installed
The receiveText() function previously read the body as-is without any additional steps. Starting with version 2.0.2 Ktor tries to parse it as JSON when the request sets a Content-Type header. This is not expected behavior.
In our case we use the receiveText() function to receive JSON bodies in our webhook routes and send the body to third party libraries that do the actual deserialization.
The following code can be used to reproduce the issue (using ktor 2.0.2 and kotlinx.serialization 1.3.3)
embeddedServer(Netty, port = 8080) {
install(ContentNegotiation) {
serialization(ContentType.Application.Json, Json.Default)
}
routing {
post("test") {
val body = call.receiveText()
call.respond("OK")
}
}
}.start(true)
Sending a JSON request responds with "OK" in 2.0.0 and throws an exception in 2.0.2 using the following request.
POST http://localhost:8080/test
Content-Type: application/json
{
"test": true
}
Development mode class loader leads to ClassCastException within a CouroutineScope
In a CoroutineScope of a Ktor-2.0.0-application (netty-engine), the "ClassLoaders@AppClassLoader" is not aware of Objects loaded by the (default?) Application ClassLoader. Therefore, for exampe, received objects from an (Apache-ActiveMQ-Artemis-)message-queue cannot be cast to its corresponding DTOs - see attached screenshots with Ktor-1.6.8/Kotlin-1.6.10 vs. Ktor-2.0.0/Kotlin-1.6.20.
Validate that the body of an incoming request is received completely
From time to time, we receive server requests with an incomplete body.
It seems that the cause of that is lack of `Content-Length` validation during request's body read.
In the `ServletReader` validated excess length of the body, but no assertion is made that received bodySize is equal to the `Content-Length`.
Direct byte buffers are increased in size when server slowly processes request
Hello everyone
We use ktor 2.0.0 with Netty engine
We process large tsv, tsv transmitted over http
We represent http request body as Flow<ByteBuffer>
How we read from channel
fun ApplicationCall.receiveFlow(): Flow<ByteBuffer> = flow<ByteBuffer> {
val channel = receiveChannel()
try {
while (true) {
val buffer = ByteBuffer.allocate(4096)
if (channel.readAvailable(buffer) == -1) {
break
}
buffer.flip()
emit(buffer)
}
} catch (e: Exception) {
log.error("URL: '${request.uri}' error reading body", e)
throw e
}
}
If server slowly process http request - we see increase direct byte buffers (non-heap), this buffers create netty for buffering request (buffers grows to 2 gb)
I expected back pressure in ktor, but buffers are growing.
I communicated with netty users - they recommend disable autoread at channel and use read() manually
Does ktor support back pressure? If so, how enable it?
ResponseConverter NPE when returning ByteArray with the ContentNegotiation plugin
it.converter.serialize(
contentType = contentType ?: it.contentType,
charset = acceptCharset ?: Charsets.UTF_8,
typeInfo = call.response.responseType!!, // NPE triggered by responseType
value = subject
)
Stacktrace:
java.lang.NullPointerException
at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1$1.invokeSuspend(ResponseConverter.kt:52)
at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1$1.invoke(ResponseConverter.kt)
at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1$1.invoke(ResponseConverter.kt)
at io.ktor.server.application.OnCallRespondContext.transformBody(KtorCallContexts.kt:86)
at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1.invokeSuspend(ResponseConverter.kt:23)
at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1.invoke(ResponseConverter.kt)
at io.ktor.server.plugins.contentnegotiation.ResponseConverterKt$convertResponseBody$1.invoke(ResponseConverter.kt)
at io.ktor.server.application.PluginBuilder$onDefaultPhase$1.invokeSuspend(PluginBuilder.kt:217)
at io.ktor.server.application.PluginBuilder$onDefaultPhase$1.invoke(PluginBuilder.kt)
at io.ktor.server.application.PluginBuilder$onDefaultPhase$1.invoke(PluginBuilder.kt)
at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1$1.invokeSuspend(PluginBuilder.kt:200)
at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1$1.invoke(PluginBuilder.kt)
at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1$1.invoke(PluginBuilder.kt)
at io.ktor.util.debug.ContextUtilsKt.addToContextInDebugMode(ContextUtils.kt:30)
at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1.invokeSuspend(PluginBuilder.kt:196)
at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1.invoke(PluginBuilder.kt)
at io.ktor.server.application.PluginBuilder$onDefaultPhaseWithMessage$1$1.invoke(PluginBuilder.kt)
at io.ktor.server.application.ApplicationPluginKt$addAllInterceptors$1$1$1.invokeSuspend(ApplicationPlugin.kt:167)
at io.ktor.server.application.ApplicationPluginKt$addAllInterceptors$1$1$1.invoke(ApplicationPlugin.kt)
at io.ktor.server.application.ApplicationPluginKt$addAllInterceptors$1$1$1.invoke(ApplicationPlugin.kt)
at io.ktor.util.pipeline.DebugPipelineContext.proceedLoop(DebugPipelineContext.kt:80)
at io.ktor.util.pipeline.DebugPipelineContext.proceed(DebugPipelineContext.kt:57)
at io.ktor.util.pipeline.DebugPipelineContext.execute$ktor_utils(DebugPipelineContext.kt:63)
@Test
fun test() {
testApplication {
application {
routing {
install(ContentNegotiation) {
register(ContentType.Application.Json, JacksonConverter())
}
get("/") {
call.respond("test".toByteArray())
}
}
}
client.get("/")
}
}
"No instance for key AttributeKey: ApplicationPluginRegistry" when exception is thrown during the Call phase
Step to reproduce:
- Run this code fragment:
package ru.vs.control.server
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
fun main() {
val env = applicationEngineEnvironment {
connector {
host = "0.0.0.0"
port = 8080
}
module {
intercept(ApplicationCallPipeline.Call) {
throw RuntimeException("my-unexpected-exception")
}
}
}
embeddedServer(Netty, env).start(true)
}
- Send any request to server
Expected result:
Stack trace with my exception inside
Actual result:
Internal ktor engine stack tace:
stack trace
java.lang.IllegalStateException: No instance for key AttributeKey: ApplicationPluginRegistry
at io.ktor.util.Attributes$DefaultImpls.get(Attributes.kt:62) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.AttributesJvmBase.get(AttributesJvm.kt:15) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.logging.LoggingKt.getMdcProvider(Logging.kt:36) ~[ktor-server-core-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt.logError(DefaultEnginePipeline.kt:59) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt.handleFailure(DefaultEnginePipeline.kt:53) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invokeSuspend(NettyApplicationCallHandler.kt:46) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.6.21.jar:1.6.21-release-334(1.6.21)]
at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:141) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.access$resumeRootWith(SuspendFunctionGun.kt:14) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:58) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) ~[kotlin-stdlib-1.6.21.jar:1.6.21-release-334(1.6.21)]
at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:141) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.access$resumeRootWith(SuspendFunctionGun.kt:14) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:58) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) ~[kotlin-stdlib-1.6.21.jar:1.6.21-release-334(1.6.21)]
at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:141) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:126) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.BaseApplicationEngineKt$installDefaultTransformationChecker$1.invokeSuspend(BaseApplicationEngine.kt:122) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.BaseApplicationEngineKt$installDefaultTransformationChecker$1.invoke(BaseApplicationEngine.kt) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.BaseApplicationEngineKt$installDefaultTransformationChecker$1.invoke(BaseApplicationEngine.kt) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.execute$ktor_utils(SuspendFunctionGun.kt:101) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:77) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1$invokeSuspend$$inlined$execute$1.invokeSuspend(Pipeline.kt:478) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1$invokeSuspend$$inlined$execute$1.invoke(Pipeline.kt) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1$invokeSuspend$$inlined$execute$1.invoke(Pipeline.kt) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.debug.ContextUtilsKt.initContextInDebugMode(ContextUtils.kt:17) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1.invokeSuspend(DefaultEnginePipeline.kt:118) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1.invoke(DefaultEnginePipeline.kt) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$1.invoke(DefaultEnginePipeline.kt) ~[ktor-server-host-common-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.SuspendFunctionGun.execute$ktor_utils(SuspendFunctionGun.kt:101) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:77) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1$invokeSuspend$$inlined$execute$1.invokeSuspend(Pipeline.kt:478) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1$invokeSuspend$$inlined$execute$1.invoke(Pipeline.kt) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1$invokeSuspend$$inlined$execute$1.invoke(Pipeline.kt) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.ktor.util.debug.ContextUtilsKt.initContextInDebugMode(ContextUtils.kt:17) ~[ktor-utils-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invokeSuspend(NettyApplicationCallHandler.kt:119) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:55) ~[kotlinx-coroutines-core-jvm-1.6.1.jar:?]
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:112) ~[kotlinx-coroutines-core-jvm-1.6.1.jar:?]
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) ~[kotlinx-coroutines-core-jvm-1.6.1.jar:?]
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56) ~[kotlinx-coroutines-core-jvm-1.6.1.jar:?]
at kotlinx.coroutines.BuildersKt.launch(Unknown Source) ~[kotlinx-coroutines-core-jvm-1.6.1.jar:?]
at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:37) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:29) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:61) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:370) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:995) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.ktor.server.netty.EventLoopGroupProxy$Companion.create$lambda-1$lambda-0(NettyApplicationEngine.kt:260) ~[ktor-server-netty-jvm-2.0.2.jar:2.0.2]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-common-4.1.77.Final.jar:4.1.77.Final]
at java.lang.Thread.run(Thread.java:829) [?:?]
CallLogging: JVM crashes when jansi checks whether a file descriptor refers to a terminal
стектрейс и инфо по окружению во вложении
Resources plugin fails to process parameters of type UShort
Sample code that crashes:
package com.example
import io.ktor.resources.Resource
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.resources.Resources
import io.ktor.server.resources.get
import io.ktor.server.response.respondText
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import kotlinx.serialization.Serializable
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
configureRouting()
}.start(wait = true)
}
fun Application.configureRouting() {
install(Resources)
routing {
get("/") {
call.respondText("Hello World!")
}
get<UShortTest> {
call.respondText("Res: string=${it.testString}, ushort=${it.testUShort}")
}
}
}
@Serializable
@Resource("/test-ushort")
class UShortTest(val testString: String, val testUShort: UShort)
Crash details:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -3 at kotlinx.serialization.internal.PluginGeneratedSerialDescriptor.getElementDescriptor(PluginGeneratedSerialDescriptor.kt:135) at io.ktor.resources.serialization.ResourcesFormat.collectAllParameters(ResourcesFormat.kt:76) at io.ktor.resources.serialization.ResourcesFormat.collectAllParameters(ResourcesFormat.kt:78) at io.ktor.resources.serialization.ResourcesFormat.encodeToQueryParameters(ResourcesFormat.kt:64) at io.ktor.server.resources.RoutingKt.resource(Routing.kt:186) ...
While the issue affects UShort, ULong etc, value classes like the one below seem to work fine:
@JvmInline
@Serializable
value class TestCustom(val iamstring: String)
Test Infrastructure
Support the HttpTimeout capability in the DelegatingTestClientEngine
The use case is a client makes a request in a route's handler with a timeout.
fun Application.configureRouting(httpClient: HttpClient) {
routing {
get("/test") {
httpClient.get("http://fake.site.io/toto") {
timeout {
requestTimeoutMillis = 100
}
}
call.respondText("Hello World!")
}
}
}
class ApplicationTest {
@Test
fun testRoot() = testApplication {
val myClient = createClient {
install(HttpTimeout)
}
application {
configureRouting(myClient)
}
externalServices {
hosts ("http://fake.site.io") {
routing {
get("/toto") {
call.respond(HttpStatusCode.OK)
}
}
}
}
myClient.get("/test").apply {
assertEquals(HttpStatusCode.OK, status)
}
}
}
https://kotlinlang.slack.com/archives/C0A974TJ9/p1654617495485869
Other
Limit the number of parallel running requests in Netty
Update kotlinx.coroutines to 1.6.2
Ignore SIGPIPE for server sockets
CIO engine doesn't apply a request timeout from the `HttpTimeout` plugin
Ignore ByteReadChannel as receive type in ContentNegotiation
Non-decipherable exception "No result transformation found"
When something is configured incorrectly, Ktor client launches the following exception
No request transformation found:
java.lang.IllegalStateException: No request transformation found: [Lcom.hadihariri.keolink.LoginData;@23d1e5d0
The only insight it provides is a data class. The help for this exception leads to even less information
As a user, I have no idea what I am meant to do to solve the problem. It would be good to provide some guidance.
Resources plugin doesn't respect default values for Enum
2.0.2
released 30th May 2022
Build System Plugin
Apply `application` plugin automatically
Since ShadowJar and GraalVM plugins rely on application
plugin, it's better to apply this plugin automatically when ktor plugin is applied.
`buildFatJar` task needs `mainClassName` to be set even if `mainClass` is set
We can fix it by setting mainClassName
property from application.mainClass
before running shadowJar
Tasks for publishing image create two images instead of one
publishImage
and publishImageToLocalRegistry
are affected
Client
[iOS] Prevent HttpClient from persisting cookies across requests
Hello!
I'm using Ktor in a KMM app, and I am not having any kind of problem in Android, but in iOS it behaves weird. I don't have the HttpCookies plugin installed, because I'm handling and storing the session cookie outside of Ktor.
What happens is that the very first request that is sent to the server (GET /api/session
) contains a Set-Cookie
header in the response, and for this request I don't save the cookie nor do anything with it, the request serves only to check if there is a session based solely on the response of the server. Both in Android and iOS we get the Set-Cookie
header for this request.
The second request (POST /api/session
) is made after the user attempts to log in using their credentials. For this request, we need the response to contain the Set-Cookie
header so we can store the session token it contains and then pass it onto further requests. This happens in Android, but not in iOS. I tried removing the first request, and magically, I get a cookie for this one!
Is the iOS backend automatically storing and passing the cookie without my knowledge or consent? As I said before, I didn't install the HttpCookies plugin.
Darwin engine: Client connection is closed after each request
I am using the iOS HttpClientEngine of Ktor and the connection seems to be closed after each request, even if I do not intentionally close any connection. Keep Alive header is received correctly in the server and in the Android side (using OkHtttp) the socket connection is maintained to be reused in next requests, but in the iOS side the socket connection is closed after each request.
The output from the request of the iOS App in the server (run in a local machine) is the following:
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder[26]
Attempting to validate the bound parameter 'loginRequest' of type 'ApiHosteleriaMobile.Entities.requests.LoginRequest' ...
dbug: Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder[27]
Done attempting to validate the bound parameter 'loginRequest' of type 'ApiHosteleriaMobile.Entities.requests.LoginRequest'.
info: ApiHosteleriaMobile.ParameterAdapterActionFilter[0]
[Request][][/api/v1/login/login]
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[1]
Executing action method ApiHosteleriaMobile.Controllers.LoginController.ValidateLogin (ApiHosteleriaMobile) - Validation state: Invalid
fail: HosteleriaApp.api.Repositories.LoginRepository[0]
[validateUser][ILoginRepository] ErrorContent: No se han encontrado ni operadores ni hosteleros Username:sendoa.vaz@gmail.com
fail: BaseLogger[0]
[LoginUseCase][ErrorCode=6000] Username:sendoa.vaz@gmail.com
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed action method ApiHosteleriaMobile.Controllers.LoginController.ValidateLogin (ApiHosteleriaMobile), returned result Microsoft.AspNetCore.Mvc.ObjectResult in 9317.4553ms.
dbug: Microsoft.AspNetCore.Mvc.Infrastructure.DefaultOutputFormatterSelector[11]
List of registered output formatters, in the following order: Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter, Microsoft.AspNetCore.Mvc.Formatters.StringOutputFormatter, Microsoft.AspNetCore.Mvc.Formatters.StreamOutputFormatter, Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter
dbug: Microsoft.AspNetCore.Mvc.Infrastructure.DefaultOutputFormatterSelector[6]
Attempting to select an output formatter based on Accept header 'application/json'.
dbug: Microsoft.AspNetCore.Mvc.Infrastructure.DefaultOutputFormatterSelector[2]
Selected output formatter 'Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter' and content type 'application/json' to write the response.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
Executing ObjectResult, writing value of type 'ApiHosteleriaMobile.Entities.Responses.LoginResponse'.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed action ApiHosteleriaMobile.Controllers.LoginController.ValidateLogin (ApiHosteleriaMobile) in 9455.4859ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'ApiHosteleriaMobile.Controllers.LoginController.ValidateLogin (ApiHosteleriaMobile)'
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
Connection id "0HMGRQRK5P7RA" completed keep alive response.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 9577.0655ms 200 application/json; charset=utf-8
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[6]
Connection id "0HMGRQRK5P7RA" received FIN.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
Connection id "0HMGRQRK5P7RA" sending FIN because: "The client closed the connection."
dbug: Microsoft.AspNetCore.Server.Kestrel[10]
Connection id "0HMGRQRK5P7RA" disconnecting.
dbug: Microsoft.AspNetCore.Server.Kestrel[2]
Connection id "0HMGRQRK5P7RA" stopped.
Strings are not decoded when received as application/json
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1861
Ktor Version and Engine Used (client or server and name)
io.ktor:ktor-client:1.3.2
io.ktor:ktor-client-cio:1.3.2
io.ktor:ktor-client-json:1.3.2
io.ktor:ktor-client-gson:1.3.2
Describe the bug
When requesting a simple string (e.g. foo
) from a ASP.NET Core Web-API the server returns the simple string encoded as Json string ("foo"
) with mime type application/json
.
The ktor client does not use the configured serializer (e.g. GsonSerializer
) to deserialize the response body, but rather directly return the string "foo"
with the quotes to the requesting code. This is a bigger problem when the simple string contains character which must be encoded in json strings like "
or LF. Then the string in the HTTP response look like "\"\n"
and ktor returns the string "\"\n"
(this is a string with length 6) but it should be a string with two characters, the first character a \
and the second a LF.
To Reproduce
Steps to reproduce the behavior:
- Write the following
client = HttpClient {
install(JsonFeature) {
serializer = GsonSerializer()
}
}
val simpleString = client.get<String>(someUrl)
println(simpleString)
- The server at
someUrl
must respond with a HTTP response like following:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
"foo\"\n"
- See
"foo\"\n"
printed as output
Expected behavior
only foo"
should be printed (two line breaks)
Can I logging single line?
Hi There.
I'am try to using ktor-client-logging-jvm
.
This logging module display multiple line like this and so many line.
HttpClient: REQUEST: http://localhost:8081/testPost
HttpClient: METHOD: HttpMethod(value=POST)
HttpClient: COMMON HEADERS
HttpClient: -> Accept: application/json
HttpClient: -> Accept-Charset: UTF-8
HttpClient: -> User-Agent: Apache-HttpAsyncClient/4.5.3(npay-point)
HttpClient: CONTENT HEADERS
HttpClient: -> Content-Length: 12
HttpClient: -> Content-Type: text/plain; charset=UTF-8
HttpClient: BODY Content-Type: text/plain; charset=UTF-8
HttpClient: BODY START
HttpClient: asdfasdfasdf
HttpClient: BODY END
HttpClient: RESPONSE: 502
HttpClient: METHOD: HttpMethod(value=POST)
HttpClient: FROM: http://localhost:8081/testPost
HttpClient: COMMON HEADERS
HttpClient: -> Connection: keep-alive, keep-alive
HttpClient: -> Content-Length: 11
HttpClient: -> Content-Type: text/plain;charset=UTF-8
HttpClient: -> Date: Fri, 04 Dec 2020 08:14:04 GMT
HttpClient: -> Keep-Alive: timeout=60
HttpClient: BODY Content-Type: text/plain; charset=UTF-8
HttpClient: BODY START
HttpClient: Post Result
HttpClient: BODY END
Can I logging this to single line like this?
Request : Url[http....], Headers, body, params
Response : Header, body
and if logging with elapsed time. it is very helpful.
Thank u.
HttpResponse.bodyAsChannel should not be converted by ContentNegotiation
HttpResponse.bodyAsChannel should not try to convert body to object using jackson.
Run following code, expected: no exception, actual: Caused by: io.ktor.serialization.JsonConvertException: Illegal json parameter found
import io.ktor.client.*
import io.ktor.client.engine.apache.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.jackson.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.jetty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.coroutines.runBlocking
fun main() {
embeddedServer(Jetty, 8787) {
routing {
get("/") {
call.respondText("{ }", ContentType.Application.Json, HttpStatusCode.OK)
}
}
}.apply { start() }
val client = HttpClient(Apache) {
install(ContentNegotiation) {
jackson {}
}
}
runBlocking {
val response = client.get("http://localhost:8787")
response.bodyAsChannel()
}
}
Ios: NullPointerException when query parameters contain cyrillic symbols in values
I execute a request from the application to ios and pass a string in Cyrillic to the query, I get an error when executing the query. if the string consists of Latin characters, then everything is fine. There is no such problem on the Android platform.
A native application with the Darwin engine doesn't make a request
To reproduce the issue:
- Clone documentation repo: https://github.com/ktorio/ktor-documentation
- Open the
codeSnippets
project in IDEA: https://github.com/ktorio/ktor-documentation/tree/2.0.0-kmm-tutorial/codeSnippets - Run the client-engine-darwin sample:
./gradlew :client-engine-darwin:runReleaseExecutableNative
=> The application is hanging in this state:
66% EXECUTING
> :client-engine-darwin:runReleaseExecutableNative
Version: 2.0.0-eap-327
P.S. The same example for Curl
works fine: https://github.com/ktorio/ktor-documentation/blob/2.0.0-kmm-tutorial/codeSnippets/snippets/client-engine-curl/src/nativeMain/kotlin/Main.kt
Darwin and Kotlin/JS: "List has more than one element" error when header like Content-type is duplicated in a response
When I try to fetch html and analyze it in Multiplatform Mobile share lib, I got a crash in iOS, but work well in Android.
iOS error msg: IllegalArgumentException("List has more than one element.")
URL: https://mp.weixin.qq.com/s/UJipNKgGPzZ1iPJBAaLJXw
so I compile the ktor source code and debug inside, and then I foud out the reason is that the response header "content-type" has a repeated key-value entry,As shown below:
In this case, iOS native network lib will merge them into one key-value entry like {"content-type" : "text/html; charset=UTF-8 , text/html; charset=UTF-8" } .
But on the other hand, ContentType.parse() will parse "content-type" header and get two HeaderValue, after that it invoke Colloection.single() to check the result, then a IllegalArgumentException("List has more than one element.") will be throw.
I checked the implementation of other platforms,browserjs have the same issue.
UninitializedPropertyAccessException in the handleResponseExceptionWithRequest when request or response are accessed through HttpClientCall
To reproduce run the following code:
val client = HttpClient {
expectSuccess = true
HttpResponseValidator {
handleResponseExceptionWithRequest { exception, request ->
request.call.request.url // for request.call.response.status it happens too
}
}
}
client.get("https://httpbin.org/404").bodyAsText()
As a result, an unexpected UninitializedPropertyAccessException
is thrown:
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property request has not been initialized
at io.ktor.client.call.HttpClientCall.getRequest(HttpClientCall.kt:39)
at ClientKt$main$client$1$1$1.invokeSuspend(client.kt:44)
at ClientKt$main$client$1$1$1.invoke(client.kt)
at ClientKt$main$client$1$1$1.invoke(client.kt)
at io.ktor.client.plugins.HttpCallValidator.processException(HttpCallValidator.kt:53)
at io.ktor.client.plugins.HttpCallValidator.access$processException(HttpCallValidator.kt:39)
at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invokeSuspend(HttpCallValidator.kt:128)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.access$resumeRootWith(SuspendFunctionGun.kt:14)
at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:58)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:138)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:112)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:14)
at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:62)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:178)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
at kotlinx.coroutines.ResumeAwaitOnCompletion.invoke(JobSupport.kt:1412)
at kotlinx.coroutines.JobSupport.notifyCompletion(JobSupport.kt:1519)
at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:323)
at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:240)
at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:906)
at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:863)
at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:828)
at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
The original exception is swallowed by "No request transformation found" exception when request body is serializable object
I wrote custom plugin takes care about internet connection. If it's no connection it throws an exception. On 1.6.7 it worked as expected.
I expect that exception won't be handled by ktor internal logic. Now it looks like no exception had been thrown at all.
I debug this, onNoConnection block calls as usual.
class ConnectionStatePlugin private constructor(
private val isConnected: () -> Boolean,
private val onNoConnection: () -> Unit
) {
class Config {
var checkConnection: () -> Boolean = { false }
var onNoConnection: () -> Unit = { throw Exception() }
fun build() = ConnectionStatePlugin(
isConnected = checkConnection,
onNoConnection = onNoConnection
)
}
companion object Plugin : HttpClientPlugin<Config, ConnectionStatePlugin> {
override val key = AttributeKey<ConnectionStatePlugin>("ConnectionStatePlugin")
override fun prepare(block: Config.() -> Unit) = Config().apply(block).build()
override fun install(plugin: ConnectionStatePlugin, scope: HttpClient) {
scope.requestPipeline.intercept(HttpRequestPipeline.State) {
with(plugin) {
if (!isConnected()) {
onNoConnection()
}
}
}
}
}
}
install(ConnectionStatePlugin) {
checkConnection = { connectionStateChecker.isOnline() }
onNoConnection = { throw NoInternetException() }
}
Core
Kotlin/Native: testApplication's client sometimes fails to receive ByteArray response from a route
Tested on Kotlin/Native with ktor-server-test-host:2.0.0
dependency.
I'm defining a Route that returns a ByteArray representing a JPEG image to use it as a stub for some external service that would be called by a Ktor Client.
testApplication {
routing {
imageStub(imageBytes)
}
client.get("image.jpeg")
}
private fun Route.imageStub(imageBytes: ByteArray) {
get("image.jpeg") {
call.respondBytes(ContentType.Image.JPEG) { imageBytes }
}
}
The client that is available in testApplication
block sometimes fails to complete the request with IllegalStateException:
kotlin.IllegalStateException: Expected 2305888, actual 2305632
It's reproducible on Linux and Mac. I'm attaching a sample project demonstrating the issue.
Invalid response without error
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1883
Ktor Version and Engine Used (client or server and name)
io.ktor:ktor-server-cio:1.3.2
Describe the bug
Strings like "a", "H" are invalid responses(see https://tools.ietf.org/html/rfc2616#section-6)
To Reproduce
Steps to reproduce the behavior:
Run code:
import io.ktor.http.cio.parseResponse
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.runBlocking
fun main() {
listOf("A", "H", "a")
.forEach { test(it) }
}
fun test(http: String) {
val response = runBlocking { parseResponse(ByteReadChannel(http)) }!!
println("status=${response.status};statusText={${response.statusText}};headers=[${response.headers}];version=${response.version}")
}
Expected behavior
An error expected - exception or null result.
Invalid HTTP version should fail
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1866
Ktor Version and Engine Used (client or server and name)
io.ktor:ktor-server-cio:1.3.2
Describe the bug
Request should include HTTP version(see https://tools.ietf.org/html/rfc2616#section-3.1). Also leading zeros are prohibited.
To Reproduce
Steps to reproduce the behavior:
Run code:
import io.ktor.http.cio.parseRequest
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.runBlocking
fun main() {
listOf(
"""
GET / HTTP/1.6
Host: www.example.com
""".trimIndent(),
"""
GET / HTPT/1.1
Host: www.example.com
""".trimIndent(),
"""
GET / _
Host: www.example.com
""".trimIndent(),
"""
GET / HTTP/1.01
Host: www.example.com
""".trimIndent()
).forEach {
test(it)
}
}
fun test(http: String) {
val request = runBlocking { parseRequest(ByteReadChannel(http)) }!!
println("method=${request.method};version=${request.version};uri={${request.uri}};headers=[${request.headers}]")
}
Expected behavior
An error expected - exception or null result.
The colon after the host parameter requires a port
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1864
Ktor Version and Engine Used (client or server and name)
io.ktor:ktor-server-cio:1.3.2
Describe the bug
The colon after the host parameter requires a port(see https://tools.ietf.org/html/rfc3986#section-3.2.3)
To Reproduce
Steps to reproduce the behavior:
Run code:
import io.ktor.http.cio.parseRequest
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.runBlocking
fun main() {
test(
"""
GET / HTTP/1.1
Host: www.example.com:
""".trimIndent()
)
}
fun test(http: String) {
val request = runBlocking { parseRequest(ByteReadChannel(http)) }!!
println("method=${request.method};uri={${request.uri}};headers=[${request.headers}]")
}
Expected behavior
An error expected - exception or null result.
Docs
How to disable automatic module loading within testApplication?
The documentation (https://ktor.io/docs/testing.html#auto) states that you
can disable loading modules by customizing an environment for tests
However, it does not explain how the environment exactly has to look like to disable automatic loading.
Document how to enable/disable HTTP/2 for different client engines
https://ktor.io/docs/http-client-engines.html#limitations
val javaClient = HttpClient(Java) {
engine {
config {
sslContext(SslSettings.getSslContext())
version(java.net.http.HttpClient.Version.HTTP_2)
}
}
}
val okHttpClient = HttpClient(OkHttp) {
engine {
config {
sslSocketFactory(SslSettings.getSslContext()!!.socketFactory, SslSettings.getTrustManager())
protocols(listOf(Protocol.HTTP_1_1))
}
}
}
Document a new memory model in KMM tutorial
Make client docs less JVM-centric
https://jbs.zendesk.com/agent/tickets/4049802
- https://ktor.io/docs/client-dependencies.html - mention multiplatform and common source set
- add links from topics for specific plugins
Difference between various http client properties
Can someone tell me difference between maxConnectionsCount
, maxConnectionsPerRoute
, pipelineMaxSize
in http client with CIO engine.
Also what is the role of threadsCount
with coroutines in CIO engine ? The kotlin doc inside :
maxConnectionsCount = Maximum allowed connections count.
maxConnectionsPerRoute = Maximum connections per single route.
pipelineMaxSize = Maximum number of requests per single pipeline.
threadsCount = Network threads count advice.
It is crisp, and I understand it, but I also assume here. I would like :
- A better explanation.
- Which of these properties have a relationship with another ?
- How high can these values be set, and what are the consequences of those, for example in regards to resources (like cpu, memory, file descriptors etc) and performance (like can client choke, have a big queue, make app crash, make request durations worse etc) ?
- Is there a recommended threshold/ or tuning ?
Thank you :)
Documentation about how to configure libcurl on Windows
There should be a documentation about how to configure libcurl on Windows using minGW and Cygwin to be able to use ktor-client-curl
engine.
The issue KTOR-3989 if resolved should be documented too.
Also, some information about troubleshooting common problems would be helpful.
IncorrectDereferenceException when trying to create HttpClient from background thread on iOS
After updating to Ktor 2.0.1 from 1.6.8, creating HttpClient on non main scope, e.g. like this:
CoroutineScope(Dispatchers.Default).launch {
val client = HttpClient { }
println("client $client")
}
Causes the following crash:
Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: Trying to access top level value not marked as @ThreadLocal or @SharedImmutable from non-main thread
2022-05-02 00:44:08.016506+0700 iosApp[35807:564602] [Unknown process name] copy_read_only: vm_copy failed: status 1.
at 0 iosApp 0x102f17767 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95
at 1 iosApp 0x102f1122b kfun:kotlin.Exception#<init>(kotlin.String?){} + 91
at 2 iosApp 0x102f113df kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 91
at 3 iosApp 0x102f2318b kfun:kotlin.native.IncorrectDereferenceException#<init>(kotlin.String){} + 91
at 4 iosApp 0x102f4129f ThrowIncorrectDereferenceException + 111
at 5 iosApp 0x1031d4b87 CheckGlobalsAccessible + 43
at 6 iosApp 0x10316635b kfun:io.ktor.client.plugins#<get-PLUGIN_INSTALLED_LIST>(){}io.ktor.util.AttributeKey<io.ktor.util.Attributes> + 31
at 7 iosApp 0x103148423 kfun:io.ktor.client.HttpClientConfig.install$<anonymous>_1-3#internal + 283
at 8 iosApp 0x103148c6b kfun:io.ktor.client.HttpClientConfig.$install$<anonymous>_1-3$FUNCTION_REFERENCE$22.invoke#internal + 95
at 9 iosApp 0x103148d77 kfun:io.ktor.client.HttpClientConfig.$install$<anonymous>_1-3$FUNCTION_REFERENCE$22.$<bridge-UNNN>invoke(-1:0){}#internal + 95
at 10 iosApp 0x1031475a3 kfun:io.ktor.client.HttpClientConfig#install(io.ktor.client.HttpClient){} + 699
at 11 iosApp 0x1031428c7 kfun:io.ktor.client.HttpClient#<init>(io.ktor.client.engine.HttpClientEngine;io.ktor.client.HttpClientConfig<out|io.ktor.client.engine.HttpClientEngineConfig>){} + 3167
at 12 iosApp 0x1031430ab kfun:io.ktor.client.HttpClient#<init>(io.ktor.client.engine.HttpClientEngine;io.ktor.client.HttpClientConfig<out|io.ktor.client.engine.HttpClientEngineConfig>;kotlin.Boolean){} + 135
at 13 iosApp 0x103145bf7 kfun:io.ktor.client#HttpClient(io.ktor.client.engine.HttpClientEngineFactory<0:0>;kotlin.Function1<io.ktor.client.HttpClientConfig<0:0>,kotlin.Unit>){0§<io.ktor.client.engine.HttpClientEngineConfig>}io.ktor.client.HttpClient + 635
at 14 iosApp 0x10318132b kfun:io.ktor.client#HttpClient(kotlin.Function1<io.ktor.client.HttpClientConfig<*>,kotlin.Unit>){}io.ktor.client.HttpClient + 379
at 15 iosApp 0x1031989d7 kfun:com.example.myapplication.Greeting.$<init>$lambda-1COROUTINE$0.invokeSuspend#internal + 275
at 16 iosApp 0x102f1b5eb kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 527
at 17 iosApp 0x103056893 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 2659
at 18 iosApp 0x10301460f kfun:kotlinx.coroutines.EventLoopImplBase#processNextEvent(){}kotlin.Long + 663
at 19 iosApp 0x10306cfbf kfun:kotlinx.coroutines#runEventLoop(kotlinx.coroutines.EventLoop?;kotlin.Function0<kotlin.Boolean>){} + 695
at 20 iosApp 0x103072f83 kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.start$lambda-0#internal + 295
at 21 iosApp 0x1030730a7 kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$120.invoke#internal + 67
at 22 iosApp 0x103073197 kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$120.$<bridge-UNN>invoke(){}#internal + 67
at 23 iosApp 0x102f25a43 WorkerLaunchpad + 187
at 24 iosApp 0x1031ec95b _ZN6Worker19processQueueElementEb + 1951
at 25 iosApp 0x1031ec147 _ZN12_GLOBAL__N_113workerRoutineEPv + 99
at 26 libsystem_pthread.dylib 0x1cba8a90f _pthread_start + 115
at 27 libsystem_pthread.dylib 0x1cba85b1b thread_start + 7
Sample project attached
Documentation for migration of Authentication server plugin
The AuthenticatePhase
and ChallengePhase
public phases were effectively replaced with corresponding internal hooks so users who relied on those phases are confused about migration.
https://kotlinlang.slack.com/archives/C0A974TJ9/p1651080437152879
https://kotlinlang.slack.com/archives/C0A974TJ9/p1650208125786219
Add sample for the AuthenticationChecked hook
Documentation for appending query parameters for URL in the DefaultRequest
In Ktor 1.6.8 there is the parameter
method for the HttpRequestBuilder
that adds query parameters to a request URL. In the Ktor 2.0.0, we build a request in the scope of the DefaultRequestBuilder
that doesn't have such a method so users are confused.
https://kotlinlang.slack.com/archives/C0A974TJ9/p1644019110608419
https://kotlinlang.slack.com/archives/C0A974TJ9/p1649848172192389
https://stackoverflow.com/questions/72042828/ktor-parameters-with-defaultrequest-2-x
Infrastructure
Unable to build project on Mac
Hi,
I'm unable to build the project on Mac:
*Execution failed for task ':ktor-client:ktor-client-curl:cinteropLibcurlMacosArm64'.
*
I have installed curl & ncurses.
Any idea what might be the issue?
Thanks!
IntelliJ IDEA 2021.2.4 (Ultimate Edition)
Build #IU-212.5712.43, built on December 21, 2021
Runtime version: 11.0.13+8-b1504.49 x86_64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
macOS 11.6.5
GC: G1 Young Generation, G1 Old Generation
Memory: 4094M
Cores: 16
Non-Bundled Plugins: com.intellij.nativeDebug (212.5284.19), org.jetbrains.kotlin (212-1.6.21-release-334-IJ5457.46), kotest-plugin-intellij (1.1.49-IC-2021.2.3)
Kotlin: 212-1.6.21-release-334-IJ5457.46
IntelliJ IDEA Plugin
Generator performance: make requests from IDE only if user clicks something. Otherwise -- show static (maybe outdated) page
We are getting more users and generator gets higher RPS rate which leads to memory usage growth.
Most of the requests are first-page requests from IDE (as logs show)
We need to consider limiting requests from IDE via hardcoding first page content and only make requests in background in case user really does something.
Samples
The HttpBin sample doesn't compile
To reproduce run the ./gradlew run
command in the HttpBin sample directory.
Server
When returning a String, content negotiation is ignored
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/985
Ktor Version
1.1.3
Ktor Engine Used(client or server and name)
Server - Jetty
JVM Version, Operating System and Relevant Context
Azul 10, OSX / Debian Linux
Feedback
Currently, when you respond with a string, even if you have a header of Accept=application/json
, due to fun ApplicationSendPipeline.installDefaultTransformations()
, TextContent
is always returned. This means you can't return a bare json string, ie "foo"
.
The special case of String
is handled here:
@KtorExperimentalAPI
fun PipelineContext<Any, ApplicationCall>.transformDefaultContent(value: Any): OutgoingContent? = when (value) {
is OutgoingContent -> value
is String -> {
val contentType = call.defaultTextContentType(null)
TextContent(value, contentType, null)
}
is ByteArray -> {
ByteArrayContent(value)
}
is HttpStatusCode -> {
HttpStatusCodeContent(value)
}
is URIFileContent -> {
if (value.uri.scheme == "file")
LocalFileContent(File(value.uri))
else
null
}
else -> null
}
"Application started" is never printed
We have the following block in the io.ktor.server.engine.BaseApplicationEngine
environment.monitor.subscribe(ApplicationStarted) { val finishedAt = currentTimeMillisBridge() val elapsedTimeInSeconds = (finishedAt - info.initializedStartAt) / 1_000.0 if (info.isFirstLoading) { environment.log.info("Application started in $elapsedTimeInSeconds seconds.") info.isFirstLoading = false } else { environment.log.info("Application auto-reloaded in $elapsedTimeInSeconds seconds.") } }
The first section of the if statement is never actually called because the isFirstLoading
is always false
Route's path parameters are empty when ApplicationCall.authentication is first accessed in a different ApplicationCall context
https://github.com/rasharab/KtorBugWithMonitoring
The above is a clear reproduction of this bug (with appropriate test cases to illustrate my point).
This is a regression we observed as of Ktor 2.0.0 (was working with the betas).
The gist of the bug is that if we add an MDC context for the application JWTPrincipal, that somehow causes the application call parameters to be reset during Authentication.
This bug is only present if we attempt to do the below block in Monitoring.kt
In Monitoring.kt
we have:
mdc("identityId") {
it.principal<JWTPrincipal>()?.subject ?: "No subject"
}
In Security.kt
authentication {
jwt("bearerAuth") {
verifier(
...
)
validate { credential ->
val teamId = this.parameters["teamId"]
if (teamId == null) {
println("TeamID is unexpectedly null!")
throw Exception("TeamID is null when it should't be")
}
if (credential.payload.audience.contains("audience")) JWTPrincipal(credential.payload) else null
}
}
}
The teamId parameter should always be populated based off the below route, but it isn't when monitoring the JWTPrincipal is enabled.
Our routing is very simple:
fun Application.configureRouting() {
routing {
authenticate("bearerAuth") {
get("/teams/{teamId}") {
call.respondText("Hello World!")
}
}
}
}
Resources: builder methods return routes with PathSegmentConstantRouteSelector instead of HttpMethodRouteSelector
The Locations plugin's functions which add routes with methods (e.g. get<MyDataClass> { ... }
do not return the correct route.
Expected: they should return the route whose selector is the GET method selector (similar to the regular non-Locations get
).
Actual: They return the parent of the expected route, meaning that we get a route that matches only the path and not the method.
This is a problem for libraries that rely on the returned route to do other things (in the case of a library I author, Koa, I need the returned route to provide all the information of a path to turn it, via a DSL, into an OpenAPI Operation). The problem comes from these lines (and their equivalents with other HTTP methods): https://github.com/ktorio/ktor/blob/809ebf183c12fb580fdbfa33bf6c13422f4fe19b/ktor-server/ktor-server-plugins/ktor-server-locations/jvm/src/io/ktor/server/locations/Location.kt#L82-L91 which should return the result of method(HttpMethod.Get)
instead of returning the return value of location
.
Shared
JacksonWebsocketContentConverter.deserialize just doesn't work
In io.ktor:ktor-serialization-jackson:2.0.0
, method JacksonWebsocketContentConverter.deserialize
is not working at all. This prevent receiving anything with receiveDeserialized
in a websocket session.
Turns out, after a quick inspection the problem comes from line 37 where the frame is decoded into a string.
return withContext(Dispatchers.IO) {
val data = charset.newDecoder().decode(buildPacket { content.readBytes() })
objectmapper.readValue(data, objectmapper.constructType(typeInfo.reifiedType))
}
Here, buildPacket
expects a lambda in the context of BytePacketBuilder
but no builder method is called. content.readBytes()
throws the ByteArray
into the wind and the ByteReadPacket
is always ByteReadPacket.Empty
. The ObjectMapper will then always fail :
2022-04-28 17:10:55.132 [eventLoopGroupProxy-3-3] ERROR ktor.application - Websocket handler failed
io.ktor.serialization.JsonConvertException: Illegal json parameter found
at io.ktor.serialization.jackson.JacksonWebsocketContentConverter.deserialize(JacksonWebsocketContentConverter.kt:41)
at io.ktor.serialization.jackson.JacksonWebsocketContentConverter$deserialize$1.invokeSuspend(JacksonWebsocketContentConverter.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.ktor.server.netty.EventLoopGroupProxy$Companion.create$lambda-1$lambda-0(NettyApplicationEngine.kt:263)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
at [Source: (String)""; line: 1, column: 0]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4765)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4667)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
at io.ktor.serialization.jackson.JacksonWebsocketContentConverter$deserialize$2.invokeSuspend(JacksonWebsocketContentConverter.kt:38)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:39)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
This can be fixed by calling writeFully(content.readBytes())
or by rewriting JacksonWebsocketContentConverter.deserialize
in a similar fashion to KotlinxWebsocketSerializationConverter
.
Test Infrastructure
Test fails when using externalServices block
This test will fail when /src/main/resource∕application.conf
has an unnsatisfied substitution {SOME_ENV}
even if the test is set to use a file src/test/resources/application-test.conf
. Adding the externalServices
block is was causing the behavior of looking for /src/main/resource∕application.conf
in the test.
I would expect the file given for the test is the one that should be requiered
@Test
fun testAuth() = testApplication {
environment {
config = ApplicationConfig("application-test.conf")
}
externalServices {
//For some reason this one requiers the original config from resources
hosts("http://example.com"){
}
}
Other
Update Jackson to 2.13.3
Revert Dokka to 1.6.10 due to Publication Freeze
Update Netty to 4.1.77.Final
Default request without explicit port sets port 80 for all requests
after upgrading from 2.0.0 to 2.0.1 basically every request causes an exception, what i'm seeing is that requests now append the port in every url:
2.0.0:
https://my-website.com/v2/api
2.0.1:
https://my-website.com:80/v2/api
API Docs reference RFCs. Better to reference our own documentation
Many plugins reference RFCs, such as the one for ForwardedHeaders or AuthScheme, and yet it would make sense to reference our own documentation (which in turn actually references RFCs'). This should be applied consistently across the entire code base.
Routes with tailcard should not count for specific http error codes
Routing returns 405 even for not completely matched paths
2.0.1
released 28th April 2022
Build System Plugin
Put Gradle tasks to Ktor group
Client
Explanation on request, connection, and socket missing in KDoc
I just tried to refresh my memory on what exactly request
, connect
, and socket
timeouts mean.
However, the in-code documentation for the variables did not yield any additional information:
DefaultRequest: HTTPS protocol isn't set when using Ktor 2.0.0
I'm trying to make secure request(https).
HttpClient(OkHttp) {
defaultRequest {
url {
protocol = URLProtocol.HTTPS
}
}
Logger shows me request starts with http. Version 1.6.7 is okay, didn't face this issue before. Am I wrong? What is actually works is to set protocol in HttpRequestBuilder.
DefaultRequest: host and port aren't used for a request
I'm creating a client with defaultRequest
:
val client = HttpClient {
install(WebSockets)
defaultRequest {
port = portVal
host = hostVal
}
}
And connecting to the web socket only by path:
client.webSocket("webSocket")
It was working fine until 2.0.0, and after update it fails with the following exception, both with CIO and OkHttp clients:
Exception in thread "main" java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:777)
at io.ktor.network.sockets.SocketImpl.connect$ktor_network(SocketImpl.kt:50)
at io.ktor.network.sockets.SocketImpl$connect$1.invokeSuspend(SocketImpl.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:39)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Connecting using full URL work fine:
client.ws("ws://$hostVal:$portVal/$route")
Full code of both server and client: MRE.zip. Same client works fine after downgrading to 1.6.8
Raw Web Socket Connection Suspending Forever
I am attempting to make use of a raw web socket in a client application. This was working fine in 1.6.8 but stopped working when I bumped my version to 2.0.0 (I did not see any breaking changes listed in the release notes that should be related). Essentially the code in my ClientWebSession is never being executed and the client.wsRaw(...) { ... }
call appears to be suspending forever. I wrote the following test that should reproduce what I am seeing:
@Test
fun bugReport() {
embeddedServer(Netty, port = 8080) {
install(io.ktor.server.websocket.WebSockets)
routing {
webSocket("/ws") {
send(Frame.Text("hi"))
for (frame in incoming) { send(frame) }
}
}
}.start(wait = false)
val client = HttpClient(CIO) {
install(WebSockets)
}
runBlocking {
client.wsRaw(path = "/ws", port = 8080) {
// None of this is executed
println("Client Session Started")
incoming.consumeAsFlow().collect { println(it) }
}
}
}
None of the ClientWebSession block runs and none of the println(..)s execute. It seems like client.wsRaw
is just suspending forever (the code does not complete either).
However this code works fine if you simply switch wsRaw
for ws
.
@Test
fun bugReportWorking() {
embeddedServer(Netty, port = 8080) {
install(io.ktor.server.websocket.WebSockets)
routing {
webSocket("/ws") {
send(Frame.Text("hi"))
for (frame in incoming) { send(frame) }
}
}
}.start(wait = false)
val client = HttpClient(CIO) {
install(WebSockets)
}
runBlocking {
client.ws(path = "/ws", port = 8080) {
// This works fine
println("Client Session Started")
incoming.consumeAsFlow().collect { println(it) }
}
}
}
I get
Client Session Started
Frame TEXT (fin=true, buffer len = 2)
as expected.
Furthermore the wsRaw code works as expected on 1.6.8 which is what led me to thinking that this could be a regression vs user error.
Combination of HttpCache and Logging plugins cause receiving incomplete response body for chunked replies
To reproduce run the following code:
suspend fun main() {
val client = HttpClient(Apache) {
install(Logging) {
level = LogLevel.ALL
}
install(HttpCache)
install(ContentNegotiation) {
json()
}
}
while (true) {
val r = client.get {
url("http://localhost:3333/")
}.bodyAsText()
if (r.length < 5000) {
println(r)
break
}
}
}
fun main() {
val source = ByteArray(5000) { 'k'.code.toByte() }
val chunks = String(source).chunked(256)
embeddedServer(Netty, port = 3333) {
routing {
get("/") {
call.respondTextWriter(contentType = ContentType.Application.Json) {
chunks.forEach { chunk ->
write(chunk)
}
}
}
}
}.start()
}
As a result, eventually only some part of a response body will be returned instead of expected full response body.
In Ktor 1.6.7 this works as expected.
Core
"IllegalStateException: Operation is already in progress" when the readByte is called the second time after a timeout
Hi, I am trying to use the Ktor 2.0.0 raw sockets for an Android app that is acting as a TCP server.
I bind the address/port and get a server socket without an issue as described here. I then obtain a ByteReadChannel
using Socket.openReadChannel()
.
I am reading binary messages over the connection and I have a need to timeout when receiving the message bytes in case an incomplete message is sent. I am reading the message one byte at a time using ByteReadChannel.readByte()
and, since ByteReadChannel
doesn't seem to have any timeout feature, I wrapped the reading loop in a withTimeout
block so that the timeout will naturally occur while readByte
is suspended.
The problem is that, when I try to readByte()
again on the same ByteReadChannel
after the timeout occurs, I get an IllegalStateException: Operation is already in progress
and it seems that the channel can no longer be read. I also tried closing the channel using ByteReadChannel.cancel()
and then getting a new channel using Socket.openReadChannel()
again. However, this results in an IllegalStateException: reading channel has already been set
, implying that you can only ever get one ByteReadChannel
for the socket. So it seems that this results in an unusable state that seems to have no resolution without closing the entire connection.
Is this an unexpected behavior (i.e. a bug)? If not, is there some other way I can accomplish a timeout with readByte()
instead of it just suspending indefinitely? Here is a suspending function that reproduces this issue:
fun startServer(port: Int) = scope.produce<Unit>(Dispatchers.IO) {
while (true) {
val serverSocket = try {
aSocket(ActorSelectorManager(coroutineContext)).tcp()
.bind("0.0.0.0", port)
} catch (e: Exception) {
break
}
var socket: Socket? = null
try {
socket = serverSocket.accept()
val inputByteChannel = socket.openReadChannel()
while (isActive) {
val stringBytes = mutableListOf<Byte>()
try {
withTimeout(500) {
while (true) {
val byte = inputByteChannel.readByte()
if (byte == 0.toByte()) {
// Message would be terminated with a 0 byte
break
} else {
stringBytes.add(byte)
}
}
}
// Here we would do whatever with the complete message
} catch (e: TimeoutCancellationException) {
//inputByteChannel.cancel(null)
//inputByteChannel = socket.openReadChannel()
}
}
} catch (e: Exception) {
if (e is CancellationException) {
// Cancelled so propagate
throw e
} else if (e is ClosedReceiveChannelException) {
// Broken connection
} else {
// Unexpected exception
}
} finally {
// Close resources
socket?.dispose()
serverSocket.dispose()
// It seems that if we immediately loop around and try to bind the port again, we
// often get a "Address already in use" exception. This delay allows some time
// for the port to be released before looping back, which seems to work well.
delay(200)
}
}
}
Docs
Documentation: files("css") should be resources("css") ?
I feel like there is a typo here? Otherwise, I don't know what files("css") is referring to.
Update code for editing an article in the 'Interactive website' tutorial
Behaviour of ApplicationEngine start method not documented properly
The wait
parameter of the start
method states that
wait - if true, this function does not exit until application engine stops and exits
Mentioned here: https://api.ktor.io/1.5.2/io.ktor.server.engine/-application-engine/start.html
Does it mean when wait
is false
, the method will exit before the ApplicationEngine stops. Would this mean it only handles the first request made to it?
Generator
A newly created project with specific plugins doesn't compile
To reproduce create a new project via the wizard with the Ktor 2.0.1 and the following plugins: Authentication, JWT, Static Content, WebJars, Cors, HttpsRedirect, HTML DSL, GSON, WebSockets. As a result, the project doesn't compile:
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/HTTP.kt: (10, 5): Not enough information to infer type variable B
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/HTTP.kt: (10, 13): Unresolved reference: CORS
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/HTTP.kt: (19, 5): Not enough information to infer type variable B
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/HTTP.kt: (19, 13): Unresolved reference: HttpsRedirect
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/HTTP.kt: (21, 9): Variable expected
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/HTTP.kt: (23, 9): Variable expected
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/Security.kt: (17, 31): 'val environment: ApplicationEnvironment' can't be called in this context by implicit receiver. Use the explicit one if necessary
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/Security.kt: (18, 21): 'val environment: ApplicationEnvironment' can't be called in this context by implicit receiver. Use the explicit one if necessary
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/Security.kt: (23, 33): 'val environment: ApplicationEnvironment' can't be called in this context by implicit receiver. Use the explicit one if necessary
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/Serialization.kt: (11, 5): Not enough information to infer type variable B
e: /Users/Aleksei.Tirman/projects/ktor-sample6/src/main/kotlin/com/example/plugins/Serialization.kt: (11, 13): Unresolved reference: ContentNegotiation
IntelliJ IDEA Plugin
A table with test methods should span the entire width of the dialog
IntelliJ IDEA 2022.1 Beta (Ultimate Edition)
Server
Locations: Support trailing /
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/25
At the moment you cannot create hrefs from a location with trailing /:
/somepath/
as it would end up being /somepath
This should of cause also work for nested Locations, so i could do:
@location("/somepath/") class SomeLocation{
@location("/nested/") class Nested
}
and get /somepath/
and /somepath/nested/
embeddedServer for CIO and Netty inconsistency
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/543
CIO and Netty behave differently for simple hello-server (e.g. netty-embedded
from samples)
embeddedServer(Netty, commandLineEnvironment(args)).start()
// Works
embeddedServer(CIO, commandLineEnvironment(args)).start()
// Application exits right after start
call
Even though one may use start(wait = true)
, it would be nice for CIO
to be a drop-in replacement for Netty
Resources: Make `Route.handle` public
@PublishedApi
internal fun <T : Any> Route.handle(
serializer: KSerializer<T>,
body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit
)
This is currently internal, but published API.
Usecase: I want to implement a typed routing plugin, with typed body and response (rpc). I would like to reuse your existing handle
function.
StatusPages plugin does not handle most specific exception in Ktor 2.0.0
In Ktor 2.0, the StatusPages plugin does not match the most specific exception class when one is thrown, which was guaranteed by the documentation in Ktor 1.6 ("Each call is only caught by a single exception handler, the closest exception on the object graph from the thrown exception.") but is seemingly absent in Ktor 2.0's documentation and implementation.
Here is the code of the Ktor 1.6 version: https://github.com/ktorio/ktor/blob/9ca85c50e297a2c0a124f11f1c1a453909417ecf/ktor-server/ktor-server-core/jvm/src/io/ktor/features/StatusPages.kt#L115
Here is the new code for Ktor 2.0: https://github.com/ktorio/ktor/blob/d2f79c55b8e73f0df93bf166892e3f8c362a51f2/ktor-server/ktor-server-plugins/ktor-server-status-pages/jvmAndNix/src/io/ktor/server/plugins/statuspages/StatusPages.kt#L31
It should be noted that the changelog does not mention this breaking change in any way.
StatusPages: SerializationException isn't handled when CallID plugin is installed after StatusPages plugin
Hi there,
I recently started a new project with Ktor and I encountered a strange behavior since 2.0.0.
Context
The application is a REST API using kotlinx serialization as JSON de/serializer. I'm making use of the StatusPages plugin to handle unwanted exceptions like SerializationException or statuses.
Behavior on 2.0.0-beta-1
When a SerializationException
occurs, the StatusPages
plugin handles it as expected (which in my case means sending back a 400).
Behavior on 2.0.0
When a SerializationException
occurs, the StatusPages plugin does not handle a CallFailed
event but a ResponseBodyReadyForSend
. The content is an HttpStatusCodeContent
with a 404.
Investigation
I tracked down the exception until reaching the CallId
plugin which has a hook on CallFailed
but does not consume the call since it is not a RejectedCallIdException
. I then loose track of it but the CallFailed
hook of the StatusPages
plugin does not get called.
The 404 comes from the Fallback
hook of the BaseApplicationEngine
because the response's status is null and this is not a routing failure.
Workaround
By declaring the StatusPages
plugin after the CallId
plugin, the problem is gone.
The workaround is doing great without impacts for now but maybe others plugins have bad interactions between them.
ConditionalHeaders cause the Last-Modified header appears twice in a response (2.0.0)
package com.example
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.conditionalheaders.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.time.ZonedDateTime
// Gradle implementation
/*
dependencies {
implementation "io.ktor:ktor-server-conditional-headers:$ktor_version"
implementation "io.ktor:ktor-server-core:$ktor_version"
implementation "io.ktor:ktor-server-netty:$ktor_version"
implementation "ch.qos.logback:logback-classic:$logback_version"
testImplementation "io.ktor:ktor-server-tests-jvm:$ktor_version"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
}
*/
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
install(ConditionalHeaders) { }
routing {
get("/") {
val lastModified = ZonedDateTime.now()
call.response.lastModified(lastModified)
call.respondText("Hello World!")
}
}
}.start(wait = true)
}
Response heders:
Last-Modified: Thu, 14 Apr 2022 11:44:49 GMT
Last-Modified: Thu, 14 Apr 2022 11:44:49 GMT
Content-Length: 12
Content-Type: text/plain; charset=UTF-8
Connection: keep-alive
If comment install(ConditionalHeaders) { } response is as expected, Last-Modified appears only once as it should.
DefaultHeaders: The Server header appears twice in a response (2.0.0)
install(DefaultHeaders) {
header(HttpHeaders.Server, "BRS")
header("X-Content-Type-Options", "nosniff")
}
...
call.response.lastModified(lastModified)
Response look like:
HTTP/1.1 200 OK
<span style="color:dodgerblue;">**Last-Modified: Tue, 12 Apr 2022 19:06:45 GMT**</span>
Expires: Tue, 12 Apr 2022 20:06:45 GMT
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
Date: Tue, 12 Apr 2022 19:06:45 GMT
**Server: BRS**
X-Content-Type-Options: nosniff
**Server: Ktor/debug**
<span style="color:dodgerblue;">**Last-Modified: Tue, 12 Apr 2022 19:06:45 GMT**</span>
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
transfer-encoding: chunked
Server and Last-Modified appears twice in response header with Ktor v2.0.0
Test Infrastructure
Testing: Resolving a substitution to a value in default config fails when custom HOCON config is used
We use an application.conf which dynamically resolves a lot of its variables from the environment with directives like host = ${MY_HOST}
. In tests we define a custom ApplicationConfig which resolves these env placeholders in case the environment variables are not set (i.e. when using IDEA).
In Ktor 2.0 (beta-1) I thought I could make this work with something like the following:
testApplication {
environment {
this.config = MyAppConfig()
}
}
However this does not work since TestApplicationBuilder
will force loading of application.conf via DefaultTestConfig()
with no way to skip this process. It'd be real nice if ktor only does automatic loading if no manual config is provided (like in a fallback getter).
It's not even possible to create your own TestApplication
to work around this issue - the provided builder block also is only called after TestApplicationBuilder
has already tried loading the application.conf (and of course crashes on it).
Other
A test with MultiPartFormData using a new API (testApplication) hangs
- Open the https://github.com/ktorio/ktor-documentation/tree/2.0.0/codeSnippets project.
- Open the
upload-file
sample and go to the test: https://github.com/ktorio/ktor-documentation/blob/2.0.0/codeSnippets/snippets/upload-file/src/test/kotlin/UploadFileTest.kt - Run the
testUpload
test.
=> A test hangs and doesn't return the result
P.S. The same code works fine in the client.
Ktor version: 2.0.0-eap-355
Fix URL representation
Update Coroutines to 1.6.1
Fix CURL flaky initialization
Optimize Slow Native Tests
Print Native Stacktrace on Timeout
Update Kotlin to 1.6.21
CORS plugin should be route scoped
HttpClient.wss defaults to port 80 instead of 443
The following code snippet worked using Ktor 1.6.x (i.e. it correctly led to a 404 error being reported):
suspend fun main() {
val client = HttpClient(CIO) {
install(WebSockets)
}
client.wss(
method = HttpMethod.Get,
host = "rueckgr.at",
path = "/websocket"
) {
/* do stuff */
}
}
However, it stopped working after migrating to Ktor 2.0.0, throwing this exception:
Exception in thread "main" java.lang.IllegalArgumentException: Invalid TLS record type code: 72
at io.ktor.network.tls.TLSRecordType$Companion.byCode(TLSRecordType.kt:27)
at io.ktor.network.tls.ParserKt.readTLSRecord(Parser.kt:19)
at io.ktor.network.tls.ParserKt$readTLSRecord$1.invokeSuspend(Parser.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:39)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
If I explicitely request port 443
to be used, the above IllegalArgumentException
goes away:
suspend fun main() {
val client = HttpClient(CIO) {
install(WebSockets)
}
client.wss(
method = HttpMethod.Get,
host = "rueckgr.at",
port = 443,
path = "/websocket"
) {
/* do stuff */
}
}
This means that for HttpClient.wss
the port does not default to 443
anymore as it was the case when using Ktor 1.6.x.
Missing subject parameter in StatusPages `status` config method
2.0.0
released 11th April 2022
Build System Plugin
Gradle Plugins fails to publish with no tags provided
Also it would be good to allow publishing to the local repository instead of Gradle Plugin Portal.
CLI Generator
Support macOs M1 in CLI generator
Add colors to CLI client
We need to separate messages with line breaks and colors (for instance, red for critical errors, and greed for success messages)
Client
Can't set a base url that includes path data
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/684
My team is trying to move from using OkHttp to using ktor as our HttpClient. We've found this difficult because there doesn't seem to be a way to set the default base url.
If we have the following requests:
https://www.example.com/api/getA/
https://www.example.com/api/getB/
https://www.example.com/api/getC/
It's common practice to have the ability to set the base url for the http client as https://www.example.com/api/
, and then only specify the suffix of the path (/getA/
, /getB/
, /getC/
) at the actual request site.
I like the way you can create a defaultRequest
when creating the http client with ktor and then intuitively override what's needed at the request site, however this functionality needs to be extended to allow the inclusion of path data in the base url. This functionality is available in OkHttp and also Retrofit. Otherwise it requires significant refactoring in our code-base when switching between environments, as base urls and base paths often change between environments. I imagine this will be a common problem.
If somehow this is already possible, it's not clear that it is, and in which case, this should be documented better.
Client: DefaultRequest apply defaults before request builder
Currently the default request plugin is applied AFTER the request builder.
val client = HttpClient {
defaultRequest {
header("Foo", "Bar")
}
}
client.get<String>("https://google.com") {
header("Foo", "42")
}
Expected
header[Foo] == "42"
[client] MPP WebSockets client
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/556
Since client is MPP now, it'll be great if websocket support also ported to MPP. Now it's jvm-only since http-cio is jvm module.
JS Client doesn't support ServiceWorker
Can't send request from ServiceWorker, it fails with `require is not defined`
ServiceWorker scope doesn't have the window object.
This is a suggestion for service worker support: https://github.com/ktorio/ktor/pull/2696
Ktor IOS client sometimes deadlocks if Logging is installed
If the Logging plugin is installed the Ktor client deadlocks/blocks sometimes after BODY START
is printed to the console.
The response is still getting returned back to the calling function, but any future requests will block.
It doesn't happen every time, but it is reasonably easy to reproduce it with the attached Sample app.
HttpClient breaks permanently when certain exceptions occur while consuming ByteReadChannel
When data for a HttpClient response is streamed using ByteReadChannel, exceptions thrown while the channel is read may cause the client to break, so that all subsequent requests with this client instance fail. In particular, I'm having issues when the client encounters truncated HTTP responses using chunked encoding, causing TruncatedChunkException
with the Apache engine and EOFException
with the CIO engine. I would expect such malformed HTTP responses to affect the current request only, but here all subsequent requests with the same client also throw the original exception. I can only recover by instantiating a new client.
The API in question is the following:
httpClient.request<HttpStatement>(url)
.execute { response ->
val receiveChannel = response.receive<ByteReadChannel>()
// Consume the channel, e.g. like this
receiveChannel.copyAndClose(destinationChannel)
}
It's taken quite some time to understand this behaviour as it occurs very sporadically in our production (it likely gets triggered by network failures), but I have finally been able to reproduce it locally by setting up a TCP server to respond with truncated HTTP.
Minimal reproducible example:
Gist: https://gist.github.com/daniesso/de5912ef2f13b87a5324c4b8e3c17410
Runnable repo: https://github.com/daniesso/Ktor-HttpClient-chunked-encoding
Platform: JVM
Ktor version: 1.6.3
OS: MacOS
Engine: Tested with both CIO and Apache
ContentNegotiation: the plugin removes Content-Type header even when a matching registration is not found
To reproduce run the following code:
suspend fun main() {
val client = HttpClient(Apache) {
install(ContentNegotiation) {
json()
}
}
val r = client.post("https://httpbin.org/post") {
contentType(ContentType.Image.JPEG)
setBody(ByteArray(1024))
}.bodyAsText()
println(r)
}
As a result, the Content-Type
header with the value application/octet-stream
will be sent instead of expected image/jpeg
.
In the Ktor 2.0.0 the Content-Type
header is removed without checking for a matching registration.
ContentNegotiation plugins don't accept null-responses from ContentConverts
Issue created to attach to an already-existing github PR: https://github.com/ktorio/ktor/pull/2660
Description copied below:
Subsystem
Client
Motivation
The ContentConverter
documentation as well as the server implementation of ContentNegotiation
both specify that ContentConverter::(de)serialize
should return null when the converter is not suitable for the current content-type/body. This is to allow other registrations / converters to (de)serialize the content.
The client implementation of ContentNegotiation
only tried (de)serialization with the first matching registration.
Solution
Instead of only (de)serializing with the first matching registration, this PR changes the behaviour to filter out matching registrations and try (de)serialization on each until one succeeds.
Fix `testErrorHandling` with JS
testErrorHandling
fails on JS with assertFailsWith
.
HttpCookies: parse / in the name of a cookie
I am receiving a cookie looking like that 384f8bdb/sessid=GLU787LwmQa9uLqnM7nWHzBm; path=/
.
Using the HttpCookies feature, i want ktor to send the 384f8bdb/sessid=GLU787LwmQa9uLqnM7nWHzBm
for all subsequent requests. Unfortunately, the cookie is parsed in a way that ignore everything after 384f8bdb
.
I've tracked down the issue to this point:
(commonMain/io/ktor/http/Cookie.kt)
@ThreadLocal
private val clientCookieHeaderPattern = """(^|;)\s*([^()<>@;:/\\"\[\]\?=\{\}\s]+)\s*(=\s*("[^"]*"|[^;]*))?""".toRegex()
When I check the regex against the cookie, it gives the following results:
Problem with that is, the cookie storage only save 384f8bdb=""
so the session id is never sent to the server in the following requests.
Is there any workaround for that (using the HttpCookies feature) ?
Is that the wanted behavior for ktor ?
Thanks
HttpResponseValidator.handleResponseException should have access to request to provide valuable information in exceptions
I'm mapping a common Exception format in our app, but currently there is no way to get the request in the handleResponseException
method. We just have access to the exception.
This limits my ability to pass information back about the exception completely. When I get a crash report on iOS or even on Android sometimes, I'm left with no information about where this crash is coming from (as it's an asyncronous request, and the caller isn't in the callstack). If I had the ability to at least include information about what request is failing, I could track down everywhere this is being used and better find the crash. As it is now, I've got almost no information to go off of.
Ktor http client with java engine uses incorrect timeout.
When Ktor creates a java.net.http.HttpRequest
it uses HttpTimeout.socketTimeoutMillis
to set HttpRequest.timeout
. But in java docs it's clear that HttpRequest.timeout
sets timeout for whole request, not for time without any data in a socket, so it's better to use HttpTimeout.requestTimeoutMillis
for that.
Place in ktor code where ktor sets HttpRequest.timeout
HttpClient request hangs when Logging plugin is installed
The HTTP Client gets stuck in the following configuration:
- The
Logging
plugin is installed, withlevel=LogLevel.ALL
- HTTP request body as
OutputStreamContent
, either directly or viaContentNegotiation
withJackson
- This uses the Blocking IO channels bridge
- The request body is greater than
io.ktor.utils.io.BYTE_BUFFER_CAPACITY
(4088 bytes)
The Logging
plugin splits the request body on two separate Channels, one for doing the logging and the other to be passed to the HTTP engine for request execution.
When splitting the request body, new coroutines are started -- but these use Dispatchers.Unconfined
, therefore will continue execution on the same thread. At the same time, OutputAdapter
(used by JacksonConverter
) will park this thread when content cannot be further flushed/it hasn't been consumed (when the buffer is full). This doesn't give chance to the copy coroutines (started via OutgoingContent.WriteChannelContent.toReadChannel
and ByteReadChannel.copyToBoth
) to consume the upstream channel.
One (naive) solution could be to use a different dispatcher for the copy operation, to ensure this could progress when blocking adapters (like OutputStreamContent)
are used.
// in ObservingUtils.kt
private val executor = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private fun OutgoingContent.WriteChannelContent.toReadChannel(): ByteReadChannel =
GlobalScope.writer(executor) {
writeTo(channel)
}.channel
I've attached a test, with two scenarios: ~3k and ~6k of text being serialized in a simple JSON with Jackson. The test with 6k
scenario hangs indefinitely.
Native websocket client support
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1215
Guys, Do you have any plans to support ktor-http-cio/posix/src/io/ktor/http/cio/websocket?
There is the nice one library https://github.com/korlibs/korio, thanks Carlos.
But now this library is not supported for 1.3.30+ kotlin. And I think we should use best api for this one, like in ktor.
Change visibility from internal to public to HttpResponse and HttpClientCall
Allowing creating fakes of those classes can help to improve some use cases related to testing and/or creating of some Ktor plugins.
Context: https://kotlinlang.slack.com/archives/C0A974TJ9/p1646239677040239
ResponseObserver does not respect MDC context
When logging response from. Ktor-client Logging
feature with the help of ResponseObserver
, the log does not contains the MDC context.
This mean when tracking all logs bound to an MDC context, response content log remains untracked.
This test illustrates the mentioned scenario :
in package [jvmTest]io.ktor.client.features.logging
, in file LoggingTest.kt
@Test
fun loggingWithMDCContextTest(): Unit = runBlocking {
val messages: MutableList<String> = mutableListOf()
val testLogger: Logger = object : Logger by LoggerFactory.getLogger("ktor.test") {
override fun trace(message: String?) = add("TRACE: $message")
override fun debug(message: String?) = add("DEBUG: $message")
override fun info(message: String?) = add("INFO: $message")
private fun add(message: String?) {
if (message != null) {
val mdcText = MDC.getCopyOfContextMap()?.let { mdc ->
if (mdc.isNotEmpty()) {
mdc.entries.sortedBy { it.key }
.joinToString(prefix = " [", postfix = "]") { "${it.key}=${it.value}" }
} else {
""
}
} ?: ""
messages.add(message + mdcText)
}
}
}
val localTestLogger: io.ktor.client.features.logging.Logger = object : io.ktor.client.features.logging.Logger {
override fun log(message: String) {
testLogger.info(message)
}
}
val client: HttpClient = HttpClient {
install(Logging) {
level = LogLevel.ALL
logger = localTestLogger
// responseCoroutineContext = MDCContext() This is to provide CoroutineContext from feature config
}
}
MDC.put("trace_id", "48b49e15-f7c4-4991-9e33-23850a218718")
val response: String = withContext(MDCContext()) {
client.get("https://jsonplaceholder.typicode.com/todos/1")
}
MDC.clear()
assertNotNull(messages)
assertNotNull(response)
}
Theory : First I suspected that this is because of EmptyCoroutineContext
being used in launch
, but when I gave MDCContext()
from kotlinx-coroutines-slf4j
to it, it still didn't work. Now I suspect, it is because function is provided as a predicate. However, I am not too sure about it. Can you help please ?
Change I tested :
scope.launch(feature.coroutineContext) {
try {
feature.responseHandler(sideCall.response)
} catch (_: Throwable) {
}
val content = sideCall.response.content
if (!content.isClosedForRead) {
content.discard()
}
}
where ResponseObserver = public class ResponseObserver(private val responseHandler: ResponseHandler, private val coroutineContext: CoroutineContext = EmptyCoroutineContext)
Elements of messages :
INFO: REQUEST: https://jsonplaceholder.typicode.com/todos/1 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: METHOD: HttpMethod(value=GET) [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: COMMON HEADERS [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> Accept: */* [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> Accept-Charset: UTF-8 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: CONTENT HEADERS [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> Content-Length: 0 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: BODY Content-Type: null [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: BODY START [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: BODY END [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: RESPONSE: 200 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: METHOD: HttpMethod(value=GET) [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: FROM: https://jsonplaceholder.typicode.com/todos/1 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: COMMON HEADERS [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> access-control-allow-credentials: true [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> age: 5692 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> cache-control: max-age=43200 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> cf-cache-status: HIT [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> cf-ray: 63a05ebc7fdf10f3-CPH [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> cf-request-id: 09381989cb000010f38b157000000001 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> content-type: application/json; charset=utf-8 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> date: Sat, 03 Apr 2021 06:51:43 GMT [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> etag: W/"53-hfEnumeNh6YirfjyjaujcOPPT+s" [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> expires: -1 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> nel: {"max_age":604800,"report_to":"cf-nel"} [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> pragma: no-cache [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> report-to: {"group":"cf-nel","endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=39vBUwnOjgmuV0yQJPiunD1hXHE28ku4%2FDq55GSjP%2B66ufTXnkgV1a%2F2H7DHSc28QRc1OiJA6k%2BKKXceS0z%2BaAFzxTU%2FuDpZCeBrgceQ7gvyxUQ45qbyYIa4sg2x"}],"max_age":604800} [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> server: cloudflare [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> set-cookie: __cfduid=dbe3ed123d14f8db7af7d7697a9f4049f1617432703; expires=Mon, 03-May-21 06:51:43 GMT; path=/; domain=.typicode.com; HttpOnly; SameSite=Lax [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> vary: Origin, Accept-Encoding [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> via: 1.1 vegur [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> x-content-type-options: nosniff [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> x-powered-by: Express [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> x-ratelimit-limit: 1000 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> x-ratelimit-remaining: 999 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: -> x-ratelimit-reset: 1616387857 [trace_id=48b49e15-f7c4-4991-9e33-23850a218718]
INFO: BODY Content-Type: application/json; charset=utf-8
INFO: BODY START
INFO: {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
INFO: BODY END
Bearer Auth: refreshTokens callback blocks indefinitely when server returns 401
I am using ktor client v2.0.0-beta1,
I call the API that I called '/me' with the invalid token, then refresh token API as I excepted is called, and it failed because of our server logic,
my expected behavior is to get the error on my first API('/me') because refreshtoken API is failed. but nothing has happened. even I haven't got the exception anywhere.
ContentEncoding: "Unexpected EOF: expected 10 more bytes" when trying to decode HEAD response
If a web server responds to HEAD
request with Content-encode: gzip
header, ktor client fails with the following exception:
Unexpected exception occurred during switching: kotlinx.coroutines.channels.ClosedReceiveChannelException: Unexpected EOF: expected 10 more bytes
at io.ktor.utils.io.ByteBufferChannel.readFullySuspend(ByteBufferChannel.kt:594)
at io.ktor.utils.io.ByteBufferChannel.readFully(ByteBufferChannel.kt:586)
at io.ktor.utils.io.ByteBufferChannel.readPacketSuspend(ByteBufferChannel.kt:802)
at io.ktor.utils.io.ByteBufferChannel.readPacket$suspendImpl(ByteBufferChannel.kt:788)
at io.ktor.utils.io.ByteBufferChannel.readPacket(ByteBufferChannel.kt)
at io.ktor.utils.io.ByteReadChannelKt.readPacket(ByteReadChannel.kt:207)
at io.ktor.util.EncodersJvmKt$inflate$1.invokeSuspend(EncodersJvm.kt:68)
at io.ktor.util.EncodersJvmKt$inflate$1.invoke(EncodersJvm.kt)
at io.ktor.util.EncodersJvmKt$inflate$1.invoke(EncodersJvm.kt)
at io.ktor.utils.io.CoroutinesKt$launchChannel$job$1.invokeSuspend(Coroutines.kt:132)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Memory leak in ktor-client-curl
Receiving byte array data by GET request with using ktor-client-curl on native (macosX64) seems to be leaking memory, here's the snippet used to reproduce:
repeat(1000) {
runBlocking {
client.get("http://some-test-image.jpg")
val responseBody: ByteArray = httpResponse.receive()
}
}
Running that code shows constantly increasing memory usage of the process - using quite big images seems to be quickest way to observe it. This seems to be reproducible with 1.5.3, 1.6.0, 1.6.7 as well as with 2.0.0-beta-1 (with introduced API changes into snippet). I actually cherry-picked these versions amongst others and each seems to be affected.
Is there anything I'm assuming wrong here? I would expect heap size to stabilise around some usage instead.
Default request: Query parameters in default URL are overwritten
Ktor version: 2.0.0-beta-1
I migrated to Ktor 2.0 but attaching query parameters with api key in defaultRequest
seems not to work anymore:
HttpClient {
// ...
defaultRequest {
url {
protocol = URLProtocol.HTTPS
host = "api.themoviedb.org"
encodedPath = "/3$encodedPath" // prepend API version to path
parametersOf("api_key", "my_api_key_value")
}
}
}
suspend fun getConfiguration(): ApiConfig = client.get("configuration").body()
produces:
io.ktor.client.plugins.ClientRequestException: Client request(GET https://api.themoviedb.org/3/configuration) invalid: 401 Unauthorized. Text: "{"status_code":7,"status_message":"Invalid API key: You must be granted a valid key.","success":false}
Query parameters are only appended if I add them to every individual request like this:
suspend fun getConfiguration(): ApiConfig = client.get("configuration") {
parameter(API_KEY, API_KEY_VALUE)
}.body()
Timeout in receiving streaming body breaks client
Setting DefaultRequest.url.protocol on the client side breaks the ability to establish a ws connection
Hello, guys! It seems setting defaultRequest.url.protocol
on the client side breaks the ability to establish a ws connection. Please, check it out.
If I set defaultRequest.url.protocol
, the client tries to establish a ws connection via http GET and fails.
But if I just comment off that setting, everything works.
JS: Websocket errors are not being handled correctly
There currently is an issue when some error is thrown whilst connecting to a websocket
Example:
val session = client.webSocketSession {
url("<host>")
header("Authorization", "<auth>")
}
If the server e.g. returns an unexpected http error code instead of throwing ServerResponseException
the following js error gets printed
var exception = new WebSocketException(JSON.stringify(event));
^
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Socket'
| property 'parser' -> object with constructor 'HTTPParser'
--- property 'socket' closes the circle
at JSON.stringify (<anonymous>)
at WebSocket.<anonymous> (D:\WORK\lavakord\build\js\node_modules\ktor-ktor-client-core-jsLegacy\ktor-ktor-client-core-jsLegacy.js:17440:51)
at WebSocket.onError (D:\WORK\lavakord\build\js\node_modules\ws\lib\event-target.js:128:16)
at WebSocket.emit (events.js:311:20)
at abortHandshake (D:\WORK\lavakord\build\js\node_modules\ws\lib\websocket.js:718:15)
at ClientRequest.<anonymous> (D:\WORK\lavakord\build\js\node_modules\ws\lib\websocket.js:595:7)
at ClientRequest.emit (events.js:311:20)
at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:603:27)
at HTTPParser.parserOnHeadersComplete (_http_common.js:119:17)
at Socket.socketOnData (_http_client.js:476:22)
Retry on HttpCode or network error
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1285
Subsystem
Client
Is your feature request related to a problem? Please describe.
Sometimes external service returns 500/502 errors, however, next call can be successful. In this case, do connection retry after a small delay. Similar, a timeout or network errors could be fixed by a retry.
Describe the solution you'd like
I'd like to have separate Ktor Client feature to automate these retries. For example, it can get handler like needRetry: Response -> Bool
on input and configurable retry pauses and retry count.
Motivation to include to ktor
Of course, retries can be implemented above the call. However, the client can do them better, because:
- It can preserve low-level resources (like data buffers, resolved headers, etc.)
- It already does retry logic on 401 responses
multipart/form-data requests: No way of streaming data asynchronously
Right now the FormBuilder
and MultiPartFormDataContent
support only one streaming primitive Input
that is synchronous. This prevents streaming data in environments like JS because it's not viable to block the only thread for reading.
The suggested solution is to implement reading from the ByteReadChannel
.
DefaultRequest API doc contains missing members
For example, there are no appendIfNameMissing
and appendIfNameAndValueMissing
. AFAIU, Missing
is renamed to Absent
.
defaultRequest {
url("https://base.url/dir/")
headers.appendIfNameMissing(HttpHeaders.ContentType, ContentType.Application.Json)
}
The 'refreshTokens' callback isn't invoked when an API returns a 401 response without the 'WWW-Authenticate' header
In the docs sample (https://ktor.io/docs/auth.html#bearer), the bearer
authentication is used to access the Google API. The problem is that the Google API doesn't return the WWW-Authenticate
with the 401
response when an access token is expired. This causes that the refreshTokens
callback isn't called and there is no way to refresh a token.
The log below doesn't have the WWW-Authenticate
header in a response:
11:33:50.424 [main] INFO io.ktor.client.HttpClient - REQUEST: https://www.googleapis.com/oauth2/v2/userinfo
11:33:50.424 [main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
11:33:50.424 [main] INFO io.ktor.client.HttpClient - COMMON HEADERS
11:33:50.425 [main] INFO io.ktor.client.HttpClient - -> Accept: application/json
11:33:50.425 [main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
11:33:50.425 [main] INFO io.ktor.client.HttpClient - -> Authorization: Bearer ya29.a0ARrdaM9sworHbT_H9DAQ3VKU9j_DvNdBvGBDBf_H87qioXt49-oZsE7c088qG4PIgBz4fCSrGGsJvMi0hsZO5FMjFpz2HjZ1wjufF7N47LLtQn6maZINLLlgWQ4CUgaRMxpIeb13byee9JqhtBUNPMfhcnYE
11:33:50.425 [main] INFO io.ktor.client.HttpClient - CONTENT HEADERS
11:33:50.425 [main] INFO io.ktor.client.HttpClient - -> Content-Length: 0
11:33:50.536 [main] INFO io.ktor.client.HttpClient - RESPONSE: 401 Unauthorized
11:33:50.536 [main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=GET)
11:33:50.536 [main] INFO io.ktor.client.HttpClient - FROM: https://www.googleapis.com/oauth2/v2/userinfo
11:33:50.536 [main] INFO io.ktor.client.HttpClient - COMMON HEADERS
11:33:50.536 [main] INFO io.ktor.client.HttpClient - -> Accept-Ranges: none
11:33:50.536 [main] INFO io.ktor.client.HttpClient - -> Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Cache-Control: no-cache, no-store, max-age=0, must-revalidate
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Content-Type: application/json; charset=UTF-8
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Date: Wed, 01 Dec 2021 08:33:50 GMT
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Expires: Mon, 01 Jan 1990 00:00:00 GMT
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Pragma: no-cache
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Server: ESF
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Transfer-Encoding: chunked
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> Vary: X-Origin; Referer; Origin,Accept-Encoding
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> X-Content-Type-Options: nosniff
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> X-Frame-Options: SAMEORIGIN
11:33:50.537 [main] INFO io.ktor.client.HttpClient - -> X-XSS-Protection: 0
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
401 Unauthorized
null
submitFormWithBinaryData: mutation attempt of frozen <object>@194c6a8
I wanted to test if the ByteArray upload already works on linuxX64 (ticket KTOR-2883), unfortunately it still crashes on the exception. Interestingly though, it only happens on the 200th byte. It's stably repeatable.
Tested on
$ git log
commit c9948dca45146a8c22da7d615e50a5f65cecd6c8 (HEAD -> main, origin/main, origin/HEAD)
Author: Leonid Stashevsky <e5l@users.noreply.github.com>
Date: Tue Jul 20 11:38:49 2021 +0300
KTOR-2883 Fix SingleInstancePool freeze (#2558)
Console log
> Task :runDebugExecutableNative
Length 195 ...OK!
Length 196 ...OK!
Length 197 ...OK!
Length 198 ...OK!
Length 199 ...OK!
Length 200 ...FAIL: mutation attempt of frozen <object>@194c6a8
Length 201 ...FAIL: mutation attempt of frozen <object>@197f8e8
Length 202 ...FAIL: mutation attempt of frozen <object>@19a9208
Length 203 ...FAIL: mutation attempt of frozen <object>@19c99e8
Length 204 ...FAIL: mutation attempt of frozen <object>@1a05078
Length 205 ...FAIL: mutation attempt of frozen <object>@19ec988
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen <object>@19ec988
at kfun:kotlin.Throwable#<init>(kotlin.String?){} (0x488899)
at kfun:kotlin.Exception#<init>(kotlin.String?){} (0x481c77)
at kfun:kotlin.RuntimeException#<init>(kotlin.String?){} (0x481e27)
at kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} (0x4b4277)
at ThrowInvalidMutabilityException (0x4b5976)
at MutationCheck (0x636660)
at kfun:io.ktor.utils.io.core.object-1.<set-pinned>#internal (0x76a442)
at kfun:io.ktor.utils.io.core.object-1.disposeInstance#internal (0x76ab4e)
at kfun:io.ktor.utils.io.pool.SingleInstancePool#recycle(1:0){} (0x7409a1)
at kfun:io.ktor.utils.io.core.internal.ChunkBuffer#release(io.ktor.utils.io.pool.ObjectPool<io.ktor.utils.io.core.internal.ChunkBuffer>){} (0x72fd81)
at kfun:io.ktor.utils.io.core.AbstractInput.ensureNext#internal (0x6f217a)
at kfun:io.ktor.utils.io.core.AbstractInput#ensureNext(io.ktor.utils.io.core.internal.ChunkBuffer){}io.ktor.utils.io.core.internal.ChunkBuffer? (0x6f0d79)
at kfun:io.ktor.utils.io.core.AbstractInput#ensureNextHead(io.ktor.utils.io.core.internal.ChunkBuffer){}io.ktor.utils.io.core.internal.ChunkBuffer? (0x6f0be0)
at kfun:io.ktor.utils.io.core.internal#prepareReadNextHead__at__io.ktor.utils.io.core.Input(io.ktor.utils.io.core.internal.ChunkBuffer){}io.ktor.utils.io.core.internal.ChunkBuffer? (0x73ac40)
at kfun:io.ktor.utils.io.core#readFully__at__io.ktor.utils.io.core.Input(kotlin.ByteArray;kotlin.Int;kotlin.Int){} (0x719f08)
at kfun:io.ktor.utils.io.core#readBytes__at__io.ktor.utils.io.core.ByteReadPacket(kotlin.Int){}kotlin.ByteArray (0x72cbc0)
at kfun:io.ktor.utils.io.core#readBytes$default__at__io.ktor.utils.io.core.ByteReadPacket(kotlin.Int;kotlin.Int){}kotlin.ByteArray (0x72cde7)
at kfun:io.ktor.client.engine.curl.internal.$toCurlByteArrayCOROUTINE$4#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? (0x927eac)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} (0x4a87c0)
at kfun:kotlinx.coroutines.DispatchedTask#run(){} (0x69aebd)
at kfun:kotlinx.coroutines.EventLoopImplBase#processNextEvent(){}kotlin.Long (0x674b40)
at kfun:kotlinx.coroutines#runEventLoop(kotlinx.coroutines.EventLoop?;kotlin.Function0<kotlin.Boolean>){} (0x6ace38)
at kfun:kotlinx.coroutines.BlockingCoroutine.joinBlocking#internal (0x6ac5fd)
at kfun:kotlinx.coroutines#runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}0:0 (0x6abb0a)
at kfun:kotlinx.coroutines#runBlocking$default(kotlin.coroutines.CoroutineContext?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>;kotlin.Int){0§<kotlin.Any?>}0:0 (0x6abfc6)
at kfun:#main(){} (0x46aa4f)
at Konan_start (0x46e7ab)
at Init_and_run_start (0x46eaab)
at __libc_start_main (0x7f5d447dcb75)
at (0x46a903)
at ((nil))
main.kt
import io.ktor.client.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.runBlocking
fun main(): Unit = runBlocking {
var lastError: Exception? = null
(195..205).forEach { repeatCount ->
try {
print("Length $repeatCount ...")
val data = "a".repeat(repeatCount).toByteArray()
sendData("http://localhost", 8080, "/api", data)
println("OK!")
} catch (e: Exception) {
println("FAIL: ${e.message}")
lastError = e
}
}
lastError?.run {
printStackTrace()
}
}
private suspend fun sendData(server: String, port: Int, endpoint: String, data: ByteArray) {
HttpClient {}.submitFormWithBinaryData<HttpResponse>(
url = "$server:$port$endpoint/upload",
formData = formData {
append("blob", data, Headers.build {
append(HttpHeaders.ContentType, "text/plain")
append(HttpHeaders.ContentDisposition, "filename=file.txt")
})
}
)
}
build.gradle.kts
plugins {
kotlin("multiplatform") version "1.5.21"
kotlin("plugin.serialization") version "1.5.0"
}
group = "me.mira"
version = "1.0-SNAPSHOT"
val kotlinxCliVersion = "0.3.2"
val kotlinxSerialisationVersion = "1.2.1"
val ktorVersion = "1.6.2-SNAPSHOT"
val datetimeVersion = "0.2.1"
repositories {
mavenCentral()
mavenLocal()
}
kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTarget.apply {
binaries {
executable {
entryPoint = "main"
freeCompilerArgs += "-Xallocator=mimalloc -Xopt-in=kotlin.RequiresOptIn"
}
}
}
sourceSets {
val nativeMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-cli:$kotlinxCliVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerialisationVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-curl:$ktorVersion")
implementation("io.ktor:ktor-client-logging:$ktorVersion")
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:$datetimeVersion")
}
}
val nativeTest by getting
}
}
iOS: Failed to find HttpClientEngineContainer with new native memory model
When creating a Kotlin Multiplatform Mobile Library using Ktor, when creating a httpclient with an empty constructor(should use default httpclientengine), then the following exception is thrown:
kotlin.IllegalStateException: Failed to find HttpClientEngineContainer. Consider adding [HttpClientEngine] implementation in dependencies.[kotlin.Exception]
I have added the following to my build gradle:
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-ios:$ktor_version")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version")
}
}
val iosSimulatorArm64Main by getting
iosSimulatorArm64Main.dependsOn(iosMain)
val iosTest by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
}
}
val iosSimulatorArm64Test by getting
iosSimulatorArm64Test.dependsOn(iosTest)
Android behaves as expected but for some reason ktor is not using the default client engine.
Just for clarity, simply doing:
private val httpClientConfig: HttpClientConfig<*>.() -> Unit = {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}
HttpClient(httpClientConfig)
In the common layer, throws the exception, but i would expect it would use the default client engine for each of the platforms respectively.
Using any Suspend or Coroutine function in Bearer Auth functions cause crash on iOS
Kotlin: 1.5.30
Ktor: 1.6.3
Coroutines: 1.5.2-native-mt
When any suspend function or coroutines function is called inside either loadTokens or refreshTokens it causes a crash on iOS as soon as the code is loaded
import io.ktor.client.HttpClient
import io.ktor.client.features.auth.Auth
import io.ktor.client.features.auth.providers.bearer
class Api {
suspend fun check() {
}
private val httpClient = HttpClient() {
install(Auth) {
bearer {
loadTokens {
check()
null
}
}
}
}
}
Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
Process finished with exit code 0
[iOS] InvalidMutabilityException: mutation attempt of frozen
ONLY ON iOS, on android work correctly.
I've this class
open class BaseClient(val baseUrl: String) {
open val serializer = KotlinxSerializer(Json {
isLenient = true
ignoreUnknownKeys = true
})
open val logLevel = LogLevel.ALL
open val header = mapOf<String, String>()
open val client = HttpClient() {
install(JsonFeature) {
serializer = this@BaseClient.serializer
}
install(Logging) {
level = logLevel
}
defaultRequest {
header.forEach {
parameter(it.key, it.value)
}
if (this.method != HttpMethod.Get) contentType(ContentType.Application.Json)
}
}
}
On Android works correctly on iOS it crash with this error: InvalidMutabilityException: mutation attempt of frozen
If i delete all configuration in install command and configuration of default request, it doesn't crash on iOS.
So if I change from
install(JsonFeature) {
serializer = this@BaseClient.serializer
}
install(Logging) {
level = logLevel
}
defaultRequest {
header.forEach {
parameter(it.key, it.value)
}
if (this.method != HttpMethod.Get) contentType(ContentType.Application.Json)
}
to
install(JsonFeature)
install(Logging)
it works correctly.
Is this my bug or yours?
My Environment:
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9-native-mt")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0")
implementation("io.ktor:ktor-client-serialization:1.4.1")
implementation("io.ktor:ktor-client-json:1.4.1")
implementation("io.ktor:ktor-client-serialization:1.4.1")
implementation("io.ktor:ktor-client-core:1.4.1")
implementation("io.ktor:ktor-client-cio:1.4.1")
implementation("io.ktor:ktor-client-logging:1.4.1")
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:1.4.1")
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0")
implementation("io.ktor:ktor-client-serialization-jvm:1.4.1")
}
}
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-ios:1.4.1")
implementation("io.ktor:ktor-client-serialization-iosx64:1.4.1")
implementation("com.github.florent37:multiplatform-livedata-ios:0.0.5")
}
}
InvalidMutabilityException: Configuration issues for ios
There is such a ktor configuration only for ios. Ignored ssl certificate.
actual fun get(): HttpClient {
return HttpClient(Ios) {
engine {
configureSession {
handleChallenge { session, task, challenge, completionHandler ->
val trust = challenge.protectionSpace.serverTrust
completionHandler(NSURLSessionAuthChallengeUseCredential, NSURLCredential.create(trust))
}
}
}
install(JsonFeature) {
serializer = KotlinxSerializer(
kotlinx.serialization.json.Json {
isLenient = true
ignoreUnknownKeys = true
}
)
}
install(Logging) {
logger = Logger.SIMPLE
//level = LogLevel.ALL
}
install(HttpTimeout) {
connectTimeoutMillis = TIMEOUT_MILLIS
requestTimeoutMillis = TIMEOUT_MILLIS
}
}
}
}```
I get an error on the platform: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen io.ktor.client.engine.ios.IosClientEngineConfig
Also tried this client configuration
```actual object HttpClientImpl {
actual fun get(): HttpClient {
return HttpClient {
engine {
Ios.config {
configureSession {
handleChallenge { session, task, challenge, completionHandler ->
val trust = challenge.protectionSpace.serverTrust
completionHandler(NSURLSessionAuthChallengeUseCredential, NSURLCredential.create(trust))
}
}
}
}
install(JsonFeature) {
serializer = KotlinxSerializer(
kotlinx.serialization.json.Json {
isLenient = true
ignoreUnknownKeys = true
}
)
}
install(Logging) {
logger = Logger.SIMPLE
//level = LogLevel.ALL
}
install(HttpTimeout) {
connectTimeoutMillis = TIMEOUT_MILLIS
requestTimeoutMillis = TIMEOUT_MILLIS
}
}
}
}```
Then the configuration block is ignored.
iOS testing MockEngine issue
My goal is to mock the httpClient to test several error cases on Android and iOS (since there are different errors on each platform).
For Android everything work fine, but for iOS I am still facing the same error:
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen ***.shared.MockEngine
I have tested a lot of iosTest dependencies but didn't succeed to find the good ktor-client-mock.
the last I tested was this one:
val iosTest by getting {
dependencies {
implementation("io.ktor:ktor-client-mock-iosx64:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-iosx64:$coroutinesVersion")
}
}
I am using kotlin 1.4.0
and Ktor 1.4.3
I am running iosTest using :
val iosTest: Task by tasks.creating {
val device = project.findProperty("iosDevice")?.toString() ?: "iPhone X"
val testExecutable =
kotlin.targets.getByName<KotlinNativeTarget>("iosX64").binaries.getTest("DEBUG")
dependsOn(testExecutable.linkTaskName)
group = JavaBasePlugin.VERIFICATION_GROUP
description = "Runs tests for target 'ios' on an iOS simulator"
doLast {
exec {
println(testExecutable.outputFile.absolutePath)
commandLine(
"xcrun",
"simctl",
"spawn",
"--standalone",
device,
testExecutable.outputFile.absolutePath
)
}
}
}
"InvalidMutabilityException: Frozen during lazy computation" when using by lazy for HttpClient
After updating PeopleInSpace (https://github.com/joreilly/PeopleInSpace) to following dependencies
const val kotlinCoroutines = "1.3.9-native-mt-2"
const val ktor = "1.4.1"
const val kotlinxSerialization = "1.0.0-RC2"
getting following crash in iOS client (as reported in https://github.com/joreilly/PeopleInSpace/issues/11)
Uncaught Kotlin exception: kotlinx.coroutines.CoroutinesInternalError: Fatal exception in coroutines machinery for DispatchedContinuation[MainDispatcher, Continuation @ $<init>$lambda-0COROUTINE$5]. Please read KDoc to 'handleFatalException' method and report this incident to maintainers
at 0 PeopleInSpaceSwiftUI 0x000000010e1e2b1c kfun:kotlin.Error#<init>(kotlin.String?;kotlin.Throwable?){} + 124
at 1 PeopleInSpaceSwiftUI 0x000000010e38b83c kfun:kotlinx.coroutines.CoroutinesInternalError#<init>(kotlin.String;kotlin.Throwable){} + 124
at 2 PeopleInSpaceSwiftUI 0x000000010e3d72e9 kfun:kotlinx.coroutines.DispatchedTask#handleFatalException(kotlin.Throwable?;kotlin.Throwable?){} + 953
at 3 PeopleInSpaceSwiftUI 0x000000010e3d6ec7 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 3543
at 4 PeopleInSpaceSwiftUI 0x000000010e40a0b8 kfun:kotlinx.coroutines.DarwinMainDispatcher.dispatch$lambda-0#internal + 88
at 5 PeopleInSpaceSwiftUI 0x000000010e40a48e kfun:kotlinx.coroutines.DarwinMainDispatcher.$dispatch$lambda-0$FUNCTION_REFERENCE$43.invoke#internal + 62
at 6 PeopleInSpaceSwiftUI 0x000000010e40a4ee kfun:kotlinx.coroutines.DarwinMainDispatcher.$dispatch$lambda-0$FUNCTION_REFERENCE$43.$<bridge-UNN>invoke(){}#internal + 62
at 7 PeopleInSpaceSwiftUI 0x000000010e40b5a7 _6f72672e6a6574627261696e732e6b6f746c696e783a6b6f746c696e782d636f726f7574696e65732d636f7265_knbridge8 + 183
at 8 libdispatch.dylib 0x000000010f1c28ac _dispatch_call_block_and_release + 12
at 9 libdispatch.dylib 0x000000010f1c3a88 _dispatch_client_callout + 8
at 10 libdispatch.dylib 0x000000010f1d1f23 _dispatch_main_queue_callback_4CF + 1152
at 11 CoreFoundation 0x00007fff203a8276 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
at 12 CoreFoundation 0x00007fff203a2b06 __CFRunLoopRun + 2685
at 13 CoreFoundation 0x00007fff203a1b9e CFRunLoopRunSpecific + 567
at 14 GraphicsServices 0x00007fff2b773db3 GSEventRunModal + 139
at 15 UIKitCore 0x00007fff24660af3 -[UIApplication _run] + 912
at 16 UIKitCore 0x00007fff24665a04 UIApplicationMain + 101
at 17 PeopleInSpaceSwiftUI 0x000000010e1281cb main + 75 (/Users/mbonnin/git/PeopleInSpace/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/AppDelegate.swift:5:7)
at 18 libdyld.dylib 0x00007fff20257415 start + 1
Caused by: kotlin.native.concurrent.InvalidMutabilityException: Frozen during lazy computation
at 0 PeopleInSpaceSwiftUI 0x000000010e1ea07d kfun:kotlin.Throwable#<init>(kotlin.String?){} + 93
at 1 PeopleInSpaceSwiftUI 0x000000010e1e2c2b kfun:kotlin.Exception#<init>(kotlin.String?){} + 91
at 2 PeopleInSpaceSwiftUI 0x000000010e1e2e7b kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 91
at 3 PeopleInSpaceSwiftUI 0x000000010e218d6b kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 91
at 4 PeopleInSpaceSwiftUI 0x000000010e21b419 kfun:kotlin.native.concurrent.FreezeAwareLazyImpl.getOrInit#internal + 1641
at 5 PeopleInSpaceSwiftUI 0x000000010e21b95e kfun:kotlin.native.concurrent.FreezeAwareLazyImpl#<get-value>(){}1:0 + 670
at 6 PeopleInSpaceSwiftUI 0x000000010e13f183 kfun:com.surrus.common.remote.PeopleInSpaceApi.<get-client>#internal + 323
at 7 PeopleInSpaceSwiftUI 0x000000010e13feb7 kfun:com.surrus.common.remote.PeopleInSpaceApi.$fetchPeopleCOROUTINE$0#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 2983
at 8 PeopleInSpaceSwiftUI 0x000000010e141a74 kfun:com.surrus.common.remote.PeopleInSpaceApi#fetchPeople(){}com.surrus.common.remote.AstroResult + 244
at 9 PeopleInSpaceSwiftUI 0x000000010e146666 kfun:com.surrus.common.repository.PeopleInSpaceRepository.$fetchAndStorePeopleCOROUTINE$2.invokeSuspend#internal + 886
at 10 PeopleInSpaceSwiftUI 0x000000010e146bb4 kfun:com.surrus.common.repository.PeopleInSpaceRepository.fetchAndStorePeople#internal + 244
at 11 PeopleInSpaceSwiftUI 0x000000010e1487da kfun:com.surrus.common.repository.PeopleInSpaceRepository.$<init>$lambda-0COROUTINE$5.invokeSuspend#internal + 602
at 12 PeopleInSpaceSwiftUI 0x000000010e20bba6 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 758
at 13 PeopleInSpaceSwiftUI 0x000000010e3d6be2 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 2802
at 14 PeopleInSpaceSwiftUI 0x000000010e40a0b8 kfun:kotlinx.coroutines.DarwinMainDispatcher.dispatch$lambda-0#internal + 88
... and 14 more common stack frames skipped
CoreSimulator 732.17 - Device: iPhone 11 (8BACA2EC-79DF-49D0-937D-AFA747B52692) - Runtime: iOS 14.0 (18A372) - DeviceType: iPhone 11
(lldb)
The particular code that's triggering this is in PeopleInSpaceRepository
. That uses Ktor to make api request and then stores in db (but see same issue if I only do ktor request there)
init {
GlobalScope.launch(Dispatchers.Main) {
fetchAndStorePeople()
}
}
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.collections on iOS when deserializing class that contains less properties than the json
I migrated to kotlin 1.5.0 and ktor 1.6.0. I’m using the native-mt
version of coroutines 1.5.0.
I have a very simple test that fails on iOS with a kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.collections.HashMap
while deserializing a simple class that contains less keys than the json
Here is the test below:
@Serializable
data class TestData(val key1: String, val key2: String)
@Test
fun `json serializer should ignore unknown keys`() = runTest {
val http = HttpClient(MockEngine) {
engine {
addHandler {
respond(
status = HttpStatusCode.OK,
headers = headersOf(
HttpHeaders.ContentType,
ContentType.Application.Json.toString()
),
content = """
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
""".trimIndent()
)
}
}
Json {
serializer = KotlinxSerializer(kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
})
}
}
val data = http.get<TestData>()
assertEquals(TestData("value1", "value2"), data)
}
Here is the full stacktrace if it can help:
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.collections.HashMap@9390fc68
at kotlin.Throwable#<init>(Unknown Source)
at kotlin.Exception#<init>(Unknown Source)
at kotlin.RuntimeException#<init>(Unknown Source)
at kotlin.native.concurrent.InvalidMutabilityException#<init>(Unknown Source)
at <global>.ThrowInvalidMutabilityException(Unknown Source)
at <global>.MutationCheck(Unknown Source)
at kotlin.collections.HashMap.<set-length>#internal(Unknown Source)
at kotlin.collections.HashMap#addKey(Unknown Source)
at kotlin.collections.HashMap#put(Unknown Source)
at kotlinx.serialization.json.internal.DescriptorSchemaCache#set(Unknown Source)
at kotlinx.serialization.json.internal.DescriptorSchemaCache#getOrPut(Unknown Source)
at kotlinx.serialization.json.internal#getJsonNameIndex__at__kotlinx.serialization.descriptors.SerialDescriptor(Unknown Source)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeObjectIndex#internal(Unknown Source)
at kotlinx.serialization.json.internal.StreamingJsonDecoder#decodeElementIndex(Unknown Source)
at com.bethesomm.http.HttpClientTest.TestData.$serializer#deserialize(Unknown Source)
at kotlinx.serialization.json.internal#decodeSerializableValuePolymorphic__at__kotlinx.serialization.json.JsonDecoder(Unknown Source)
at kotlinx.serialization.json.internal.StreamingJsonDecoder#decodeSerializableValue(Unknown Source)
at kotlinx.serialization.json.Json#decodeFromString(Unknown Source)
at io.ktor.client.features.json.serializer.KotlinxSerializer#read(Unknown Source)
at io.ktor.client.features.json.JsonSerializer#read(Unknown Source)
at io.ktor.client.features.json.JsonFeature.Feature.$install$lambda-1COROUTINE$1.invokeSuspend#internal(Unknown Source)
at io.ktor.client.features.json.JsonFeature.Feature.$install$lambda-1COROUTINE$1.invoke#internal(Unknown Source)
at io.ktor.util.pipeline.SuspendFunctionGun.$loop$<anonymous>_2COROUTINE$8.invokeSuspend#internal(Unknown Source)
at io.ktor.util.pipeline.SuspendFunctionGun.$loop$<anonymous>_2COROUTINE$8.invoke#internal(Unknown Source)
at io.ktor.util.pipeline.SuspendFunctionGun.loop#internal(Unknown Source)
at io.ktor.util.pipeline.SuspendFunctionGun#proceed(Unknown Source)
at io.ktor.util.pipeline.SuspendFunctionGun#proceedWith(Unknown Source)
at io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-2COROUTINE$22.invokeSuspend#internal(Unknown Source)
at io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-2COROUTINE$22.invoke#internal(Unknown Source)
at io.ktor.util.pipeline.SuspendFunctionGun.$loop$<anonymous>_2COROUTINE$8.invokeSuspend#internal(Unknown Source)
Native: Cannot mutate objects inside onDownload and onUpload lambdas
To reproduce run the following test:
@Test
fun testSome() = clientTests {
test { client ->
var flag = false
client.post<ByteArray>("$TEST_SERVER/content/echo") {
body = ByteArray(1025 * 16) { 1 }
onDownload { _, _ ->
flag = true
}
}
}
}
Unexpectedly it fails with InvalidMutabilityException
:
kotlin.IllegalStateException: Test failed with engine: Curl
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.native.internal.Ref@3113008
kfun:kotlin.Throwable#<init>(kotlin.String?){} (0x421a49)
kfun:kotlin.Exception#<init>(kotlin.String?){} (0x41a8b7)
kfun:kotlin.RuntimeException#<init>(kotlin.String?){} (0x41aa67)
kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} (0x44e107)
ThrowInvalidMutabilityException (0x44f736)
MutationCheck (0xd96aa0)
kfun:kotlin.native.internal.Ref#<set-element>(1:0){} (0x466472)
kfun:io.ktor.client.tests.BodyProgressTest.$testSome$lambda-47$lambda-46$<anonymous>_8COROUTINE$44.invokeSuspend#internal (0x983f72)
kfun:io.ktor.client.tests.BodyProgressTest.$testSome$lambda-47$lambda-46$<anonymous>_8COROUTINE$44.invoke#internal (0x98437e)
kfun:io.ktor.client.tests.BodyProgressTest.$testSome$lambda-47$lambda-46$<anonymous>_8COROUTINE$44.$<bridge-NNNBB>invoke(kotlin.Long;kotlin.Long){}kotlin.Any?#internal (0x984518)
kfun:io.ktor.client.utils.$observable$lambda-0COROUTINE$686.invokeSuspend#internal (0x83fcbf)
kfun:io.ktor.client.utils.$observable$lambda-0COROUTINE$686.invoke#internal (0x840aa3)
kfun:io.ktor.utils.io.$launchChannel$lambda-0COROUTINE$760.invokeSuspend#internal (0x6323fe)
kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} (0x443050)
kfun:kotlinx.coroutines.DispatchedTask#run(){} (0x5d1f10)
kfun:kotlinx.coroutines.EventLoopImplBase#processNextEvent(){}kotlin.Long (0x57ee00)
kfun:kotlinx.coroutines#runEventLoop(kotlinx.coroutines.EventLoop?;kotlin.Function0<kotlin.Boolean>){} (0x5fab4b)
kfun:kotlinx.coroutines.BlockingCoroutine.joinBlocking#internal (0x5fbdad)
kfun:kotlinx.coroutines#runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}0:0 (0x5fb40e)
kfun:io.ktor.test.dispatcher#testSuspend(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,kotlin.Unit>){} (0x84ca76)
kfun:io.ktor.test.dispatcher#testSuspend$default(kotlin.coroutines.CoroutineContext?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,kotlin.Unit>;kotlin.Int){} (0x84cbbe)
kfun:io.ktor.client.tests.utils#testWithEngine(io.ktor.client.engine.HttpClientEngineFactory<0:0>;io.ktor.client.tests.utils.ClientLoader?;kotlin.coroutines.SuspendFunction1<io.ktor.client.tests.utils.TestClientBuilder<0:0>,kotlin.Unit>){0§<io.ktor.client.engine.HttpClientEngineConfig>} (0x8d2ea2)
kfun:io.ktor.client.tests.utils#testWithEngine$default(io.ktor.client.engine.HttpClientEngineFactory<0:0>;io.ktor.client.tests.utils.ClientLoader?;kotlin.coroutines.SuspendFunction1<io.ktor.client.tests.utils.TestClientBuilder<0:0>,kotlin.Unit>;kotlin.Int){0§<io.ktor.client.engine.HttpClientEngineConfig>} (0x8d2ff5)
kfun:io.ktor.client.tests.utils.ClientLoader#clientTests(kotlin.collections.List<kotlin.String>;kotlin.coroutines.SuspendFunction1<io.ktor.client.tests.utils.TestClientBuilder<io.ktor.client.engine.HttpClientEngineConfig>,kotlin.Unit>){} (0x8dae1f)
kfun:io.ktor.client.tests.utils.ClientLoader#clientTests$default(kotlin.collections.List<kotlin.String>?;kotlin.coroutines.SuspendFunction1<io.ktor.client.tests.utils.TestClientBuilder<io.ktor.client.engine.HttpClientEngineConfig>,kotlin.Unit>;kotlin.Int){} (0x8db462)
kfun:io.ktor.client.tests.BodyProgressTest#testSome(){} (0x94e5bd)
kfun:io.ktor.client.tests.$BodyProgressTest$test$0.$testSome$FUNCTION_REFERENCE$23.invoke#internal (0x98acf5)
kfun:io.ktor.client.tests.$BodyProgressTest$test$0.$testSome$FUNCTION_REFERENCE$23.$<bridge-UNNN>invoke(-1:0){}#internal (0x98aefb)
kfun:kotlin.native.internal.test.BaseClassSuite.TestCase#run(){} (0x47be5d)
kfun:kotlin.native.internal.test.TestRunner.run#internal (0x475a7f)
kfun:kotlin.native.internal.test.TestRunner.runIteration#internal (0x476ff8)
kfun:kotlin.native.internal.test.TestRunner#run(){}kotlin.Int (0x477937)
kfun:kotlin.native.internal.test#testLauncherEntryPoint(kotlin.Array<kotlin.String>){}kotlin.Int (0x46ba9e)
kfun:kotlin.native.internal.test#main(kotlin.Array<kotlin.String>){} (0x46b949)
Konan_start (0x46bbcd)
Init_and_run_start (0xd97ceb)
__libc_start_main (0x7f667af810b3)
(0x4063b1)
((nil))
HttpClient / native: "mutation attempt of frozen" crash when configuring the client
I made a simple sample project that demonstrates the issue.
I have a SampleClientImpl
class that has a clientConfiguration
property. An HttpClient
is created and configured using the clientConfiguration
property. When that happens, I get this crash:
$ samples/sample-native-osx/build/bin/macos/debugExecutable/sample-native-osx.kexe
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen org.jraf.ktorhttpclientsample.internal.client.SampleClientImpl@ccf14ef8
at 0 sample-native-osx.kexe 0x000000010aa4102f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Throwable.kt:23:37)
at 1 sample-native-osx.kexe 0x000000010aa39c6d kfun:kotlin.Exception#<init>(kotlin.String?){} + 93 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
at 2 sample-native-osx.kexe 0x000000010aa39e4d kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
at 3 sample-native-osx.kexe 0x000000010aa6fefd kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 93 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22:60)
at 4 sample-native-osx.kexe 0x000000010aa716bf ThrowInvalidMutabilityException + 431 (/Users/teamcity/buildAgent/work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:92:11)
at 5 sample-native-osx.kexe 0x000000010ab714d0 MutationCheck + 128
at 6 sample-native-osx.kexe 0x000000010a9dc5d5 kfun:org.jraf.ktorhttpclientsample.internal.client.SampleClientImpl#<init>(org.jraf.ktorhttpclientsample.client.ClientConfiguration){} + 437 (/Users/bod/gitrepo/ktor-http-client-sample/library/src/commonMain/kotlin/org/jraf/ktorhttpclientsample/internal/client/SampleClientImpl.kt:52:30)
at 7 sample-native-osx.kexe 0x000000010a9d9696 kfun:org.jraf.ktorhttpclientsample.client.SampleClient.Companion#newInstance(org.jraf.ktorhttpclientsample.client.ClientConfiguration){}org.jraf.ktorhttpclientsample.client.SampleClient + 214 (/Users/bod/gitrepo/ktor-http-client-sample/library/src/commonMain/kotlin/org/jraf/ktorhttpclientsample/client/SampleClient.kt:34:77)
at 8 sample-native-osx.kexe 0x000000010a9e34c2 kfun:Sample#<init>(){} + 738 (/Users/bod/gitrepo/ktor-http-client-sample/samples/sample-native-osx/src/macosMain/kotlin/Sample.kt:40:22)
at 9 sample-native-osx.kexe 0x000000010a9e422c kfun:#main(){} + 124 (/Users/bod/gitrepo/ktor-http-client-sample/samples/sample-native-osx/src/macosMain/kotlin/Sample.kt:72:14)
at 10 sample-native-osx.kexe 0x000000010a9e4312 Konan_start + 146 (/Users/bod/gitrepo/ktor-http-client-sample/samples/sample-native-osx/src/macosMain/kotlin/Sample.kt:72:1)
at 11 sample-native-osx.kexe 0x000000010a9ea48b Init_and_run_start + 107
at 12 libdyld.dylib 0x00007fff203a6621 start + 1
at 13 ??? 0x0000000000000001 0x0 + 1
Unfinished workers detected, 1 workers leaked!
Use `Platform.isMemoryLeakCheckerActive = false` to avoid this check.
Abort trap: 6
If, instead instead of being a property, clientConfiguration
is made into an argument to the constructor (so, just removing private val
from its declaration), this crash no longer happens.
internal class SampleClientImpl(
- private val clientConfiguration: ClientConfiguration
+ clientConfiguration: ClientConfiguration
) : SampleClient {
I do not understand how reading a val
can provoke a mutation attempt
error?
Steps to reproduce:
git clone git@github.com:BoD/ktor-http-client-sample.git
cd ktor-http-client-sample
./gradlew :sample-native-osx:build
samples/sample-native-osx/build/bin/macos/debugExecutable/sample-native-osx.kexe
Client request builder: add shortcuts for authentication headers
The idea was to provide a shortcut function for simple one-time usage. For more configs/options (like default auth on every request) you should use the auth
artifact instead.
Expected:
client.get<String>("google.com") {
basic("username", "password") // shortcut for header(HttpHeaders.Authorization, "Basic ${"$username:$password".encodeBase64()}")
bearer("token") //shortcut for header(HttpHeaders.Authorization, "Bearer ${token}")
}
webSocketSession method suspends indefinitely when there in connection error (Ktor beta)
So if I want to connect to a websocket it works as it should I can just use this:
socket = http.webSocketSession("some websocket I can connect to")
And won't block and just connects to the websocket.
But if I have a websocket were I might have a problem connecting to, this statement just blocks the coroutine and won't do anything
E.g. connecting to this websocket can never work and it just blocks, what the webSocketSession should never do:
socket = http.webSocketSession("wss://testabcde.com/ws")
It never throws an error or something. Using HttpTimeouts also doesn't help. Tested on okhttp and cio engines.
Does anyone have a solution for that? Using just http.webSocket works and throws an exception but thats not what I need.
Version is 2.0.0-beta-1 (I can only select eap on the affected versions)
HttpRequestRetry plugin expects Retry-After header value to be in milliseconds
The value of this field can be either an HTTP-date or an integer number of seconds (in decimal) after the time of the response.
Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
Two examples of its use are
Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
Retry-After: 120
In the latter example, the delay is 2 minutes.
HttpRequestTimeoutException should not inherit CancellationException in ktor http client
Details could be found in the thread:
https://jetbrains.team/im/group/2kbClc4SKdaq?message=B4cWn0SBMxJ&channel=2kbClc4SKdaq
HttpRequestRetry in KTOR 2.0 should allow for request altering between retries
Related to KTOR-572 by @rustam.siniukov ...
In site operations, it is useful when you have retries to be able to see retry traffic from the server viewpoint, as well as control retry decisions. This helps in cases where retry logic is causing storms of traffic across many clients even with falloff.
What is needed is the retry plugin to allow:
- when "deciding to retry or not", the lambda answering this questions requires visibility of the try count AND the request/response instances (i.e. response may have a header saying DON'T retry, or the opposite with a Retry-After header). Looking at count, headers, error code are all important.
- when "deciding the delay of the retry", the lambda answering this question needs the retry count AND the request/response instances since it might respect
Retry-After
style headers (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After). This applies to rate limiter headers (remaining, limit, reset, etc) as well. Alsox-no-retry
custom headers from the ops team blocking retries in an emergency at their edge firewall. Looking at count, headers, error code are all important. - before a retry the opportunity to modify the request object in needed based on the retry count and the request/response instances. This allows setting ops headers such as
x-retry-count: 5
which again allow edge control over retries. For example, in an outage recovery I might want to kill all retries with a count above 1, or any with a count at all to stop retries for a moment. The server needs to see evidence of the retry to do so, therefore modifying the headers is important.
The HttpRequestRetry
is useful but incomplete as it takes into account what the client wants, but not what a server/ops might want and to cooperate.
The request here is for the hooks for the events with the correct data, not implementing the specific examples given. They vary wildly other than Retry-After
which even browsers implement (https://stackoverflow.com/questions/3764075/retry-after-http-response-header-does-it-affect-anything).
Objections to changing boundary to internal on MultiPartFormDataContent?
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1970
I'd like to change this to internal so I can consume the boundary like so:
class MultiPartMixedDataContent(
parts: List<PartData>
) : MultiPartFormDataContent {
override val contentType: ContentType = ContentType.MultiPart.Mixed.withParameter("boundary", boundary)
}
Rename the 'Ios' client engine to more generic term to cover all Apple operating systems
The Ios
engine uses NSURLSession from the Foundation framework, which can be used not only for iOS but for other operating systems, including macOS, tvOS, etc. This leads to confusion that the Ios
engine can be used for iOS devices only. So it makes sense to rename the engine and artifacts to something more general, for example, Darwin or smth else.
Bearer Authentication: Queue requests until refresh of tokens is completed
Hi, I'm using Ktor 1.6.4 in a Kotlin Multiplatform project together with the Bearer Authentication feature.
We are logging the user out when the refresh of the token wasn't successful. Our Open ID Connect server responds with a 400 when the refresh token was used before for a refresh. So when there are multiple requests running in parallel the first refresh is successful but the other request already responds with a 401 and the Auth feature tries to refresh with the same refresh token this leads to a 400 error.
Is there something we can do to queue requests or is this the expected behaviour?
install(Auth) {
bearer {
loadTokens {
val tokenData = getTokenDataInteractor.execute() // Get tokens from storage
if (tokenData.status == SUCCESS && tokenData.data?.accessToken != null) {
BearerTokens(tokenData.data.accessToken, tokenData.data.refreshToken ?: "")
} else {
null
}
}
refreshTokens {
val tokenData = refreshTokenInteractor.execute() // Fetches and stores new token
if (tokenData.status == SUCCESS && tokenData.data?.accessToken != null) {
BearerTokens(tokenData.data.accessToken, tokenData.data.refreshToken ?: "")
} else {
null
}
}
}
}
XML Support in Ktor
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1570
Subsystem
Client
Is your feature request related to a problem? Please describe.
I want to make http requests to XML API.
Describe the solution you'd like
Create ktor-client-xml
like ktor-client-json to (de-) serialize request/response.
Motivation to include to ktor
XML very popular format for data transferring.
ProxyConfig.type checking for DIRECT instead of SOCKS
ProxyBuilder.socks sets up a Proxy of Proxy.Type.SOCKS, but the ProxyConfig.type is checking for Proxy.Type.DIRECT to set ProxyType to SOCKS.
Following tutorial from https://ktor.io/docs/proxy.html#platform-specific-configuration to setup with
a socks proxy (Tor embedded in Android Application running socks proxy on 127.0.0.1:9050) throws:
java.lang.IllegalStateException: Proxy of type UNKNOWN is unsupported by CIO engine.
Am I missing something?
Thanks!
KDoc: HttpRequestBuilder.header actually appends header value, does not set it
/**
* Sets a single header of [key] with a specific [value] if the value is not null.
*/
public fun HttpRequestBuilder.header(key: String, value: Any?): Unit =
value?.let { headers.append(key, it.toString()) } ?: Unit
The KDoc states that the header is set. This word usually signifies that the resulting value will be equal to the parameter, replacing existing value. But since the implementation appends the resulting value might not be what the caller expected.
The KDoc should be more explicit about what the function does. I can imagine even deprecating the current function name in favor of something more descriptive, such as appendHeader
instead of just header
[Ktor Client] CborFeature
Add support for CBOR serialization format to unlock ktor client usage with CBOR servers.
Solution
Base solution on existing JSON format and implement new serializer module based on kotlinx-serialization-cbor (MPP). Note that due to trying to keep existing API surface unaffected, a lot of code duplication was introduced between JsonFeature and CborFeature. Implemented in #2610
Long term I'd recommend unifying various different serializatrion formats under a ContentNegotiation
feature for the client (just like we have on the server). I'm happy to raise a PR for that if you'd accept breaking changes.
Add method to Client and ServerResponseException
Currently, the exception contains only the URL, but the used method is helpful too to understand and find the cause.
Solution
Add the method to the exception
SerializationException when serializing request body object of generic class type
It seems like when using ktor client with the kotlinx serialization feature for JSON<=>Entity conversions the use of a generic for the payload is broken (might be related to #KTOR-744). A test project that shows this issue can be found here: https://github.com/fkrauthan/ktor-client-issue/blob/master/src/main/kotlin/main.kt
The problem by the looks of it is that within the JsonFeature it picks the serializer based on bodyInstance::class.serializer()
which does not work for generics. The generic itself can be serialized without any issues when directly using kotlinx serialization.
Response types do not seem to have that issue.
Add filtering support in Ktor client response interceptor
The Ktor Client ResponseObserver interceptor is heavy, therefore it becomes tricky to avoid the overhead when one needs the interceptor just for a subset of requests. Since the interceptor is attached on the Client and not a single call, the only option one has currently is to use two different clients.
The filtering support for the client response interceptor would provide the possibility to enable the interceptor for certain requests only based on a request header or parameter or any other flag.
Allow using the client itself inside Auth plugin in the refreshTokens lambda.
Currently, it is not possible to use the client itself inside the auth feature, so if you want to refresh the token you need to use another different client or declaring the current one as lateinit var
val client = HttpClient() {
install(Auth) {
bearer {
loadTokens { ... }
refreshTokens {
// How can I do the request here with client itself?
// Maybe the `this` in this lambda should be the client?
}
}
}
}
Ktor Client JS: request to /example requests http://localhost/example
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1695
Ktor Version and Engine Used (client or server and name)
Ktor Client Common 1.3.1, in browser (JS)
Describe the bug
request to /example requests http://localhost/example
To Reproduce
Steps to reproduce the behavior:
- Clone https://github.com/Ribesg/Kita/tree/2c7941183ebcde1fc2193bd00a1bed53f1249b10 on a machine which can be reached from the network (a server)
git clone https://github.com/Ribesg/Kita cd Kita git reset --hard 2c7941183ebcde1fc2193bd00a1bed53f1249b10
- Run
./runServer.sh
- Browse to
https://yourserver:12345
- Try to login using whatever credentials (correct ones are admin/admin)
- See failing requests to
https://localhost/api/auth/login
instead ofhttps://yourserver:12345/api/auth/login
in the console
Expected behavior
Requests should use the current location.origin
when none is provided in the browser
Make `body` nullable for request builder
Currently in the HttpClient, when building a request, if you want to set the body to empty, you have to do body = EmptyContent
.
Why not just have the body nullable?
This the current contract requires me to have special knowledge about how to use EmptyContent
and do something like this:
CLIENT.request<String> {
url(website.pollUrl)
for (header in store.websiteExtractor.headers) {
header(header.key, header.value)
}
body = extractor.body?.injectVariables(website.data) ?: EmptyContent
}
when this requires no special knowledge and is easier:
body = extractor.body?.injectVariables(website.data)
Core
AttributeKey instance is identified by its identity instead of its name
In my project i need pass any attributes when intercept the call with an authorization, the attributes are put correctly in the call request but when get any attribute this generate a exception: java.lang.IllegalStateException: No instance for key AttributeKey: caller.id
. But I print the list of AttributeKeys, and the key is in the list: [AttributeKey: caller.siteId, AttributeKey: AuthContext, AttributeKey: SendPipelineExecutedAttributeKey, AttributeKey: EngineResponse, AttributeKey: caller.scopes, AttributeKey: caller.id, AttributeKey: client.id, AttributeKey: caller.status], I need help please.
Module "io.ktor:ktor-network (io.ktor:ktor-network-iosarm64)" has a reference to symbol kotlinx.coroutines/SingleThreadDispatcher|null[0]
Using the new memory model with versions:
kotlinVersion=1.6.0
coroutinesVersion=1.5.1-new-mm-dev2
ktorVersion=1.6.2-native-mm-eap-196
Produces the following error when referencing ktor-network:
Module "io.ktor:ktor-network (io.ktor:ktor-network-iosarm64)" has a reference to symbol kotlinx.coroutines/SingleThreadDispatcher|null[0]. Neither the module itself nor its dependencies contain such declaration.
This could happen if the required dependency is missing in the project. Or if there is a dependency of "io.ktor:ktor-network (io.ktor:ktor-network-iosarm64)" that has a different version in the project than the version that "io.ktor:ktor-network (io.ktor:ktor-network-iosarm64): 1.6.2-native-mm-eap-196" was initially compiled with. Please check that the project configuration is correct and has consistent versions of all required dependencies.The list of "io.ktor:ktor-network (io.ktor:ktor-network-iosarm64): 1.6.2-native-mm-eap-196" dependencies that may lead to conflicts:
1. "io.ktor:ktor-io (io.ktor:ktor-io-iosarm64): 1.6.2-native-mm-eap-196" (was initially compiled with "io.ktor:ktor-io (io.ktor:ktor-io-iosarm64)")
2. "stdlib: 1.6.0" (was initially compiled with "stdlib: 1.5.30-RC")
3. "org.jetbrains.kotlin.native.platform.CoreFoundation: 1.6.0" (was initially compiled with "org.jetbrains.kotlin.native.platform.CoreFoundation: 1.5.30-RC")
4. "org.jetbrains.kotlin.native.platform.darwin: 1.6.0" (was initially compiled with "org.jetbrains.kotlin.native.platform.darwin: 1.5.30-RC")
5. "org.jetbrains.kotlin.native.platform.iconv: 1.6.0" (was initially compiled with "org.jetbrains.kotlin.native.platform.iconv: 1.5.30-RC")
6. "org.jetbrains.kotlin.native.platform.posix: 1.6.0" (was initially compiled with "org.jetbrains.kotlin.native.platform.posix: 1.5.30-RC")
7. "org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-iosarm64): 0.17.0" (was initially compiled with "org.jetbrains.kotlinx:atomicfu (org.jetbrains.kotlinx:atomicfu-iosarm64): 0.16.1")
8. "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-iosarm64): 1.5.1-new-mm-dev2" (was initially compiled with "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-iosarm64): 1.5.1-new-mm-dev1")
Support for Unix domain sockets for both server and client
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/412
Netty (I believe) already has support for this (https://netty.io/4.1/api/io/netty/channel/unix/package-summary.html) so this may already be possible with the existing engines with some tweaking or configuration.
Example server use case
Authoring a Docker plugin: https://docs.docker.com/engine/extend/plugin_api/ that runs locally. With jlink
in Java 9 entire "native"-like services can be shipped.
Example client use case
Communicating with Docker engine which by default /var/run/docker.sock
. For example, curl
supports making requests to a UNIX socket:
curl --unix-socket /var/run/docker.sock http:/containers/json
[]
Pipeline version for debug
The basic idea is to have PipelineContext
implementation, that is used when -ea
flag enabled. This implementation should have no optimizations in preserve all stack traces.
Sockets no longer working on Android since 2.0.0-beta-1
Since https://github.com/ktorio/ktor/pull/2603 was merged the sockets no longer work on Android API level < 24. This PR migrated calls on socket()
to StandardSocketOptions
which is only available starting from Android API 24. This causes Ktor to crash.
No contextual serializers when KotlinxSerializationConverter is used
Repro: https://github.com/Reproducers/ktor-serialization-repro
If I add a custom serializer to the Json
, it doesn't work with Ktor, meanwhile, the same approach is working with Kotlin Serialization without Ktor.
serializersModule =
SerializersModule {
contextual(Either::class) { serializers: List<KSerializer<*>> ->
EitherSerializer(serializers[0], serializers[1])
}
}
Additionally, looks like empty json responses are not working correctly too but they works with Kotlin Serialization: https://github.com/Kotlin/kotlinx.serialization/issues/1850#issuecomment-1025773267
The repro has three modules:
either
which has theEither
class and its custom serializer, theEither
class must not be annotated asSerializable
without-ktor
has amain
function which prints multiple strings/objects after being serialized/deserialized with Kotlin Serialization.with-ktor
has amain
function which should print the analogous data to thewithout-ktor
but it crashes because there is no serializers in thecontextual
lambda.
IllegalStateException when writing in coroutine context backed by more than one thread
Expected results:
Successfully able to write to ByteWriteChannel regardless of coroutine context dispatched in
Actual results:
When the coroutine context write is called in is backed by more than 1 thread (e.g. Dispatchers.IO) after a few writes this exception get's thrown.
Exception in thread "DefaultDispatcher-worker-1" java.lang.IllegalStateException: Writing is not available in state Writing
at io.ktor.utils.io.internal.ReadWriteBufferState.startWriting$ktor_io(ReadWriteBufferState.kt:20)
at io.ktor.utils.io.ByteBufferChannel.setupStateForWrite$ktor_io(ByteBufferChannel.kt:263)
at io.ktor.utils.io.ByteBufferChannel.writeInt$suspendImpl(ByteBufferChannel.kt:2972)
at io.ktor.utils.io.ByteBufferChannel.writeInt(ByteBufferChannel.kt)
at EchoServer$process$1$1.invokeSuspend(EchoServer.kt:38)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
MRE project attached, changing the number of threads in newFixedThreadPoolContext
stops the error from happening, alternatively changing the delay from 500 to 100ms
Looks like a concurrency issue, it might be the expected behaviour however it's not documented anywhere I found/the error message isn't very descriptive, and it would also break in the default example in the docs
Related to https://youtrack.jetbrains.com/issue/KTOR-710 which is apparently not a kotlin-io bug https://github.com/Kotlin/kotlinx-io/issues/43
Possible duplicate of https://youtrack.jetbrains.com/issue/KTOR-3025
Prohibit Nesting of `install` Blocks for Client and Server Configuration
This code snippet is allowed, but leads to invalid configuration:
val client = HttpClient() {
install(JsonFeature) {
install(Auth) {
// ...
}
}
}
Drop @ExperimentalTime
It is stable with Kotlin 1.6.0
Ktor-Utils references a Java API not supported by Android
In the IntellijIdeaDebugDetector.kt class, present in the jvm sourceSet, the java.lang.management API is used. This API is not present in the versions of Java Android supports, which causes an ugly warning when depending on ktor-client-core, which has ktor-utils as a transitive dependency.
It seems ktor-utils does not have an android sourceSet, which would be required to deliver an actual
implementation of the IntellijIdeaDebugDetector that uses an API supported by Android.
ByteReadPacket.headerSizeHint is unused
May be it's a good time to remove headerSizeHint
in 2.0, as it not used at all?
Hooks don't work with routing scoped plugins
If you write a simple hook in the following way:
val OnCallHook = object : Hook<() -> Unit> {
override fun install(application: ApplicationCallPipeline, handler: () -> Unit) {
application.intercept(ApplicationCallPipeline.Call) {
handler()
}
}
}
And a simple routing-scoped plugin:
class MyConfig {
var myValue: Int = 0
}
val MyRoutePlugin = createRouteScopedPlugin("MyRoutePlugin", ::MyConfig) {
on(OnCallHook) {
println(pluginConfig.myValue)
}
}
Then, install this plugin into a couple of routes:
routing {
route("/1") {
install(MyRoutePlugin) {
myValue = 1
}
get {
call.respond("Hello-1")
}
}
route("/2") {
install(MyRoutePlugin) {
myValue = 2
}
get {
call.respond("Hello-2")
}
}
get("/3") {
call.respond("Hello-3")
}
}
If you request /1
you'll get the following output:
1
2
(expected: only "1")
If you request /2
you'll get the following output:
1
2
(expected: only "2")
If you request /3
you'll get, again, the following output:
1
2
(expected: no output)
So, hooks don't respect installation into a single route independently
URLBuilder from string with trailing slash or from `Url` with no trailing slash, produces double slash when appending segments
I don't know if this is intended behavior, but if you create a URLBuilder
with a url that has a host part with a trailing slash, then call appendPathSegments
on the builder, the resulting URL will have a double-slash between the host and the first path component. I dug around in URLBuilder.takeFromUnsafe
, and this edge case is triggered by lines 99-102 there:
val relativePath = when (slashCount) {
1 -> listOf("")
else -> emptyList()
} + rawPath.split('/')
In the case of a trailing slash, this leaves relativePath == listOf("", "")
, which is then assigned to URLBuilder.encodedPathSegments
. I'm not familiar enough with all the use cases of this method to attempt a fix myself at this moment, and wanted to be sure this isn't intentional before doing anything else.
This can be reproduced by adding this test in io.ktor.tests.http.URLBuilderTest
@Test
fun fromStringWithTrailingSlashAppendsSinglePathSegment() {
val url = URLBuilder("https://example.com/").appendPathSegments("foo")
assertEquals("https://example.com/foo", url.buildString())
}
which fails with:
Expected :https://example.com/foo
Actual :https://example.com//foo
Edited to add:
This also happens if you create an URLBuilder
from a URL
created from a string, even without a trailing slash. Here's another test case:
@Test
fun fromUrlAppendsSinglePathSegment() {
val url = URLBuilder(Url("https://example.com")).appendPathSegments("foo")
assertEquals("https://example.com/foo", url.buildString())
which also fails with:
Expected :https://example.com/foo
Actual :https://example.com//foo
Add modulepath support for Java >= 9 breaking-change
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1137
Ktor application can't be compiled with JDK >=9. I'm getting a lot of such errors:
error: module kotlinx.io.jvm reads package io.ktor.http from both ktor.server.core and ktor.http.jvm
error: module kotlinx.io.jvm reads package io.ktor.http.content from both ktor.server.core and ktor.http.jvm
error: module kotlinx.io.jvm reads package io.ktor.util from both ktor.server.core and ktor.utils.jvm
error: module kotlinx.coroutines.jdk8 reads package io.ktor.http from both ktor.server.core and ktor.http.jvm
error: module kotlinx.coroutines.jdk8 reads package io.ktor.http.content from both ktor.server.core and ktor.http.jvm
error: module kotlinx.coroutines.jdk8 reads package io.ktor.util from both ktor.server.core and ktor.utils.jvm
error: module kotlinx.coroutines.core reads package io.ktor.http from both ktor.server.core and ktor.http.jvm
error: module kotlinx.coroutines.core reads package io.ktor.http.content from both ktor.server.core and ktor.http.jvm
error: module kotlinx.coroutines.core reads package io.ktor.util from both ktor.server.core and ktor.utils.jvm
...................
Support of Java 8 ends quite soon, so it makes sense to start supporting Jigsaw. IMHO, this should be prioritized, as it will require breaking backward compatibility. The less Ktor is popular, the easier would be to break backward compatibility. And Ktor will definitely become more popular over time.
Ktor Version
1.2.0
JVM Version, Operating System and Relevant Context
JDK 12
StringValuesBuilder.appendIfNameAbsent appends only if name is already present
In StringValues.kt, the if
statement should be inverted in appendIfNameAbsent()
and appendIfNameAndValueAbsent()
:
public fun StringValuesBuilder.appendIfNameAbsent(name: String, value: String): StringValuesBuilder = apply {
if (!contains(name)) return@apply
append(name, value)
}
At present it appends only if it is present.
Compression slow due to using BEST_COMPRESSION for deflate/gzip
Ktor currently uses BEST_COMPRESSION for java.util.zip.Deflater
when the feature Compression
is enabled. It should use DEFAULT_COMPRESSION rather than BEST_COMPRESSION.
BEST_COMPRESSION is generally many times slower than DEFAULT_COMPRESSION for miniscule improvements to compressed size, making BEST_COMPRESSION almost always the wrong choice when compression time is part of the equation.
Fixing this would probably also partially fix KTOR-1341 (compression is still expensive, but significantly less so).
Ideally compression level should be configurable, since the optimal choice depends on expected network speed, CPU speed, and which implementation the JDK uses, but DEFAULT_COMPRESSION is a pretty good default.
java.lang.NoSuchMethodError: java.nio.ByteBuffer.limit(I)Ljava/nio/ByteBuffer when Ktor is built with JDK 9+
To reproduce:
- Run
./gradlew publishToMavenLocal
in the root directory of the Ktor framework repository on the latest master. Use JDK 9+ for a build. - Use the
1.4.3-SNAPSHOT
(at the time of writing) version of Ktor in a project with Maven local repository. - Build and run the following code with JDK 8:
suspend fun main(args: Array<String>) {
val client = HttpClient(CIO)
client.get<String>("http://example.com")
}
As the result unexpected exception will be thrown:
Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.limit(I)Ljava/nio/ByteBuffer;
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at io.ktor.utils.io.ExceptionUtilsJvmKt$createConstructor$$inlined$safeCtor$3.invoke(ExceptionUtilsJvm.kt:106)
at io.ktor.utils.io.ExceptionUtilsJvmKt$createConstructor$$inlined$safeCtor$3.invoke(ExceptionUtilsJvm.kt)
at io.ktor.utils.io.ExceptionUtilsJvmKt.tryCopyException(ExceptionUtilsJvm.kt:66)
at io.ktor.utils.io.ByteBufferChannelKt.rethrowClosed(ByteBufferChannel.kt:2606)
at io.ktor.utils.io.ByteBufferChannelKt.access$rethrowClosed(ByteBufferChannel.kt:1)
at io.ktor.utils.io.ByteBufferChannel.setupStateForWrite$ktor_io(ByteBufferChannel.kt:211)
at io.ktor.utils.io.ByteBufferChannel.copyDirect$ktor_io(ByteBufferChannel.kt:2758)
at io.ktor.utils.io.ByteReadChannelJVMKt.copyTo(ByteReadChannelJVM.kt:276)
at io.ktor.client.engine.cio.UtilsKt$withoutClosePropagation$2.invokeSuspend(utils.kt:173)
at io.ktor.client.engine.cio.UtilsKt$withoutClosePropagation$2.invoke(utils.kt)
at io.ktor.utils.io.CoroutinesKt$launchChannel$job$1.invokeSuspend(Coroutines.kt:129)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.lang.NoSuchMethodError: java.nio.ByteBuffer.limit(I)Ljava/nio/ByteBuffer;
at io.ktor.utils.io.ByteBufferChannel.prepareBuffer(ByteBufferChannel.kt:193)
at io.ktor.utils.io.ByteBufferChannel.setupStateForWrite$ktor_io(ByteBufferChannel.kt:242)
at io.ktor.utils.io.ByteBufferChannel.copyDirect$ktor_io(ByteBufferChannel.kt:2758)
at io.ktor.utils.io.ByteReadChannelJVMKt.copyTo(ByteReadChannelJVM.kt:276)
at io.ktor.utils.io.ByteReadChannelKt.copyAndClose(ByteReadChannel.kt:267)
at io.ktor.utils.io.ByteReadChannelKt.copyAndClose$default(ByteReadChannel.kt:266)
at io.ktor.network.sockets.TimeoutExceptionsCommonKt$mapEngineExceptions$2.invokeSuspend(TimeoutExceptionsCommon.kt:61)
at io.ktor.network.sockets.TimeoutExceptionsCommonKt$mapEngineExceptions$2.invoke(TimeoutExceptionsCommon.kt)
... 7 more
You can find a similar issue here.
Cyclic dependency issue in latest 2.0 (main branch)
Detecting cycle in external variants for :
io.ktor:ktor-network:main-235:
- io.ktor:ktor-http-cio-jvm:main-235:
- io.ktor:ktor-http-cio:main-235:
- io.ktor:ktor-server-host-common:main-235:
- io.ktor:ktor-server-host-common-kotlinMultiplatform:main-235:
- io.ktor:ktor-server-netty:main-235: com.example:bcf:0.0.1
Dependency resolution has ignored the cycle to produce a result. It is recommended to resolve the cycle by upgrading one or more dependencies.
parameterOf() should have a variant that takes in a Map<String, List<String>>
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1833
Subsystem
Server
Is your feature request related to a problem? Please describe.
There is currently no way to build a Parameters instance from a Map, even though the internal representation of the values is a map. Parameters
can only be constructed using:
parametersOf()
parametersOf(name: String, value: String)
parametersOf(name: String, values: List<String>)
parametersOf(vararg pairs: Pair<String, List<String>>)
ParametersImpl
is internal and cannot be constructed directly.
Describe the solution you'd like
I'd like there to be a parametersOf(parameters: Map<String, List<String>>)
:
fun parametersOf(vararg parameters: Map<String, List<String>>): Parameters =
ParametersImpl(parameters)
Motivation to include to ktor
Seems like it should be there. There are many cases where you're already working with a parameters map, and having to transform that into a list of pairs just to have it retransformed back under the hood seems wasteful.
URLBuilder: Move Default Values to build() function
For implementing a baseURL feature PR (KTOR-730, KTOR-759, KTOR-699), it is not possible to distinguish between the relative urlString/sub
and the explicit, overriding the given baseURLString http://localhost/sub
, because both different urlStrings resolves to the same URLBuilder (http://localhost/sub/
).
Motivation:
Make all parameters of URLBuilder
optional and move the default values into the build()
function. This allows you inside the URLBuilder to decide, if the requested URL is only a relative URL or an explicit urlString.
// See [PR](https://github.com/ktorio/ktor/pull/2212) for `defaultRequest`
@Test
@Ignore
fun overriddenLocalhostWithPath() {
val localhost = HttpRequestBuilder().apply {
url.takeFrom("http://localhost/sub")
}.defaultRequest().build().url
assertEquals("http://localhost/sub", localhost.toString())
}
Query of pre-signed URL has been altered after decode and re-encode process breaking-change
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/429
should the query string from URI be considered encoded already and skip the decode and re-encode process?
Docs
Clarify "Creating HTTP APIs" documentation regarding testing approach
The documentation for "Creating HTTP APIs" for Ktor has a section on "Automated Testing". As a part of the testing approach, it is suggested that a "testing = true" parameter needs to be added to the Application.module() function. However, a reason is not given as to why this is necessary to do.
Based on my experience, making changes to production code for the sake if testability is a code smell and should be avoided. Therefore, I tried to remove this "testing" parameter and so far as I can tell. it appears that the test still functions as expected.
Searching for other documentation regarding testing Ktor, the "Testing Overview" section illustrates the most basic of testing approaches and it doesn't appear as if adding a "testing" parameter is necessary.
I would be happy to make a PR on this part of the documentation to clarify, however before doing so I thought it would be best to confirm with the team:
- Was there a reason for adding the "testing" parameter? Is the code still able to be properly tested without this parameter?
- Given my lack of knowledge around Ktor and testing Ktor, are the other considerations with regards to testing that need to be taken into consideration for the documentation?
If the above points can be clarified I'd be happy to make a PR for this. Thank you!
[Doc] invalid KDoc link for https://ktor.io/docs/http-client-engines.html#darwin
link from
DarwinClientEngineConfig
is invalid
Update the 'Manual Configuration' help link after the 2.0.0 release
Where did 1.6.8 docs go?
Hey guys,
We are in the middle of development using the 1.6.8 version of Ktor, and the docs have disappeared!
Ktor app with Kotlin/Native fails with "There is no event loop. Use runBlocking { ... } to start one."
I created a sample project by following the documentation from https://github.com/ktorio/ktor-documentation/tree/main/codeSnippets/snippets/embedded-server-native
Here's are the sources https://github.com/antonarhipov/ktor-app
What I run ./gradlew runReleaseExecutableNative the result is a failure:
/Users/anton/tmp/ktor-app % ./gradlew runReleaseExecutableNative
> Configure project :
Kotlin Multiplatform Projects are an Alpha feature. See: https://kotlinlang.org/docs/reference/evolution/components-stability.html. To hide this message, add 'kotlin.mpp.stability.nowarn=true' to the Gradle properties.
The property 'kotlin.mpp.enableGranularSourceSetsMetadata=true' has no effect in this and future Kotlin versions, as Hierarchical Structures support is now enabled by default. It is safe to remove the property.
The property 'kotlin.native.enableDependencyPropagation=false' has no effect in this and future Kotlin versions, as Kotlin/Native dependency commonization is now enabled by default. It is safe to remove the property.
> Task :runReleaseExecutableNative FAILED
Uncaught Kotlin exception: kotlin.IllegalStateException: There is no event loop. Use runBlocking { ... } to start one.
at 0 ktor-app.kexe 0x108ff3304 kfun:kotlinx.coroutines.takeEventLoop#internal + 212
at 1 ktor-app.kexe 0x108ff315c kfun:kotlinx.coroutines.OldDefaultExecutor#dispatch(kotlin.coroutines.CoroutineContext;kotlinx.coroutines.Runnable){} + 76
at 2 ktor-app.kexe 0x108fecf04 kfun:kotlinx.coroutines.internal#resumeCancellableWith__at__kotlin.coroutines.Continuation<0:0>(kotlin.Result<0:0>;kotlin.Function1<kotlin.Throwable,kotlin.Unit>?){0§<kotlin.Any?>} + 1124
at 3 ktor-app.kexe 0x108ff0968 kfun:kotlinx.coroutines.intrinsics#startCoroutineCancellable$default__at__kotlin.coroutines.SuspendFunction1<0:0,0:1>(0:0;kotlin.coroutines.Continuation<0:1>;kotlin.Function1<kotlin.Throwable,kotlin.Unit>?;kotlin.Int){0§<kotlin.Any?>;1§<kotlin.Any?>} + 120
at 4 ktor-app.kexe 0x108fd9560 kfun:kotlinx.coroutines#launch__at__kotlinx.coroutines.CoroutineScope(kotlin.coroutines.CoroutineContext;kotlinx.coroutines.CoroutineStart;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,kotlin.Unit>){}kotlinx.coroutines.Job + 560
at 5 ktor-app.kexe 0x108fd96b3 kfun:kotlinx.coroutines#launch$default__at__kotlinx.coroutines.CoroutineScope(kotlin.coroutines.CoroutineContext?;kotlinx.coroutines.CoroutineStart?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,kotlin.Unit>;kotlin.Int){}kotlinx.coroutines.Job + 243
at 6 ktor-app.kexe 0x109078113 kfun:io.ktor.server.cio.CIO#create(io.ktor.server.engine.ApplicationEngineEnvironment;kotlin.Function1<io.ktor.server.cio.CIOApplicationEngine.Configuration,kotlin.Unit>){}io.ktor.server.cio.CIOApplicationEngine + 2483
at 7 ktor-app.kexe 0x10906d01a kfun:io.ktor.server.engine#embeddedServer__at__kotlinx.coroutines.CoroutineScope(io.ktor.server.engine.ApplicationEngineFactory<0:0,0:1>;kotlin.Array<out|io.ktor.server.engine.EngineConnectorConfig>...;kotlin.collections.List<kotlin.String>;kotlin.coroutines.CoroutineContext;kotlin.Function1<0:1,kotlin.Unit>;kotlin.Function1<io.ktor.server.application.Application,kotlin.Unit>){0§<io.ktor.server.engine.ApplicationEngine>;1§<io.ktor.server.engine.ApplicationEngine.Configuration>}0:0 + 634
at 8 ktor-app.kexe 0x10906cc29 kfun:io.ktor.server.engine#embeddedServer__at__kotlinx.coroutines.CoroutineScope(io.ktor.server.engine.ApplicationEngineFactory<0:0,0:1>;kotlin.Int;kotlin.String;kotlin.collections.List<kotlin.String>;kotlin.coroutines.CoroutineContext;kotlin.Function1<0:1,kotlin.Unit>;kotlin.Function1<io.ktor.server.application.Application,kotlin.Unit>){0§<io.ktor.server.engine.ApplicationEngine>;1§<io.ktor.server.engine.ApplicationEngine.Configuration>}0:0 + 873
at 9 ktor-app.kexe 0x1090a7934 Init_and_run_start + 740
at 10 ktor-app.kexe 0x1090a7a3d Konan_main + 13
at 11 libdyld.dylib 0x7fff2044bf3c start + 0
FAILURE: Build failed with an exception.
Update limitations for Kotlin/Native
https://ktor.io/docs/native-server.html#limitations
K/N server supports WebSockets
JMXReporter not included in ktor-metrics:1.6.8
According to the documentation the JmxReporter
should be included in the metrics-jmx
dependency, but that doesn't appear to be the case in version 1.6.8
. I had to manually add the dependency to "io.dropwizard.metrics:metrics-jmx:4.2.9" instead.
Screenshot with the new project wizard on the "Create a new project" topic is outdated
I'm trying to step through the "Creating a Client Application" tutorial, following this page:
https://ktor.io/docs/eap/getting-started-ktor-client.html#new-project
But my "IntelliJ IDEA Ultimate" IDE does not include the "JVM Application" option.
Please refer to the image attached. In the "New Project" windowm, under "Project Template", in the JVM section, it only lists "Console Application", but not the "JVM Application" option.
What can I do in this case?
Provide an example how to use new MultiPartFormDataContent (#KTOR-325)
Recently PR for #KTOR-325 has been pushed, and I'd like to have a test or an example in documentation how to use this new API as changing the contentType is what I'd like to have in my project (currently in my version (I had to copy the whole file into my project) it receives baseContentType
with a restriction to match ContentType.MultipartAny
without any parameters.
Client docs for desktop are misleading
As evident from this slack thread, ktor-client docs for native desktop platforms are misleading. The docs should be explicit that they are referring only to desktop apps built with kotlin native. Additionally, it might also be worthwhile to review the language for jvm platform to indicate that client engines there can be used for jvm desktop apps as well, not just servers.
This also leads to false-positive issues like KTOR-3794
In docs and generated Gradle, Prometheus is misspelled as Promteteus
This might seem a bit of a petty issue to raise, but I've noticed that in the documentation, code snippets, and Gradle generated files, Prometheus is misspelled as Prometeus.
implementation("io.micrometer:micrometer-registry-prometheus:$prometeus_version")
Despite it being spelled as "prometheus" right next to it.
Annoying, but really annoying for some people.
Again, I appreciate it's a little petty, but help a guy with OCD out? :)
Client logging: no description of default loggers' behavior on different platforms
There is no description in the documentation of what type of logger is used on different platforms by default, particularly on JVM and POSIX.
The default logger for JVM (Logger.DEFAULT
) is an slf4j logger that requires implementation, e.g Logback, and the default logger for POSIX (Logger.SIMPLE
) is just a logger that prints everything to stdout.
Mention about closing ActorSelector manager
This issue was imported from GitHub issue: https://github.com/ktorio/ktorio.github.io/issues/283
This page has CIO sockets sample but has no explicit ActorSelectorManager
close.
https://ktor.io/servers/raw-sockets.html#simple-client-connecting-to-an-echo-server
TomCat Documentation
It would be good to have some docs on how to deploy using TomCat, and also how to configure SSL and also to have app run off of sub-route (i.e. https://domain.com/my-app-route)
default resource package don't work
Simply defining staticBasePackage
as stated in the documentation does not work. https://ktor.io/docs/eap/serving-static-content.html#default-resource-package
static("/") {
staticBasePackage = "static"
}
Update Documentation and Code for DoubleReceive Feature
Include changes from hands-on PR: Update 03_customer-routes.md #120
(Changes proposed initially in the kotlin-hands-on repository, since the tutorial is now moved to https://ktor.io/docs/creating-http-apis.html, linking it here):
The section about how to create a mutable list and where it should be in the code was a little bit confusing
Changing the return HttpStatusCode of Post route to store customer. In that this, case it's more common to use 201 (Created) instead of 202 (Accepted)
Nested routing fails to match route
Ktor fails to match the route GET /my/abc.json
with the following routing configuration:
routing {
route("/my") {
route("/{id}") {
get(".json") {
}
}
}
}
For it to work it has to be collapsed to:
routing {
route("/my") {
get("/{id}.json") {
}
}
}
Update URL for the 'Adding Ktor dependencies' topic and add redirects
Insecure user session samples in documentation
Right now documentation samples (here and here) don't mention authentication and encryption configuration of sessions which may result in insecure implementation if user doesn't already know importance of it and won't configure it on their own.
Unsigned/unencrlypted sessions are very insecure and some users may not be aware of it.
Also documentation and samples should not provide a sample of a key, and if a sample given then it should be blacklisted, so users won't be able to copy paste code snippet from documentation and reuse it, making the implementation insecure.
Update Documentation and Code for HSTS Feature
Update Documentation and Code for Webjars Feature
ktor.io/learn typo
I noticed that on https://ktor.io/learn/ Server-Sent Events are referred as Server-Side Events. I guess that's a typo.
Update Documentation and Code for HttpsRedirect Feature
Provide better support for Ktor clients
I came across Ktor looking for a HTTP client that could do streaming but in specific chunks. I found it to be insanely difficult to do using Netty, Spring WebClient, Reactor HttpClient etc. I was able to do it in Ktor comparatively easily but came across few issues:
- Streaming is mentioned in the document almost as a joke, not to mention the shown code doesn't compile. https://ktor.io/clients/http-client/quick-start/streaming.html
- There's no mention of the client Maven GAVs, anywhere, in the docs. After some digging around, I came across engines, and assumed that those correspond directly to the client libraries. This can be much clearly documented.
- The API seems to be too keen on throwing exceptions. For instance, reading
n
bytes from a stream fails with an exception when the last chunk is < n bytes. Really? - I ended up with the following code, but it seems too much boilerplate that the library could easily do.
return flow {
HttpClient(CIO).use {
it.get<HttpStatement>(uri.scheme, uri.host, uri.port, uri.path).execute { response: HttpResponse ->
val channel = response.receive<ByteReadChannel>()
var bytesRead = 1
while (bytesRead > 0) {
val buffer = ByteBuffer.allocate(chunkSize)
val packet = channel.readRemaining(buffer.capacity().toLong(), 1)
bytesRead = packet.readAvailable(buffer)
packet.release()
if (bytesRead > 0) emit(buffer.flip())
}
}
}
}
Deploy Ktor application to docker topic contains hard coded project name
https://ktor.io/docs/docker.html#prepare-docker-image
The following line COPY ./build/install/docker/ /app/
from the Dockerfile
is confusing because the name of a user's project could be and most likely will be different from docker
.
Improvements for Docker sample in documentation
Feedback from https://kotlinlang.slack.com/archives/C0A974TJ9/p1634659805496800 about sample from documentation article.
./gradlew installDist
has to be run manually before running docker which seems rather odd considering most use cases are deployment on the cloud.- Naming the project docker could be confused with running docker itself
Suggested improved Dockerfile:
FROM gradle:6-jdk8 AS build
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle installDist --no-daemon
FROM openjdk:8-jdk
EXPOSE 8080:8080
RUN mkdir /app
COPY --from=build /home/gradle/src/build/install/docker/ /app/
WORKDIR /app/bin
CMD ["./docker"]
Code Snippets use Groovy in build files as opposed to default Kotlin option for Wizard
We default the wizard to use Kotlin for Gradle files. However, the code snippets from documentation all use Groovy, and it's inconsistent experience for someone starting a new project and then referring to documentation.
We have two solutions
- Provide Groovy/Kotlin variants for everything
- Convert all to Kotlin
The issue with the first option is the complexity of maintaining both build systems (and how we'd interact with these in the IDE). Let's discuss prior to resolving.
Improve documentation for native/Apple client engines
The native client engines documentation mentions two client engines (ios
and curl
).
Looking at the documentation it seems the ios
implementation will only work for iOS targets, but it actually supports other Apple targets as well (like macOS).
In case of a macOS app you should probably prefer the ios
implementation (using NSURLSession
) over the curl
one, right?
It would be great if the documentation could be a little more specific about which targets are support.
Article about storing sensitive data and accessing it in application.conf
We need to describe a secure way of storing sensitive data, like credentials, and a way of accessing them in Ktor application.
Update Documentation and Code for CallId Feature
Error in 2.0 doc/sample for HttpClient retry
https://helpserver.labs.jb.gg/help/ktor/2.0.0/client-retry.html#configure_retry
status() should be property not function. With latest 2.0 it does not compile.
Client HttpCache feature is not documented
Feature to Plugin changes in Documentation
We should update all docs and tutorials to reference Plugin. First reference of explaining plugins should reference the fact that a plugin was previously known as a Feature. If any URL has the word feature in it, a redirect should be put in place
Update server dependencies and imports in docs for 2.0.0
Generator
Update project generator for 2.0.0
Both plugin and website need to be updated.
Major changes are:
- Moving dependencies outside of
ktor-server
KTOR-1142 - Renaming
features
toplugins
in code and package names KTOR-2370 - Moving all server code to
io.ktor.server
package KTOR-2865
Generator performance: cache Maven requests
We are getting more users and generator gets higher RPS rate which leads to memory usage growth.
We need to consider caching all internal requests to fit into our memory limits.
References for kotlinx.serialization plugin sample code in a new Ktor project created with Maven build system are unresolved
IntelliJ IDEA 2022.1 EAP (Ultimate Edition)
Build #IU-221.3635, built on January 24, 2022
Create new Ktor project with wizard, specify Maven as build system, add sample code :
{width=419px}
Add kotlinx.serialization plugin and create project.
Expected: All references in sample code are resolved correctly
Actual: References in sample code are unresolved because dependency for kotlin-maven-serialization unresolved
Migrations of the client code are not working for queries with non-trivial expression body
Migrations are unavailable
https://ktor-plugin.europe-north1-gke.intellij.net/migrations -- returns 500
From IDE it looks like after clicking migrate
button, nothing happens and a failure message appears with a Try again
button
Simplify plugin descriptions in wizard, remove empty options
Infrastructure
Logback transient depencency from ktor-server-test-host
I do believe I shouldn't get a transient dependency to logback from the testframework. The point of SLF4J after all is that you nor I should worry about which logging framework the other uses.
Please ensure that we don't get a possibly conflicting logging framework unexpectedly from your depencencies
Infrastructure: Build with JDK 11 for all modules fails: Can't inline metric micrometer because it uses jvm target 8
Kotlin 1.6.0 uses JVM target 8 by default, and Ktor dropped support for JVM target 6 and 7. If you want to use JDK 11 for all! modules, the Kotlin compiler cannot inline jvm 8 target binary code into jvm 11 byte code.
So there is no need to define JVM target 8 explicit in 1 module.
=> Remove JVM target 8 in metrics micrometer
EAP Naming: main-number conflicts with dependencies
If you want to use the latest eap release of Ktor and use a library, which uses Ktor as api
dependency, eg a Ktor plugin, Gradle don't use the main-buildNumber
release over a published version.
Reason: 1.5.3
is preferred over main-133
. The eap builds should use the version property with the prefix instead, eg 1.6.0-main-133
. This tells Gradle to use the main
eap version for all dependencies, because 1.6.0
> 1.5.4
.
See the attached project
Run the test with/without removed line implementation(ratelimit)
in build.gradle.kts
.
Apple Arm: 'Resolving NPM dependencies using yarn' returns 139
If you want to build Ktor (main branch) on an Apple Arm, the build exits with: Resolving NPM dependencies using yarn' returns 139
.
I don't know the exact reason, but it is resolved with the latest nodeJS version: 16.13.1.
Simple reproducer is attached.
nodeYarn.zip
Build and test on Apple Silicon Arm
Currently it is not possible to build and test Ktor (server and clients) on Apple Silicon with Xcode 13 and macOS 12. This release removes all x64
frameworks, so every hardcoded macosX64
target has to be replaced. This also applies to curl.
IntelliJ IDEA Plugin
Set Default LogLevel INFO for Code Generated by Plugin
Provide the description for the 'Create test for Ktor module/route' intention action
Currently, it shows Preview isn't available
.
The plugin adds the unnecessary 'kotlinx-html-jvm' dependency when generating the HTML DSL project
The plugin adds two dependencies for HTML DSL:
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.3")
implementation("io.ktor:ktor-server-html-builder:$ktor_version")
}
ktor-server-html-builder
should be enough.
creating new project from scratch result in app with failing build
Steps to reproduce start following this tutorial.
The problem is that created project has not done import
import io.ktor.server.plugins.contentnegotiation.*
ktor plugin - 213.7172.25
IntelliJ IDEA 2021.3.3 (Ultimate Edition) Build #IU-213.7172.25
Freeze the screen when I create routes
Hello,
it happens very often that when I program that the IDE goes to freeze and I have to force the closure.
This screen freezing happens when I write the routes on KTOR (an example attached).
In the attached example I mistakenly added a "d" and the IDE froze
Those few times it doesn't freeze the IDE doesn't find me errors or highlights correct code as incorrect.
Also the weird thing that it doesn't produce errors or crashes but I think the IDE can't calculate the help it usually gives me. The PC during freezing does not increase or decrease the load on the CPU and RAM. I have a MacBook Pro 16 with an i9 processor and os version Big Sur 11.4.
Support test generation from endpoints
Don't perform migrations for MPP projects
Migrations have known problems when it comes to MPP due to the lack of support of Kotlin MPP in IDE.
We must not show migration notification and/or perform migrations on MPP modules.
The main issue is that the current build tools API in IntelliJ (see DependencyModifierService) does not work properly with Kotlin MPP DSL in Gradle because this DSL is not supported.
If you run a migration for Ktor that tries to work with MPP dependencies in the same way as with a normal dependencies, it will just add a new outer block of dependencies { ... }
in your build script. Instead, it must be adding dependencies inside macOs64 { dependencies { ... } }
or other similar places for all relevant platforms
The CallID plugin missing in a New Project wizard
https://ktor.io/docs/call-id.html
Should be in the Monitoring
group.
IDEA plugin missing the Session authentication
Currently, the plugin contains only the Sessions plugin. There is also a separate Session authentication provider: https://ktor.io/docs/session-auth.html
Install plugin completion doesn't work with custom built version of Ktor from main
Migrate Generator to New Testing API
localization issue with new project wizard - plugin page
IU-221.4501.155, JRE 11.0.14+9-b1993.2x64 JetBrains s.r.o., OS Mac OS X(aarch64) v12.2.1, screens 5120.0x2880.0; Retina
Steps to reproduce the issue
- install language pack
- start IntelliJ IDEA
- create new Ktor project
- at plugin page, search "json"
- click "Add" for kotlinx.serialization
Expected
... プラグイン が追加されました
Actual:
... プラグインs が追加されました
Seems like "s" is added in a code for a message in English, to make "plugin" as "plugins"(plural), which is not appropriate for messages to be localized.
There were similar issues in IDE/bundled plugin and they were fixed already. Need a similar fix with Ktor (plugin?) as well.
{width=719px}
IDE action to migrate to 2.0.0
Ktor plugin is asking to migrate to EAP versions
Nightly 2022.1
Sort endpoints in Endpoint view and when creating tests
Currently, tests for endpoints may appear in any order in multi-test dialogs and in the created tests file itself. This is hardly testable and for larger projects the order of the tests might be a mess.
Instead of showing them in random order, we can sort them by URL/HTTP method and then they will look prettier and it will be easier to test them since the order is always the same and it does not depend on what we found first.
StackOverflowError when opening Enpoints view with local Routing function
Actions:
- Create a new Ktor project without changing anything in the wizard
- Insert this code snipped into Routing.kt:
package com.example.plugins
import io.ktor.server.application.*
import io.ktor.server.routing.*
fun Application.configureRouting() {
fun Routing.apple() {
get("/apple") {}
}
routing {
apple()
}
}
- Open Endpoints view
Expectations: You see this /apple endpoint
Reality: Endpoints view is empty and StackOverflowError happens
Stacktrace
java.lang.StackOverflowError
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.awaitWithCheckCanceled(ProgressIndicatorUtils.java:359)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.awaitWithCheckCanceled(ProgressIndicatorUtils.java:354)
at com.intellij.util.indexing.RegisteredIndexes.await(RegisteredIndexes.java:191)
at com.intellij.util.indexing.RegisteredIndexes.waitUntilIndicesAreInitialized(RegisteredIndexes.java:91)
at com.intellij.util.indexing.FileBasedIndexImpl.waitUntilIndicesAreInitialized(FileBasedIndexImpl.java:414)
at com.intellij.util.indexing.events.ChangedFilesCollector.ensureUpToDate(ChangedFilesCollector.java:190)
at com.intellij.util.indexing.FileBasedIndexImpl.ensureUpToDate(FileBasedIndexImpl.java:763)
at com.intellij.psi.stubs.StubIndexEx.getContainingIds(StubIndexEx.java:352)
at com.intellij.psi.stubs.StubIndexEx.processElements(StubIndexEx.java:158)
at com.intellij.psi.stubs.StubIndex.getElements(StubIndex.java:99)
at com.intellij.psi.stubs.StubIndex.getElements(StubIndex.java:87)
at org.jetbrains.kotlin.idea.stubindex.KotlinMultifileClassPartIndex.get(KotlinMultifileClassPartIndex.kt:17)
at org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil$getMultifileClassForPart$1.invoke(StaticFacadeIndexUtil.kt:32)
at org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil$getMultifileClassForPart$1.invoke(StaticFacadeIndexUtil.kt:12)
at org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt$sam$com_intellij_openapi_util_Computable$0.compute(ApplicationUtils.kt)
at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:920)
at org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt.runReadAction(ApplicationUtils.kt:20)
at org.jetbrains.kotlin.idea.stubindex.StaticFacadeIndexUtil.getMultifileClassForPart(StaticFacadeIndexUtil.kt:31)
at org.jetbrains.kotlin.idea.caches.resolve.IDEKotlinAsJavaSupport.findPackageParts(IDEKotlinAsJavaSupport.kt:195)
at org.jetbrains.kotlin.idea.caches.resolve.IDEKotlinAsJavaSupport.getKotlinInternalClasses(IDEKotlinAsJavaSupport.kt:189)
at org.jetbrains.kotlin.asJava.finder.JavaElementFinder.findClasses(JavaElementFinder.kt:49)
at org.jetbrains.kotlin.asJava.LightClassUtil.findFileFacade(LightClassUtil.kt:179)
at org.jetbrains.kotlin.asJava.LightClassUtil.getWrappingClass(LightClassUtil.kt:162)
at org.jetbrains.kotlin.asJava.LightClassUtil.getWrappingClasses(LightClassUtil.kt:186)
at org.jetbrains.kotlin.asJava.LightClassUtil.getPsiMethodWrappers(LightClassUtil.kt:136)
at org.jetbrains.kotlin.asJava.LightClassUtil.getPsiMethodWrappers$default(LightClassUtil.kt:135)
at org.jetbrains.kotlin.asJava.LightClassUtil.getPsiMethodWrapper(LightClassUtil.kt:132)
at org.jetbrains.kotlin.asJava.LightClassUtil.getLightClassMethod(LightClassUtil.kt:113)
at org.jetbrains.uast.kotlin.BaseKotlinConverter$DefaultImpls.convertDeclaration(BaseKotlinConverter.kt:158)
at org.jetbrains.uast.kotlin.KotlinConverter.convertDeclaration(KotlinConverter.kt:11)
at org.jetbrains.uast.kotlin.BaseKotlinConverter$DefaultImpls.convertDeclarationOrElement(BaseKotlinConverter.kt:224)
at org.jetbrains.uast.kotlin.KotlinConverter.convertDeclarationOrElement(KotlinConverter.kt:11)
at org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin.convertElementWithParent(KotlinUastLanguagePlugin.kt:49)
at org.jetbrains.uast.kotlin.KotlinConvertParentUtilsKt.convertParentImpl(kotlinConvertParentUtils.kt:154)
at org.jetbrains.uast.kotlin.KotlinConvertParentUtilsKt.convertParentImpl(kotlinConvertParentUtils.kt:115)
at org.jetbrains.uast.kotlin.BaseKotlinUastResolveProviderService$DefaultImpls.convertParent(BaseKotlinUastResolveProviderService.kt:22)
at org.jetbrains.uast.kotlin.KotlinUastResolveProviderService$DefaultImpls.convertParent(KotlinUastResolveProviderService.kt)
at org.jetbrains.uast.kotlin.internal.IdeaKotlinUastResolveProviderService.convertParent(IdeaKotlinUastResolveProviderService.kt:24)
at org.jetbrains.uast.kotlin.KotlinAbstractUElement.convertParent(KotlinAbstractUElement.kt:29)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.convertParent(KotlinUBlockExpression.kt:20)
at org.jetbrains.uast.kotlin.KotlinAbstractUElement$uastParent$2.invoke(KotlinAbstractUElement.kt:25)
at org.jetbrains.uast.kotlin.KotlinAbstractUElement$uastParent$2.invoke(KotlinAbstractUElement.kt:11)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at org.jetbrains.uast.kotlin.KotlinAbstractUElement.getUastParent(KotlinAbstractUElement.kt)
at org.jetbrains.uast.UElementKt$withContainingElements$1.get(UElement.kt:154)
at kotlin.jvm.internal.PropertyReference1.invoke(PropertyReference1.java:35)
at kotlin.sequences.GeneratorSequence$iterator$1.calcNext(Sequences.kt:591)
at kotlin.sequences.GeneratorSequence$iterator$1.hasNext(Sequences.kt:609)
at org.jetbrains.uast.UastContextKt.getUastParentOfType(UastContext.kt:230)
at org.jetbrains.uast.UastContextKt.getUastParentOfType$default(UastContext.kt:176)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:110)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
...
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at io.ktor.ide.KtorRoutingVisitor.visitCallExpression(KtorUrlResolver.kt:111)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.ULambdaExpression$DefaultImpls.accept(ULambdaExpression.kt:41)
at org.jetbrains.uast.kotlin.KotlinULambdaExpression.accept(KotlinULambdaExpression.kt:11)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:167)
at io.ktor.ide.KtorServerEndpointsProviderKt$collectUrlMappingsInside$processor$1.invoke(KtorServerEndpointsProvider.kt:87)
at io.ktor.ide.KtorServerEndpointsProviderKt$collectUrlMappingsInside$processor$1.invoke(KtorServerEndpointsProvider.kt:86)
at io.ktor.ide.KtorRoutingCallsProcessor.visitCallExpression(KtorServerEndpointsProvider.kt:71)
at org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression.accept(KotlinUFunctionCallExpression.kt:163)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UBlockExpression$DefaultImpls.accept(UBlockExpression.kt:21)
at org.jetbrains.uast.kotlin.KotlinUBlockExpression.accept(KotlinUBlockExpression.kt:11)
at org.jetbrains.uast.UMethod$DefaultImpls.accept(UMethod.kt:45)
at org.jetbrains.uast.kotlin.KotlinUMethod.accept(KotlinUMethod.kt:18)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.kotlin.AbstractKotlinUClass.accept(AbstractKotlinUClass.kt:42)
at org.jetbrains.uast.internal.ImplementationUtilsKt.acceptList(implementationUtils.kt:29)
at org.jetbrains.uast.UFile$DefaultImpls.accept(UFile.kt:87)
at org.jetbrains.uast.kotlin.KotlinUFile.accept(KotlinUFile.kt:14)
at io.ktor.ide.KtorServerEndpointsProviderKt.collectUrlMappingsInside(KtorServerEndpointsProvider.kt:89)
at io.ktor.ide.KtorServerEndpointsProviderKt.collectUrlMappingsInside$default(KtorServerEndpointsProvider.kt:83)
at io.ktor.ide.KtorServerEndpointsProvider.getEndpoints(KtorServerEndpointsProvider.kt:46)
at io.ktor.ide.KtorServerEndpointsProvider.getEndpoints(KtorServerEndpointsProvider.kt:22)
at com.intellij.microservices.ui.flat.FlatEndpointGroup.getEndpoints(EndpointsCursor.kt:187)
at com.intellij.microservices.ui.flat.EndpointsCursorKt.getProviderEndpoints(EndpointsCursor.kt:153)
at com.intellij.microservices.ui.flat.EndpointsCursorKt.access$getProviderEndpoints(EndpointsCursor.kt:1)
at com.intellij.microservices.ui.flat.EndpointsCursor$endpointsMap$1.fun(EndpointsCursor.kt:50)
at com.intellij.microservices.ui.flat.EndpointsCursor$endpointsMap$1.fun(EndpointsCursor.kt:17)
at com.intellij.util.containers.ConcurrentFactoryMap$2.create(ConcurrentFactoryMap.java:174)
at com.intellij.util.containers.ConcurrentFactoryMap.get(ConcurrentFactoryMap.java:40)
at java.base/java.util.concurrent.ConcurrentMap.getOrDefault(ConcurrentMap.java:88)
at com.intellij.microservices.ui.flat.EndpointsCursor$request$allSequence$3.invoke(EndpointsCursor.kt:63)
at com.intellij.microservices.ui.flat.EndpointsCursor$request$allSequence$3.invoke(EndpointsCursor.kt:17)
at kotlin.sequences.FlatteningSequence$iterator$1.ensureItemIterator(Sequences.kt:315)
at kotlin.sequences.FlatteningSequence$iterator$1.hasNext(Sequences.kt:303)
at kotlin.sequences.DropWhileSequence$iterator$1.drop(Sequences.kt:529)
at kotlin.sequences.DropWhileSequence$iterator$1.hasNext(Sequences.kt:556)
at kotlin.sequences.TransformingSequence$iterator$1.hasNext(Sequences.kt:214)
at kotlin.sequences.SequenceBuilderIterator.yieldAll(SequenceBuilder.kt:183)
at kotlin.sequences.SequenceScope.yieldAll(SequenceBuilder.kt:100)
at com.intellij.microservices.ui.flat.EndpointsCursor$request$2.invokeSuspend(EndpointsCursor.kt:79)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlin.sequences.SequenceBuilderIterator.hasNext(SequenceBuilder.kt:140)
at kotlin.sequences.TakeSequence$iterator$1.hasNext(Sequences.kt:421)
at kotlin.sequences.TransformingSequence$iterator$1.hasNext(Sequences.kt:214)
at kotlin.sequences.SequencesKt___SequencesKt.toCollection(_Sequences.kt:786)
at kotlin.sequences.SequencesKt___SequencesKt.toMutableList(_Sequences.kt:816)
at kotlin.sequences.SequencesKt___SequencesKt.toList(_Sequences.kt:807)
at com.intellij.microservices.ui.flat.EndpointsView$load$2.call(EndpointsView.kt:771)
at com.intellij.microservices.ui.flat.EndpointsView$load$2.call(EndpointsView.kt:91)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.insideReadAction(NonBlockingReadActionImpl.java:531)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.lambda$attemptComputation$3(NonBlockingReadActionImpl.java:496)
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1152)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runInReadActionWithWriteActionPriority$0(ProgressIndicatorUtils.java:75)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runActionAndCancelBeforeWrite(ProgressIndicatorUtils.java:158)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.lambda$runWithWriteActionPriority$1(ProgressIndicatorUtils.java:115)
at com.intellij.openapi.progress.ProgressManager.lambda$runProcess$0(ProgressManager.java:57)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:188)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$12(CoreProgressManager.java:607)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:682)
at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:638)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:606)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:60)
at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:175)
at com.intellij.openapi.progress.ProgressManager.runProcess(ProgressManager.java:57)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runWithWriteActionPriority(ProgressIndicatorUtils.java:112)
at com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(ProgressIndicatorUtils.java:75)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.attemptComputation(NonBlockingReadActionImpl.java:496)
at com.intellij.openapi.application.impl.NonBlockingReadActionImpl$Submission.lambda$transferToBgThread$1(NonBlockingReadActionImpl.java:408)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:668)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:665)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:665)
at java.base/java.lang.Thread.run(Thread.java:829)
Performance: Don't store PSI elements in Ktor Url Mappings. Use Smart Reference or PSI Anchor, instead
Put label to local history before performing migration in Ktor
Migrations are project-wide operations and they may affect the state of the project making it not compilable. This is a dangerous operation and it's recommended to add a bookmark in local history so that one may roll back to the state right before the migration.
FUS metrics in IDE
We have quite a lot of features in Ktor plugin (https://youtrack.jetbrains.com/issue/KTOR-3407) and now we need to have some FUS (feature usage statistics) metrics for them.
Some options to consider:
- Refactoring/navigation invoked
- Test generated
- Migration time
- Run configuration computation time
- Ktor settings state (some features get disabled by user => we want to track that)
etc.
Provide the capability to generate WebSocket tests
Currently, the Create Test
intention for a module suggests creating tests only for HTTP methods. It would be useful to have the capability to create WebSocket tests (see https://ktor.io/docs/websocket.html#testing).
P.S. Maybe it's worth waiting for a new testing API before adding this feature to the plugin.
Could not resolve: io.ktor:ktor-locations:2.0.0-beta-1 on a new project created with IDEA 2021.3.1
Hello,
The default version when creating a ktor project with IDEA 2021.3.1 is 2.0.0-beta-1, but the last published version for ktor-locations is https://search.maven.org/artifact/io.ktor/ktor-locations/1.6.7/pom
This causes the first build to fail with the following error.
09:39:54: Executing ':classes'...
Starting Gradle Daemon...
Gradle Daemon started in 1 s 321 ms
> Task :compileKotlin FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':compileKotlin'.
> Error while evaluating property 'filteredArgumentsMap' of task ':compileKotlin'
> Could not resolve all files for configuration ':compileClasspath'.
> Could not find io.ktor:ktor-locations:2.0.0-beta-1.
Searched in the following locations:
- https://repo.maven.apache.org/maven2/io/ktor/ktor-locations/2.0.0-beta-1/ktor-locations-2.0.0-beta-1.pom
- https://maven.pkg.jetbrains.space/public/p/ktor/eap/io/ktor/ktor-locations/2.0.0-beta-1/ktor-locations-2.0.0-beta-1.pom
Required by:
project :
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 14s
1 actionable task: 1 executed
09:40:12: Execution finished ':classes'.
Support package split in Ktor migrations in plugin (java modules support)
Save "Create Run Configuration automatically" within .idea directory
I would love that if this setting would be saved within the .idea directory, so it can be shared within git.
Endpoints view: Endpoints not populated if routes require authentication
Routes inside of an authenticate()
block aren't populated in the Endpoints view in IntelliJ Ultimate.
Currently testing with KTOR 1.6.3 and IntelliJ Ultimate 2012.2.2 (212.5284.40)
I'm attaching a sample application that demonstrates the issue. In the file Routing.kt
:
fun Route.setupRoutes() {
//authenticate("basic") {
get("/") {
call.respondText("Hello World!")
}
//}
}
With the authenticate("basic")
call commented out, endpoints populate as expected. Remove the comments and endpoints vanish.
Reduce the number of versions displayed in a plugin
Currently, the plugin and start.ktor.io allow users to choose any version of Ktor starting from 1.4.0. Do we really need old versions, including 1.6.0-1.6.3? As an idea, we can show only the latest version and make the version chooser inactive. And in case we decide to introduce Beta, the version chooser will show two versions - the latest stable and beta.
Make migrations more configurable
Issues:
- Some people work on multi-module projects in mono repositories and they don't want to migrate the whole project (this will disturb other teams when changing irrelevant modules)
- We are always showing this notification once a new version arrives. There must be an option like "Don't show again"
- We are suggesting EAPs and Betas as well as stable releases. There must be an option to select migration version levels.
Option "Add imports for Ktor modules automatically" doesn't work
It is impossible to disable the option
IntelliJ IDEA 2021.3 EAP (Ultimate Edition)
Build #IU-213.4622, built on October 5, 2021
Server features instead of client in the client `install` block
`ContentNegotiation` is missing in the plugins completion window
Update Ktor Plugin Description
Can we please update the description to the following:
Ktor is a web application framework for creating connected systems. You can use it to create server-side as well as client-side applications. It supports multiple platforms, including JVM, JavaScript, and Kotlin/Native. This plugin provides functionality in the IDE such as creating new projects, plugin configuration, navigation (including URL navigation), refactorings across client and server, migrations, and more!
Project Generated with eap-256 has Errors in Imports
Ktor: Fold internal stack frames for HTTP server
Run Ktor app:
import io.ktor.application.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
fun main(args: Array<String>): Unit = EngineMain.main(args)
@Suppress("unused") // Referenced in application.conf
@JvmOverloads
fun Application.module(testing: Boolean = false) {
embeddedServer(Netty, host = "127.0.0.1", port = 8000) {
routing {
get("/hello") {
TODO()
}
}
}.start(wait = true)
}
Open http://localhost:8080/app
Full stacktrace is shown while we can collapse some framework internal frames by default
Prioritize text found in feature titles over descriptions
For example, when I'm trying to find the HTML DSL
feature, the wizard shows template engines first.
The 'Create test for Ktor module' intention actions changes files from other modules for a multimodule Gradle project
Steps to reproduce:
- Open the
codeSnippets
Gradle project: https://github.com/ktorio/ktor-documentation/tree/main/codeSnippets#ktor-code-examples - Open the
codeSnippets/snippets/upload-file/src/main/kotlin/com/example/UploadFile.kt
file. - Place the caret at
Application.main
, press Alt+Enter and choose 'Create test for Ktor module'.
EXP: Plugin creates onlyMainTest.kt
.
ACT: Plugin createsMainTest.kt
and also changes 154 files from other modules (see a screen).
IntelliJ IDEA 2021.2.1 RC (Ultimate Edition)
Build #IU-212.5080.39, built on August 18, 2021
Ktor plugin version: bundled 212.5080.39
Wizard Plugin listing strange link
Not sure what "See feature's Github" is, but it keeps going to the same link. It's also incorrect. What is the purpose of this link?
The 'Create test for Ktor module' intention action doesn't create any tests if routes are defined inside the extension function
- Create a project with the HOCON file as described here: https://ktor.io/docs/intellij-idea.html.
- Place the caret at
module
, press Alt+Enter, and chooseCreate test for Ktor module
. - Click Create in the invoked dialog -> No tests are created
ADE at io.ktor.ide.plugins.add.KtorMarketplacePluginsUpdater.checkForUpdates
213.2899
after the project (just created micronaut gradle-based one) closing
com.intellij.serviceContainer.AlreadyDisposedException: Cannot create light service io.ktor.ide.plugins.add.KtorPluginsService because container is already disposed (container=Project(name=aaamkntestggg, containerState=DISPOSE_COMPLETED, componentStore=C:\Users\irina\IdeaProjects\aaa-mkn-test-ggg) (disposed))
at com.intellij.serviceContainer.ContainerUtilKt.throwAlreadyDisposedError(containerUtil.kt:34)
at com.intellij.serviceContainer.ComponentManagerImpl.checkThatCreatingOfLightServiceIsAllowed(ComponentManagerImpl.kt:669)
at com.intellij.serviceContainer.ComponentManagerImpl.getOrCreateLightService(ComponentManagerImpl.kt:641)
at com.intellij.serviceContainer.ComponentManagerImpl.doGetService(ComponentManagerImpl.kt:605)
at com.intellij.serviceContainer.ComponentManagerImpl.getService(ComponentManagerImpl.kt:567)
at com.intellij.openapi.client.ClientAwareComponentManager.getFromSelfOrCurrentSession(ClientAwareComponentManager.kt:37)
at com.intellij.openapi.client.ClientAwareComponentManager.getService(ClientAwareComponentManager.kt:22)
at io.ktor.ide.plugins.add.KtorMarketplacePluginsUpdater.checkForUpdates(KtorMarketplacePluginsUpdater.kt:16)
at io.ktor.ide.plugins.add.KtorMarketplacePluginsUpdater.access$checkForUpdates(KtorMarketplacePluginsUpdater.kt:9)
at io.ktor.ide.plugins.add.KtorMarketplacePluginsUpdater$runActivity$1.run(KtorMarketplacePluginsUpdater.kt:42)
at com.intellij.ide.IdeEventQueue$MyFireIdleRequest.run(IdeEventQueue.java:1005)
at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:240)
at com.intellij.util.Alarm$Request.runSafely(Alarm.java:391)
at com.intellij.util.Alarm$Request.run(Alarm.java:377)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at com.intellij.util.concurrency.SchedulingWrapper$MyScheduledFutureTask.run(SchedulingWrapper.java:220)
at com.intellij.openapi.application.TransactionGuardImpl.runWithWritingAllowed(TransactionGuardImpl.java:214)
at com.intellij.openapi.application.TransactionGuardImpl.access$200(TransactionGuardImpl.java:21)
at com.intellij.openapi.application.TransactionGuardImpl$2.run(TransactionGuardImpl.java:196)
at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:797)
at com.intellij.openapi.application.impl.ApplicationImpl.lambda$invokeLater$4(ApplicationImpl.java:350)
at com.intellij.openapi.application.impl.FlushQueue.doRun(FlushQueue.java:84)
at com.intellij.openapi.application.impl.FlushQueue.runNextEvent(FlushQueue.java:133)
at com.intellij.openapi.application.impl.FlushQueue.flushNow(FlushQueue.java:46)
at com.intellij.openapi.application.impl.FlushQueue$FlushNow.run(FlushQueue.java:189)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:776)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:746)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:891)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:760)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$6(IdeEventQueue.java:447)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:825)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$7(IdeEventQueue.java:446)
at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:797)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:492)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Define completion priorities for Ktor keywords
Samples
Using proguard and CallLogging feature causes JVM crashes
Using the proguard configuration from the docs while using the CallLogging feature will make the JVM crash. This can be solved by adding a proguard rule class org.fusesource.jansi.** { public *; }
.
Update Samples to Ktor 2.0
Update non-generic samples to 2.0
Server
Server for Kotlin Native
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/571
Hello !
Does Ktor support Kotlin Native for now (using Netty)? I saw few comments in JetBrains Blog, but can't find any detailed information about it.
Thank you !
When working with SessionStorage, write is called every time after read
When using directorySessionStorage
or a custom implementation of SessionStorage
, I noticed that every time after the read function is called, the write function is called which invalidates the cache, making the use of the cache completely pointless.
Expected behavior: After the read SessionStorage
method is called, the write SessionStorage
method is not called.
Actual behavior: After the read SessionStorage
method is called, the write SessionStorage
method is also called.
Steps for reproduce:
-
Compile and run the code from the snippet
-
Authorize by sending HTTP POST request to http://localhost:9090/authenticate sending body in format x-www-form-urlencoded with user:user password:password, any data are considered correct, should return HTTP Status Code 200
-
Make sure authorization was successful and sending HTTP GET request to http://localhost:9090/hello returns HTTP Status Code 200 and "Hello World!" string.
When sending subsequent HTTP GET requests to http://localhost:9090/hello, the file with information about the session, which will be written to the /tmp folder, will be overwritten each time. Also, in the debugger in the io.ktor.sessions.CacheStorage
class, you can make sure that for each request the read method will be called and then the write method will be called.
Code to reproduce the issue:
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.sessions.*
import java.io.File
fun main() {
embeddedServer(Netty, port = 9090, host = "0.0.0.0") {
install(Authentication) {
session<UserIdPrincipal>("auth-session") {
validate { it }
challenge { call.respond(HttpStatusCode.Unauthorized) }
}
form("auth-form") {
validate { UserIdPrincipal(it.name) }
challenge { call.respond(HttpStatusCode.Forbidden) }
}
}
install(Sessions) {
cookie<UserIdPrincipal>(
"user_session",
directorySessionStorage(File("/tmp"))
)
}
routing {
authenticate("auth-form") {
post("/authenticate") {
val session = call.principal<UserIdPrincipal>()
call.sessions.set(session)
call.respond(HttpStatusCode.OK)
}
}
}
routing {
authenticate("auth-session") {
get("/hello") {
call.respondText("Hello World!")
}
}
}
}.start(wait = true)
}
Is it possible to have multiple ContentNegotiation, separate per module?
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1002
Ktor Version
1.1.2
When trying to install ContentNegotiation inside additional (not main) module:
install(ContentNegotiation) {
register(ContentType.Application.Xml, MyContentConverter()) {
}
}
I have this error:
Caused by: io.ktor.application.DuplicateApplicationFeatureException: Conflicting application feature is already installed with the same key as `ContentNegotiation`
however I would like that part of the routes could by default use different content negotiation.
some API require to have XML.
The per route can be defined with help of contentType
:
routing {
contentType(ContentType.Application.Xml){
get("/myxml/api"){
}
}
Is there way to have that configured per module, how the ContentNegotiation happens?
That would help to have modules as separate dependencies to be reused on different Ktor applications.
XForwardedHeaderSupport should let you specify which index (from end) to choose
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1305
Ktor Version and Engine Used (client or server and name)
ktor-server-core 1.2.3
Describe the bug
The XForwardedHeaderSupport feature assigns the first entry in X-Forwarded-For to call.request.origin.remoteHost
. However, typically the way that proxies work is that they accept arbitrary X-Forwarded-For
values from clients and add their own IPs to the end of the list. So to properly figure out a client's IP address, one needs to know how many proxies one is behind, and count that far from the end of the list.
Now, to be fair, the docs on remoteHost say to not use it for user authentication. But if you do know how many proxies you are behind, it should be safe to use it for user authentication, by counting that many IPs from the end. So it would be good if you could do this easily!
So I guess I have two separate concerns:
(a) Today's behavior is surprisingly insecure, and though it's documented on RequestConnectionPoint it should be documented on https://ktor.io/servers/features/forward-headers.html
(b) Ideally you should be able to configure XForwardedHeader with something like
install(XForwardedHeaderSupport) {
// Given `x-forwarded-for: GARBAGE1, GARBAGE2, REALCLIENTIP1, PROXY1` and request.local being the second proxy, this will get REALCLIENTIP1
proxyCount = 2
}
ie, "behind one proxy" means the last IP address is correct, "behind two proxies" means the second-to-last, etc.
To Reproduce
- Run with XForwardedHeaderSupport behind some proxy, send a request with
X-Forwarded-For: 123.123.123.123.
, see request.origin.remoteHost becomes 123.123.123.123.
Expected behavior
It should be possible to configure XForwardedHeaderSupport to reliably ascertain the remoteHost.
Happy to send in a PR, esp if people like the proposed variable name.
Support for adding values to the MDC later on in the pipeline.
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1414
Hi and thanks for this powerful library. In advance, apologies if I misunderstood some of the mechanics I describe here.
Currently, the CallLogging feature creates a "Logging" phase inserted before "Monitoring" and intercepts it to setup the MDC context for the rest of the pipeline with:
pipeline.intercept(loggingPhase) {
withMDC {
proceed()
feature.logSuccess(call)
}
}
This makes the MDC nicely available in all the sub-coroutines for reading. However any manual modification to the context in the rest of the pipeline is lost on suspension, making it effectively read-only.
From what I understand, this is expected and similar to the caveat described in the kotlinx documentation about ThreadLocal & coroutines
As a result, it very hard to add values to the MDC that are not trivially available during the Logging phase. In our use-case, these are:
- The id of the user making the request (it is computed during the authentication phase).
- The graphql operation requested by the user (it is computed in the routing phase as our graphql library parses the request).
I will try to find a hacky way to handle this for now, but it seems to me that the general use-case (of adding additional data to the MDC context later-on in the pipeline) is legitimate and it would be great to have "native" ktor support for it!
Ktor uses too much memory compared to other Http server libraries
Hi, Under the load scenario we are testing in my team lately, Ktor is using way more memory than other libraries, like Kooby(Jooby).
jdk1.8.0_221
After 500 seconds of stress tests of the same 4 endpoints, check the memory and CPU usage:
Jooby 2.13.0:
Ktor:
Ktor implementation code:
server:
embeddedServer(
Netty,
host = ip,
port = port,
module = Application::module,
configure = {
requestQueueLimit = 8
runningLimit = 5
shareWorkGroup = true
configureBootstrap = {
group(NioEventLoopGroup(8, Executors.newCachedThreadPool()))
}
}
).start(wait = true)
features:
install(DefaultHeaders)
install(ContentNegotiation) {
register(ContentType.Application.Json, JacksonConverter(JSON))
}
routes:
fun route(parentRoute: Route) {
parentRoute.invoke {
post("/games/{gameId}/validate") { call.respond(HttpStatusCode.MethodNotAllowed) }
post("/games/{gameId}/play") {
call.respond(HttpStatusCode.OK, "hola")
}
get("/games/{gameId}/config") {
call.respond(HttpStatusCode.OK, "hola")
}
post("/games/{gameId}/checksum") {
call.respond(HttpStatusCode.OK, "hola")
}
get("/games/{gameId}/version") {
call.respond(HttpStatusCode.OK, "hola")
}
get("/games/{gameId}/initialize") {
call.respond(HttpStatusCode.OK, "hola")
}
post("/games/{gameId}/evaluate/{objectiveId}") {
call.respond(HttpStatusCode.OK, "hola")
}
}
}
As you can see, the endopints are quite simple.
The test:
We used Gatling to stress the servers, this is the code:
Basically, 500 users per second peform 4 api calls.
Any further question, please let me know.
NoClassDefFoundError is thrown on Android because ktor-utils references a not supported Java API
NettyEngine
2022-01-14 19:18:33.540 24057-24219/com.android.sidecar E/ktor.application: 26133 [eventLoopGroupProxy-4-2][DefaultUncaughtExceptionHandler.kt:handleException:30] - Unhandled exception caught for CoroutineName(call-handler)
java.lang.NoClassDefFoundError: Failed resolution of: Ljava/lang/management/ManagementFactory;
at io.ktor.util.debug.IntellijIdeaDebugDetector$isDebuggerConnected$2.invoke(IntellijIdeaDebugDetectorJvm.kt:14)
at io.ktor.util.debug.IntellijIdeaDebugDetector$isDebuggerConnected$2.invoke(IntellijIdeaDebugDetectorJvm.kt:12)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at io.ktor.util.debug.IntellijIdeaDebugDetector.isDebuggerConnected(IntellijIdeaDebugDetectorJvm.kt:12)
at io.ktor.util.debug.ContextUtilsKt.addToContextInDebugMode(ContextUtils.kt:18)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invokeSuspend(NettyApplicationCallHandler.kt:121)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:55)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:112)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:41)
at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:33)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:61)
at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:370)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.ktor.server.netty.EventLoopGroupProxy$Companion.create$lambda-1$lambda-0(NettyApplicationEngine.kt:258)
at io.ktor.server.netty.EventLoopGroupProxy$Companion.$r8$lambda$GV1s6RcVb-9HNgd1JsHck5tmF3c(NettyApplicationEngine.kt)
at io.ktor.server.netty.EventLoopGroupProxy$Companion$$ExternalSyntheticLambda0.run(Unknown Source)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.ClassNotFoundException: Didn't find class "java.lang.management.ManagementFactory" on path: DexPathList[[zip file "/data/app/com.android.sidecar-2/base.apk"],nativeLibraryDirectories=[/data/app/com.android.sidecar-2/lib/arm, /system/lib, /vendor/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 28 common frames omitted
Proper way of getting a port after server startup
I have a HOCON config file that contains the field ktor.deployment.port
. Expected behaviour is that the flag -port
will override its value so I'll be able to access current server port using environment.config.property("ktor.deployment.port").getString()
, but it doesn't work and will return the value from configuration file.
At the same time flag -P:ktor.deployment.port
works fine.
Support application/xml as part for the ContentNegotiation feature
In addition to support application/json: https://ktor.io/features/jackson.html
Migrate plugins to new API
It takes long time to stop server with Netty engine
it takes couple of seconds to stop running app via CTRL+C
Fail to create response observer in different native thread.
Hey there!
I am trying out the new Kotlin Native Memory Management on iOS with Kotlin 1.6.0-RC
with Ktor Version 1.6.2-native-mm-eap-196
and coroutines version 1.5.1-new-mm-dev2
.
Some requests work fine but sometimes I get the following error:
Caused by: kotlin.IllegalStateException: Fail to create response observer in different native thread.
at 0 0x0000000105dfb81c kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96
at 1 0x0000000105df3660 kfun:kotlin.Exception#<init>(kotlin.String?){} + 92
at 2 0x0000000105df38b4 kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92
at 3 0x0000000105df3c38 kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92
at 4 0x00000001061f52bc kfun:io.ktor.client.features.observer#wrapWithContent__at__io.ktor.client.call.HttpClientCall(io.ktor.utils.io.ByteReadChannel){}io.ktor.client.call.HttpClientCall + 280
at 5 0x00000001061d0374 kfun:io.ktor.client.features#withObservableDownload__at__io.ktor.client.call.HttpClientCall(kotlin.coroutines.SuspendFunction2<kotlin.Long,kotlin.Long,kotlin.Unit>){}io.ktor.client.call.HttpClientCall + 468
at 6 0x00000001061cf6b0 kfun:io.ktor.client.features.BodyProgress.$handle$lambda-1COROUTINE$209.invokeSuspend#internal + 1088
at 7 0x00000001061d011c kfun:io.ktor.client.features.BodyProgress.$handle$lambda-1COROUTINE$209.invoke#internal + 296
at 8 0x0000000106171b00 kfun:io.ktor.util.pipeline.SuspendFunctionGun.$loop$<anonymous>_2COROUTINE$256.invokeSuspend#internal + 476
at 9 0x000000010617204c kfun:io.ktor.util.pipeline.SuspendFunctionGun.$loop$<anonymous>_2COROUTINE$256.invoke#internal + 232
at 10 0x0000000106170480 kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1044
at 11 0x000000010616fb28 kfun:io.ktor.util.pipeline.SuspendFunctionGun#proceed(){}1:0 + 364
at 12 0x000000010616fd30 kfun:io.ktor.util.pipeline.SuspendFunctionGun#proceedWith(1:0){}1:0 + 192
at 13 0x00000001062be67c kfun:io.ktor.client.features.logging.Logging.Companion.$install$lambda-1COROUTINE$187.invokeSuspend#internal + 1660
at 14 0x0000000105e1e480 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 596
at 15 0x0000000105ff6ba4 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 2772
at 16 0x0000000106023d34 kfun:kotlinx.coroutines.DarwinGlobalQueueDispatcher.dispatch$<anonymous>_3#internal + 128
I found out that the error disappears when I remove the ktor logging plugin from the client instance. I guess because then there is no ResponseObserver anymore that could cause this error. The logging plugin is from io.ktor:ktor-client-logging:1.6.2-native-mm-eap-196
.
Ktor stopped working with latest Tomcat 9.0.39
Performance Issue / Ktor & Netty
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1163
Ktor Version
1.2.0
Ktor Engine Used(client or server and name)
Netty Server
JVM Version, Operating System and Relevant Context
11.0.2, Debian (docker)
4 Cores -Xmx2G (parallelism = 4)
Default settings
Feedback
For most of the cases performance is pretty good, we get < 50ms including db save (all done asynchronously). Unfortunately from time to time, we are getting requests which stuck for > 1s, in worst cases even > 20 seconds. Server got the request, but processing route wasn't invoked yet. In below there is part of NewRelic instrumentation stuck for such slow call, the problem is that Ktor is not yet well instrumented, and the only consecutive calls I see taking that time are below:
0 | 0.00% | Truncated: NettyUpstreamDispatcher | | 0.000 s
-- | -- | -- | -- | --
0 | 0.00% | HttpServerExpectContinueHandler.channelRead() | Async | 0.000 s
0 | 0.00% | HttpServerExpectContinueHandler.channelRead() | Async | 0.000 s
0 | 0.00% | RequestBodyHandler.channelRead() | Async | 0.000 s
16.0 | 0.08% | NettyApplicationCallHandler.channelRead() | Async | 20.025 s
16.0 | 0.08% | NewRelicFeature.wrapIntoNewRelicTransaction() | | 20.025 s
16.0 | 0.08% | com.revolut.eventstore.api.write.EventsControllerKt/saveEvent | | 20.025 s
1.0 | 0.00% | Application code (in com.*.api.write.EventsControllerKt/saveEvent)
What I am trying to understand is - what could happen between:
0 | 0.00% | RequestBodyHandler.channelRead() | Async | 0.000 s
-- | -- | -- | -- | --
16.0 | 0.08% | NettyApplicationCallHandler.channelRead() | Async | 20.025 s
Why it took such long? It's pretty hard to understand where may be any blocking/under sourced part - so I would really appreciate help with it
httpMethod is not affected by X-Http-Method-Override (in opposite to docs)
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1825
Ktor Version and Engine Used (client or server and name)
ktor 1.3.2 (Netty)
Describe the bug
https://api.ktor.io/1.3.2/io.ktor.request/http-method.html states:
Returns request HTTP method possibly overridden via header X-Http-Method-Override
However it doesn't work. httpMethod() returns POST while headers[" X-Http-Method-Override"] returns GET.
To Reproduce
Execute POST request with X-Http-Method-Override: GET header.
Expected behavior
Returns 'GET' or documentation is not correct.
Screenshots
Is that correct that routing API also ignores X-Http-Method-Override or it's a bug ?
Android: Failed resolution of: Ljava/nio/file/Paths using API 25 and lower
Ktor 1.6.4 runs on Android's 6.0 version device
ERROR ktor.application - Unhandled: GET - /
System.out: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/nio/file/Paths;
System.out: at io.ktor.http.content.JarFileContent.<init>(JarFileContent.kt:27)
System.out: at io.ktor.http.content.StaticContentResolutionKt.resourceClasspathResource(StaticContentResolution.kt:62)
How to deal with this problem?
Remove checking body transformation from ContentNegotation
ContentNegotation
plugin checks if the body was transformed and throws exception otherwise. There are two issues in it:
- If another plugin that transforms body is installed after CN, it doesn't have a chance to run
- If CN is not installed, no one checks body transformation
Checking of body should be done outside of CN after transforming(rendering) phase. This new plugin should be pre-installed.
Feature: Use websockets with serialization
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1768
Subsystem
Server(/Client?):
- ktor-serialization
- ktor-websockets
Is your feature request related to a problem? Please describe.
Right now I don't see a good way to combine websockets with serialization.
Describe the solution you'd like
In http you can do it like this:
call.respond(SomeSerializableClass())
I'd propose something like this:
outgoing.send(
Frame.Text.fromSerializable(SomeSerializableClass())
)
and
textFrame.toSerializable<SomeSerializableClass>()
Either in some kind of library or at least somewhere in the documentation.
Motivation to include to ktor
Currently there's not a really good way to communicate over websockets via JSON.
[netty] Headers are only flushed after first byte is written
I've been trying to get a streaming response working for a while and I don't think it's currently possible to flush response headers before any byte is written when using the netty server engine. The exact same code works perfectly with cio.
The code looks something like this (I've tried using WriteChannelContent
with 0-byte writes and explicit flushes as well):
route("/timed") {
get {
val byteStream = ByteChannel(autoFlush = true)
launch(Dispatchers.Unconfined) {
delay(5000)
byteStream.writeFully(Charsets.UTF_8.encode("foo\n"))
byteStream.close(null)
}
call.respond(object : OutgoingContent.ReadChannelContent() {
override val status: HttpStatusCode? = HttpStatusCode.OK
override val contentType: ContentType? = ContentType.Text.Plain
override val headers: Headers = Headers.Empty
override fun readFrom() = byteStream
})
}
}
I'm testing with curl -XGET localhost:8080/timed -v
. When running the server with CIO this will immediately print the headers and then 5s later it'll print foo and close the response. With netty, this will wait for the full 5s before printing the whole response at once.
Writing multiple messages shows that this is only an issue with the initial header response. Streaming works fine with all messages after the i