Skip to content

Commit b42d338

Browse files
authored
KTOR-865 Support CIO server for WasmJS and JS (#4590)
* Make server-test-host depend on CIO in common * Expose test-base from test-suites to have access to it in server-cio tests * Explicitly use CIO in JS/WasmJs server tests * Move WebSocketEngineSuite to common
1 parent 980ccf0 commit b42d338

File tree

34 files changed

+40
-59
lines changed

34 files changed

+40
-59
lines changed

ktor-server/ktor-server-cio/api/ktor-server-cio.klib.api

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Klib ABI Dump
2-
// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
2+
// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
33
// Rendering settings:
44
// - Signature version: 2
55
// - Show manifest properties: true

ktor-server/ktor-server-cio/build.gradle.kts

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
description = ""
66

77
kotlin.sourceSets {
8-
jvmAndPosixMain {
8+
commonMain {
99
dependencies {
1010
api(project(":ktor-server:ktor-server-core"))
1111
api(project(":ktor-http:ktor-http-cio"))
1212
api(project(":ktor-shared:ktor-websockets"))
1313
api(project(":ktor-network"))
1414
}
1515
}
16-
jvmAndPosixTest {
16+
commonTest {
1717
dependencies {
18-
api(project(":ktor-server:ktor-server-core"))
1918
api(project(":ktor-client:ktor-client-cio"))
2019
api(project(":ktor-server:ktor-server-test-suites"))
2120
api(project(":ktor-server:ktor-server-test-base"))

ktor-server/ktor-server-cio/jvmAndPosix/src/io/ktor/server/cio/CIOApplicationEngine.kt ktor-server/ktor-server-cio/common/src/io/ktor/server/cio/CIOApplicationEngine.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public class CIOApplicationEngine(
7676
return this
7777
}
7878

79-
override fun start(wait: Boolean): ApplicationEngine = runBlocking { startSuspend(wait) }
79+
override fun start(wait: Boolean): ApplicationEngine = runBlockingBridge { startSuspend(wait) }
8080

8181
override suspend fun stopSuspend(gracePeriodMillis: Long, timeoutMillis: Long) {
8282
stopRequest.complete()
@@ -96,7 +96,7 @@ public class CIOApplicationEngine(
9696
}
9797
}
9898

99-
override fun stop(gracePeriodMillis: Long, timeoutMillis: Long): Unit = runBlocking {
99+
override fun stop(gracePeriodMillis: Long, timeoutMillis: Long): Unit = runBlockingBridge {
100100
stopSuspend(gracePeriodMillis, timeoutMillis)
101101
}
102102

ktor-server/ktor-server-cio/jvmAndPosix/src/io/ktor/server/cio/backend/HttpServer.kt ktor-server/ktor-server-cio/common/src/io/ktor/server/cio/backend/HttpServer.kt

+4-6
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import io.ktor.server.engine.internal.*
1212
import io.ktor.util.logging.*
1313
import io.ktor.utils.io.*
1414
import kotlinx.coroutines.*
15-
import kotlinx.io.IOException
15+
import kotlinx.io.*
1616
import kotlin.time.Duration.Companion.seconds
1717

18+
private val LOGGER = KtorSimpleLogger("io.ktor.server.cio.HttpServer")
19+
1820
/**
1921
* Start a http server with [settings] invoking [handler] for every request
2022
*/
@@ -37,18 +39,14 @@ public fun CoroutineScope.httpServer(
3739
val selector = SelectorManager(coroutineContext)
3840
val timeout = settings.connectionIdleTimeoutSeconds.seconds
3941

40-
val logger = KtorSimpleLogger(
41-
HttpServer::class.simpleName ?: HttpServer::class.qualifiedName ?: HttpServer::class.toString()
42-
)
43-
4442
val acceptJob = launch(serverJob + CoroutineName("accept-${settings.port}")) {
4543
aSocket(selector).tcp().bind(settings.host, settings.port) {
4644
reuseAddress = settings.reuseAddress
4745
}.use { server ->
4846
socket.complete(server)
4947

5048
val exceptionHandler = coroutineContext[CoroutineExceptionHandler]
51-
?: DefaultUncaughtExceptionHandler(logger)
49+
?: DefaultUncaughtExceptionHandler(LOGGER)
5250

5351
val connectionScope = CoroutineScope(
5452
coroutineContext +

ktor-server/ktor-server-cio/jvmAndPosix/src/io/ktor/server/cio/internal/CoroutineUtils.kt ktor-server/ktor-server-cio/common/src/io/ktor/server/cio/internal/CoroutineUtils.kt

+2
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ package io.ktor.server.cio.internal
77
import kotlinx.coroutines.*
88

99
internal expect val Dispatchers.IOBridge: CoroutineDispatcher
10+
11+
internal expect fun <T> runBlockingBridge(block: suspend CoroutineScope.() -> T): T

ktor-server/ktor-server-cio/jvmAndPosix/test/io/ktor/tests/server/cio/RAWExample.kt ktor-server/ktor-server-cio/common/test/io/ktor/tests/server/cio/RAWExample.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import io.ktor.http.*
88
import io.ktor.http.cio.*
99
import io.ktor.server.cio.*
1010
import io.ktor.server.cio.backend.*
11+
import io.ktor.server.cio.internal.*
1112
import io.ktor.util.date.*
1213
import io.ktor.utils.io.*
1314
import io.ktor.utils.io.core.*
@@ -29,7 +30,7 @@ private val notFound404_11 = RequestResponseBuilder().apply {
2930
* This is just an example demonstrating how to create CIO low-level http server
3031
*/
3132
@OptIn(DelicateCoroutinesApi::class)
32-
fun main() {
33+
fun example() {
3334
val settings = HttpServerSettings()
3435

3536
GlobalScope.launch {
@@ -64,7 +65,7 @@ fun main() {
6465
}
6566
)
6667

67-
runBlocking {
68+
runBlockingBridge {
6869
server.rootServerJob.join()
6970
}
7071
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.server.cio.internal
6+
7+
import kotlinx.coroutines.*
8+
9+
internal actual val Dispatchers.IOBridge: CoroutineDispatcher
10+
get() = Default
11+
12+
internal actual fun <T> runBlockingBridge(block: suspend CoroutineScope.() -> T): T =
13+
error("runBlocking is not supported on JS and WASM")

ktor-server/ktor-server-cio/jvm/src/io/ktor/server/cio/internal/CoroutineUtilsJvm.kt

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ import kotlinx.coroutines.*
88

99
internal actual val Dispatchers.IOBridge: CoroutineDispatcher
1010
get() = IO
11+
12+
internal actual fun <T> runBlockingBridge(block: suspend CoroutineScope.() -> T): T = runBlocking(block = block)

ktor-server/ktor-server-cio/posix/src/io/ktor/server/cio/backend/SocketAddressUtilsNative.kt ktor-server/ktor-server-cio/nonJvm/src/io/ktor/server/cio/backend/SocketAddressUtils.nonJvm.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ import io.ktor.network.sockets.*
88
import io.ktor.util.network.*
99

1010
internal actual fun SocketAddress.toNetworkAddress(): NetworkAddress {
11-
val inetAddress = this as? InetSocketAddress ?: error("Expected inet socket address")
12-
return NetworkAddress(inetAddress.hostname, inetAddress.port)
11+
check(this is InetSocketAddress) { "Expected inet socket address" }
12+
return NetworkAddress(hostname, port)
1313
}

ktor-server/ktor-server-cio/posix/src/io/ktor/server/cio/internal/CoroutineUtilsNix.kt

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ import kotlinx.coroutines.*
88

99
internal actual val Dispatchers.IOBridge: CoroutineDispatcher
1010
get() = IO
11+
12+
internal actual fun <T> runBlockingBridge(block: suspend CoroutineScope.() -> T): T = runBlocking(block = block)

ktor-server/ktor-server-test-base/jsAndWasmShared/src/io/ktor/server/test/base/EngineTestBase.jsAndWasmShared.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package io.ktor.server.test.base
66

77
import io.ktor.client.*
8+
import io.ktor.client.engine.cio.*
89
import io.ktor.client.plugins.*
910
import io.ktor.client.request.*
1011
import io.ktor.client.statement.*
@@ -132,7 +133,6 @@ actual constructor(
132133
val starting = GlobalScope.async {
133134
server.startSuspend(wait = false)
134135
_port = server.engine.resolvedConnectors().first().port
135-
delay(500)
136136
}
137137

138138
return try {
@@ -161,7 +161,7 @@ actual constructor(
161161
builder: suspend HttpRequestBuilder.() -> Unit,
162162
block: suspend HttpResponse.(Int) -> Unit
163163
) {
164-
HttpClient {
164+
HttpClient(CIO) {
165165
followRedirects = false
166166
expectSuccess = false
167167

ktor-server/ktor-server-test-base/posix/src/io/ktor/server/test/base/EngineTestBaseNix.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ actual constructor(
113113
// as far as we have retry loop on call side
114114
val starting = GlobalScope.async {
115115
server.start(wait = false)
116-
delay(500)
116+
// await for a server to be started
117+
server.engine.resolvedConnectors()
117118
}
118119

119120
return try {

ktor-server/ktor-server-test-host/build.gradle.kts

+1-6
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,13 @@ val jetty_alpn_boot_version: String? by extra
99
kotlin.sourceSets {
1010
commonMain {
1111
dependencies {
12+
api(project(":ktor-client:ktor-client-cio"))
1213
api(project(":ktor-server:ktor-server-core"))
1314
api(project(":ktor-client:ktor-client-core"))
1415
api(project(":ktor-test-dispatcher"))
1516
}
1617
}
1718

18-
jvmAndPosixMain {
19-
dependencies {
20-
api(project(":ktor-client:ktor-client-cio"))
21-
}
22-
}
23-
2419
jvmMain {
2520
dependencies {
2621
api(project(":ktor-network:ktor-network-tls"))

ktor-server/ktor-server-test-host/common/src/io/ktor/server/testing/Utils.kt

+1-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import io.ktor.client.request.*
99
import io.ktor.util.*
1010
import io.ktor.utils.io.*
1111
import kotlinx.coroutines.*
12-
import kotlinx.io.*
12+
import io.ktor.network.sockets.SocketTimeoutException as NetworkSocketTimeoutException
1313

1414
/**
1515
* [on] function receiver object
@@ -67,10 +67,3 @@ internal fun Throwable.mapToKtor(data: HttpRequestData): Throwable = when {
6767
cause?.rootCause is NetworkSocketTimeoutException -> SocketTimeoutException(data, cause?.rootCause)
6868
else -> this
6969
}
70-
71-
// There are two SocketTimeoutException in ktor:
72-
// * io.ktor.network.sockets.SocketTimeoutException
73-
// * io.ktor.client.network.sockets.SocketTimeoutException
74-
// on JVM they both are java.net.SocketTimeoutException, but on other targets it's not true
75-
// additionally `network.sockets` exception is in `ktor-network` modules which don't have support for js/wasm target
76-
internal expect class NetworkSocketTimeoutException(message: String) : IOException

ktor-server/ktor-server-test-host/jsAndWasmShared/src/io/ktor/server/testing/Utils.jsAndWasmShared.kt

-9
This file was deleted.

ktor-server/ktor-server-test-host/jvm/src/io/ktor/server/testing/Utils.jvm.kt

-8
This file was deleted.

ktor-server/ktor-server-test-host/posix/src/io/ktor/server/testing/Utils.posix.kt

-8
This file was deleted.

ktor-server/ktor-server-test-suites/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ kotlin.sourceSets {
1212
implementation(project(":ktor-server:ktor-server-plugins:ktor-server-status-pages"))
1313
implementation(project(":ktor-server:ktor-server-plugins:ktor-server-hsts"))
1414
implementation(project(":ktor-server:ktor-server-plugins:ktor-server-websockets"))
15-
implementation(project(":ktor-server:ktor-server-test-base"))
15+
api(project(":ktor-server:ktor-server-test-base"))
1616
}
1717
}
1818

0 commit comments

Comments
 (0)