Changelog 1.6 version
1.6.8
released 15th March 2022
Docs
Broken link in docs for Ktor Testing
The link in the screenshot (https://ktor.io/docs/old/testing.html#map) attached goes to the following url which is a 404
Server
java.lang.StackOverflowError in logger on KTor 1.3.4
Services on ktor 1.3.4 are logging sometimes
java.lang.StackOverflowError: null
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:54)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:72)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:72)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:72)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:72)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:72)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:72)
which might be related to https://github.com/Kotlin/kotlinx.coroutines/issues/1264,
however I can't find a place where JobCancellationException might have been caught and wrapped in another exception.
Route that is logging this is not doing anything in parallel.
This is only happening on ktor 1.3.4, never seen on earlier versions.
Other
Youtrack: Add 1.6.8
Backpost fix to 1.6.7. : Provides transitive vulnerable dependency ch.qos.logback:logback-classic:1.2.6, logback-core, jetty-http
testImplementation("io.ktor:ktor-server-tests:1.6.7")
leads to:
Provides transitive vulnerable dependency ch.qos.logback:logback-classic:1.2.6 Deserialization of Untrusted Data vulnerability pending CVSS allocation
Provides transitive vulnerable dependency ch.qos.logback:logback-core:1.2.6 Deserialization of Untrusted Data vulnerability pending CVSS allocation
Provides transitive vulnerable dependency org.eclipse.jetty:jetty-http:9.4.42.v20210604 Exposure of Sensitive Information to an Unauthorized Actor vulnerability pending CVSS allocation
https://advisory.checkmarx.net/advisory/vulnerability/CVE-2021-42550
1.6.7
released 8th December 2021
Client
CIO client bidirectional streaming
I'm working on a project which requires a client to stream a potentially large amount of binary data to a server and receive a stream in response. Because the client to server stream could be very long, I need to be able to read the response on the client side before the client has completed sending. From my testing, this does not appear to be possible using the CIO client.
Here is a small example which illustrates the limitation:
suspend fun main() {
runServer()
runClient()
}
suspend fun runServer() {
embeddedServer(Netty, port = 8763) {
routing {
post("/post") {
launch { readChannel("Server", call.receiveChannel()) }
launch { call.respond(createContent("Server")) }
}
}
}.also { it.start(false) }
}
suspend fun runClient() {
HttpClient(CIO).request<HttpStatement> {
url("http://localhost:8763/post")
method = HttpMethod.Post
body = createContent("Client")
}.execute { readChannel("Client", it.receive()) }
}
fun createContent(name: String) = createContent {
repeat(3) {
println("$name: Sending ${BYTES.size} bytes")
writeFully(BYTES)
flush()
delay(1000)
}
}
fun createContent(body: suspend ByteWriteChannel.() -> Unit) =
ChannelWriterContent(body, null)
suspend fun readChannel(name: String, channel: ByteReadChannel) {
val buffer = ByteArray(BYTES.size)
while (true) {
try { channel.readFully(buffer) }
catch(e: Exception) { break }
println("$name: Read ${buffer.size} bytes")
}
}
Running the above sample produces the following output:
Client: Sending 1024 bytes
Server: Read 1024 bytes
Server: Sending 1024 bytes
Client: Sending 1024 bytes
Server: Read 1024 bytes
Server: Sending 1024 bytes
Client: Sending 1024 bytes
Server: Read 1024 bytes
Server: Sending 1024 bytes
Client: Read 1024 bytes
Client: Read 1024 bytes
Client: Read 1024 bytes
As you can see, the client reads occur after all client sends which is not acceptable for my use case. Ideally, the output would look like this:
Client: Sending 1024 bytes
Server: Read 1024 bytes
Server: Sending 1024 bytes
Client: Read 1024 bytes
Client: Sending 1024 bytes
Server: Read 1024 bytes
Server: Sending 1024 bytes
Client: Read 1024 bytes
Client: Sending 1024 bytes
Server: Read 1024 bytes
Server: Sending 1024 bytes
Client: Read 1024 bytes
From what I can tell, it seems the HttpStatement.execute
method blocks until the client has sent it's entire payload.
Core
Incompatible change from io.ktor:ktor-server-core:1.6.5 to 1.6.6
Hi Team!
Within your class: io.ktor.http.URLBuilder.kt you changed the signiture of Url from
public data class Url
To
public data class Url internal constructor
Which prevents the usage for us. We are using it like:
private fun sanitize(url: Url): Url {
return Url(
protocol = url.protocol,
host = url.host,
specifiedPort = url.specifiedPort,
encodedPath = url.encodedPath,
parameters = sanitizeParameters(url.parameters),
fragment = url.fragment,
user = url.user,
password = url.password,
trailingQuery = url.trailingQuery,
)
}
Can you provide a fixed version e.g. 1.6.7 ? This would be the best for all customers of your library.
or provide a fix. I guess it should work somehow with the URLBuilder, but the new ParameterBuilder makes the compiler unhappy.
Generator
Implement frontend for redesigned Ktor Project Generator
Current frontend: http://start.ktor.io/
New Design: https://zpl.io/V0XkGkK
Current frontend repo: github.com/ktorio/ktor-plugin
Localization: no
Current questions:
- [x] Put together gatsbyjs based project to start working on generator
- [x] Make a decision whether develop generator as separate project (create a new repo in this case) or as a separate component and put them to Ktor landing
- [x] Discuss UX pitfalls such as ability to move between wizard steps, page with markdown layout, how to edit selected feature list.
- [ ] Do we need a dark theme? It needs to add that states to the design.
- [ ] Do we need a mobile version? (probably not, so we need a disclaimer in this case) How it should look like on a wide screen?
- [x] Margins and components are not consistent with webteam UI, is it ok to use webteam UI design system?
Current development tasks:
- [x] Fix saving staff to local storage (@pshenichniy.eugene)
- [x] API and react-actions refactoring (@Ekaterina_Zaikina)
- [x] Artifact control (@pshenichniy.eugene, @Ekaterina_Zaikina)
- [x] "Configuration in" control (@pshenichniy.eugene)
- [x] "Add sample code” control (@pshenichniy.eugene)
- [x] Simple third tab with link to Download, if not yet (@Ekaterina_Zaikina)
- [x] Fix the rest of warnings in console (@Ekaterina_Zaikina)
- [ ] Fix tests for redux store
- [x] Prettify loader for loading documentation for the selected plugin (@Ekaterina_Zaikina)
- [x] Apply comments after design review to the first step (@Ekaterina_Zaikina)
- [x] Apply comments after design review to the second step (@Ekaterina_Zaikina)
- [x] Create task for wording review and ask someone to review (@Ekaterina_Zaikina)
- [x] Design review of the third tab (@Ekaterina_Zaikina)
- [x] Add shortcuts navigation to the list of plugins (@Ekaterina_Zaikina)
- [x] Add google or/and FUS analytics
- [x] Improve header layout for narrow screens
IntelliJ IDEA Plugin
New Project Wizard: Ktor generator is empty on first open (UPAE from KtorProjectSettingsStep)
-
Open 221 EAP Nightly
-
File -> New Project
-
Go to Ktor in Generators sidebar
-
Ktor tab is empty. Next button is available.
-
Click Next button. Exception is thrown, nothing happens
kotlin.UninitializedPropertyAccessException: lateinit property projectSettingsTemplate has not been initialized
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep$initialProjectName$2.invoke(KtorProjectSettingsStep.kt:43)
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep$initialProjectName$2.invoke(KtorProjectSettingsStep.kt:42)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep.getInitialProjectName(KtorProjectSettingsStep.kt:42)
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep.access$getInitialProjectName(KtorProjectSettingsStep.kt:33)
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep$projectNameProperty$1.invoke(KtorProjectSettingsStep.kt:48)
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep$projectNameProperty$1.invoke(KtorProjectSettingsStep.kt:47)
at com.intellij.openapi.observable.properties.AtomicLazyProperty$update$resolve$1.invoke(AtomicLazyProperty.kt:32)
at com.intellij.openapi.observable.properties.AtomicLazyProperty$update$1.apply(AtomicLazyProperty.kt:33)
at java.base/java.util.concurrent.atomic.AtomicReference.updateAndGet(AtomicReference.java:209)
at com.intellij.openapi.observable.properties.AtomicLazyProperty.update(AtomicLazyProperty.kt:33)
at com.intellij.openapi.observable.properties.AtomicLazyProperty.get(AtomicLazyProperty.kt:16)
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep.getLocation(KtorProjectSettingsStep.kt:239)
at io.ktor.initializr.intellij.settings.KtorProjectSettingsStep.validate(KtorProjectSettingsStep.kt:218)
at com.intellij.ide.projectWizard.ProjectTypeStep.validate(ProjectTypeStep.java:653)
at com.intellij.ide.util.newProjectWizard.AbstractProjectWizard.commitStepData(AbstractProjectWizard.java:236)
at com.intellij.ide.util.newProjectWizard.AbstractProjectWizard.doNextAction(AbstractProjectWizard.java:255)
at com.intellij.ide.wizard.AbstractWizard.proceedToNextStep(AbstractWizard.java:249)
at com.intellij.ide.wizard.AbstractWizard$5.actionPerformed(AbstractWizard.java:201)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:270)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6654)
at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3345)
at java.desktop/java.awt.Component.processEvent(Component.java:6419)
at java.desktop/java.awt.Container.processEvent(Container.java:2263)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5029)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4861)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2790)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4861)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:778)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:885)
at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.java:814)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:737)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$6(IdeEventQueue.java:433)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:802)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$7(IdeEventQueue.java:432)
at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:106)
at com.intellij.ide.IdeEventQueue.performActivity(IdeEventQueue.java:598)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:430)
at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:873)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:478)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117)
at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:190)
at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:235)
at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:233)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:233)
at java.desktop/java.awt.Dialog.show(Dialog.java:1070)
at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl$MyDialog.show(DialogWrapperPeerImpl.java:701)
at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl.show(DialogWrapperPeerImpl.java:438)
at com.intellij.openapi.ui.DialogWrapper.doShow(DialogWrapper.java:1672)
at com.intellij.openapi.ui.DialogWrapper.show(DialogWrapper.java:1630)
at com.intellij.openapi.ui.DialogWrapper.showAndGet(DialogWrapper.java:1644)
at com.intellij.ide.impl.NewProjectUtil.createNewProject(NewProjectUtil.java:75)
at com.intellij.ide.actions.NewProjectAction.actionPerformed(NewProjectAction.java:21)
at com.intellij.openapi.actionSystem.ex.ActionUtil.lambda$performActionDumbAwareWithCallbacks$4(ActionUtil.java:244)
at com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareWithCallbacks(ActionUtil.java:265)
at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAwareWithCallbacks(ActionUtil.java:244)
at com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenActionsUtil.performAnActionForComponent(WelcomeScreenActionsUtil.java:96)
at com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenActionsUtil$ToolbarTextButtonWrapper$1.actionPerformed(WelcomeScreenActionsUtil.java:56)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:270)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6654)
at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3345)
at java.desktop/java.awt.Component.processEvent(Component.java:6419)
at java.desktop/java.awt.Container.processEvent(Container.java:2263)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5029)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4861)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2790)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4861)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:778)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:885)
at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.java:814)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:737)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$6(IdeEventQueue.java:433)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:802)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$7(IdeEventQueue.java:432)
at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:119)
at com.intellij.ide.IdeEventQueue.performActivity(IdeEventQueue.java:598)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:430)
at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:873)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:478)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
- Close wizard, go to step 2. Ktor tab is usually shown now
EA link for exception
https://ea.jetbrains.com/browser/ea_reports/9040613
Reproduced on
- IDEA: 2022.1 EAP nightly, #IU-221.4286, built on February 10, 2022
- Kotlin: 221-1.6.20-RC-138-IJ4286
Ktor plugin in IDEA 2021.3 suggests migrating the 2.0.0-eap project to 1.6.6
- Run IDEA 2021.3.
- Open the https://github.com/ktorio/ktor-documentation/tree/main/codeSnippets project and switch to the 2.0.0 branch.
=> IDEA suggests migrating this project to 1.6.6.
P.S. Maybe it's related to absense of the 2.0.0-eap version in a plugin for IDEA 2021.3.
Other
Binary compatibility issue with Ktor 1.6.5 (affecting JDK 1.8 & 11 users)
I haven't checked the details yet but some tests using Ktor's testing module in our project detected a binary-compatibility issue.
As a workaround, we locked the version to 1.6.4 and it worked for us. https://github.com/slackapi/java-slack-sdk/commit/8a9063bf694376074a3a4bed93871416f48241c3
I hope this helps.
java.lang.NoSuchMethodError: java.nio.ByteBuffer.limit(I)Ljava/nio/ByteBuffer;
at io.ktor.utils.io.ByteBufferChannel.prepareBuffer(ByteBufferChannel.kt:231)
at io.ktor.utils.io.ByteBufferChannel.setupStateForWrite$ktor_io(ByteBufferChannel.kt:285)
at io.ktor.utils.io.ByteBufferChannel.writeAsMuchAsPossible(ByteBufferChannel.kt:3487)
at io.ktor.utils.io.ByteBufferChannel.writeFully$suspendImpl(ByteBufferChannel.kt:1425)
at io.ktor.utils.io.ByteBufferChannel.writeFully(ByteBufferChannel.kt)
at io.ktor.utils.io.ByteWriteChannelKt.writeFully(ByteWriteChannel.kt:152)
at io.ktor.server.engine.BaseApplicationResponse$respondFromBytes$3$1.invokeSuspend(BaseApplicationResponse.kt:191)
at (Coroutine boundary.()
at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invokeSuspend(DefaultTransform.kt:35)
at test_locally.app.events.TestKt$main$2$1.invokeSuspend(test.kt:42)
at io.ktor.routing.Routing.executeResult(Routing.kt:154)
at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:107)
at io.ktor.server.testing.TestApplicationEngine$callInterceptor$1.invokeSuspend(TestApplicationEngine.kt:296)
at io.ktor.server.testing.TestApplicationEngine$2.invokeSuspend(TestApplicationEngine.kt:50)
at io.ktor.server.testing.TestApplicationEngine$handleRequest$pipelineJob$1.invokeSuspend(TestApplicationEngine.kt:295)
at io.ktor.server.testing.TestApplicationEngine$handleRequest$1.invokeSuspend(TestApplicationEngine.kt:153)
1.6.6
released 29th November 2021
Client
Cookies that added to request got removed if HttpCookies plugin is installed
To reproduce run the following test:
@Test
fun test() = runBlocking {
val client = HttpClient(MockEngine) {
engine {
addHandler { request ->
assertEquals("test=value", request.headers["Cookie"])
respond("")
}
}
install(HttpCookies) { // Without HttpCookies plugin the test passes
storage = AcceptAllCookiesStorage()
}
}
client.get<Unit>("/example") {
cookie("test", "value")
}
}
Basic auth not sending second request
Code:
private val ktorHttpClient = HttpClient(OkHttp) {
install(Auth) {
basic {
credentials { BasicAuthCredentials(username = "[REDACTED]", password = "[REDACTED]") }
}
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Log.d("Logger Ktor =>", message)
}
}
level = LogLevel.ALL
}
}
private suspend fun login() {
val url = "https://api.backblazeb2.com/b2api/v2/b2_authorize_account"
val resp = ktorHttpClient.get<HttpResponse>(url)
Log.d("BACKBLAZE", resp.readText())
}
Log output:
D/Logger Ktor =>: REQUEST: https://api.backblazeb2.com/b2api/v2/b2_authorize_account
D/Logger Ktor =>: METHOD: HttpMethod(value=GET)
COMMON HEADERS
-> Accept: */*
D/Logger Ktor =>: -> Accept-Charset: UTF-8
CONTENT HEADERS
-> Content-Length: 0
BODY Content-Type: null
D/Logger Ktor =>: BODY START
D/Logger Ktor =>: BODY END
D/EGL_emulation: eglMakeCurrent: 0xe73848a0: ver 2 0 (tinfo 0xe7383440)
D/Logger Ktor =>: RESPONSE: 401
METHOD: HttpMethod(value=GET)
D/Logger Ktor =>: FROM: https://api.backblazeb2.com/b2api/v2/b2_authorize_account
COMMON HEADERS
D/Logger Ktor =>: -> cache-control: max-age=0, no-cache, no-store
-> connection: keep-alive
D/Logger Ktor =>: -> content-length: 64
-> content-type: application/json;charset=utf-8
-> date: Mon, 22 Nov 2021 18:21:26 GMT
D/Logger Ktor =>: -> keep-alive: timeout=5
-> www-authenticate: BASIC realm="authorize_account"
D/Logger Ktor =>: BODY Content-Type: application/json; charset=utf-8
D/Logger Ktor =>: BODY START
D/Logger Ktor =>: {
"code": "bad_auth_token",
"message": "",
"status": 401
}
D/Logger Ktor =>: BODY END
E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-4
io.ktor.client.features.ClientRequestException: Client request(https://api.backblazeb2.com/b2api/v2/b2_authorize_account) invalid: 401 . Text: "{
"code": "bad_auth_token",
"message": "",
"status": 401
}"
From what I understand, upon receiving a 401, Ktor is supposed to send a second request with the Authorization
header. This is not happening. I have tried with expectSuccess = false
and it still doesn't happen. It does work with sendWithoutRequest { true }
.
Docs
EAP 2.0 Documentation mention non existing method `sendWithoutRequest`
The sendWithoutRequest
method mentionned here https://ktor.io/docs/eap/auth.html#digest is not available
I don't know if you expect bug report about documentation about EAP, sorry if you know the doc is outdated and plan to update it later.
And if I work my way to enable it, their is an NPE. As a workaround, I did :
install(Auth) {
providers+=object:AuthProvider by DigestAuthProvider({DigestAuthCredentials("foo", "bar")}, "MyRealm"){
val notFirst= Collections.synchronizedSet(mutableSetOf<String>())
override fun sendWithoutRequest(request: HttpRequestBuilder): Boolean {
return !notFirst.add(request.url.host+":"+request.url.port)
}
}
If not used, EACH call start without auth header.... So each call make 2 request (first the 401, then the 200)
Also, the nc
attribute of the authorization header is incremented even when the challenge is reset.
Website docs issue with CORS hosts subdomain
Here: https://ktor.io/docs/cors.html#hosts
it gives "subDomains" as an example like so: subDomains = listOf("en,de,es")
but I'm pretty sure it should be subDomains = listOf("en", "de", "es")
Server
corsCheckRequestHeaders false
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1722
Ktor Version and Engine Used (client or server and name)
ktor_version=1.3.1
server
Describe the bug
CORS feature installed in Application
install(CORS)
{
method(HttpMethod.Options)
header(HttpHeaders.XForwardedProto)
anyHost()
allowSameOrigin = false
allowCredentials = true
allowNonSimpleContentTypes = true
maxAgeDuration = 1.toDuration(DurationUnit.DAYS)
}
The preflight request of some browser contains Access-Control-Request-Headers, but the field-value of it is empty, function corsCheckRequestHeaders will return false, and the browser will get 403 status.
preflight request header
Host: xxx
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: https://xxx
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36 QBCore/4.0.1295.400 QQBrowser/9.0.2524.400 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2875.116 Safari/537.36 NetType/WIFI MicroMessenger/7.0.5 WindowsWechat
Access-Control-Request-Headers:
Accept: /
Referer: https://xxx
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.5;q=0.4
corsCheckRequestHeaders()
private fun ApplicationCall.corsCheckRequestHeaders(): Boolean {
val requestHeaders =
request.headers.getAll(HttpHeaders.AccessControlRequestHeaders)
?.filter { !it.isBlank() } // add this line to fix the issue <<<<<<<<<<<<<<<<<<<<<<<
?.flatMap { it.split(",") }
?.map {
it.trim().toLowerCasePreservingASCIIRules()
} ?: emptyList()
return requestHeaders.none { it !in allHeadersSet }
}
Session cookie with BASE64 encoding fails to set correct cookie
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1447
Ktor Version and Engine Used (client or server and name)
1.2.5 and 1.3.0-beta-1, server, netty and CIO
Describe the bug
When using the base64 encoding for a session cookie, the correct cookie is not picked up.
In my repro example below I am basically calling /login
, which sets the session, and then /
, where the session is evaluated. When using base64 encoding, the second call doesn't succeed, as no session is found.
Important to notice that this works with all other encodings, and even with base64 in the test engine.
To Reproduce
Minimal repro here: https://github.com/AndreasVolkmann/ktor-session
Steps to reproduce the behavior:
- Start the server
- Go to
localhost:5000/login
which should returnLogged in
and set the session / cookie. - Go to
localhost:5000
where an error is shown
When changing the encoding, the above scenario works.
Expected behavior
The session cookie should be set and valid for the next call, just like with all other encodings and under test.
Tested with multiple browsers and postman.
Some Netty EngineMain properties are not set
Ktor Version
1.6.4
Ktor Engine Used
Netty Server
Behaviour
Only some of NettyApplicationEngine
's properties are loaded from ApplicationConfig
set when using EngineMain
.
Expected behaviour
After reading https://ktor.io/docs/engines.html#configure-engine I assumed it would be possible to configure all properties (besides the functions) via the configuration files.
Missing runningLimit
, requestReadTimeoutSeconds
and tcpKeepAlive
DropwizardMetrics does not append baseName to the 'per endpoint'-metrics breaking-change
When setting basename
install(DropwizardMetrics) {
registry = metricRegistry
baseName = "my.prefix"
}
metrics for endpoints does not get prefixed with the baseName I set.
When GET-ing /some/path/
I get metrics /some/path/(method:GET)
but I expect my.prefix./some/path/(method:GET)
I am using ktor 1.5.2
Development mode isn't taken into account for subroutes
To reproduce run the following code and make a GET / request:
fun main() {
val env = applicationEngineEnvironment {
developmentMode = true
connector {
port = 4444
}
module {
routing {
get("/") {
throw RuntimeException()
}
}
}
}
embeddedServer(Netty, env).start()
}
In the stack trace I observe a line: io.ktor.util.pipeline.SuspendFunctionGun.execute
. Since development mode is enabled I expect that only DebugPipelineContext
is used but for all routes, except for the Routing
, SuspendFunctionGun
is executed.
Other
Drop Old Patch Releases from API Docs to Reduce Space Consumption
We should drop everything before 1.6.6
URL port should be in 0..65535
Update Kotlin to 1.6.0
1.6.5
released 2nd November 2021
Client
Fix Flaky Timeout Priority Test
Core
Add Support for Apple Silicon Targets
Im trying to run the wizard project for KMP with Kotlin 1.5.30 on a M1 mac
I added this on my gradle
val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget = when {
System.getenv("SDK_NAME")?.startsWith("iphoneos") == true -> ::iosArm64
System.getenv("NATIVE_ARCH")?.startsWith("arm") == true -> ::iosSimulatorArm64
else -> ::iosX64
}
iosTarget("ios") {
binaries {
framework {
baseName = "shared"
}
}
}
I could run the app on android and iOS, but then I added the Ktor dependencies and getting getting the following issue when I trying to run the app on an iOS simulator
Could not resolve all files for configuration ':shared:iosCompileKlibraries'.
> Could not resolve io.ktor:ktor-client-core:1.6.2.
Required by:
project :shared
> No matching variant of io.ktor:ktor-client-core:1.6.2 was found. The consumer was configured to find a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native', attribute 'org.jetbrains.kotlin.native.target' with value 'ios_simulator_arm64' but:
- Variant 'commonMainMetadataElements' capability io.ktor:ktor-client-core:1.6.2 declares a usage of 'kotlin-api' of a component:
- Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'common' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
- Other compatible attribute:
- Doesn't say anything about org.jetbrains.kotlin.native.target (required 'ios_simulator_arm64')
- Variant 'iosArm32ApiElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
- Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_arm32' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_simulator_arm64'
- Variant 'iosArm32MetadataElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
- Incompatible because this component declares a usage of 'kotlin-metadata' of a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_arm32' and the consumer needed a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_simulator_arm64'
- Variant 'iosArm64ApiElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
- Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_arm64' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_simulator_arm64'
- Variant 'iosArm64MetadataElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
- Incompatible because this component declares a usage of 'kotlin-metadata' of a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_arm64' and the consumer needed a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_simulator_arm64'
- Variant 'iosX64ApiElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
- Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_x64' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_simulator_arm64'
- Variant 'iosX64MetadataElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native':
- Incompatible because this component declares a usage of 'kotlin-metadata' of a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_x64' and the consumer needed a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.native.target' with value 'ios_simulator_arm64'
- Variant 'jsIrApiElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a usage of 'kotlin-api' of a component:
- Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
- Other compatible attribute:
- Doesn't say anything about org.jetbrains.kotlin.native.target (required 'ios_simulator_arm64')
- Variant 'jsIrRuntimeElements-published' capability io.ktor:ktor-client-core:1.6.2:
- Incompatible because this component declares a usage of 'kotlin-runtime' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
- Other compatible attribute:
- Doesn't say anything about org.jetbrains.kotlin.native.target (required 'ios_simulator_arm64')
- Variant 'jsLegacyApiElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a usage of 'kotlin-api' of a component:
- Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
- Other compatible attribute:
- Doesn't say anything about org.jetbrains.kotlin.native.target (required 'ios_simulator_arm64')
- Variant 'jsLegacyRuntimeElements-published' capability io.ktor:ktor-client-core:1.6.2:
- Incompatible because this component declares a usage of 'kotlin-runtime' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'js' and the consumer needed a usage of 'kotlin-api' of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
- Other compatible attribute:
- Doesn't say anything about org.jetbrains.kotlin.native.target (required 'ios_simulator_arm64')
- Variant 'jvmApiElements-published' capability io.ktor:ktor-client-core:1.6.2 declares an API of a component:
- Incompatible because this component declares a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm' and the consumer needed a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'native'
- Other compatible attribute:
- Doesn't say anything about org.jetbrains.kotlin.native.target (required 'ios_simulator_arm64')
- Variant 'jvmRuntimeElements-published' capability io.ktor:ktor-client-core:1.6.2 declares a runtime of a component:
Bump kotlinx.serialization to 1.3.0
Remove internal require and switch to Stdlib
Use the stdlib require
function instead of io.ktor.utils.io.core.internal.require
, with has the same signature.
Host parameter may contain illegal symbols
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1862
Ktor Version and Engine Used (client or server and name)
io.ktor:ktor-server-cio:1.3.2
Describe the bug
"/", "?", "#", "@" symbols in 'Host' parameter value are prohibited(see https://tools.ietf.org/html/rfc2396#appendix-A)
To Reproduce
Steps to reproduce the behavior:
Run code:
import io.ktor.http.cio.parseRequest
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.runBlocking
fun main() {
listOf(
"""
POST / HTTP/1.1
Host: www/example.com
""".trimIndent(),
"""
POST / HTTP/1.1
Host: www.example?com
""".trimIndent(),
"""
POST / HTTP/1.1
Host: www.ex#mple.com
""".trimIndent(),
"""
POST / HTTP/1.1
Host: www.ex@mple.com
""".trimIndent()
).forEach {
test(it)
}
}
fun test(http: String) {
val request = runBlocking { parseRequest(ByteReadChannel(http)) }!!
println("method=${request.method};uri={${request.uri}};headers=[${request.headers}]")
}
Expected behavior
An error expected - exception or null result.
Docs
ktor-documentation code snippets - project e2e can't run
I can't make 'e2e' to start.
https://github.com/ktorio/ktor-documentation/tree/main/codeSnippets/snippets/e2e
Expected:
./gradlew :e2e:run
This has to work.
Infrastructure
Ktor client 1.6.5 not published for 1.6.5
Its seems that the publish infrastructure is broken again and
https://search.maven.org/artifact/io.ktor/ktor-client-core-watchosx64/1.6.5
Wasn't published properly. It's however referenced in the module https://repo1.maven.org/maven2/io/ktor/ktor-client-core/1.6.5/ktor-client-core-1.6.5.module
Could not find tvOS in ktor-client
Hi!
In version 1.6.4 there are no problems, tvOS is supported without problems.
When upgrading the version to 1.6.5, it cannot find tvOS support.
FAILURE: Build completed with 2 failures.
1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':shared:compileKotlinTvosArm64'.
> Could not resolve all files for configuration ':shared:tvosArm64CompileKlibraries'.
> Could not find io.ktor:ktor-client-core-tvosarm64:1.6.5.
Searched in the following locations:
- https://dl.google.com/dl/android/maven2/io/ktor/ktor-client-core-tvosarm64/1.6.5/ktor-client-core-tvosarm64-1.6.5.pom
- https://repo.maven.apache.org/maven2/io/ktor/ktor-client-core-tvosarm64/1.6.5/ktor-client-core-tvosarm64-1.6.5.pom
- https://oss.sonatype.org/content/repositories/snapshots/io/ktor/ktor-client-core-tvosarm64/1.6.5/ktor-client-core-tvosarm64-1.6.5.pom
Required by:
project :shared > io.ktor:ktor-client-core:1.6.5
> Could not find io.ktor:ktor-client-ios-tvosarm64:1.6.5.
Searched in the following locations:
- https://dl.google.com/dl/android/maven2/io/ktor/ktor-client-ios-tvosarm64/1.6.5/ktor-client-ios-tvosarm64-1.6.5.pom
- https://repo.maven.apache.org/maven2/io/ktor/ktor-client-ios-tvosarm64/1.6.5/ktor-client-ios-tvosarm64-1.6.5.pom
- https://oss.sonatype.org/content/repositories/snapshots/io/ktor/ktor-client-ios-tvosarm64/1.6.5/ktor-client-ios-tvosarm64-1.6.5.pom
Required by:
project :shared > io.ktor:ktor-client-ios:1.6.5
Server
NullPointerException if static file is not found
Currently, if you use a static file from the JVM resources like this
static {
resources()
}
and try accessing a file this error get's thrown instead of a 404
java.lang.NullPointerException: jarEntry must not be null
at io.ktor.http.content.JarFileContent.<init>(JarFileContent.kt:41)
at io.ktor.http.content.StaticContentResolutionKt.resourceClasspathResource(StaticContentResolution.kt:62)
at io.ktor.http.content.StaticContentResolutionKt.resolveResource(StaticContentResolution.kt:38)
at io.ktor.http.content.StaticContentResolutionKt.resolveResource$default(StaticContentResolution.kt:24)
at io.ktor.http.content.StaticContentKt$resources$1.invokeSuspend(StaticContent.kt:230)
at io.ktor.http.content.StaticContentKt$resources$1.invoke(StaticContent.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:246)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
at com.homedia.backend.middleware.PublicKt$public$1$1.invokeSuspend(Public.kt:22)
at com.homedia.backend.middleware.PublicKt$public$1$1.invoke(Public.kt)
at com.homedia.backend.middleware.PublicKt$public$1$1.invoke(Public.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:246)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(SuspendFunctionGun.kt:136)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:79)
at io.ktor.routing.Routing.executeResult(Routing.kt:155)
at io.ktor.routing.Routing.interceptor(Routing.kt:39)
at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:107)
at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:246)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:110)
at io.ktor.features.ContentNegotiation$Feature$install$1.invoke(ContentNegotiation.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:246)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
at io.ktor.features.StatusPages$interceptCall$2.invokeSuspend(StatusPages.kt:102)
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:194)
at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:101)
at io.ktor.features.StatusPages$Feature$install$2.invokeSuspend(StatusPages.kt:142)
at io.ktor.features.StatusPages$Feature$install$2.invoke(StatusPages.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:246)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
at io.ktor.metrics.micrometer.MicrometerMetrics$Feature$install$2.invokeSuspend(MicrometerMetrics.kt:209)
at io.ktor.metrics.micrometer.MicrometerMetrics$Feature$install$2.invoke(MicrometerMetrics.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:246)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(SuspendFunctionGun.kt:136)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:79)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invokeSuspend(DefaultEnginePipeline.kt:124)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:246)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(SuspendFunctionGun.kt:136)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:79)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invokeSuspend(NettyApplicationCallHandler.kt:123)
at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:55)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:112)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:43)
at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:34)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:61)
at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:370)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
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.ktor.server.netty.EventLoopGroupProxy$Companion$create$factory$1$1.run(NettyApplicationEngine.kt:241)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:831)
Change default log level for CallLogging feature to INFO
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/319
The rational behind this proposal is that a production deployment of an application shall typically run with INFO
threshold on its logs (DEBUG
and TRACE
are typically used only for debugging of certain subsystems) and we expect that all requests shall be logged in a production deployment by default, since the log of requests represents the starting point for investigation of any production incidents.
PartialContent need responed content length when respcode = 206
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1995
now just range
Server CORS Plugin: Throw IllegalArgumentException if anyHost and allowCredentials is true
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials
install(CORS) {
anyHost()
allowCredentials = true
}
Expected: IAE during installing CORS plugin with this configuration
java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1652
Hi all
When I run my Ktor application with gradle run then I've got the following exception:
19:21:11.795 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
19:21:11.810 [main] DEBUG io.netty.util.internal.PlatformDependent0 - -Dio.netty.noUnsafe: false
19:21:11.810 [main] DEBUG io.netty.util.internal.PlatformDependent0 - Java version: 11
19:21:11.811 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available
19:21:11.812 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available
19:21:11.812 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available
19:21:11.814 [main] DEBUG io.netty.util.internal.PlatformDependent0 - direct buffer constructor: unavailable
java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled
at io.netty.util.internal.ReflectionUtil.trySetAccessible(ReflectionUtil.java:31)
at io.netty.util.internal.PlatformDependent0$4.run(PlatformDependent0.java:225)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at io.netty.util.internal.PlatformDependent0.<clinit>(PlatformDependent0.java:219)
at io.netty.util.internal.PlatformDependent.isAndroid(PlatformDependent.java:273)
at io.netty.util.internal.PlatformDependent.<clinit>(PlatformDependent.java:92)
at io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:225)
at io.netty.channel.epoll.Native.<clinit>(Native.java:57)
at io.netty.channel.epoll.Epoll.<clinit>(Epoll.java:39)
at io.ktor.server.netty.EventLoopGroupProxy$Companion.create(NettyApplicationEngine.kt:189)
at io.ktor.server.netty.NettyApplicationEngine.<init>(NettyApplicationEngine.kt:74)
at io.ktor.server.netty.EngineMain.main(EngineMain.kt:22)
19:21:11.814 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Bits.unaligned: available, true
19:21:11.815 [main] DEBUG io.netty.util.internal.PlatformDependent0 - jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable
java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent0$6 cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @557caf28
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:591)
at java.base/java.lang.reflect.Method.invoke(Method.java:558)
at io.netty.util.internal.PlatformDependent0$6.run(PlatformDependent0.java:335)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at io.netty.util.internal.PlatformDependent0.<clinit>(PlatformDependent0.java:326)
at io.netty.util.internal.PlatformDependent.isAndroid(PlatformDependent.java:273)
at io.netty.util.internal.PlatformDependent.<clinit>(PlatformDependent.java:92)
at io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:225)
at io.netty.channel.epoll.Native.<clinit>(Native.java:57)
at io.netty.channel.epoll.Epoll.<clinit>(Epoll.java:39)
at io.ktor.server.netty.EventLoopGroupProxy$Companion.create(NettyApplicationEngine.kt:189)
at io.ktor.server.netty.NettyApplicationEngine.<init>(NettyApplicationEngine.kt:74)
at io.ktor.server.netty.EngineMain.main(EngineMain.kt:22)
the content of build.gradle.kts file
plugins {
application
kotlin("jvm") version "1.3.61"
}
group = "io.flatmap"
version = "1.0-SNAPSHOT"
val ktor_version = "1.3.0"
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation(kotlin("stdlib-jdk8"))
compile("io.ktor:ktor-server-netty:$ktor_version")
compile("io.ktor:ktor-server-core:$ktor_version")
compile("ch.qos.logback:logback-classic:1.2.3")
testCompile(group = "junit", name = "junit", version = "4.12")
}
tasks {
compileKotlin {
kotlinOptions.jvmTarget = "11"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "11"
}
}
application {
mainClassName = "io.ktor.server.netty.EngineMain"
}
I am using Zulu OpenJDK 11:
java --version
openjdk 11.0.6 2020-01-14 LTS
OpenJDK Runtime Environment Zulu11.37+17-CA (build 11.0.6+10-LTS)
OpenJDK 64-Bit Server VM Zulu11.37+17-CA (build 11.0.6+10-LTS, mixed mode)
I thought, this is solved through https://github.com/ktorio/ktor/issues/1190.
Thanks
Other
Provide color on log out when running application.
Distinguishing green, yellow, red.
Incorrect grammar in notice about internal api
The last sentence is not required and is grammatically incorrect. Let's drop it please.
1.6.4
released 1st October 2021
Client
Installed Closeable features not closed when closing HttpClient
Hi! I'm writing a custom feature that saves cookies to a file when the installed HttpClient
closes. From reading the HttpClient.close()
function here, it seems that the intention is that the close()
call will cascade to installed Closeable
features; but I discovered that my feature's close()
function is not called due to seemingly another layer of indirection in HttpClientConfig.install()
.
This test below demonstrates the problem:
class FeatureCloseTest {
@Test
fun testFeatureCloseIsCalled() {
val client = HttpClient(MockEngine) {
engine { addHandler { respond("") } }
install(TestFeature)
}
client.close()
assert(client.feature(TestFeature)!!.called) // This fails
}
class TestFeature : Closeable {
var called = false
override fun close() {
called = true
}
companion object : HttpClientFeature<Unit, TestFeature> {
override val key: AttributeKey<TestFeature> = AttributeKey("TestFeature")
override fun install(feature: TestFeature, scope: HttpClient) = Unit
override fun prepare(block: Unit.() -> Unit): TestFeature = TestFeature()
}
}
}
I think this is a bug? I can work around it at the moment to explicitly call my feature's close()
function, but it would be good to get this fixed.
Core
Significant performance penalty with socket write() and file read() on JVM
Hi,
It seems that there are performance issues with the current implementation of TCP socket write and also with reading files. Attaching the performance comparison for ktor IO vs regular JVM raw IO:
Files:
--------------------------------------------------------------------
Ktor channel read (128MiB), 20 iterations
Avg: 1239.65ms Min: 1095ms, Max: 1723ms
Raw: 1095, 1104, 1113, 1113, 1113, 1122, 1123, 1126, 1151, 1184, 1249, 1251, 1254, 1254, 1278, 1314, 1351, 1399, 1476, 1723
--------------------------------------------------------------------
Random File Read (128MiB), 20 iterations
Avg: 75.4ms Min: 65ms, Max: 161ms
Raw: 65, 65, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 70, 70, 75, 79, 83, 96, 161
--------------------------------------------------------------------
Stream Read (128MiB), 20 iterations
Avg: 58.5ms Min: 56ms, Max: 69ms
Raw: 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 59, 59, 59, 59, 59, 65, 69
Sockets:
--------------------------------------------------------------------
JVM socket write (128MiB), 20 iterations
Avg: 87.7ms Min: 79ms, Max: 112ms
Raw: 79, 81, 81, 82, 82, 82, 83, 83, 84, 84, 84, 86, 87, 90, 90, 91, 92, 96, 105, 112
--------------------------------------------------------------------
Ktor socket write (128MiB), 20 iterations
Avg: 675.3ms Min: 572ms, Max: 968ms
Raw: 572, 582, 583, 589, 598, 612, 633, 636, 651, 676, 682, 683, 686, 688, 689, 702, 727, 763, 786, 968
You can reproduce the performance measurements with the following tests:
https://gist.github.com/Malinskiy/2dbe05a38321417fd4f23e1872593806
https://gist.github.com/Malinskiy/0172b369040cabc074ccfea9a787a79c
This is the result of an investigation of the issue raised here https://github.com/Malinskiy/marathon/issues/462
Cheers,
Anton
ContentType.parse("text/html qqq") must fail with error
STR:
- invoke
ContentType.parse("text/html xxx")
ER: fail
AR:ContentType
with value"text/html xxx"
Docs
In multi-module application, a plugin installed in a single module will be executed for requests handled by any module.
Per Ktor docs https://ktor.io/docs/modules.html, installed plugins should only apply to the module in which they are installed. However we are seeing that if a plugin is installed in one module, but not another, the plugin is executed for requests handled by the module that DOES NOT have the plugin installed.This is either a bug, or the documentation is misleading or incomplete on how to separate plugins for different modules.
Example (source, and runnable JAR provided):
I have a Ktor application with 2 modules, each defining a distinct Route,
module1() /module1
module2() /module2
I have the CallLogging plugin installed in module1, but not in module2
However when making calls to routes in either module the CallLogging plugin is executed:
Calling http://localhost:8080/module1
we see in console, from CallLogging plugin
2021-10-07 10:36:30.671 [eventLoopGroupProxy-4-1] TRACE Application - 200 OK: GET - /module1
Calling http://localhost:8080/module2
we see in console, from CallLogging plugin
2021-10-07 10:37:57.069 [eventLoopGroupProxy-4-1] TRACE Application - 200 OK: GET - /module2
Explain method(HttpMethod.Options) in docs for CORS
method(HttpMethod.Options
Is always required in CORS feature because a browser always sends Options preflight request to check CORS (https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request)
But for some reason it is not documented
Please, also stay in sync about API that may be changed here (https://youtrack.jetbrains.com/issue/KTOR-2912)
Update JSON topics using code snippets from the 'codeSnippets' project
Update the 'Modules' topic
https://ktor.io/docs/modules.html
- add information on how to use modules depending on the way used to run a server (https://ktor.io/docs/engines.html#configure)
Generator
start.ktor.io - oauth not importing io.ktor.sessions.* in Security.kt by default causing build error
Summary
Attempting to create a starter project using start.ktor.io has a build error by default if you select Authentication OAuth.
Steps to Reproduce
- Navigate to start.ktor.io
- Maintain default settings
- Gradle Kotlin, Ktor 1.6.3, Netty
- Add Authentication OAuth Plugin
- Generate Project
- Attempt to build project
Detailed Description
The generated project includes a Security.kt in plugins to configure OAuth for Google. Within routing
for auth-oauth-google, there is a line of code:
call.sessions.set(UserSession(principal?.accessToken.toString()))
Attempting to then build the project, we get the following error:
> Task :compileKotlin FAILED
e: ...\ktor-sample\src\main\kotlin\com\example\plugins\Security.kt: (42, 30): Unresolved reference: sessions
e: ...\ktor-sample\src\main\kotlin\com\example\plugins\Security.kt: (42, 43): Unresolved reference: sessions
This is due to the following import missing in Security.kt: import io.ktor.sessions.*
Solution
When generating a project with OAuth, include import io.ktor.sessions.*
in Security.kt
Could not find artifact org.jetbrains.kotlinx:kotlinx-html-jvm:pom:0.7.2
To reproduce create a project with all features selected and the the following properties:
- Build system: Maven
- Ktor version 1.4.0
- Engine: Tomcat
- Configuration: HOCON file
After project generation, sync fails with the following error:
Could not find artifact org.jetbrains.kotlinx:kotlinx-html-jvm:pom:0.7.2 in kotlinx-html (https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven)
Also, build fails with an unresolved reference error:
/home/stexe/projects/ktor_plugin/ktor-sample7/src/main/kotlin/com/example/plugins/Sockets.kt:11:24
Kotlin: Unresolved reference: tls
IntelliJ IDEA Plugin
Ktor 2.0.0-eap-256 some dependecies are not imported
When creating a new Ktor project and targeting Ktor 2.0.0-eap-256, some dependencies are not imported by default in gradle file (despite being marked as needed in Plugins list).
These 3 are the ones that I had to import manually:
ktor-server-http-redirect
ktor-server-default-headers
ktor-server-content-negotiation
Env:
IDEA 2021.2.2 (Ultimate Edition)
Build #IU-212.5284.40, built on September 14, 2021
Runtime version: 11.0.12+7-b1504.28 x86_64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
macOS 10.15.7
GC: G1 Young Generation, G1 Old Generation
Memory: 2048M
Cores: 16
Kotlin: 212-1.5.10-release-IJ5284.40
Samples
GraalVM binary using CIO fails on start "Module function cannot be found"
Hi! I noticed the example you have in the repository builds successfully but fails upon execution.
21:21:09.621 [DefaultDispatcher-worker-1] INFO ktor.application - Autoreload is disabled because the development mode is off.
Exception in thread "DefaultDispatcher-worker-1" java.lang.ClassNotFoundException: Module function cannot be found for the fully qualified name 'io.ktorgraal.ApplicationKt$main$1.invoke'
at io.ktor.server.engine.internal.CallableUtilsKt.executeModuleFunction(CallableUtils.kt:27)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$launchModuleByName$1.invoke(ApplicationEngineEnvironmentReloading.kt:332)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$launchModuleByName$1.invoke(ApplicationEngineEnvironmentReloading.kt:331)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.avoidingDoubleStartupFor(ApplicationEngineEnvironmentReloading.kt:356)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.launchModuleByName(ApplicationEngineEnvironmentReloading.kt:331)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.access$launchModuleByName(ApplicationEngineEnvironmentReloading.kt:30)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$instantiateAndConfigureApplication$1.invoke(ApplicationEngineEnvironmentReloading.kt:319)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$instantiateAndConfigureApplication$1.invoke(ApplicationEngineEnvironmentReloading.kt:310)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.avoidingDoubleStartup(ApplicationEngineEnvironmentReloading.kt:338)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.instantiateAndConfigureApplication(ApplicationEngineEnvironmentReloading.kt:310)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.createApplication(ApplicationEngineEnvironmentReloading.kt:143)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.start(ApplicationEngineEnvironmentReloading.kt:277)
at io.ktor.server.cio.CIOApplicationEngine$serverJob$1$2.invokeSuspend(CIOApplicationEngine.kt:67)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:553)
at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
Exception in thread "main" java.lang.ClassNotFoundException: Module function cannot be found for the fully qualified name 'io.ktorgraal.ApplicationKt$main$1.invoke'
at io.ktor.server.engine.internal.CallableUtilsKt.executeModuleFunction(CallableUtils.kt:27)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$launchModuleByName$1.invoke(ApplicationEngineEnvironmentReloading.kt:332)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$launchModuleByName$1.invoke(ApplicationEngineEnvironmentReloading.kt:331)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.avoidingDoubleStartupFor(ApplicationEngineEnvironmentReloading.kt:356)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.launchModuleByName(ApplicationEngineEnvironmentReloading.kt:331)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.access$launchModuleByName(ApplicationEngineEnvironmentReloading.kt:30)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$instantiateAndConfigureApplication$1.invoke(ApplicationEngineEnvironmentReloading.kt:319)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$instantiateAndConfigureApplication$1.invoke(ApplicationEngineEnvironmentReloading.kt:310)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.avoidingDoubleStartup(ApplicationEngineEnvironmentReloading.kt:338)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.instantiateAndConfigureApplication(ApplicationEngineEnvironmentReloading.kt:310)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.createApplication(ApplicationEngineEnvironmentReloading.kt:143)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.start(ApplicationEngineEnvironmentReloading.kt:277)
at io.ktor.server.cio.CIOApplicationEngine$serverJob$1$2.invokeSuspend(CIOApplicationEngine.kt:67)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:553)
at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
Repo's gradle.properties file
ktor_version=1.6.2
kotlin_version=1.5.20
...
Let me know if I can help debug this further.
Server
MultiPartData.readAllParts throws IOException when the epilogue is omitted
Stack trace:
java.io.IOException: Broken delimiter occurred
at io.ktor.utils.io.DelimitedKt$skipDelimiterSuspend$2.invokeSuspend(Delimited.kt:58)
at io.ktor.utils.io.DelimitedKt$skipDelimiterSuspend$2.invoke(Delimited.kt)
at io.ktor.utils.io.DelimitedKt$skipDelimiterSuspend$2.invoke(Delimited.kt)
at io.ktor.utils.io.ByteBufferChannel.lookAheadSuspend$suspendImpl(ByteBufferChannel.kt:1822)
at io.ktor.utils.io.ByteBufferChannel.lookAheadSuspend(ByteBufferChannel.kt)
at io.ktor.utils.io.DelimitedKt.skipDelimiterSuspend(Delimited.kt:56)
at io.ktor.utils.io.DelimitedKt.skipDelimiter(Delimited.kt:51)
at io.ktor.http.cio.MultipartKt$parseMultipart$1.invokeSuspend(Multipart.kt:378)
Reproducible with the following command using the attached file:
nc localhost 8080 < req.txt
[Auth] [Interceptors] Phase Phase('Challenge') was not registered for this pipeline
Hi! I want to observe and store/retrieve state parameter in Ktor OAuth. But when I started researching I found that I can't insert phases before any Authentication phases.
Here's the code from my sandbox repo:
val writeAuthPhase = PipelinePhase("WriteAuth")
routing {
route("/test") {
val testRoute = authenticate {
get {
call.respondText("123")
}
}
val item = testRoute.items.find { it.name == "Challenge" }
println(item)
testRoute.insertPhaseBefore(Authentication.ChallengePhase, writeAuthPhase)
testRoute.intercept(writeAuthPhase) {
println(call.response.headers.allValues().toMap())
proceed()
}
println(testRoute.items)
}
}
And gradle run:
> Task :run
Phase('Challenge')
[Phase('Setup'), Phase('Monitoring'), Phase('Features'), Phase('Authenticate'), Phase('WriteAuth'), Phase('Challenge'), Phase('Call'), Phase('Fallback')]
12:44:25.889 [eventLoopGroupProxy-4-1] ERROR ktor.application - Unhandled: GET - /test
io.ktor.util.pipeline.InvalidPhaseException: Phase Phase('Challenge') was not registered for this pipeline
<stack trace>
What am I doing wrong?….. And how can I retrieve `state` URL parameter from OAuth response/requests?
insertPhaseBefore and insertPhaseAfter lead to different order
This issue was imported from GitHub issue: https://github.com/ktorio/ktor/issues/1736
Ktor Version and Engine Used (client or server and name)
1.3.2 server
Describe the bug
Using nested Feature's interceptPipeline leads to different results when using insertPhaseBefore and insertPhaseAfter.
Example:
customA { customB(1) { get { call.respond("") } } }
Both added to Child of a Route via
pipeline.insertPhaseBefore(ApplicationCallPipeline.Features, customA/B) pipeline.intercept(customA/B)
Using after:
[Phase('Setup'), Phase('Monitoring'), Phase('Features'), Phase('customB'), Phase('Call'), Phase('Fallback')]
[Phase('Setup'), Phase('Monitoring'), Phase('Features'), Phase('customA'), Phase('Call'), Phase('Fallback')]
Using before
[Phase('Setup'), Phase('Monitoring'), Phase('customA'), Phase('Features'), Phase('Call'), Phase('Fallback')]
[Phase('Setup'), Phase('Monitoring'), Phase('customB'), Phase('Features'), Phase('Call'), Phase('Fallback')]
Expected behavior
I would expect that both variants put Phase(customA) first, then Phase(customB) when merging the pipelines.
Upon googling I found the similar issue here.
Ktor 1.6.3 crashes on restart due to java.lang.ClassNotFoundException: Didn't find class "java.nio.file.WatchService" on Android 24
Ktor version: 1.6.3
Android version: 24
Engine: Netty
Exception thrown when calling `fun stop(gracePeriodMillis: Long, timeoutMillis: Long)`:
Related commit: https://github.com/ktorio/ktor/commit/b3ca535610f232d506aa2d104cd20fa1c27ba665
Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/nio/file/WatchService;
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.getWatcher(ApplicationEngineEnvironmentReloading.kt:72)
at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.stop(ApplicationEngineEnvironmentReloading.kt:296)
at io.ktor.server.netty.NettyApplicationEngine.stop(NettyApplicationEngine.kt:222)
at *.*.*.core.components.net.server.KtorServer.stopService(KtorServer.kt:102)
at *.*.*.core.components.net.server.KtorServer.startService(KtorServer.kt:93)
...
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.ClassNotFoundException: Didn't find class "java.nio.file.WatchService" on path:...
Other
Logging in Shutdown thread looks not informative
Subsystem
Server
Motivation
Logging in shutdown thread looks not informative:
14:02:10 INFO Thread-1 link.kotlin.server.ApplicationFactory Going to shutdown X
Solution
Set thread name to ShutdownHook thread
Native engines tests are not run outside of the ` ktor-client-tests` module
@Test
fun test() = clientTests {
test {
assertEquals(1, 2)
}
}
always succeeds