From 37d96768c31a280ddbc748e8d53e797b142d2685 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Tue, 19 Nov 2024 15:56:29 +0100 Subject: [PATCH 1/6] Use "import on demand" only for Ktor modules (#4495) --- .editorconfig | 9 ++++++--- .gitignore | 1 + .idea/externalDependencies.xml | 6 ++++++ CONTRIBUTING.md | 9 +++++---- .../jvm/src/io/ktor/http/cio/Multipart.kt | 11 ++++++----- .../src/io/ktor/http/cio/RequestResponseBuilder.kt | 7 +++---- 6 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 .idea/externalDependencies.xml diff --git a/.editorconfig b/.editorconfig index 9278db4f510..1be295016c4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,8 +11,7 @@ indent_size = 2 [*.{kt,kts}] ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL -ij_kotlin_name_count_to_use_star_import = 1 -ij_kotlin_name_count_to_use_star_import_for_members = 1 +ij_kotlin_packages_to_use_import_on_demand = java.util.*, io.ktor.** ktlint_standard_no-wildcard-imports = disabled ktlint_standard_trailing-comma-on-call-site = disabled @@ -20,4 +19,8 @@ ktlint_standard_trailing-comma-on-declaration-site = disabled ktlint_standard_filename = disabled ktlint_standard_class-naming = disabled ktlint_standard_annotation = disabled -ktlint_standard_comment-wrapping = disabled \ No newline at end of file +ktlint_standard_comment-wrapping = disabled + +[*.kts] +# Always use wildcard imports in scripts +ij_kotlin_name_count_to_use_star_import = 2 diff --git a/.gitignore b/.gitignore index 937cd0aaada..7975de28d3e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build .gradle .gradletasknamecache .idea/* +!.idea/externalDependencies.xml !.idea/runConfigurations !.idea/runConfigurations/* !.idea/vcs.xml diff --git a/.idea/externalDependencies.xml b/.idea/externalDependencies.xml new file mode 100644 index 00000000000..2e11b766c4c --- /dev/null +++ b/.idea/externalDependencies.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9530a0b537c..9fdf23b3528 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -98,13 +98,14 @@ A few things to remember: * Your code should conform to the official [Kotlin code style guide](https://kotlinlang.org/docs/reference/coding-conventions.html) - except that star imports should be always enabled - (ensure Preferences | Editor | Code Style | Kotlin, tab **Imports**, both `Use import with '*'` should be checked). + except that star imports should always be used for `io.ktor.*` packages. + Code style is managed by [EditorConfig](https://www.jetbrains.com/help/idea/editorconfig.html), + so make sure the EditorConfig plugin is enabled in the IDE. * Every new source file should have a copyright header. * Every public API (including functions, classes, objects and so on) should be documented, every parameter, property, return types and exceptions should be described properly. -* A questionable and new API should be marked with the `@KtorExperimentalAPI` annotation. -* A Public API that is not intended to be used by end-users that couldn't be made private/internal due to technical reasons, +* A Public API which is not intended to be used by end-users that couldn't be made private/internal due to technical + reasons, should be marked with `@InternalAPI` annotation. ### Commit messages diff --git a/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/Multipart.kt b/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/Multipart.kt index 3eeab2f5afa..231b85cf487 100644 --- a/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/Multipart.kt +++ b/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/Multipart.kt @@ -6,14 +6,15 @@ package io.ktor.http.cio import io.ktor.http.cio.internals.* import io.ktor.utils.io.* -import io.ktor.utils.io.ByteString import io.ktor.utils.io.core.* import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import kotlinx.io.* -import kotlinx.io.bytestring.* +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.produce +import kotlinx.io.IOException +import kotlinx.io.Source +import kotlinx.io.bytestring.ByteString import java.io.EOFException -import java.nio.* +import java.nio.ByteBuffer /** * Represents a multipart content starting event. Every part need to be completely consumed or released via [release] diff --git a/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/RequestResponseBuilder.kt b/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/RequestResponseBuilder.kt index c38581fd8dd..e0447a9c1dc 100644 --- a/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/RequestResponseBuilder.kt +++ b/ktor-http/ktor-http-cio/jvm/src/io/ktor/http/cio/RequestResponseBuilder.kt @@ -1,14 +1,13 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.http.cio import io.ktor.http.* -import io.ktor.utils.io.* import io.ktor.utils.io.core.* -import kotlinx.io.* -import java.nio.* +import kotlinx.io.Source +import java.nio.ByteBuffer /** * Builds an HTTP request or response From a3dabe77eed66fb01cff47f57b901d39a84a9b2b Mon Sep 17 00:00:00 2001 From: Leonid Stashevsky Date: Thu, 21 Nov 2024 10:07:42 +0100 Subject: [PATCH 2/6] KTOR-4534 Add retry for flaky tests (#4493) * Increase timeouts for linuxX64 tests to check timeout tests * Add test retry --- .../ktor/client/tests/utils/ClientLoader.kt | 1 + .../tests/utils/CommonClientTestUtils.kt | 67 ++++++++++++------- .../io/ktor/client/tests/HttpTimeoutTest.kt | 14 ++-- .../io/ktor/client/tests/WebSocketTest.kt | 2 +- .../ktor/client/tests/utils/ClientLoaderJs.kt | 3 +- .../client/tests/utils/ClientLoaderJvm.kt | 3 +- .../client/tests/utils/ClientLoaderNative.kt | 3 +- .../client/tests/utils/ClientLoaderWasm.kt | 7 +- .../src/io/ktor/utils/io/ByteChannel.kt | 3 +- .../src/io/ktor/server/test/base/BaseTest.kt | 1 + .../test/base/BaseTest.jsAndWasmShared.kt | 3 + .../io/ktor/server/test/base/BaseTestJvm.kt | 3 + .../io/ktor/server/test/base/BaseTestNix.kt | 3 + .../suites/HttpServerCommonTestSuite.kt | 5 +- 14 files changed, 74 insertions(+), 44 deletions(-) diff --git a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt index 47fb84bf809..ecd84cc776c 100644 --- a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt +++ b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/ClientLoader.kt @@ -17,6 +17,7 @@ expect abstract class ClientLoader(timeoutSeconds: Int = 60) { fun clientTests( skipEngines: List = emptyList(), onlyWithEngine: String? = null, + retries: Int = 1, block: suspend TestClientBuilder.() -> Unit ): TestResult diff --git a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt index 3efe5ea95c8..59ac996b784 100644 --- a/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt +++ b/ktor-client/ktor-client-tests/common/src/io/ktor/client/tests/utils/CommonClientTestUtils.kt @@ -34,8 +34,9 @@ const val TCP_SERVER: String = "http://127.0.0.1:8082" fun testWithEngine( engine: HttpClientEngine, timeoutMillis: Long = 60 * 1000L, + retries: Int = 1, block: suspend TestClientBuilder<*>.() -> Unit -) = testWithClient(HttpClient(engine), timeoutMillis, block) +) = testWithClient(HttpClient(engine), timeoutMillis, retries, block) /** * Perform test with selected [client]. @@ -43,16 +44,21 @@ fun testWithEngine( private fun testWithClient( client: HttpClient, timeout: Long, + retries: Int, block: suspend TestClientBuilder.() -> Unit ) = runTest(timeout = timeout.milliseconds) { - val builder1 = TestClientBuilder().also { it.block() } - concurrency(builder1.concurrency) { threadId -> - repeat(builder1.repeatCount) { attempt -> - @Suppress("UNCHECKED_CAST") - client.config { builder1.config(this as HttpClientConfig) } - .use { client -> builder1.test(TestInfo(threadId, attempt), client) } + val builder = TestClientBuilder().also { it.block() } + + retryTest(retries) { + concurrency(builder.concurrency) { threadId -> + repeat(builder.repeatCount) { attempt -> + @Suppress("UNCHECKED_CAST") + client.config { builder.config(this as HttpClientConfig) } + .use { client -> builder.test(TestInfo(threadId, attempt), client) } + } } } + client.engine.close() } @@ -64,6 +70,7 @@ fun testWithEngine( factory: HttpClientEngineFactory, loader: ClientLoader? = null, timeoutMillis: Long = 60L * 1000L, + retries: Int = 1, block: suspend TestClientBuilder.() -> Unit ) = runTest(timeout = timeoutMillis.milliseconds) { val builder = TestClientBuilder().apply { block() } @@ -75,31 +82,45 @@ fun testWithEngine( } } - withContext(Dispatchers.Default.limitedParallelism(1)) { - concurrency(builder.concurrency) { threadId -> - repeat(builder.repeatCount) { attempt -> - val client = HttpClient(factory, block = builder.config) + retryTest(retries) { + withContext(Dispatchers.Default.limitedParallelism(1)) { + concurrency(builder.concurrency) { threadId -> + repeat(builder.repeatCount) { attempt -> + val client = HttpClient(factory, block = builder.config) - client.use { - builder.test(TestInfo(threadId, attempt), it) - } + client.use { + builder.test(TestInfo(threadId, attempt), it) + } - try { - val job = client.coroutineContext[Job]!! - while (job.isActive) { - yield() + try { + val job = client.coroutineContext[Job]!! + while (job.isActive) { + yield() + } + } catch (cause: Throwable) { + client.cancel("Test failed", cause) + throw cause + } finally { + builder.after(client) } - } catch (cause: Throwable) { - client.cancel("Test failed", cause) - throw cause - } finally { - builder.after(client) } } } } } +internal suspend fun retryTest(attempts: Int, block: suspend () -> T): T { + var currentAttempt = 0 + while (true) { + try { + return block() + } catch (cause: Throwable) { + if (currentAttempt >= attempts) throw cause + currentAttempt++ + } + } +} + private suspend fun concurrency(level: Int, block: suspend (Int) -> Unit) { coroutineScope { List(level) { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt index fd4efed4e8f..7b603940353 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/HttpTimeoutTest.kt @@ -170,7 +170,7 @@ class HttpTimeoutTest : ClientLoader() { @Test fun testGetWithSeparateReceive() = clientTests { config { - install(HttpTimeout) { requestTimeoutMillis = 1000 } + install(HttpTimeout) { requestTimeoutMillis = 2000 } } test { client -> @@ -185,7 +185,7 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testGetWithSeparateReceivePerRequestAttributes() = clientTests { + fun testGetWithSeparateReceivePerRequestAttributes() = clientTests(retries = 5) { config { install(HttpTimeout) } @@ -204,9 +204,9 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testGetRequestTimeoutWithSeparateReceive() = clientTests(listOf("Js")) { + fun testGetRequestTimeoutWithSeparateReceive() = clientTests(listOf("Js"), retries = 5) { config { - install(HttpTimeout) { requestTimeoutMillis = 1000 } + install(HttpTimeout) { requestTimeoutMillis = 2000 } } test { client -> @@ -233,7 +233,7 @@ class HttpTimeoutTest : ClientLoader() { method = HttpMethod.Get parameter("delay", 10000) - timeout { requestTimeoutMillis = 1000 } + timeout { requestTimeoutMillis = 2000 } }.body() assertFailsWith { response.readUTF8Line() @@ -263,7 +263,7 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testGetStream() = clientTests { + fun testGetStream() = clientTests(retries = 5) { config { install(HttpTimeout) { requestTimeoutMillis = 1000 } } @@ -278,7 +278,7 @@ class HttpTimeoutTest : ClientLoader() { } @Test - fun testGetStreamPerRequestAttributes() = clientTests { + fun testGetStreamPerRequestAttributes() = clientTests(retries = 5) { config { install(HttpTimeout) } diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt index be90945596f..a3cf69f71ed 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt @@ -376,7 +376,7 @@ class WebSocketTest : ClientLoader() { } @Test - fun testAuthenticationWithValidInitialToken() = clientTests(ENGINES_WITHOUT_WS + "Js") { + fun testAuthenticationWithValidInitialToken() = clientTests(ENGINES_WITHOUT_WS + "Js", retries = 5) { config { install(WebSockets) diff --git a/ktor-client/ktor-client-tests/js/src/io/ktor/client/tests/utils/ClientLoaderJs.kt b/ktor-client/ktor-client-tests/js/src/io/ktor/client/tests/utils/ClientLoaderJs.kt index 16497edf244..cc158c7b8c6 100644 --- a/ktor-client/ktor-client-tests/js/src/io/ktor/client/tests/utils/ClientLoaderJs.kt +++ b/ktor-client/ktor-client-tests/js/src/io/ktor/client/tests/utils/ClientLoaderJs.kt @@ -21,6 +21,7 @@ actual abstract class ClientLoader actual constructor(private val timeoutSeconds actual fun clientTests( skipEngines: List, onlyWithEngine: String?, + retries: Int, block: suspend TestClientBuilder.() -> Unit ): TestResult { val skipEnginesLowerCase = skipEngines.map { it.lowercase() } @@ -28,7 +29,7 @@ actual abstract class ClientLoader actual constructor(private val timeoutSeconds return runTest { } } - return testWithEngine(Js, timeoutMillis = timeoutSeconds * 1000L, block = block) + return testWithEngine(Js, retries = retries, timeoutMillis = timeoutSeconds * 1000L, block = block) } actual fun dumpCoroutines() { diff --git a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt index 7aaea789cb7..07ee187b3b0 100644 --- a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt +++ b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt @@ -28,6 +28,7 @@ actual abstract class ClientLoader actual constructor(val timeoutSeconds: Int) { actual fun clientTests( skipEngines: List, onlyWithEngine: String?, + retries: Int, block: suspend TestClientBuilder.() -> Unit ) { DebugProbes.install() @@ -37,7 +38,7 @@ actual abstract class ClientLoader actual constructor(val timeoutSeconds: Int) { } runBlocking { withTimeout(timeoutSeconds.seconds.inWholeMilliseconds) { - testWithEngine(engine.factory, this@ClientLoader, timeoutSeconds * 1000L, block) + testWithEngine(engine.factory, this@ClientLoader, timeoutSeconds * 1000L, retries, block) } } } diff --git a/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoaderNative.kt b/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoaderNative.kt index 0d15345a810..153ee6cb6f9 100644 --- a/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoaderNative.kt +++ b/ktor-client/ktor-client-tests/posix/src/io/ktor/client/tests/utils/ClientLoaderNative.kt @@ -5,10 +5,8 @@ package io.ktor.client.tests.utils import io.ktor.client.engine.* -import io.ktor.util.* import io.ktor.utils.io.* import kotlin.experimental.* -import kotlin.native.runtime.* private class TestFailure(val name: String, val cause: Throwable) { @OptIn(ExperimentalNativeApi::class) @@ -32,6 +30,7 @@ actual abstract class ClientLoader actual constructor(private val timeoutSeconds actual fun clientTests( skipEngines: List, onlyWithEngine: String?, + retries: Int, block: suspend TestClientBuilder.() -> Unit ) { if (skipEngines.any { it.startsWith("native") }) return diff --git a/ktor-client/ktor-client-tests/wasmJs/src/io/ktor/client/tests/utils/ClientLoaderWasm.kt b/ktor-client/ktor-client-tests/wasmJs/src/io/ktor/client/tests/utils/ClientLoaderWasm.kt index ebcb2dd50c9..bac76c033d8 100644 --- a/ktor-client/ktor-client-tests/wasmJs/src/io/ktor/client/tests/utils/ClientLoaderWasm.kt +++ b/ktor-client/ktor-client-tests/wasmJs/src/io/ktor/client/tests/utils/ClientLoaderWasm.kt @@ -21,17 +21,14 @@ actual abstract class ClientLoader actual constructor(private val timeoutSeconds actual fun clientTests( skipEngines: List, onlyWithEngine: String?, + retries: Int, block: suspend TestClientBuilder.() -> Unit ): TestResult { val skipEnginesLowerCase = skipEngines.map { it.lowercase() } return if ((onlyWithEngine != null && onlyWithEngine != "js") || skipEnginesLowerCase.contains("js")) { runTest {} } else { - testWithEngine(Js) { - withTimeout(timeoutSeconds.toLong() * 1000) { - block() - } - } + testWithEngine(Js, retries = retries, timeoutMillis = timeoutSeconds * 1000L, block = block) } } diff --git a/ktor-io/common/src/io/ktor/utils/io/ByteChannel.kt b/ktor-io/common/src/io/ktor/utils/io/ByteChannel.kt index 0f4dfad2346..5146b853714 100644 --- a/ktor-io/common/src/io/ktor/utils/io/ByteChannel.kt +++ b/ktor-io/common/src/io/ktor/utils/io/ByteChannel.kt @@ -272,5 +272,4 @@ public class ByteChannel(public val autoFlush: Boolean = false) : ByteReadChanne public class ConcurrentIOException( taskName: String, cause: Throwable? = null -) : IllegalStateException("Concurrent $taskName attempts", cause) { -} +) : IllegalStateException("Concurrent $taskName attempts", cause) diff --git a/ktor-server/ktor-server-test-base/common/src/io/ktor/server/test/base/BaseTest.kt b/ktor-server/ktor-server-test-base/common/src/io/ktor/server/test/base/BaseTest.kt index 55dd988fc23..acf429ba97b 100644 --- a/ktor-server/ktor-server-test-base/common/src/io/ktor/server/test/base/BaseTest.kt +++ b/ktor-server/ktor-server-test-base/common/src/io/ktor/server/test/base/BaseTest.kt @@ -12,4 +12,5 @@ expect abstract class BaseTest() { open val timeout: Duration fun collectUnhandledException(error: Throwable) // TODO: better name? fun runTest(block: suspend CoroutineScope.() -> Unit): TestResult + fun runTest(timeout: Duration, block: suspend CoroutineScope.() -> Unit): TestResult } diff --git a/ktor-server/ktor-server-test-base/jsAndWasmShared/src/io/ktor/server/test/base/BaseTest.jsAndWasmShared.kt b/ktor-server/ktor-server-test-base/jsAndWasmShared/src/io/ktor/server/test/base/BaseTest.jsAndWasmShared.kt index d1e9e299dd2..a5d5436c57e 100644 --- a/ktor-server/ktor-server-test-base/jsAndWasmShared/src/io/ktor/server/test/base/BaseTest.jsAndWasmShared.kt +++ b/ktor-server/ktor-server-test-base/jsAndWasmShared/src/io/ktor/server/test/base/BaseTest.jsAndWasmShared.kt @@ -38,6 +38,9 @@ actual abstract class BaseTest actual constructor() { actual fun runTest(block: suspend CoroutineScope.() -> Unit): TestResult = runTestWithRealTime(timeout = timeout, testBody = block) + + actual fun runTest(timeout: Duration, block: suspend CoroutineScope.() -> Unit): TestResult = + runTestWithRealTime(timeout = timeout, testBody = block) } private class UnhandledErrorsException(override val message: String) : Exception() diff --git a/ktor-server/ktor-server-test-base/jvm/src/io/ktor/server/test/base/BaseTestJvm.kt b/ktor-server/ktor-server-test-base/jvm/src/io/ktor/server/test/base/BaseTestJvm.kt index e6e381d60c2..c193ced8e55 100644 --- a/ktor-server/ktor-server-test-base/jvm/src/io/ktor/server/test/base/BaseTestJvm.kt +++ b/ktor-server/ktor-server-test-base/jvm/src/io/ktor/server/test/base/BaseTestJvm.kt @@ -44,4 +44,7 @@ actual abstract class BaseTest actual constructor() { actual fun runTest(block: suspend CoroutineScope.() -> Unit): TestResult = runTestWithRealTime(CoroutineName("test-$testName"), timeout, block) + + actual fun runTest(timeout: Duration, block: suspend CoroutineScope.() -> Unit): TestResult = + runTestWithRealTime(CoroutineName("test-$testName"), timeout, block) } diff --git a/ktor-server/ktor-server-test-base/posix/src/io/ktor/server/test/base/BaseTestNix.kt b/ktor-server/ktor-server-test-base/posix/src/io/ktor/server/test/base/BaseTestNix.kt index 209d9cd6fb9..2f62e04be98 100644 --- a/ktor-server/ktor-server-test-base/posix/src/io/ktor/server/test/base/BaseTestNix.kt +++ b/ktor-server/ktor-server-test-base/posix/src/io/ktor/server/test/base/BaseTestNix.kt @@ -46,6 +46,9 @@ actual abstract class BaseTest actual constructor() { actual fun runTest(block: suspend CoroutineScope.() -> Unit): TestResult = runTestWithRealTime(timeout = timeout, testBody = block) + + actual fun runTest(timeout: Duration, block: suspend CoroutineScope.() -> Unit): TestResult = + runTestWithRealTime(timeout = timeout, testBody = block) } private class UnhandledErrorsException(override val message: String) : Exception() diff --git a/ktor-server/ktor-server-test-suites/common/src/io/ktor/server/testing/suites/HttpServerCommonTestSuite.kt b/ktor-server/ktor-server-test-suites/common/src/io/ktor/server/testing/suites/HttpServerCommonTestSuite.kt index 5b2a6c7a924..f29594ce514 100644 --- a/ktor-server/ktor-server-test-suites/common/src/io/ktor/server/testing/suites/HttpServerCommonTestSuite.kt +++ b/ktor-server/ktor-server-test-suites/common/src/io/ktor/server/testing/suites/HttpServerCommonTestSuite.kt @@ -31,6 +31,7 @@ import kotlinx.coroutines.* import kotlinx.io.* import kotlin.coroutines.* import kotlin.test.* +import kotlin.time.Duration.Companion.minutes abstract class HttpServerCommonTestSuite( hostFactory: ApplicationEngineFactory @@ -738,7 +739,7 @@ abstract class HttpServerCommonTestSuite Date: Thu, 21 Nov 2024 12:31:29 +0300 Subject: [PATCH 3/6] KTOR-7744 Add changelog for 2.3.13 (#4498) --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1b052761eb..d6c9a07a7af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -161,6 +161,21 @@ * Ktor client for Kotlin/Wasm ([KTOR-5587](https://youtrack.jetbrains.com/issue/KTOR-5587)) * CSRF protection feature ([KTOR-2910](https://youtrack.jetbrains.com/issue/KTOR-2910)) + +# 2.3.13 +> Published 20 November 2024 + +### Bugfixes +* CIO: Requests face connection timeouts when executed on the Android main dispatcher ([KTOR-6803](https://youtrack.jetbrains.com/issue/KTOR-6803)) +* io.ktor.util.TextKt.chomp doesn't work on strings with more than one character ([KTOR-7209](https://youtrack.jetbrains.com/issue/KTOR-7209)) +* "java.lang.IllegalArgumentException: Failed requirement." in SelectorManagerSupport ([KTOR-2914](https://youtrack.jetbrains.com/issue/KTOR-2914)) +* Backport fix for CVE-2024-49580 to Ktor 2 ([KTOR-7727](https://youtrack.jetbrains.com/issue/KTOR-7727)) + +### Improvements +* Replace custom withTimeout implementation using WeakTimeoutQueue with coroutines.withTimeout ([KTOR-3658](https://youtrack.jetbrains.com/issue/KTOR-3658)) +* Add watchosDeviceArm64 target ([KTOR-6368](https://youtrack.jetbrains.com/issue/KTOR-6368)) + + # 2.3.12 > Published 20 June 2024 From c9f58b1bd75c735d335dfaa9ca1a09ff9b1d7766 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:52:22 +0100 Subject: [PATCH 4/6] Update tomcat.jakarta to v10.1.33 (#4470) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 062d96b6f3b..08713580d6a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ jetty-alpn-boot = "8.1.13.v20181017" jetty-alpn-openjdk8 = "9.4.56.v20240826" tomcat = "9.0.97" -tomcat-jakarta = "10.1.31" +tomcat-jakarta = "10.1.33" apache = "4.1.5" apache5 = "5.3.1" From a3bccae4e48b7e13ecae1e27c31ca9e9c9825ce4 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Fri, 22 Nov 2024 12:00:26 +0100 Subject: [PATCH 5/6] KTOR-7812 Use default JDK for running tests on CI (#4342) * Use LTS JDK in ktor-java-modules-test * Allow reflective access to java.base modules from tests * Enable JDK auto-provision * Drop usage of unsupported curve secp128r1 * Enable dynamic agent loading * Disable testCookiesWithWrongValue for Java engine * Refactor onlyWithEngine * Remove JUnit RetrySupport --- buildSrc/settings.gradle.kts | 1 + buildSrc/src/main/kotlin/JvmConfig.kt | 38 ++++++--- .../src/main/kotlin/KtorBuildProperties.kt | 28 +------ .../client/engine/cio/ConnectErrorsTest.kt | 32 ++++---- .../ktor-client-auth/build.gradle.kts | 10 +-- .../ktor-client-bom-remover/build.gradle.kts | 4 +- .../ktor-client-encoding/build.gradle.kts | 4 +- .../ktor-client-json/build.gradle.kts | 6 +- .../ktor-client-websockets/build.gradle.kts | 6 +- .../ktor-client-tests/build.gradle.kts | 8 -- .../tests/plugins/CookiesIntegrationTests.kt | 6 +- .../client/tests/utils/ClientLoaderJvm.kt | 23 ++++-- ktor-java-modules-test/build.gradle.kts | 13 +-- .../tls/certificates/CertificatesTest.kt | 16 ++-- .../tls/certificates/KeyStoreBuilderTest.kt | 14 ++-- .../tests/server/cio/CIOWebSocketTestJvm.kt | 19 ++--- .../testing/suites/ClientCertTestSuite.kt | 13 ++- .../server/testing/suites/ContentTestSuite.kt | 14 ++-- .../testing/suites/SustainabilityTestSuite.kt | 12 +-- .../server/tomcat/jakarta/TomcatEngineTest.kt | 24 ++++-- .../tomcat/jakarta/TomcatWebSocketTest.kt | 7 +- .../jvm/src/io/ktor/junit/RetrySupport.kt | 81 ------------------- .../tests/utils/DeflaterReadChannelTest.kt | 19 +++-- .../io/ktor/tests/utils/FileChannelTest.kt | 4 +- settings.gradle.kts | 1 + 25 files changed, 150 insertions(+), 253 deletions(-) delete mode 100644 ktor-shared/ktor-junit/jvm/src/io/ktor/junit/RetrySupport.kt diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index fafb519c2a2..ed0a0634d2b 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -7,6 +7,7 @@ pluginManagement { } plugins { + id("org.gradle.toolchains.foojay-resolver-convention") id("conventions-dependency-resolution-management") } diff --git a/buildSrc/src/main/kotlin/JvmConfig.kt b/buildSrc/src/main/kotlin/JvmConfig.kt index e24500fa1ae..345c64c5452 100644 --- a/buildSrc/src/main/kotlin/JvmConfig.kt +++ b/buildSrc/src/main/kotlin/JvmConfig.kt @@ -11,7 +11,7 @@ import org.gradle.kotlin.dsl.* import org.jetbrains.kotlin.gradle.targets.jvm.tasks.* fun Project.configureJvm() { - val jdk = when (name) { + val compileJdk = when (name) { in jdk11Modules -> 11 else -> 8 } @@ -56,7 +56,7 @@ fun Project.configureJvm() { maxHeapSize = "2g" exclude("**/*StressTest*") useJUnitPlatform() - configureJavaLauncher(jdk) + configureJavaToolchain(compileJdk) } tasks.register("stressTest") { @@ -69,7 +69,7 @@ fun Project.configureJvm() { systemProperty("enable.stress.tests", "true") include("**/*StressTest*") useJUnitPlatform() - configureJavaLauncher(jdk) + configureJavaToolchain(compileJdk) } val configuredVersion: String by rootProject.extra @@ -86,15 +86,31 @@ fun Project.configureJvm() { } /** - * JUnit 5 requires Java 11+ + * On local machine use for tests the JDK used for compilation. + * On CI use the default JDK. */ -fun Test.configureJavaLauncher(jdk: Int) { - if (jdk < 11) { - val javaToolchains = project.extensions.getByType() - val customLauncher = javaToolchains.launcherFor { - languageVersion = JavaLanguageVersion.of("11") - } - javaLauncher = customLauncher +private fun Test.configureJavaToolchain(compileJdk: Int) { + // JUnit 5 requires JDK 11+ + val testJdk = (if (CI) currentJdk else compileJdk).coerceAtLeast(11) + val javaToolchains = project.the() + + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(testJdk) + } + + if (testJdk >= 16) { + // Allow reflective access from tests + jvmArgs( + "--add-opens=java.base/java.net=ALL-UNNAMED", + "--add-opens=java.base/java.time=ALL-UNNAMED", + "--add-opens=java.base/java.util=ALL-UNNAMED", + ) + } + + if (testJdk >= 21) { + // coroutines-debug use dynamic agent loading under the hood. + // Remove as soon as the issue is fixed: https://youtrack.jetbrains.com/issue/KT-62096/ + jvmArgs("-XX:+EnableDynamicAgentLoading") } } diff --git a/buildSrc/src/main/kotlin/KtorBuildProperties.kt b/buildSrc/src/main/kotlin/KtorBuildProperties.kt index 183950b200f..dca652dce69 100644 --- a/buildSrc/src/main/kotlin/KtorBuildProperties.kt +++ b/buildSrc/src/main/kotlin/KtorBuildProperties.kt @@ -3,21 +3,6 @@ */ import org.gradle.api.* -import org.gradle.api.tasks.testing.* -import org.gradle.jvm.toolchain.* -import org.gradle.kotlin.dsl.* - -/* - * Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -private val java_version: String = System.getProperty("java.version", "8.0.0") - -private val versionComponents = java_version - .split(".") - .take(2) - .filter { it.isNotBlank() } - .map { Integer.parseInt(it) } val IDEA_ACTIVE: Boolean = System.getProperty("idea.active") == "true" @@ -30,7 +15,7 @@ val HOST_NAME = when { else -> error("Unknown os name `$OS_NAME`") } -val currentJdk = if (versionComponents[0] == 1) versionComponents[1] else versionComponents[0] +val currentJdk = JavaVersion.current().majorVersion.toInt() val jdk11Modules = listOf( "ktor-client-java", @@ -40,14 +25,3 @@ val jdk11Modules = listOf( "ktor-server-jetty-test-http2-jakarta", "ktor-server-tomcat-jakarta", ) - -fun Project.useJdkVersionForJvmTests(version: Int) { - tasks.named("jvmTest") { - val javaToolchains = project.extensions.getByType() - javaLauncher.set( - javaToolchains.launcherFor { - languageVersion.set(JavaLanguageVersion.of(version)) - } - ) - } -} diff --git a/ktor-client/ktor-client-cio/jvm/test/io/ktor/client/engine/cio/ConnectErrorsTest.kt b/ktor-client/ktor-client-cio/jvm/test/io/ktor/client/engine/cio/ConnectErrorsTest.kt index a401f5703e7..b8da7fc3156 100644 --- a/ktor-client/ktor-client-cio/jvm/test/io/ktor/client/engine/cio/ConnectErrorsTest.kt +++ b/ktor-client/ktor-client-cio/jvm/test/io/ktor/client/engine/cio/ConnectErrorsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.client.engine.cio @@ -11,26 +11,26 @@ import io.ktor.client.plugins.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* -import io.ktor.junit.* import io.ktor.network.tls.certificates.* import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.response.* import io.ktor.server.routing.* -import io.ktor.utils.io.* -import kotlinx.coroutines.* -import kotlinx.coroutines.debug.junit5.* -import org.junit.jupiter.api.extension.* -import java.io.* -import java.net.* -import java.util.concurrent.* -import javax.net.ssl.* -import kotlin.concurrent.* +import kotlinx.coroutines.debug.junit5.CoroutinesTimeout +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout +import java.io.File +import java.net.ConnectException +import java.net.ServerSocket +import java.net.SocketException +import java.util.concurrent.TimeUnit +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager +import kotlin.concurrent.thread import kotlin.test.* @CoroutinesTimeout(5 * 60 * 1000) -@ExtendWith(RetrySupport::class) class ConnectErrorsTest { private val serverSocket = ServerSocket(0, 1) @@ -40,7 +40,7 @@ class ConnectErrorsTest { serverSocket.close() } - @RetryableTest(3) + @Test fun testConnectAfterConnectionErrors(): Unit = runBlocking { val client = HttpClient(CIO) { engine { @@ -84,7 +84,7 @@ class ConnectErrorsTest { } } - @RetryableTest(3) + @Test fun testResponseWithNoLengthChunkedAndConnectionClosedWithHttp10(): Unit = runBlocking { val client = HttpClient(CIO) @@ -110,7 +110,7 @@ class ConnectErrorsTest { } } - @RetryableTest(3) + @Test fun testResponseErrorWithNoLengthChunkedAndConnectionClosedWithHttp11(): Unit = runBlocking { val client = HttpClient(CIO) @@ -183,7 +183,7 @@ class ConnectErrorsTest { } } - @RetryableTest(3) + @Test fun testLateServerStart(): Unit = runBlocking { val keyStoreFile = File("build/temp.jks") val keyStore = generateCertificate(keyStoreFile, algorithm = "SHA256withECDSA", keySizeInBits = 256) diff --git a/ktor-client/ktor-client-plugins/ktor-client-auth/build.gradle.kts b/ktor-client/ktor-client-plugins/ktor-client-auth/build.gradle.kts index 84423b6effb..722e3355ec1 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-auth/build.gradle.kts +++ b/ktor-client/ktor-client-plugins/ktor-client-auth/build.gradle.kts @@ -1,11 +1,9 @@ -import test.server.* - /* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import test.server.* description = "Ktor client Auth support" apply() - -useJdkVersionForJvmTests(11) diff --git a/ktor-client/ktor-client-plugins/ktor-client-bom-remover/build.gradle.kts b/ktor-client/ktor-client-plugins/ktor-client-bom-remover/build.gradle.kts index 29e577c32ef..d57a69d5e2d 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-bom-remover/build.gradle.kts +++ b/ktor-client/ktor-client-plugins/ktor-client-bom-remover/build.gradle.kts @@ -1,7 +1,5 @@ /* - * Copyright 2014-2020 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ description = "Ktor client Byte Order Mark support" - -useJdkVersionForJvmTests(11) diff --git a/ktor-client/ktor-client-plugins/ktor-client-encoding/build.gradle.kts b/ktor-client/ktor-client-plugins/ktor-client-encoding/build.gradle.kts index 0f28e3f977b..540cc733c06 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-encoding/build.gradle.kts +++ b/ktor-client/ktor-client-plugins/ktor-client-encoding/build.gradle.kts @@ -1,9 +1,7 @@ /* - * Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -useJdkVersionForJvmTests(11) - apply() kotlin.sourceSets { diff --git a/ktor-client/ktor-client-plugins/ktor-client-json/build.gradle.kts b/ktor-client/ktor-client-plugins/ktor-client-json/build.gradle.kts index 2b682c2da70..f30cd80004d 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-json/build.gradle.kts +++ b/ktor-client/ktor-client-plugins/ktor-client-json/build.gradle.kts @@ -1,6 +1,6 @@ /* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ description = "Ktor client JSON support" @@ -29,5 +29,3 @@ kotlin { } } } - -useJdkVersionForJvmTests(11) diff --git a/ktor-client/ktor-client-plugins/ktor-client-websockets/build.gradle.kts b/ktor-client/ktor-client-plugins/ktor-client-websockets/build.gradle.kts index e0c3f5a09e5..0cbe00d537c 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-websockets/build.gradle.kts +++ b/ktor-client/ktor-client-plugins/ktor-client-websockets/build.gradle.kts @@ -1,6 +1,6 @@ /* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ kotlin.sourceSets { commonTest { @@ -9,5 +9,3 @@ kotlin.sourceSets { } } } - -useJdkVersionForJvmTests(11) diff --git a/ktor-client/ktor-client-tests/build.gradle.kts b/ktor-client/ktor-client-tests/build.gradle.kts index bb37cd747f9..2de670961aa 100644 --- a/ktor-client/ktor-client-tests/build.gradle.kts +++ b/ktor-client/ktor-client-tests/build.gradle.kts @@ -4,10 +4,6 @@ import test.server.* -/* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ - description = "Common tests for client" plugins { @@ -16,8 +12,6 @@ plugins { apply() -val osName = System.getProperty("os.name") - kotlin.sourceSets { commonMain { dependencies { @@ -105,5 +99,3 @@ kotlin.sourceSets { } } } - -useJdkVersionForJvmTests(11) diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt index 6d32ae4c905..6d8b63216a5 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/CookiesIntegrationTests.kt @@ -1,6 +1,6 @@ /* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ package io.ktor.client.tests.plugins @@ -186,7 +186,7 @@ class CookiesIntegrationTests : ClientLoader() { } @Test - fun testCookiesWithWrongValue() = clientTests(listOf("js", "Darwin", "DarwinLegacy", "WinHttp")) { + fun testCookiesWithWrongValue() = clientTests(listOf("Js", "Darwin", "DarwinLegacy", "WinHttp", "Java")) { config { install(HttpCookies) } diff --git a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt index 07ee187b3b0..53f655685b0 100644 --- a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt +++ b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/utils/ClientLoaderJvm.kt @@ -8,8 +8,11 @@ import io.ktor.client.* import io.ktor.client.engine.* import io.ktor.util.reflect.* import io.ktor.utils.io.* -import kotlinx.coroutines.* -import kotlinx.coroutines.debug.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.debug.CoroutineInfo +import kotlinx.coroutines.debug.DebugProbes +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout import java.util.* import kotlin.time.Duration.Companion.seconds @@ -44,10 +47,17 @@ actual abstract class ClientLoader actual constructor(val timeoutSeconds: Int) { } } - fun shouldSkip(engine: HttpClientEngineContainer, skipEngines: List, onlyWithEngine: String?): Boolean = - skipEngines.any { shouldSkip(engine.toString(), it, onlyWithEngine) } + private fun shouldSkip( + engine: HttpClientEngineContainer, + skipEngines: List, + onlyWithEngine: String? + ): Boolean { + val engineName = engine.toString() + return onlyWithEngine != null && !onlyWithEngine.equals(engineName, ignoreCase = true) || + skipEngines.any { shouldSkip(engineName, it) } + } - fun shouldSkip(engineName: String, skipEngine: String, onlyWithEngine: String?): Boolean { + private fun shouldSkip(engineName: String, skipEngine: String): Boolean { val locale = Locale.getDefault() val skipEngineArray = skipEngine.lowercase(locale).split(":") @@ -61,9 +71,8 @@ actual abstract class ClientLoader actual constructor(val timeoutSeconds: Int) { val engineShouldBeSkipped = "*" == skipEngineName || engineName.lowercase(locale) == skipEngineName.lowercase( locale ) - val notOnlyEngine = onlyWithEngine != null && engineName.lowercase(locale) != onlyWithEngine.lowercase(locale) - return (engineShouldBeSkipped && platformShouldBeSkipped) || notOnlyEngine + return engineShouldBeSkipped && platformShouldBeSkipped } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/ktor-java-modules-test/build.gradle.kts b/ktor-java-modules-test/build.gradle.kts index af1835d4aa1..6899d006313 100644 --- a/ktor-java-modules-test/build.gradle.kts +++ b/ktor-java-modules-test/build.gradle.kts @@ -40,23 +40,18 @@ tasks.named("compileJava") { classpath = emptyClasspath } } + +// Here should be specified the latest LTS version java { toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion = JavaLanguageVersion.of(21) } } dependencies { rootProject.subprojects .filter { it.hasJavaModule } - .map { - generateSequence(it) { it.parent } - .toList() - .dropLast(1) - .reversed() - .joinToString(":", prefix = ":") { it.name } - } - .forEach { api(project(it)) } + .forEach { implementation(project(it.path)) } } internal val Project.hasJavaModule: Boolean diff --git a/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/CertificatesTest.kt b/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/CertificatesTest.kt index 3544ec2afdc..8ef6898e18e 100644 --- a/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/CertificatesTest.kt +++ b/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/CertificatesTest.kt @@ -1,14 +1,16 @@ /* - * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.network.tls.certificates import io.ktor.network.tls.extensions.* -import java.io.* -import java.time.temporal.* -import javax.security.auth.x500.* -import kotlin.test.* +import java.io.File +import java.time.temporal.ChronoUnit +import javax.security.auth.x500.X500Principal +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals class CertificatesTest { @@ -137,10 +139,10 @@ class CertificatesTest { algorithm = HashAndSign(HashAlgorithm.SHA256, SignatureAlgorithm.ECDSA).name, keyAlias = "customAlias", keyPassword = "customPassword", - keySizeInBits = 128, + keySizeInBits = 256, ) - assertHasPrivateKey(keyStore, alias = "customAlias", password = "customPassword", algorithm = "EC", size = 128) + assertHasPrivateKey(keyStore, alias = "customAlias", password = "customPassword", algorithm = "EC", size = 256) assertHasX509Certificate(keyStore, alias = "customAlias", algorithm = "SHA256withECDSA") } diff --git a/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/KeyStoreBuilderTest.kt b/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/KeyStoreBuilderTest.kt index 3539f2257a0..a68f1daa825 100644 --- a/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/KeyStoreBuilderTest.kt +++ b/ktor-network/ktor-network-tls/ktor-network-tls-certificates/jvm/test/io/ktor/network/tls/certificates/KeyStoreBuilderTest.kt @@ -1,14 +1,16 @@ /* - * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.network.tls.certificates import io.ktor.network.tls.extensions.* import java.net.InetAddress -import java.time.temporal.* -import javax.security.auth.x500.* -import kotlin.test.* +import java.time.temporal.ChronoUnit +import javax.security.auth.x500.X500Principal +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals class KeyStoreBuilderTest { @@ -47,11 +49,11 @@ class KeyStoreBuilderTest { hash = HashAlgorithm.SHA256 sign = SignatureAlgorithm.ECDSA password = "keyPass" - keySizeInBits = 128 + keySizeInBits = 256 } } - assertHasPrivateKey(keyStore, alias = "someKey", password = "keyPass", algorithm = "EC", size = 128) + assertHasPrivateKey(keyStore, alias = "someKey", password = "keyPass", algorithm = "EC", size = 256) assertHasX509Certificate(keyStore, alias = "someKey", algorithm = "SHA256withECDSA") } diff --git a/ktor-server/ktor-server-cio/jvm/test/io/ktor/tests/server/cio/CIOWebSocketTestJvm.kt b/ktor-server/ktor-server-cio/jvm/test/io/ktor/tests/server/cio/CIOWebSocketTestJvm.kt index 8f18918fc79..5fddab36917 100644 --- a/ktor-server/ktor-server-cio/jvm/test/io/ktor/tests/server/cio/CIOWebSocketTestJvm.kt +++ b/ktor-server/ktor-server-cio/jvm/test/io/ktor/tests/server/cio/CIOWebSocketTestJvm.kt @@ -1,21 +1,23 @@ /* - * Copyright 2014-2023 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.tests.server.cio import io.ktor.client.* import io.ktor.client.plugins.websocket.* -import io.ktor.junit.* import io.ktor.server.cio.* import io.ktor.server.test.base.* -import io.ktor.server.testing.* -import kotlinx.coroutines.* -import kotlinx.coroutines.debug.* -import org.junit.jupiter.api.extension.* -import kotlin.test.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.debug.DebugProbes +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertFails +import kotlin.test.assertTrue -@ExtendWith(RetrySupport::class) class CIOWebSocketTestJvm : EngineTestBase(CIO) { init { @@ -24,7 +26,6 @@ class CIOWebSocketTestJvm : EngineTestBase( val engine: ApplicationEngineFactory ) { @@ -41,7 +39,6 @@ abstract class ClientCertTestSuite( hostFactory: ApplicationEngineFactory ) : EngineTestBase(hostFactory) { @@ -422,7 +421,6 @@ abstract class ContentTestSuite( hostFactory: ApplicationEngineFactory ) : EngineTestBase(hostFactory) { @@ -256,7 +253,6 @@ abstract class SustainabilityTestSuite(Tomcat) { @@ -63,6 +69,10 @@ class TomcatHttpServerCommonTest : @Ignore // KTOR-6480 override fun testErrorInBodyClosesConnectionWithContentLength() {} + + @Ignore + override fun testHeadRequest() { + } } class TomcatHttpServerJvmTest : diff --git a/ktor-server/ktor-server-tomcat-jakarta/jvm/test/io/ktor/tests/server/tomcat/jakarta/TomcatWebSocketTest.kt b/ktor-server/ktor-server-tomcat-jakarta/jvm/test/io/ktor/tests/server/tomcat/jakarta/TomcatWebSocketTest.kt index 4a0539deb52..0f0ce411f56 100644 --- a/ktor-server/ktor-server-tomcat-jakarta/jvm/test/io/ktor/tests/server/tomcat/jakarta/TomcatWebSocketTest.kt +++ b/ktor-server/ktor-server-tomcat-jakarta/jvm/test/io/ktor/tests/server/tomcat/jakarta/TomcatWebSocketTest.kt @@ -1,16 +1,13 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.tests.server.tomcat.jakarta -import io.ktor.junit.* import io.ktor.server.testing.suites.* import io.ktor.server.tomcat.jakarta.* -import org.junit.jupiter.api.extension.ExtendWith -import kotlin.test.* +import kotlin.test.Ignore -@ExtendWith(RetrySupport::class) class TomcatWebSocketTest : WebSocketEngineSuite(Tomcat) { diff --git a/ktor-shared/ktor-junit/jvm/src/io/ktor/junit/RetrySupport.kt b/ktor-shared/ktor-junit/jvm/src/io/ktor/junit/RetrySupport.kt deleted file mode 100644 index 7bbfcb29036..00000000000 --- a/ktor-shared/ktor-junit/jvm/src/io/ktor/junit/RetrySupport.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.junit - -import org.junit.jupiter.api.* -import org.junit.jupiter.api.extension.* -import java.util.stream.* -import kotlin.streams.* - -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.ANNOTATION_CLASS) -@TestTemplate -@Retention -annotation class RetryableTest(val retries: Int = 1, val delay: Long = 1_000L) - -class RetrySupport : TestTemplateInvocationContextProvider { - - override fun supportsTestTemplate(context: ExtensionContext): Boolean { - return context.testMethod.isPresent && context.testMethod.get().isAnnotationPresent(RetryableTest::class.java) - } - - override fun provideTestTemplateInvocationContexts( - context: ExtensionContext - ): Stream { - val testMethod = context.testMethod.get() - val annotation = testMethod.getAnnotation(RetryableTest::class.java) - - return RetryableTestContext(annotation.retries, annotation.delay) - .getInvocationContexts() - .asStream() - } - - class RetryableTestContext(private val retries: Int, private val delay: Long) { - - private var lastException: Throwable? = null - - fun getInvocationContexts(): Sequence = - when (retries) { - 0 -> sequenceOf(object : TestTemplateInvocationContext {}) - 1 -> sequenceOf(FirstAttemptContext(), LastAttemptContext()) - else -> sequenceOf(FirstAttemptContext()) + generateSequence(::IntermediateRetryContext).take( - retries - 1 - ) + sequenceOf(LastAttemptContext()) - } - - inner class FirstAttemptContext : TestTemplateInvocationContext { - override fun getDisplayName(invocationIndex: Int) = "First attempt" - override fun getAdditionalExtensions() = mutableListOf(SetLastException()) - } - - open inner class IntermediateRetryContext : TestTemplateInvocationContext { - override fun getDisplayName(invocationIndex: Int): String = "Retry #${invocationIndex - 1}" - override fun getAdditionalExtensions() = mutableListOf( - OnlyIfPreviousFailed(), - SetLastException() - ) - } - - inner class LastAttemptContext : IntermediateRetryContext() { - override fun getAdditionalExtensions(): MutableList = - mutableListOf(OnlyIfPreviousFailed()) - } - - inner class OnlyIfPreviousFailed : ExecutionCondition { - override fun evaluateExecutionCondition(context: ExtensionContext?): ConditionEvaluationResult = - when (lastException) { - null -> ConditionEvaluationResult.disabled("Previous attempt passed") - else -> ConditionEvaluationResult.enabled("Previous attempt failed") - } - } - - inner class SetLastException : TestExecutionExceptionHandler { - override fun handleTestExecutionException(context: ExtensionContext?, throwable: Throwable?) { - throwable?.printStackTrace() - Thread.sleep(delay) - lastException = throwable - } - } - } -} diff --git a/ktor-utils/jvm/test/io/ktor/tests/utils/DeflaterReadChannelTest.kt b/ktor-utils/jvm/test/io/ktor/tests/utils/DeflaterReadChannelTest.kt index d9a9567898d..e3093040976 100644 --- a/ktor-utils/jvm/test/io/ktor/tests/utils/DeflaterReadChannelTest.kt +++ b/ktor-utils/jvm/test/io/ktor/tests/utils/DeflaterReadChannelTest.kt @@ -1,25 +1,24 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.tests.utils -import io.ktor.junit.* import io.ktor.util.* import io.ktor.util.cio.* import io.ktor.utils.io.* import io.ktor.utils.io.jvm.javaio.* import kotlinx.coroutines.* -import kotlinx.coroutines.debug.junit5.* -import org.junit.jupiter.api.extension.* -import java.io.* -import java.nio.* -import java.util.zip.* -import kotlin.random.* +import kotlinx.coroutines.debug.junit5.CoroutinesTimeout +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.nio.ByteBuffer +import java.util.zip.GZIPInputStream +import kotlin.random.Random import kotlin.test.* @CoroutinesTimeout(60_000) -@ExtendWith(RetrySupport::class) class DeflaterReadChannelTest : CoroutineScope { private val testJob = Job() override val coroutineContext get() = testJob + Dispatchers.Unconfined @@ -114,7 +113,7 @@ class DeflaterReadChannelTest : CoroutineScope { testWriteChannel(text, asyncOf(text)) } - @RetryableTest(3) + @Test fun testFaultyGzippedBiggerThan8k() { val text = buildString { for (i in 1..16 * 1024 * 1024) { diff --git a/ktor-utils/jvm/test/io/ktor/tests/utils/FileChannelTest.kt b/ktor-utils/jvm/test/io/ktor/tests/utils/FileChannelTest.kt index fbcb2a273f8..3a32b8dc2d9 100644 --- a/ktor-utils/jvm/test/io/ktor/tests/utils/FileChannelTest.kt +++ b/ktor-utils/jvm/test/io/ktor/tests/utils/FileChannelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ package io.ktor.tests.utils @@ -15,7 +15,6 @@ import java.io.* import kotlin.test.* import kotlin.test.Test -@ExtendWith(RetrySupport::class) class FileChannelTest { private val sandbox = File("build/files") private lateinit var temp: File @@ -73,7 +72,6 @@ class FileChannelTest { assertEquals(byteArrayOf(7, 8, 9).toList(), temp.readChannel().toInputStream().use { it.readBytes().toList() }) } - @RetryableTest // random failures on Windows agent @Test fun `readChannel should not lock file pre read`() { // Arrange diff --git a/settings.gradle.kts b/settings.gradle.kts index 12a3d5c139e..c27d5af3516 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,6 +7,7 @@ pluginManagement { } plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" id("conventions-dependency-resolution-management") id("conventions-develocity") } From db760a2fe97a9838f95f0d2b3e142b74898404f6 Mon Sep 17 00:00:00 2001 From: Brian McNamara Date: Mon, 25 Nov 2024 00:04:53 -0800 Subject: [PATCH 6/6] Fix Unix Sockets on Windows (#4479) * Fix Unix Sockets on Windows * Rename method to remove tcp reference * rerun ci --- .../jvm/src/io/ktor/network/sockets/SocketImpl.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ktor-network/jvm/src/io/ktor/network/sockets/SocketImpl.kt b/ktor-network/jvm/src/io/ktor/network/sockets/SocketImpl.kt index 2125712a766..2c30689c5b0 100644 --- a/ktor-network/jvm/src/io/ktor/network/sockets/SocketImpl.kt +++ b/ktor-network/jvm/src/io/ktor/network/sockets/SocketImpl.kt @@ -50,7 +50,7 @@ internal class SocketImpl( if (channel.finishConnect()) { // TCP has a well known self-connect problem, which client can connect to the client itself // without any program listen on the port. - if (selfConnect()) { + if (inetSelfConnect()) { if (java7NetworkApisAvailable) { channel.close() } else { @@ -74,7 +74,7 @@ internal class SocketImpl( interestOp(SelectInterest.CONNECT, state) } - private fun selfConnect(): Boolean { + private fun inetSelfConnect(): Boolean { val localAddress = if (java7NetworkApisAvailable) { channel.localAddress } else { @@ -93,6 +93,10 @@ internal class SocketImpl( val localInetSocketAddress = localAddress as? java.net.InetSocketAddress val remoteInetSocketAddress = remoteAddress as? java.net.InetSocketAddress + if (localInetSocketAddress == null && remoteInetSocketAddress == null) { + return false + } + val localHostAddress = localInetSocketAddress?.address?.hostAddress ?: "" val remoteHostAddress = remoteInetSocketAddress?.address?.hostAddress ?: "" val isRemoteAnyLocalAddress = remoteInetSocketAddress?.address?.isAnyLocalAddress ?: false