Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KTOR-7213 Allow access to the configuration options of a HttpRequestRetry client plugin #4602

Merged
merged 4 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ktor-client/ktor-client-core/api/ktor-client-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ public final class io/ktor/client/plugins/HttpRequestRetryConfig {
public final fun exponentialDelay (DJJJZ)V
public static synthetic fun exponentialDelay$default (Lio/ktor/client/plugins/HttpRequestRetryConfig;DJJJZILjava/lang/Object;)V
public final fun getMaxRetries ()I
public final fun getModifyRequest ()Lkotlin/jvm/functions/Function2;
public final fun getRetryIf ()Lkotlin/jvm/functions/Function3;
public final fun getRetryOnExceptionIf ()Lkotlin/jvm/functions/Function3;
public final fun modifyRequest (Lkotlin/jvm/functions/Function2;)V
public final fun noRetry ()V
public final fun retryIf (ILkotlin/jvm/functions/Function3;)V
Expand Down
7 changes: 7 additions & 0 deletions ktor-client/ktor-client-core/api/ktor-client-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -653,9 +653,16 @@ final class io.ktor.client.plugins/HttpRedirectConfig { // io.ktor.client.plugin
final class io.ktor.client.plugins/HttpRequestRetryConfig { // io.ktor.client.plugins/HttpRequestRetryConfig|null[0]
constructor <init>() // io.ktor.client.plugins/HttpRequestRetryConfig.<init>|<init>(){}[0]

final val retryIf // io.ktor.client.plugins/HttpRequestRetryConfig.retryIf|{}retryIf[0]
final fun <get-retryIf>(): kotlin/Function3<io.ktor.client.plugins/HttpRetryShouldRetryContext, io.ktor.client.request/HttpRequest, io.ktor.client.statement/HttpResponse, kotlin/Boolean>? // io.ktor.client.plugins/HttpRequestRetryConfig.retryIf.<get-retryIf>|<get-retryIf>(){}[0]
final val retryOnExceptionIf // io.ktor.client.plugins/HttpRequestRetryConfig.retryOnExceptionIf|{}retryOnExceptionIf[0]
final fun <get-retryOnExceptionIf>(): kotlin/Function3<io.ktor.client.plugins/HttpRetryShouldRetryContext, io.ktor.client.request/HttpRequestBuilder, kotlin/Throwable, kotlin/Boolean>? // io.ktor.client.plugins/HttpRequestRetryConfig.retryOnExceptionIf.<get-retryOnExceptionIf>|<get-retryOnExceptionIf>(){}[0]

final var maxRetries // io.ktor.client.plugins/HttpRequestRetryConfig.maxRetries|{}maxRetries[0]
final fun <get-maxRetries>(): kotlin/Int // io.ktor.client.plugins/HttpRequestRetryConfig.maxRetries.<get-maxRetries>|<get-maxRetries>(){}[0]
final fun <set-maxRetries>(kotlin/Int) // io.ktor.client.plugins/HttpRequestRetryConfig.maxRetries.<set-maxRetries>|<set-maxRetries>(kotlin.Int){}[0]
final var modifyRequest // io.ktor.client.plugins/HttpRequestRetryConfig.modifyRequest|{}modifyRequest[0]
final fun <get-modifyRequest>(): kotlin/Function2<io.ktor.client.plugins/HttpRetryModifyRequestContext, io.ktor.client.request/HttpRequestBuilder, kotlin/Unit> // io.ktor.client.plugins/HttpRequestRetryConfig.modifyRequest.<get-modifyRequest>|<get-modifyRequest>(){}[0]

final fun constantDelay(kotlin/Long = ..., kotlin/Long = ..., kotlin/Boolean = ...) // io.ktor.client.plugins/HttpRequestRetryConfig.constantDelay|constantDelay(kotlin.Long;kotlin.Long;kotlin.Boolean){}[0]
final fun delay(kotlin.coroutines/SuspendFunction1<kotlin/Long, kotlin/Unit>) // io.ktor.client.plugins/HttpRequestRetryConfig.delay|delay(kotlin.coroutines.SuspendFunction1<kotlin.Long,kotlin.Unit>){}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,26 @@ public class HttpRequestRetryConfig {
internal lateinit var shouldRetry: HttpRetryShouldRetryContext.(HttpRequest, HttpResponse) -> Boolean
internal lateinit var shouldRetryOnException: HttpRetryShouldRetryContext.(HttpRequestBuilder, Throwable) -> Boolean
internal lateinit var delayMillis: HttpRetryDelayContext.(Int) -> Long
internal var modifyRequest: HttpRetryModifyRequestContext.(HttpRequestBuilder) -> Unit = {}
internal var delay: suspend (Long) -> Unit = { kotlinx.coroutines.delay(it) }

/**
* Function that determines whether a request should be retried based on the response.
*/
public val retryIf: (HttpRetryShouldRetryContext.(HttpRequest, HttpResponse) -> Boolean)?
get() = if (::shouldRetry.isInitialized) shouldRetry else null

/**
* Function that determines whether a request should be retried based on the exception.
*/
public val retryOnExceptionIf: (HttpRetryShouldRetryContext.(HttpRequestBuilder, Throwable) -> Boolean)?
get() = if (::shouldRetryOnException.isInitialized) shouldRetryOnException else null

/**
* Indicated how the request should be modified before retrying.
*/
public var modifyRequest: HttpRetryModifyRequestContext.(HttpRequestBuilder) -> Unit = {}
private set

/**
* The maximum amount of retries to perform for a request.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
package io.ktor.client.plugins.auth

import io.ktor.client.plugins.auth.providers.*
import io.ktor.test.dispatcher.*
import kotlinx.coroutines.*
import kotlinx.coroutines.test.runTest
import kotlin.test.*

class AuthTokenHolderTest {

@Test
@OptIn(DelicateCoroutinesApi::class)
fun testSetTokenCalledOnce() = testSuspend {
fun testSetTokenCalledOnce() = runTest {
val holder = AuthTokenHolder<BearerTokens> { TODO() }

val monitor = Job()
Expand Down Expand Up @@ -44,7 +44,7 @@ class AuthTokenHolderTest {

@Test
@OptIn(DelicateCoroutinesApi::class)
fun testLoadTokenWaitsUntilTokenIsLoaded() = testSuspend {
fun testLoadTokenWaitsUntilTokenIsLoaded() = runTest {
val monitor = Job()
val holder = AuthTokenHolder {
monitor.join()
Expand All @@ -66,7 +66,7 @@ class AuthTokenHolderTest {

@Test
@OptIn(DelicateCoroutinesApi::class)
fun testClearCalledWhileLoadingTokens() = testSuspend {
fun testClearCalledWhileLoadingTokens() = runTest {
val monitor = Job()

var clearTokenCalled = false
Expand Down Expand Up @@ -97,7 +97,7 @@ class AuthTokenHolderTest {

@Test
@OptIn(DelicateCoroutinesApi::class)
fun testClearCalledWhileSettingTokens() = testSuspend {
fun testClearCalledWhileSettingTokens() = runTest {
val monitor = Job()

var clearTokenCalled = false
Expand Down Expand Up @@ -128,7 +128,7 @@ class AuthTokenHolderTest {
}

@Test
fun testExceptionInLoadTokens() = testSuspend {
fun testExceptionInLoadTokens() = runTest {
var firstCall = true
val holder = AuthTokenHolder {
if (firstCall) {
Expand All @@ -142,7 +142,7 @@ class AuthTokenHolderTest {
}

@Test
fun testExceptionInSetTokens() = testSuspend {
fun testExceptionInSetTokens() = runTest {
val holder = AuthTokenHolder<String> {
fail("loadTokens argument function shouldn't be invoked")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

@file:Suppress("DEPRECATION")

package io.ktor.client.plugins.auth

import io.ktor.client.plugins.auth.providers.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.http.auth.*
import io.ktor.test.dispatcher.*
import io.ktor.util.*
import kotlinx.coroutines.test.runTest
import kotlin.test.*

class DigestProviderTest {
Expand Down Expand Up @@ -66,8 +63,8 @@ class DigestProviderTest {
}

@Test
fun addRequestHeadersSetsExpectedAuthHeaderFields() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend
fun addRequestHeadersSetsExpectedAuthHeaderFields() = runTest {
if (!PlatformUtils.IS_JVM) return@runTest

runIsApplicable(authAllFields)
val authHeader = addRequestHeaders(authAllFields)
Expand All @@ -78,8 +75,8 @@ class DigestProviderTest {
}

@Test
fun addRequestHeadersMissingRealm() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend
fun addRequestHeadersMissingRealm() = runTest {
if (!PlatformUtils.IS_JVM) return@runTest

@Suppress("DEPRECATION_ERROR")
val providerWithoutRealm = DigestAuthProvider("username", "pass", null)
Expand All @@ -93,8 +90,8 @@ class DigestProviderTest {
}

@Test
fun addRequestHeadersChangedRealm() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend
fun addRequestHeadersChangedRealm() = runTest {
if (!PlatformUtils.IS_JVM) return@runTest

@Suppress("DEPRECATION_ERROR")
val providerWithoutRealm = DigestAuthProvider("username", "pass", "wrong!")
Expand All @@ -104,8 +101,8 @@ class DigestProviderTest {
}

@Test
fun addRequestHeadersOmitsQopAndOpaqueWhenMissing() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend
fun addRequestHeadersOmitsQopAndOpaqueWhenMissing() = runTest {
if (!PlatformUtils.IS_JVM) return@runTest

runIsApplicable(authMissingQopAndOpaque)
val authHeader = addRequestHeaders(authMissingQopAndOpaque)
Expand All @@ -116,8 +113,8 @@ class DigestProviderTest {
}

@Test
fun testTokenWhenMissingRealmAndQop() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend
fun testTokenWhenMissingRealmAndQop() = runTest {
if (!PlatformUtils.IS_JVM) return@runTest

@Suppress("DEPRECATION_ERROR")
val providerWithoutRealm = DigestAuthProvider("username", "pass", null)
Expand Down
Loading