Changelog 1.3 version
1.3.2
released 12th March 2020
Client
Allow to create threadless engine (CIO) for Ktor client
Add response text to the message of ResponseException and derived exceptions
Make the message
of ResponseException
contain response's text from response.readText()
.
HttpClient cancels parent coroutine despite try-catch block
The HttpClient doesn't give me a chance to check thrown exceptions and directly cancels any parent coroutine instead, which results in some weird race conditions.
fun Application.module() {
val client = HttpClient(Apache)
routing {
get {
val deferred = async { client.get<String>("Some erroneous request") }
try {
call.respondText(deferred.await())
} catch (e: Exception) {
call.respondText("Error") //I will be cancelled
}
}
}
}
As a workaround I have added a SupervisorJob
to the async
coroutine builder for now.
How to use ContentEncoding?
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1996
I setup client like here:
HttpClient {
ContentEncoding()
}
But I still cannot read request (using call.content
) with Accept-Encoding: gzip
, I don't see normal text, just some binary data. What do I need to do to read decoded text through ByteReadChannel?
Support Java HTTP Client
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1930
Subsystem Client, JVM
Describe the solution you'd like In Java 11 was introduced HTTP Client based on reactive-streams.
Motivation to include to ktor Since more projects are switching to modern Java versions it could be valuable to use built-in Java HTTP Client without need to use additional libraries. It supports HTTP/1.1 and HTTP/2 protocol and provides async APIs.
Support async DNS resolve in Client
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1924
Ktor Client HTTP Cache for invalid or blank Expires header
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1892
Ktor Version and Engine Used (client or server and name) 1.3.2, client, io.ktor.client.features.cache.HttpCache
Describe the bug
If a server returns a blank or an invalid Expires HTTP header, the ktor client crashes if the feature HttpCache is enabled install(HttpCache)
To Reproduce Steps to reproduce the behavior:
- Write the following:
val articleUrl = Url("https://www.nachrichten.at/panorama/weltspiegel/30-jahre-mauerfall-sinnbild-der-freiheit;art17,3184503")
val newsClient = HttpClient(Apache) {
install(HttpCache)
install(UserAgent) {
agent = "Mozilla/5.0"
}
}
try {
val call: HttpStatement = newsClient.get(articleSimilarityRequest.articleUrl)
val response: HttpResponse = call.receive()
val requestUrl = response.request.url.toString()
val rawHtml = response.readText()
println(rawHtml)
} catch (cause: Throwable) {
println("Unable to download article")
} finally {
newsClient.close()
}
- If you open the URL
https://www.nachrichten.at/panorama/weltspiegel/30-jahre-mauerfall-sinnbild-der-freiheit;art17,3184503
in the browser, you can see that the Expires response header is blank - An exception is thrown if you run the code:
2020-05-23 14:41:24.124 [DefaultDispatcher-worker-2 @request#48] ERROR a.s.api.util.service.DownloadService - Unable to download article
java.lang.IllegalStateException: Failed to parse date:
at io.ktor.http.DateUtilsKt.fromHttpToGmtDate(DateUtils.kt:40)
at io.ktor.client.features.cache.HttpCacheEntryKt.cacheExpires(HttpCacheEntry.kt:84)
at io.ktor.client.features.cache.HttpCacheEntryKt.HttpCacheEntry(HttpCacheEntry.kt:18)
at io.ktor.client.features.cache.HttpCacheEntryKt$HttpCacheEntry$1.invokeSuspend(HttpCacheEntry.kt)
(Coroutine boundary)
at io.ktor.client.HttpClient$1.invokeSuspend(HttpClient.kt:123)
at io.ktor.client.engine.HttpClientEngine$install$1.invokeSuspend(HttpClientEngine.kt:68)
at io.ktor.client.features.HttpSend$DefaultSender.execute(HttpSend.kt:116)
at io.ktor.client.features.HttpSend$Feature$install$1.invokeSuspend(HttpSend.kt:79)
at io.ktor.client.features.HttpCallValidator$Companion$install$1.invokeSuspend(HttpCallValidator.kt:89)
at io.ktor.client.features.HttpRequestLifecycle$Feature$install$1.invokeSuspend(HttpRequestLifecycle.kt:34)
at io.ktor.client.HttpClient.execute(HttpClient.kt:164)
at io.ktor.client.statement.HttpStatement.executeUnsafe(HttpStatement.kt:104)
at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:43)
at app.shwoo.api.util.service.DownloadService.downloadArticle$suspendImpl(DownloadService.kt:101)
at app.shwoo.api.server.web.SimilarityResourceKt$similarity$1$4.invokeSuspend(SimilarityResource.kt:38)
at io.ktor.routing.Routing.executeResult(Routing.kt:147)
at io.ktor.routing.Routing.interceptor(Routing.kt:34)
at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:99)
at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:107)
at io.ktor.server.testing.TestApplicationEngine$callInterceptor$1.invokeSuspend(TestApplicationEngine.kt:292)
at io.ktor.server.testing.TestApplicationEngine$2.invokeSuspend(TestApplicationEngine.kt:50)
at io.ktor.server.testing.TestApplicationEngine$handleRequest$pipelineJob$1.invokeSuspend(TestApplicationEngine.kt:290)
Caused by: java.lang.IllegalStateException: Failed to parse date:
at io.ktor.http.DateUtilsKt.fromHttpToGmtDate(DateUtils.kt:40)
at io.ktor.client.features.cache.HttpCacheEntryKt.cacheExpires(HttpCacheEntry.kt:84)
at io.ktor.client.features.cache.HttpCacheEntryKt.HttpCacheEntry(HttpCacheEntry.kt:18)
at io.ktor.client.features.cache.HttpCacheEntryKt$HttpCacheEntry$1.invokeSuspend(HttpCacheEntry.kt)
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)
Expected behavior It seems that the problem occurs in https://github.com/ktorio/ktor/blob/master/ktor-client/ktor-client-core/common/src/io/ktor/client/features/cache/HttpCacheEntry.kt#L84. If the Expires header is blank an exception is thrown. The expected behavior is that the application handles the cache as expired if an invalid date is provided in the HTTP Expires header. Like described in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires
Content-type header is missing in Request when using LogLevel.BODY
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1690
Ktor Version and Engine Used (client or server and name)
version: 1.3.1
lib: ktor-client-cio
additional modules: ktor-client-logging-jvm
Describe the bug
Content-type header is missing when LogLevel.BODY
is set. (also I am using multi-part body, not sure if relevant). Header exists if removing logging or setting LogLevel.HEADER
.
To Reproduce Steps to reproduce the behavior:
- Set logging
HttpClient {
install(Logging) {
logger = Logger.ANDROID
level = LogLevel.BODY
}
}
- Do multi-form request
val response = httpClient.post<String> {
url("http://someurl")
body = MultiPartFormDataContent(formData {
append(
"file",
"file.txt",
ContentType.parse("text/plain"),
contentBytes.size.toLong()
) {
writeFully(contentBytes)
}
})
- Observe missing
Content-Type: multipart/form-data; <...>
header
Expected behavior
Content-Type: multipart/form-data; <...>
in request exists
Can't compile project with Ktor JS client because of webpack errors
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1400
Ktor Version and Engine Used (client or server and name) io.ktor:ktor-client-js 1.3.0-beta-1 and 1.2.5
Describe the bug Webpack "Module not found" errors are thrown.
Logs:
WARNING in .._imported/ktor-ktor-client-core/1.3.0-beta-1/ktor-ktor-client-core.js 12165:13-32
Critical dependency: the request of a dependency is an expression
@ ./kotlin/gaia.js
@ multi ./kotlin/gaia.js source-map-support/browser-source-map-support.js
WARNING in .._imported/ktor-ktor-client-core/1.3.0-beta-1/ktor-ktor-client-core.js
Module not found: Error: Can't resolve 'ws' in 'C:\Users\Daan\code\gaia\build\js\packages_imported\ktor-ktor-client-core\1.3.0-beta-1'
@ .._imported/ktor-ktor-client-core/1.3.0-beta-1/ktor-ktor-client-core.js
@ ./kotlin/gaia.js
@ multi ./kotlin/gaia.js source-map-support/browser-source-map-support.js
ERROR in .._imported/ktor-ktor-io/1.3.0-beta-1/ktor-ktor-io.js
Module not found: Error: Can't resolve 'text-encoding' in 'C:\Users\Daan\code\gaia\build\js\packages_imported\ktor-ktor-io\1.3.0-beta-1'
@ .._imported/ktor-ktor-io/1.3.0-beta-1/ktor-ktor-io.js 20149:21-45 20217:21-45
@ .._imported/ktor-ktor-client-json/1.3.0-beta-1/ktor-ktor-client-json.js
@ ./kotlin/gaia.js
@ multi ./kotlin/gaia.js source-map-support/browser-source-map-support.js
To Reproduce Steps to reproduce the behavior:
- Add the io.ktor:ktor-client-js dependency to a Kotlin multi-platform project
- Run ./gradlew jsBrowserWebpack
- See errors
WebSocket throws a vague IOException when internet is disconnected
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1119
Ktor Version
1.2.0-rc
Ktor Engine Used(client or server and name)
Client/CIO
JVM Version, Operating System and Relevant Context
OpenJDK 11.0.3, Arch Linux.
Feedback
When the internet is disconnected, a vague java.io.IOException
is thrown twice, and it cannot be caught.
Exception in thread "pool-2-thread-3" java.io.IOException: Connection reset by peer
at java.base/sun.nio.ch.FileDispatcherImpl.read0(Native Method)
at java.base/sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)
at java.base/sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:276)
at java.base/sun.nio.ch.IOUtil.read(IOUtil.java:233)
at java.base/sun.nio.ch.IOUtil.read(IOUtil.java:223)
at java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:358)
at kotlinx.io.nio.ChannelsKt.read(Channels.kt:117)
at io.ktor.network.sockets.CIOReaderKt$attachForReadingDirectImpl$1$1.invokeSuspend(CIOReader.kt:75)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)
at kotlinx.coroutines.DispatchedKt.resume(Dispatched.kt:309)
at kotlinx.coroutines.DispatchedKt.resumeUnconfined(Dispatched.kt:49)
at kotlinx.coroutines.DispatchedKt.dispatch(Dispatched.kt:295)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:250)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:260)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:189)
at io.ktor.network.selector.SelectorManagerSupport.handleSelectedKey(SelectorManagerSupport.kt:83)
at io.ktor.network.selector.SelectorManagerSupport.handleSelectedKeys(SelectorManagerSupport.kt:63)
at io.ktor.network.selector.ActorSelectorManager.process(ActorSelectorManager.kt:74)
at io.ktor.network.selector.ActorSelectorManager$process$1.invokeSuspend(ActorSelectorManager.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
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:834)
[Question] CPU 100% Apache Ktor Http Client
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1018
Ktor Version
1.1.3
Ktor Engine Used(client or server and name)
Apache
JVM Version, Operating System and Relevant Context
Feedback
I utilize ktor http client to communicating from service to other service, but after 10 minutes deployment my machine experience CPU 100%, any idea why ?
here's the building block that I use,
override fun sendAsync(request: HttpRequest, responseCallback: Callback<HttpResponse>) {
GlobalScope.launch (Dispatchers.IO + CoroutineName(key ?: "")) {
try {
decorateRequest(request)
val requestUrl = URL(request.url)
val requestBody = String(request.body.asArray(), StandardCharsets.UTF_8)
val requestMethod = HttpMethod(request.method)
val requestContentType = createContentType(request)
val clientResponse = httpClient!!.request<io.ktor.client.response.HttpResponse>{
method = requestMethod
url(requestUrl)
body = TextContent(requestBody, requestContentType)
request.headers.forEach {
if (!unsafeHeaderSet.contains(it.name)) {
header(it.name, it.value)
}
}
}
val response = HttpResponse()
val responseBodyString = String(clientResponse.readBytes())
response.statusCode = clientResponse.status.value
response.bodyEncoding = "UTF-8"
response.body = IndirectNIOBuffer(ByteBuffer.wrap(responseBodyString.toByteArray(charset("UTF-8"))), true)
clientResponse.headers.forEach { name, value ->
val headerValue = value.joinToString(",")
response.addHeader(name, headerValue)
}
clientResponse.close()
responseCallback.onComplete(response)
} catch (error: Exception) {
responseCallback.onException(error)
}
}
}
Disable system out for dropped connections
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/545
At least conditionally, otherwise any benchmarking session hangs after a single-shot run and prints every connection to System.out
2018-08-28 12:43:54.623 [engine-thread] ERROR Application - 200 OK: GET - /
java.io.IOException: Connection reset by peer
at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
at sun.nio.ch.IOUtil.write(IOUtil.java:51)
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471)
at io.ktor.network.sockets.CIOWriterKt$attachForWritingDirectImpl$1$1.doResume(CIOWriter.kt:73)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:42)
at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:150)
at kotlinx.coroutines.experimental.io.internal.MutableDelegateContinuation.run(MutableDelegateContinuation.kt:14)
at io.ktor.network.util.IOCoroutineDispatcher$IODispatchedTask.run(IOCoroutineDispatcher.kt)
at io.ktor.network.util.IOCoroutineDispatcher$IOThread$run$1.doResume(IOCoroutineDispatcher.kt:73)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:42)
at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:150)
at kotlinx.coroutines.experimental.DispatchedContinuation.run(Dispatched.kt:14)
at kotlinx.coroutines.experimental.EventLoopBase.processNextEvent(EventLoop.kt:136)
Uploading file without contenttype and filename cause server error
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/458
Hi I use Apache HttpClient library to submit a multipart form to ktor service, Using the following Entity:
val entity = MultipartEntityBuilder.create()
.addBinaryBody("image", img)
Then there will be exception at server.
kotlinx.io.core.MalformedUTF8InputException: Expected 2 more character bytes
at kotlinx.io.core.ByteReadPacket.readUtf8(Packet.kt:488)
at kotlinx.io.core.ByteReadPacket.readASCII(Packet.kt:267)
at kotlinx.io.core.ByteReadPacket.readText(Packet.kt:224)
at kotlinx.io.core.ByteReadPacket.readText$default(Packet.kt:220)
at io.ktor.http.cio.CIOMultipartDataBase.partToData(CIOMultipartData.kt:115)
at io.ktor.http.cio.CIOMultipartDataBase$partToData$1.doResume(CIOMultipartData.kt)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:42)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:41)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:41)
at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:162)
at kotlinx.coroutines.experimental.io.internal.MutableDelegateContinuation.run(MutableDelegateContinuation.kt:14)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
If I set contenttype and filename, the exception will dispear.
val entity = MultipartEntityBuilder.create()
.addBinaryBody("image", img, ContentType.create("image/jpeg"), "ximg")
Add ability to override HttpAsyncResponseConsumer
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/360
Currently the HttpAsyncResponseConsumer in use is the internal ApacheResponseConsumer and this doesn't seem to be overridable.
For custom behaviours such as limiting the total size of the response payload without needing to actually fetch everything beforehand, would be advantageous
Core
of multipart request, part.dispose should be called explicitly?
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/469
Hi When I test file upload function, I found the temporary files used by file uploading are never deleted. Should I call part.dispose manually?
Is Ktor really that slow compared to other micro java frameworks?
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/411
I've noticed that in this test https://www.techempower.com/benchmarks/#section=data-r15&hw=ph&test=plaintext ktor result are pretty bad compared to Rapidoid, Vertx, Netty.
Here's source code of this benchmark: https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/frameworks/Kotlin/ktor/src/main/kotlin/org/jetbrains/ktor/benchmarks/Hello.kt
Is Ktor really that slow or there is some problem in benchmark?
That benchmark looks better for Ktor: https://github.com/orangy/http-benchmarks
Docs
"Using a Self-Signed Certificate" docs provide wrong dependency for 1.3.x
https://ktor.io/servers/self-signed-certificate.html says :
- This function is defined in the method io.ktor.network.tls.certificates.generateCertificate in the artifact io.ktor:ktor-network-tls:$ktor_version.
But it is wrong for 1.3.x Ktor versions (see https://github.com/ktorio/ktor/issues/1612). The workaround is to use "io.ktor:ktor-network-tls-certificates:$ktor_version" for 1.3.x. Samples also need to be fixed
Documentation for kotlinx.serialization wrong and outdated
This issue was imported from GitHub issue: https://github.com/ktorio/ktorio.github.io/issues/272
I tried the kotlinx serialization example from the docs: https://ktor.io/servers/features/content-negotiation/serialization-converter.html
Upon using the example I get a deprecation warning for the „serialization(...“ Block because it has been replaced by json. Second the example does not work. In contrast, the gson example works perfectly fine. Either fix or remove the section, so other people don’t waste their time trying to get kotlinx.serializtion to work.
Prometheus Registry unavailable for Micrometer
This issue was imported from GitHub issue: https://github.com/ktorio/ktorio.github.io/issues/224
The documentation for Micrometer metrics hint towards a Prometheus Registry being available for use here - https://github.com/ktorio/ktorio.github.io/blob/3e1f9c9c52ebbed7f429a05aa1c2b41ed69c6e93/servers/features/metrics-micrometer.md#L97
However, it appears that https://github.com/ktorio/ktor/pull/1105 has stalled out. Can you provide advise on if this feature will still be implemented, if the documentation needs to be updated, or something different. I would also like to know how I can help push this imitative along.
Can I create a SOAP service with Ktor?
This issue was imported from GitHub issue: https://github.com/ktorio/ktorio.github.io/issues/203
Looking at ktor as an alternative to spring boot and wondering if I can migrate my SOAP Service s built by Spring Boot to ktor?
What is a "VOs"
This issue was imported from GitHub issue: https://github.com/ktorio/ktorio.github.io/issues/197
/master/quickstart/guides/3chat.md#L176
The exercise challenges the reader to send and receive "VOs" using web sockets. There isn't much context as to what a "VOs" is/are. Is it some kind of abbreviation? What is a "VOs"?
Use hugo for building website?
This issue was imported from GitHub issue: https://github.com/ktorio/ktorio.github.io/issues/161
Hi! I'd like to migrate full site to hugo, so it will build faster and much simpler for website authors (since hugo distributing as single binary).
What your opinion on this?
Add previous and next navigation links to bottom of pages
This issue was imported from GitHub issue: https://github.com/ktorio/ktorio.github.io/issues/105
Problem
As a new Ktor user I wanted to read the entire manual after getting started with the "Quick Start" section and before doing any serious coding.
It got frustrating reading the "Servers" section because once I'd reached the end of a long page, I'd have to scroll back towards the top, find the active navigation link, and then click on the next one.
Solution
Add previous and next links at the bottom of each page that has a category
. I have a local branch that implements this approach that I could merge and use to submit a PR after I finish it.
Update
I have a working branch here: https://github.com/kensiprell/ktorio.github.io/tree/prev-next. I don't want to create a PR until someone takes a look because it touches so much.
_layouts/page.html:
{% if page.status == 'todo' %}
Coming soon...
{% else %}
{{ content }}
{% endif %}
{% if page.category != null %}
{% include prev-next.html %}
{% endif %}
</div>
</div>
</div>
</div>
_includes/prev-next.html:
{% assign entries = site.pages | sort: "path" %}
{% for entry in entries %}
{% if entry.url == page.url %}
{% assign prevIndex = forloop.index0 | minus: 1 %}
{% assign nextIndex = forloop.index0 | plus: 1 %}
<div class="container-fluid col-sm-12" style="padding-top:2rem;border-top:1px solid #ddd;">
<div class="row">
<div class="col-sm-6">
{% if prevIndex %}
{% if entries[prevIndex].category == page.category %}
<a href="{{ entries[prevIndex].url }}">
← {{ entries[prevIndex].title }}
</a>
{% else %}
First page in {{ page.category | capitalize }}
{% endif %}
{% endif %}
</div>
<div class="col-sm-6 text-right">
{% if nextIndex %}
{% if entries[nextIndex].category == page.category %}
<a href="{{ entries[nextIndex].url }}">
{{ entries[nextIndex].title }} →
</a>
{% else %}
Last page in {{ page.category | capitalize }}
{% endif %}
{% endif %}
</div>
</div>
</div>
{% endif %}
{% endfor %}
Here are some images showing the proof of concept.
First page in the "Kotlinx" category
:
Second page in the "Kotlinx" category
:
Last page in the "Kotlinx" category
:
The real work involves making children
directory names match the file names and then adding permalink
s (see image below). I used Jekyll for the first time two days ago, so I'm not 100% certain that my approach won't have negative ramifications elsewhere. However, I haven't noticed any issues running it locally on my computer.
metrics prometheus better documentation
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1842
Subsystem Server - metrics
Is your feature request related to a problem? Please describe. https://ktor.io/servers/features/metrics-micrometer.html
- I'm new to ktor and trying to have prometheus metrics, I do it in java app servers with simpleclient_servlet very easy
- I read install MicrometerMetrics, PrometheusMeterRegistry -> in doc missing dependency
- There is no doc where the metrics are exposed(URI)
- There is no doc if metrics need auth and which type of auth
- There is no doc should I need a route which expose metrics
Describe the solution you'd like
- More clear docs or working sample
Motivation to include to ktor Monitoring of app is very important and prometheus is very popular
Custom JSON mapping with Jackson
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1208
Ktor Version
1.2.2
Feedback
I'm using Jackson mapping in the Client (here: https://ktor.io/clients/http-client/features/json-feature.html), but there seems to be no way to configure the mapper. Normally, I would just include a custom-configured object mapper (the same way I do for server component), but here it's just a single-constructor class tha explicitly calls for the default mapper inside.
Can we get a configuration param for the object mapper? Alternatively, a builder to specify it.
Add support for the SameSite cookie attribute
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/466
It would be useful if ktor was able to set the SameSite
attribute on a cookie, including as part of the sessions support (CookieIdSessionBuilder
).
https://www.owasp.org/index.php/SameSite
Process
Ktor 1.3.2 missing from Maven Central
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1942
It is in JCenter, but not in Central.
I noticed you guys use Bintray to synchronize with Maven Central. Been there, done that. My advise: don't. Instead, use both Nexus Staging plugin and Nexus Publish Plugin.
Note that both plugins are being merged into a new Nexus Publish Plugin, but that's a work in progress. In Micronaut, we still use the former 2, and other than Maven Central being down, it is a much faster and reliable process.
Samples
Corrupted files with low connection rate (Jetty)
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/538
According to users, if a user has poor connection some resources and scripts received by Chrome web browsers are corrupted
Routing: get matcher has higher priority than param matcher on the same level
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/378
Feel free to rename this issue, once the problem is located. Reported by @qweryty at slack:
Giving /test?error=error
and this snippet, the param
route is not handled:
fun main(args: Array<String>) {
val server = embeddedServer(Netty, 9995) {
install(DefaultHeaders)
install(Compression)
install(CallLogging)
routing {
route("test") {
param("error") {
handle {
call.respondText { "error" } // Not handled
}
}
get {
call.respondText { "test" }
}
}
}
}
server.start()
}
With the trace feature from the wip 0.9.2, this is the output:
// /, segment:0 -> SUCCESS @ /test/(method:GET))
// /test, segment:1 -> SUCCESS @ /test/(method:GET))
// /test/[error], segment:1 -> SUCCESS @ /test/[error])
// /test/(method:GET), segment:1 -> SUCCESS @ /test/(method:GET))
Even when not the same, since the previous sample should match all the methods for the param, this other sample would work (matching only get requests for the error param):
routing {
route("test") {
method(HttpMethod.Get) {
param("error") {
handle {
call.respondText { "error" } // Matched
}
}
handle {
call.respondText { "test" }
}
}
}
}
// /, segment:0 -> SUCCESS; Parameters [error=[error]] @ /test/(method:GET)/[error])
// /test, segment:1 -> SUCCESS; Parameters [error=[error]] @ /test/(method:GET)/[error])
// /test/(method:GET), segment:1 -> SUCCESS; Parameters [error=[error]] @ /test/(method:GET)/[error])
// /test/(method:GET)/[error], segment:1 -> SUCCESS @ /test/(method:GET)/[error])
ApplicationCall.push fails under HTTP/1.1
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/349
The problem reproduces with http2-push
from https://github.com/ktorio/ktor-samples
- Run
./gradlew :http2-push:run
- Navigate to http://localhost:8080
Resulting exception:
2018-03-07 14:44:55.948 [nettyCallPool-4-1] ERROR Application - Unhandled: GET - /
java.lang.IllegalArgumentException: ParametersBuilder can only build a single Parameters instance
at io.ktor.http.ParametersBuilder.build(Parameters.kt:26)
at io.ktor.http.URLBuilder.appendTo(URLBuilder.kt:46)
at io.ktor.http.URLBuilder.buildString(URLBuilder.kt:62)
at io.ktor.server.engine.BaseApplicationResponse.push(BaseApplicationResponse.kt:188)
at io.ktor.routing.RoutingApplicationResponse.push(RoutingApplicationCall.kt)
at io.ktor.http.PushKt.push(Push.kt:26)
at io.ktor.http.PushKt.push(Push.kt:13)
at io.ktor.http.PushKt.push(Push.kt:9)
Server
Support routing prefix
Servlet containers and custom /contextPath: war could be deployed to any context path so request's URI will always start with a configurable prefix that will prevent routing from matching URLs so the user has to add an unknown number of wildcards at the root of routing. Removing this prefix by default on the other side makes impossible to reconstruct URL as there is no way to get real URI.
Support for the prefix routing, via call.local.rootpath
Exception after WebSocketSession.close() invocation.
Invoke WebSocketSession.close(). It causes the following exception:
kotlinx.coroutines.experimental.channels.ClosedSendChannelException: Channel was closed
at kotlinx.coroutines.experimental.channels.Closed.getSendException(AbstractChannel.kt:1008)
at kotlinx.coroutines.experimental.channels.AbstractSendChannel.offer(AbstractChannel.kt:185)
at kotlinx.coroutines.experimental.channels.AbstractSendChannel.send(AbstractChannel.kt:175)
at kotlinx.coroutines.experimental.channels.ChannelCoroutine.send$suspendImpl(ChannelCoroutine.kt:33)
at kotlinx.coroutines.experimental.channels.ChannelCoroutine.send(ChannelCoroutine.kt)
at kotlinx.coroutines.experimental.channels.LazyActorCoroutine.send(Actor.kt:192)
at io.ktor.http.cio.websocket.WebSocketWriter.flush(WebSocketWriter.kt:122)
at io.ktor.http.cio.websocket.RawWebSocket.flush(RawWebSocket.kt:37)
at io.ktor.websocket.DelegatedWebSocketServerSession.flush(WebSocketServerSession.kt)
at io.ktor.http.cio.websocket.DefaultWebSocketSessionImpl.flush(DefaultWebSocketSessionImpl.kt)
at io.ktor.websocket.DelegatedDefaultWebSocketServerSession.flush(WebSocketServerSession.kt)
at io.ktor.http.cio.websocket.WebSocketSessionKt.close(WebSocketSession.kt:58)
WebSocketSession.close invokes flush that fails because the websocket is already closed at the moment.
java.lang.NoClassDefFoundError using Netty backend
Hi,
we're sometimes (a few times a week) getting java.lang.NoClassDefFoundError
from Netty. This often/always renders the server unresponsive. We've verified that the class that is not found is present in the (fat) jar that is deployed.
Ktor version: 1.3.2 Kotlin version: 1.3.72
Jul 08 08:59:42 dev java[24380]: Exception in thread "nioEventLoopGroup-4-1" java.lang.NoClassDefFoundError: io/netty/util/concurrent/DefaultPromise$1
Jul 08 08:59:42 dev java[24380]: at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:498)
Jul 08 08:59:42 dev java[24380]: at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
Jul 08 08:59:42 dev java[24380]: at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
Jul 08 08:59:42 dev java[24380]: at io.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:96)
Jul 08 08:59:42 dev java[24380]: at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:1051)
Jul 08 08:59:42 dev java[24380]: at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
Jul 08 08:59:42 dev java[24380]: at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
Jul 08 08:59:42 dev java[24380]: at java.base/java.lang.Thread.run(Thread.java:834)
Jul 08 08:59:42 dev java[24380]: Caused by: java.lang.ClassNotFoundException: io.netty.util.concurrent.DefaultPromise$1
Jul 08 08:59:42 dev java[24380]: at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
Jul 08 08:59:42 dev java[24380]: at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
Jul 08 08:59:42 dev java[24380]: at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
Jul 08 08:59:42 dev java[24380]: ... 8 more
I'm not ruling out that we're doing something wrong, but there's no real giveaway into what that is exactly.
Thanks, Ilja JOIN, https://join.cc
Parent coroutine context cancellation sometimes leads to hanging coroutine
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1947
Ktor Version and Engine Used (client or server and name) ktor-server-jetty and ktor-websockets 1.3.2 coroutines version 1.3.4
Describe the bug Sometimes my tests hang on post-test test job cancellation. That job contains both client and server coroutine scopes/jobs as decdendants.
Coroutines dump (printed after test timeout) gives this:
[15:17:55] : [gant] [junit] Coroutine "jetty-call-handler#6995":StandaloneCoroutine{Cancelling}@258a8584, state: SUSPENDED
[15:17:55] : [gant] [junit] at io.ktor.server.jetty.JettyKtorHandler$handle$2.invokeSuspend(JettyKtorHandler.kt:125)
[15:17:55] : [gant] [junit] at (Coroutine creation stacktrace. ( )
[15:17:55] : [gant] [junit] at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt.createCoroutineUnintercepted(IntrinsicsJvm.kt:116)
[15:17:55] : [gant] [junit] at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
[15:17:55] : [gant] [junit] at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
[15:17:55] : [gant] [junit] at io.ktor.server.jetty.JettyKtorHandler.handle(JettyKtorHandler.kt:85)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.server.Server.handle(Server.java:500)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:270)
[15:17:55] : [gant]
[15:17:55] : [gant] [junit] at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:388)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
[15:17:55] : [gant] [junit] at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
To Reproduce Unfortunately, the bug can't be reproduced locally even with the existing tests, happens only on CI. No minimal sample yet.
Expected behavior No suspended coroutines after a parent-induced cancellation.
Class cast exception on web-socket cancelation
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1929
Ktor Version and Engine Used (client or server and name) ktor-cio server 1.3.2
Describe the bug I am getting an internal error when force-closing server with opened web-socket connection Here is the stack trace:
13:32:20.839 [DefaultDispatcher-worker-4] ERROR io.ktor.server.cio.HttpServer - Unhandled exception caught for CoroutineName(http-pipeline-writer)
kotlinx.coroutines.CoroutinesInternalError: Fatal exception in coroutines machinery for DispatchedContinuation[ExperimentalCoroutineDispatcher@45e6d6a4[scheduler = DefaultDispatcher@6341301e[Pool Size {core = 10, max = 1024}, Worker States {CPU = 6, blocking = 0, parked = 8, dormant = 0, terminated = 0}, running workers queues = [0c, 0c, 0c, 0c, 0c, 0c], global CPU queue size = 5, global blocking queue size = 0, Control State {created workers= 14, blocking tasks = 0, CPUs acquired = 6}]], Continuation at io.ktor.server.cio.backend.ServerPipelineKt$startServerConnectionPipeline$1$outputsActor$1.invokeSuspend(ServerPipeline.kt:49)@2482cf33]. Please read KDoc to 'handleFatalException' method and report this incident to maintainers
at kotlinx.coroutines.DispatchedTask.handleFatalException$kotlinx_coroutines_core(DispatchedTask.kt:93)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:64)
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.ClassCastException: class kotlin.coroutines.jvm.internal.CompletedContinuation cannot be cast to class kotlinx.coroutines.DispatchedContinuation (kotlin.coroutines.jvm.internal.CompletedContinuation and kotlinx.coroutines.DispatchedContinuation are in unnamed module of loader 'app')
at kotlinx.coroutines.CoroutineDispatcher.releaseInterceptedContinuation(CoroutineDispatcher.kt:103)
at kotlin.coroutines.jvm.internal.ContinuationImpl.releaseIntercepted(ContinuationImpl.kt:118)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:39)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:55)
... 4 common frames omitted
CORS - request with non-simple content type not failing
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1906
Ktor Version and Engine Used Ktor 1.3.2, Netty
Here is the failing test:
@Test
fun testRequestWithNonSimpleContentTypeShouldFail() {
withTestApplication {
application.install(CORS) {
anyHost()
}
application.routing {
post("/") {
call.respond(HttpStatusCode.OK)
}
}
handleRequest(HttpMethod.Post, "/") {
addHeader(HttpHeaders.Origin, "http://my-host")
addHeader(HttpHeaders.ContentType, "application/json")
setBody("""{"cors":"test"}""")
}.apply {
assertEquals(HttpStatusCode.Forbidden, response.status())
}
}
}
I've read it that CORS feature doesn’t allow non-simple request body content types by default but this test fails. Am I understanding it wrong?
ktor not closing user connections
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1885
Ktor Version and Engine Used (client or server and name) 1.3.2, Netty server, routes
ktor_version=1.3.2,
kotlin_version=1.3.70
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
implementation("io.ktor:ktor-server-netty:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
testImplementation("io.ktor:ktor-server-tests:$ktor_version")
Describe the bug
I migrated one of my project form php to ktor and deployed it in production for about 5k users. After one day server stopped working and linux file opened limits exceed which was 20k and when i checked with command ss -ant | grep :8383 | wc -l
connections were 20k+ but i don't have users more than 5k which were not all active. Then i switched my floating ip to old php server and no more request to this server next day when i checked again with command ss -ant | grep :8383 | wc -l
connections were still 20k+ even no more new request were coming and all connections were in ESTAB state.
So after that i created new project which was empty with ktor, kotlin dsl i mentioned its dependencies above and run that on that server Then i run this command watch -n 1 'ss -ant | grep {MY_IP}'
on server to filter connections for my ip first it was no connection from my ip on port 8383 then i send single request from postman on SERVER_IP:8388
which was simple get request and response was from call.respond("Testing")
and on server terminal it show me connection on 8383 with my ip. In postman request was complete and i got result. Then i closed postman and keep checking when it will close my connection from server. I checked that after an hour still connection was not closed and when i requested again it was still using that same connection. So it was reusing connection and not closing even after some hours. I'm not sure but may be error with keep alive or http2. I'm not sending any header i request.
I didn't tried any other ktor or kotlin version and I'm not able to reproduce it local. I'm running jar file generated with shadowjar on linux server with systemd service.
To Reproduce
- create new project with ktor version 1.3.2 and kotlin 1.3.70 with GradleKotlinDsl
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
@Suppress("unused") // Referenced in application.conf
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
install(Routing) {
get("/") {
call.respond("Testing")
}
}
}
- create jar file with shadowjar
tasks {
named<ShadowJar>("shadowJar") {
archiveBaseName.set("shadow")
mergeServiceFiles()
manifest {
attributes(mapOf("Main-Class" to mainClasses))
}
transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer::class.java) {
setPath("META-INF/services")
include("org.eclipse.jetty.http.HttpFieldPreEncoder")
}
archiveFileName.set("example.jar")
}
}
- upload on ubuntu server and create its systemd service and run with systemctl start example service file:
[Unit]
Description=example
After=syslog.target
[Service]
User=root
LimitNOFILE=20536
ExecStart=/var/www/example/example-script
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
example-script:
#!/bin/sh
cd /var/www/example
java -jar exmaple.jar
Expected behavior
When user make request to http://SERVER_IP:APP_PORT it should close it after request complete but it keep opened it until we will stop server. Checking with ss -ant | grep {CLIENT_PUBLIC_IP}
Screenshots
This is screenshot which i get after an hour when made last request in screenshot last connection with port 8383 that is from my machine and it is still not closed.
Scheme instead of version in OriginConnectionPoint
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1869
Ktor Version and Engine Used (client or server and name) 1.3.2, server
Describe the bug
ApplicationCall.request.origin.remoteHost
holds the scheme instead of the version (like 'http' instead of HTTP/1.1
).
The root cause is this line.
I provided a fix here: https://github.com/ktorio/ktor/pull/1871
To Reproduce Steps to reproduce the behavior:
- Configure request logging for a ktor server
- Install 'XForwardedHeaderSupport`
- print
call.request.origin.remoteHost
- Run the server behind a proxy
- Send a request to the proxy
- Observe that
http
is printed
Expected behavior
HTTP/1.1
should be printed
Get client certificate information from request
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1767
Subsystem Server
Is your feature request related to a problem? Please describe. We want to use client-side certificates to authenticate HTTPS requests from connected devices. In our code, we need to get the client certificate information to determine the DN in the certificate.
In a regular servlet, client certificate information can be accessed using request.getAttribute("javax.servlet.request.X509Certificate")
, which returns the certificate chain.
Our strategy to get this to work in ktor was to run our application as a WAR in a Tomcat instance configured to do client authentication. Unfortunately, request attributes do not seem to be available. (Note that a Spring Boot application deployed under the same Tomcat can access these attributes, so we know Tomcat is configured correctly for TLS.)
Describe the solution you'd like We'd like some way to access servlet request attributes from ktor application code.
Motivation to include to ktor Client certificates are an important part of IoT deployments, where ktor would shine because typically a server needs to manage a lot of concurrent connections. Ktor itself doesn't support client certificate authentication - our solution would be a workaround if only we could access request attributes.
Occasionally Ktor utilizes 100% CPU without any load after failed request.
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1433
Ktor Version and Engine Used (client or server and name) Our application uses both - Ktor server and client. Versions are ktor: 1.2.3 kotlin: 1.3.50 server's engine: Netty client's engine: CIO
// http server
implementation "io.ktor:ktor-server-core:$ktor_version"
implementation "io.ktor:ktor-server-netty:$ktor_version"
implementation "io.ktor:ktor-server-host-common:$ktor_version"
implementation "io.ktor:ktor-server-sessions:$ktor_version"
implementation "io.ktor:ktor-auth:$ktor_version"
implementation "io.ktor:ktor-jackson:$ktor_version"
implementation "io.ktor:ktor-auth-jwt:$ktor_version"
implementation "io.ktor:ktor-locations:$ktor_version"
// http client
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-core-jvm:$ktor_version"
implementation "io.ktor:ktor-client-apache:$ktor_version"
implementation "io.ktor:ktor-client-cio:$ktor_version"
implementation "io.ktor:ktor-client-json:$ktor_version"
implementation "io.ktor:ktor-client-json-jvm:$ktor_version"
implementation "io.ktor:ktor-client-jackson:$ktor_version"
implementation "io.ktor:ktor-client-logging:$ktor_version"
implementation "io.ktor:ktor-client-logging-jvm:$ktor_version"
implementation "io.ktor:ktor-client-auth:$ktor_version"
implementation "io.ktor:ktor-client-auth-jvm:$ktor_version"
Run on a VPC: Linux Debian 9.11
Describe the bug Monitoring reported that our server consumes 100%. The server does nothing (it's test environment and issue happened later evening) but shows high CPU usage. In jstack, I found only one suspicious thread:
"Thread-8@16328" daemon prio=10 tid=0x6f nid=NA runnable
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPoll.wait(EPoll.java:-1)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:120)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:124)
- locked <0x3ff5> (a sun.nio.ch.EPollSelectorImpl)
- locked <0x403d> (a sun.nio.ch.Util$2)
at sun.nio.ch.SelectorImpl.selectNow(SelectorImpl.java:146)
at io.ktor.network.selector.ActorSelectorManager.process(ActorSelectorManager.kt:81)
at io.ktor.network.selector.ActorSelectorManager$process$1.invokeSuspend(ActorSelectorManager.kt:-1)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:834)
Strace shows that some thread is performing a lot of epolls (all with same fd) and these polls are finished at once:
[pid 5938] 23:07:27.637413 epoll_wait(63, [], 1024, 0) = 0 <0.000014>
[pid 5938] 23:07:27.637473 epoll_wait(63, [], 1024, 0) = 0 <0.000016>
[pid 5938] 23:07:27.637538 epoll_wait(63, [], 1024, 0) = 0 <0.000015>
[pid 5938] 23:07:27.637598 epoll_wait(63, [], 1024, 0) = 0 <0.000043>
[pid 5938] 23:07:27.637687 epoll_wait(63, [], 1024, 0) = 0 <0.000016>
[pid 5938] 23:07:27.637739 epoll_wait(63, [], 1024, 0) = 0 <0.000015>
[pid 5938] 23:07:27.637799 epoll_wait(63, [], 1024, 0) = 0 <0.000015>
[pid 5938] 23:07:27.637860 epoll_wait(63, [], 1024, 0) = 0 <0.000016>
lsof shows:
java 3405 root 63u a_inode 0,13 0 17139 [eventpoll]
To Reproduce According to monitoring system, usage spike happened at same time then an user tried to logout using expired session. In our system it will lead to throwing an exception and handing it with status page:
install(StatusPages) {
...
Exceptions.apply {
httpExceptions(testing)
}
}
@KtorExperimentalAPI
fun StatusPages.Configuration.httpExceptions(testing: Boolean) {
exception<HttpError> {
logger.warn("${this.context.request.uri} - failed due to ${it.message}${it.cause?.let {"(caused by $it)"}}", it)
if (it.code == HttpStatusCode.Unauthorized) {
call.unauthorized(it.body)
} else {
it.body?.let { body ->
call.respond(it.code, body)
} ?: call.respond(it.code)
}
}
...
@KtorExperimentalAPI
suspend fun ApplicationCall.unauthorized(maybeError: HttpErrorBody? = null): Unit {
// set WWW-Authenticate (as per RFC required for 401 status)
val realm = application.environment.config.property("authentication.realm").getString()
val header = HttpAuthHeader.Parameterized(AuthenticationScheme, mapOf(HttpAuthHeader.Parameters.Realm to realm))
response.headers.append(HttpHeaders.WWWAuthenticate, header.toString())
// clear session
sessions.clear<MySessionCookie>()
// return status code and body
maybeError?.let { body ->
respond(HttpStatusCode.Unauthorized, body)
} ?: respond(HttpStatusCode.Unauthorized)
}
In log, we have:
2019-11-08 19:31:58,277 DEBUG [nioEventLoopGroup-4-1][172.26.0.8][REQ-496] auth - session 8670142f-97a2-4035-9f17-e62774e0a7c5 has expired
2019-11-08 19:31:58,277 INFO [nioEventLoopGroup-4-1][172.26.0.8][REQ-496] application - finished POST /web/v1/logout with null
2019-11-08 19:31:58,278 WARN [nioEventLoopGroup-4-1][172.26.0.8][REQ-496] application - /web/v1/logout - failed due to SessionExpired com.example.http.HttpError$UnauthorizedAccess: SessionExpired
at com.example.ApplicationKt$module$12$$special$$inlined$session$lambda$1.invokeSuspend(Application.kt:297)
at com.example.ApplicationKt$module$12$$special$$inlined$session$lambda$1.invoke(Application.kt)
at com.example.ApplicationKt$module$12$$special$$inlined$session$1.invokeSuspend(SessionAuth.kt:156)
at com.example.ApplicationKt$module$12$$special$$inlined$session$1.invoke(SessionAuth.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.auth.Authentication.processAuthentication(Authentication.kt:228)
at io.ktor.auth.Authentication$interceptPipeline$2.invokeSuspend(Authentication.kt:123)
at io.ktor.auth.Authentication$interceptPipeline$2.invoke(Authentication.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.routing.Routing.executeResult(Routing.kt:147)
at io.ktor.routing.Routing.interceptor(Routing.kt:34)
at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:99)
at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:106)
at io.ktor.features.ContentNegotiation$Feature$install$1.invoke(ContentNegotiation.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.features.StatusPages$interceptCall$2.invokeSuspend(StatusPages.kt:98)
at io.ktor.features.StatusPages$interceptCall$2.invoke(StatusPages.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:180)
at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:97)
at io.ktor.features.StatusPages$Feature$install$2.invokeSuspend(StatusPages.kt:137)
at io.ktor.features.StatusPages$Feature$install$2.invoke(StatusPages.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.features.CallLogging$Feature$install$1$invokeSuspend$$inlined$withMDC$1.invokeSuspend(CallLogging.kt:226)
at io.ktor.features.CallLogging$Feature$install$1$invokeSuspend$$inlined$withMDC$1.invoke(CallLogging.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:156)
at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
at io.ktor.features.CallLogging$Feature$install$1.invokeSuspend(CallLogging.kt:230)
at io.ktor.features.CallLogging$Feature$install$1.invoke(CallLogging.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invokeSuspend(DefaultEnginePipeline.kt:118)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invokeSuspend(NettyApplicationCallHandler.kt:36)
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:111)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:154)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:54)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:26)
at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:20)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:56)
at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:365)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:416)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:515)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)
To me, It looks very similar to: https://github.com/ktorio/ktor/issues/1041 But it was about client, while we've issue with a server.
Expected behavior Doesn't consume CPU without load.
Multiple matching paths do not choose higher quality path (component with more static values) when the lower quality one is authenticated
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1158
Ktor Version
1.1.3
Ktor Engine Used(client or server and name)
Jetty server
JVM Version, Operating System and Relevant Context
JDK 11, Linux 5.0
Feedback
Given the following code:
import io.ktor.application.call
import io.ktor.response.respondText
import io.ktor.routing.get
import io.ktor.routing.routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.jetty.Jetty
fun main() {
val server = embeddedServer(Jetty, port = 9010) {
routing {
get("/foo:{bar}/{baz}") {
call.respondText("Received route1 bar=${call.parameters["bar"]}, baz=${call.parameters["baz"]}")
}
get("/{bar}/{baz}") {
call.respondText("Received route2 bar=${call.parameters["bar"]}, baz=${call.parameters["baz"]}")
}
}
}
server.start(wait = true)
}
The first route has a static component beginning with /foo:
whereas the second route is all dynamic parameters. The first route is therefore of higher quality for matching purposes.
With this call:
http get :9010/foo:a/b
the result is as expected: Received route1 bar=a, baz=b
However, when changing the code to wrap the second route in an authenticate
block like this:
routing {
get("/foo:{bar}/{baz}") {
call.respondText("Received route1 bar=${call.parameters["bar"]}, baz=${call.parameters["baz"]}")
}
authenticate {
get("/{bar}/{baz}") {
call.respondText("Received route2 bar=${call.parameters["bar"]}, baz=${call.parameters["baz"]}")
}
}
}
I would expect the routing decision to be the same, but:
http get :9010/foo:a/b
now attempts to execute the lower quality authenticated path:
HTTP/1.1 401 Unauthorized
"HTTP Exception 404" for POST from Location
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/601
Unhandled: POST - /sample00
sample code: https://github.com/BorzdeG/sample-ktor-kodein-controllers/blob/master/sample-00/src/main/kotlin/sample/sample00/Sample00Controller.kt#L24
tests: https://github.com/BorzdeG/sample-ktor-kodein-controllers/blob/master/sample-00/src/test/kotlin/sample/sample00/Sample00ControllerTest.kt
ktor-client doesn't send body sometimes
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/569
I uploaded sample project.
With netty ktor-server and apache ktor-client, it posts form-data a lot in short time and any moment a request with missing body occurs. This appears as java.io.IOException: Broken delimiter occurred
.
Is this bug?
[Regression Bug 0.9.4] Adding call.respond to pipeline throws java.lang.reflect.InvocationTargetException
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/566
Hi, I'm having trouble using call.respond anywhere in my application. If I add it, the application pipeline dies and the server exits.
Error log App Source Gradle dependencies
Machine: Windows 10, running in Intellij IDE
The strange thing is that if I use only call.respondText
, the server can listen to messages. Using call.respond
is what kills the app before it can start listening
Thanks in advance for any help
It might have been related to this ticket, but the error specified there is more high level than my stack dump.
Websockets and Netty weird exception
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/558
I run into this exception every now and then but my application functionality is not prevented. The only thing I notice when it happens is that my websocket has to reconnect but I didn't close it.
Exception in thread "nettyWorkerPool-3-6 @coroutine#2070" io.ktor.util.cio.ChannelWriteException: Cannot write to a channel at io.ktor.server.netty.cio.NettyResponsePipeline.processJobs(NettyResponsePipeline.kt:425) at io.ktor.server.netty.cio.NettyResponsePipeline$processJobs$1.doResume(NettyResponsePipeline.kt) at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resumeWithException(CoroutineImpl.kt:48) at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resumeWithException(CoroutineImpl.kt:47) at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resumeWithException(CoroutineImpl.kt:47) at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:41) at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:149) at kotlinx.coroutines.experimental.io.internal.MutableDelegateContinuation.run(MutableDelegateContinuation.kt:14) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) Caused by: java.nio.channels.ClosedChannelException at io.netty.channel.AbstractChannel$AbstractUnsafe.write(...)(Unknown Source)
Setting multiple cache control directives is impossible with current API
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/557
Using the install for CachingHeaders I want to configure it in such a way it produces this header:
Cache-Control: no-cache, no-store, must-revalidate
I cannot do it. For example I tried this:
options { outgoingContent -> when (outgoingContent.contentType?.withoutParameters()) { ContentType.Text.CSS -> CachingOptions(CacheControl.NoCache(null)) else -> null } } options { outgoingContent -> when (outgoingContent.contentType?.withoutParameters()) { ContentType.Text.CSS -> CachingOptions(CacheControl.NoStore(null)) else -> null } }
it makes it twice:
cache-control: no-cache cache-control: no-store
The best would be if options can return a lamda with a list of CachingOptions as return value, then the system would add all of them. Or allow caching options to accept varargs?
Also, why is "must revalidate" tied to max age? See this, it can be also on it's own: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Examples
Sensible defaults for compression feature
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/544
At the moment Ktor is trying to compress even the steel. I added this method in my core lib to overcome that. I think there should be a similar thing active by default. What do you reckon?
fun Application.installDefaultCompresion() {
install(Compression) {
gzip {
matchContentType(
ContentType.Text.Any,
ContentType.Application.JavaScript,
ContentType.Application.Json,
ContentType.Application.Rss,
ContentType.Application.Xml,
ContentType.Application.Xml_Dtd,
ContentType.Application.Atom,
ContentType.Image.SVG,
ContentType.Image.XIcon
)
minimumSize(1400) // not worth compressing
}
}
}
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
MultiPartData.readAllParts() throws java.io.IOException when multipart list is empty
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/482
Problem description: When receiving an empty multipart-form request on a POST route, the request handler throws an IOException, preventing graceful handling or the display of an error message. Instead, a 500 error message is returned.
Minimum working example:
import io.ktor.application.*
import io.ktor.content.readAllParts
import io.ktor.response.*
import io.ktor.request.*
import io.ktor.routing.post
import io.ktor.routing.routing
fun main(args: Array<String>): Unit = io.ktor.server.netty.DevelopmentEngine.main(args)
fun Application.module() {
routing {
post("/add") {
if(call.request.isMultipart()) {
val multipart = call.receiveMultipart()
val formItems = multipart.readAllParts() // <-- this breaks when sent an empty request
call.respondText("recvd: $multipart")
}
else {
call.respond("no multipart, fix request!")
}
}
}
}
Send a request with an empty multipart body to see the issue:
curl -X POST \
http://localhost:8080/add \
-H 'Cache-Control: no-cache' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
Error Message:
22:57:11.866 [nettyCallPool-4-4] ERROR Application - Unhandled: POST - /add
java.io.IOException: Broken delimiter occurred
at kotlinx.coroutines.experimental.io.DelimitedKt$skipDelimiterSuspend$2.doResume(Delimited.kt:57)
at kotlinx.coroutines.experimental.io.DelimitedKt$skipDelimiterSuspend$2.invoke(Delimited.kt)
at kotlinx.coroutines.experimental.io.DelimitedKt$skipDelimiterSuspend$2.invoke(Delimited.kt)
at kotlinx.coroutines.experimental.io.ByteBufferChannel.lookAheadSuspend(ByteBufferChannel.kt:1746)
at kotlinx.coroutines.experimental.io.DelimitedKt.skipDelimiterSuspend(Delimited.kt:55)
at kotlinx.coroutines.experimental.io.DelimitedKt.skipDelimiter(Delimited.kt:50)
at io.ktor.http.cio.MultipartKt.boundary(Multipart.kt:89)
at io.ktor.http.cio.MultipartKt$parseMultipart$1.doResume(Multipart.kt:158)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:42)
at kotlinx.coroutines.experimental.DispatchedKt.resumeCancellable(Dispatched.kt:209)
at kotlinx.coroutines.experimental.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:35)
at kotlinx.coroutines.experimental.CoroutineStart.invoke(CoroutineStart.kt:111)
at kotlinx.coroutines.experimental.AbstractCoroutine.start(AbstractCoroutine.kt:165)
at kotlinx.coroutines.experimental.channels.ProduceKt.produce(Produce.kt:95)
at kotlinx.coroutines.experimental.channels.ProduceKt.produce$default(Produce.kt:88)
at io.ktor.http.cio.MultipartKt.parseMultipart(Multipart.kt:145)
at io.ktor.http.cio.MultipartKt.parseMultipart(Multipart.kt:138)
at io.ktor.http.cio.CIOMultipartDataBase.<init>(CIOMultipartData.kt:33)
at io.ktor.http.cio.CIOMultipartDataBase.<init>(CIOMultipartData.kt:31)
at io.ktor.server.engine.DefaultTransformKt.multiPartData(DefaultTransform.kt:70)
at io.ktor.server.engine.DefaultTransformKt.access$multiPartData(DefaultTransform.kt:1)
at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$2.doResume(DefaultTransform.kt:33)
at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$2.invoke(DefaultTransform.kt)
at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$2.invoke(DefaultTransform.kt)
at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
at io.ktor.pipeline.Pipeline.execute(Pipeline.kt:22)
at io.ktor.request.ApplicationReceiveFunctionsKt.receive(ApplicationReceiveFunctions.kt:64)
at io.sebi.ApplicationKt$module$1$1.doResume(application.kt:31)
at io.sebi.ApplicationKt$module$1$1.invoke(application.kt)
at io.sebi.ApplicationKt$module$1$1.invoke(application.kt)
at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
at io.ktor.pipeline.Pipeline.execute(Pipeline.kt:22)
at io.ktor.routing.Routing.executeResult(Routing.kt:100)
at io.ktor.routing.Routing.interceptor(Routing.kt:25)
at io.ktor.routing.Routing$Feature$install$1.doResume(Routing.kt:66)
at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt)
at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt:51)
at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
at io.ktor.pipeline.Pipeline.execute(Pipeline.kt:22)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.doResume(DefaultEnginePipeline.kt:66)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
at io.ktor.pipeline.PipelineContext.proceed(PipelineContext.kt:49)
at io.ktor.pipeline.Pipeline.execute(Pipeline.kt:22)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.doResume(NettyApplicationCallHandler.kt:31)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt:10)
at kotlinx.coroutines.experimental.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:44)
at kotlinx.coroutines.experimental.CoroutineStart.invoke(CoroutineStart.kt:113)
at kotlinx.coroutines.experimental.AbstractCoroutine.start(AbstractCoroutine.kt:165)
at kotlinx.coroutines.experimental.BuildersKt__Builders_commonKt.launch(Builders.common.kt:72)
at kotlinx.coroutines.experimental.BuildersKt.launch(Unknown Source)
at kotlinx.coroutines.experimental.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:64)
at kotlinx.coroutines.experimental.BuildersKt.launch$default(Unknown Source)
at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:22)
at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:16)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:38)
at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:353)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:844)
Confusing log message about failed session lookup
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/445
Hello,
I have a problem when I do : call.sessions.get
"For unexistant sessions that are not available, would be like if no session was provided and you can do whatever you need in your route handler"
Thank for your help !
raw socket bug?
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/409
Hi, I currently use ktor raw socket tools to write a network program. I found the reader fail to read data sometimes. Here is code reproducing error:
ktor version is 0.9.2
import io.ktor.network.sockets.aSocket
import io.ktor.network.sockets.openReadChannel
import io.ktor.network.sockets.openWriteChannel
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking
import java.net.InetSocketAddress
fun main(args: Array<String>) {
async {
val server = aSocket().tcp().bind(InetSocketAddress(12000))
while (true) {
println("accept")
val socket = server.accept()
launch {
val rc = socket.openReadChannel()
val wc = socket.openWriteChannel(autoFlush = true)
var seq = 0
while (true) {
wc.writeByte(1)
wc.writeInt(1)
}
}
}
}
Thread.sleep(1000)
runBlocking {
aSocket().tcp().connect(InetSocketAddress("localhost", 12000)).use { socket ->
val rc = socket.openReadChannel()
val wc = socket.openWriteChannel(autoFlush = true)
while (true) {
check(rc.readByte() == 1.toByte()) { "byte" }
check(rc.readInt() == 1) { "int" }
}
}
}
}
Brotli Support
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/408
Just curious if there are plans to add support for brotli compression?
Saw the implementation in ktor-server/ktor-server-core/src/io/ktor/features/Compression.kt
for Gzip and Deflate. Wanted my application to respond with a brotli compressed js script (as it is 30% smaller than the gzip version) but failed to overwrite the Content-Encoding in the response header with br.
Miserable attempt: (the file was already compressed outside of ktor)
get("/bundle.min.js") {
// attempt 1
call.respondFile(File("public/js/bundle.min.js.br"), {
setProperty(AttributeKey("Content-Type"), "application/octet-stream")
setProperty(AttributeKey("Content-Encoding"), "br")
finish()
})
// attempt 2 bout = ByteArrayOutputStream
call.respondWrite(ContentType.Application.OctetStream, HttpStatusCode.OK, { bout })
}
HTTP/2 push fails with netty engine
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/350
The problem reproduces with http2-push
from https://github.com/ktorio/ktor-samples
- Run
./gradlew :http2-push:run
- Navigate to https://localhost:8443/
Resulting exception:
2018-03-07 14:47:07.089 [nettyWorkerPool-3-2] WARN i.n.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
java.lang.IllegalArgumentException: Stream no longer exists: 3
at io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder.requireStream(DefaultHttp2ConnectionEncoder.java:343)
at io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder.writePushPromise(DefaultHttp2ConnectionEncoder.java:279)
at io.ktor.server.netty.http2.NettyHttp2Handler.startHttp2PushPromise$ktor_server_netty(NettyHttp2Handler.kt:104)
at io.ktor.server.netty.http2.NettyHttp2ApplicationResponse$push$1.run(NettyHttp2ApplicationResponse.kt:49)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
2018-03-07 14:47:07.093 [nettyWorkerPool-3-2] WARN i.n.util.concurrent.DefaultPromise - An exception was thrown by io.ktor.server.netty.http2.NettyHttp2Handler$startHttp2PushPromise$1.operationComplete()
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Stream no longer exists: 3
at io.netty.util.concurrent.AbstractFuture.get(AbstractFuture.java:41)
at io.ktor.server.netty.http2.NettyHttp2Handler$startHttp2PushPromise$1.operationComplete(NettyHttp2Handler.kt:109)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:507)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:481)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:420)
at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:163)
at io.netty.channel.DefaultChannelPromise.addListener(DefaultChannelPromise.java:93)
at io.ktor.server.netty.http2.NettyHttp2Handler.startHttp2PushPromise$ktor_server_netty(NettyHttp2Handler.kt:108)
at io.ktor.server.netty.http2.NettyHttp2ApplicationResponse$push$1.run(NettyHttp2ApplicationResponse.kt:49)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Stream no longer exists: 3
at io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder.requireStream(DefaultHttp2ConnectionEncoder.java:343)
at io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder.writePushPromise(DefaultHttp2ConnectionEncoder.java:279)
at io.ktor.server.netty.http2.NettyHttp2Handler.startHttp2PushPromise$ktor_server_netty(NettyHttp2Handler.kt:104)
... 7 common frames omitted
Receive JSON formatted POST data into ValuesMap with Gson
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/294
I've set up ContentNegotiation
with Gson
but had troubles figuring out how I can receive POST
data.
Examples pointed me towards call.receive<ValuesMap>()
which doesn't work with JSON in the request body, instead it worked after creating a custom data class
and mapping it directly into it. It would be nice to be able to directly map into a ValuesMap
. I guess up for discussion would be how to handle nested objects, if they are stored "as is" or with nested maps.
Sample using Swagger
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/158
Would be nice to have a sample with Swagger integration. Especially I am interested whether Swagger could work with Ktor like with Spring Boot and automatically generate the API using reflection, i.e. generate a Swagger console that reflects the API of the implementation.
Generating code from Swagger specification is another pattern but one that usually does not work in practice beyond "Hello, World!".
Enable Ktor services to expose something like http://petstore.swagger.io, to communicate their APIs would be a key asset to Ktor.
Support Sealed Classes inside Session-Objects
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/129
Test Infrastructure
TestApplicationRequest returns status code "400 Bad Request" when server handling ContentNegotiation Jackson on a post request (application/json) but works well from a curl request
[General Information]
ktor_version=1.3.2
kotlin_version=1.3.70
server engine : io.ktor.server.netty.EngineMain
Short version of ApplicationTest.kt
class ApplicationTest {
@Test
fun postPlace() {
withTestApplication({ module(testing = true) }) {
handleRequest(HttpMethod.Post, "/places") {
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.contentType)
setBody(json { "title" to "Yammiii" }.toString())
}.apply {
assertEquals(HttpStatusCode.BadRequest, response.status(), "Missing mandatory fields")
}
handleRequest(HttpMethod.Post, "/places") {
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.contentType)
setBody(json {
"title" to "Yammiii"
"country" to "Belgique"
"city" to "Bruxelles"
}.toString())
}.apply {
assertEquals(HttpStatusCode.Created, response.status(), "all mandatory fields are provided")
}
}
}
}
The lineassertEquals(HttpStatusCode.Created, response.status(), "all mandatory fields are provided")
fails with a 400 Bad Request while the same curl (on Windows) request works : curl -d "{\"title\":\"value1\", \"country\":\"value2\", \"city\":\"value3\"}" -H "Content-Type:application/json" -X POST http://localhost:8080/places
Short version of 'Application.kt'
@Suppress("unused") // Referenced in application.conf
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
install(DefaultHeaders)
install(CallLogging)
install(WebSockets)
install(ContentNegotiation) {
jackson {
}
}
DatabaseFactory.init()
val beehivePlaceService = BeehivePlaceService()
...
install(Routing) {
beehivePlace(beehivePlaceService)
...
}
}
fun Route.beehivePlace(beehivePlaceService: BeehivePlaceService) {
route("/places") {
...
post("/") {
val beehivePlace = call.receive<NewBeehivePlace>()
call.respond(HttpStatusCode.Created, beehivePlaceService.addBeehivePlace(beehivePlace))
}
....
}
}
BeehivePlace.kt
package com.sokarcreative.beefamily.model
import org.jetbrains.exposed.sql.Table
...
data class NewBeehivePlace(
val id: Int? = null,
val title: String = "",
val country: String? = null,
val city: String? = null,
val postalCode: Int? = null,
val streetAddress: String? = null,
val streetNumber: String? = null,
val streetBox: String? = null,
val phoneNumber: String? = null,
val websiteUrl: String? = null,
val facebookUrl : String? = null,
val twitterUrl: String? = null,
val instagramUrl: String? = null,
val mailAddress: String? = null
)
The 400 Bad Request is thrown when call.receive is called at this line : val beehivePlace = call.receive<NewBeehivePlace>()
Why is this post request working on a simple curl request and not in the TestApplicationRequest ?
I started calls directly from the test application class and I didn't understand why it isn't working, I thought it was a problem of mine before testing in a curl request and see that it was actually working on it.
Any help would be really appreciated :)
A beginner on ktor,
Other
Unsubscribe from events
Environment monitor events unsubscription is too hard to do in client's code, especially if it is not a feature (that has close()) Use DisposableHandle in features
Support routing data in content negotiation
Sometimes it is not very convenient to provide Accept
headers to request a specific type of content. Variants include /foo?type=json
or /foo.json
. It would be nice if ContentNegotiation
could use information from routing in some way to decide on the desired content type.
Send 100 Continue response only when getting a request to receive `IncomingContent`
Thymeleaf ExpressionObjecs invalied , ex #httpServletRequest
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1997
StandardExpressionObjectFactory.buildObject(
final IExpressionContext context,
final String expressionObjectName
)
Context is EngineContext, not WebEngineContext, so can't get session
, request
, response
and so on;
how to resolve it?
Header Content-Length is controlled by the engine and cannot be set explicitly
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1993
When using the Ktor http client (ktor 1.3.2) for PUT request without payload the Content-Length is not set, but it should be.
But, setting the Content-Length explicitly in the code with "header(HttpHeaders.ContentLength, 0)" cause the following exception:
io.ktor.http.UnsafeHeaderException: Header Content-Length is controlled by the engine and cannot be set explicitly
at io.ktor.client.engine.HttpClientEngineKt.validateHeaders(HttpClientEngine.kt:140)
at io.ktor.client.engine.HttpClientEngineKt.access$validateHeaders(HttpClientEngine.kt:1)
at io.ktor.client.engine.HttpClientEngine$install$1.invokeSuspend(HttpClientEngine.kt:62)
at io.ktor.client.engine.HttpClientEngine$install$1.invoke(HttpClientEngine.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.client.features.HttpSend$DefaultSender.execute(HttpSend.kt:115)
at io.ktor.client.features.HttpSend$Feature$install$1.invokeSuspend(HttpSend.kt:79)
at io.ktor.client.features.HttpSend$Feature$install$1.invoke(HttpSend.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.client.features.HttpRequestLifecycle$Feature$install$1.invokeSuspend(HttpRequestLifecycle.kt:34)
at io.ktor.client.features.HttpRequestLifecycle$Feature$install$1.invoke(HttpRequestLifecycle.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.client.HttpClient.execute(HttpClient.kt:164)
at io.ktor.client.statement.HttpStatement.executeUnsafe(HttpStatement.kt:104)
at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:43)
at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:58)
Client for OkHttp 3.12.x
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1983
Hi! We'd like to use ktor with OkHttp in Android app but OkHttp 3.13 and newer supports only Android 5+. We still have to to support Android 4 in our app.
Subsystem ktor-client-okhttp
Describe the solution you'd like Add separate client ktor-client-okhttp312 based on OkHttp 3.12.x branch or downgrade OkHttp dependency in ktor-client-okhttp
Motivation to include to ktor Android 4 share is still significant for apps with large audience. OkHttp 3.12.x is still supported (latest release was about a month ago)
GSON Support turns List<Long> into List<Double> during deserialization
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1981
Ktor Version and Engine Used (client or server and name) Ktor 1.3.2, server, using ktor-gson for content negotiation
Describe the bug
When using Ktor's GSON support for content negotiation, call.receive
calls for generic collection types accidentally return the wrong collection type.
Example: A sample route, expecting a JSON number array as payload. Inside the endpoint implementation, the JSON payload is intended to be deserialized into List<Long>. But unfortunately, the result type is List<Double>:
post("/test") {
val listOfLongs:List<Long> = call.receive<List<Long>>()
// listOfLongs is of type java.util.ArrayList<kotlin.Double> !!!
call.respond(listOfLongs)
}
To Reproduce Steps to reproduce the behavior:
- Add the sample endpoint to your Ktor application
- Use Ktor GSON support by setting
install(ContentNegotiation) {
gson {
}
}
- Perform a POST /test with payload
[1, 2, 3]
- The response payload is
[ 1.0, 2.0, 3.0]
indicating that the deserialized value is of type List<Double> instead of List
Expected behavior
It is expected that the return type of call.receive<List<Long>>()
is List<Long> and not List<Double>
standalone ktor server (eg.: binary linux, or exe windows)
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1976
Describe the solution you'd like standalone ktor server (eg.: binary linux, or exe windows)
CIO, Canceling static file request; Connection reset exception
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1975
Ktor Version and Engine Used (client or server and name)
1.3.2, CIO
server
Describe the bug
CIO
engine throws an exception when a static file HTTP request is canceled.
Exception in thread "DefaultDispatcher-worker-3" java.net.SocketException: Connection reset
at java.base/sun.nio.ch.SocketChannelImpl.throwConnectionReset(SocketChannelImpl.java:345)
at java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:376)
at io.ktor.utils.io.nio.ChannelsKt.read(Channels.kt:135)
at io.ktor.network.sockets.CIOReaderKt$attachForReadingDirectImpl$1$1$1.invokeSuspend(CIOReader.kt:90)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:175)
at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:137)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:108)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:306)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:316)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:248)
at io.ktor.network.selector.SelectorManagerSupport.handleSelectedKey(SelectorManagerSupport.kt:84)
at io.ktor.network.selector.SelectorManagerSupport.handleSelectedKeys(SelectorManagerSupport.kt:64)
at io.ktor.network.selector.ActorSelectorManager.process(ActorSelectorManager.kt:73)
at io.ktor.network.selector.ActorSelectorManager$process$1.invokeSuspend(ActorSelectorManager.kt)
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)
To Reproduce Steps to reproduce the behavior:
- Write the following
fun main() = runBlocking<Unit> {
embeddedServer(CIO, port = 8080, module = Application::module).start(wait = true)
}
fun Application.module() {
routing {
static {
resource("/hello.txt") // Some big file so you have time to cancel the request
}
}
}
-
Run using browser/curl/etc and access
localhost:8080/hello.txt
; Cancel the request before ending. -
See the error logged.
Other things to mention
- This line is logged after the error. So, the server probably sees the request cancellation.
18:28:42.835 [DefaultDispatcher-worker-1] INFO ktor.application - 200 OK: GET - /hello.txt, cancelled
- This won't happen with
Netty
server engine (it does the expected behavior).
Expected behavior
I guess, a simple INFO
log about request cancellation and nothing more.
CIO client silently failing on connection closed
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1948
Ktor Version and Engine Used (client or server and name) 1.3.2, client, CIO
Describe the bug I've encountered issue with CIO client during file download. When server, for whatever reason (in my case due to Jenkins httpKeepAliveTimeout issue: https://issues.jenkins-ci.org/browse/JENKINS-61823) closes connection before sending the whole file, CIO client does not throw any exception and it's very hard to tell what happened.
To Reproduce
client.request<HttpStatement>(url).execute { response ->
if (!response.status.isSuccess()) {
onError(response)
return@execute
}
val channel = response.receive<ByteReadChannel>()
while (!channel.isClosedForRead) {
val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
while (!packet.isEmpty) {
val bytes = packet.readBytes()
destination.appendBytes(bytes)
}
}
if (headResponse.contentLength() ?: 0 > 0 && headResponse.contentLength() != destination.length()) {
throw RuntimeException("[DOWNLOAD FAILURE] '$url'. Expected size of downloaded file '${headResponse.contentLength()}' actual size '${destination.length()}'.")
}
}
Expected behavior
On the contrary Apache client behaves nicely and throws:
org.apache.http.ConnectionClosedException: Premature end of Content-Length delimited message body (expected: 113531; received: 15329
Client: URL encode / escaping is wrong
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1945
Ktor Version and Engine Used (client or server and name) Version: 1.3.2-1.4-M1-2 ktor-client-core ktor-client-json-native ktor-client-serialization-native ktor-client-curl
Describe the bug The URL is wrong escaped.
To Reproduce
class FahrplanApi(private val serverUrl: String = "http://api.deutschebahn.com/freeplan/v1") {
private val client = HttpClient {
install(JsonFeature)
// workaround for version 1.4-M1
// https://github.com/ktorio/ktor/issues/1820#issuecomment-633519483
HttpResponseValidator {
validateResponse { response ->
when (response.status.value) {
in 300..399 -> throw RedirectResponseException(response)
in 400..499 -> throw ClientRequestException(response)
in 500..599 -> throw ServerResponseException(response)
}
}
}
expectSuccess = false
}
suspend fun departureBoardDateTime(id: Long, date: String) =
client.get<Departure>("$serverUrl/departureBoard/$id?date=$date")
}
fun main(): Unit = runBlocking {
val api = FahrplanApi()
api.departureBoardDateTime(8000096, "2020-06-14T20:21:22")
}
after run:
Uncaught Kotlin exception: io.ktor.client.features.ClientRequestException: Client request(https://api.deutschebahn.com/freeplan/v1/departureBoard/8000096?date=2020-06-14T20%253A21%253A22) invalid: 400 Bad Request
Expected behavior
https://api.deutschebahn.com/freeplan/v1/departureBoard/8000096?date=2020-06-14T20%3A21%3A22
%253A
--> %3A
(25 is actually %
)
Can't use ktor-ktor-client-core with webpack
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1940
Webpack can't find dynamic require which is located in the following code https://github.com/ktorio/ktor/blob/916cff4b964573cf5f7cac30eb42c656890902ad/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/compatibility/Utils.kt#L58
Webpack bundled code says Error load 'node-fetch' module
Failed to build ktor locally: java.lang.OutOfMemoryError: GC overhead limit exceeded
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1931
To Reproduce
- Clone this repository from the
master
branch - Run the
./gradlew clean check
command
Describe the bug
> java -version
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
> sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.6
BuildVersion: 19G36e
> uname -a
Darwin XXX 19.6.0 Darwin Kernel Version 19.6.0: Sun May 17 22:15:23 PDT 2020; root:xnu-6153.140.21~10/RELEASE_X86_64 x86_64
AndroidClientEngine set a Content-Lenght: 2 on a GET request with an EmptyContent as body
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1900
Ktor Version and Engine Used (client or server and name) 1.3.2
Describe the bug
- On a
GET
request, the body is anEmptyContent
- The
JsonFeature
with aKotlinxSerializer
serialize thisEmptyContent
as{}
Code Here - The
AndroidClientEngine
calculateContent-Length
based on this{}
and set it to2
Code Here - Then the
GET
request is executed without body but with aContent-Lenght: 2
header
Expected behavior
GET
request executed without body and with a Content-Lenght: 0
header
Maybe, this PR could resolve this issue ?
TestHttpClientEngine KNPE
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1891
Ktor Version and Engine Used (client or server and name) 1.3.2, test client/server, ktor-server-test-host
Describe the bug Take this example.
fun main() {
withTestApplication(Application::helloModule) {
runBlocking {
val res = client.get<HttpResponse>("NonExistenceRoute")
println(res)
}
}
}
fun Application.helloModule() {
routing {
get {
call.respond(HttpStatusCode.OK, "Hello")
}
}
}
Throws KNPE because route doesn't exists. If you follow stack trace, it's obvious where KNPE was thrown.
It's pretty nice to have this kind of tests. Is TestHttpClientEngine
in experimental state? I also had another issue where the tests would stuck using multiparts.
Expected behavior A response with 404 not found status.
Screenshots Stack trace of the thrown exception,
Exception in thread "main" kotlin.KotlinNullPointerException
at io.ktor.server.testing.client.TestHttpClientEngine.execute(TestHttpClientEngine.kt:37)
at io.ktor.client.engine.HttpClientEngine$executeWithinCallContext$2.invokeSuspend(HttpClientEngine.kt:83)
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)
Ktor Client Null Pointer Exception (HTTPS)
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1890
Ktor Version and Engine Used (client or server and name) 1.3.1 (client)
Describe the bug When sending a POST request over HTTPS, the ktor client will intermittently throw a NPE.
To Reproduce Not sure exactly how to reproduce... given that the failure is intermittent. I get this NPE approximately 50% of the time.
Screenshots
kotlin.KotlinNullPointerException
at io.ktor.network.tls.TLSClientHandshake.handleServerDone(TLSClientHandshake.kt:327)
at io.ktor.network.tls.TLSClientHandshake.handleCertificatesAndKeys(TLSClientHandshake.kt:291)
at io.ktor.network.tls.TLSClientHandshake.negotiate(TLSClientHandshake.kt:155)
at io.ktor.network.tls.TLSClientHandshake$negotiate$1.invokeSuspend(TLSClientHandshake.kt)
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:561)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:727)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:667)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:655)
Ktor should resolve trailing slash as a different URL
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1876
From the beginning to Ktor 1.3.2.
Ktor makes no difference between URLs ending with a trailing slash or not.
The following code will produce an error because two handlers are trying to respond to the incoming request.
@Test fun `routing trailing slash`() = withTestApplication {
application.routing {
get("foo") {
println("server processing /foo")
call.respondText("/foo")
}
get("foo/") {
println("server procession /foo/")
call.respondText("/foo/")
}
}
on ("making /foo request") {
val result = handleRequest {
uri = "/foo"
method = HttpMethod.Get
}
it("should be handled") {
assertTrue(result.requestHandled)
}
it("should have /foo as a response") {
assertEquals("/foo", result.response.content)
}
}
}
//produces
server processing /foo
server procession /foo/
Response has already been sent
io.ktor.server.engine.BaseApplicationResponse$ResponseAlreadySentException: Response has already been sent
This error has an impact on SEO because Google respects the difference between /foo
and /foo/
that may produce different results.
It is also a problem when you want to use a first-level path parameter like "/{user}/"
coupled with static content serving. Browsers can send requests to get a favicon file. These requests don't have any trailing slash, but still, Ktor will route them to the "/{user}/"
handler instead of serving the file.
Expected behavior A router "//" should not handle a "/favicon.png" request.
Because it is a modification of behavior, users should activate this correction by configuration.
Ktor 1.3.1 Fails File Upload with MalformedInputException
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1845
Hi. I am using Ktor Client with version 1.3.1 for MPP
I have been using Ktor version 1.2.6 in my MPP project with Kotlin version 1.3.61 and it was working fine, so I was able to upload files without a problem. Later I update Kotlin version to 1.3.72 which forced me to update Ktor version 1.3.1 as version 1.2.6 was compiled with Kotlin version 1.3.61 which IDE can not compile with Kotlin plugin 1.3.72. After that file upload call started to give me the following exception.
2020-05-02 17:35:35.186 14564-14671/com.inivo.client.debug E/AndroidRuntime: FATAL EXCEPTION: ktor-android-dispatcher-worker-2
Process: com.inivo.client.debug, PID: 14564
io.ktor.utils.io.charsets.MalformedInputException: Input length = 1
at io.ktor.utils.io.charsets.CharsetJVMKt.throwExceptionWrapped(CharsetJVM.kt:323)
at io.ktor.utils.io.charsets.CharsetJVMKt.decode(CharsetJVM.kt:199)
at io.ktor.utils.io.charsets.EncodingKt.decode(Encoding.kt:101)
at io.ktor.utils.io.core.StringsKt.readText(Strings.kt:251)
at io.ktor.utils.io.core.StringsKt.readText$default(Strings.kt:250)
at io.ktor.client.features.logging.Logging$logRequestBody$2$1.invokeSuspend(Logging.kt:111)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:184)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:108)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:307)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:317)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:249)
at io.ktor.utils.io.ByteBufferChannel.resumeWriteOp(ByteBufferChannel.kt:2210)
at io.ktor.utils.io.ByteBufferChannel.bytesRead(ByteBufferChannel.kt:930)
at io.ktor.utils.io.ByteBufferChannel.readAsMuchAsPossible(ByteBufferChannel.kt:544)
at io.ktor.utils.io.ByteBufferChannel.readAvailable$suspendImpl(ByteBufferChannel.kt:606)
at io.ktor.utils.io.ByteBufferChannel.readAvailable(Unknown Source:0)
at io.ktor.utils.io.jvm.javaio.WritingKt.copyTo(Writing.kt:21)
at io.ktor.utils.io.jvm.javaio.WritingKt.copyTo$default(Writing.kt:12)
at io.ktor.client.engine.android.AndroidClientEngineKt.writeTo(AndroidClientEngine.kt:112)
at io.ktor.client.engine.android.AndroidClientEngine.execute(AndroidClientEngine.kt:79)
at io.ktor.client.engine.HttpClientEngine$executeWithinCallContext$2.invokeSuspend(HttpClientEngine.kt:83)
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)
I am uploading a file like this.
override suspend fun sendImageAsync(
token: String,
file: File,
fileType: String,
id: Long
): Unit = client.request {
fullUrl("${RestApi.SEND_IMAGE}/${id}")
method = HttpMethod.Post
body = MultiPartFormDataContent(
formData {
append(
"image",
file.fileData.bytes,
headers = Headers.build {
append(HttpHeaders.ContentType, "image/jpg")
append(
HttpHeaders.ContentDisposition,
"filename=${file.name}"
)
append("Connection", "Close")
}
)
}
)
header(HttpHeaders.Connection, "close")
header(HttpHeaders.Authorization, makeAuthToken(token))
}
Here file data is just an object which keeps File data as a byte array so by that I can convert image to byte array (either from Android / IOS) and upload it.
Thank you very much for your help in advanced.
URLBuilder to append paths to currently encoded path
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1826
Subsystem
URLBuilder
Is your feature request related to a problem? Please describe.
I want to create a final URL from several paths. Let's say my starting url is https://example.com/items/
and incoming paths are: /category/
, /directory/
, /product
.
Doing:
val url = URLBuilder("https://example.com/items/").run {
path("/category/", "/directory/", "/product")
buildString()
}
I will end up with: https://example.com//category///directory///product
.
Notice that /items/
from starting URL is gone and the unnecessary slash duplications.
To preserve the original path, one can use:
path(encodedPath, "/category/", "/directory/", "/product")
but that does not fix the path separator problem (in case where you don't control them).
Describe the solution you'd like
Adding a simple function, appendToPath
, which will handle path extending and separators.
Exemplary implementation:
fun URLBuilder.appendToPath(vararg components: String) {
// removes all trailing and leading separators
val paths = components
.map { it.replace(Regex("""(^/|/+$)"""), "") }
.joinToString("/", transform = { it.encodeURLQueryComponent() })
// make sure that there's a slash separator at the end of current path
if (!encodedPath.endsWith('/')) {
encodedPath = "${encodedPath}/"
}
this.encodedPath += paths
}
Motivation to include to ktor
I think this might be a common case (building up URL), so in my opinion, this small helper would be a beneficial addition. I'm more than happy to open up PR.
Windows Curl invalid mutability exception
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1820
Ktor Version and Engine Used (client or server and name) OS: Windows 10 (64 bit) Client version: CURL (msys64) ktor version: 1.3.2 Kotlin version: 1.3.72
Describe the bug Sending a request that is not successful (status code != 200) throws InvalidMutabilityException
To Reproduce
val client = HttpClient(Curl)
client.get<String>("https://any-url-that-returns-status-code-not-200.com")
Expected behavior Code throws ClientRequestException, like it does on JVM
Screenshots
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.collections.HashMap@1240b58
at kfun:kotlin.Throwable.<init>(kotlin.String?;kotlin.Throwable?)kotlin.Throwable (000000000045e120)
at kfun:kotlin.Throwable.<init>(kotlin.String?)kotlin.Throwable (000000000045e410)
at kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception (0000000000457800)
at kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException (0000000000457340)
at kfun:kotlin.native.concurrent.InvalidMutabilityException.<init>(kotlin.String)kotlin.native.concurrent.InvalidMutabilityException (00000000004848e0)
at ThrowInvalidMutabilityException (0000000000485d50)
at MutationCheck (00000000008b3800)
at kfun:kotlin.collections.HashMap.<set-length>#internal (0000000000468fc0)
at kfun:kotlin.collections.HashMap.addKey$stdlib(K)kotlin.Int (000000000046cea0)
at kfun:kotlin.collections.HashMap.put(K;V)V? (000000000046a000)
at kfun:io.ktor.util.AttributesNative.put#internal (0000000000660e50)
at kfun:io.ktor.client.features.$addDefaultResponseValidation$lambda-1$lambda-0COROUTINE$174.invokeSuspend#internal (00000000006b6db0)
at kfun:io.ktor.client.features.$addDefaultResponseValidation$lambda-1$lambda-0COROUTINE$174.invoke#internal (00000000006b7980)
at kfun:io.ktor.client.features.HttpCallValidator.$validateResponseCOROUTINE$180.invokeSuspend#internal (00000000006bfb00)
at kfun:io.ktor.client.features.HttpCallValidator.validateResponse#internal (00000000006c0210)
at kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$179.invokeSuspend#internal (00000000006c2800)
at kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$179.invoke#internal (00000000006c3430)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.proceed#internal (000000000065cf50)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.execute#internal (000000000065d2a0)
at kfun:io.ktor.util.pipeline.Pipeline.execute(TContext;TSubject)TSubject (00000000006583d0)
at kfun:io.ktor.client.call.HttpClientCall.$receiveCOROUTINE$169.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? (00000000006aa240)
at kfun:io.ktor.client.call.HttpClientCall.receive(io.ktor.client.call.TypeInfo)kotlin.Any (00000000006aaf50)
at kfun:sample.$main$lambda-0COROUTINE$0.invokeSuspend#internal (000000000084a030)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
at kfun:kotlinx.coroutines.DispatchedTask.run() (000000000057ee30)
at kfun:kotlinx.coroutines.EventLoopImplBase.processNextEvent()kotlin.Long (000000000053d3f0)
at kfun:kotlinx.coroutines.BlockingCoroutine.joinBlocking#internal (000000000058cd40)
at kfun:kotlinx.coroutines.runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,T>){0<kotlin.Any?>}Generic (000000000058c130)
at kfun:kotlinx.coroutines.runBlocking$default(kotlin.coroutines.CoroutineContext?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,T>;kotlin.Int){0<kotlin.Any?>}Generic (000000000058c9f0)
at kfun:sample.main() (0000000000849e60)
at Konan_start (000000000084cc10)
at Init_and_run_start (00000000008920c0)
at __tmainCRTStartup (0000000000401180)
at mainCRTStartup (00000000004014f0)
at (00007fffccf67bc0)
at (00007fffcd22ce30)
MalformedInputException: Input length = 1, when uploading an image multipart
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1766
Ktor Version and Engine Used (client or server and name) Ktor 1.3.2, Kotlin 1.3.70, Netty server, ContentNegotiation, Jackson parser, Exposed.
I simply used the Ktor project generator for Gradle, and checked a few boxes for modules as mentioned above. After which I managed to connect a database & a few endpoints, but my core endpoint for uploading images doesn't work.
Describe the bug When trying to upload a simple image using Retrofit & Multipart support in Android, I get a MalformedInputException on my server.
Retrofit/Android code:
@Multipart
@POST("/api/upload")
suspend fun uploadImages(@Part("image") imageFile: RequestBody): UploadResponse
Upload code:
val requestBody: RequestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "Image Upload")
.addFormDataPart(
"image", // part name
file.path.split("/").last(), // file name
file.asRequestBody("image/jpg".toMediaType()) // Content-Type: image/jpg
)
.build()
return apiService.uploadImages(requestBody)
Using the new way of creating RequestBody objects in OkHttp. I followed the documentation to receive the multipart data, and I've formed a MalformedInputException.
To Reproduce Steps to reproduce the behavior:
- Use Retrofit to Upload an image to a Ktor server.
- call
receiveMultipart()
to receive MP on the Ktor side. - See error
Expected behavior Multipart data should be received correctly.
Stack trace:
[nioEventLoopGroup-4-1] ERROR Application - Unhandled: POST - /api/upload
io.ktor.utils.io.charsets.MalformedInputException: Input length = 1
at io.ktor.utils.io.charsets.CharsetJVMKt.throwExceptionWrapped(CharsetJVM.kt:323)
at io.ktor.utils.io.charsets.CharsetJVMKt.decodeImplSlow(CharsetJVM.kt:289)
at io.ktor.utils.io.charsets.CharsetJVMKt.decodeExactBytes(CharsetJVM.kt:254)
at io.ktor.utils.io.core.StringsKt.readTextExactBytes(Strings.kt:293)
at io.ktor.utils.io.core.StringsKt.readTextExactBytes$default(Strings.kt:292)
at io.ktor.utils.io.core.AbstractInput.readText(AbstractInput.kt:468)
at io.ktor.utils.io.core.AbstractInput.readText$default(AbstractInput.kt:465)
at io.ktor.http.cio.CIOMultipartDataBase.partToData(CIOMultipartData.kt:127)
at io.ktor.http.cio.CIOMultipartDataBase$partToData$1.invokeSuspend(CIOMultipartData.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:844)
InvalidMutabilityException on macOS when API call fails
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1759
Ktor 1.3.2 Kotlin 1.3.71 Coroutines 1.3.5 Engine: Curl
Running native on macOS. When doing an API call that return an error, like 500 (internal server errror), ktor throws the following exception:
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.collections.HashMap@d1d12608
at 0 pos-ecr.kexe 0x0000000109c43237 kfun:kotlin.Throwable.<init>(kotlin.String?)kotlin.Throwable + 87 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/Throwable.kt:22:37)
at 1 pos-ecr.kexe 0x0000000109c3c4f5 kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception + 85 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
at 2 pos-ecr.kexe 0x0000000109c3c695 kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException + 85 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
at 3 pos-ecr.kexe 0x0000000109c71255 kfun:kotlin.native.concurrent.InvalidMutabilityException.<init>(kotlin.String)kotlin.native.concurrent.InvalidMutabilityException + 85 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22:60)
at 4 pos-ecr.kexe 0x0000000109c72ad8 ThrowInvalidMutabilityException + 680 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:90:11)
at 5 pos-ecr.kexe 0x0000000109d63ac8 MutationCheck + 104
at 6 pos-ecr.kexe 0x0000000109c52a1d kfun:kotlin.collections.HashMap.<set-length>#internal + 77 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:16:17)
at 7 pos-ecr.kexe 0x0000000109c5756d kfun:kotlin.collections.HashMap.addKey$stdlib(K)kotlin.Int + 1181 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:279:36)
at 8 pos-ecr.kexe 0x0000000109c53b5d kfun:kotlin.collections.HashMap.put(K;V)V? + 317 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:63:21)
at 9 pos-ecr.kexe 0x0000000109eacebc kfun:io.ktor.util.AttributesNative.put#internal + 396 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/posix/src/io/ktor/util/AttributesNative.kt:21:0)
at 10 pos-ecr.kexe 0x0000000109f16c7f kfun:io.ktor.client.features.$addDefaultResponseValidation$lambda-1$lambda-0COROUTINE$13.invokeSuspend#internal + 1647 (/Users/teamcity1/teamcity_work/4d622a065c544371/backend.native/build/stdlib/kotlin/util/Standard.kt:83:0)
at 11 pos-ecr.kexe 0x0000000109f17380 kfun:io.ktor.client.features.$addDefaultResponseValidation$lambda-1$lambda-0COROUTINE$13.invoke#internal + 256 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/features/DefaultResponseValidation.kt:23:26)
at 12 pos-ecr.kexe 0x0000000109f1f76d kfun:io.ktor.client.features.HttpCallValidator.$validateResponseCOROUTINE$19.invokeSuspend#internal + 1341 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/features/HttpCallValidator.kt:37:0)
at 13 pos-ecr.kexe 0x0000000109f1faa4 kfun:io.ktor.client.features.HttpCallValidator.validateResponse#internal + 308 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/features/HttpCallValidator.kt:36:21)
at 14 pos-ecr.kexe 0x0000000109f22793 kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$18.invokeSuspend#internal + 1139 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/features/HttpCallValidator.kt:99:29)
at 15 pos-ecr.kexe 0x0000000109f23148 kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$18.invoke#internal + 312 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/features/HttpCallValidator.kt:97:61)
at 16 pos-ecr.kexe 0x0000000109ea99e2 kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1122 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:207:0)
at 17 pos-ecr.kexe 0x0000000109ea903b kfun:io.ktor.util.pipeline.SuspendFunctionGun.proceed#internal + 395 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:18:0)
at 18 pos-ecr.kexe 0x0000000109ea949a kfun:io.ktor.util.pipeline.SuspendFunctionGun.execute#internal + 474 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:183:16)
at 19 pos-ecr.kexe 0x0000000109ea35c9 kfun:io.ktor.util.pipeline.Pipeline.execute(TContext;TSubject)TSubject + 361 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/Pipeline.kt:27:41)
at 20 pos-ecr.kexe 0x0000000109f09a6a kfun:io.ktor.client.call.HttpClientCall.$receiveCOROUTINE$5.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 2042 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/call/HttpClientCall.kt:75:50)
at 21 pos-ecr.kexe 0x0000000109f0a0f4 kfun:io.ktor.client.call.HttpClientCall.receive(io.ktor.client.call.TypeInfo)kotlin.Any + 308 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/call/HttpClientCall.kt:67:13)
at 22 pos-ecr.kexe 0x0000000109bdcff0 kfun:com.mobilepay.pos.api.V10PointOfSalesRepository.$createPointOfSaleCOROUTINE$5.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 9296 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/call/HttpClientCall.kt:148:65)
at 23 pos-ecr.kexe 0x0000000109c637fc kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) + 700 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:26:0)
at 24 pos-ecr.kexe 0x0000000109ea9ff6 kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal + 918 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:238:18)
at 25 pos-ecr.kexe 0x0000000109ea9bcf kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1615 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:194:21)
at 26 pos-ecr.kexe 0x0000000109eabd21 kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal + 353 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:144:13)
at 27 pos-ecr.kexe 0x0000000109c63add kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) + 1437 (/Users/teamcity1/teamcity_work/4d622a065c544371/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:26:0)
at 28 pos-ecr.kexe 0x0000000109ea9ff6 kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal + 918 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:238:18)
at 29 pos-ecr.kexe 0x0000000109ea9bcf kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1615 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:194:21)
ktor client does not log request body
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1729
Ktor Version and Engine Used (client or server and name) ver: 1.3.1, 1.3.2 engine: Apache app: client dependencies:
dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation(platform("io.ktor:ktor-bom:1.3.2"))
implementation("io.ktor:ktor-client-core")
implementation("io.ktor:ktor-client-logging-jvm")
implementation("io.ktor:ktor-client-apache")
implementation("io.ktor:ktor-client-json-jvm")
implementation("org.slf4j:slf4j-simple:1.7.26")
implementation("org.apache.httpcomponents:httpasyncclient:4.1.4")
}
Describe the bug Request body is not logged even with LogLevel.ALL
To Reproduce
package com.example
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.client.features.logging.DEFAULT
import io.ktor.client.features.logging.LogLevel
import io.ktor.client.features.logging.Logger
import io.ktor.client.features.logging.Logging
import io.ktor.client.request.post
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.runBlocking
@KtorExperimentalAPI
fun main() {
runBlocking {
val client = HttpClient(Apache) {
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}
client.post<String>("http://localhost:8080") {
body = """{ "test": "testVal" }"""
}
}
}
Expected behavior Request body is in the log
Actual result
[main] INFO io.ktor.client.HttpClient - REQUEST: http://localhost:8080/
[main] INFO io.ktor.client.HttpClient - METHOD: HttpMethod(value=POST)
[main] INFO io.ktor.client.HttpClient - COMMON HEADERS
[main] INFO io.ktor.client.HttpClient - -> Accept-Charset: UTF-8
[main] INFO io.ktor.client.HttpClient - -> Accept: */*
[main] INFO io.ktor.client.HttpClient - CONTENT HEADERS
[main] INFO io.ktor.client.HttpClient - BODY Content-Type: text/plain; charset=UTF-8
[main] INFO io.ktor.client.HttpClient - RESPONSE: 200
--- remaining response stuff skipped for brevity ---
Ktor CIO client, connect timeout handling
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1720
- Using the following code (full example at https://github.com/rrva/ktor-client-connection-leak)
- Run the main method repeatedly. Sometimes, all futures created will not complete . Why? Expected behavior is that all created futures will complete eventually, so
Done with all
will be printed.
class Client(val fooServiceUrl: String) {
val client = HttpClient(CIO) {
engine {
requestTimeout = 500
endpoint.connectTimeout = 100
endpoint.connectRetryAttempts = 2
}
install(JsonFeature) {
serializer = KotlinxSerializer(
Json(
JsonConfiguration(
isLenient = true,
ignoreUnknownKeys = true,
serializeSpecialFloatingPointValues = true,
useArrayPolymorphism = true
)
)
)
}
}
suspend fun fetchFoo(ids: List<String>): List<Foo> {
return client.request {
header("User-Agent", "Foo")
parameter("input", ids.joinToString(","))
url("$fooServiceUrl/foo")
}
}
}
@Serializable
data class Foo(val id: String)
val client = Client("http://localhost:9090")
fun fetchFooAsync(ids:List<String>): CompletableFuture<List<Foo>> {
return CoroutineScope(Job() + Dispatchers.IO + MDCContext()).async {
try {
client.fetchFoo(ids)
} catch (e: Throwable) {
println(e.message)
listOf<Foo>(Foo("timeout"))
}
}.asCompletableFuture()
}
fun main() {
HttpServer.create(InetSocketAddress(9090), 1000).apply {
createContext("/foo") { http ->
http.responseHeaders.add("Content-type", "application/json")
http.sendResponseHeaders(200, 0)
PrintWriter(http.responseBody).use { out ->
out.println("""[{"id":"1"}]""")
}
}
start()
}
println("Start")
val requests = (1..100).map {
fetchFooAsync(listOf(it.toString()))
}
for (request in requests) {
println("Waiting for coroutine...")
println(request.get())
println("Done waiting")
}
println("Done with all")
}
Reproduced on 1.3.1
Content-Disposition additional parameters should be inside quotes
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1691
Ktor Version and Engine Used (client or server and name) 1.3.1, client
Describe the bug
Content-Disposition
additional parameters should be inside quotes in multi-part Request body.
To Reproduce Steps to reproduce the behavior:
- Multi-part request
import io.ktor.client.request.forms.append
<...>
httpClient.post<String> {
body = MultiPartFormDataContent(formData {
append(
"file",
"file.txt",
ContentType.parse("text/plain")
) {
writeText("content")
}
})
}
<...>
- Observe part data in Request body:
Content-Disposition: form-data; name=file; filename=file.txt
Expected behavior Header matching spec:
The first parameter in the HTTP context is always form-data. Additional parameters are case-insensitive and have arguments that use quoted-string syntax after the '=' sign. Multiple parameters are separated by a semi-colon (';').
Content-Disposition: form-data; name="file"; filename="file.txt"
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
Empty body in response using macosx64 target
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1622
Ktor Version and Engine Used (client or server and name) 1.3.0, client (macosx64) (using curl)
Describe the bug When using macosx64 target in a Multiplatform project I'm getting empty BODY in response
HttpClient: REQUEST: http://api.open-notify.org/astros.json
HttpClient: METHOD: HttpMethod(value=GET)
HttpClient: COMMON HEADERS
HttpClient: -> Accept: application/json
HttpClient: -> Accept-Charset: UTF-8
HttpClient: CONTENT HEADERS
HttpClient: BODY Content-Type: null
HttpClient: BODY START
HttpClient: BODY END
The same code for iOS logs
HttpClient: REQUEST: http://api.open-notify.org/astros.json
HttpClient: METHOD: HttpMethod(value=GET)
HttpClient: COMMON HEADERS
HttpClient: -> Accept: application/json
HttpClient: -> Accept-Charset: UTF-8
HttpClient: CONTENT HEADERS
HttpClient: BODY Content-Type: null
HttpClient: BODY START
HttpClient: BODY END
HttpClient: BODY Content-Type: application/json
HttpClient: BODY START
HttpClient: {"people": [{"craft": "ISS", "name": "Andrew Morgan"}, {"craft": "ISS", "name": "Oleg Skripochka"}, {"craft": "ISS", "name": "Jessica Meir"}], "message": "success", "number": 3}
HttpClient: BODY END
The following is the common code that uses ktor
class PeopleInSpaceApi {
private val url = "http://api.open-notify.org/astros.json"
private val client by lazy {
HttpClient() {
install(JsonFeature) {
serializer = KotlinxSerializer(Json(JsonConfiguration(strictMode = false)))
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}
}
suspend fun fetchPeople(): AstroResult {
return client.get(url)
}
}
I'm using following dependencies
macOSMain.dependencies {
// Coroutines
implementation('org.jetbrains.kotlinx:kotlinx-coroutines-core-macosx64') {
version {
strictly '1.3.3-native-mt'
}
}
// Ktor
implementation "io.ktor:ktor-client-curl:${Versions.ktor}"
implementation "io.ktor:ktor-client-core-macosx64:${Versions.ktor}"
implementation "io.ktor:ktor-client-json-macosx64:${Versions.ktor}"
implementation "io.ktor:ktor-client-logging-macosx64:${Versions.ktor}"
implementation "io.ktor:ktor-client-serialization-macosx64:${Versions.ktor}"
// Serialize
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-macosx64:${Versions.kotlinxSerialization}"
}
(kotlinxSerialization = "0.14.0")
kotlinMultiplatform publications miss the gradle metadata
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1619
Checked on releases 1.3.0 and 1.3.1.
Example : https://bintray.com/version/files/kotlin/ktor/ktor/1.3.0?order=asc&sort=name&basePath=io%2Fktor%2Fktor-auth-jwt-kotlinMultiplatform%2F1.3.0&tab=files
This makes these publications plain useless.
HttpClient can only be used on the main thread for native targets
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1559
Ktor Version and Engine Used (client or server and name) Ktor client 1.2.6, default platform engine (iOS)
Describe the bug HttpClient can only be used from the main thread. It does not matter on which thread the object is instantiated.
The issue is caused by global declarations that are not annotated properly for Kotlin/Native.
Uncaught Kotlin exception: io.ktor.http.URLParserException: Fail to parse url:
I am using the CoroutineWorker library from Autodesk.
To Reproduce Steps to reproduce the behavior:
- Make a client request from a non-main thread for a native target.
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.StandaloneCoroutine@28a82d8
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1550
works on android, breaks on ios
code:
fun triggerNetworkCall(){
GlobalScope.apply {
launch(Background) {
try {
val data = repository.getSettings()
} catch (t: Throwable) {
Logger.d("ktor", t.message?:"null")
}
}
}
}
suspend fun getSettings(): String? {
val response = apiConfig.getReferralData()
return response
}
suspend fun getReferralData(): String? {
val localClient = HttpClient(PlatformHttpClient.httpClientEngine){
install(JsonFeature)
}
return localClient.post {
url {
protocol = URLProtocol.HTTPS
host = "postman-echo.com"
encodedPath = "post"
}
contentType(ContentType.Application.Json)
body = "{}"
HttpMethod.Post
}
}
This post request works in android but fails in iOS with following stack trace:
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.StandaloneCoroutine@1a8b4e8
whereas a get request works on both platforms. Following is the function being used:
suspend fun getReferralData(): String? {
val localClient = HttpClient(PlatformHttpClient.httpClientEngine){
install(JsonFeature)
}
val address = Url("https://postman-echo.com/get?foo1=bar1&foo2=bar2")
return localClient.get {
url {
url(address.toString())
}
}
}
Dispatchers definition: iOS
internal actual val Main: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())
internal actual val Background: CoroutineDispatcher = Main
internal class NsQueueDispatcher(
private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatch_async(dispatchQueue) {
block.run()
}
}
}
android
internal actual val Main: CoroutineDispatcher = Dispatchers.Main
internal actual val Background: CoroutineDispatcher = Dispatchers.Default
dependencies
commonMain {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.14.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.2-1.3.60"
implementation "co.touchlab:stately:0.9.4"
implementation "co.touchlab:stately-collections:0.9.4"
implementation "io.ktor:ktor-client-core:1.2.6"
implementation "io.ktor:ktor-client-json:1.2.6"
implementation "io.ktor:ktor-client-logging:1.2.6"
implementation "io.ktor:ktor-client-serialization:1.2.6"
implementation "com.github.aakira:napier:1.1.0"
}
}
androidMain {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.61"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2-1.3.60"
implementation "io.ktor:ktor-client-core-jvm:1.2.6"
implementation "io.ktor:ktor-client-json-jvm:1.2.6"
implementation "io.ktor:ktor-client-logging-jvm:1.2.6"
implementation "io.ktor:ktor-client-serialization-jvm:1.2.6"
implementation "io.ktor:ktor-client-android:1.2.6"
implementation "com.github.aakira:napier-android:1.1.0"
}
}
iosMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.14.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.3.2-1.3.60"
implementation "io.ktor:ktor-client-core-native:1.2.6"
implementation "io.ktor:ktor-client-json-native:1.2.6"
implementation "io.ktor:ktor-client-logging-native:1.2.6"
implementation "io.ktor:ktor-client-serialization-native:1.2.6"
implementation "io.ktor:ktor-client-ios:1.2.6"
implementation "com.github.aakira:napier-ios:1.1.0"
}
}
kotlin.native.concurrent.InvalidMutabilityException with 1.3.3-native-mt
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1543
KTOR native with the new multi threaded coroutines fails with InvalidMutabilityException. coroutines version: 1.3.3-native-mt ktor version: 1.3.0-rc2 Here is the related code:
suspend fun doFetch() {
HttpClient().use {
val htmlContent = it.get<String>("https://en.wikipedia.org/wiki/Main_Page")
println(htmlContent)
}
}
fun doesNotWork() {
runBlocking(Dispatchers.Default) { // remove Dispatchers.Default and it works
doFetch()
}
}
fun main() {
doesNotWork()
}
to reproduce, checkout the following project: https://github.com/yigit/ktor-coroutines-bug
run ./gradlew runDELX --stacktrace
from command line.
It fails with :
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen io.ktor.utils.io.ByteChannelNative@c006ba18
at kfun:kotlin.Throwable.<init>(kotlin.String?)kotlin.Throwable (0x2d9607)
at kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception (0x2d3115)
at kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException (0x2d2cd5)
at kfun:kotlin.native.concurrent.InvalidMutabilityException.<init>(kotlin.String)kotlin.native.concurrent.InvalidMutabilityException (0x2fff65)
at ThrowInvalidMutabilityException (0x301673)
at MutationCheck (0x5a742e)
at kfun:io.ktor.utils.io.ByteChannelSequentialBase.<set-waitingForRead>#internal (0x403fed)
at kfun:io.ktor.utils.io.ByteChannelSequentialBase.$awaitSuspendCOROUTINE$73.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? (0x417d9f)
at kfun:io.ktor.utils.io.ByteChannelSequentialBase.awaitSuspend(kotlin.Int)ValueType (0x418244)
at kfun:io.ktor.utils.io.ByteChannelSequentialBase.$readRemainingSuspendCOROUTINE$60.invokeSuspend#internal (0x40f22c)
at kfun:io.ktor.utils.io.ByteChannelSequentialBase.readRemainingSuspend#internal (0x40f5c0)
at kfun:io.ktor.utils.io.ByteChannelSequentialBase.$readRemainingCOROUTINE$59.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? (0x40e936)
at kfun:io.ktor.utils.io.ByteChannelSequentialBase.readRemaining(kotlin.Long;kotlin.Int)io.ktor.utils.io.core.ByteReadPacket (0x40ec6e)
at kfun:io.ktor.utils.io.readRemaining@io.ktor.utils.io.ByteReadChannel.()io.ktor.utils.io.core.ByteReadPacket (0x421354)
at kfun:io.ktor.client.features.HttpPlainText.Feature.$install$lambda-1COROUTINE$26.invokeSuspend#internal (0x53c9c4)
at kfun:io.ktor.client.features.HttpPlainText.Feature.$install$lambda-1COROUTINE$26.invoke#internal (0x53d3ef)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (0x4d3e3d)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.proceed#internal (0x4d34be)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.proceedWith#internal (0x4d3697)
at kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$22.invokeSuspend#internal (0x538612)
at kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$22.invoke#internal (0x538baf)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (0x4d3e3d)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.proceed#internal (0x4d34be)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.execute#internal (0x4d38c1)
at kfun:io.ktor.util.pipeline.Pipeline.execute(#GENERIC_kotlin.Any;#GENERIC_kotlin.Any)#GENERIC_kotlin.Any (0x4ce690)
at kfun:io.ktor.client.call.HttpClientCall.$receiveCOROUTINE$12.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? (0x5205d3)
at kfun:io.ktor.client.call.HttpClientCall.receive(io.ktor.client.call.TypeInfo)kotlin.Any (0x520c06)
at kfun:$doFetchCOROUTINE$0.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? (0x565423)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0x2f4968)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (0x4d4508)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (0x4d4072)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (0x4d5d9c)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0x2f4c49)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (0x4d4508)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (0x4d4072)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (0x4d5d9c)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0x2f4c49)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (0x4d4508)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (0x4d4072)
at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (0x4d5d9c)
at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0x2f4c49)
at kfun:kotlinx.coroutines.DispatchedTask.run() (0x3e2fca)
at kfun:kotlinx.coroutines.EventLoopImplBase.processNextEvent()ValueType (0x3bb507)
at kfun:kotlinx.coroutines.runEventLoop$kotlinx-coroutines-core(kotlinx.coroutines.EventLoop?;kotlin.Function0<kotlin.Boolean>) (0x3f665c)
at kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.start$lambda-0#internal (0x3fe271)
at kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$80.invoke#internal (0x3fe3db)
at kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$80.$<bridge-UNN>invoke()#internal (0x3fe43b)
at WorkerLaunchpad (0x30129c)
at _ZN6Worker19processQueueElementEb (0x5ac1c9)
at _ZN12_GLOBAL__N_113workerRoutineEPv (0x5aca2d)
at (0x7f54c8681c73)
at clone (0x7f54c83b3def)
at ((nil))
Error Ktor running on background thread on iOS
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1538
Ktor Version and Engine Used (client or server and name) Ktor client version 1.3.0-rc2 build.gradle:
ext.ktor_version = '1.3.0-rc2'
ext.kotlin_coroutines_version = '1.3.3-native-mt'
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
implementation ("io.ktor:ktor-client-core:$ktor_version") {
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-core-common"
}
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutines_version"
}
}
iosMain {
dependencies {
implementation ("io.ktor:ktor-client-ios:$ktor_version") {
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-core-native"
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-core-common"
}
implementation ("io.ktor:ktor-client-core-native:$ktor_version") {
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-core-native"
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-core-common"
}
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$kotlin_coroutines_version"
}
}
}
Describe the bug Executing an API request on the background thread results in kotlin.native.concurrent.InvalidMutabilityException Full stacktrace
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen io.ktor.client.request.HttpRequestPipeline@b5b748
at 0 app 0x0000000105688a87 kfun:kotlin.Throwable.<init>(kotlin.String?)kotlin.Throwable + 87
at 1 app 0x0000000105682215 kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception + 85
at 2 app 0x0000000105681dd5 kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException + 85
at 3 app 0x00000001056b6005 kfun:kotlin.native.concurrent.InvalidMutabilityException.<init>(kotlin.String)kotlin.native.concurrent.InvalidMutabilityException + 85
at 4 app 0x00000001056b77d8 ThrowInvalidMutabilityException + 680
at 5 app 0x00000001059a8c58 MutationCheck + 104
at 6 app 0x00000001058c9400 kfun:io.ktor.util.pipeline.Pipeline.<set-interceptors>#internal + 96
at 7 app 0x00000001058c98f5 kfun:io.ktor.util.pipeline.Pipeline.notSharedInterceptorsList#internal + 85
at 8 app 0x00000001058c8a8a kfun:io.ktor.util.pipeline.Pipeline.cacheInterceptors#internal + 650
at 9 app 0x00000001058c97a4 kfun:io.ktor.util.pipeline.Pipeline.sharedInterceptorsList#internal + 340
at 10 app 0x00000001058c5f57 kfun:io.ktor.util.pipeline.Pipeline.createContext$ktor-utils(#GENERIC_kotlin.Any;#GENERIC_kotlin.Any)io.ktor.util.pipeline.PipelineExecutor<#GENERIC_kotlin.Any> + 263
at 11 app 0x00000001058c5d71 kfun:io.ktor.util.pipeline.Pipeline.execute(#GENERIC_kotlin.Any;#GENERIC_kotlin.Any)#GENERIC_kotlin.Any + 273
at 12 app 0x000000010590797a kfun:io.ktor.client.HttpClient.$executeCOROUTINE$6.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 666
at 13 app 0x0000000105907cc4 kfun:io.ktor.client.HttpClient.execute(io.ktor.client.request.HttpRequestBuilder)io.ktor.client.call.HttpClientCall + 308
at 14 app 0x000000010593bb7c kfun:io.ktor.client.statement.HttpStatement.$executeUnsafeCOROUTINE$35.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 956
at 15 app 0x000000010593be8b kfun:io.ktor.client.statement.HttpStatement.executeUnsafe$ktor-client-core()io.ktor.client.statement.HttpResponse + 235
at 16 app 0x000000010594b557 kfun:sample.ClientApi.$getQuote$lambda-0COROUTINE$0.invokeSuspend#internal + 6775
at 17 app 0x00000001056a9ec8 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) + 712
at 18 app 0x00000001057ca07c kfun:kotlinx.coroutines.DispatchedTask.run() + 2780
at 19 app 0x000000010577fa18 kfun:kotlinx.coroutines.EventLoopImplBase.processNextEvent()ValueType + 792
at 20 app 0x00000001057e3e31 kfun:kotlinx.coroutines.runEventLoop$kotlinx-coroutines-core(kotlinx.coroutines.EventLoop?;kotlin.Function0<kotlin.Boolean>) + 881
at 21 app 0x00000001057eb412 kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.start$lambda-0#internal + 402
at 22 app 0x00000001057eb5fb kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$71.invoke#internal + 59
at 23 app 0x00000001057eb65b kfun:kotlinx.coroutines.WorkerCoroutineDispatcherImpl.$start$lambda-0$FUNCTION_REFERENCE$71.$<bridge-UNN>invoke()#internal + 59
at 24 app 0x00000001056b73a1 WorkerLaunchpad + 177
at 25 app 0x00000001059b1909 _ZN6Worker19processQueueElementEb + 2569
at 26 app 0x00000001059b1eb6 _ZN12_GLOBAL__N_113workerRoutineEPv + 54
at 27 libsystem_pthread.dylib 0x00007fff51bfe2eb _pthread_body + 126
at 28 libsystem_pthread.dylib 0x00007fff51c01249 _pthread_start + 66
at 29 libsystem_pthread.dylib 0x00007fff51bfd40d thread_start + 13
To Reproduce Steps to reproduce the behavior:
- Clone sample app from https://github.com/aleksey-chugaev/ktor-mt-app
- Run iosApp in emulator
- See error
Expected behavior Should successfully execute the request.
Url(…) doesn't parse non-HTTP-like URL strings correctly
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1204
Url
is supposed to not just support HTTP-like URLs but any kind of URLs.
However the string parser seems to properly support only HTTP-like URLs:
Example
Url("mailto:marc@knaup.io").toString()
Url("file:///var/www").toString()
Expected
"mailto:marc@knaup.io"
"file:///var/www"
Actual
"mailto://localhost/marc@knaup.io"
"file://var/www"
Ktor 1.2.2
Code autoreload not working for me
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/975
Ktor Version
1.1.3
Ktor Engine Used(client or server and name)
EngineMain
I guess?
JVM Version, Operating System and Relevant Context
JDK 1.8. Windows 10
Feedback
Okay I have a new project generated using the Ktor IDEA plugin. I'm just trying to make autoreload work (which should by default?). I have only one Kotlin file in src/Application.kt
. My application.conf
looks like this:
ktor {
deployment {
port = 8080
port = ${?PORT}
autoreload = true
watch = [ MyProjectFolder ]
}
application {
modules = [ com.company.ApplicationKt.module ]
}
}
The issue is, when I change Application.kt
the project doesn't reload and the changes are not reflected in the browser.
I see this in the run output:
2019-02-25 11:01:46.894 [main] DEBUG Application - Watching F:\Projects\MyProjectFolder\out\production\classes for changes.
2019-02-25 11:01:46.895 [main] DEBUG Application - Watching F:\Projects\MyProjectFolder\out\production\classes\com for changes.
...
I also tried com.company
instead of MyProjectFolder
in the watch list but that one is even worse. The run output shows autoreload is disabled.
MPP JS client: Can't resolve 'text-encoding'
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/961
Ktor Version
1.1.2
Ktor Engine Used(client or server and name)
Ktor client with default engine
JVM Version, Operating System and Relevant Context
JDK 1.8, Windows, Kotlin JS
Feedback
We are using the ktor client in a multiplatform project (android, iOS and web). Starting with version 1.1.0 the build of the JS client fails with the following message:
ERROR in ./kotlinx-io.js
Module not found: Error: Can't resolve 'text-encoding' in '<mypath>\kotlinmultiplatform\web\build\kotlin-js-min\main'
@ ./kotlinx-io.js 5309:21-45 5320:21-45
@ ./ktor-utils.js
@ ./web.js
If I manually add text-encoding
as a npm-dependeny, it works.
If I list the npm dependency tree with the previous working ktor version 1.0.1 I get:
+-- kotlinx-io@0.1.4 -> <mypath>\kotlinmultiplatform\web\build\node_modules_imported\kotlinx-io extraneous
+-- kotlinx-io-js@0.1.1 -> <mypath>\kotlinmultiplatform\web\build\node_modules_imported\kotlinx-io-js
| +-- kotlin@1.3.10
| +-- kotlin-test@1.3.10
| | `-- kotlin@1.3.10 deduped
| +-- kotlinx-atomicfu@0.11.12 -> <mypath>\kotlinmultiplatform\web\build\node_modules_imported\kotlinx-atomicfu deduped
| `-- text-encoding@0.7.0
If I list the npm dependency tree with a ktor version >= 1.1.0 the kotlinx-io-js
(and of course text-encoding
) is missing
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen io.ktor.client.request.HttpRequestPipeline@545e18f8
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/887
Ktor Version
implementation("io.ktor:ktor-client-ios:1.1.1")
Ktor Engine Used(client or server and name)
Client
JVM Version, Operating System and Relevant Context
MBP 2017 JVM 1.8
Feedback
This exception was printed out,and the test can't print out the expected result
suspend fun getDemoJSONDataFromNet(): String {
val result = client.get<String>("https://en.wikipedia.org/wiki/Main_Page")
Logger.d(tag, "the return data is $result")
return result
}
fun getDataIos(callBack: (result: String) -> Unit) {
runBlocking {
val job = async {
val result = HttpUtil.getDemoJSONDataFromNet()
Logger.d("SimpleIos","result value is $result")
callBack(result)
}
job.await()
}
}
@Test
fun testGetData() {
getDataIos {
println(it)
}
}
Exception Log:
Instances of kotlin.Error, kotlin.RuntimeException and subclasses aren't propagated from Kotlin to Objective-C/Swift.
Other exceptions can be propagated as NSError if method has or inherits @Throws annotation.
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen io.ktor.client.request.HttpRequestPipeline@2ef2a08
at 0 app 0x000000010dba2896 kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception + 70
at 1 app 0x000000010dba27b6 kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException + 70
at 2 app 0x000000010dba40e6 kfun:kotlin.native.concurrent.InvalidMutabilityException.<init>(kotlin.String)kotlin.native.concurrent.InvalidMutabilityException + 70
at 3 app 0x000000010dc3c6b8 ThrowInvalidMutabilityException + 280
at 4 app 0x000000010dc5ee48 MutationCheck + 24
at 5 app 0x000000010dce8a83 kfun:io.ktor.util.pipeline.Pipeline.<set-interceptors>#internal + 67
at 6 app 0x000000010dce86a6 kfun:io.ktor.util.pipeline.Pipeline.notSharedInterceptorsList#internal + 70
at 7 app 0x000000010dce84d6 kfun:io.ktor.util.pipeline.Pipeline.cacheInterceptors#internal + 710
at 8 app 0x000000010dce6292 kfun:io.ktor.util.pipeline.Pipeline.sharedInterceptorsList#internal + 98
at 9 app 0x000000010dce61ca kfun:io.ktor.util.pipeline.Pipeline.createContext$ktor-utils(#GENERIC_kotlin.Any;#GENERIC_kotlin.Any)io.ktor.util.pipeline.PipelineExecutor<#GENERIC_kotlin.Any> + 90
at 10 app 0x000000010dce60f0 kfun:io.ktor.util.pipeline.Pipeline.execute(#GENERIC_kotlin.Any;#GENERIC_kotlin.Any)#GENERIC_kotlin.Any + 96
at 11 app 0x000000010dd313a8 kfun:io.ktor.client.HttpClient.$execute$COROUTINE$1.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 296
at 12 app 0x000000010dd31522 kfun:io.ktor.client.HttpClient.execute(io.ktor.client.request.HttpRequestBuilder)io.ktor.client.call.HttpClientCall + 130
at 13 app 0x000000010dd32696 kfun:io.ktor.client.call.$call$COROUTINE$3.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 758
at 14 app 0x000000010dd32852 kfun:io.ktor.client.call.call@io.ktor.client.HttpClient.(kotlin.coroutines.SuspendFunction1<io.ktor.client.request.HttpRequestBuilder,kotlin.Unit>)io.ktor.client.call.HttpClientCall + 130
at 15 app 0x000000010dd330eb kfun:io.ktor.client.call.call@io.ktor.client.HttpClient.(io.ktor.client.request.HttpRequestBuilder)io.ktor.client.call.HttpClientCall + 123
at 16 app 0x000000010db97f3f kfun:sample.HttpUtil.$getDemoJSONDataFromNet$COROUTINE$0.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 1551
at 17 app 0x000000010db97821 kfun:sample.HttpUtil.getDemoJSONDataFromNet()kotlin.String + 113
at 18 app 0x000000010dba0de9 kfun:sample.$getDataIos$lambda-1$lambda-0$COROUTINE$3.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? + 313
at 19 app 0x000000010dbd4801 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) + 385
at 20 app 0x000000010dca7b89 kfun:kotlinx.coroutines.DispatchedTask.run() + 1129
at 21 app 0x000000010dca92d6 kfun:kotlinx.coroutines.EventLoopImpl.processNextEvent()ValueType + 422
at 22 app 0x000000010dccf7f7 kfun:kotlinx.coroutines.BlockingCoroutine.joinBlocking#internal + 263
at 23 app 0x000000010dccf5d7 kfun:kotlinx.coroutines.runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,#GENERIC>)Generic + 983
at 24 app 0x000000010dccfb4c kfun:kotlinx.coroutines.runBlocking$default(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,#GENERIC>;kotlin.Int)Generic + 204
at 25 app 0x000000010dba06e4 kfun:sample.getDataIos(kotlin.Function1<kotlin.String,kotlin.Unit>) + 100
at 26 app 0x000000010dba0618 kfun:sample.main(kotlin.Array<kotlin.String>) + 216
at 27 iosApp 0x000000010d897774 $S6iosApp14ViewControllerC11viewDidLoadyyFTo + 36
at 28 UIKitCore 0x0000000112ea84e1 -[UIViewController loadViewIfRequired] + 1186
When Route.authenticate has multiple configurations (>1) only first configuration is used to authenticate
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/847
Ktor Version
1.1.1
Ktor Engine Used(client or server and name)
Server Netty
JVM Version, Operating System and Relevant Context
OpenJDK 11, OSX
Feedback
authenticate("a1", "a2") {... If "a1" authentication is not successful, "a2" is not used and response is 401 Unauthorized
OAuth feature is incompatible with Dropbox
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/771
Ktor Version
1.0.0
Ktor Engine Used(client or server and name)
Server: Netty
Client: Apache
Using OAuth feature
JVM Version, Operating System and Relevant Context
JVM: 10
OS: Mac
Feedback
OAuth2 with Dropbox fails during the "request access token" step. In summary, the OAuth feature includes the state
parameter in the request. Dropbox's API does not allow unexpected parameters, and fails the OAuth access token request.
From what I can tell, there is currently no way to prevent the state from being added to the access token request.
Details
Consider the following 3 client-server exchanges. Here the Client is the Ktor server, and the Server is the identity provider (Dropbox, Google, Github, etc).
- [Client] --
/authorize
-> [Server] - [Client] <-
...?code=...
-- [Server] - [Client] --
/access_token
-> [Server]
In a typical OAuth2 exchange, the Client sends a state=<nonce>
parameter with the request in 1
and the Server responds with state=<nonce>
in the redirect in 2
. (This state sending is not mandatory).
The OAuth feature is configured to include the state
in 3
when the Client requests the access token from the Server. Some APIs such as Github's want the state to be included in the request. Dropbox however, reject requests that include the state.
The following code sets the state
on the access token request.
https://github.com/ktorio/ktor/blob/85210082c729b12099ad25f509e99b23ccea23b8/ktor-features/ktor-auth/src/io/ktor/auth/OAuth2.kt#L149-L151
In short, if a state
was returned from the Server in 2
, it will be non-null
at this point in the code.
Using a custom OAuth2StateProvider
OAuth2ServerSettings
allows the settings of a custom OAuth2StateProvider
with which we can generate a custom state
. I mentioned previously that the state
is not mandatory, so maybe we can not generate a state? This is not an option, as the state
must not be null
. An empty state
results in a request that has state=
, but state is still there.
"response.readText()" never returns (probably infinite loop)
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/768
Ktor Version
1.0.0
Ktor Engine Used(client or server and name)
Apache
JVM Version, Operating System and Relevant Context
JDK 1.8.x, Win / Linux
Feedback
After about 1-2 hours of constant requests using ktor client it unexpectedly hangs on line:
val data = response.readText()
ktor will never pass this line. Even after 9 hours it's still on this line. No exceptions are thrown and the process is not closed.
This might do something with the response from the server because it's unpredictable at all. It might happen after 10 minutes and might be after 6 hours.
Are there any suggestions what this can be or how can I investigate this issue? Maybe, there are some timeouts that I need to set?
Thank you.
Simple code straight from documentation returns ERR_EMPTY_RESPONSE
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/467
Using 0.9.3 (current version) I tried the following simple code from the documentation:
fun main(args: Array<String>) {
val s = embeddedServer(Netty, port = 8080) {
routing {
get("/") {
call.respondText("Hello World!", ContentType.Text.Plain)
}
get("/demo") {
call.respondText("HELLO WORLD!")
}
}
}
s.start(wait = true)
}
When I run this code I see:
238 [main] INFO ktor.application - No ktor.deployment.watch patterns specified, automatic reload is not active
290 [main] INFO ktor.application - Responding at http://0.0.0.0:8080
When I visit http://localhost:8080/ in my browser I get ERR_EMPTY_RESPONSE. Adding a println() at the beginning of the get("/")
block reveals that it doesn't seem to be executing any of that code at all.
I'm currently using ktor in a separate project which doesn't suffer from this problem - I've got no idea what's different between one and the other. Would appreciate any ideas.
Exception in thread "nettyWorkerPool-3-2" java.lang.ClassCastException: kotlin.Unit cannot be cast to java.base/java.lang.Boolean
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/461
I'm getting this exception with ktor 0.9.3
Exception in thread "nettyWorkerPool-3-2" java.lang.ClassCastException: kotlin.Unit cannot be cast to java.base/java.lang.Boolean
at io.ktor.server.netty.cio.NettyResponsePipeline$processBodyFlusher$2.doResume(NettyResponsePipeline.kt:269)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:42)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:41)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:41)
at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:162)
at kotlinx.coroutines.experimental.AbstractContinuation.run(AbstractContinuation.kt:31)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:844)
Invalid page gives a blank page instead of 404
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/456
Instead of a 404, when there's a page with no route, you get a 404
Dynamically changing a html page
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/390
Hello! In fact, there is no problem here. The point is that even after studying all available examples, I never found a way to realize this possibility using ktor. Briefly: html page code should be changed after it was opened in the browser without updating it. When the server has data, it should immediately show them to the user. I will be very grateful if you provide a sample code or a link to the official documentation (maybe I missed something) Thank you in advance
Support JDK9
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/321
package 'sun.security.x509' is not available in JDK9
e: /Users/jonnyzzz/Work/ktor/ktor-server/ktor-server-core/src/io/ktor/util/SSL.kt: (62, 10): Symbol is declared in module 'java.base' which does not export package 'sun.security.x509'