Upgrade to Ktor 2.0.2, use CIO engine for linux targets; Remove ktor-client-okhttp from mirai-core-api jvmBaseMain

Use Input.readAllText instead of Input.readText, because readText only reads one buffer.
#2084
This commit is contained in:
Him188 2022-06-07 16:22:20 +01:00
parent 6293208c26
commit 4c6b879873
38 changed files with 273 additions and 280 deletions

View File

@ -31,7 +31,7 @@ object Versions {
const val coroutines = "1.6.2"
const val atomicFU = "0.17.2"
const val serialization = "1.3.2"
const val ktor = "1.6.8"
const val ktor = "2.0.2"
const val binaryValidator = "0.4.0"
@ -110,7 +110,7 @@ val `ktor-client-core` = ktor("client-core", Versions.ktor)
val `ktor-client-cio` = ktor("client-cio", Versions.ktor)
val `ktor-client-mock` = ktor("client-mock", Versions.ktor)
val `ktor-client-curl` = ktor("client-curl", Versions.ktor)
val `ktor-client-ios` = ktor("client-ios", Versions.ktor)
val `ktor-client-darwin` = ktor("client-darwin", Versions.ktor)
val `ktor-client-okhttp` = ktor("client-okhttp", Versions.ktor)
val `ktor-client-android` = ktor("client-android", Versions.ktor)
val `ktor-client-logging` = ktor("client-logging", Versions.ktor)

View File

@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* 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/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("UnusedImport")
@ -32,6 +32,7 @@ dependencies {
testImplementation(`slf4j-api`)
testImplementation(project(":mirai-core"))
testImplementation(project(":mirai-core-utils"))
testImplementation(`ktor-client-okhttp`)
}
configurePublishing("mirai-logging-log4j2")

View File

@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* 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/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("UnusedImport")
@ -33,6 +33,7 @@ dependencies {
testImplementation(project(":mirai-core"))
testImplementation(project(":mirai-core-utils"))
testImplementation(`ktor-client-okhttp`)
}
configurePublishing("mirai-logging-slf4j-logback")

View File

@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* 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/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("UnusedImport")
@ -33,6 +33,7 @@ dependencies {
testImplementation(project(":mirai-core"))
testImplementation(project(":mirai-core-utils"))
testImplementation(`ktor-client-okhttp`)
}
configurePublishing("mirai-logging-slf4j-simple")

View File

@ -1,10 +1,10 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* 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/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("UnusedImport")
@ -32,6 +32,7 @@ dependencies {
testImplementation(project(":mirai-core"))
testImplementation(project(":mirai-core-utils"))
testImplementation(`ktor-client-okhttp`)
}
configurePublishing("mirai-logging-slf4j")

View File

@ -0,0 +1,27 @@
/*
* 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
*/
@file:Suppress("UnusedImport")
plugins {
kotlin("jvm")
kotlin("plugin.serialization")
id("java")
}
version = "0.0.0"
kotlin {
explicitApiWarning()
}
dependencies {
api(project(":mirai-console.integration-test"))
compileOnly(`ktor-client-okhttp`)
}

View File

@ -22,7 +22,7 @@ public class PluginUseConsoleDepsFallback :
override fun onEnable() {
logger.info { "Plugin loaded" }
logger.info {
HttpClient(OkHttp).toString()
HttpClient(OkHttp).toString() // dependency is compileOnly
}
}
}

View File

