diff --git a/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt index b2251b9a1..87cac335e 100644 --- a/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/impl/netty/NettyNetworkHandler.kt @@ -107,7 +107,7 @@ internal open class NettyNetworkHandler( handlePipelineException(ctx, cause) } }) - .addLast(OutgoingPacketEncoder()) + .addLast("outgoing-packet-encoder", OutgoingPacketEncoder()) .addLast(LengthFieldBasedFrameDecoder(Int.MAX_VALUE, 0, 4, -4, 4)) .addLast(ByteBufToIncomingPacketDecoder()) .addLast("raw-packet-collector", RawIncomingPacketCollector(decodePipeline)) diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTest.kt index 21ad528d5..fca29eee6 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTest.kt @@ -14,14 +14,26 @@ import io.netty.channel.embedded.EmbeddedChannel import io.netty.util.ReferenceCountUtil import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope +import kotlinx.io.core.buildPacket +import kotlinx.io.core.readBytes +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.serializer import net.mamoe.mirai.internal.AbstractBot import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor +import net.mamoe.mirai.internal.network.components.RawIncomingPacket import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandler +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.internal.utils.io.ProtoBuf +import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.utils.ExceptionCollector +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.cast +import net.mamoe.mirai.utils.error import java.net.SocketAddress /** @@ -72,8 +84,49 @@ internal abstract class AbstractNettyNHTest : AbstractRealNetworkHandlerTest, var fakeServer: (NettyNHTestChannel.(msg: Any?) -> Unit)? = null, ) : EmbeddedChannel() { + @OptIn(InternalSerializationApi::class) + fun listen(listener: (OutgoingPacket) -> Any?) { + fakeServer = { packet -> + if (packet is OutgoingPacket) { + val rsp0 = when (val rsp = listener(packet)) { + null -> null + is Unit -> null + is ByteArray -> { + RawIncomingPacket( + commandName = packet.commandName, + sequenceId = packet.sequenceId, + body = rsp + ) + } + is RawIncomingPacket -> rsp + is ProtoBuf -> { + RawIncomingPacket( + commandName = packet.commandName, + sequenceId = packet.sequenceId, + body = buildPacket { + writeProtoBuf( + rsp::class.serializer().cast>(), + rsp + ) + }.readBytes() + ) + } + else -> { + logger.value.error { "Failed to respond $rsp" } + null + } + } + if (rsp0 != null) { + pipeline().fireChannelRead(rsp0) + } + } + ReferenceCountUtil.release(packet) + } + } + public /*internal*/ override fun doRegister() { super.doRegister() // Set channel state to ACTIVE // Drop old handlers @@ -93,7 +146,9 @@ internal abstract class AbstractNettyNHTest : AbstractRealNetworkHandlerTest { object : TestNettyNH(bot, createContext(), createAddress()) { diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealTimeActionTestUnit.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealTimeActionTestUnit.kt new file mode 100644 index 000000000..813b14eaa --- /dev/null +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealTimeActionTestUnit.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2019-2021 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.framework + +import net.mamoe.mirai.internal.network.KeyWithCreationTime +import net.mamoe.mirai.internal.network.KeyWithExpiry +import net.mamoe.mirai.internal.network.WLoginSigInfo +import net.mamoe.mirai.internal.network.WLoginSimpleInfo +import net.mamoe.mirai.internal.notice.processors.GroupExtensions +import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import net.mamoe.mirai.utils.hexToBytes +import org.junit.jupiter.api.BeforeEach + +internal abstract class AbstractRealTimeActionTestUnit : AbstractNettyNHTest(), GroupExtensions { + @BeforeEach + internal fun prepareEnv() { + bot.client.wLoginSigInfoField = WLoginSigInfo( + uin = bot.id, + encryptA1 = "01 23 33 AF EA".hexToBytes(), + noPicSig = "55 47 20 23 54".hexToBytes(), + simpleInfo = WLoginSimpleInfo( + uin = bot.id, + imgType = EMPTY_BYTE_ARRAY, + imgFormat = EMPTY_BYTE_ARRAY, + imgUrl = EMPTY_BYTE_ARRAY, + mainDisplayName = EMPTY_BYTE_ARRAY, + ), + appPri = 0, + a2ExpiryTime = 0, + a2CreationTime = 849415181, + loginBitmap = 1145141919810, + tgt = "EA 5B CE FA 6C".hexToBytes(), + tgtKey = "66 F5 A9 B8 FF".hexToBytes(), + userStSig = KeyWithCreationTime(data = "3C FF FF FF 07".hexToBytes(), creationTime = 0), + userStKey = "07 F5 A9 B8 0B".hexToBytes(), + userStWebSig = KeyWithExpiry(data = "A1 5B CE FA 60".hexToBytes(), creationTime = 0, expireTime = 0), + userA5 = KeyWithCreationTime(data = "66 CC FF AA AA".hexToBytes(), creationTime = 0), + userA8 = KeyWithExpiry(data = "65 c1 B9 7A 1F".hexToBytes(), creationTime = 0, expireTime = 0), + lsKey = KeyWithExpiry(data = "65 c1 B9 7A 1F".hexToBytes(), creationTime = 0, expireTime = 0), + sKey = KeyWithExpiry(data = "D6 B1 9C 66 3A".hexToBytes(), creationTime = 0, expireTime = 0), + userSig64 = KeyWithCreationTime(data = "D6 B1 9C 66 3A".hexToBytes(), creationTime = 0), + openId = "D6 B1 9C 66 3A".hexToBytes(), + openKey = KeyWithCreationTime(data = "B4 6E 5E 7A 3C".hexToBytes(), creationTime = 0), + vKey = KeyWithExpiry(data = "A1 34 17 48 21".hexToBytes(), creationTime = 0, expireTime = 0), + accessToken = KeyWithCreationTime(data = "12 35 87 14 A1".hexToBytes(), creationTime = 0), + aqSig = KeyWithExpiry(data = "22 0C DC AC 30".hexToBytes(), creationTime = 0, expireTime = 0), + superKey = "22 33 66 CC FF".hexToBytes(), + sid = KeyWithExpiry(data = "11 45 14 19 19".hexToBytes(), creationTime = 0, expireTime = 0), + psKeyMap = mutableMapOf(), + pt4TokenMap = mutableMapOf(), + d2 = KeyWithExpiry(data = "81 00 07 64 11".hexToBytes(), creationTime = 0, expireTime = 0), + d2Key = "404 not found!!!!!!".toByteArray(), + payToken = "What's this".toByteArray(), + pf = "> 1 + 1 == 11\n< true".toByteArray(), + pfKey = "Don't change anything if it runs".toByteArray(), + da2 = "sudo rm -rf /".toByteArray(), + wtSessionTicket = KeyWithCreationTime(data = "deluser root".toByteArray(), creationTime = 0), + wtSessionTicketKey = "500 Server Internal Error".toByteArray(), + deviceToken = "Winserver datacenter 2077".toByteArray(), + ) + bot.client._bot = bot + network.setStateOK(channel) + removeOutgoingPacketEncoder() + } +}