mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-05 07:30:09 +08:00
Simplify platform structure, simplify network implementations
This commit is contained in:
parent
9c71a9c953
commit
6ccd20c377
@ -130,7 +130,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
|||||||
|
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
try {
|
try {
|
||||||
channel.connect(coroutineContext + CoroutineName("Socket"), host, port)
|
channel.connect(host, port)
|
||||||
break
|
break
|
||||||
} catch (e: SocketException) {
|
} catch (e: SocketException) {
|
||||||
if (e is NoRouteToHostException || e.message?.contains("Network is unreachable") == true) {
|
if (e is NoRouteToHostException || e.message?.contains("Network is unreachable") == true) {
|
||||||
|
@ -24,8 +24,8 @@ import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY
|
|||||||
import net.mamoe.mirai.internal.network.protocol.packet.PacketLogger
|
import net.mamoe.mirai.internal.network.protocol.packet.PacketLogger
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.Tlv
|
import net.mamoe.mirai.internal.network.protocol.packet.Tlv
|
||||||
import net.mamoe.mirai.internal.utils.*
|
import net.mamoe.mirai.internal.utils.*
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.ECDH
|
import net.mamoe.mirai.internal.utils.crypto.ECDH
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.TEA
|
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||||
import net.mamoe.mirai.network.LoginFailedException
|
import net.mamoe.mirai.network.LoginFailedException
|
||||||
import net.mamoe.mirai.network.NoServerAvailableException
|
import net.mamoe.mirai.network.NoServerAvailableException
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
|
@ -31,7 +31,6 @@ import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
|
|||||||
import net.mamoe.mirai.internal.utils.io.withUse
|
import net.mamoe.mirai.internal.utils.io.withUse
|
||||||
import net.mamoe.mirai.utils.internal.ReusableInput
|
import net.mamoe.mirai.utils.internal.ReusableInput
|
||||||
import net.mamoe.mirai.utils.verbose
|
import net.mamoe.mirai.utils.verbose
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
import kotlin.time.measureTime
|
import kotlin.time.measureTime
|
||||||
@ -143,7 +142,7 @@ internal object HighwayHelper {
|
|||||||
val socket = PlatformSocket()
|
val socket = PlatformSocket()
|
||||||
while (client.bot.network.areYouOk() && client.bot.isActive) {
|
while (client.bot.network.areYouOk() && client.bot.isActive) {
|
||||||
try {
|
try {
|
||||||
socket.connect(EmptyCoroutineContext, serverIp, serverPort)
|
socket.connect(serverIp, serverPort)
|
||||||
break
|
break
|
||||||
} catch (e: SocketException) {
|
} catch (e: SocketException) {
|
||||||
delay(3000)
|
delay(3000)
|
||||||
|
@ -14,8 +14,8 @@ import kotlinx.io.core.ByteReadPacket
|
|||||||
import kotlinx.io.core.buildPacket
|
import kotlinx.io.core.buildPacket
|
||||||
import kotlinx.io.core.writeFully
|
import kotlinx.io.core.writeFully
|
||||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.ECDH
|
import net.mamoe.mirai.internal.utils.crypto.ECDH
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.ECDHKeyPair
|
import net.mamoe.mirai.internal.utils.crypto.ECDHKeyPair
|
||||||
import net.mamoe.mirai.internal.utils.io.encryptAndWrite
|
import net.mamoe.mirai.internal.utils.io.encryptAndWrite
|
||||||
import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray
|
import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
|
|||||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
||||||
import net.mamoe.mirai.internal.network.readUShortLVByteArray
|
import net.mamoe.mirai.internal.network.readUShortLVByteArray
|
||||||
import net.mamoe.mirai.internal.utils.*
|
import net.mamoe.mirai.internal.utils.*
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.TEA
|
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.adjustToPublicKey
|
import net.mamoe.mirai.internal.utils.crypto.adjustToPublicKey
|
||||||
import net.mamoe.mirai.internal.utils.io.readPacketExact
|
import net.mamoe.mirai.internal.utils.io.readPacketExact
|
||||||
import net.mamoe.mirai.internal.utils.io.readString
|
import net.mamoe.mirai.internal.utils.io.readString
|
||||||
import net.mamoe.mirai.internal.utils.io.useBytes
|
import net.mamoe.mirai.internal.utils.io.useBytes
|
||||||
|
@ -16,7 +16,7 @@ import net.mamoe.mirai.internal.network.*
|
|||||||
import net.mamoe.mirai.internal.network.protocol.LoginType
|
import net.mamoe.mirai.internal.network.protocol.LoginType
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.*
|
import net.mamoe.mirai.internal.network.protocol.packet.*
|
||||||
import net.mamoe.mirai.internal.utils.*
|
import net.mamoe.mirai.internal.utils.*
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.TEA
|
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||||
import net.mamoe.mirai.internal.utils.guidFlag
|
import net.mamoe.mirai.internal.utils.guidFlag
|
||||||
import net.mamoe.mirai.internal.utils.io.*
|
import net.mamoe.mirai.internal.utils.io.*
|
||||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 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/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils
|
|
||||||
|
|
||||||
import kotlinx.atomicfu.AtomicLong
|
|
||||||
import kotlinx.atomicfu.atomic
|
|
||||||
import kotlinx.atomicfu.locks.reentrantLock
|
|
||||||
import kotlinx.atomicfu.locks.withLock
|
|
||||||
import net.mamoe.mirai.utils.currentTimeMillis
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamically sized cache list with retention period.
|
|
||||||
* No concurrency guaranteed on same elements.
|
|
||||||
*/
|
|
||||||
internal class AtomicResizeCacheList<E>(private val retention: Long) {
|
|
||||||
private inner class Cache {
|
|
||||||
@Volatile
|
|
||||||
@JvmField
|
|
||||||
var element: E? = null
|
|
||||||
|
|
||||||
val time: AtomicLong = atomic(0L)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val initialCapacity: Int = 32
|
|
||||||
}
|
|
||||||
|
|
||||||
private val list: MutableList<Cache> = ArrayList(initialCapacity)
|
|
||||||
private val lock = reentrantLock()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an element, also cleanup outdated caches, but no duplication is removed.
|
|
||||||
* No concurrency guaranteed on same [element].
|
|
||||||
*/
|
|
||||||
private fun add(element: E) {
|
|
||||||
val currentTime = currentTimeMillis()
|
|
||||||
findAvailable@ while (true) {
|
|
||||||
for (cache in list) {
|
|
||||||
val instant = cache.time.value
|
|
||||||
when {
|
|
||||||
instant == 0L -> {
|
|
||||||
if (cache.time.compareAndSet(instant, currentTime + retention)) {
|
|
||||||
cache.element = element
|
|
||||||
return
|
|
||||||
} else continue@findAvailable
|
|
||||||
}
|
|
||||||
// outdated
|
|
||||||
instant < currentTime -> cache.time.compareAndSet(instant, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no more Cache instance available
|
|
||||||
lock.withLock {
|
|
||||||
list.add(Cache().apply {
|
|
||||||
this.element = element
|
|
||||||
this.time.value = currentTime + retention
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No concurrency guaranteed on same [element]
|
|
||||||
*/
|
|
||||||
private fun removeDuplication(element: E): Boolean {
|
|
||||||
val duplicate = list.firstOrNull { it.time.value != 0L && it.element == element } ?: return false
|
|
||||||
duplicate.time.value = 0
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ensureNoDuplication(element: E): Boolean {
|
|
||||||
return if (removeDuplication(element)) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
add(element)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,26 +9,97 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.utils
|
package net.mamoe.mirai.internal.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.io.core.Closeable
|
import kotlinx.io.core.Closeable
|
||||||
|
import kotlinx.io.nio.readPacketAtMost
|
||||||
|
import kotlinx.io.nio.writePacket
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.nio.channels.DatagramChannel
|
||||||
|
import java.nio.channels.ReadableByteChannel
|
||||||
|
import java.nio.channels.WritableByteChannel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多平台适配的 DatagramChannel.
|
* 多平台适配的 DatagramChannel.
|
||||||
*/
|
*/
|
||||||
internal expect class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Closeable {
|
internal class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Closeable {
|
||||||
/**
|
@PublishedApi
|
||||||
* @throws SendPacketInternalException
|
internal val channel: DatagramChannel =
|
||||||
*/
|
DatagramChannel.open().connect(InetSocketAddress(serverHost, serverPort.toInt()))
|
||||||
suspend inline fun send(packet: ByteReadPacket): Boolean
|
val isOpen: Boolean get() = channel.isOpen
|
||||||
|
override fun close() = channel.close()
|
||||||
|
|
||||||
/**
|
suspend inline fun send(packet: ByteReadPacket): Boolean = withContext(Dispatchers.IO) {
|
||||||
* @throws ReadPacketInternalException
|
try {
|
||||||
*/
|
(channel as WritableByteChannel).writePacket(packet)
|
||||||
suspend inline fun read(): ByteReadPacket
|
} catch (e: Throwable) {
|
||||||
|
throw SendPacketInternalException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isOpen: Boolean
|
suspend inline fun read(): ByteReadPacket = withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
(channel as ReadableByteChannel).readPacketAtMost(Long.MAX_VALUE)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
throw ReadPacketInternalException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
actual class PlatformDatagramChannel actual constructor(serverHost: String, serverPort: Short) : Closeable {
|
||||||
|
private val serverAddress: InetSocketAddress = InetSocketAddress(serverHost, serverPort.toInt())
|
||||||
|
|
||||||
|
@KtorExperimentalAPI
|
||||||
|
val socket = runBlocking { aSocket(ActorSelectorManager(Dispatchers.IO)).tcp()
|
||||||
|
.connect(remoteAddress = serverAddress) }
|
||||||
|
|
||||||
|
@KtorExperimentalAPI
|
||||||
|
val readChannel = socket.openReadChannel()
|
||||||
|
|
||||||
|
@KtorExperimentalAPI
|
||||||
|
val writeChannel = socket.openWriteChannel(true)
|
||||||
|
|
||||||
|
@KtorExperimentalAPI
|
||||||
|
@Throws(ReadPacketInternalException::class)
|
||||||
|
actual suspend fun read(buffer: IoBuffer) =
|
||||||
|
try {
|
||||||
|
readChannel.readAvailable(buffer)
|
||||||
|
} catch (e: ClosedChannelException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
throw ReadPacketInternalException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@KtorExperimentalAPI
|
||||||
|
@Throws(SendPacketInternalException::class)
|
||||||
|
actual suspend fun send(buffer: IoBuffer) =
|
||||||
|
buffer.readDirect {
|
||||||
|
try {
|
||||||
|
writeChannel.writeFully(it)
|
||||||
|
} catch (e: ClosedChannelException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
throw SendPacketInternalException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@KtorExperimentalAPI
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun close() {
|
||||||
|
socket.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@KtorExperimentalAPI
|
||||||
|
actual val isOpen: Boolean
|
||||||
|
get() = socket.isClosed.not()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
|
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
|
||||||
*/
|
*/
|
||||||
|
@ -9,38 +9,95 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.utils
|
package net.mamoe.mirai.internal.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.io.core.Closeable
|
import kotlinx.io.core.Closeable
|
||||||
import kotlinx.io.errors.IOException
|
import kotlinx.io.streams.readPacketAtMost
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlinx.io.streams.writePacket
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.NoRouteToHostException
|
||||||
|
import java.net.Socket
|
||||||
|
import java.net.UnknownHostException
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多平台适配的 TCP Socket.
|
* TCP Socket.
|
||||||
*/
|
*/
|
||||||
internal expect class PlatformSocket() : Closeable {
|
internal class PlatformSocket : Closeable {
|
||||||
@kotlin.Throws(SocketException::class)
|
private lateinit var socket: Socket
|
||||||
suspend fun connect(coroutineContext: CoroutineContext, serverHost: String, serverPort: Int)
|
|
||||||
|
val isOpen: Boolean
|
||||||
|
get() =
|
||||||
|
if (::socket.isInitialized)
|
||||||
|
socket.isConnected
|
||||||
|
else false
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if (::socket.isInitialized) {
|
||||||
|
socket.close()
|
||||||
|
}
|
||||||
|
thread.shutdownNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal lateinit var writeChannel: BufferedOutputStream
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal lateinit var readChannel: BufferedInputStream
|
||||||
|
|
||||||
|
suspend fun send(packet: ByteArray, offset: Int, length: Int) {
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
writeChannel.write(packet, offset, length)
|
||||||
|
writeChannel.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws SendPacketInternalException
|
* @throws SendPacketInternalException
|
||||||
*/
|
*/
|
||||||
suspend fun send(packet: ByteArray, offset: Int = 0, length: Int = packet.size - offset)
|
suspend fun send(packet: ByteReadPacket) {
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
writeChannel.writePacket(packet)
|
||||||
|
writeChannel.flush()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw SendPacketInternalException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private val thread = Executors.newSingleThreadExecutor()
|
||||||
* @throws SendPacketInternalException
|
|
||||||
*/
|
|
||||||
suspend fun send(packet: ByteReadPacket)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ReadPacketInternalException
|
* @throws ReadPacketInternalException
|
||||||
*/
|
*/
|
||||||
suspend fun read(): ByteReadPacket
|
suspend fun read(): ByteReadPacket = suspendCancellableCoroutine { cont ->
|
||||||
|
thread.submit {
|
||||||
|
kotlin.runCatching {
|
||||||
|
readChannel.readPacketAtMost(Long.MAX_VALUE)
|
||||||
|
}.let {
|
||||||
|
cont.resumeWith(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isOpen: Boolean
|
suspend fun connect(serverHost: String, serverPort: Int) {
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
override fun close()
|
withContext(Dispatchers.IO) {
|
||||||
|
socket = Socket(serverHost, serverPort)
|
||||||
|
readChannel = socket.getInputStream().buffered()
|
||||||
|
writeChannel = socket.getOutputStream().buffered()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal expect open class SocketException : IOException
|
|
||||||
internal expect class NoRouteToHostException : SocketException
|
internal typealias SocketException = java.net.SocketException
|
||||||
internal expect class UnknownHostException : IOException
|
internal typealias NoRouteToHostException = NoRouteToHostException
|
||||||
|
internal typealias UnknownHostException = UnknownHostException
|
@ -135,7 +135,9 @@ internal fun Any?._miraiContentToString(prefix: String = ""): String = when (thi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal expect fun KProperty1<*, *>.getValueAgainstPermission(receiver: Any): Any?
|
internal fun KProperty1<*, *>.getValueAgainstPermission(receiver: Any): Any? {
|
||||||
|
return this.javaField?.apply { isAccessible = true }?.get(receiver)
|
||||||
|
}
|
||||||
|
|
||||||
private fun Any.canBeIgnored(): Boolean {
|
private fun Any.canBeIgnored(): Boolean {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
|
@ -7,13 +7,11 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils.cryptor
|
package net.mamoe.mirai.internal.utils.crypto
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.utils.chunkedHexToBytes
|
import net.mamoe.mirai.internal.utils.chunkedHexToBytes
|
||||||
|
|
||||||
internal expect interface ECDHPrivateKey {
|
internal expect interface ECDHPrivateKey
|
||||||
fun getEncoded(): ByteArray
|
|
||||||
}
|
|
||||||
|
|
||||||
internal expect interface ECDHPublicKey {
|
internal expect interface ECDHPublicKey {
|
||||||
fun getEncoded(): ByteArray
|
fun getEncoded(): ByteArray
|
@ -7,7 +7,7 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils.cryptor
|
package net.mamoe.mirai.internal.utils.crypto
|
||||||
|
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.io.pool.useInstance
|
import kotlinx.io.pool.useInstance
|
||||||
@ -16,7 +16,6 @@ import net.mamoe.mirai.internal.utils.toByteArray
|
|||||||
import net.mamoe.mirai.internal.utils.toUHexString
|
import net.mamoe.mirai.internal.utils.toUHexString
|
||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
import kotlin.experimental.xor
|
import kotlin.experimental.xor
|
||||||
import kotlin.jvm.JvmStatic
|
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
@ -15,9 +15,7 @@ package net.mamoe.mirai.internal.utils.io
|
|||||||
|
|
||||||
import kotlinx.io.core.*
|
import kotlinx.io.core.*
|
||||||
import net.mamoe.mirai.internal.utils.coerceAtMostOrFail
|
import net.mamoe.mirai.internal.utils.coerceAtMostOrFail
|
||||||
import net.mamoe.mirai.internal.utils.cryptor.TEA
|
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||||
import kotlin.jvm.JvmMultifileClass
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
|
|
||||||
internal fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLength: Int) {
|
internal fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLength: Int) {
|
||||||
if (array.size <= maxLength) {
|
if (array.size <= maxLength) {
|
||||||
|
@ -1,212 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 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/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.io.core.ByteReadPacket
|
|
||||||
import kotlinx.io.core.Closeable
|
|
||||||
import kotlinx.io.errors.IOException
|
|
||||||
import kotlinx.io.streams.readPacketAtMost
|
|
||||||
import kotlinx.io.streams.writePacket
|
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.BufferedOutputStream
|
|
||||||
import java.net.Socket
|
|
||||||
import java.net.SocketException
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 多平台适配的 TCP Socket.
|
|
||||||
*/
|
|
||||||
internal actual class PlatformSocket : Closeable {
|
|
||||||
private lateinit var socket: Socket
|
|
||||||
|
|
||||||
actual val isOpen: Boolean
|
|
||||||
get() =
|
|
||||||
if (::socket.isInitialized)
|
|
||||||
socket.isConnected
|
|
||||||
else false
|
|
||||||
|
|
||||||
actual override fun close() {
|
|
||||||
if (::socket.isInitialized) {
|
|
||||||
socket.close()
|
|
||||||
}
|
|
||||||
thread.shutdownNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal lateinit var writeChannel: BufferedOutputStream
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal lateinit var readChannel: BufferedInputStream
|
|
||||||
|
|
||||||
actual suspend fun send(packet: ByteArray, offset: Int, length: Int) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
writeChannel.write(packet, offset, length)
|
|
||||||
writeChannel.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws SendPacketInternalException
|
|
||||||
*/
|
|
||||||
actual suspend fun send(packet: ByteReadPacket) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
writeChannel.writePacket(packet)
|
|
||||||
writeChannel.flush()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw SendPacketInternalException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val thread = Executors.newSingleThreadExecutor()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ReadPacketInternalException
|
|
||||||
*/
|
|
||||||
actual suspend fun read(): ByteReadPacket = suspendCancellableCoroutine { cont ->
|
|
||||||
thread.submit {
|
|
||||||
kotlin.runCatching {
|
|
||||||
readChannel.readPacketAtMost(Long.MAX_VALUE)
|
|
||||||
}.let {
|
|
||||||
cont.resumeWith(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual suspend fun connect(coroutineContext: CoroutineContext, serverHost: String, serverPort: Int) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
socket = Socket(serverHost, serverPort)
|
|
||||||
readChannel = socket.getInputStream().buffered()
|
|
||||||
writeChannel = socket.getOutputStream().buffered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("ACTUAL_WITHOUT_EXPECT")
|
|
||||||
internal actual typealias NoRouteToHostException = java.net.NoRouteToHostException
|
|
||||||
|
|
||||||
@Suppress("ACTUAL_WITHOUT_EXPECT")
|
|
||||||
internal actual typealias SocketException = SocketException
|
|
||||||
|
|
||||||
// ktor aSocket
|
|
||||||
|
|
||||||
/*
|
|
||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils
|
|
||||||
|
|
||||||
import io.ktor.network.selector.ActorSelectorManager
|
|
||||||
import io.ktor.network.sockets.*
|
|
||||||
import io.ktor.util.KtorExperimentalAPI
|
|
||||||
import io.ktor.utils.io.ByteReadChannel
|
|
||||||
import io.ktor.utils.io.ByteWriteChannel
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.io.core.ByteReadPacket
|
|
||||||
import kotlinx.io.core.Closeable
|
|
||||||
import kotlinx.io.core.ExperimentalIoApi
|
|
||||||
import kotlinx.io.core.buildPacket
|
|
||||||
import kotlinx.io.errors.IOException
|
|
||||||
import useBytes
|
|
||||||
import java.net.InetSocketAddress
|
|
||||||
import java.net.SocketException
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 多平台适配的 TCP Socket.
|
|
||||||
*/
|
|
||||||
internal actual class PlatformSocket : Closeable {
|
|
||||||
private lateinit var socket: io.ktor.network.sockets.Socket
|
|
||||||
|
|
||||||
actual val isOpen: Boolean
|
|
||||||
get() =
|
|
||||||
if (::socket.isInitialized)
|
|
||||||
!socket.isClosed
|
|
||||||
else false
|
|
||||||
|
|
||||||
actual override fun close() {
|
|
||||||
if (::socket.isInitialized) {
|
|
||||||
socket.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal lateinit var writeChannel: ByteWriteChannel
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal lateinit var readChannel: ByteReadChannel
|
|
||||||
|
|
||||||
actual suspend fun send(packet: ByteArray, offset: Int, length: Int) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
writeChannel.writeFully(packet, offset, length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws SendPacketInternalException
|
|
||||||
*/
|
|
||||||
actual suspend fun send(packet: ByteReadPacket) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
packet.useBytes { data: ByteArray, length: Int ->
|
|
||||||
writeChannel.writeFully(data, offset = 0, length = length)
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw SendPacketInternalException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws ReadPacketInternalException
|
|
||||||
*/
|
|
||||||
actual suspend fun read(): ByteReadPacket {
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
buildPacket {
|
|
||||||
readChannel.read {
|
|
||||||
writeFully(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw ReadPacketInternalException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalIoApi::class, KtorExperimentalAPI::class)
|
|
||||||
actual suspend fun connect(coroutineContext: CoroutineContext, serverHost: String, serverPort: Int) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
socket = aSocket(ActorSelectorManager(coroutineContext)).tcp().tcpNoDelay()
|
|
||||||
.connect(InetSocketAddress(serverHost, serverPort))
|
|
||||||
readChannel = socket.openReadChannel()
|
|
||||||
writeChannel = socket.openWriteChannel(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual typealias NoRouteToHostException = java.net.NoRouteToHostException
|
|
||||||
|
|
||||||
actual typealias SocketException = SocketException
|
|
||||||
*/
|
|
||||||
@Suppress("ACTUAL_WITHOUT_EXPECT")
|
|
||||||
internal actual typealias UnknownHostException = java.net.UnknownHostException
|
|
@ -1,104 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 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/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.io.core.ByteReadPacket
|
|
||||||
import kotlinx.io.core.Closeable
|
|
||||||
import kotlinx.io.nio.readPacketAtMost
|
|
||||||
import kotlinx.io.nio.writePacket
|
|
||||||
import java.net.InetSocketAddress
|
|
||||||
import java.nio.channels.DatagramChannel
|
|
||||||
import java.nio.channels.ReadableByteChannel
|
|
||||||
import java.nio.channels.WritableByteChannel
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 多平台适配的 DatagramChannel.
|
|
||||||
*/
|
|
||||||
internal actual class PlatformDatagramChannel actual constructor(
|
|
||||||
serverHost: String,
|
|
||||||
serverPort: Short
|
|
||||||
) : Closeable {
|
|
||||||
@PublishedApi
|
|
||||||
internal val channel: DatagramChannel = DatagramChannel.open().connect(InetSocketAddress(serverHost, serverPort.toInt()))
|
|
||||||
actual val isOpen: Boolean get() = channel.isOpen
|
|
||||||
override fun close() = channel.close()
|
|
||||||
|
|
||||||
actual suspend inline fun send(packet: ByteReadPacket): Boolean = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
(channel as WritableByteChannel).writePacket(packet)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
throw SendPacketInternalException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual suspend inline fun read(): ByteReadPacket = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
(channel as ReadableByteChannel).readPacketAtMost(Long.MAX_VALUE)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
throw ReadPacketInternalException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
actual class PlatformDatagramChannel actual constructor(serverHost: String, serverPort: Short) : Closeable {
|
|
||||||
private val serverAddress: InetSocketAddress = InetSocketAddress(serverHost, serverPort.toInt())
|
|
||||||
|
|
||||||
@KtorExperimentalAPI
|
|
||||||
val socket = runBlocking { aSocket(ActorSelectorManager(Dispatchers.IO)).tcp()
|
|
||||||
.connect(remoteAddress = serverAddress) }
|
|
||||||
|
|
||||||
@KtorExperimentalAPI
|
|
||||||
val readChannel = socket.openReadChannel()
|
|
||||||
|
|
||||||
@KtorExperimentalAPI
|
|
||||||
val writeChannel = socket.openWriteChannel(true)
|
|
||||||
|
|
||||||
@KtorExperimentalAPI
|
|
||||||
@Throws(ReadPacketInternalException::class)
|
|
||||||
actual suspend fun read(buffer: IoBuffer) =
|
|
||||||
try {
|
|
||||||
readChannel.readAvailable(buffer)
|
|
||||||
} catch (e: ClosedChannelException) {
|
|
||||||
throw e
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
throw ReadPacketInternalException(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@KtorExperimentalAPI
|
|
||||||
@Throws(SendPacketInternalException::class)
|
|
||||||
actual suspend fun send(buffer: IoBuffer) =
|
|
||||||
buffer.readDirect {
|
|
||||||
try {
|
|
||||||
writeChannel.writeFully(it)
|
|
||||||
} catch (e: ClosedChannelException) {
|
|
||||||
throw e
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
throw SendPacketInternalException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@KtorExperimentalAPI
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun close() {
|
|
||||||
socket.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
@KtorExperimentalAPI
|
|
||||||
actual val isOpen: Boolean
|
|
||||||
get() = socket.isClosed.not()
|
|
||||||
}
|
|
||||||
*/
|
|
@ -7,7 +7,7 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils.cryptor
|
package net.mamoe.mirai.internal.utils.crypto
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.utils.MiraiPlatformUtils
|
import net.mamoe.mirai.internal.utils.MiraiPlatformUtils
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
@ -31,9 +31,7 @@ internal actual class ECDHKeyPairImpl(
|
|||||||
override val initialShareKey: ByteArray by lazy { ECDH.calculateShareKey(privateKey, initialPublicKey) }
|
override val initialShareKey: ByteArray by lazy { ECDH.calculateShareKey(privateKey, initialPublicKey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
internal actual fun ECDH() = ECDH(ECDH.generateKeyPair())
|
||||||
internal actual fun ECDH() =
|
|
||||||
ECDH(ECDH.generateKeyPair())
|
|
||||||
|
|
||||||
internal actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
internal actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
||||||
actual companion object {
|
actual companion object {
|
@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 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/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.utils
|
|
||||||
|
|
||||||
import kotlin.reflect.KProperty1
|
|
||||||
import kotlin.reflect.jvm.javaField
|
|
||||||
|
|
||||||
internal actual fun KProperty1<*, *>.getValueAgainstPermission(receiver: Any): Any? {
|
|
||||||
return this.javaField?.apply { isAccessible = true }?.get(receiver)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user