Skip to content

Commit

Permalink
Support http forward
Browse files Browse the repository at this point in the history
  • Loading branch information
ryoii committed Oct 9, 2023
1 parent 0a3e454 commit ea1cc79
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2023 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.api.http.adapter.http.support

import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.util.pipeline.*

suspend fun ApplicationCall.forward(forward: String) {
application.execute(ApplicationForwardCall(this, forward))
}

suspend fun ApplicationCall.forward(forward: String, body: Any) {
application.execute(ApplicationForwardCall(this, forward, body))
}

internal fun forwardReceivePipeline(body: Any): ApplicationReceivePipeline = ApplicationReceivePipeline().apply {
intercept(ApplicationReceivePipeline.Transform) { proceedWith(body) }
}

internal class ApplicationForwardCall(
val call: ApplicationCall, forward: String, body: Any? = null
) : ApplicationCall by call {
override val request: ApplicationRequest = DelegateApplicationRequest(call.request, forward, body)
}

internal class DelegateApplicationRequest(
private val delegate: ApplicationRequest, forward: String, body: Any?
) : ApplicationRequest by delegate {
private val _pipeline by lazy { body?.let { forwardReceivePipeline(it) } ?: delegate.pipeline }
override val local = DelegateRequestConnectionPoint(delegate.local, forward)
override val pipeline: ApplicationReceivePipeline = _pipeline
}

internal class DelegateRequestConnectionPoint(
private val delegate: RequestConnectionPoint, override val uri: String
) : RequestConnectionPoint by delegate
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2023 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.api.http.adapter.http.plugin

import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.doublereceive.*
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 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
import kotlin.test.Test
import kotlin.test.assertEquals

class HttpForwardTest {

@Test
fun testGetRequestForward() = testApplication {
routing {
get("/test") {
call.forward("/forward")
}

get("/forward") {
call.respondText(call.parameters["key"] ?: "null")
}
}

client.get("/test") {
parameter("key", "value")
}.also {
assertEquals(HttpStatusCode.OK, it.status)
assertEquals("value", it.bodyAsText())
}
}

@Test
fun testPostRequestForwardReceiveBody() = testApplication {
install(ContentNegotiation) { json(json=BuiltinJsonSerializer.buildJson()) }

routing {
post("/test") {
call.forward("/forward")
}

post("/forward") {
val receive = call.receive<LongTargetDTO>()
call.respondText(receive.target.toString())
}
}

client.post("/test") {
contentType(ContentType.Application.Json)
setBody("""{"target":123}""")
}.also {
assertEquals(HttpStatusCode.OK, it.status)
assertEquals("123", it.bodyAsText())
}
}

@Test
fun testPostRequestForwardDoubleReceiveBody() = testApplication {
install(DoubleReceive)
install(ContentNegotiation) { json(json=BuiltinJsonSerializer.buildJson()) }

routing {
post("/test") {
val receive = call.receive<LongTargetDTO>()
assertEquals(123, receive.target)
call.forward("/forward")
}

post("/forward") {
val receive = call.receive<LongTargetDTO>()
call.respondText(receive.target.toString())
}
}

client.post("/test") {
contentType(ContentType.Application.Json)
setBody("""{"target":123}""")
}.also {
assertEquals(HttpStatusCode.OK, it.status)
assertEquals("123", it.bodyAsText())
}
}

@Test
fun testPostRequestForwardResetBody() = testApplication {
// No need for DoubleReceive
install(DoubleReceive)
install(HttpRouterMonitor)
install(ContentNegotiation) { json(json=BuiltinJsonSerializer.buildJson()) }

routing {
post("/test") {
val receive = call.receive<LongTargetDTO>()
assertEquals(123, receive.target)
call.forward("/forward", NudgeDTO(321, 321, "kind"))
}

post("/forward") {
val receive = call.receive<NudgeDTO>()
call.respondText(receive.target.toString())
}
}

client.post("/test") {
contentType(ContentType.Application.Json)
setBody("""{"target":123}""")
}.also {
assertEquals(HttpStatusCode.OK, it.status)
assertEquals("321", it.bodyAsText())
}
}
}

0 comments on commit ea1cc79

Please sign in to comment.