From fc9edf91dac352459127830adec430256f7170c0 Mon Sep 17 00:00:00 2001 From: Oleg Yukhnevich Date: Tue, 11 Mar 2025 19:18:30 +0200 Subject: [PATCH] Migrate chat sample to new transport API and kotlinx.io Simplify the code, as we now have ktor transports fully KMP ready --- samples/chat/README.md | 21 ++--- samples/chat/api/build.gradle.kts | 10 ++- .../src/commonMain/kotlin/Serialization.kt | 10 +-- .../chat/api/src/commonMain/kotlin/Servers.kt | 39 ++++----- samples/chat/client/build.gradle.kts | 32 ++++---- .../client/src/commonMain/kotlin/ApiClient.kt | 44 ---------- .../client/src/commonMain/kotlin/client.kt | 81 +++++++++++++++++++ .../client/src/commonMain/kotlin/usage.kt | 33 -------- samples/chat/client/src/jsMain/kotlin/App.kt | 13 +-- .../src/jsMain/kotlin/clientTransport.kt | 31 ------- .../client/src/jsMain/resources/index.html | 27 ------- samples/chat/client/src/jvmMain/kotlin/App.kt | 9 +-- .../src/jvmMain/kotlin/clientTransport.kt | 32 -------- .../chat/client/src/nativeMain/kotlin/App.kt | 9 +-- .../src/nativeMain/kotlin/clientTransport.kt | 32 -------- .../kotlin/App.kt} | 15 ++-- samples/chat/gradle.properties | 10 +-- samples/chat/server/build.gradle.kts | 31 ++++--- .../server/src/commonMain/kotlin/Messages.kt | 5 +- .../server/src/commonMain/kotlin/acceptor.kt | 32 +++++--- .../server/src/commonMain/kotlin/expects.kt | 10 +-- .../kotlin/serverTransport.common.kt | 29 ------- samples/chat/server/src/jsMain/kotlin/App.kt | 4 +- .../src/jsMain/kotlin/serverTransport.kt | 30 ------- samples/chat/server/src/jvmMain/kotlin/App.kt | 4 +- .../server/src/jvmMain/kotlin/expects.jvm.kt | 26 ------ .../src/jvmMain/kotlin/serverTransport.kt | 32 -------- .../chat/server/src/nativeMain/kotlin/App.kt | 4 +- .../src/nativeMain/kotlin/expects.native.kt | 28 ------- .../src/nativeMain/kotlin/serverTransport.kt | 32 -------- .../kotlin/App.kt} | 11 +-- 31 files changed, 206 insertions(+), 520 deletions(-) delete mode 100644 samples/chat/client/src/commonMain/kotlin/ApiClient.kt create mode 100644 samples/chat/client/src/commonMain/kotlin/client.kt delete mode 100644 samples/chat/client/src/commonMain/kotlin/usage.kt delete mode 100644 samples/chat/client/src/jsMain/kotlin/clientTransport.kt delete mode 100644 samples/chat/client/src/jsMain/resources/index.html delete mode 100644 samples/chat/client/src/jvmMain/kotlin/clientTransport.kt delete mode 100644 samples/chat/client/src/nativeMain/kotlin/clientTransport.kt rename samples/chat/client/src/{commonMain/kotlin/clientTransport.common.kt => wasmJsMain/kotlin/App.kt} (66%) delete mode 100644 samples/chat/server/src/commonMain/kotlin/serverTransport.common.kt delete mode 100644 samples/chat/server/src/jsMain/kotlin/serverTransport.kt delete mode 100644 samples/chat/server/src/jvmMain/kotlin/expects.jvm.kt delete mode 100644 samples/chat/server/src/jvmMain/kotlin/serverTransport.kt delete mode 100644 samples/chat/server/src/nativeMain/kotlin/expects.native.kt delete mode 100644 samples/chat/server/src/nativeMain/kotlin/serverTransport.kt rename samples/chat/server/src/{jsMain/kotlin/expects.js.kt => wasmJsMain/kotlin/App.kt} (74%) diff --git a/samples/chat/README.md b/samples/chat/README.md index f5ed8741..64cfd066 100644 --- a/samples/chat/README.md +++ b/samples/chat/README.md @@ -2,18 +2,19 @@ * api - shared chat API for both client and server * client - client API implementation via requesting to RSocket with Protobuf serialization. - Works on JVM(TCP/WS), Native(TCP/WS), Js(WS). + Works on JVM/Js/WasmJs/Native over TCP and WS. Tasks for running clients: - * JVM: `run` - * Native: `runDebugExecutable[TARGET]` / `runReleaseExecutable[TARGET]` - (where `[TARGET]` is one of `LinuxX64`, `MacosArm64` or `MacosX64`) - * NodeJs: `jsNodeRun` / `jsNodeDevelopmentRun` / `jsNodeProductionRun` - * Browser: `jsBrowserRun` / `jsBrowserDevelopmentRun` / `jsBrowserProductionRun` + * JVM: `jvmRun` + * Native: `runDebugExecutable[TARGET]` / `runReleaseExecutable[TARGET]` + (where `[TARGET]` is one of `LinuxX64`, `MacosArm64`, `MacosX64` or `MingwX64`) + * JS: `jsNodeRun` / `jsNodeDevelopmentRun` / `jsNodeProductionRun` + * WasmJs: `wasmJsNodeRun` / `wasmJsNodeDevelopmentRun` / `wasmJsNodeProductionRun` * server - server API implementation with storage in concurrent map and exposing it through RSocket with Protobuf serialization. Can be started on JVM(TCP/WS), Native(TCP/WS), NodeJS(TCP). Tasks for running servers: - * JVM: `run` - * Native: `runDebugExecutable[TARGET]` / `runReleaseExecutable[TARGET]` - (where `[TARGET]` is one of `LinuxX64`, `MacosArm64` or `MacosX64`) - * NodeJs: `jsNodeRun` / `jsNodeDevelopmentRun` / `jsNodeProductionRun` + * JVM: `jvmRun` + * Native: `runDebugExecutable[TARGET]` / `runReleaseExecutable[TARGET]` + (where `[TARGET]` is one of `LinuxX64`, `MacosArm64`, `MacosX64` or `MingwX64`) + * JS: `jsNodeRun` / `jsNodeDevelopmentRun` / `jsNodeProductionRun` + * WasmJs: `wasmJsNodeRun` / `wasmJsNodeDevelopmentRun` / `wasmJsNodeProductionRun` diff --git a/samples/chat/api/build.gradle.kts b/samples/chat/api/build.gradle.kts index 3b53e14a..e3ad72da 100644 --- a/samples/chat/api/build.gradle.kts +++ b/samples/chat/api/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,8 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.* + plugins { kotlin("multiplatform") kotlin("plugin.serialization") @@ -25,12 +27,16 @@ val kotlinxSerializationVersion: String by rootProject kotlin { jvm() js { - browser() + nodejs() + } + @OptIn(ExperimentalWasmDsl::class) + wasmJs { nodejs() } linuxX64() macosX64() macosArm64() + mingwX64() sourceSets { commonMain.dependencies { diff --git a/samples/chat/api/src/commonMain/kotlin/Serialization.kt b/samples/chat/api/src/commonMain/kotlin/Serialization.kt index f590e930..64ed42e5 100644 --- a/samples/chat/api/src/commonMain/kotlin/Serialization.kt +++ b/samples/chat/api/src/commonMain/kotlin/Serialization.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,10 @@ package io.rsocket.kotlin.samples.chat.api -import io.ktor.utils.io.core.* import io.rsocket.kotlin.* import io.rsocket.kotlin.metadata.* import io.rsocket.kotlin.payload.* +import kotlinx.io.* import kotlinx.serialization.* import kotlinx.serialization.protobuf.* import kotlin.jvm.* @@ -29,7 +29,7 @@ import kotlin.jvm.* val ConfiguredProtoBuf = ProtoBuf @ExperimentalSerializationApi -inline fun ProtoBuf.decodeFromPayload(payload: Payload): T = decodeFromByteArray(payload.data.readBytes()) +inline fun ProtoBuf.decodeFromPayload(payload: Payload): T = decodeFromByteArray(payload.data.readByteArray()) @ExperimentalSerializationApi @OptIn(ExperimentalMetadataApi::class) @@ -55,8 +55,8 @@ inline fun ProtoBuf.decoding(payload: Payload, block: (I) -> Unit): } @OptIn(ExperimentalMetadataApi::class) -fun Payload(route: String, packet: ByteReadPacket = ByteReadPacket.Empty): Payload = buildPayload { - data(packet) +fun Payload(route: String, data: Buffer = Buffer()): Payload = buildPayload { + data(data) metadata(RoutingMetadata(route)) } diff --git a/samples/chat/api/src/commonMain/kotlin/Servers.kt b/samples/chat/api/src/commonMain/kotlin/Servers.kt index efb6f09b..1d38a425 100644 --- a/samples/chat/api/src/commonMain/kotlin/Servers.kt +++ b/samples/chat/api/src/commonMain/kotlin/Servers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,29 +21,24 @@ enum class TransportType { TCP, WS } data class ServerAddress(val port: Int, val type: TransportType) object Servers { - object JVM { - val TCP = ServerAddress(port = 8001, type = TransportType.TCP) - val WS = ServerAddress(port = 8002, type = TransportType.WS) - } - - object JS { - val TCP = ServerAddress(port = 7001, type = TransportType.TCP) - } - - object Native { - val TCP = ServerAddress(port = 9001, type = TransportType.TCP) - val WS = ServerAddress(port = 9002, type = TransportType.WS) - } + val JVM = listOf( + ServerAddress(port = 9011, type = TransportType.TCP), + ServerAddress(port = 9012, type = TransportType.WS), + ) - val WS = setOf( - JVM.WS, - Native.WS + val JS = listOf( + ServerAddress(port = 9051, type = TransportType.TCP), + ServerAddress(port = 9052, type = TransportType.WS), ) - val TCP = setOf( - JVM.TCP, - JS.TCP, - Native.TCP + + val WasmJS = listOf( + ServerAddress(port = 9061, type = TransportType.TCP), + ServerAddress(port = 9062, type = TransportType.WS), ) - val ALL = WS + TCP + val Native = listOf( + ServerAddress(port = 9041, type = TransportType.TCP), + ServerAddress(port = 9042, type = TransportType.WS), + ) + val ALL = JVM + JS + WasmJS + Native } diff --git a/samples/chat/client/build.gradle.kts b/samples/chat/client/build.gradle.kts index db573a8d..9c8de1dd 100644 --- a/samples/chat/client/build.gradle.kts +++ b/samples/chat/client/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,33 +14,37 @@ * limitations under the License. */ +import org.jetbrains.kotlin.gradle.* import org.jetbrains.kotlin.gradle.plugin.mpp.* plugins { kotlin("multiplatform") kotlin("plugin.serialization") - application } val rsocketVersion: String by rootProject val ktorVersion: String by rootProject -application { - mainClass.set("io.rsocket.kotlin.samples.chat.client.AppKt") -} - kotlin { jvm { - withJava() + @OptIn(ExperimentalKotlinGradlePluginApi::class) + mainRun { + this.mainClass.set("io.rsocket.kotlin.samples.chat.client.AppKt") + } } js { - browser() + nodejs() + binaries.executable() + } + @OptIn(ExperimentalWasmDsl::class) + wasmJs { nodejs() binaries.executable() } linuxX64() macosX64() macosArm64() + mingwX64() targets.withType().configureEach { binaries { executable { @@ -52,18 +56,10 @@ kotlin { sourceSets { commonMain.dependencies { implementation(project(":api")) - implementation("io.rsocket.kotlin:rsocket-transport-ktor-websocket-client:$rsocketVersion") - } - jvmMain.dependencies { - implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") - implementation("io.ktor:ktor-client-cio:$ktorVersion") - } - nativeMain.dependencies { + implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") + implementation("io.rsocket.kotlin:rsocket-transport-ktor-websocket-client:$rsocketVersion") implementation("io.ktor:ktor-client-cio:$ktorVersion") } - jsMain.dependencies { - implementation("io.ktor:ktor-client-js:$ktorVersion") - } } } diff --git a/samples/chat/client/src/commonMain/kotlin/ApiClient.kt b/samples/chat/client/src/commonMain/kotlin/ApiClient.kt deleted file mode 100644 index 0d65623e..00000000 --- a/samples/chat/client/src/commonMain/kotlin/ApiClient.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.client - -import io.rsocket.kotlin.* -import io.rsocket.kotlin.core.* -import io.rsocket.kotlin.payload.* -import io.rsocket.kotlin.samples.chat.api.* -import kotlinx.serialization.* - -@OptIn(ExperimentalSerializationApi::class) -class ApiClient(rSocket: RSocket) { - private val proto = ConfiguredProtoBuf - val users = UserApiClient(rSocket, proto) - val chats = ChatApiClient(rSocket, proto) - val messages = MessageApiClient(rSocket, proto) -} - -suspend fun ApiClient( - address: ServerAddress, - name: String -): ApiClient { - println("Connecting client to: $address") - val connector = RSocketConnector { - connectionConfig { - setupPayload { buildPayload { data(name) } } - } - } - return ApiClient(connector.connect(ClientTransport(address))) -} diff --git a/samples/chat/client/src/commonMain/kotlin/client.kt b/samples/chat/client/src/commonMain/kotlin/client.kt new file mode 100644 index 00000000..27c79e1b --- /dev/null +++ b/samples/chat/client/src/commonMain/kotlin/client.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.rsocket.kotlin.samples.chat.client + +import io.ktor.client.engine.cio.* +import io.rsocket.kotlin.* +import io.rsocket.kotlin.core.* +import io.rsocket.kotlin.payload.* +import io.rsocket.kotlin.samples.chat.api.* +import io.rsocket.kotlin.transport.ktor.tcp.* +import io.rsocket.kotlin.transport.ktor.websocket.client.* +import kotlinx.coroutines.* +import kotlinx.serialization.* +import kotlin.coroutines.* + +@OptIn(ExperimentalSerializationApi::class) +class ApiClient(rSocket: RSocket) { + private val proto = ConfiguredProtoBuf + val users = UserApiClient(rSocket, proto) + val chats = ChatApiClient(rSocket, proto) + val messages = MessageApiClient(rSocket, proto) +} + +suspend fun runClient( + addresses: List, + name: String, + target: String, +): Unit = supervisorScope { + addresses.forEach { address -> + launch { + val client = ApiClient(coroutineContext, address, name) + val message = "RSocket is awesome! (from $target)" + + val chat = client.chats.all().firstOrNull() ?: client.chats.new("rsocket-kotlin chat") + + val sentMessage = client.messages.send(chat.id, message) + println("Send to [$address]: $sentMessage") + + client.messages.messages(chat.id, -1).collect { + println("Received from [$address]: $it") + } + } + } +} + +private suspend fun ApiClient( + context: CoroutineContext, + address: ServerAddress, + name: String, +): ApiClient { + println("Connecting client to: $address") + val connector = RSocketConnector { + connectionConfig { + setupPayload { buildPayload { data(name) } } + } + } + + val target = when (address.type) { + TransportType.TCP -> KtorTcpClientTransport(context).target(host = "127.0.0.1", port = address.port) + TransportType.WS -> KtorWebSocketClientTransport(context) { + httpEngine(CIO) + }.target(host = "127.0.0.1", port = address.port) + } + + + return ApiClient(connector.connect(target)) +} diff --git a/samples/chat/client/src/commonMain/kotlin/usage.kt b/samples/chat/client/src/commonMain/kotlin/usage.kt deleted file mode 100644 index 736eefa2..00000000 --- a/samples/chat/client/src/commonMain/kotlin/usage.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.client - -import io.rsocket.kotlin.samples.chat.api.* - -suspend fun ApiClient.use( - receiver: ServerAddress, - message: String -) { - val chat = chats.all().firstOrNull() ?: chats.new("rsocket-kotlin chat") - - val sentMessage = messages.send(chat.id, message) - println("Send to [$receiver]: $sentMessage") - - messages.messages(chat.id, -1).collect { - println("Received from [$receiver]: $it") - } -} diff --git a/samples/chat/client/src/jsMain/kotlin/App.kt b/samples/chat/client/src/jsMain/kotlin/App.kt index 8592ff9d..3fbab687 100644 --- a/samples/chat/client/src/jsMain/kotlin/App.kt +++ b/samples/chat/client/src/jsMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,7 @@ package io.rsocket.kotlin.samples.chat.client import io.rsocket.kotlin.samples.chat.api.* -import kotlinx.coroutines.* -suspend fun main(): Unit = coroutineScope { - // only WS is supported on browser JS - Servers.WS.forEach { - val client = ApiClient(it, "Kolya") - launch { - client.use(it, "RSocket is awesome! (from js)") - } - } +suspend fun main() { + runClient(Servers.ALL, "Kolya", "JS") } diff --git a/samples/chat/client/src/jsMain/kotlin/clientTransport.kt b/samples/chat/client/src/jsMain/kotlin/clientTransport.kt deleted file mode 100644 index 8fe31039..00000000 --- a/samples/chat/client/src/jsMain/kotlin/clientTransport.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2015-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.client - -import io.ktor.client.engine.js.* -import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* -import io.rsocket.kotlin.transport.ktor.websocket.client.* - -internal actual fun clientTransport( - type: TransportType, - host: String, - port: Int -): ClientTransport = when (type) { - TransportType.TCP -> error("TCP is not supported") - TransportType.WS -> WebSocketClientTransport(Js, host, port) -} diff --git a/samples/chat/client/src/jsMain/resources/index.html b/samples/chat/client/src/jsMain/resources/index.html deleted file mode 100644 index c07fddc8..00000000 --- a/samples/chat/client/src/jsMain/resources/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - Chat - - - - - - diff --git a/samples/chat/client/src/jvmMain/kotlin/App.kt b/samples/chat/client/src/jvmMain/kotlin/App.kt index c32db17d..d4af1410 100644 --- a/samples/chat/client/src/jvmMain/kotlin/App.kt +++ b/samples/chat/client/src/jvmMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,5 @@ import io.rsocket.kotlin.samples.chat.api.* import kotlinx.coroutines.* fun main(): Unit = runBlocking { - Servers.ALL.forEach { - val client = ApiClient(it, "Oleg") - launch { - client.use(it, "RSocket is awesome! (from JVM)") - } - } + runClient(Servers.ALL, "Oleg", "JVM") } diff --git a/samples/chat/client/src/jvmMain/kotlin/clientTransport.kt b/samples/chat/client/src/jvmMain/kotlin/clientTransport.kt deleted file mode 100644 index f6978757..00000000 --- a/samples/chat/client/src/jvmMain/kotlin/clientTransport.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.client - -import io.ktor.client.engine.cio.* -import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* -import io.rsocket.kotlin.transport.ktor.tcp.* -import io.rsocket.kotlin.transport.ktor.websocket.client.* - -internal actual fun clientTransport( - type: TransportType, - host: String, - port: Int -): ClientTransport = when (type) { - TransportType.TCP -> TcpClientTransport(host, port) - TransportType.WS -> WebSocketClientTransport(CIO, host, port) -} diff --git a/samples/chat/client/src/nativeMain/kotlin/App.kt b/samples/chat/client/src/nativeMain/kotlin/App.kt index 866f0e0f..9b70d0c6 100644 --- a/samples/chat/client/src/nativeMain/kotlin/App.kt +++ b/samples/chat/client/src/nativeMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,5 @@ import io.rsocket.kotlin.samples.chat.api.* import kotlinx.coroutines.* fun main(): Unit = runBlocking { - Servers.ALL.forEach { - val client = ApiClient(it, "Gloria") - launch { - client.use(it, "RSocket is awesome! (from Native)") - } - } + runClient(Servers.ALL, "Gloria", "Native") } diff --git a/samples/chat/client/src/nativeMain/kotlin/clientTransport.kt b/samples/chat/client/src/nativeMain/kotlin/clientTransport.kt deleted file mode 100644 index f6978757..00000000 --- a/samples/chat/client/src/nativeMain/kotlin/clientTransport.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.client - -import io.ktor.client.engine.cio.* -import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* -import io.rsocket.kotlin.transport.ktor.tcp.* -import io.rsocket.kotlin.transport.ktor.websocket.client.* - -internal actual fun clientTransport( - type: TransportType, - host: String, - port: Int -): ClientTransport = when (type) { - TransportType.TCP -> TcpClientTransport(host, port) - TransportType.WS -> WebSocketClientTransport(CIO, host, port) -} diff --git a/samples/chat/client/src/commonMain/kotlin/clientTransport.common.kt b/samples/chat/client/src/wasmJsMain/kotlin/App.kt similarity index 66% rename from samples/chat/client/src/commonMain/kotlin/clientTransport.common.kt rename to samples/chat/client/src/wasmJsMain/kotlin/App.kt index c054af0d..48dfc31c 100644 --- a/samples/chat/client/src/commonMain/kotlin/clientTransport.common.kt +++ b/samples/chat/client/src/wasmJsMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,13 +17,8 @@ package io.rsocket.kotlin.samples.chat.client import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* +import kotlinx.coroutines.* -fun ClientTransport(address: ServerAddress): ClientTransport = - clientTransport(address.type, "localhost", address.port) - -internal expect fun clientTransport( - type: TransportType, - host: String, - port: Int -): ClientTransport +suspend fun main(): Unit = coroutineScope { + runClient(Servers.ALL, "Zalim", "WasmJS") +} diff --git a/samples/chat/gradle.properties b/samples/chat/gradle.properties index a1724b01..1c32f3f0 100644 --- a/samples/chat/gradle.properties +++ b/samples/chat/gradle.properties @@ -1,5 +1,5 @@ # -# Copyright 2015-2024 the original author or authors. +# Copyright 2015-2025 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,10 +15,10 @@ # group=io.rsocket.kotlin.sample.chat version=1.0.0 -kotlinVersion=2.0.0 -ktorVersion=2.3.11 -kotlinxSerializationVersion=1.6.3 -rsocketVersion=0.16.0 +kotlinVersion=2.1.10 +ktorVersion=3.1.1 +kotlinxSerializationVersion=1.8.0 +rsocketVersion=0.20.0 #Kotlin kotlin.js.yarn=false diff --git a/samples/chat/server/build.gradle.kts b/samples/chat/server/build.gradle.kts index 8af42195..8556c74f 100644 --- a/samples/chat/server/build.gradle.kts +++ b/samples/chat/server/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2015-2024 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,31 +20,35 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.* plugins { kotlin("multiplatform") kotlin("plugin.serialization") - application } val rsocketVersion: String by rootProject val ktorVersion: String by rootProject -application { - mainClass.set("io.rsocket.kotlin.samples.chat.server.AppKt") -} - kotlin { @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { freeCompilerArgs.add("-Xexpect-actual-classes") } jvm { - withJava() + @OptIn(ExperimentalKotlinGradlePluginApi::class) + mainRun { + mainClass.set("io.rsocket.kotlin.samples.chat.server.AppKt") + } } js { nodejs() binaries.executable() } + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + nodejs() + binaries.executable() + } linuxX64() macosX64() macosArm64() + mingwX64() targets.withType().configureEach { binaries { executable { @@ -57,20 +61,13 @@ kotlin { commonMain.dependencies { implementation(project(":api")) + implementation("org.jetbrains.kotlinx:atomicfu:0.27.0") // for Atomic (drop after Kotlin 2.2) + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.2") // for Instant (drop after Kotlin 2.2) implementation("io.ktor:ktor-utils:$ktorVersion") //for concurrent map implementation and shared list - } - jvmMain.dependencies { - implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") - implementation("io.rsocket.kotlin:rsocket-transport-ktor-websocket-server:$rsocketVersion") - implementation("io.ktor:ktor-server-cio:$ktorVersion") - } - nativeMain.dependencies { + implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") implementation("io.rsocket.kotlin:rsocket-transport-ktor-websocket-server:$rsocketVersion") implementation("io.ktor:ktor-server-cio:$ktorVersion") } - jsMain.dependencies { - implementation("io.rsocket.kotlin:rsocket-transport-nodejs-tcp:$rsocketVersion") - } } } diff --git a/samples/chat/server/src/commonMain/kotlin/Messages.kt b/samples/chat/server/src/commonMain/kotlin/Messages.kt index fbda725f..2a8f7fcc 100644 --- a/samples/chat/server/src/commonMain/kotlin/Messages.kt +++ b/samples/chat/server/src/commonMain/kotlin/Messages.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package io.rsocket.kotlin.samples.chat.server import io.rsocket.kotlin.samples.chat.api.* +import kotlinx.datetime.* class Messages { private val storage = Storage() @@ -37,7 +38,7 @@ class Messages { fun create(userId: Int, chatId: Int, content: String): Message { val messageId = storage.nextId() - val message = Message(messageId, chatId, userId, currentMillis(), content) + val message = Message(messageId, chatId, userId, Clock.System.now().toEpochMilliseconds(), content) storage.save(messageId, message) return message } diff --git a/samples/chat/server/src/commonMain/kotlin/acceptor.kt b/samples/chat/server/src/commonMain/kotlin/acceptor.kt index 5395ec40..4b683b39 100644 --- a/samples/chat/server/src/commonMain/kotlin/acceptor.kt +++ b/samples/chat/server/src/commonMain/kotlin/acceptor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,27 +16,36 @@ package io.rsocket.kotlin.samples.chat.server +import io.ktor.server.cio.* import io.rsocket.kotlin.* import io.rsocket.kotlin.core.* import io.rsocket.kotlin.samples.chat.api.* +import io.rsocket.kotlin.transport.ktor.tcp.* +import io.rsocket.kotlin.transport.ktor.websocket.server.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* +import kotlinx.io.* import kotlinx.serialization.* -suspend fun startServer(vararg addresses: ServerAddress) { +suspend fun startServer(addresses: List) { val server = RSocketServer { } val job = SupervisorJob() - val scope = CoroutineScope( - job + CoroutineExceptionHandler { coroutineContext, throwable -> - println("Error happened $coroutineContext: $throwable") - } - ) + val context = job + CoroutineExceptionHandler { coroutineContext, throwable -> + println("Error happened $coroutineContext: $throwable") + } val acceptor = createAcceptor() - addresses.forEach { - println("Start server at: $it") - server.bindIn(scope, ServerTransport(it), acceptor) + addresses.forEach { address -> + println("Start server at: $address") + + val target = when (address.type) { + TransportType.TCP -> KtorTcpServerTransport(context).target(host = "0.0.0.0", port = address.port) + TransportType.WS -> KtorWebSocketServerTransport(context) { + httpEngine(CIO) + }.target(host = "0.0.0.0", port = address.port) + } + server.startServer(target, acceptor) } job.join() } @@ -53,7 +62,7 @@ private fun createAcceptor(): ConnectionAcceptor { val messagesApi = MessageApiImpl(messages, chats) return ConnectionAcceptor { - val userName = config.setupPayload.data.readText() + val userName = config.setupPayload.data.readString() val user = users.getOrCreate(userName) val session = Session(user.id) @@ -80,6 +89,7 @@ private fun createAcceptor(): ConnectionAcceptor { "messages.send" -> proto.decoding(it) { (chatId, content) -> messagesApi.send(chatId, content) } + "messages.history" -> proto.decoding>(it) { (chatId, limit) -> messagesApi.history(chatId, limit) } diff --git a/samples/chat/server/src/commonMain/kotlin/expects.kt b/samples/chat/server/src/commonMain/kotlin/expects.kt index a3275e82..18c880d5 100644 --- a/samples/chat/server/src/commonMain/kotlin/expects.kt +++ b/samples/chat/server/src/commonMain/kotlin/expects.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,13 +17,13 @@ package io.rsocket.kotlin.samples.chat.server import io.ktor.util.collections.* +import kotlinx.atomicfu.* -expect class Counter() { - fun next(): Int +class Counter() { + private val value = atomic(0) + fun next(): Int = value.addAndGet(1) } -expect fun currentMillis(): Long - class Storage { private val map: MutableMap = ConcurrentMap() private val id = Counter() diff --git a/samples/chat/server/src/commonMain/kotlin/serverTransport.common.kt b/samples/chat/server/src/commonMain/kotlin/serverTransport.common.kt deleted file mode 100644 index 33c3b39e..00000000 --- a/samples/chat/server/src/commonMain/kotlin/serverTransport.common.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.server - -import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* - -fun ServerTransport(address: ServerAddress): ServerTransport<*> = - serverTransport(address.type, "0.0.0.0", address.port) - -expect fun serverTransport( - type: TransportType, - host: String, - port: Int -): ServerTransport<*> diff --git a/samples/chat/server/src/jsMain/kotlin/App.kt b/samples/chat/server/src/jsMain/kotlin/App.kt index 75869a0e..e22f7138 100644 --- a/samples/chat/server/src/jsMain/kotlin/App.kt +++ b/samples/chat/server/src/jsMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,5 +19,5 @@ package io.rsocket.kotlin.samples.chat.server import io.rsocket.kotlin.samples.chat.api.* suspend fun main() { - startServer(Servers.JS.TCP) + startServer(Servers.JS) } diff --git a/samples/chat/server/src/jsMain/kotlin/serverTransport.kt b/samples/chat/server/src/jsMain/kotlin/serverTransport.kt deleted file mode 100644 index 2179fdcc..00000000 --- a/samples/chat/server/src/jsMain/kotlin/serverTransport.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.server - -import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* -import io.rsocket.kotlin.transport.nodejs.tcp.* - -actual fun serverTransport( - type: TransportType, - host: String, - port: Int -): ServerTransport<*> = when (type) { - TransportType.TCP -> TcpServerTransport(port, host) - TransportType.WS -> error("WebSocket is not supported") -} diff --git a/samples/chat/server/src/jvmMain/kotlin/App.kt b/samples/chat/server/src/jvmMain/kotlin/App.kt index 3cd204e7..fec72307 100644 --- a/samples/chat/server/src/jvmMain/kotlin/App.kt +++ b/samples/chat/server/src/jvmMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,5 +19,5 @@ package io.rsocket.kotlin.samples.chat.server import io.rsocket.kotlin.samples.chat.api.* suspend fun main() { - startServer(Servers.JVM.TCP, Servers.JVM.WS) + startServer(Servers.JVM) } diff --git a/samples/chat/server/src/jvmMain/kotlin/expects.jvm.kt b/samples/chat/server/src/jvmMain/kotlin/expects.jvm.kt deleted file mode 100644 index d3bd866e..00000000 --- a/samples/chat/server/src/jvmMain/kotlin/expects.jvm.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.server - -import java.util.concurrent.atomic.* - -actual class Counter { - private val atomic = AtomicInteger(0) - actual fun next(): Int = atomic.incrementAndGet() -} - -actual fun currentMillis(): Long = System.currentTimeMillis() diff --git a/samples/chat/server/src/jvmMain/kotlin/serverTransport.kt b/samples/chat/server/src/jvmMain/kotlin/serverTransport.kt deleted file mode 100644 index 84a8b6fe..00000000 --- a/samples/chat/server/src/jvmMain/kotlin/serverTransport.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.server - -import io.ktor.server.cio.* -import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* -import io.rsocket.kotlin.transport.ktor.tcp.* -import io.rsocket.kotlin.transport.ktor.websocket.server.* - -actual fun serverTransport( - type: TransportType, - host: String, - port: Int -): ServerTransport<*> = when (type) { - TransportType.TCP -> TcpServerTransport(host, port) - TransportType.WS -> WebSocketServerTransport(CIO, port, host) -} diff --git a/samples/chat/server/src/nativeMain/kotlin/App.kt b/samples/chat/server/src/nativeMain/kotlin/App.kt index 4d60112b..4d710291 100644 --- a/samples/chat/server/src/nativeMain/kotlin/App.kt +++ b/samples/chat/server/src/nativeMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,5 +20,5 @@ import io.rsocket.kotlin.samples.chat.api.* import kotlinx.coroutines.* fun main(): Unit = runBlocking { - startServer(Servers.Native.TCP, Servers.Native.WS) + startServer(Servers.Native) } diff --git a/samples/chat/server/src/nativeMain/kotlin/expects.native.kt b/samples/chat/server/src/nativeMain/kotlin/expects.native.kt deleted file mode 100644 index cd3482b0..00000000 --- a/samples/chat/server/src/nativeMain/kotlin/expects.native.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2015-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.server - -import kotlin.concurrent.* -import kotlin.system.* - -actual class Counter { - private val atomic = AtomicInt(0) - actual fun next(): Int = atomic.addAndGet(1) -} - -@Suppress("DEPRECATION") -actual fun currentMillis(): Long = getTimeMillis() diff --git a/samples/chat/server/src/nativeMain/kotlin/serverTransport.kt b/samples/chat/server/src/nativeMain/kotlin/serverTransport.kt deleted file mode 100644 index 84a8b6fe..00000000 --- a/samples/chat/server/src/nativeMain/kotlin/serverTransport.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.rsocket.kotlin.samples.chat.server - -import io.ktor.server.cio.* -import io.rsocket.kotlin.samples.chat.api.* -import io.rsocket.kotlin.transport.* -import io.rsocket.kotlin.transport.ktor.tcp.* -import io.rsocket.kotlin.transport.ktor.websocket.server.* - -actual fun serverTransport( - type: TransportType, - host: String, - port: Int -): ServerTransport<*> = when (type) { - TransportType.TCP -> TcpServerTransport(host, port) - TransportType.WS -> WebSocketServerTransport(CIO, port, host) -} diff --git a/samples/chat/server/src/jsMain/kotlin/expects.js.kt b/samples/chat/server/src/wasmJsMain/kotlin/App.kt similarity index 74% rename from samples/chat/server/src/jsMain/kotlin/expects.js.kt rename to samples/chat/server/src/wasmJsMain/kotlin/App.kt index a209e833..732258f2 100644 --- a/samples/chat/server/src/jsMain/kotlin/expects.js.kt +++ b/samples/chat/server/src/wasmJsMain/kotlin/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2015-2022 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,8 @@ package io.rsocket.kotlin.samples.chat.server -import kotlin.js.* +import io.rsocket.kotlin.samples.chat.api.* -actual class Counter { - private var value = 0 - actual fun next(): Int = value++ +suspend fun main() { + startServer(Servers.WasmJS) } - -actual fun currentMillis(): Long = Date.now().toLong()