MicroUtils/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/FlowsWebsockets.kt

79 lines
2.8 KiB
Kotlin
Raw Normal View History

2020-09-22 02:20:22 +00:00
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.coroutines.safely
2020-09-26 15:42:05 +00:00
import dev.inmo.micro_utils.ktor.common.*
2020-09-22 02:20:22 +00:00
import io.ktor.client.HttpClient
import io.ktor.client.plugins.websocket.ws
2022-03-04 14:16:59 +00:00
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.websocket.Frame
import io.ktor.websocket.readBytes
2020-09-22 02:20:22 +00:00
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.serialization.DeserializationStrategy
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
inline fun <T> HttpClient.createStandardWebsocketFlow(
url: String,
crossinline checkReconnection: (Throwable?) -> Boolean = { true },
2022-03-04 14:16:59 +00:00
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
2020-09-26 15:42:05 +00:00
crossinline conversation: suspend (StandardKtorSerialInputData) -> T
2020-09-22 02:20:22 +00:00
): Flow<T> {
val correctedUrl = url.asCorrectWebSocketUrl
return channelFlow {
val producerScope = this@channelFlow
do {
val reconnect = try {
safely {
2022-03-04 14:16:59 +00:00
ws(correctedUrl, requestBuilder) {
2020-09-26 15:42:05 +00:00
for (received in incoming) {
when (received) {
is Frame.Binary -> producerScope.send(conversation(received.readBytes()))
2020-09-22 02:20:22 +00:00
else -> {
producerScope.close()
return@ws
}
}
}
}
}
checkReconnection(null)
} catch (e: Throwable) {
checkReconnection(e).also {
if (!it) {
producerScope.close(e)
}
}
}
} while (reconnect)
if (!producerScope.isClosedForSend) {
safely(
2020-09-26 15:42:05 +00:00
{ it.printStackTrace() }
2020-09-22 02:20:22 +00:00
) {
producerScope.close()
}
}
}
}
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
inline fun <T> HttpClient.createStandardWebsocketFlow(
url: String,
crossinline checkReconnection: (Throwable?) -> Boolean = { true },
2020-11-27 07:30:02 +00:00
deserializer: DeserializationStrategy<T>,
2022-03-04 14:16:59 +00:00
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
2020-09-22 02:20:22 +00:00
) = createStandardWebsocketFlow(
url,
2022-03-04 14:16:59 +00:00
checkReconnection,
requestBuilder
2020-09-22 02:20:22 +00:00
) {
2020-11-27 07:30:02 +00:00
serialFormat.decodeDefault(deserializer, it)
2020-09-22 02:20:22 +00:00
}