Shamrock: 兼容性正反向HTTP调整

This commit is contained in:
WhiteChi 2023-12-20 18:52:24 +08:00
parent ccbfc9a1e1
commit aeabc66067
5 changed files with 85 additions and 3 deletions

View File

@ -8,6 +8,7 @@ import android.os.Bundle
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.header import io.ktor.client.request.header
import io.ktor.client.request.parameter
import io.ktor.client.request.url import io.ktor.client.request.url
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
@ -58,7 +59,7 @@ object DashboardInitializer {
url("http://127.0.0.1:$servicePort/get_account_info") url("http://127.0.0.1:$servicePort/get_account_info")
val token = ShamrockConfig.getToken(context) val token = ShamrockConfig.getToken(context)
if (token.isNotBlank()) { if (token.isNotBlank()) {
header("Authorization", "Bearer $token") parameter("token", token)
} }
}.let { }.let {
if (it.status == HttpStatusCode.OK) { if (it.status == HttpStatusCode.OK) {

View File

@ -0,0 +1,8 @@
package moe.fuqiuluo.qqinterface.servlet
/**
* QQ收藏相关接口
*/
internal object QFavSvc: BaseSvc() {
}

View File

@ -1,6 +1,7 @@
package moe.fuqiuluo.shamrock.remote.api package moe.fuqiuluo.shamrock.remote.api
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call import io.ktor.server.application.call
import io.ktor.server.request.httpVersion import io.ktor.server.request.httpVersion
import io.ktor.server.response.respondText import io.ktor.server.response.respondText
@ -8,8 +9,12 @@ import io.ktor.server.routing.Routing
import io.ktor.server.routing.get import io.ktor.server.routing.get
import io.ktor.server.routing.post import io.ktor.server.routing.post
import io.ktor.server.routing.route import io.ktor.server.routing.route
import io.ktor.util.pipeline.PipelineContext
import kotlinx.serialization.Contextual import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import moe.fuqiuluo.shamrock.remote.HTTPServer import moe.fuqiuluo.shamrock.remote.HTTPServer
import moe.fuqiuluo.shamrock.remote.action.ActionManager import moe.fuqiuluo.shamrock.remote.action.ActionManager
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
@ -18,12 +23,16 @@ import moe.fuqiuluo.shamrock.remote.entries.EmptyObject
import moe.fuqiuluo.shamrock.remote.entries.IndexData import moe.fuqiuluo.shamrock.remote.entries.IndexData
import moe.fuqiuluo.shamrock.remote.entries.Status import moe.fuqiuluo.shamrock.remote.entries.Status
import moe.fuqiuluo.shamrock.tools.EmptyJsonObject import moe.fuqiuluo.shamrock.tools.EmptyJsonObject
import moe.fuqiuluo.shamrock.tools.asJsonObjectOrNull
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.fetchOrNull import moe.fuqiuluo.shamrock.tools.fetchOrNull
import moe.fuqiuluo.shamrock.tools.fetchOrThrow import moe.fuqiuluo.shamrock.tools.fetchOrThrow
import moe.fuqiuluo.shamrock.tools.fetchPostJsonElement import moe.fuqiuluo.shamrock.tools.fetchPostJsonElement
import moe.fuqiuluo.shamrock.tools.fetchPostJsonElementOrNull
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject
import moe.fuqiuluo.shamrock.tools.fetchPostJsonObjectOrNull import moe.fuqiuluo.shamrock.tools.fetchPostJsonObjectOrNull
import moe.fuqiuluo.shamrock.tools.isJsonArray import moe.fuqiuluo.shamrock.tools.isJsonArray
import moe.fuqiuluo.shamrock.tools.isJsonData
import moe.fuqiuluo.shamrock.tools.isJsonObject import moe.fuqiuluo.shamrock.tools.isJsonObject
import moe.fuqiuluo.shamrock.tools.isJsonString import moe.fuqiuluo.shamrock.tools.isJsonString
import moe.fuqiuluo.shamrock.tools.json import moe.fuqiuluo.shamrock.tools.json
@ -39,6 +48,31 @@ data class OldApiResult<T>(
val data: T? = null val data: T? = null
) )
suspend fun PipelineContext<Unit, ApplicationCall>.handleAsJsonObject(data: JsonObject) {
val action = data["action"].asString
val echo = data["echo"]!!
call.attributes.put(ECHO_KEY, echo)
val params = data["params"].asJsonObjectOrNull ?: EmptyJsonObject
val handler = ActionManager[action]
if (handler == null) {
respond(false, Status.UnsupportedAction, EmptyObject, "不支持的Action", echo = echo)
} else {
call.respondText(handler.handle(ActionSession(params, echo)), ContentType.Application.Json)
}
}
suspend fun PipelineContext<Unit, ApplicationCall>.handleAsJsonArray(data: JsonArray) {
data.forEach {
when (it) {
is JsonArray -> handleAsJsonArray(it)
is JsonObject -> handleAsJsonObject(it)
else -> handleAsJsonObject(it.jsonObject)
}
}
}
fun Routing.echoVersion() { fun Routing.echoVersion() {
route("/") { route("/") {
get { get {
@ -49,6 +83,15 @@ fun Routing.echoVersion() {
) )
} }
post { post {
fetchPostJsonElementOrNull()?.let {
if (it is JsonArray) {
handleAsJsonArray(it)
return@post
} else if (it is JsonObject) {
handleAsJsonObject(it)
return@post
}
}
val action = fetchOrThrow("action") val action = fetchOrThrow("action")
val echo = if (isJsonObject("echo") || isJsonArray("echo")) { val echo = if (isJsonObject("echo") || isJsonArray("echo")) {
fetchPostJsonElement("echo") fetchPostJsonElement("echo")

View File

@ -30,7 +30,7 @@ internal abstract class HttpTransmitServlet : BaseTransmitServlet {
if (!allowTransmit()) return null if (!allowTransmit()) return null
try { try {
if (address.startsWith("http://") || address.startsWith("https://")) { if (address.startsWith("http://") || address.startsWith("https://")) {
return GlobalClient.post(address) { val response = GlobalClient.post(address) {
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(body) setBody(body)
@ -44,6 +44,11 @@ internal abstract class HttpTransmitServlet : BaseTransmitServlet {
header("X-Client-Role", "Universal") header("X-Client-Role", "Universal")
header("Sec-WebSocket-Protocol", "11.Shamrock") header("Sec-WebSocket-Protocol", "11.Shamrock")
} }
return if (response.status.value == 204) {
null
} else {
response
}
} else { } else {
LogCenter.log("HTTP推送地址错误: ${address}", Level.ERROR) LogCenter.log("HTTP推送地址错误: ${address}", Level.ERROR)
} }

View File

@ -37,6 +37,8 @@ annotation class ShamrockDsl
private val keyIsJson = AttributeKey<Boolean>("isJson") private val keyIsJson = AttributeKey<Boolean>("isJson")
private val keyJsonObject = AttributeKey<JsonObject>("paramsJson") private val keyJsonObject = AttributeKey<JsonObject>("paramsJson")
private val keyJsonArray = AttributeKey<JsonArray>("paramsJsonArray")
private val keyJsonElement = AttributeKey<JsonElement>("paramsJsonElement")
private val keyParts = AttributeKey<Parameters>("paramsParts") private val keyParts = AttributeKey<Parameters>("paramsParts")
suspend fun ApplicationCall.fetch(key: String): String { suspend fun ApplicationCall.fetch(key: String): String {
@ -167,7 +169,9 @@ suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostOrThrow(key: String)
} }
fun PipelineContext<Unit, ApplicationCall>.isJsonData(): Boolean { fun PipelineContext<Unit, ApplicationCall>.isJsonData(): Boolean {
return ContentType.Application.Json == call.request.contentType() || (keyIsJson in call.attributes && call.attributes[keyIsJson]) return ContentType.Application.Json == call.request.contentType()
|| (keyIsJson in call.attributes && call.attributes[keyIsJson])
|| (keyJsonElement in call.attributes)
} }
suspend fun PipelineContext<Unit, ApplicationCall>.isJsonString(key: String): Boolean { suspend fun PipelineContext<Unit, ApplicationCall>.isJsonString(key: String): Boolean {
@ -245,12 +249,33 @@ suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonObjectOrNull(key
call.attributes[keyJsonObject] call.attributes[keyJsonObject]
} else { } else {
Json.parseToJsonElement(call.receiveText()).jsonObject.also { Json.parseToJsonElement(call.receiveText()).jsonObject.also {
call.attributes.put(keyIsJson, true)
call.attributes.put(keyJsonObject, it) call.attributes.put(keyJsonObject, it)
} }
} }
return data[key].asJsonObjectOrNull return data[key].asJsonObjectOrNull
} }
suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonElementOrNull(): JsonElement? {
return runCatching {
if (call.attributes.contains(keyJsonObject)) {
call.attributes[keyJsonObject]
} else if (call.attributes.contains(keyJsonArray)) {
call.attributes[keyJsonArray]
} else if (call.attributes.contains(keyJsonElement)) {
call.attributes[keyJsonElement]
} else {
Json.parseToJsonElement(call.receiveText()).also {
call.attributes.put(keyJsonElement, it)
if (it is JsonObject) {
call.attributes.put(keyJsonObject, it)
} else if (it is JsonArray) {
call.attributes.put(keyJsonArray, it)
}
}
}
}.getOrNull()
}
suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonArray(key: String): JsonArray { suspend fun PipelineContext<Unit, ApplicationCall>.fetchPostJsonArray(key: String): JsonArray {
val data = if (call.attributes.contains(keyJsonObject)) { val data = if (call.attributes.contains(keyJsonObject)) {