Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fixed the bug of failure to connect to new devices #336

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.clipevery.app

import com.clipevery.utils.JsonUtils
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString

const val AppName: String = "Clipevery"

Expand All @@ -9,4 +11,9 @@ data class AppInfo(
val appInstanceId: String,
val appVersion: String,
val userName: String
)
) {

override fun toString(): String {
return JsonUtils.JSON.encodeToString(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ package com.clipevery.dto.sync

import com.clipevery.app.AppInfo
import com.clipevery.endpoint.EndpointInfo
import com.clipevery.utils.JsonUtils
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString

@Serializable
data class SyncInfo(val appInfo: AppInfo, val endpointInfo: EndpointInfo)
data class SyncInfo(val appInfo: AppInfo, val endpointInfo: EndpointInfo) {

override fun toString(): String {
return JsonUtils.JSON.encodeToString(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ package com.clipevery.endpoint

import com.clipevery.dao.sync.HostInfo
import com.clipevery.platform.Platform
import com.clipevery.utils.JsonUtils
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString

@Serializable
data class EndpointInfo(val deviceId: String,
val deviceName: String,
val platform: Platform,
val hostInfoList: List<HostInfo>,
val port: Int)

data class ExplicitEndpointInfo(val deviceId: String,
val deviceName: String,
val platform: Platform,
val hostInfo: HostInfo,
val port: Int)

val port: Int) {
override fun toString(): String {
return JsonUtils.JSON.encodeToString(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import com.clipevery.dto.sync.RequestTrustSyncInfo
import com.clipevery.dto.sync.SyncInfo
import com.clipevery.exception.StandardErrorCode
import com.clipevery.net.CheckAction
import com.clipevery.net.ClientHandlerManager
import com.clipevery.net.DeviceRefresher
import com.clipevery.utils.encodePreKeyBundle
import com.clipevery.utils.failResponse
import com.clipevery.utils.getAppInstanceId
import com.clipevery.utils.successResponse
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.server.application.call
import io.ktor.server.request.receive
import io.ktor.server.routing.Routing
Expand All @@ -34,6 +36,8 @@ import java.util.Objects

fun Routing.syncRouting() {

val logger = KotlinLogging.logger {}

val koinApplication = Dependencies.koinApplication

val signalDao = koinApplication.koin.get<SignalDao>()
Expand All @@ -44,6 +48,8 @@ fun Routing.syncRouting() {

val deviceRefresher = koinApplication.koin.get<DeviceRefresher>()

val clientHandlerManager = koinApplication.koin.get<ClientHandlerManager>()

post("/sync/syncInfos") {
getAppInstanceId(call).let { appInstanceId ->
val signalProtocolAddress = SignalProtocolAddress(appInstanceId, 1)
Expand All @@ -53,6 +59,10 @@ fun Routing.syncRouting() {
val identityKeys = requestTrustSyncInfos.map { ClipIdentityKey(it.syncInfo.appInfo.appInstanceId, it.identityKey.serialize()) }
syncRuntimeInfoDao.inertOrUpdate(syncInfos)
signalDao.saveIdentities(identityKeys)
for (syncInfo in syncInfos) {
logger.debug { "syncInfo = $syncInfo" }
clientHandlerManager.addHandler(syncInfo.appInfo.appInstanceId)
}
deviceRefresher.refresh(CheckAction.CheckNonConnected)
successResponse(call)
} ?: failResponse(call, StandardErrorCode.SIGNAL_UNTRUSTED_IDENTITY.toErrorCode(), "not trust $appInstanceId")
Expand All @@ -68,7 +78,7 @@ fun Routing.syncRouting() {

val signalProtocolAddress = SignalProtocolAddress(appInstanceId, 1)

signalProtocolStore.getIdentity(signalProtocolAddress)?.let {
signalProtocolStore.getIdentity(signalProtocolAddress) ?: run {
failResponse(call, StandardErrorCode.SIGNAL_UNTRUSTED_IDENTITY.toErrorCode(), "not trust $appInstanceId")
return@get
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,51 @@ package com.clipevery.utils
import com.clipevery.dao.sync.HostInfo
import com.clipevery.net.ClipClient
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeout

class TelnetUtils(private val clipClient: ClipClient) {

private val logger = KotlinLogging.logger {}

suspend fun switchHost(hostInfoList: List<HostInfo>, port: Int, timeout: Long = 500L): HostInfo? {
if (hostInfoList.isEmpty()) {
return null
}
val deferredArray = hostInfoList.map { hostInfo ->
CoroutineScope(ioDispatcher).async {
if (telnet(hostInfo, port, timeout)) hostInfo else null
}
}
if (hostInfoList.isEmpty()) return null

val result = CompletableDeferred<HostInfo?>()
val mutex = Mutex()
val scope = CoroutineScope(Dispatchers.IO)

var result: HostInfo? = null
select {
deferredArray.forEach { deferred ->
deferred.onAwait { hostInfo ->
if (hostInfo != null) {
result = hostInfo
deferredArray.forEach { it.cancel() }
hostInfoList.forEach { hostInfo ->
scope.launch {
try {
if (telnet(hostInfo, port, timeout)) {
mutex.withLock {
if (!result.isCompleted) {
result.complete(hostInfo)
}
}
}
}
} catch (ignore: Exception) { }
}
}
return result

return try {
withTimeout(timeout) { result.await() }
} catch (e: TimeoutCancellationException) {
null
} finally {
scope.cancel()
}
}


private suspend fun telnet(hostInfo: HostInfo, port: Int, timeout: Long): Boolean {
return try {
val httpResponse = clipClient.get(timeout = timeout) { urlBuilder ->
Expand Down
Loading