@ -56,7 +56,6 @@ kotlin {
val jvmBaseMain by getting {
dependencies {
api(`ktor-client-okhttp`)
api(`kotlinx-coroutines-jdk8`)
implementation(`jetbrains-annotations`)
implementation(`log4j-api`)

View File

@ -96,7 +96,6 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
public abstract fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getBotFactory ()Lnet/mamoe/mirai/BotFactory;
public abstract fun getFileCacheStrategy ()Lnet/mamoe/mirai/utils/FileCacheStrategy;
public abstract fun getHttp ()Lio/ktor/client/HttpClient;
public fun getOnlineOtherClientsList (Lnet/mamoe/mirai/Bot;Z)Ljava/util/List;
public abstract fun getOnlineOtherClientsList (Lnet/mamoe/mirai/Bot;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getOnlineOtherClientsList$default (Lnet/mamoe/mirai/IMirai;Lnet/mamoe/mirai/Bot;ZILjava/lang/Object;)Ljava/util/List;
@ -125,7 +124,6 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
public fun sendNudge (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/action/Nudge;Lnet/mamoe/mirai/contact/Contact;)Z
public abstract fun sendNudge (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/action/Nudge;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun setFileCacheStrategy (Lnet/mamoe/mirai/utils/FileCacheStrategy;)V
public abstract fun setHttp (Lio/ktor/client/HttpClient;)V
}
public abstract interface class net/mamoe/mirai/LowLevelApiAccessor {

View File

@ -96,7 +96,6 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
public abstract fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getBotFactory ()Lnet/mamoe/mirai/BotFactory;
public abstract fun getFileCacheStrategy ()Lnet/mamoe/mirai/utils/FileCacheStrategy;
public abstract fun getHttp ()Lio/ktor/client/HttpClient;
public fun getOnlineOtherClientsList (Lnet/mamoe/mirai/Bot;Z)Ljava/util/List;
public abstract fun getOnlineOtherClientsList (Lnet/mamoe/mirai/Bot;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getOnlineOtherClientsList$default (Lnet/mamoe/mirai/IMirai;Lnet/mamoe/mirai/Bot;ZILjava/lang/Object;)Ljava/util/List;
@ -125,7 +124,6 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
public fun sendNudge (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/action/Nudge;Lnet/mamoe/mirai/contact/Contact;)Z
public abstract fun sendNudge (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/action/Nudge;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun setFileCacheStrategy (Lnet/mamoe/mirai/utils/FileCacheStrategy;)V
public abstract fun setHttp (Lio/ktor/client/HttpClient;)V
}
public abstract interface class net/mamoe/mirai/LowLevelApiAccessor {

View File

@ -92,15 +92,15 @@ public interface IMirai : LowLevelApiAccessor {
*/
public var FileCacheStrategy: FileCacheStrategy
/**
* Mirai 上传好友图片等使用的 Ktor [HttpClient].
* 默认使用 [OkHttp] 引擎, 连接超时为 30s.
*
* 覆盖后将会立即应用到全局.
*/
@Deprecated("Mirai is not going to use ktor. This is deprecated for removal.", level = DeprecationLevel.WARNING)
@DeprecatedSinceMirai(warningSince = "2.11.0")
public var Http: HttpClient
// /**
// * Mirai 上传好友图片等使用的 Ktor [HttpClient].
// * 默认使用 [OkHttp] 引擎, 连接超时为 30s.
// *
// * 覆盖后将会立即应用到全局.
// */
// @Deprecated("Mirai is not going to use ktor. This is deprecated for removal.", level = DeprecationLevel.WARNING)
// @DeprecatedSinceMirai(warningSince = "2.11.0")
// public var Http: HttpClient
/**
* 获取 uin.

View File

@ -11,27 +11,15 @@ package net.mamoe.mirai.utils
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.*
import net.mamoe.mirai.Mirai
internal abstract class TxCaptchaHelper {
private val newClient: Boolean
val client: HttpClient
private val newClient: Boolean = true
val client: HttpClient = HttpClient()
private lateinit var queue: Job
init {
var newClient = false
client = try {
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
Mirai.Http
} catch (ignore: Throwable) {
newClient = true
HttpClient()
}
this.newClient = newClient
}
internal var latestDisplay = "Sending request..."
private var latestDisplay = "Sending request..."
abstract fun onComplete(ticket: String)
abstract fun updateDisplay(msg: String)
@ -42,7 +30,7 @@ internal abstract class TxCaptchaHelper {
updateDisplay(latestDisplay)
while (isActive) {
try {
val response: String = client.get(url0)
val response: String = client.get(url0).bodyAsText()
if (response.startsWith("请在")) {
if (response != latestDisplay) {
latestDisplay = response

View File

@ -220,7 +220,7 @@ internal open class CommonByteArrayOpTest {
val result =
"1F 8B 08 00 00 00 00 00 00 FF 2B 74 CF F3 32 0C 2A 72 73 B6 04 00 A8 35 6D D9 0A 00 00 00".hexToBytes()
.toReadPacket(release = { released = true }).let { input ->
GzipDecompressionInput(input).readText().also {
GzipDecompressionInput(input).readAllText().also {
assertEquals(true, input.endOfInput)
assertEquals(true, released)
}
@ -278,7 +278,7 @@ internal open class CommonByteArrayOpTest {
val result =
"78 9C 2B 74 CF F3 32 0C 2A 72 73 B6 04 00 12 82 03 28".hexToBytes()
.toReadPacket(release = { released = true }).let { input ->
InflateInput(input).readText().also {
InflateInput(input).readAllText().also {
assertEquals(true, input.endOfInput)
assertEquals(true, released)
}

View File

@ -153,7 +153,7 @@ internal class ZlibInput(
private val zlibHasPending: ((z_streamp) -> Boolean)?, // null lambda means operation not defined
private val zlibFlushMode: (shouldFlushAll: Boolean) -> Int,
private val zlibEnd: (z_streamp) -> Int,
) : Input {
) : Input() {
private val z: z_stream = nativeHeap.alloc()
// Zlib manual: https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-inflate-1.html
@ -165,123 +165,73 @@ internal class ZlibInput(
}
}
@Deprecated(
"Not supported anymore. All operations are big endian by default. Use readXXXLittleEndian or readXXX then X.reverseByteOrder() instead.",
level = DeprecationLevel.ERROR
)
override var byteOrder: ByteOrder
get() = throw UnsupportedOperationException()
set(_) {
throw UnsupportedOperationException()
}
private var bufferReadableSize = 0
private var bufferReadableSize = 0L
private val inputBuffer = nativeHeap.allocArray<ByteVar>(ZLIB_BUFFER_SIZE)
private val buffer = nativeHeap.allocArray<ByteVar>(ZLIB_BUFFER_SIZE)
private var bufferIndex = 0L
private var closed: Boolean = false
override val endOfInput: Boolean
get() = closed || !prepare()
private var closed = false
override fun close() {
debug { "closing" }
if (closed) return
this.closed = true
source.close()
zlibEnd(z.ptr)
nativeHeap.free(z)
nativeHeap.free(buffer)
closed = true
debug { "close" }
super.close()
debug { "freeing inputBuffer" }
nativeHeap.free(inputBuffer)
debug { "freed" }
}
override fun discard(n: Long): Long {
if (closed) {
return 0
}
val old = bufferIndex
if (old >= bufferReadableSize) {
if (prepare()) return discard(n)
return 0
}
bufferIndex = (bufferIndex + n).coerceAtMost(ZLIB_BUFFER_SIZE)
return bufferIndex - old
override fun closeSource() {
debug { "closeSource" }
source.close()
debug { "zlibEnd" }
zlibEnd(z.ptr)
debug { "zlibEnd done" }
}
override fun peekTo(destination: Memory, destinationOffset: Long, offset: Long, min: Long, max: Long): Long {
debug()
debug { "peekTo" }
require(min <= max) { "min > max" }
if (!prepare()) return 0
val readableLength = (bufferReadableSize - bufferIndex - offset).coerceAtLeast(0)
if (offset > readableLength) {
if (min == 0L) {
throw EOFException("offset($offset) > readableLength($readableLength)")
}
}
if (min > readableLength) return 0
val len = readableLength.coerceAtMost(max).coerceAtMost(destination.size)
debug { "peekTo: read $len" }
buffer.copyTo(destination, bufferIndex + offset, len, destinationOffset)
override fun fill(destination: Memory, offset: Int, length: Int): Int {
require(offset in 0..destination.size32) { "invalid offset: $offset" }
require(length in 0..destination.size32) { "invalid length: $length" }
require(offset + length in 0..destination.size32) { "invalid offset and length: $offset, $length" }
return len
}
override fun readByte(): Byte {
if (!prepare()) {
throw EOFException("One more byte required")
}
return buffer[bufferIndex++]
}
override fun tryPeek(): Int {
if (!prepare()) {
return -1
}
return buffer[bufferIndex].toIntUnsigned()
}
private fun prepare(): Boolean {
if (closed) {
return false
}
debug { "prepare: bufferIndex = $bufferIndex, bufferReadableSize = $bufferReadableSize" }
if (bufferIndex < bufferReadableSize) {
debug { "prepare returned, because " }
return true // has buf unused
}
bufferIndex = 0
debug { "prepare: bufferReadableSize = $bufferReadableSize" }
debug { "prepare: previous value: z.avail_in=${z.avail_in}, z.avail_out=${z.avail_out}" }
if (z.avail_in == 0u) {
val filled = try {
if (z.avail_in == 0u) {
// These two cases are similar.
// These two cases are similar.
// if (z.avail_out == 0u) {
// // Last time we used all the output, there is either something cached in Zlib, or no further source.
// } else {
// // We did not use all the inputs, meaning least time we used all avail_in.
// }
// bot input and output are used
val flush = updateAvailIn() ?: return false
copyOutputsFromZlib(flush)
} else {
// Inputs not used up.
copyOutputsFromZlib(Z_NO_FLUSH)
}
// bot input and output are used
val flush = updateAvailIn() ?: return 0
copyOutputsFromZlib(destination, offset, length, flush)
} else {
// Inputs not used up.
copyOutputsFromZlib(destination, offset, length, Z_NO_FLUSH)
}
return true
} catch (e: Throwable) {
// If you throw this error up, ktor will somehow kill the process. (Ktor 2.0.2)
debug { e.printStackTrace(); "" }
return 0
}
check(filled in 0..length) { "Filled more than $length bytes: $filled" }
check(filled in 0..destination.size) { "Filled more than ${destination.size} bytes: $filled" }
return filled
}
private fun copyOutputsFromZlib(flush: Int): Boolean {
z.avail_out = ZLIB_BUFFER_SIZE.toUInt()
z.next_out = buffer.reinterpret()
private fun copyOutputsFromZlib(memory: Memory, offset: Int, length: Int, flush: Int): Int {
debug { "copyOutputsFromZlib, memory.offset = $offset, memory.length=$length, memory.size=${memory.size}" }
z.avail_out = length.convert()
z.next_out = (memory.pointer + offset)!!.reinterpret()
// We still have input, no need to update.
debug { "Set z.avail_out=${z.avail_out}, z.next_out=buffer.reinterpret()" }
debug { "Set z.avail_out=${z.avail_out}, z.next_out=(memory.pointer + offset)!!.reinterpret()" }
debug { "Calling zlib, flush = $flush" }
val p = zlibProcess(z.ptr, flush)
@ -293,23 +243,23 @@ internal class ZlibInput(
Z_NEED_DICT -> error("Zlib failed to process data. (Z_NEED_DICT)")
else -> debug { "zlib: $p" }
}
bufferReadableSize = (ZLIB_BUFFER_SIZE.toUInt() - z.avail_out).toInt()
val readSize = (length.toUInt() - z.avail_out).toInt()
debug { "Zlib produced bufferReadableSize=$bufferReadableSize bytes" }
debug { "Partial output: ${buffer.readBytes(bufferReadableSize).toUHexString()}" }
debug { "Zlib produced readSize=$readSize bytes" }
// debug { "Partial output: ${memory.readBytes(bufferReadableSize).toUHexString()}" }
debug { "Now z.avail_in=${z.avail_in}, z.avail_out=${z.avail_out}" }
if (p == Z_FINISH) {
debug { "Zlib returned Z_FINISH. Ignoring result check." }
return true
return readSize
}
if (p == Z_STREAM_END) {
debug { "Zlib returned Z_STREAM_END. Ignoring result check." }
return true
return readSize
}
if (bufferReadableSize == 0 && (z.avail_in == 0u && source.endOfInput)) {
if (bufferReadableSize == 0L && (z.avail_in == 0u && source.endOfInput)) {
if (zlibHasPending?.invoke(z.ptr) == true) {
// has pending. So the data must be incomplete.
error("Failed to process data, possibly bad data inputted.")
@ -324,7 +274,7 @@ internal class ZlibInput(
}
// can't read
}
return true
return readSize
}
private fun updateAvailIn(): Int? {
@ -334,6 +284,7 @@ internal class ZlibInput(
close() // automatically close
return null // no more source available
}
bufferReadableSize = read
z.avail_in = read.toUInt()
val flush = zlibFlushMode(read < ZLIB_BUFFER_SIZE || source.endOfInput)
debug { "inputBuffer content: " + inputBuffer.readBytes(read.toInt()).toUHexString() }

View File

@ -14,7 +14,6 @@ package net.mamoe.mirai.utils
import io.ktor.utils.io.bits.*
import io.ktor.utils.io.core.*
import io.ktor.utils.io.errors.*
import io.ktor.utils.io.streams.*
import kotlinx.cinterop.*
import platform.posix.*
@ -162,8 +161,7 @@ internal class FileNotFoundException(message: String, cause: Throwable? = null)
@Suppress("DEPRECATION")
@OptIn(ExperimentalIoApi::class)
internal class PosixFileInstanceOutput(val file: CPointer<FILE>) : AbstractOutput() {
internal class PosixFileInstanceOutput(val file: CPointer<FILE>) : Output() {
private var closed = false
override fun flush(source: Memory, offset: Int, length: Int) {
@ -171,7 +169,12 @@ internal class PosixFileInstanceOutput(val file: CPointer<FILE>) : AbstractOutpu
var currentOffset = offset
while (currentOffset < end) {
val result = fwrite(source, currentOffset, end - currentOffset, file.cast())
val result = fwrite(
source.pointer + currentOffset.convert(),
sizeOf<ByteVar>().convert(),
(end - currentOffset).convert(),
file.cast()
).convert<Int>()
if (result == 0) {
throw PosixException.forErrno(posixFunctionName = "fwrite()").wrapIO()
}
@ -190,12 +193,16 @@ internal class PosixFileInstanceOutput(val file: CPointer<FILE>) : AbstractOutpu
}
@Suppress("DEPRECATION")
@OptIn(ExperimentalIoApi::class)
internal class PosixInputForFile(val file: CPointer<FILE>) : AbstractInput() {
internal class PosixInputForFile(val file: CPointer<FILE>) : Input() {
private var closed = false
override fun fill(destination: Memory, offset: Int, length: Int): Int {
val size = fread(destination, offset, length, file.cast())
val size = fread(
destination.pointer + offset.convert(),
sizeOf<ByteVar>().convert(),
length.convert(),
file.cast()
).toInt()
if (size == 0) {
if (feof(file) != 0) return 0
throw PosixException.forErrno(posixFunctionName = "read()").wrapIO()
@ -214,6 +221,5 @@ internal class PosixInputForFile(val file: CPointer<FILE>) : AbstractInput() {
}
}
@OptIn(ExperimentalIoApi::class)
public fun PosixException.wrapIO(): IOException =
IOException("I/O operation failed due to posix error code $errno", this)

View File

@ -14,7 +14,6 @@ import io.ktor.utils.io.errors.*
import kotlinx.cinterop.*
import platform.posix.*
@OptIn(ExperimentalIoApi::class)
private fun readlink(path: String): String = memScoped {
val len = realpath(path, null)
if (len != null) {
@ -114,7 +113,6 @@ internal actual class MiraiFileImpl actual constructor(
return resolve(parent).resolve(file.name)
}
@OptIn(UnsafeNumber::class)
override fun createNewFile(): Boolean {
memScoped {
val fp = fopen(absolutePath, "w")
@ -155,7 +153,6 @@ internal actual class MiraiFileImpl actual constructor(
}
}
@OptIn(ExperimentalIoApi::class)
override fun input(): Input {
val handle = fopen(absolutePath, "rb")
?: throw IOException(
@ -165,7 +162,6 @@ internal actual class MiraiFileImpl actual constructor(
return PosixInputForFile(handle)
}
@OptIn(ExperimentalIoApi::class)
override fun output(): Output {
val handle = fopen(absolutePath, "wb")
?: throw IOException(

View File

@ -153,13 +153,13 @@ kotlin {
configure(LINUX_TARGETS.map { getByName(it + "Main") }) {
dependencies {
implementation(`ktor-client-curl`)
implementation(`ktor-client-cio`)
}
}
val darwinMain by getting {
dependencies {
implementation(`ktor-client-ios`)
implementation(`ktor-client-darwin`)
}
}

View File

@ -14,6 +14,7 @@ package net.mamoe.mirai.internal
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.utils.io.core.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
@ -93,8 +94,8 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
override var FileCacheStrategy: FileCacheStrategy = net.mamoe.mirai.utils.FileCacheStrategy.PlatformDefault
@Deprecated("Mirai is not going to use ktor. This is deprecated for removal.", level = DeprecationLevel.WARNING)
override var Http: HttpClient = createDefaultHttpClient()
@Suppress("PrivatePropertyName")
private val httpClient: HttpClient = createDefaultHttpClient()
override suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent) {
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@ -530,8 +531,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
override suspend fun getRawGroupActiveData(bot: Bot, groupId: Long, page: Int): GroupActiveData =
bot.asQQAndroidBot().run {
val rep = network.run {
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
Mirai.Http.get<String> {
httpClient.get() {
url("https://qqweb.qq.com/c/activedata/get_mygroup_data")
parameter("bkn", client.wLoginSigInfo.bkn)
parameter("gc", groupId)
@ -547,7 +547,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
}
}
}
return json.decodeFromString(GroupActiveData.serializer(), rep)
return json.decodeFromString(GroupActiveData.serializer(), rep.bodyAsText())
}
@LowLevelApi
@ -558,8 +558,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
type: GroupHonorType
): GroupHonorListData? = bot.asQQAndroidBot().run {
val rep = network.run {
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
Mirai.Http.get<String> {
httpClient.get {
url("https://qun.qq.com/interactive/honorlist")
parameter("gc", groupId)
parameter("type", type.value)
@ -575,7 +574,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
}
}
}
val jsonText = Regex("""window.__INITIAL_STATE__=(.+?)</script>""").find(rep)?.groupValues?.get(1)
val jsonText = Regex("""window.__INITIAL_STATE__=(.+?)</script>""").find(rep.bodyAsText())?.groupValues?.get(1)
return jsonText?.let { json.decodeFromString(GroupHonorListData.serializer(), it) }
}
@ -670,16 +669,17 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
seconds: Int
) {
bot as QQAndroidBot
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
val response = Mirai.Http.post<String> {
val response = httpClient.post {
url("https://qqweb.qq.com/c/anonymoustalk/blacklist")
body = MultiPartFormDataContent(formData {
append("anony_id", anonymousId)
append("group_code", groupId)
append("seconds", seconds)
append("anony_nick", anonymousNick)
append("bkn", bot.client.wLoginSigInfo.bkn)
})
setBody(
MultiPartFormDataContent(formData {
append("anony_id", anonymousId)
append("group_code", groupId)
append("seconds", seconds)
append("anony_nick", anonymousNick)
append("bkn", bot.client.wLoginSigInfo.bkn)
})
)
headers {
// ktor bug
append(
@ -687,7 +687,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
"uin=o${bot.id}; skey=${bot.sKey};"
)
}
}
}.bodyAsText()
val jsonObj = Json.decodeFromString(JsonObject.serializer(), response)
if ((jsonObj["retcode"] ?: jsonObj["cgicode"] ?: error("missing response code")).jsonPrimitive.long != 0L) {
throw IllegalStateException(response)
@ -828,8 +828,6 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
bot.asQQAndroidBot()
when (val resp = bot.network.sendAndExpect(MultiMsg.ApplyDown(bot.client, 2, resourceId, 1))) {
is MultiMsg.ApplyDown.Response.RequireDownload -> {
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
val http = Mirai.Http
val origin = resp.origin
val data: ByteArray = if (origin.msgExternInfo?.channelType == 2) {
@ -841,16 +839,16 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
resourceKind = resourceKind,
channelKind = ChannelKind.HTTP
) { host, _ ->
http.get("$host${origin.thumbDownPara}")
}
httpClient.get("$host${origin.thumbDownPara}")
}.readBytes()
} else tryServersDownload(
bot = bot,
servers = origin.uint32DownIp.zip(origin.uint32DownPort),
resourceKind = resourceKind,
channelKind = ChannelKind.HTTP
) { ip, port ->
http.get("http://$ip:$port${origin.thumbDownPara}")
}
httpClient.get("http://$ip:$port${origin.thumbDownPara}")
}.readBytes()
val body = data.read {
check(readByte() == 40.toByte()) {

View File

@ -203,6 +203,7 @@ internal open class QQAndroidBot constructor(
set(SsoProcessor, SsoProcessorImpl(get(SsoProcessorContext)))
set(HeartbeatProcessor, HeartbeatProcessorImpl())
set(HeartbeatScheduler, TimeBasedHeartbeatSchedulerImpl(networkLogger.subLogger("HeartbeatScheduler")))
set(HttpClientProvider, HttpClientProviderImpl())
set(KeyRefreshProcessor, KeyRefreshProcessorImpl(networkLogger.subLogger("KeyRefreshProcessor")))
set(ConfigPushProcessor, ConfigPushProcessorImpl(networkLogger.subLogger("ConfigPushProcessor")))
set(BotOfflineEventMonitor, BotOfflineEventMonitorImpl())

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.internal.contact
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.Stranger
@ -27,6 +26,7 @@ import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrateg
import net.mamoe.mirai.internal.network.component.buildComponentStorage
import net.mamoe.mirai.internal.network.components.BdhSession
import net.mamoe.mirai.internal.network.components.ClockHolder
import net.mamoe.mirai.internal.network.components.HttpClientProvider
import net.mamoe.mirai.internal.network.highway.ChannelKind
import net.mamoe.mirai.internal.network.highway.Highway
import net.mamoe.mirai.internal.network.highway.ResourceKind.PRIVATE_IMAGE
@ -202,7 +202,7 @@ internal sealed class AbstractUser(
resourceKind = PRIVATE_IMAGE,
channelKind = ChannelKind.HTTP
) { ip, port ->
@Suppress("DEPRECATION", "DEPRECATION_ERROR") Mirai.Http.postImage(
bot.components[HttpClientProvider].getHttpClient().postImage(
serverIp = ip,
serverPort = port,
htcmd = "0x6ff0070",
@ -214,7 +214,7 @@ internal sealed class AbstractUser(
}
}.recoverCatchingSuppressed {
// try upload by http on fallback server
@Suppress("DEPRECATION", "DEPRECATION_ERROR") Mirai.Http.postImage(
bot.components[HttpClientProvider].getHttpClient().postImage(
serverIp = "htdata2.qq.com",
htcmd = "0x6ff0070",
uin = bot.id,

View File

@ -16,7 +16,6 @@ package net.mamoe.mirai.internal.contact
import io.ktor.utils.io.core.*
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
@ -27,6 +26,7 @@ import net.mamoe.mirai.internal.contact.roaming.RoamingMessagesImplFriend
import net.mamoe.mirai.internal.message.data.OfflineAudioImpl
import net.mamoe.mirai.internal.message.protocol.outgoing.FriendMessageProtocolStrategy
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.network.components.HttpClientProvider
import net.mamoe.mirai.internal.network.highway.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x346
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
@ -93,6 +93,7 @@ internal class FriendImpl(
override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = AudioToSilkService.convert(
resource
).useAutoClose { res ->
var audio: OfflineAudioImpl? = null
kotlin.runCatching {
val resp = Highway.uploadResourceBdh(
@ -133,8 +134,8 @@ internal class FriendImpl(
ResourceKind.GROUP_AUDIO,
ChannelKind.HTTP
) { ip, port ->
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
Mirai.Http.postPtt(ip, port, res, resp.uKey, resp.fileKey)
bot.components[HttpClientProvider].getHttpClient()
.postPtt(ip, port, res, resp.uKey, resp.fileKey)
}
audio = OfflineAudioImpl(
filename = "${res.md5.toUHexString("")}.amr",

View File

@ -15,7 +15,6 @@ package net.mamoe.mirai.internal.contact
import kotlinx.atomicfu.atomic
import net.mamoe.mirai.Bot
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.announcement.Announcements
import net.mamoe.mirai.contact.file.RemoteFiles
@ -36,6 +35,7 @@ import net.mamoe.mirai.internal.message.image.getImageTypeById
import net.mamoe.mirai.internal.message.protocol.outgoing.GroupMessageProtocolStrategy
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.network.components.BdhSession
import net.mamoe.mirai.internal.network.components.HttpClientProvider
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.highway.ChannelKind
import net.mamoe.mirai.internal.network.highway.Highway
@ -322,8 +322,8 @@ internal abstract class CommonGroupImpl constructor(
GROUP_AUDIO,
ChannelKind.HTTP
) { ip, port ->
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
Mirai.Http.postPtt(ip, port, resource, resp.uKey, resp.fileKey)
bot.components[HttpClientProvider].getHttpClient()
.postPtt(ip, port, resource, resp.uKey, resp.fileKey)
}
}
}

View File

@ -13,19 +13,17 @@ package net.mamoe.mirai.internal.contact.announcement
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.util.*
import kotlinx.coroutines.flow.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.announcement.*
import net.mamoe.mirai.contact.checkBotPermission
import net.mamoe.mirai.internal.AbstractBot
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.asQQAndroidBot
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.OnlineAnnouncementImpl
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.deleteGroupAnnouncement
@ -34,6 +32,8 @@ import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getRaw
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.sendGroupAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toGroupAnnouncement
import net.mamoe.mirai.internal.network.client
import net.mamoe.mirai.internal.network.components.HttpClientProvider
import net.mamoe.mirai.internal.network.highway.ChannelKind
import net.mamoe.mirai.internal.network.highway.ResourceKind
import net.mamoe.mirai.internal.network.highway.tryServersUpload
@ -154,31 +154,32 @@ internal object AnnouncementProtocol {
) : CheckableResponseA(), JsonStruct
suspend fun uploadGroupAnnouncementImage(
bot: Bot,
bot: AbstractBot,
resource: ExternalResource
): AnnouncementImage = bot.asQQAndroidBot().run {
@OptIn(InternalAPI::class) // ktor bug
val resp = Mirai.Http.post<String> {
): AnnouncementImage = bot.run {
val resp = bot.components[HttpClientProvider].getHttpClient().post {
url("https://web.qun.qq.com/cgi-bin/announce/upload_img")
body = MultiPartFormDataContent(formData {
append("\"bkn\"", client.wLoginSigInfo.bkn)
append("\"source\"", "troopNotice")
append("m", "0")
append(
"\"pic_up\"",
headers = Headers.build {
append(HttpHeaders.ContentType, ContentType.Image.PNG)
append(HttpHeaders.ContentDisposition, "filename=\"temp_uploadFile.png\"")
setBody(
MultiPartFormDataContent(formData {
append("\"bkn\"", client.wLoginSigInfo.bkn)
append("\"source\"", "troopNotice")
append("m", "0")
append(
"\"pic_up\"",
headers = Headers.build {
append(HttpHeaders.ContentType, ContentType.Image.PNG)
append(HttpHeaders.ContentDisposition, "filename=\"temp_uploadFile.png\"")
}
) {
writeResource(resource)
}
) {
writeResource(resource)
}
})
})
)
cookie("uin", "o$id")
cookie("p_uin", "o$id")
cookie("skey", sKey)
cookie("p_skey", psKey("qun.qq.com"))
}.loadSafelyAs(UploadImageResp.serializer()).check()
}.bodyAsText().loadSafelyAs(UploadImageResp.serializer()).check()
return resp.id.replace("&quot;", "\"").loadSafelyAs(GroupAnnouncementImage.serializer()).check().toPublic()
}
@ -194,7 +195,7 @@ internal object AnnouncementProtocol {
announcement: GroupAnnouncement,
image: AnnouncementImage?,
): String {
return Mirai.Http.post<String> {
return bot.components[HttpClientProvider].getHttpClient().post {
url(
"https://web.qun.qq.com/cgi-bin/announce/add_qun_" + if (announcement.type == 20) {
"instruction"
@ -202,28 +203,30 @@ internal object AnnouncementProtocol {
"notice"
}
)
body = MultiPartFormDataContent(formData {
append("qid", groupId)
append("bkn", client.wLoginSigInfo.bkn)
append("text", announcement.msg.text)
append("pinned", announcement.pinned)
image?.let {
append("pic", image.id)
append("imgWidth", image.width)
append("imgHeight", image.height)
}
append(
"settings",
announcement.settings.toJsonString(GroupAnnouncementSettings.serializer()),
)
append("format", "json")
// append("type", announcement.type.toString())
})
setBody(
MultiPartFormDataContent(formData {
append("qid", groupId)
append("bkn", client.wLoginSigInfo.bkn)
append("text", announcement.msg.text)
append("pinned", announcement.pinned)
image?.let {
append("pic", image.id)
append("imgWidth", image.width)
append("imgHeight", image.height)
}
append(
"settings",
announcement.settings.toJsonString(GroupAnnouncementSettings.serializer()),
)
append("format", "json")
// append("type", announcement.type.toString())
})
)
cookie("uin", "o$id")
cookie("p_uin", "o$id")
cookie("skey", sKey)
cookie("p_skey", psKey("qun.qq.com"))
}.loadSafelyAs(SendGroupAnnouncementResp.serializer()).check().fid
}.bodyAsText().loadSafelyAs(SendGroupAnnouncementResp.serializer()).check().fid
}
suspend fun QQAndroidBot.getRawGroupAnnouncements(
@ -231,20 +234,22 @@ internal object AnnouncementProtocol {
page: Int,
amount: Int = 10
): Either<DeserializationFailure, GroupAnnouncementList> {
return Mirai.Http.post<String> {
return bot.components[HttpClientProvider].getHttpClient().post {
url("https://web.qun.qq.com/cgi-bin/announce/list_announce")
body = MultiPartFormDataContent(formData {
append("qid", groupId)
append("bkn", client.wLoginSigInfo.bkn)
append("ft", 23) //好像是一个用来识别应用的参数
append("s", if (page == 1) 0 else -(page * amount + 1)) // 第一页这里的参数应该是-1
append("n", amount)
append("ni", if (page == 1) 1 else 0)
append("format", "json")
})
setBody(
MultiPartFormDataContent(formData {
append("qid", groupId)
append("bkn", client.wLoginSigInfo.bkn)
append("ft", 23) //好像是一个用来识别应用的参数
append("s", if (page == 1) 0 else -(page * amount + 1)) // 第一页这里的参数应该是-1
append("n", amount)
append("ni", if (page == 1) 1 else 0)
append("format", "json")
})
)
cookie("uin", "o$id")
cookie("skey", sKey)
}.loadSafelyAs(GroupAnnouncementList.serializer())
}.bodyAsText().loadSafelyAs(GroupAnnouncementList.serializer())
}
@Serializable
@ -254,25 +259,25 @@ internal object AnnouncementProtocol {
) : CheckableResponseA(), JsonStruct
suspend fun QQAndroidBot.deleteGroupAnnouncement(groupId: Long, fid: String): Boolean {
Mirai.Http.post<String> {
components[HttpClientProvider].getHttpClient().post {
url("https://web.qun.qq.com/cgi-bin/announce/del_feed")
body = feedBody(groupId, fid)
setBody(feedBody(groupId, fid))
cookie("uin", "o$id")
cookie("p_uin", "o$id")
cookie("skey", sKey)
cookie("p_skey", psKey("qun.qq.com"))
}.loadSafelyAs(DeleteResp.serializer()).check()
}.bodyAsText().loadSafelyAs(DeleteResp.serializer()).check()
return true
}
suspend fun QQAndroidBot.getGroupAnnouncement(groupId: Long, fid: String): GroupAnnouncement {
return Mirai.Http.post<String> {
return bot.components[HttpClientProvider].getHttpClient().post {
url("https://web.qun.qq.com/cgi-bin/announce/get_feed")
body = feedBody(groupId, fid)
setBody(feedBody(groupId, fid))
cookie("uin", "o$id")
cookie("p_uin", "o$id")
cookie("skey", sKey)
}.loadAs(GroupAnnouncement.serializer())
}.bodyAsText().loadAs(GroupAnnouncement.serializer())
}
private fun QQAndroidBot.feedBody(

View File

@ -140,7 +140,7 @@ internal class RichMessageProtocol : MessageProtocol() {
{ "resId=" + lightApp.msgResid + "data=" + lightApp.data.toUHexString() }) {
when (lightApp.data[0].toInt()) {
0 -> lightApp.data.decodeToString(startIndex = 1)
1 -> lightApp.data.toReadPacket(offset = 1).inflateInput().readText()
1 -> lightApp.data.toReadPacket(offset = 1).inflateInput().readAllText()
else -> error("unknown compression flag=${lightApp.data[0]}")
}
}
@ -159,7 +159,7 @@ internal class RichMessageProtocol : MessageProtocol() {
val content = runWithBugReport("解析 richMsg", { richMsg.template1.toUHexString() }) {
when (richMsg.template1[0].toInt()) {
0 -> richMsg.template1.decodeToString(startIndex = 1)
1 -> richMsg.template1.toReadPacket(offset = 1).inflateInput().readText()
1 -> richMsg.template1.toReadPacket(offset = 1).inflateInput().readAllText()
else -> error("unknown compression flag=${richMsg.template1[0]}")
}
}

View File

@ -10,11 +10,11 @@
package net.mamoe.mirai.internal.network.components
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.utils.crypto.ECDH
@ -89,8 +89,11 @@ internal class EcdhInitialPublicKeyUpdaterImpl(
} else {
logger.info("ECDH key is invalid, start to fetch ecdh public key from server.")
val respStr =
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
withTimeout(10.seconds) { Mirai.Http.get<String>("https://keyrotate.qq.com/rotate_key?cipher_suite_ver=305&uin=${bot.client.uin}") }
withTimeout(10.seconds) {
bot.components[HttpClientProvider].getHttpClient()
.get("https://keyrotate.qq.com/rotate_key?cipher_suite_ver=305&uin=${bot.client.uin}")
.bodyAsText()
}
val resp = json.decodeFromString(ServerRespPOJO.serializer(), respStr)
resp.pubKeyMeta.let { meta ->
val isValid = ECDH.verifyPublicKey(

View File

@ -0,0 +1,25 @@
/*
* 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.client.*
import net.mamoe.mirai.internal.createDefaultHttpClient
import net.mamoe.mirai.internal.network.component.ComponentKey
internal interface HttpClientProvider {
fun getHttpClient(): HttpClient
companion object : ComponentKey<HttpClientProvider>
}
internal class HttpClientProviderImpl : HttpClientProvider {
private val instance by lazy { createDefaultHttpClient() }
override fun getHttpClient(): HttpClient = instance
}

View File

@ -49,7 +49,7 @@ internal suspend fun HttpClient.postImage(
groupcode: Long?,
imageInput: ExternalResource,
uKeyHex: String,
): Boolean = post<HttpStatusCode> {
): Boolean = post {
url {
protocol = URLProtocol.HTTP
host = serverIp // "htdata2.qq.com"
@ -71,7 +71,7 @@ internal suspend fun HttpClient.postImage(
}
body = imageInput.consumeAsWriteChannelContent(ContentType.Image.Any)
} == HttpStatusCode.OK
}.status == HttpStatusCode.OK
internal suspend fun HttpClient.postPtt(
serverIp: String,
@ -80,7 +80,7 @@ internal suspend fun HttpClient.postPtt(
uKey: ByteArray,
fileKey: ByteArray,
) {
post<String> {
post {
url("http://$serverIp:$serverPort")
parameter("ver", 4679)
parameter("ukey", uKey.toUHexString(""))
@ -89,6 +89,6 @@ internal suspend fun HttpClient.postPtt(
parameter("bmd5", resource.md5.toUHexString(""))
parameter("mType", "pttDu")
parameter("voice_encodec", resource.voiceCodec)
body = resource.consumeAsWriteChannelContent(null)
setBody(resource.consumeAsWriteChannelContent(null))
}
}

View File

@ -97,7 +97,6 @@ internal class TarsOld internal constructor(
* From: com.qq.taf.Tars.TarsOutputStream
*/
@Suppress("unused", "MemberVisibilityCanBePrivate")
@OptIn(ExperimentalIoApi::class)
private open inner class TarsEncoder(
val output: BytePacketBuilder,
) : TaggedEncoder<Int>() {

View File

@ -95,7 +95,7 @@ private fun <T : JceStruct> ByteArray.doLoadAs(
} catch (secondFailure: Exception) {
throw contextualBugReportException(
"解析 " + deserializer.descriptor.serialName,
build.readText(),
build.readAllText(),
ExceptionCollector.compressExceptions(originalException, secondFailure)
)
}

View File

@ -9,6 +9,7 @@
package net.mamoe.mirai.internal.message
import io.ktor.utils.io.core.EOFException
import io.ktor.utils.io.errors.*
import net.mamoe.mirai.internal.message.image.calculateImageInfo
import net.mamoe.mirai.internal.test.AbstractTest
@ -78,7 +79,7 @@ internal class ImageReadingTest : AbstractTest() {
ImageType.JPG
)
}
assertFailsWith(IllegalStateException::class) {
assertFailsWith(EOFException::class) {
"FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF E1 00 5A".testMatch(
ImageType.JPG
)

View File

@ -76,6 +76,7 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT
set(ImagePatcher, TestImagePatcher())
set(PacketLoggingStrategy, PacketLoggingStrategyImpl(bot))
set(AccountSecretsManager, MemoryAccountSecretsManager())
set(HttpClientProvider, HttpClientProviderImpl())
}
fun NetworkHandler.assertState(state: NetworkHandler.State) {

View File

@ -10,11 +10,11 @@
package net.mamoe.mirai.internal
import io.ktor.client.*
import io.ktor.client.engine.ios.*
import io.ktor.client.features.*
import io.ktor.client.engine.darwin.*
import io.ktor.client.plugins.*
internal actual fun createDefaultHttpClient(): HttpClient {
return HttpClient(Ios) {
return HttpClient(Darwin) {
install(HttpTimeout) {
this.requestTimeoutMillis = 30_0000
this.connectTimeoutMillis = 30_0000

View File

@ -13,7 +13,7 @@ package net.mamoe.mirai.internal
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.features.*
import io.ktor.client.plugins.*
import kotlinx.atomicfu.atomic
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade

View File

@ -10,11 +10,11 @@
package net.mamoe.mirai.internal
import io.ktor.client.*
import io.ktor.client.engine.curl.*
import io.ktor.client.features.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.*
internal actual fun createDefaultHttpClient(): HttpClient {
return HttpClient(Curl) {
return HttpClient(CIO) {
install(HttpTimeout) {
this.requestTimeoutMillis = 30_0000
this.connectTimeoutMillis = 30_0000

View File

@ -11,7 +11,7 @@ package net.mamoe.mirai.internal
import io.ktor.client.*
import io.ktor.client.engine.curl.*
import io.ktor.client.features.*
import io.ktor.client.plugins.*
internal actual fun createDefaultHttpClient(): HttpClient {
return HttpClient(Curl) {

View File

@ -45,14 +45,12 @@ internal actual class PlatformSocket(
actual val isOpen: Boolean
get() = write(socket, null, 0) != 0
@OptIn(ExperimentalIoApi::class)
actual override fun close() {
if (close(socket) != 0) {
throw PosixException.forErrno(posixFunctionName = "close()").wrapIO()
}
}
@OptIn(ExperimentalIoApi::class)
actual suspend fun send(packet: ByteArray, offset: Int, length: Int): Unit = readLock.withLock {
withContext(dispatcher) {
require(offset >= 0) { "offset must >= 0" }
@ -66,10 +64,7 @@ internal actual class PlatformSocket(
}
}
/**
* @throws SendPacketInternalException
*/
@OptIn(ExperimentalIoApi::class)
actual override suspend fun send(packet: ByteReadPacket): Unit = readLock.withLock {
withContext(dispatcher) {
val writeBuffer = writeBuffer
@ -93,7 +88,6 @@ internal actual class PlatformSocket(
actual companion object {
@OptIn(UnsafeNumber::class, ExperimentalIoApi::class)
actual suspend fun connect(
serverIp: String,
serverPort: Int

View File

@ -79,7 +79,7 @@ internal class LengthDelimitedPacketReader(
else -> {
if (missingLength == 0L) {
debugLogger.info { "Multiple packets length perfectly matched." }
sendDecode(buildPacket(bufferedParts.sumOf { it.remaining }.toInt()) {
sendDecode(buildPacket {
bufferedParts.forEach { writePacket(it) }
})
@ -95,7 +95,7 @@ internal class LengthDelimitedPacketReader(
if (combinedLength < 0) return // not enough, still more parts missing.
sendDecode(buildPacket(combinedLength) {
sendDecode(buildPacket {
repeat(bufferedParts.size - 1) { i ->
writePacket(bufferedParts[i])
}

View File

@ -55,7 +55,6 @@ internal actual class PlatformSocket(
writeBuffer.unpin()
}
@OptIn(ExperimentalIoApi::class)
actual suspend fun send(packet: ByteArray, offset: Int, length: Int): Unit = writeLock.withLock {
withContext(sendDispatcher) {
require(offset >= 0) { "offset must >= 0" }
@ -72,7 +71,6 @@ internal actual class PlatformSocket(
/**
* @throws SendPacketInternalException
*/
@OptIn(ExperimentalIoApi::class)
actual override suspend fun send(packet: ByteReadPacket): Unit = writeLock.withLock {
withContext(sendDispatcher) {
logger.info { "Native socket sending: len=${packet.remaining}" }