[core] Introduce CacheValidator for validating caches (#2388)

* [core] Export DeviceInfo.serializer() to mirai-core

* [core] Introduce CacheValidator for validating caches
This commit is contained in:
微莹·纤绫 2023-01-02 23:39:36 +08:00 committed by GitHub
parent 6365e23f2c
commit 553ea9abbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 139 additions and 3 deletions

View File

@ -118,6 +118,8 @@ public expect class DeviceInfo(
*/
@JvmStatic
public fun random(random: Random): DeviceInfo
public fun serializer(): KSerializer<DeviceInfo>
}
/**

View File

@ -52,6 +52,7 @@ import net.mamoe.mirai.internal.network.notice.priv.PrivateMessageProcessor
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.utils.ImagePatcher
import net.mamoe.mirai.internal.utils.ImagePatcherImpl
import net.mamoe.mirai.internal.utils.actualCacheDir
import net.mamoe.mirai.internal.utils.subLogger
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.MiraiLogger
@ -208,6 +209,14 @@ internal open class QQAndroidBot constructor(
set(SsoProcessorContext, SsoProcessorContextImpl(bot))
set(SsoProcessor, SsoProcessorImpl(get(SsoProcessorContext)))
val cacheValidator = CacheValidatorImpl(
get(SsoProcessorContext),
configuration.actualCacheDir().resolve("validator.bin"),
networkLogger.subLogger("CacheValidator"),
)
set(CacheValidator, cacheValidator)
set(HeartbeatProcessor, HeartbeatProcessorImpl())
set(HeartbeatScheduler, TimeBasedHeartbeatSchedulerImpl(networkLogger.subLogger("HeartbeatScheduler")))
set(HttpClientProvider, HttpClientProviderImpl())
@ -251,6 +260,9 @@ internal open class QQAndroidBot constructor(
configuration.createAccountsSecretsManager(bot.logger.subLogger("AccountSecretsManager")),
)
set(ImagePatcher, ImagePatcherImpl())
cacheValidator.register(get(AccountSecretsManager))
cacheValidator.register(get(BdhSessionSyncer))
}
/**

View File

@ -35,10 +35,10 @@ import kotlin.jvm.Volatile
* @see FileCacheAccountSecretsManager
* @see CombinedAccountSecretsManager
*/
internal interface AccountSecretsManager {
internal interface AccountSecretsManager : Cacheable {
fun saveSecrets(account: BotAccount, secrets: AccountSecrets)
fun getSecrets(account: BotAccount): AccountSecrets?
fun invalidate()
override fun invalidate()
companion object : ComponentKey<AccountSecretsManager>
}

View File

@ -22,7 +22,7 @@ import net.mamoe.mirai.internal.utils.actualCacheDir
import net.mamoe.mirai.utils.*
import kotlin.jvm.Volatile
internal interface BdhSessionSyncer {
internal interface BdhSessionSyncer : Cacheable {
val bdhSession: CompletableDeferred<BdhSession>
val hasSession: Boolean
@ -77,6 +77,11 @@ internal class BdhSessionSyncerImpl(
private val serverListCacheFile: MiraiFile
get() = configuration.actualCacheDir().resolve("servers.json")
override fun invalidate() {
sessionCacheFile.delete()
serverListCacheFile.delete()
}
override fun loadServerListFromCache() {
val serverListCacheFile = this.serverListCacheFile
if (serverListCacheFile.isFile) {

View File

@ -0,0 +1,115 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.components
import io.ktor.utils.io.core.*
import net.mamoe.mirai.internal.network.ProtoBufForCache
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.utils.MiraiProtocolInternal
import net.mamoe.mirai.internal.utils.io.writeShortLVString
import net.mamoe.mirai.utils.*
/**
* Validator for checking caches is usable for current bot or not.
*/
internal interface CacheValidator {
fun register(cache: Cacheable)
fun validate()
companion object : ComponentKey<CacheValidator>
}
internal interface Cacheable {
fun invalidate()
}
internal class CacheValidatorImpl(
private val ssoProcessorContext: SsoProcessorContext,
private val hashFile: MiraiFile,
private val logger: MiraiLogger,
) : CacheValidator {
private val caches: MutableList<Cacheable> = mutableListOf()
override fun register(cache: Cacheable) {
caches.add(cache)
}
override fun validate() {
val hash: ByteArray = buildPacket {
val botConf = ssoProcessorContext.configuration
writeInt(botConf.protocol.ordinal)
val internalProtocol = MiraiProtocolInternal[botConf.protocol]
writeShortLVString(internalProtocol.apkId)
writeLong(internalProtocol.id)
writeShortLVString(internalProtocol.sdkVer)
writeInt(internalProtocol.miscBitMap)
writeInt(internalProtocol.subSigMap)
writeInt(internalProtocol.mainSigMap)
writeShortLVString(internalProtocol.sign)
writeLong(internalProtocol.buildTime)
writeInt(internalProtocol.ssoVersion)
val device = ssoProcessorContext.device
@Suppress("INVISIBLE_MEMBER")
writeFully(ProtoBufForCache.encodeToByteArray(DeviceInfo.serializer(), device))
}.let { pkg ->
try {
pkg.readBytes()
} finally {
pkg.release()
}
}.sha1()
if (!hashFile.exists()) {
logger.verbose { "Invalidate caches because hash file not available." }
invalidate()
kotlin.runCatching {
hashFile.writeBytes(hash)
}.onFailure { logger.warning("Exception in writing hash to validation file", it) }
return
}
if (!hashFile.isFile) {
logger.verbose { "hash file isn't a file." }
invalidate()
kotlin.runCatching {
hashFile.deleteRecursively()
hashFile.writeBytes(hash)
}.onFailure { logger.warning("Exception in writing hash to validation file", it) }
return
}
try {
val hashInFile = hashFile.readBytes()
if (hashInFile.contentEquals(hash)) {
logger.verbose { "Validated caches." }
return
}
logger.verbose { "Hash not match. Invaliding caches....." }
invalidate()
hashFile.writeBytes(hash)
} catch (e: Throwable) {
logger.warning("Exception in validation. Invalidating.....", e)
invalidate()
}
}
private fun invalidate() {
caches.forEach { it.invalidate() }
}
}

View File

@ -146,6 +146,8 @@ internal class SsoProcessorImpl(
* Do login. Throws [LoginFailedException] if failed
*/
override suspend fun login(handler: NetworkHandler) = withExceptionCollector {
components[CacheValidator].validate()
components[BdhSessionSyncer].loadServerListFromCache()
try {
if (client.wLoginSigInfoInitialized) {