Skip to content

Commit

Permalink
[ktor] Put back the workaround for KTOR-6883 but for Ktor WASM/JS tes…
Browse files Browse the repository at this point in the history
…ts this time (artificial delay)"

This reverts commit bb60044
  • Loading branch information
joffrey-bion committed Jan 27, 2025
1 parent bd227e4 commit 43f5f92
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(
statusCodeSupport: StatusCodeSupport = StatusCodeSupport.All,
shouldTestNegotiatedSubprotocol: Boolean = true,
) : WebSocketClientTestSuite(statusCodeSupport, shouldTestNegotiatedSubprotocol) {
headersTestDelay: Duration? = null,
) : WebSocketClientTestSuite(statusCodeSupport, shouldTestNegotiatedSubprotocol, headersTestDelay) {

override fun provideClient(): WebSocketClient = KtorWebSocketClient(
HttpClient(provideEngine()) { install(WebSockets) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +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.time.Duration.Companion.milliseconds

private val Platform.statusCodeSupport: StatusCodeSupport
get() = when (this) {
Expand All @@ -26,6 +27,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 (NOT fixed for WASM)
headersTestDelay = 200.milliseconds.takeIf { currentPlatform() == Platform.WasmJs.NodeJs },
) {
override fun provideClient(): WebSocketClient = KtorWebSocketClient(
HttpClient {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.hildan.krossbow.websocket.ktor

import io.ktor.client.engine.*
import io.ktor.client.engine.js.*
import org.hildan.krossbow.websocket.test.*
import kotlin.time.Duration.Companion.milliseconds

class KtorWasmJsWebSocketClientTest : KtorClientTestSuite(
// The browser cannot reveal status codes for security reasons
statusCodeSupport = if (currentPlatform() is Platform.WasmJs.Browser) StatusCodeSupport.None else StatusCodeSupport.All,
// See https://youtrack.jetbrains.com/issue/KTOR-6970
shouldTestNegotiatedSubprotocol = false,
// workaround for https://youtrack.jetbrains.com/issue/KTOR-6883 (NOT fixed for WASM)
headersTestDelay = 200.milliseconds.takeIf { currentPlatform() == Platform.WasmJs.NodeJs },
) {
override fun provideEngine(): HttpClientEngineFactory<*> = Js
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ sealed interface StatusCodeSupport {
abstract class WebSocketClientTestSuite(
private val statusCodeSupport: StatusCodeSupport = StatusCodeSupport.All,
private val shouldTestNegotiatedSubprotocol: Boolean = true,
private val headersTestDelay: Duration? = null,
) {
abstract fun provideClient(): WebSocketClient

Expand Down Expand Up @@ -205,8 +206,14 @@ abstract class WebSocketClientTestSuite(

@Test
fun testHandshakeSubprotocolHeader_noProtocol() = runTestRealTime {
// 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 = "testHandshakeSubprotocolHeader_noProtocol"),
url = testUrl(
path = "/sendHandshakeHeaders",
testCaseName = "testHandshakeSubprotocolHeader_noProtocol",
otherParams = extraParams,
),
protocols = emptyList(),
)
try {
Expand All @@ -221,8 +228,14 @@ abstract class WebSocketClientTestSuite(

@Test
fun testHandshakeSubprotocolHeader_singleProtocol() = runTestRealTime {
// 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 = "testHandshakeSubprotocolHeader_singleProtocol"),
url = testUrl(
path = "/sendHandshakeHeaders",
testCaseName = "testHandshakeSubprotocolHeader_singleProtocol",
otherParams = extraParams,
),
protocols = listOf("v12.stomp"),
)
try {
Expand All @@ -239,8 +252,14 @@ abstract class WebSocketClientTestSuite(

@Test
fun testHandshakeSubprotocolHeader_multipleProtocols() = runTestRealTime {
// 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 = "testHandshakeSubprotocolHeader_singleProtocol"),
url = testUrl(
path = "/sendHandshakeHeaders",
testCaseName = "testHandshakeSubprotocolHeader_multipleProtocols",
otherParams = extraParams,
),
protocols = listOf("unknown-protocol", "v12.stomp", "v11.stomp", "v10.stomp"),
)
try {
Expand All @@ -263,14 +282,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")
Expand Down Expand Up @@ -332,7 +357,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()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")))
Expand All @@ -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?) {
Expand Down Expand Up @@ -59,4 +74,13 @@ internal class EchoWebSocketServer(port: Int = 0) : WebSocketServer(
}
port
}

override fun stop(timeout: Int, closeMessage: String?) {
super.stop(timeout, closeMessage)
delayedHeadersScope.cancel()
}
}

private fun URI.queryAsMap() = query.split("&")
.map { it.split("=") }
.associate { it[0] to it[1] }

0 comments on commit 43f5f92

Please sign in to comment.