fix: Use Job.invokeOnCompletion(onCancelling=true) for responsive socket cancellation
This commit is contained in:
parent
b54089dacc
commit
163da57ea5
3 changed files with 24 additions and 9 deletions
|
|
@ -9,8 +9,8 @@ import app.hermes.mobile.core.network.HermesRestClient
|
|||
import app.hermes.mobile.core.security.TokenVault
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
|
|
@ -62,11 +62,6 @@ class PkceLoopbackAuthManager(
|
|||
val port = serverSocket.localPort
|
||||
serverSocket.soTimeout = 180_000 // 3 minutes timeout
|
||||
|
||||
currentCoroutineContext().job.invokeOnCompletion {
|
||||
try {
|
||||
serverSocket?.close()
|
||||
} catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
val redirectUri = "http://127.0.0.1:$port/callback"
|
||||
|
||||
|
|
@ -92,7 +87,22 @@ class PkceLoopbackAuthManager(
|
|||
var retries = 0
|
||||
val MAX_RETRIES = 5
|
||||
while (authCode == null && retries < MAX_RETRIES) {
|
||||
val socket: Socket = runInterruptible(Dispatchers.IO) { serverSocket!!.accept() }
|
||||
currentCoroutineContext().ensureActive()
|
||||
val socket: Socket = suspendCancellableCoroutine { cont ->
|
||||
cont.invokeOnCancellation {
|
||||
try {
|
||||
serverSocket?.close()
|
||||
} catch (_: Throwable) {}
|
||||
}
|
||||
try {
|
||||
val s = serverSocket!!.accept()
|
||||
cont.resumeWith(Result.success(s))
|
||||
} catch (e: Throwable) {
|
||||
if (!cont.isCancelled) {
|
||||
cont.resumeWith(Result.failure(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
authCode = handleCallbackSocket(socket, state)
|
||||
} catch (e: Exception) {
|
||||
|
|
|
|||
|
|
@ -154,11 +154,13 @@ class HermesConnectionManager(
|
|||
|
||||
suspend fun addHost(host: HermesHost) {
|
||||
hostDao.insertOrUpdateHost(host.toEntity())
|
||||
_hosts.value = _hosts.value.filter { it.id != host.id } + host
|
||||
getOrCreateRuntime(host)
|
||||
}
|
||||
|
||||
suspend fun updateHost(host: HermesHost) {
|
||||
hostDao.insertOrUpdateHost(host.toEntity())
|
||||
_hosts.value = _hosts.value.map { if (it.id == host.id) host else it }
|
||||
val rt = runtimes[host.id]
|
||||
rt?.updateHost(host)
|
||||
}
|
||||
|
|
@ -168,6 +170,7 @@ class HermesConnectionManager(
|
|||
rt?.close()
|
||||
tokenVault.clearTokens(hostId.value)
|
||||
hostDao.deleteHost(hostId.value)
|
||||
_hosts.value = _hosts.value.filter { it.id != hostId }
|
||||
if (_activeHostId.value == hostId) {
|
||||
_activeHostId.value = _hosts.value.firstOrNull { it.id != hostId }?.id
|
||||
}
|
||||
|
|
@ -175,6 +178,7 @@ class HermesConnectionManager(
|
|||
|
||||
suspend fun connectHost(hostId: HermesHostId): Result<Unit> {
|
||||
val host = _hosts.value.find { it.id == hostId }
|
||||
?: hostDao.getHost(hostId.value)?.toDomain()
|
||||
?: return Result.failure(IllegalArgumentException("Host not found: ${hostId.value}"))
|
||||
val rt = getOrCreateRuntime(host)
|
||||
return rt.connect()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ class LoopbackCancellationTest {
|
|||
)
|
||||
|
||||
val authUrlDeferred = CompletableDeferred<String>()
|
||||
val job = launch(Dispatchers.IO) {
|
||||
val testScope = kotlinx.coroutines.CoroutineScope(Dispatchers.IO)
|
||||
val job = testScope.launch {
|
||||
authManager.startAuthFlow(
|
||||
context = null,
|
||||
connectionId = "test-host",
|
||||
|
|
|
|||
Loading…
Reference in a new issue