Jorge Domínguez
09/04/2023, 4:13 PMHttpRequestTimeoutException
? currently I’m doing the following:
on(Send) {
return try {
proceed(request)
} catch (e: Throwable) {
mapException(e)
}
}
in a custom plugin which works fine at runtime but for some reason when trying to test it using MockEngine
like this:
MockEngine {
delay(10_000L)
respondOk()
}
I get the HttpRequestTimeoutException
directly instead of the custom one I map it to in my custom pluginAleksei Tirman [JB]
09/05/2023, 6:35 AMJorge Domínguez
09/05/2023, 3:27 PMApplication.kt
fun main() {
defaultServer(Application::main).start()
runBlocking {
val client = HttpClient(CIO) {
install(HttpTimeout) {
requestTimeoutMillis = 1000
}
install(errorMapperPlugin)
}
// …
}
val errorMapperPlugin = createClientPlugin("ErrorMapperPlugin") {
on(Send) { request ->
return@on try {
proceed(request)
} catch(cause: Throwable) {
throw when (cause) {
is HttpRequestTimeoutException -> CustomRequestTimeoutException(
message = "Request timed out",
cause = cause
)
else -> cause
}
}
}
}
class CustomRequestTimeoutException(
override val message: String?,
override val cause: Throwable?
) : RuntimeException(message, cause)
• ApplicationTest
private val mockEngine = MockEngine {
delay(6000)
respondOk()
}
private val testHttpClient = HttpClient(mockEngine) {
install(HttpTimeout)
install(errorMapperPlugin)
}
@Test
fun exceptionShouldBeMappedCorrectly(): Unit = runBlocking {
try {
testHttpClient.request {
timeout {
requestTimeoutMillis = 3000
}
}
} catch (e: Throwable) {
println("Exception is $e") // This prints out "exception is HttpRequestTimeoutException"
assert(e is CustomRequestTimeoutException)
}
}
• build.gradle.kts
// ...
testImplementation("io.ktor:ktor-client-mock:$ktor_version")
Jorge Domínguez
09/05/2023, 3:32 PMMockEngine {
throw HttpRequestTimeoutException(url = "", timeoutMillis = null)
}
however I’m still curious about the original issueAleksei Tirman [JB]
09/06/2023, 7:26 AMreturn@on try {
) doesn't catch any exceptions. When timeout happening the HttpRequestBuilder.executionContext
is cancelled. You can detect the cancellation with the following code:
val errorMapperPlugin = createClientPlugin("ErrorMapperPlugin") {
onRequest { request, _ ->
request.executionContext.invokeOnCompletion { cause ->
if (cause?.cause is HttpRequestTimeoutException) {
// ...
}
}
}
// ...
}
Unfortunately, you cannot rethrow that exception from the plugin.