From 6e83f3f3ed43eacf82a362a4ac8c6d917ac0da51 Mon Sep 17 00:00:00 2001 From: Oleg Yukhnevich Date: Thu, 31 Oct 2024 15:24:47 +0200 Subject: [PATCH] KTOR-6004 Support TCP and Unix socket for wasm-js and js via NodeJS (#4411) * Drop `jvmAndNix` shared source set * Commonize `ktor-network` and `ktor-network-tls` * Support TCP and Unix sockets for wasm-js and js on Node * Move `supportsUnixDomainSockets` to posix and use Platform instead of expect/actual --- buildSrc/src/main/kotlin/TargetsConfig.kt | 10 +- ktor-network/api/ktor-network.klib.api | 26 ++- ktor-network/build.gradle.kts | 4 +- .../io/ktor/network/selector/Selectable.kt | 0 .../network/selector/SelectorManagerCommon.kt | 1 - .../src/io/ktor/network/sockets/Builders.kt | 0 .../src/io/ktor/network/sockets/Datagram.kt | 4 +- .../io/ktor/network/sockets/SocketAddress.kt | 2 +- .../io/ktor/network/sockets/SocketEngine.kt | 32 ++++ .../io/ktor/network/sockets/SocketOptions.kt | 7 +- .../src/io/ktor/network/sockets/Sockets.kt | 4 +- .../ktor/network/sockets/TcpSocketBuilder.kt | 4 +- .../io/ktor/network/sockets/TypeOfService.kt | 4 +- .../ktor/network/sockets/UDPSocketBuilder.kt | 17 +- .../network/sockets/tests/TCPSocketTest.kt | 65 +++---- .../ktor/network/sockets/tests/TestUtils.kt | 35 ++++ ktor-network/gradle.properties | 5 + .../network/sockets/tests/TestUtilsIos.kt | 7 - .../network/sockets/nodejs/node.net.js.kt | 45 +++++ .../selector/Selectable.jsAndWasmShared.kt} | 4 +- .../SelectorManager.jsAndWasmShared.kt | 63 +++++++ .../network/sockets/ServerSocketContext.kt | 73 ++++++++ .../io/ktor/network/sockets/SocketContext.kt | 116 ++++++++++++ .../sockets/SocketEngine.jsAndWasmShared.kt | 40 ++++ .../ktor/network/sockets/nodejs/node.net.kt | 177 ++++++++++++++++++ .../ktor/network/sockets/tests/TestUtils.kt} | 2 +- .../ktor/network/selector/SelectorManager.kt | 1 - .../ktor/network/sockets/ConnectUtilsJvm.kt | 8 +- .../network/sockets/UDPSocketBuilderJvm.kt | 4 +- .../network/sockets/tests/TestUtilsJvm.kt | 15 +- .../io/ktor/network/sockets/ConnectUtils.kt | 19 -- .../ktor/network/sockets/tests/TestUtils.kt | 28 --- .../network/sockets/tests/UDPSocketTest.kt | 8 +- .../api/ktor-network-tls.klib.api | 2 +- .../ktor-network-tls/build.gradle.kts | 2 +- .../src/io/ktor/network/tls/CipherSuites.kt | 7 +- .../src/io/ktor/network/tls/OID.kt | 6 +- .../io/ktor/network/tls/TLSClientSession.kt | 4 +- .../src/io/ktor/network/tls/TLSCommon.kt | 4 +- .../src/io/ktor/network/tls/TLSConfig.kt} | 4 +- .../network/tls/TLSConfigBuilderCommon.kt | 4 +- .../network/tls/extensions/NamedCurves.kt | 6 +- .../network/tls/extensions/PointFormat.kt | 6 +- .../tls/extensions/SignatureAlgorithm.kt | 4 +- .../network/tls/extensions/TLSExtension.kt | 4 +- .../src/io/ktor/network/tls/TLSConfig.kt | 7 - .../ktor/network/tls/CipherSuites.nonJvm.kt} | 0 .../src/io/ktor/network/tls/TLS.nonJvm.kt} | 0 .../network/tls/TLSClientSession.nonJvm.kt} | 0 .../io/ktor/network/tls/TLSConfig.nonJvm.kt} | 0 .../network/tls/TLSConfigBuilder.nonJvm.kt} | 0 .../network/sockets/tests/TestUtilsLinux.kt | 7 - .../network/sockets/SocketAddress.nonJvm.kt} | 0 .../network/sockets/SocketTimeoutException.kt | 8 +- .../ktor/network/selector/SelectorManager.kt | 1 - .../network/sockets/ConnectUtilsNative.kt | 8 +- .../network/sockets/UDPSocketBuilderNative.kt | 4 +- .../network/sockets/tests/TestUtils.posix.kt | 13 ++ .../network/sockets/tests/TestUtilsNix.kt | 13 -- .../network/sockets/tests/TestUtilsTvos.kt | 7 - .../network/sockets/nodejs/node.net.wasmJs.kt | 65 +++++++ .../network/sockets/tests/TestUtilsWatchos.kt | 7 - .../io/ktor/tests/server/plugins/CORSTest.kt | 0 63 files changed, 780 insertions(+), 243 deletions(-) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/selector/Selectable.kt (100%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/selector/SelectorManagerCommon.kt (98%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/Builders.kt (100%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/Datagram.kt (92%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/SocketAddress.kt (96%) create mode 100644 ktor-network/common/src/io/ktor/network/sockets/SocketEngine.kt rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/SocketOptions.kt (96%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/Sockets.kt (96%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/TcpSocketBuilder.kt (88%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/TypeOfService.kt (87%) rename ktor-network/{jvmAndPosix => common}/src/io/ktor/network/sockets/UDPSocketBuilder.kt (67%) rename ktor-network/{jvmAndPosix => common}/test/io/ktor/network/sockets/tests/TCPSocketTest.kt (73%) create mode 100644 ktor-network/common/test/io/ktor/network/sockets/tests/TestUtils.kt create mode 100644 ktor-network/gradle.properties delete mode 100644 ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt create mode 100644 ktor-network/js/src/io/ktor/network/sockets/nodejs/node.net.js.kt rename ktor-network/{androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt => jsAndWasmShared/src/io/ktor/network/selector/Selectable.jsAndWasmShared.kt} (54%) create mode 100644 ktor-network/jsAndWasmShared/src/io/ktor/network/selector/SelectorManager.jsAndWasmShared.kt create mode 100644 ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/ServerSocketContext.kt create mode 100644 ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketContext.kt create mode 100644 ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketEngine.jsAndWasmShared.kt create mode 100644 ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/nodejs/node.net.kt rename ktor-network/{macos/test/io/ktor/network/sockets/tests/TestUtilsMacos.kt => jsAndWasmShared/test/io/ktor/network/sockets/tests/TestUtils.kt} (72%) delete mode 100644 ktor-network/jvmAndPosix/src/io/ktor/network/sockets/ConnectUtils.kt delete mode 100644 ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/TestUtils.kt rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/CipherSuites.kt (96%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/OID.kt (95%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/TLSClientSession.kt (71%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/TLSCommon.kt (92%) rename ktor-network/{windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt => ktor-network-tls/common/src/io/ktor/network/tls/TLSConfig.kt} (54%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/TLSConfigBuilderCommon.kt (79%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/extensions/NamedCurves.kt (91%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/extensions/PointFormat.kt (81%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/extensions/SignatureAlgorithm.kt (96%) rename ktor-network/ktor-network-tls/{jvmAndPosix => common}/src/io/ktor/network/tls/extensions/TLSExtension.kt (86%) delete mode 100644 ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSConfig.kt rename ktor-network/ktor-network-tls/{posix/src/io/ktor/network/tls/CipherSuitesNative.kt => nonJvm/src/io/ktor/network/tls/CipherSuites.nonJvm.kt} (100%) rename ktor-network/ktor-network-tls/{posix/src/io/ktor/network/tls/TLSNative.kt => nonJvm/src/io/ktor/network/tls/TLS.nonJvm.kt} (100%) rename ktor-network/ktor-network-tls/{posix/src/io/ktor/network/tls/TLSClientSessionNative.kt => nonJvm/src/io/ktor/network/tls/TLSClientSession.nonJvm.kt} (100%) rename ktor-network/ktor-network-tls/{posix/src/io/ktor/network/tls/TLSConfigNative.kt => nonJvm/src/io/ktor/network/tls/TLSConfig.nonJvm.kt} (100%) rename ktor-network/ktor-network-tls/{posix/src/io/ktor/network/tls/TLSConfigBuilderNative.kt => nonJvm/src/io/ktor/network/tls/TLSConfigBuilder.nonJvm.kt} (100%) delete mode 100644 ktor-network/linux/test/io/ktor/network/sockets/tests/TestUtilsLinux.kt rename ktor-network/{posix/src/io/ktor/network/sockets/SocketAddressNative.kt => nonJvm/src/io/ktor/network/sockets/SocketAddress.nonJvm.kt} (100%) rename ktor-network/{posix => nonJvm}/src/io/ktor/network/sockets/SocketTimeoutException.kt (51%) create mode 100644 ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtils.posix.kt delete mode 100644 ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtilsNix.kt delete mode 100644 ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt create mode 100644 ktor-network/wasmJs/src/io/ktor/network/sockets/nodejs/node.net.wasmJs.kt delete mode 100644 ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt delete mode 100644 ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CORSTest.kt diff --git a/buildSrc/src/main/kotlin/TargetsConfig.kt b/buildSrc/src/main/kotlin/TargetsConfig.kt index d82cb47dfe3..d8f652d5b04 100644 --- a/buildSrc/src/main/kotlin/TargetsConfig.kt +++ b/buildSrc/src/main/kotlin/TargetsConfig.kt @@ -110,16 +110,16 @@ private val hierarchyTemplate = KotlinHierarchyTemplate { group("posix") } - group("jvmAndNix") { - withJvm() - group("nix") - } - group("desktop") { group("linux") group("windows") group("macos") } + + group("nonJvm") { + group("posix") + group("jsAndWasmShared") + } } } diff --git a/ktor-network/api/ktor-network.klib.api b/ktor-network/api/ktor-network.klib.api index d2cdca9a47b..c3700e181ed 100644 --- a/ktor-network/api/ktor-network.klib.api +++ b/ktor-network/api/ktor-network.klib.api @@ -1,5 +1,6 @@ // Klib ABI Dump -// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Alias: native => [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true @@ -36,11 +37,6 @@ abstract interface <#A: out io.ktor.network.sockets/Configurable<#A, #B>, #B: io open fun configure(kotlin/Function1<#B, kotlin/Unit>): #A // io.ktor.network.sockets/Configurable.configure|configure(kotlin.Function1<1:1,kotlin.Unit>){}[0] } -abstract interface io.ktor.network.selector/Selectable { // io.ktor.network.selector/Selectable|null[0] - abstract val descriptor // io.ktor.network.selector/Selectable.descriptor|{}descriptor[0] - abstract fun (): kotlin/Int // io.ktor.network.selector/Selectable.descriptor.|(){}[0] -} - abstract interface io.ktor.network.selector/SelectorManager : io.ktor.utils.io.core/Closeable, kotlinx.coroutines/CoroutineScope { // io.ktor.network.selector/SelectorManager|null[0] abstract fun notifyClosed(io.ktor.network.selector/Selectable) // io.ktor.network.selector/SelectorManager.notifyClosed|notifyClosed(io.ktor.network.selector.Selectable){}[0] abstract suspend fun select(io.ktor.network.selector/Selectable, io.ktor.network.selector/SelectInterest) // io.ktor.network.selector/SelectorManager.select|select(io.ktor.network.selector.Selectable;io.ktor.network.selector.SelectInterest){}[0] @@ -103,10 +99,6 @@ final class io.ktor.network.selector/ClosedChannelCancellationException : kotlin constructor () // io.ktor.network.selector/ClosedChannelCancellationException.|(){}[0] } -final class io.ktor.network.selector/SocketError : kotlin/IllegalStateException { // io.ktor.network.selector/SocketError|null[0] - constructor () // io.ktor.network.selector/SocketError.|(){}[0] -} - final class io.ktor.network.sockets/Connection { // io.ktor.network.sockets/Connection|null[0] constructor (io.ktor.network.sockets/Socket, io.ktor.utils.io/ByteReadChannel, io.ktor.utils.io/ByteWriteChannel) // io.ktor.network.sockets/Connection.|(io.ktor.network.sockets.Socket;io.ktor.utils.io.ByteReadChannel;io.ktor.utils.io.ByteWriteChannel){}[0] @@ -284,3 +276,17 @@ final fun <#A: io.ktor.network.sockets/Configurable<#A, *>> (#A).io.ktor.network final fun io.ktor.network.selector/SelectorManager(kotlin.coroutines/CoroutineContext = ...): io.ktor.network.selector/SelectorManager // io.ktor.network.selector/SelectorManager|SelectorManager(kotlin.coroutines.CoroutineContext){}[0] final fun io.ktor.network.sockets/aSocket(io.ktor.network.selector/SelectorManager): io.ktor.network.sockets/SocketBuilder // io.ktor.network.sockets/aSocket|aSocket(io.ktor.network.selector.SelectorManager){}[0] final suspend fun (io.ktor.network.sockets/ASocket).io.ktor.network.sockets/awaitClosed() // io.ktor.network.sockets/awaitClosed|awaitClosed@io.ktor.network.sockets.ASocket(){}[0] + +// Targets: [native] +abstract interface io.ktor.network.selector/Selectable { // io.ktor.network.selector/Selectable|null[0] + abstract val descriptor // io.ktor.network.selector/Selectable.descriptor|{}descriptor[0] + abstract fun (): kotlin/Int // io.ktor.network.selector/Selectable.descriptor.|(){}[0] +} + +// Targets: [native] +final class io.ktor.network.selector/SocketError : kotlin/IllegalStateException { // io.ktor.network.selector/SocketError|null[0] + constructor () // io.ktor.network.selector/SocketError.|(){}[0] +} + +// Targets: [js, wasmJs] +abstract interface io.ktor.network.selector/Selectable // io.ktor.network.selector/Selectable|null[0] diff --git a/ktor-network/build.gradle.kts b/ktor-network/build.gradle.kts index a3c871ddec5..b2ac809bb7d 100644 --- a/ktor-network/build.gradle.kts +++ b/ktor-network/build.gradle.kts @@ -10,13 +10,13 @@ kotlin { } sourceSets { - jvmAndPosixMain { + commonMain { dependencies { api(project(":ktor-utils")) } } - jvmAndPosixTest { + commonTest { dependencies { api(project(":ktor-test-dispatcher")) } diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/selector/Selectable.kt b/ktor-network/common/src/io/ktor/network/selector/Selectable.kt similarity index 100% rename from ktor-network/jvmAndPosix/src/io/ktor/network/selector/Selectable.kt rename to ktor-network/common/src/io/ktor/network/selector/Selectable.kt diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/selector/SelectorManagerCommon.kt b/ktor-network/common/src/io/ktor/network/selector/SelectorManagerCommon.kt similarity index 98% rename from ktor-network/jvmAndPosix/src/io/ktor/network/selector/SelectorManagerCommon.kt rename to ktor-network/common/src/io/ktor/network/selector/SelectorManagerCommon.kt index beb54cbb3ac..214c1571ab6 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/selector/SelectorManagerCommon.kt +++ b/ktor-network/common/src/io/ktor/network/selector/SelectorManagerCommon.kt @@ -11,7 +11,6 @@ import kotlin.coroutines.* /** * Creates the selector manager for current platform. */ -@Suppress("FunctionName") public expect fun SelectorManager( dispatcher: CoroutineContext = EmptyCoroutineContext ): SelectorManager diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Builders.kt b/ktor-network/common/src/io/ktor/network/sockets/Builders.kt similarity index 100% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Builders.kt rename to ktor-network/common/src/io/ktor/network/sockets/Builders.kt diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Datagram.kt b/ktor-network/common/src/io/ktor/network/sockets/Datagram.kt similarity index 92% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Datagram.kt rename to ktor-network/common/src/io/ktor/network/sockets/Datagram.kt index 003f542ce60..ea916b86f9f 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Datagram.kt +++ b/ktor-network/common/src/io/ktor/network/sockets/Datagram.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.network.sockets diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/SocketAddress.kt b/ktor-network/common/src/io/ktor/network/sockets/SocketAddress.kt similarity index 96% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/SocketAddress.kt rename to ktor-network/common/src/io/ktor/network/sockets/SocketAddress.kt index eb66b7062bc..d7df452da83 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/SocketAddress.kt +++ b/ktor-network/common/src/io/ktor/network/sockets/SocketAddress.kt @@ -1,5 +1,5 @@ /* - * 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.network.sockets diff --git a/ktor-network/common/src/io/ktor/network/sockets/SocketEngine.kt b/ktor-network/common/src/io/ktor/network/sockets/SocketEngine.kt new file mode 100644 index 00000000000..479cb567153 --- /dev/null +++ b/ktor-network/common/src/io/ktor/network/sockets/SocketEngine.kt @@ -0,0 +1,32 @@ +/* + * 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.sockets + +import io.ktor.network.selector.* + +internal expect suspend fun tcpConnect( + selector: SelectorManager, + remoteAddress: SocketAddress, + socketOptions: SocketOptions.TCPClientSocketOptions +): Socket + +internal expect suspend fun tcpBind( + selector: SelectorManager, + localAddress: SocketAddress?, + socketOptions: SocketOptions.AcceptorOptions +): ServerSocket + +internal expect suspend fun udpConnect( + selector: SelectorManager, + remoteAddress: SocketAddress, + localAddress: SocketAddress?, + options: SocketOptions.UDPSocketOptions +): ConnectedDatagramSocket + +internal expect suspend fun udpBind( + selector: SelectorManager, + localAddress: SocketAddress?, + options: SocketOptions.UDPSocketOptions +): BoundDatagramSocket diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/SocketOptions.kt b/ktor-network/common/src/io/ktor/network/sockets/SocketOptions.kt similarity index 96% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/SocketOptions.kt rename to ktor-network/common/src/io/ktor/network/sockets/SocketOptions.kt index 539536086da..5bf016ac4e7 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/SocketOptions.kt +++ b/ktor-network/common/src/io/ktor/network/sockets/SocketOptions.kt @@ -4,12 +4,11 @@ package io.ktor.network.sockets -internal const val INFINITE_TIMEOUT_MS = Long.MAX_VALUE +private const val INFINITE_TIMEOUT_MS = Long.MAX_VALUE /** * Socket options builder */ -@OptIn(ExperimentalUnsignedTypes::class) public sealed class SocketOptions( protected val customOptions: MutableMap ) { @@ -30,7 +29,7 @@ public sealed class SocketOptions( } } - internal fun acceptor(): AcceptorOptions { + internal fun tcpAccept(): AcceptorOptions { return AcceptorOptions(HashMap(customOptions)).apply { copyCommon(this@SocketOptions) } @@ -113,7 +112,7 @@ public sealed class SocketOptions( } } - internal fun tcp(): TCPClientSocketOptions { + internal fun tcpConnect(): TCPClientSocketOptions { return TCPClientSocketOptions(HashMap(customOptions)).apply { copyCommon(this@PeerSocketOptions) } diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Sockets.kt b/ktor-network/common/src/io/ktor/network/sockets/Sockets.kt similarity index 96% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Sockets.kt rename to ktor-network/common/src/io/ktor/network/sockets/Sockets.kt index cdaca9436ca..c8a9fa005b7 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/Sockets.kt +++ b/ktor-network/common/src/io/ktor/network/sockets/Sockets.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.network.sockets diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/TcpSocketBuilder.kt b/ktor-network/common/src/io/ktor/network/sockets/TcpSocketBuilder.kt similarity index 88% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/TcpSocketBuilder.kt rename to ktor-network/common/src/io/ktor/network/sockets/TcpSocketBuilder.kt index 99342ff9e32..ec88283f44e 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/TcpSocketBuilder.kt +++ b/ktor-network/common/src/io/ktor/network/sockets/TcpSocketBuilder.kt @@ -37,7 +37,7 @@ public class TcpSocketBuilder internal constructor( public suspend fun connect( remoteAddress: SocketAddress, configure: SocketOptions.TCPClientSocketOptions.() -> Unit = {} - ): Socket = connect(selector, remoteAddress, options.tcp().apply(configure)) + ): Socket = tcpConnect(selector, remoteAddress, options.tcpConnect().apply(configure)) /** * Bind server socket to listen to [localAddress]. @@ -45,5 +45,5 @@ public class TcpSocketBuilder internal constructor( public suspend fun bind( localAddress: SocketAddress? = null, configure: SocketOptions.AcceptorOptions.() -> Unit = {} - ): ServerSocket = bind(selector, localAddress, options.acceptor().apply(configure)) + ): ServerSocket = tcpBind(selector, localAddress, options.tcpAccept().apply(configure)) } diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/TypeOfService.kt b/ktor-network/common/src/io/ktor/network/sockets/TypeOfService.kt similarity index 87% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/TypeOfService.kt rename to ktor-network/common/src/io/ktor/network/sockets/TypeOfService.kt index df2fb6324e0..f080844b264 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/TypeOfService.kt +++ b/ktor-network/common/src/io/ktor/network/sockets/TypeOfService.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.network.sockets diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/UDPSocketBuilder.kt b/ktor-network/common/src/io/ktor/network/sockets/UDPSocketBuilder.kt similarity index 67% rename from ktor-network/jvmAndPosix/src/io/ktor/network/sockets/UDPSocketBuilder.kt rename to ktor-network/common/src/io/ktor/network/sockets/UDPSocketBuilder.kt index 5fb65194de3..eb8402565e6 100644 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/UDPSocketBuilder.kt +++ b/ktor-network/common/src/io/ktor/network/sockets/UDPSocketBuilder.kt @@ -19,7 +19,7 @@ public class UDPSocketBuilder internal constructor( public suspend fun bind( localAddress: SocketAddress? = null, configure: SocketOptions.UDPSocketOptions.() -> Unit = {} - ): BoundDatagramSocket = bindUDP(selector, localAddress, options.udp().apply(configure)) + ): BoundDatagramSocket = udpBind(selector, localAddress, options.udp().apply(configure)) /** * Create a datagram socket to listen datagrams at [localAddress] and set to [remoteAddress]. @@ -28,18 +28,5 @@ public class UDPSocketBuilder internal constructor( remoteAddress: SocketAddress, localAddress: SocketAddress? = null, configure: SocketOptions.UDPSocketOptions.() -> Unit = {} - ): ConnectedDatagramSocket = connectUDP(selector, remoteAddress, localAddress, options.udp().apply(configure)) + ): ConnectedDatagramSocket = udpConnect(selector, remoteAddress, localAddress, options.udp().apply(configure)) } - -internal expect fun connectUDP( - selector: SelectorManager, - remoteAddress: SocketAddress, - localAddress: SocketAddress?, - options: SocketOptions.UDPSocketOptions -): ConnectedDatagramSocket - -internal expect fun bindUDP( - selector: SelectorManager, - localAddress: SocketAddress?, - options: SocketOptions.UDPSocketOptions -): BoundDatagramSocket diff --git a/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/TCPSocketTest.kt b/ktor-network/common/test/io/ktor/network/sockets/tests/TCPSocketTest.kt similarity index 73% rename from ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/TCPSocketTest.kt rename to ktor-network/common/test/io/ktor/network/sockets/tests/TCPSocketTest.kt index 71f9543899b..3687478c547 100644 --- a/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/TCPSocketTest.kt +++ b/ktor-network/common/test/io/ktor/network/sockets/tests/TCPSocketTest.kt @@ -62,48 +62,49 @@ class TCPSocketTest { if (!supportsUnixDomainSockets()) return@testSockets val socketPath = createTempFilePath("ktor-echo-test") + try { + val tcp = aSocket(selector).tcp() + val server = tcp.bind(UnixSocketAddress(socketPath)) - val tcp = aSocket(selector).tcp() - val server = tcp.bind(UnixSocketAddress(socketPath)) + val serverConnectionPromise = async { + server.accept() + } - val serverConnectionPromise = async { - server.accept() - } + val clientConnection = tcp.connect(UnixSocketAddress(socketPath)) + val serverConnection = serverConnectionPromise.await() - val clientConnection = tcp.connect(UnixSocketAddress(socketPath)) - val serverConnection = serverConnectionPromise.await() + val clientOutput = clientConnection.openWriteChannel() + try { + clientOutput.writeStringUtf8("Hello, world\n") + clientOutput.flush() + } finally { + clientOutput.flushAndClose() + } - val clientOutput = clientConnection.openWriteChannel() - try { - clientOutput.writeStringUtf8("Hello, world\n") - clientOutput.flush() - } finally { - clientOutput.flushAndClose() - } + val serverInput = serverConnection.openReadChannel() + val message = serverInput.readUTF8Line() + assertEquals("Hello, world", message) - val serverInput = serverConnection.openReadChannel() - val message = serverInput.readUTF8Line() - assertEquals("Hello, world", message) + val serverOutput = serverConnection.openWriteChannel() + try { + serverOutput.writeStringUtf8("Hello From Server\n") + serverOutput.flush() - val serverOutput = serverConnection.openWriteChannel() - try { - serverOutput.writeStringUtf8("Hello From Server\n") - serverOutput.flush() + val clientInput = clientConnection.openReadChannel() + val echo = clientInput.readUTF8Line() - val clientInput = clientConnection.openReadChannel() - val echo = clientInput.readUTF8Line() + assertEquals("Hello From Server", echo) + } finally { + serverOutput.flushAndClose() + } - assertEquals("Hello From Server", echo) + serverConnection.close() + clientConnection.close() + + server.close() } finally { - serverOutput.flushAndClose() + removeFile(socketPath) } - - serverConnection.close() - clientConnection.close() - - server.close() - - removeFile(socketPath) } @Test diff --git a/ktor-network/common/test/io/ktor/network/sockets/tests/TestUtils.kt b/ktor-network/common/test/io/ktor/network/sockets/tests/TestUtils.kt new file mode 100644 index 00000000000..d80261d204c --- /dev/null +++ b/ktor-network/common/test/io/ktor/network/sockets/tests/TestUtils.kt @@ -0,0 +1,35 @@ +/* + * 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.sockets.tests + +import io.ktor.network.selector.* +import io.ktor.test.dispatcher.* +import io.ktor.utils.io.core.* +import kotlinx.coroutines.* +import kotlinx.coroutines.test.* +import kotlinx.io.files.* +import kotlin.time.* +import kotlin.time.Duration.Companion.minutes +import kotlin.uuid.* + +internal fun testSockets( + timeout: Duration = 1.minutes, + block: suspend CoroutineScope.(SelectorManager) -> Unit +): TestResult = runTestWithRealTime(timeout = timeout) { + SelectorManager().use { selector -> + block(selector) + } +} + +internal expect fun Any.supportsUnixDomainSockets(): Boolean + +@OptIn(ExperimentalUuidApi::class) +internal fun createTempFilePath(basename: String): String { + return Path(SystemTemporaryDirectory, "$basename-${Uuid.random()}").toString() +} + +internal fun removeFile(path: String) { + SystemFileSystem.delete(Path(path), mustExist = false) +} diff --git a/ktor-network/gradle.properties b/ktor-network/gradle.properties new file mode 100644 index 00000000000..d12c10bfad8 --- /dev/null +++ b/ktor-network/gradle.properties @@ -0,0 +1,5 @@ +# +# Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# +target.js.browser=false +target.wasmJs.browser=false diff --git a/ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt b/ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt deleted file mode 100644 index 190092748d8..00000000000 --- a/ktor-network/ios/test/io/ktor/network/sockets/tests/TestUtilsIos.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.network.sockets.tests - -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false diff --git a/ktor-network/js/src/io/ktor/network/sockets/nodejs/node.net.js.kt b/ktor-network/js/src/io/ktor/network/sockets/nodejs/node.net.js.kt new file mode 100644 index 00000000000..7a89dc616b7 --- /dev/null +++ b/ktor-network/js/src/io/ktor/network/sockets/nodejs/node.net.js.kt @@ -0,0 +1,45 @@ +/* + * 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.sockets.nodejs + +import io.ktor.network.sockets.* +import org.khronos.webgl.* + +internal actual fun nodeNet(): NodeNet? = js("eval('require')('node:net')").unsafeCast() + +internal actual fun TcpCreateConnectionOptions( + block: TcpCreateConnectionOptions.() -> Unit +): TcpCreateConnectionOptions = createObject(block) + +internal actual fun IpcCreateConnectionOptions( + block: IpcCreateConnectionOptions.() -> Unit +): IpcCreateConnectionOptions = createObject(block) + +internal actual fun CreateServerOptions( + block: CreateServerOptions.() -> Unit +): CreateServerOptions = createObject(block) + +internal actual fun ServerListenOptions( + block: ServerListenOptions.() -> Unit +): ServerListenOptions = createObject(block) + +private fun createObject(block: T.() -> Unit): T = js("{}").unsafeCast().apply(block) + +internal actual fun JsError.toThrowable(): Throwable = unsafeCast() +internal actual fun Throwable.toJsError(): JsError? = unsafeCast() + +internal actual fun ByteArray.toJsBuffer(fromIndex: Int, toIndex: Int): JsBuffer { + return unsafeCast().subarray(fromIndex, toIndex).unsafeCast() +} + +internal actual fun JsBuffer.toByteArray(): ByteArray { + return Int8Array(unsafeCast()).unsafeCast() +} + +internal actual fun ServerLocalAddressInfo.toSocketAddress(): SocketAddress { + if (jsTypeOf(this) == "string") return UnixSocketAddress(unsafeCast()) + val info = unsafeCast() + return InetSocketAddress(info.address, info.port) +} diff --git a/ktor-network/androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt b/ktor-network/jsAndWasmShared/src/io/ktor/network/selector/Selectable.jsAndWasmShared.kt similarity index 54% rename from ktor-network/androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt rename to ktor-network/jsAndWasmShared/src/io/ktor/network/selector/Selectable.jsAndWasmShared.kt index 5006b7bb955..d462ef91350 100644 --- a/ktor-network/androidNative/test/io/ktor/network/sockets/tests/TestUtils.androidNative.kt +++ b/ktor-network/jsAndWasmShared/src/io/ktor/network/selector/Selectable.jsAndWasmShared.kt @@ -2,6 +2,6 @@ * 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.sockets.tests +package io.ktor.network.selector -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false +public actual interface Selectable diff --git a/ktor-network/jsAndWasmShared/src/io/ktor/network/selector/SelectorManager.jsAndWasmShared.kt b/ktor-network/jsAndWasmShared/src/io/ktor/network/selector/SelectorManager.jsAndWasmShared.kt new file mode 100644 index 00000000000..4331d052a50 --- /dev/null +++ b/ktor-network/jsAndWasmShared/src/io/ktor/network/selector/SelectorManager.jsAndWasmShared.kt @@ -0,0 +1,63 @@ +/* +* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +*/ + +package io.ktor.network.selector + +import io.ktor.utils.io.core.* +import kotlinx.coroutines.* +import kotlin.coroutines.* + +public actual fun SelectorManager(dispatcher: CoroutineContext): SelectorManager = NoopSelectorManager + +public actual interface SelectorManager : CoroutineScope, Closeable { + /** + * Notifies the selector that selectable has been closed. + */ + public actual fun notifyClosed(selectable: Selectable) + + /** + * Suspends until [interest] is selected for [selectable] + * May cause manager to allocate and run selector instance if not yet created. + * + * Only one selection is allowed per [interest] per [selectable] but you can + * select for different interests for the same selectable simultaneously. + * In other words you can select for read and write at the same time but should never + * try to read twice for the same selectable. + */ + public actual suspend fun select( + selectable: Selectable, + interest: SelectInterest + ) + + public actual companion object +} + +/** + * Select interest kind + */ +public actual enum class SelectInterest { + READ, WRITE, ACCEPT, CONNECT; + + public actual companion object { + public actual val AllInterests: Array + get() = entries.toTypedArray() + } +} + +// TODO: how coroutine context should be used? +private object NoopSelectorManager : SelectorManager { + override val coroutineContext: CoroutineContext get() = EmptyCoroutineContext + + override fun notifyClosed(selectable: Selectable) { + error("not supported") + } + + override suspend fun select(selectable: Selectable, interest: SelectInterest) { + error("not supported") + } + + override fun close() { + // no-op so it can be called in common code + } +} diff --git a/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/ServerSocketContext.kt b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/ServerSocketContext.kt new file mode 100644 index 00000000000..f14801e28e8 --- /dev/null +++ b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/ServerSocketContext.kt @@ -0,0 +1,73 @@ +/* + * 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.sockets + +import io.ktor.network.sockets.nodejs.* +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* +import kotlinx.io.* +import kotlin.coroutines.* + +internal class ServerSocketContext( + private val server: Server, + private val localAddress: SocketAddress?, + parentContext: Job? +) { + private val incomingSockets = Channel(Channel.UNLIMITED) + private val serverContext = SupervisorJob(parentContext) + + fun initiate(cont: CancellableContinuation) { + cont.invokeOnCancellation { + server.close() + + serverContext.cancel() + incomingSockets.cancel() + } + + server.onConnection { socket -> + val context = SocketContext(socket, localAddress, serverContext) + context.initiate(null) + incomingSockets.trySend(context.createSocket()) + } + server.onClose { + if (cont.isActive) { + cont.resumeWithException(IOException("Failed to bind")) + } else { + serverContext.job.cancel("Server closed") + } + } + server.onError { error -> + if (cont.isActive) { + cont.resumeWithException(IOException("Failed to bind", error.toThrowable())) + } else { + serverContext.job.cancel("Server failed", error.toThrowable()) + } + } + server.onListening { + cont.resume(ServerSocketImpl(server.address()!!.toSocketAddress(), serverContext, incomingSockets, server)) + } + server.listen(ServerListenOptions(localAddress)) + } +} + +private class ServerSocketImpl( + override val localAddress: SocketAddress, + override val socketContext: Job, + private val incoming: ReceiveChannel, + private val server: Server +) : ServerSocket { + override suspend fun accept(): Socket = incoming.receive() + + init { + socketContext.invokeOnCompletion { + server.close() + incoming.cancel() + } + } + + override fun close() { + socketContext.cancel("Server socket closed") + } +} diff --git a/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketContext.kt b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketContext.kt new file mode 100644 index 00000000000..909d86b0ed2 --- /dev/null +++ b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketContext.kt @@ -0,0 +1,116 @@ +/* + * 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.sockets + +import io.ktor.network.sockets.nodejs.* +import io.ktor.utils.io.* +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* +import kotlinx.io.* +import kotlin.coroutines.* +import io.ktor.network.sockets.nodejs.Socket as NodejsSocket + +internal class SocketContext( + private val socket: NodejsSocket, + private val address: SocketAddress?, + parentContext: Job? +) { + private val incomingFrames: Channel = Channel(Channel.UNLIMITED) + private val socketContext = Job(parentContext) + + fun initiate(connectCont: CancellableContinuation?) { + connectCont?.invokeOnCancellation { + socket.destroy(it?.toJsError()) + + socketContext.cancel() + incomingFrames.cancel() + } + socket.onError { error -> + when (connectCont?.isActive) { + true -> connectCont.resumeWithException(IOException("Failed to connect", error.toThrowable())) + else -> socketContext.job.cancel("Socket error", error.toThrowable()) + } + } + socket.onTimeout { + when (connectCont?.isActive) { + true -> connectCont.resumeWithException(SocketTimeoutException("timeout")) + else -> socketContext.job.cancel("Socket timeout", SocketTimeoutException("timeout")) + } + } + socket.onEnd { + incomingFrames.close() + } + socket.onClose { + socketContext.job.cancel("Socket closed") + } + socket.onData { data -> + incomingFrames.trySend(data) + } + + if (connectCont != null) { + socket.onConnect { + connectCont.resume(createSocket()) + } + } + } + + // Socket real address could be resolved only after the ` connect ` event. + // Also, Node.js doesn't give access to unix address from the ` socket ` object, + // so we need to store it. + fun createSocket(): Socket = SocketImpl( + localAddress = when (address) { + is UnixSocketAddress -> address + else -> InetSocketAddress(socket.localAddress, socket.localPort) + }, + remoteAddress = when (address) { + is UnixSocketAddress -> address + else -> InetSocketAddress(socket.remoteAddress, socket.remotePort) + }, + coroutineContext = socketContext, + incoming = incomingFrames, + socket = socket + ) +} + +private class SocketImpl( + override val localAddress: SocketAddress, + override val remoteAddress: SocketAddress, + override val coroutineContext: CoroutineContext, + private val incoming: ReceiveChannel, + private val socket: NodejsSocket +) : Socket { + override val socketContext: Job get() = coroutineContext.job + + init { + socketContext.invokeOnCompletion { + socket.destroy(it?.toJsError()) + incoming.cancel(CancellationException("Socket closed", it)) + } + } + + override fun attachForReading(channel: ByteChannel): WriterJob = writer(Dispatchers.Unconfined, channel = channel) { + incoming.consumeEach { buffer -> + channel.writeByteArray(buffer.toByteArray()) + channel.flush() + } + } + + override fun attachForWriting(channel: ByteChannel): ReaderJob = reader(Dispatchers.Unconfined, channel = channel) { + while (true) { + val result = channel.read { bytes, startIndex, endIndex -> + socket.write(bytes.toJsBuffer(startIndex, endIndex)) + endIndex - startIndex + } + if (result == -1) { + socket.end() + break + } + } + } + + override fun close() { + socketContext.cancel("Socket closed") + } +} diff --git a/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketEngine.jsAndWasmShared.kt b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketEngine.jsAndWasmShared.kt new file mode 100644 index 00000000000..9a404891f34 --- /dev/null +++ b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/SocketEngine.jsAndWasmShared.kt @@ -0,0 +1,40 @@ +/* + * 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.sockets + +import io.ktor.network.selector.* +import io.ktor.network.sockets.nodejs.* +import kotlinx.coroutines.* + +internal actual suspend fun tcpConnect( + selector: SelectorManager, + remoteAddress: SocketAddress, + socketOptions: SocketOptions.TCPClientSocketOptions +): Socket = suspendCancellableCoroutine { cont -> + val socket = nodeNet.createConnection(CreateConnectionOptions(remoteAddress, socketOptions)) + SocketContext(socket, remoteAddress, null).initiate(cont) +} + +internal actual suspend fun tcpBind( + selector: SelectorManager, + localAddress: SocketAddress?, + socketOptions: SocketOptions.AcceptorOptions +): ServerSocket = suspendCancellableCoroutine { cont -> + val server = nodeNet.createServer(CreateServerOptions {}) + ServerSocketContext(server, localAddress, null).initiate(cont) +} + +internal actual suspend fun udpConnect( + selector: SelectorManager, + remoteAddress: SocketAddress, + localAddress: SocketAddress?, + options: SocketOptions.UDPSocketOptions +): ConnectedDatagramSocket = error("UDP sockets are unsupported on WASM/JS") + +internal actual suspend fun udpBind( + selector: SelectorManager, + localAddress: SocketAddress?, + options: SocketOptions.UDPSocketOptions +): BoundDatagramSocket = error("UDP sockets are unsupported on WASM/JS") diff --git a/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/nodejs/node.net.kt b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/nodejs/node.net.kt new file mode 100644 index 00000000000..ed3541b0cde --- /dev/null +++ b/ktor-network/jsAndWasmShared/src/io/ktor/network/sockets/nodejs/node.net.kt @@ -0,0 +1,177 @@ +/* + * 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.sockets.nodejs + +import io.ktor.network.sockets.* + +// js.Error +internal external interface JsError { + val message: String? +} + +internal expect fun JsError.toThrowable(): Throwable +internal expect fun Throwable.toJsError(): JsError? + +internal external interface JsBuffer // Int8Array + +internal expect fun ByteArray.toJsBuffer(fromIndex: Int, toIndex: Int): JsBuffer +internal expect fun JsBuffer.toByteArray(): ByteArray + +internal external interface NodeNet { + fun createConnection(options: CreateConnectionOptions): Socket + fun createServer(options: CreateServerOptions): Server +} + +internal expect fun nodeNet(): NodeNet? + +internal val nodeNet by lazy { + requireNotNull(runCatching { nodeNet() }.getOrNull()) { + "Node.js net module is not available. Please verify that you are using Node.js" + } +} + +internal fun CreateConnectionOptions( + remoteAddress: SocketAddress, + socketOptions: SocketOptions.TCPClientSocketOptions +): CreateConnectionOptions = when (remoteAddress) { + is InetSocketAddress -> TcpCreateConnectionOptions { + host = remoteAddress.hostname + port = remoteAddress.port + noDelay = socketOptions.noDelay + timeout = when (socketOptions.socketTimeout) { + Long.MAX_VALUE -> Int.MAX_VALUE + else -> socketOptions.socketTimeout.toInt() + } + keepAlive = socketOptions.keepAlive + } + + is UnixSocketAddress -> IpcCreateConnectionOptions { + path = remoteAddress.path + timeout = when (socketOptions.socketTimeout) { + Long.MAX_VALUE -> Int.MAX_VALUE + else -> socketOptions.socketTimeout.toInt() + } + } +} + +internal external interface CreateConnectionOptions { + var timeout: Int? + var allowHalfOpen: Boolean? +} + +internal expect fun TcpCreateConnectionOptions( + block: TcpCreateConnectionOptions.() -> Unit +): TcpCreateConnectionOptions + +internal external interface TcpCreateConnectionOptions : CreateConnectionOptions { + var port: Int + var host: String? + + var localAddress: String? + var localPort: Int? + var family: Int? // ip stack + var noDelay: Boolean? + var keepAlive: Boolean? +} + +internal expect fun IpcCreateConnectionOptions( + block: IpcCreateConnectionOptions.() -> Unit +): IpcCreateConnectionOptions + +internal external interface IpcCreateConnectionOptions : CreateConnectionOptions { + var path: String +} + +internal external interface Socket { + val localAddress: String + val localPort: Int + + val remoteAddress: String + val remotePort: Int + + fun write(buffer: JsBuffer): Boolean + + fun destroy(error: JsError?) + + // sends FIN + fun end() + + fun on(event: String /* "close" */, listener: (hadError: Boolean) -> Unit) + fun on(event: String /* "connect", "end", "timeout", */, listener: () -> Unit) + fun on(event: String /* "data" */, listener: (data: JsBuffer) -> Unit) + fun on(event: String /* "error" */, listener: (error: JsError) -> Unit) +} + +internal fun Socket.onClose(block: (hadError: Boolean) -> Unit): Unit = on("close", block) +internal fun Socket.onConnect(block: () -> Unit): Unit = on("connect", block) +internal fun Socket.onEnd(block: () -> Unit): Unit = on("end", block) +internal fun Socket.onTimeout(block: () -> Unit): Unit = on("timeout", block) +internal fun Socket.onData(block: (data: JsBuffer) -> Unit): Unit = on("data", block) +internal fun Socket.onError(block: (error: JsError) -> Unit): Unit = on("error", block) + +internal expect fun CreateServerOptions( + block: CreateServerOptions.() -> Unit +): CreateServerOptions + +internal external interface CreateServerOptions { + var allowHalfOpen: Boolean? + var keepAlive: Boolean? + var noDelay: Boolean? +} + +internal external interface Server { + fun address(): ServerLocalAddressInfo? + fun listen(options: ServerListenOptions) + + // stop accepting new connections + fun close() + + fun on(event: String /* "close", "listening" */, listener: () -> Unit) + fun on(event: String /* "connection" */, listener: (socket: Socket) -> Unit) + fun on(event: String /* "error" */, listener: (error: JsError) -> Unit) +} + +internal fun Server.onClose(block: () -> Unit): Unit = on("close", block) +internal fun Server.onListening(block: () -> Unit): Unit = on("listening", block) +internal fun Server.onConnection(block: (socket: Socket) -> Unit): Unit = on("connection", block) +internal fun Server.onError(block: (error: JsError) -> Unit): Unit = on("error", block) + +internal fun ServerListenOptions(localAddress: SocketAddress?): ServerListenOptions = ServerListenOptions { + when (localAddress) { + is InetSocketAddress -> { + port = localAddress.port + host = localAddress.hostname + } + + is UnixSocketAddress -> { + path = localAddress.path + } + + null -> { + host = "0.0.0.0" + port = 0 + } + } +} + +internal expect fun ServerListenOptions( + block: ServerListenOptions.() -> Unit +): ServerListenOptions + +internal external interface ServerListenOptions { + var port: Int? + var host: String? + var path: String? +} + +internal external interface ServerLocalAddressInfo + +internal external interface TcpServerLocalAddressInfo : ServerLocalAddressInfo { + val address: String + val family: String + val port: Int +} + +internal expect fun ServerLocalAddressInfo.toSocketAddress(): SocketAddress diff --git a/ktor-network/macos/test/io/ktor/network/sockets/tests/TestUtilsMacos.kt b/ktor-network/jsAndWasmShared/test/io/ktor/network/sockets/tests/TestUtils.kt similarity index 72% rename from ktor-network/macos/test/io/ktor/network/sockets/tests/TestUtilsMacos.kt rename to ktor-network/jsAndWasmShared/test/io/ktor/network/sockets/tests/TestUtils.kt index a283858bbb3..51820258638 100644 --- a/ktor-network/macos/test/io/ktor/network/sockets/tests/TestUtilsMacos.kt +++ b/ktor-network/jsAndWasmShared/test/io/ktor/network/sockets/tests/TestUtils.kt @@ -1,5 +1,5 @@ /* - * 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.network.sockets.tests diff --git a/ktor-network/jvm/src/io/ktor/network/selector/SelectorManager.kt b/ktor-network/jvm/src/io/ktor/network/selector/SelectorManager.kt index d47045bf021..6041df1ce3b 100644 --- a/ktor-network/jvm/src/io/ktor/network/selector/SelectorManager.kt +++ b/ktor-network/jvm/src/io/ktor/network/selector/SelectorManager.kt @@ -64,7 +64,6 @@ public inline fun SelectorManager.buildOrClose( * Select interest kind * @property [flag] to be set in NIO selector */ - public actual enum class SelectInterest(public val flag: Int) { READ(SelectionKey.OP_READ), WRITE(SelectionKey.OP_WRITE), diff --git a/ktor-network/jvm/src/io/ktor/network/sockets/ConnectUtilsJvm.kt b/ktor-network/jvm/src/io/ktor/network/sockets/ConnectUtilsJvm.kt index 95827ae803f..7ac5c105c77 100644 --- a/ktor-network/jvm/src/io/ktor/network/sockets/ConnectUtilsJvm.kt +++ b/ktor-network/jvm/src/io/ktor/network/sockets/ConnectUtilsJvm.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.network.sockets @@ -9,7 +9,7 @@ import java.net.* import java.nio.channels.* import java.nio.channels.spi.* -internal actual suspend fun connect( +internal actual suspend fun tcpConnect( selector: SelectorManager, remoteAddress: SocketAddress, socketOptions: SocketOptions.TCPClientSocketOptions @@ -22,7 +22,7 @@ internal actual suspend fun connect( } } -internal actual fun bind( +internal actual suspend fun tcpBind( selector: SelectorManager, localAddress: SocketAddress?, socketOptions: SocketOptions.AcceptorOptions diff --git a/ktor-network/jvm/src/io/ktor/network/sockets/UDPSocketBuilderJvm.kt b/ktor-network/jvm/src/io/ktor/network/sockets/UDPSocketBuilderJvm.kt index deb8bd96365..0b465b7cb58 100644 --- a/ktor-network/jvm/src/io/ktor/network/sockets/UDPSocketBuilderJvm.kt +++ b/ktor-network/jvm/src/io/ktor/network/sockets/UDPSocketBuilderJvm.kt @@ -6,7 +6,7 @@ package io.ktor.network.sockets import io.ktor.network.selector.* -internal actual fun connectUDP( +internal actual suspend fun udpConnect( selector: SelectorManager, remoteAddress: SocketAddress, localAddress: SocketAddress?, @@ -25,7 +25,7 @@ internal actual fun connectUDP( return DatagramSocketImpl(this, selector) } -internal actual fun bindUDP( +internal actual suspend fun udpBind( selector: SelectorManager, localAddress: SocketAddress?, options: SocketOptions.UDPSocketOptions diff --git a/ktor-network/jvm/test/io/ktor/network/sockets/tests/TestUtilsJvm.kt b/ktor-network/jvm/test/io/ktor/network/sockets/tests/TestUtilsJvm.kt index 55f12746864..8d4a8a5e72e 100644 --- a/ktor-network/jvm/test/io/ktor/network/sockets/tests/TestUtilsJvm.kt +++ b/ktor-network/jvm/test/io/ktor/network/sockets/tests/TestUtilsJvm.kt @@ -1,12 +1,9 @@ /* - * 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.network.sockets.tests -import java.nio.file.* -import kotlin.io.path.* - private const val UNIX_DOMAIN_SOCKET_ADDRESS_CLASS = "java.net.UnixDomainSocketAddress" internal actual fun Any.supportsUnixDomainSockets(): Boolean { @@ -17,13 +14,3 @@ internal actual fun Any.supportsUnixDomainSockets(): Boolean { false } } - -internal actual fun createTempFilePath(basename: String): String { - val tempFile = Files.createTempFile(basename, "") - tempFile.deleteIfExists() - return tempFile.toString() -} - -internal actual fun removeFile(path: String) { - Files.deleteIfExists(Path(path)) -} diff --git a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/ConnectUtils.kt b/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/ConnectUtils.kt deleted file mode 100644 index 15db7cce981..00000000000 --- a/ktor-network/jvmAndPosix/src/io/ktor/network/sockets/ConnectUtils.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ - -package io.ktor.network.sockets - -import io.ktor.network.selector.* - -internal expect suspend fun connect( - selector: SelectorManager, - remoteAddress: SocketAddress, - socketOptions: SocketOptions.TCPClientSocketOptions -): Socket - -internal expect fun bind( - selector: SelectorManager, - localAddress: SocketAddress?, - socketOptions: SocketOptions.AcceptorOptions -): ServerSocket diff --git a/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/TestUtils.kt b/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/TestUtils.kt deleted file mode 100644 index 1a255234492..00000000000 --- a/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/TestUtils.kt +++ /dev/null @@ -1,28 +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.network.sockets.tests - -import io.ktor.network.selector.* -import io.ktor.test.dispatcher.* -import io.ktor.util.* -import kotlinx.coroutines.* -import kotlin.time.* -import kotlin.time.Duration.Companion.seconds - -internal fun testSockets(timeout: Duration = 1.seconds, block: suspend CoroutineScope.(SelectorManager) -> Unit) { - if (!PlatformUtils.IS_JVM && !PlatformUtils.IS_NATIVE) return - testSuspend { - withTimeout(timeout) { - SelectorManager().use { selector -> - block(selector) - } - } - } -} - -internal expect fun Any.supportsUnixDomainSockets(): Boolean - -internal expect fun createTempFilePath(basename: String): String -internal expect fun removeFile(path: String) diff --git a/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/UDPSocketTest.kt b/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/UDPSocketTest.kt index 1c947038cb6..7c5b9f49893 100644 --- a/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/UDPSocketTest.kt +++ b/ktor-network/jvmAndPosix/test/io/ktor/network/sockets/tests/UDPSocketTest.kt @@ -18,7 +18,7 @@ class UDPSocketTest { private val done = atomic(0) @Test - fun testBroadcastFails(): Unit = testSockets { selector -> + fun testBroadcastFails() = testSockets { selector -> if (isJvmWindows()) { return@testSockets } @@ -103,7 +103,7 @@ class UDPSocketTest { } @Test - fun testClose(): Unit = testSockets { selector -> + fun testClose() = testSockets { selector -> val socket = aSocket(selector) .udp() .bind() @@ -194,7 +194,7 @@ class UDPSocketTest { } @Test - fun testSendReceive(): Unit = testSockets { selector -> + fun testSendReceive() = testSockets { selector -> aSocket(selector) .udp() .bind(InetSocketAddress("127.0.0.1", 8000)) { @@ -224,7 +224,7 @@ class UDPSocketTest { } @Test - fun testSendReceiveLarge(): Unit = testSockets { selector -> + fun testSendReceiveLarge() = testSockets { selector -> val datagramSize = 10000 // must be larger than Segment.SIZE (8192) for this test val largeData = Random.nextBytes(datagramSize) diff --git a/ktor-network/ktor-network-tls/api/ktor-network-tls.klib.api b/ktor-network/ktor-network-tls/api/ktor-network-tls.klib.api index 980bd2d90a5..4b5d8c1eb10 100644 --- a/ktor-network/ktor-network-tls/api/ktor-network-tls.klib.api +++ b/ktor-network/ktor-network-tls/api/ktor-network-tls.klib.api @@ -1,5 +1,5 @@ // Klib ABI Dump -// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true diff --git a/ktor-network/ktor-network-tls/build.gradle.kts b/ktor-network/ktor-network-tls/build.gradle.kts index 610ffe0c03e..64419e47b29 100644 --- a/ktor-network/ktor-network-tls/build.gradle.kts +++ b/ktor-network/ktor-network-tls/build.gradle.kts @@ -3,7 +3,7 @@ */ kotlin.sourceSets { - jvmAndPosixMain { + commonMain { dependencies { api(project(":ktor-http")) api(project(":ktor-network")) diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/CipherSuites.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/CipherSuites.kt similarity index 96% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/CipherSuites.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/CipherSuites.kt index 4e95b5b59f2..9c96e828c80 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/CipherSuites.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/CipherSuites.kt @@ -1,12 +1,11 @@ /* -* 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.network.tls import io.ktor.network.tls.extensions.* -import io.ktor.utils.io.errors.* -import kotlinx.io.IOException +import kotlinx.io.* /** * TLS secret key exchange type. diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/OID.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/OID.kt similarity index 95% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/OID.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/OID.kt index 7f63bd72713..78b3fc88d5c 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/OID.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/OID.kt @@ -1,11 +1,9 @@ /* -* 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.network.tls -import io.ktor.util.* - public data class OID(public val identifier: String) { public val asArray: IntArray = identifier.split(".", " ").map { it.trim().toInt() }.toIntArray() diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSClientSession.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSClientSession.kt similarity index 71% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSClientSession.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSClientSession.kt index 766791b048b..aceb68b683a 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSClientSession.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSClientSession.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.network.tls diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSCommon.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSCommon.kt similarity index 92% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSCommon.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSCommon.kt index 1f3e1aa18a2..358f3e0bf91 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSCommon.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSCommon.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.network.tls diff --git a/ktor-network/windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSConfig.kt similarity index 54% rename from ktor-network/windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSConfig.kt index 5006b7bb955..10a3f75b4e1 100644 --- a/ktor-network/windows/test/io/ktor/network/sockets/tests/TestUtilsWindows.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSConfig.kt @@ -2,6 +2,6 @@ * 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.sockets.tests +package io.ktor.network.tls -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false +public expect class TLSConfig diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSConfigBuilderCommon.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSConfigBuilderCommon.kt similarity index 79% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSConfigBuilderCommon.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSConfigBuilderCommon.kt index a9e1e369789..1054ce0b90f 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSConfigBuilderCommon.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/TLSConfigBuilderCommon.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.network.tls diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/NamedCurves.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/NamedCurves.kt similarity index 91% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/NamedCurves.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/NamedCurves.kt index ad79f272939..ad1059b53b4 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/NamedCurves.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/NamedCurves.kt @@ -1,11 +1,9 @@ /* -* 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.network.tls.extensions -import kotlin.native.concurrent.* - /** * Named curves for Elliptic Curves. * @property code curve numeric code diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/PointFormat.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/PointFormat.kt similarity index 81% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/PointFormat.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/PointFormat.kt index 609b6c03e6b..9b3e4c54ffd 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/PointFormat.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/PointFormat.kt @@ -1,11 +1,9 @@ /* -* 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.network.tls.extensions -import kotlin.native.concurrent.* - /** * Elliptic curve point format * @property code numeric point format code diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/SignatureAlgorithm.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/SignatureAlgorithm.kt similarity index 96% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/SignatureAlgorithm.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/SignatureAlgorithm.kt index d1a700860fe..f62b86a09c1 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/SignatureAlgorithm.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/SignatureAlgorithm.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.network.tls.extensions diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/TLSExtension.kt b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/TLSExtension.kt similarity index 86% rename from ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/TLSExtension.kt rename to ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/TLSExtension.kt index b009c4748ec..d5b5f4e8a6b 100644 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/extensions/TLSExtension.kt +++ b/ktor-network/ktor-network-tls/common/src/io/ktor/network/tls/extensions/TLSExtension.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.network.tls.extensions diff --git a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSConfig.kt b/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSConfig.kt deleted file mode 100644 index ca84d859e16..00000000000 --- a/ktor-network/ktor-network-tls/jvmAndPosix/src/io/ktor/network/tls/TLSConfig.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* -* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. -*/ - -package io.ktor.network.tls - -public expect class TLSConfig diff --git a/ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/CipherSuitesNative.kt b/ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/CipherSuites.nonJvm.kt similarity index 100% rename from ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/CipherSuitesNative.kt rename to ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/CipherSuites.nonJvm.kt diff --git a/ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSNative.kt b/ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLS.nonJvm.kt similarity index 100% rename from ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSNative.kt rename to ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLS.nonJvm.kt diff --git a/ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSClientSessionNative.kt b/ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLSClientSession.nonJvm.kt similarity index 100% rename from ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSClientSessionNative.kt rename to ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLSClientSession.nonJvm.kt diff --git a/ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSConfigNative.kt b/ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLSConfig.nonJvm.kt similarity index 100% rename from ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSConfigNative.kt rename to ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLSConfig.nonJvm.kt diff --git a/ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSConfigBuilderNative.kt b/ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLSConfigBuilder.nonJvm.kt similarity index 100% rename from ktor-network/ktor-network-tls/posix/src/io/ktor/network/tls/TLSConfigBuilderNative.kt rename to ktor-network/ktor-network-tls/nonJvm/src/io/ktor/network/tls/TLSConfigBuilder.nonJvm.kt diff --git a/ktor-network/linux/test/io/ktor/network/sockets/tests/TestUtilsLinux.kt b/ktor-network/linux/test/io/ktor/network/sockets/tests/TestUtilsLinux.kt deleted file mode 100644 index a283858bbb3..00000000000 --- a/ktor-network/linux/test/io/ktor/network/sockets/tests/TestUtilsLinux.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.network.sockets.tests - -internal actual fun Any.supportsUnixDomainSockets(): Boolean = true diff --git a/ktor-network/posix/src/io/ktor/network/sockets/SocketAddressNative.kt b/ktor-network/nonJvm/src/io/ktor/network/sockets/SocketAddress.nonJvm.kt similarity index 100% rename from ktor-network/posix/src/io/ktor/network/sockets/SocketAddressNative.kt rename to ktor-network/nonJvm/src/io/ktor/network/sockets/SocketAddress.nonJvm.kt diff --git a/ktor-network/posix/src/io/ktor/network/sockets/SocketTimeoutException.kt b/ktor-network/nonJvm/src/io/ktor/network/sockets/SocketTimeoutException.kt similarity index 51% rename from ktor-network/posix/src/io/ktor/network/sockets/SocketTimeoutException.kt rename to ktor-network/nonJvm/src/io/ktor/network/sockets/SocketTimeoutException.kt index 7bbab1cc4cc..ca54b0fb353 100644 --- a/ktor-network/posix/src/io/ktor/network/sockets/SocketTimeoutException.kt +++ b/ktor-network/nonJvm/src/io/ktor/network/sockets/SocketTimeoutException.kt @@ -1,13 +1,11 @@ /* -* 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.network.sockets -import io.ktor.utils.io.errors.* -import kotlinx.io.IOException +import kotlinx.io.* -@Suppress("EXPECT_WITHOUT_ACTUAL") public actual class SocketTimeoutException( message: String, cause: Throwable? diff --git a/ktor-network/posix/src/io/ktor/network/selector/SelectorManager.kt b/ktor-network/posix/src/io/ktor/network/selector/SelectorManager.kt index 55746637530..fbc2cb85f52 100644 --- a/ktor-network/posix/src/io/ktor/network/selector/SelectorManager.kt +++ b/ktor-network/posix/src/io/ktor/network/selector/SelectorManager.kt @@ -38,7 +38,6 @@ public actual interface SelectorManager : CoroutineScope, Closeable { /** * Select interest kind */ -@Suppress("KDocMissingDocumentation", "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING") public actual enum class SelectInterest { READ, WRITE, ACCEPT, CONNECT; diff --git a/ktor-network/posix/src/io/ktor/network/sockets/ConnectUtilsNative.kt b/ktor-network/posix/src/io/ktor/network/sockets/ConnectUtilsNative.kt index ec9c0f4e3bb..c54d7cd90aa 100644 --- a/ktor-network/posix/src/io/ktor/network/sockets/ConnectUtilsNative.kt +++ b/ktor-network/posix/src/io/ktor/network/sockets/ConnectUtilsNative.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.network.sockets @@ -14,7 +14,7 @@ import platform.posix.* private const val DEFAULT_BACKLOG_SIZE = 50 @OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) -internal actual suspend fun connect( +internal actual suspend fun tcpConnect( selector: SelectorManager, remoteAddress: SocketAddress, socketOptions: SocketOptions.TCPClientSocketOptions @@ -79,7 +79,7 @@ internal actual suspend fun connect( } @OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) -internal actual fun bind( +internal actual suspend fun tcpBind( selector: SelectorManager, localAddress: SocketAddress?, socketOptions: SocketOptions.AcceptorOptions diff --git a/ktor-network/posix/src/io/ktor/network/sockets/UDPSocketBuilderNative.kt b/ktor-network/posix/src/io/ktor/network/sockets/UDPSocketBuilderNative.kt index eb4fd26ec59..cfb45545d9d 100644 --- a/ktor-network/posix/src/io/ktor/network/sockets/UDPSocketBuilderNative.kt +++ b/ktor-network/posix/src/io/ktor/network/sockets/UDPSocketBuilderNative.kt @@ -10,7 +10,7 @@ import kotlinx.cinterop.* import platform.posix.* @OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) -internal actual fun connectUDP( +internal actual suspend fun udpConnect( selector: SelectorManager, remoteAddress: SocketAddress, localAddress: SocketAddress?, @@ -51,7 +51,7 @@ internal actual fun connectUDP( } @OptIn(UnsafeNumber::class, ExperimentalForeignApi::class) -internal actual fun bindUDP( +internal actual suspend fun udpBind( selector: SelectorManager, localAddress: SocketAddress?, options: SocketOptions.UDPSocketOptions diff --git a/ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtils.posix.kt b/ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtils.posix.kt new file mode 100644 index 00000000000..df03ca53d52 --- /dev/null +++ b/ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtils.posix.kt @@ -0,0 +1,13 @@ +/* + * 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.sockets.tests + +import kotlin.experimental.* + +@OptIn(ExperimentalNativeApi::class) +internal actual fun Any.supportsUnixDomainSockets(): Boolean = when (Platform.osFamily) { + OsFamily.MACOSX, OsFamily.LINUX -> true + else -> false +} diff --git a/ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtilsNix.kt b/ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtilsNix.kt deleted file mode 100644 index d1d3d99310b..00000000000 --- a/ktor-network/posix/test/io/ktor/network/sockets/tests/TestUtilsNix.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.network.sockets.tests - -import platform.posix.* - -internal actual fun createTempFilePath(basename: String): String = "/tmp/$basename" - -internal actual fun removeFile(path: String) { - if (remove(path) != 0) error("Failed to delete socket node") -} diff --git a/ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt b/ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt deleted file mode 100644 index 5964f2a095f..00000000000 --- a/ktor-network/tvos/test/io/ktor/network/sockets/tests/TestUtilsTvos.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.network.sockets.tests - -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false diff --git a/ktor-network/wasmJs/src/io/ktor/network/sockets/nodejs/node.net.wasmJs.kt b/ktor-network/wasmJs/src/io/ktor/network/sockets/nodejs/node.net.wasmJs.kt new file mode 100644 index 00000000000..0b889f54ed0 --- /dev/null +++ b/ktor-network/wasmJs/src/io/ktor/network/sockets/nodejs/node.net.wasmJs.kt @@ -0,0 +1,65 @@ +/* + * 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.sockets.nodejs + +import io.ktor.network.sockets.* +import org.khronos.webgl.* + +internal actual fun nodeNet(): NodeNet? = js("eval('require')('node:net')") + +internal actual fun TcpCreateConnectionOptions( + block: TcpCreateConnectionOptions.() -> Unit +): TcpCreateConnectionOptions = createObject(block) + +internal actual fun IpcCreateConnectionOptions( + block: IpcCreateConnectionOptions.() -> Unit +): IpcCreateConnectionOptions = createObject(block) + +internal actual fun CreateServerOptions( + block: CreateServerOptions.() -> Unit +): CreateServerOptions = createObject(block) + +internal actual fun ServerListenOptions( + block: ServerListenOptions.() -> Unit +): ServerListenOptions = createObject(block) + +internal actual fun JsError.toThrowable(): Throwable = Error(message) +internal actual fun Throwable.toJsError(): JsError? = jsError(message) + +private fun jsError(message: String?): JsError = js("(new Error(message))") + +internal actual fun ByteArray.toJsBuffer(fromIndex: Int, toIndex: Int): JsBuffer { + val array = Int8Array(toIndex - fromIndex) + repeat(array.length) { index -> + array[index] = this[fromIndex + index] + } + return justCast(array) +} + +internal actual fun JsBuffer.toByteArray(): ByteArray { + val array = Int8Array(justCast(this)) + val bytes = ByteArray(array.length) + + repeat(array.length) { index -> + bytes[index] = array[index] + } + return bytes +} + +internal actual fun ServerLocalAddressInfo.toSocketAddress(): SocketAddress { + if (jsTypeOf(justCast(this)) == "string") return UnixSocketAddress(justCast(this).toString()) + val info = justCast(this) + return InetSocketAddress(info.address, info.port) +} + +private fun jsTypeOf(obj: JsAny): String = js("(typeof obj)") + +private fun createJsObject(): JsAny = js("({})") + +private fun createObject(block: T.() -> Unit): T = createJsObject().unsafeCast().apply(block) + +// overcomes the issue that expect declarations are not extending `JsAny` +@Suppress("UNCHECKED_CAST") +private fun justCast(obj: Any): T = obj as T diff --git a/ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt b/ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt deleted file mode 100644 index 5964f2a095f..00000000000 --- a/ktor-network/watchos/test/io/ktor/network/sockets/tests/TestUtilsWatchos.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.network.sockets.tests - -internal actual fun Any.supportsUnixDomainSockets(): Boolean = false diff --git a/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CORSTest.kt b/ktor-server/ktor-server-tests/jvmAndNix/test/io/ktor/tests/server/plugins/CORSTest.kt deleted file mode 100644 index e69de29bb2d..00000000000