add some stuff to backend base
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
package ru.shadowsparky.http.data
|
||||
|
||||
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.retryWhen
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.domain.Log
|
||||
import ru.shadowsparky.http.domain.AuthRequest
|
||||
import ru.shadowsparky.http.domain.AuthResponse
|
||||
import kotlin.math.pow
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@Single
|
||||
class RemoveEventProcessor(
|
||||
private val json: Json,
|
||||
private val log: Log
|
||||
) {
|
||||
fun process(
|
||||
token: String,
|
||||
session: DefaultClientWebSocketSession,
|
||||
onTokenInvalid: suspend () -> Unit
|
||||
): Flow<String> {
|
||||
val incoming = session.incoming
|
||||
val outgoing = session.outgoing
|
||||
return flow {
|
||||
authV2(token, incoming, outgoing, onTokenInvalid)
|
||||
for (frame in incoming) {
|
||||
if (frame is Frame.Text) {
|
||||
val text = frame.readText()
|
||||
try {
|
||||
log.d(TAG, "receive $text")
|
||||
emit(text)
|
||||
} catch (e: Exception) {
|
||||
log.e(TAG, e, "failed to decode message: $text")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun authV2(
|
||||
token: String,
|
||||
input: ReceiveChannel<Frame>,
|
||||
output: SendChannel<Frame>,
|
||||
onTokenInvalid: suspend () -> Unit
|
||||
) {
|
||||
output.send(Frame.Text(json.encodeToString(AuthRequest(token))))
|
||||
val rawFrame = (input.receive() as Frame.Text).readText()
|
||||
val response = json.decodeFromString<AuthResponse>(rawFrame)
|
||||
if (!response.ok) {
|
||||
onTokenInvalid()
|
||||
error("Authentication failed")
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TAG = "RemoveEventProcessor"
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<T>.retryExponential(
|
||||
maxRetries: Int = Int.MAX_VALUE,
|
||||
initialDelay: Long = 5000L,
|
||||
maxDelay: Long = 300_000L,
|
||||
factor: Double = 2.0,
|
||||
shouldRetry: (Throwable) -> Boolean = { true }
|
||||
): Flow<T> = retryWhen { cause, attempt ->
|
||||
if (!shouldRetry(cause) || attempt >= maxRetries || cause is CancellationException) {
|
||||
false
|
||||
} else {
|
||||
val delayTime = (initialDelay * factor.pow(attempt.toDouble()))
|
||||
.toLong()
|
||||
.coerceAtMost(maxDelay)
|
||||
delay(delayTime.milliseconds)
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.shadowsparky.http.domain
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("AuthRequest")
|
||||
data class AuthRequest(val token: String)
|
||||
|
||||
@Serializable
|
||||
@SerialName("AuthResponse")
|
||||
data class AuthResponse(val ok: Boolean, val error: String? = null)
|
||||
Reference in New Issue
Block a user