From b229d20b81babf85397fb68006363d0c43a48b1d Mon Sep 17 00:00:00 2001 From: Joffrey Bion Date: Sun, 26 Jan 2025 17:41:30 +0100 Subject: [PATCH] [ktor] Put back the workaround for KTOR-6883 but for Ktor WASM/JS tests this time (artificial delay)" This reverts commit bb600448 --- .../KtorClientTestSuite.kt | 4 ++- .../KtorMppWebSocketClientTest.kt | 7 +++--- .../test/WebSocketClientTestSuite.kt | 13 +++++++--- .../test/server/EchoWebSocketServer.kt | 25 ++++++++++++++++--- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorClientTestSuite.kt b/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorClientTestSuite.kt index 410507a00..e7c1ff019 100644 --- a/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorClientTestSuite.kt +++ b/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorClientTestSuite.kt @@ -5,11 +5,13 @@ import io.ktor.client.engine.* import io.ktor.client.plugins.websocket.* import org.hildan.krossbow.websocket.* import org.hildan.krossbow.websocket.test.* +import kotlin.time.Duration abstract class KtorClientTestSuite( supportsStatusCodes: Boolean, shouldTestNegotiatedSubprotocol: Boolean = true, -) : WebSocketClientTestSuite(supportsStatusCodes, shouldTestNegotiatedSubprotocol) { + headersTestDelay: Duration? = null, +) : WebSocketClientTestSuite(supportsStatusCodes, shouldTestNegotiatedSubprotocol, headersTestDelay) { override fun provideClient(): WebSocketClient = KtorWebSocketClient( HttpClient(provideEngine()) { install(WebSockets) }, diff --git a/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorMppWebSocketClientTest.kt b/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorMppWebSocketClientTest.kt index 537e994e3..f173d32a2 100644 --- a/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorMppWebSocketClientTest.kt +++ b/krossbow-websocket-ktor/src/commonTest/kotlin/org.hildan.krossbow.websocket.ktor/KtorMppWebSocketClientTest.kt @@ -4,7 +4,7 @@ import io.ktor.client.* import io.ktor.client.plugins.websocket.* import org.hildan.krossbow.websocket.* import org.hildan.krossbow.websocket.test.* -import kotlin.test.Test +import kotlin.time.Duration.Companion.milliseconds // WinHttp: error is too generic and doesn't differ per status code // JS browser: cannot support status codes for security reasons @@ -21,6 +21,8 @@ class KtorMppWebSocketClientTest : WebSocketClientTestSuite( // Just to be sure we don't attempt to test this with the Java or JS engines // See https://youtrack.jetbrains.com/issue/KTOR-6970 shouldTestNegotiatedSubprotocol = false, + // workaround for https://youtrack.jetbrains.com/issue/KTOR-6883 + headersTestDelay = 200.milliseconds.takeIf { currentPlatform() == Platform.Js.NodeJs }, ) { override fun provideClient(): WebSocketClient = KtorWebSocketClient( HttpClient { @@ -32,7 +34,4 @@ class KtorMppWebSocketClientTest : WebSocketClientTestSuite( install(WebSockets) }, ) - - @Test - fun ets() {} } diff --git a/krossbow-websocket-test/src/commonMain/kotlin/org/hildan/krossbow/websocket/test/WebSocketClientTestSuite.kt b/krossbow-websocket-test/src/commonMain/kotlin/org/hildan/krossbow/websocket/test/WebSocketClientTestSuite.kt index b92b1e43a..491a4c9aa 100644 --- a/krossbow-websocket-test/src/commonMain/kotlin/org/hildan/krossbow/websocket/test/WebSocketClientTestSuite.kt +++ b/krossbow-websocket-test/src/commonMain/kotlin/org/hildan/krossbow/websocket/test/WebSocketClientTestSuite.kt @@ -12,6 +12,7 @@ import kotlin.time.Duration.Companion.seconds abstract class WebSocketClientTestSuite( private val supportsStatusCodes: Boolean = true, private val shouldTestNegotiatedSubprotocol: Boolean = true, + private val headersTestDelay: Duration? = null, ) { abstract fun provideClient(): WebSocketClient @@ -249,14 +250,20 @@ abstract class WebSocketClientTestSuite( fun testHandshakeCustomHeaders() = runTestRealTime { if (wsClient.supportsCustomHeaders) { println("Connecting with agent $agent to ${testServerConfig.wsUrl}/sendHandshakeHeaders") + // workaround for https://youtrack.jetbrains.com/issue/KTOR-6883 + val extraParams = if (headersTestDelay != null) mapOf("scheduleDelay" to headersTestDelay.toString()) else emptyMap() val connection = wsClient.connect( - url = testUrl(path = "/sendHandshakeHeaders", testCaseName = "testHandshakeCustomHeaders"), + url = testUrl( + path = "/sendHandshakeHeaders", + testCaseName = "testHandshakeCustomHeaders", + otherParams = extraParams, + ), headers = mapOf("My-Header-1" to "my-value-1", "My-Header-2" to "my-value-2"), ) println("Connected with agent $agent to ${testServerConfig.wsUrl}/sendHandshakeHeaders") try { // for some reason, this can be pretty long with the Ktor/JS client in nodeJS tests on macOS - val echoedHeadersFrame = connection.expectTextFrame("header info frame") + val echoedHeadersFrame = connection.expectTextFrame("header info frame", 50.seconds) val headers = echoedHeadersFrame.text.lines() assertContains(headers, "My-Header-1=my-value-1") assertContains(headers, "My-Header-2=my-value-2") @@ -318,7 +325,7 @@ private fun runTestRealTime( testBody: suspend CoroutineScope.() -> Unit, ) = runTest(timeout = timeout) { // Switches to a regular dispatcher to avoid the virtual time from runTest. - // We also use limitedParallelism to keep things deterministic + // We also use limitedParallelism to keep things deterministic withContext(Dispatchers.Default.limitedParallelism(1) + context) { testBody() } diff --git a/test-server/websocket-test-server/src/main/kotlin/org/hildan/krossbow/test/server/EchoWebSocketServer.kt b/test-server/websocket-test-server/src/main/kotlin/org/hildan/krossbow/test/server/EchoWebSocketServer.kt index 640d1ae53..7e0186faa 100644 --- a/test-server/websocket-test-server/src/main/kotlin/org/hildan/krossbow/test/server/EchoWebSocketServer.kt +++ b/test-server/websocket-test-server/src/main/kotlin/org/hildan/krossbow/test/server/EchoWebSocketServer.kt @@ -8,6 +8,7 @@ import org.java_websocket.protocols.* import org.java_websocket.server.* import java.net.* import java.nio.* +import kotlin.time.Duration private val Draft6455Default = Draft_6455() private val Draft6455WithStomp12 = Draft_6455(emptyList(), listOf(Protocol("v12.stomp"))) @@ -16,20 +17,34 @@ internal class EchoWebSocketServer(port: Int = 0) : WebSocketServer( InetSocketAddress(port), listOf(Draft6455WithStomp12, Draft6455Default), ) { + private val delayedHeadersScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) + override fun onStart() { } override fun onOpen(conn: WebSocket, handshake: ClientHandshake) { val uri = URI.create(handshake.resourceDescriptor) if (uri.path == "/sendHandshakeHeaders") { - conn.sendMessageWithHeaders(handshake) + val queryParams = uri.queryAsMap() + val scheduleDelay = queryParams["scheduleDelay"]?.let(Duration::parse) + conn.sendMessageWithHeaders(handshake, scheduleDelay) } } - private fun WebSocket.sendMessageWithHeaders(handshake: ClientHandshake) { + private fun WebSocket.sendMessageWithHeaders(handshake: ClientHandshake, scheduleDelay: Duration? = null) { val headerNames = handshake.iterateHttpFields().asSequence().toList() val headersData = headerNames.joinToString("\n") { "$it=${handshake.getFieldValue(it)}" } - send(headersData) + if (scheduleDelay != null) { + // necessary due to https://youtrack.jetbrains.com/issue/KTOR-6883 + println("Scheduling message with headers in $scheduleDelay") + delayedHeadersScope.launch { + delay(scheduleDelay) + send(headersData) + println("Headers frame sent!") + } + } else { + send(headersData) + } } override fun onMessage(conn: WebSocket, message: String?) { @@ -60,3 +75,7 @@ internal class EchoWebSocketServer(port: Int = 0) : WebSocketServer( port } } + +private fun URI.queryAsMap() = query.split("&") + .map { it.split("=") } + .associate { it[0] to it[1] }