Hello all! We recently implemented a reverse proxy...
# ktor
a
Hello all! We recently implemented a reverse proxy using Ktor's Netty server and CIO client. After encountering the issue described here, we started to question whether our current approach might be too simplistic or missing important control mechanisms—such as validation, error handling, or timeouts. We're particularly concerned about how we forward the response body from the backend server. Below is a simplified example demonstrating how we currently handle the forwarding:
Copy code
fun main() {
    val client = HttpClient(CIO)

    embeddedServer(Netty, port = 8080) {
        routing {
            route("/{...}") {
                handle {
                    forwardRequest(call, client, "localhost", 9090)
                }
            }
        }
    }.start(wait = true)
}

suspend fun forwardRequest(call: ApplicationCall, client: HttpClient, targetHost: String, targetPort: Int) {
    val response = client.request {
        url {
            protocol = URLProtocol.HTTP
            host = targetHost
            port = targetPort
            encodedPath = call.request.uri
            encodedParameters.appendAll(call.request.rawQueryParameters)
        }
        method = call.request.httpMethod
        // handle headers
        if (call.request.httpMethod in listOf(<http://HttpMethod.Post|HttpMethod.Post>, HttpMethod.Put, HttpMethod.Patch)) {
            val byteReadChannel = call.receiveChannel()
            setBody(object : OutgoingContent.ReadChannelContent() {
                override val contentType = call.request.contentType()
                override val contentLength = call.request.contentLength()
                override fun readFrom() = byteReadChannel
            })
        }
    }

    val responseBodyChannel = response.bodyAsChannel()
    call.respond(
        status = response.status,
        object : OutgoingContent.WriteChannelContent() {
            // processing of headers, content type, content length, response status,... (override status,...)
            override suspend fun writeTo(channel: ByteWriteChannel) {
                    responseBodyChannel.copyAndClose(channel)
            }
        }
    )
}
We’d really appreciate any feedback from experienced developers — are there potential pitfalls or best practices we might be overlooking? What would you consider essential when building a robust reverse proxy in Ktor?
a
To have a better performance, you can: • Use
OutgoingContent.ReadChannelContent
to directly stream the original response body instead of copying it. • Develop a plugin to skip the routing process, which introduces a performance cost on every request.
a
We switched to
OutgoingContent.ReadChannelContent
and also will consider developing the plugin. Thank you!