diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/FlowsWebsockets.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/FlowsWebsockets.kt index 12772d08d9c..5f3512cdc2a 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/FlowsWebsockets.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/FlowsWebsockets.kt @@ -1,8 +1,7 @@ package dev.inmo.micro_utils.ktor.client import dev.inmo.micro_utils.coroutines.safely -import dev.inmo.micro_utils.ktor.common.asCorrectWebSocketUrl -import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat +import dev.inmo.micro_utils.ktor.common.* import io.ktor.client.HttpClient import io.ktor.client.features.websocket.ws import io.ktor.http.cio.websocket.* @@ -17,7 +16,7 @@ import kotlinx.serialization.DeserializationStrategy inline fun HttpClient.createStandardWebsocketFlow( url: String, crossinline checkReconnection: (Throwable?) -> Boolean = { true }, - crossinline conversation: suspend (ByteArray) -> T + crossinline conversation: suspend (StandardKtorSerialInputData) -> T ): Flow { val correctedUrl = url.asCorrectWebSocketUrl @@ -25,19 +24,11 @@ inline fun HttpClient.createStandardWebsocketFlow( val producerScope = this@channelFlow do { val reconnect = try { - safely( - { - throw it - } - ) { - ws( - correctedUrl - ) { - while (true) { - when (val received = incoming.receive()) { - is Frame.Binary -> producerScope.send( - conversation(received.readBytes()) - ) + safely ({ throw it }) { + ws(correctedUrl) { + for (received in incoming) { + when (received) { + is Frame.Binary -> producerScope.send(conversation(received.readBytes())) else -> { producerScope.close() return@ws @@ -57,7 +48,7 @@ inline fun HttpClient.createStandardWebsocketFlow( } while (reconnect) if (!producerScope.isClosedForSend) { safely( - { /* do nothing */ } + { it.printStackTrace() } ) { producerScope.close() } @@ -77,6 +68,5 @@ inline fun HttpClient.createStandardWebsocketFlow( url, checkReconnection ) { - standardKtorSerialFormat.decodeFromByteArray(deserializer, it) + standardKtorSerialFormat.decodeDefault(deserializer, it) } - diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/StandardHttpClientGetPost.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/StandardHttpClientGetPost.kt index 82a8876a90a..6981ba21a30 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/StandardHttpClientGetPost.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/StandardHttpClientGetPost.kt @@ -1,6 +1,6 @@ package dev.inmo.micro_utils.ktor.client -import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat +import dev.inmo.micro_utils.ktor.common.* import io.ktor.client.HttpClient import io.ktor.client.request.get import io.ktor.client.request.post @@ -11,13 +11,14 @@ typealias BodyPair = Pair, T> suspend fun HttpClient.uniget( url: String, resultDeserializer: DeserializationStrategy -) = get( +) = get( url ).let { - standardKtorSerialFormat.decodeFromByteArray(resultDeserializer, it) + standardKtorSerialFormat.decodeDefault(resultDeserializer, it) } -fun SerializationStrategy.encodeUrlQueryValue(value: T) = standardKtorSerialFormat.encodeToHexString( + +fun SerializationStrategy.encodeUrlQueryValue(value: T) = standardKtorSerialFormat.encodeHex( this, value ) @@ -26,8 +27,8 @@ suspend fun HttpClient.unipost( url: String, bodyInfo: BodyPair, resultDeserializer: DeserializationStrategy -) = post(url) { - body = standardKtorSerialFormat.encodeToByteArray(bodyInfo.first, bodyInfo.second) +) = post(url) { + body = standardKtorSerialFormat.encodeDefault(bodyInfo.first, bodyInfo.second) }.let { - standardKtorSerialFormat.decodeFromByteArray(resultDeserializer, it) + standardKtorSerialFormat.decodeDefault(resultDeserializer, it) } diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt index 1c70bcd5358..60b8dc3d99f 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt @@ -21,3 +21,4 @@ fun buildStandardUrl( subpart: String, vararg parameters: QueryParam ) = buildStandardUrl(basePart, subpart, parameters.toList()) + diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CollectionsSerializers.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CollectionsSerializers.kt deleted file mode 100644 index 54923c9d9d0..00000000000 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CollectionsSerializers.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.inmo.micro_utils.ktor.common - -import kotlinx.serialization.builtins.SetSerializer -import kotlinx.serialization.builtins.serializer - -val setIdsSerializer = SetSerializer(String.serializer()) diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt index 887c15b5b0b..f1cd5d78b0f 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt @@ -1,6 +1,6 @@ package dev.inmo.micro_utils.ktor.common -private val schemaRegex = Regex("[^:]*//") +private val schemaRegex = Regex("^[^:]*://") val String.asCorrectWebSocketUrl: String get() = if (startsWith("ws")) { diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt index 6205052b6d1..0ef45009594 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt @@ -11,7 +11,7 @@ val List.asUrlQuery: String fun String.includeQueryParams( queryParams: QueryParams -): String = "$this${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}" +): String = "$this${if(queryParams.isNotEmpty()) "${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}" else ""}" fun String.includeQueryParams( queryParams: List diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt index 6bef2004ea6..5d088c9721b 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt @@ -1,5 +1,32 @@ package dev.inmo.micro_utils.ktor.common +import kotlinx.serialization.* import kotlinx.serialization.cbor.Cbor -val standardKtorSerialFormat = Cbor +typealias StandardKtorSerialFormat = BinaryFormat +typealias StandardKtorSerialInputData = ByteArray +val standardKtorSerialFormat: StandardKtorSerialFormat = Cbor { } + +inline fun StandardKtorSerialFormat.decodeDefault( + deserializationStrategy: DeserializationStrategy, + input: StandardKtorSerialInputData +): T = decodeFromByteArray(deserializationStrategy, input) + +inline fun StandardKtorSerialFormat.encodeDefault( + serializationStrategy: SerializationStrategy, + data: T +): StandardKtorSerialInputData = encodeToByteArray(serializationStrategy, data) + +val cbor = Cbor {} + +inline fun StandardKtorSerialFormat.decodeHex( + deserializationStrategy: DeserializationStrategy, + input: String +): T = decodeFromHexString(deserializationStrategy, input) + +inline fun StandardKtorSerialFormat.encodeHex( + serializationStrategy: SerializationStrategy, + data: T +): String = encodeToHexString(serializationStrategy, data) + + diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/WebSockets.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/WebSockets.kt deleted file mode 100644 index 1fdfb5c3421..00000000000 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/WebSockets.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dev.inmo.micro_utils.ktor.common - -const val clientWebsocketHelloMessage = "Start getting of updates" -const val serverWebsocketHelloMessage = "Accepted" - -const val serverWebsocketNewMessageMessage = "NewMessage" -const val websocketFinalizationMessage = "Final" diff --git a/ktor/server/build.gradle b/ktor/server/build.gradle index b6df33972d7..7e17189e74b 100644 --- a/ktor/server/build.gradle +++ b/ktor/server/build.gradle @@ -17,6 +17,7 @@ kotlin { jvmMain { dependencies { api "io.ktor:ktor-server:$ktor_version" + api "io.ktor:ktor-server-cio:$ktor_version" api "io.ktor:ktor-server-host-common:$ktor_version" api "io.ktor:ktor-websockets:$ktor_version" } diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FlowsWebsocket.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FlowsWebsocket.kt index c591eaf8655..ed385691437 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FlowsWebsocket.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FlowsWebsocket.kt @@ -1,8 +1,7 @@ package dev.inmo.micro_utils.ktor.server import dev.inmo.micro_utils.coroutines.safely -import dev.inmo.micro_utils.ktor.common.CorrectCloseException -import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat +import dev.inmo.micro_utils.ktor.common.* import io.ktor.http.cio.websocket.* import io.ktor.routing.Route import io.ktor.websocket.webSocket @@ -20,15 +19,16 @@ private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() { fun Route.includeWebsocketHandling( suburl: String, flow: Flow, - converter: (T) -> ByteArray + converter: (T) -> StandardKtorSerialInputData ) { webSocket(suburl) { +// println("connected") safely { flow.collect { - checkReceivedAndCloseIfExists() send(converter(it)) } } +// println("disconnected") } } @@ -40,5 +40,5 @@ fun Route.includeWebsocketHandling( suburl, flow ) { - standardKtorSerialFormat.encodeToByteArray(serializer, it) + standardKtorSerialFormat.encodeDefault(serializer, it) } diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/PaginationInUrl.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/PaginationInUrl.kt new file mode 100644 index 00000000000..9bb00c623ea --- /dev/null +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/PaginationInUrl.kt @@ -0,0 +1,10 @@ +package dev.inmo.micro_utils.ktor.server + +import dev.inmo.micro_utils.pagination.* +import io.ktor.http.Parameters + +val Parameters.extractPagination: Pagination + get() = SimplePagination( + get("page") ?.toIntOrNull() ?: 0, + get("size") ?.toIntOrNull() ?: defaultMediumPageSize + ) diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/ServerRoutingShortcuts.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/ServerRoutingShortcuts.kt index 1c92ce90538..1ead1200c65 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/ServerRoutingShortcuts.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/ServerRoutingShortcuts.kt @@ -1,8 +1,10 @@ package dev.inmo.micro_utils.ktor.server -import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat +import dev.inmo.micro_utils.coroutines.safely +import dev.inmo.micro_utils.ktor.common.* import io.ktor.application.ApplicationCall import io.ktor.http.HttpStatusCode +import io.ktor.request.receive import io.ktor.response.respond import io.ktor.response.respondBytes import io.ktor.util.toByteArray @@ -12,18 +14,20 @@ suspend fun ApplicationCall.unianswer( answerSerializer: SerializationStrategy, answer: T ) { - respondBytes( - standardKtorSerialFormat.encodeToByteArray(answerSerializer, answer), + respondBytes ( + standardKtorSerialFormat.encodeDefault(answerSerializer, answer), standardKtorSerialFormatContentType ) } suspend fun ApplicationCall.uniload( deserializer: DeserializationStrategy -) = standardKtorSerialFormat.decodeFromByteArray( - deserializer, - request.receiveChannel().toByteArray() -) +) = safely { + standardKtorSerialFormat.decodeDefault( + deserializer, + receive() + ) +} suspend fun ApplicationCall.getParameterOrSendError( field: String @@ -49,7 +53,7 @@ fun ApplicationCall.decodeUrlQueryValue( field: String, deserializer: DeserializationStrategy ) = getQueryParameter(field) ?.let { - standardKtorSerialFormat.decodeFromHexString( + standardKtorSerialFormat.decodeHex( deserializer, it ) diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/StartServer.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/StartServer.kt new file mode 100644 index 00000000000..1a50ca884b6 --- /dev/null +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/StartServer.kt @@ -0,0 +1,33 @@ +package dev.inmo.micro_utils.ktor.server + +import io.ktor.application.Application +import io.ktor.server.cio.CIO +import io.ktor.server.engine.* +import kotlin.random.Random + +fun createKtorServer( + engine: ApplicationEngineFactory, + host: String = "localhost", + port: Int = Random.nextInt(1024, 65535), + block: Application.() -> Unit +): TEngine { + val env = applicationEngineEnvironment { + module(block) + connector { + this@connector.host = host + this@connector.port = port + } + } + return embeddedServer(engine, env) +} + +/** + * Create server with [CIO] server engine without starting of it + * + * @see ApplicationEngine.start + */ +fun createKtorServer( + host: String = "localhost", + port: Int = Random.nextInt(1024, 65535), + block: Application.() -> Unit +): ApplicationEngine = createKtorServer(CIO, host, port, block)