From d6402662d780dacd4e209cbf8d0a95129868c963 Mon Sep 17 00:00:00 2001 From: ryoii Date: Mon, 9 Oct 2023 19:09:02 +0800 Subject: [PATCH] Support http forward (temp impl) --- .../http/{support => plugin}/HttpForward.kt | 38 ++++++++++++-- .../api/http/adapter/http/plugin/hooks.kt | 12 +++++ .../adapter/http/plugin/HttpForwardTest.kt | 51 ++++++++++++++++++- 3 files changed, 96 insertions(+), 5 deletions(-) rename mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/{support => plugin}/HttpForward.kt (52%) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/support/HttpForward.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/HttpForward.kt similarity index 52% rename from mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/support/HttpForward.kt rename to mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/HttpForward.kt index 3cfbc9d2..b45aa6ec 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/support/HttpForward.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/HttpForward.kt @@ -7,23 +7,53 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -package net.mamoe.mirai.api.http.adapter.http.support +package net.mamoe.mirai.api.http.adapter.http.plugin import io.ktor.http.* import io.ktor.server.application.* +import io.ktor.server.engine.* import io.ktor.server.request.* +import io.ktor.util.* import io.ktor.util.pipeline.* + +val HttpForwardAttributeKey = AttributeKey("HttpForward") +val HttpForwardPhase = PipelinePhase("Forward") +val HttpForward = createApplicationPlugin("HttpForward", ::HttpForwardConfig) { + application.insertPhaseAfter(ApplicationCallPipeline.Call, HttpForwardPhase) + + application.interceptorsForPhase(ApplicationCallPipeline.Call).forEach { interceptor -> + application.intercept(HttpForwardPhase, interceptor) + + application.intercept(HttpForwardPhase) { + val forwardContext = call.attributes.getOrNull(HttpForwardAttributeKey) ?: return@intercept + val forwardCall = ApplicationForwardCall(call, forwardContext.router, forwardContext.body) + val callInterceptors = application.interceptorsForPhase(ApplicationCallPipeline.Call) + + + } +} + +class HttpForwardConfig { + var forward: String = "" +} + +data class HttpForwardContext(val router: String, val body: Any?) + suspend fun ApplicationCall.forward(forward: String) { - application.execute(ApplicationForwardCall(this, forward)) +// application.execute(ApplicationForwardCall(this, forward)) + attributes.put(HttpForwardAttributeKey, HttpForwardContext(forward, null)) } suspend fun ApplicationCall.forward(forward: String, body: Any) { - application.execute(ApplicationForwardCall(this, forward, body)) +// application.execute(ApplicationForwardCall(this, forward, body)) + attributes.put(HttpForwardAttributeKey, HttpForwardContext(forward, body)) } internal fun forwardReceivePipeline(body: Any): ApplicationReceivePipeline = ApplicationReceivePipeline().apply { - intercept(ApplicationReceivePipeline.Transform) { proceedWith(body) } + intercept(ApplicationReceivePipeline.Transform) { + proceedWith(body) + } } internal class ApplicationForwardCall( diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/hooks.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/hooks.kt index 4deaded7..241aee83 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/hooks.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/hooks.kt @@ -20,6 +20,18 @@ internal object Monitor : Hook Unit> { } } +internal object ReceiveBodyTransforming : Hook Any> { + override fun install( + pipeline: ApplicationCallPipeline, + handler: suspend (call: ApplicationCall, state: Any) -> Any + ) { + pipeline.receivePipeline.intercept(ApplicationReceivePipeline.Before) { + val body = handler(call, it) + proceedWith(body) + } + } +} + internal object ReceiveBodyTransformed : Hook Any> { override fun install( pipeline: ApplicationCallPipeline, diff --git a/mirai-api-http/src/test/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/HttpForwardTest.kt b/mirai-api-http/src/test/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/HttpForwardTest.kt index 0cb29b2e..d89f993a 100644 --- a/mirai-api-http/src/test/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/HttpForwardTest.kt +++ b/mirai-api-http/src/test/kotlin/net/mamoe/mirai/api/http/adapter/http/plugin/HttpForwardTest.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.api.http.adapter.http.plugin import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* +import io.ktor.serialization.* import io.ktor.serialization.kotlinx.json.* import io.ktor.server.application.* import io.ktor.server.plugins.contentnegotiation.* @@ -19,7 +20,11 @@ import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.server.testing.* -import net.mamoe.mirai.api.http.adapter.http.support.forward +import io.ktor.util.reflect.* +import io.ktor.utils.io.* +import io.ktor.utils.io.charsets.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement import net.mamoe.mirai.api.http.adapter.internal.dto.parameter.LongTargetDTO import net.mamoe.mirai.api.http.adapter.internal.dto.parameter.NudgeDTO import net.mamoe.mirai.api.http.adapter.internal.serializer.BuiltinJsonSerializer @@ -127,4 +132,48 @@ class HttpForwardTest { assertEquals("321", it.bodyAsText()) } } + + + @Serializable + private data class NeatedDto( + val router: String, + val body: JsonElement, + ) + + @Test + fun testPostRequestForwardNestedBody() = testApplication { + // No need for DoubleReceive + install(GlobalExceptionHandler) { printTrace = true } + install(DoubleReceive) + install(HttpRouterMonitor) + install(ContentNegotiation) { + json(json=BuiltinJsonSerializer.buildJson()) + register(ContentType.Application.Json, object : ContentConverter { + override suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any? { + TODO("Not yet implemented") + } + }) + } + + routing { + post("/test") { + val receive = call.receive() + assertEquals("/forward", receive.router) + call.forward("/forward", receive.body) + } + + post("/forward") { + val receive = call.receive() + call.respondText(receive.target.toString()) + } + } + + client.post("/test") { + contentType(ContentType.Application.Json) + setBody("""{"router":"/forward","body":{"target":321}}""") + }.also { + assertEquals(HttpStatusCode.OK, it.status) + assertEquals("321", it.bodyAsText()) + } + } } \ No newline at end of file