Home > Software design >  Ktor modify request on retry not working as expected
Ktor modify request on retry not working as expected

Time:01-24

I have a custom retry policy on receiving 5XX errors from the server. The idea is to retry until I get a non-5XX error with an exponential delay between each retry request also I would like to update the request body on every retry.

Here is my code

import io.ktor.client.*
import io.ktor.client.engine.java.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.request.*
import io.ktor.server.routing.*
import kotlinx.coroutines.*
import kotlin.time.Duration.Companion.seconds

suspend fun main() {
    val serverJob = CoroutineScope(Dispatchers.Default).launch { startServer() }

    val client = HttpClient(Java) {
        install(HttpTimeout) {
            connectTimeoutMillis = 5.seconds.inWholeMilliseconds
        }
        install(HttpRequestRetry)
    }

    client.post {
        url("http://127.0.0.1:8080/")
        setBody("Hello")
        retry {
            retryOnServerErrors(maxRetries = Int.MAX_VALUE)
            exponentialDelay(maxDelayMs = 128.seconds.inWholeMilliseconds)
            modifyRequest { it.setBody("With Different body ...") } // It's not working! if I comment this out then my retry logic works as expected
        }
    }

    client.close()
    serverJob.cancelAndJoin()
}

suspend fun startServer() {
    embeddedServer(Netty, port = 8080) {
        routing {
            post("/") {
                val text = call.receiveText()
                println("Retrying exponentially... $text")
                call.response.status(HttpStatusCode(500, "internal server error"))
            }
        }
    }.start(wait = true)
}

As you can see, if I comment out modifyRequest { it.setBody("With Different body ...") } line from retry logic then everything works fine. If I include that line it only tries once and stuck there, what I'm doing wrong here? how to change the request body for every retry?

CodePudding user response:

The problem is that rendering (transformation to an OutgoingContent) of a request body happens during the execution of the HttpRequestPipeline, which takes place only once after making an initial request. The HTTP request retrying happens after in the HttpSendPipeline. Since you pass a String as a request body it needs to be transformed before the actual sending. To solve this problem, you can manually wrap your String into the TextContent instance and pass it to the setBody method:

retry {
    retryOnServerErrors(maxRetries = Int.MAX_VALUE)
    exponentialDelay(maxDelayMs = 128.seconds.inWholeMilliseconds)
    modifyRequest {
        it.setBody(TextContent("With Different body ...", ContentType.Text.Plain))
    }
}
  • Related